From bd9e6617827818fd043452c08c606f07b78014a0 Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdesdk@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- COPYING | 346 ++ COPYING-DOCS | 397 ++ Makefile.am.in | 19 + Makefile.cvs | 15 + README | 26 + cervisia/COPYING | 339 ++ cervisia/ChangeLog | 1543 ++++++ cervisia/HACKING | 79 + cervisia/Makefile.am | 73 + cervisia/README | 19 + cervisia/TODO | 26 + cervisia/addremovedlg.cpp | 101 + cervisia/addremovedlg.h | 49 + cervisia/addrepositorydlg.cpp | 202 + cervisia/addrepositorydlg.h | 73 + cervisia/annotatectl.cpp | 198 + cervisia/annotatectl.h | 44 + cervisia/annotatedlg.cpp | 58 + cervisia/annotatedlg.h | 59 + cervisia/annotateview.cpp | 206 + cervisia/annotateview.h | 56 + cervisia/cervisia-change_repos_list.pl | 59 + cervisia/cervisia-normalize_cvsroot.pl | 53 + cervisia/cervisia.1.in | 217 + cervisia/cervisia.desktop | 74 + cervisia/cervisia.pod | 109 + cervisia/cervisia.upd | 92 + cervisia/cervisiapart.cpp | 1939 ++++++++ cervisia/cervisiapart.h | 219 + cervisia/cervisiapart.kcfg | 37 + cervisia/cervisiasettings.kcfgc | 4 + cervisia/cervisiashell.cpp | 227 + cervisia/cervisiashell.h | 71 + cervisia/cervisiashellui.rc | 19 + cervisia/cervisiaui.rc | 179 + cervisia/change_colors.pl | 31 + cervisia/changelogdlg.cpp | 188 + cervisia/changelogdlg.h | 60 + cervisia/checkoutdlg.cpp | 484 ++ cervisia/checkoutdlg.h | 89 + cervisia/commitdlg.cpp | 326 ++ cervisia/commitdlg.h | 86 + cervisia/cvsdir.cpp | 56 + cervisia/cvsdir.h | 44 + cervisia/cvsinitdlg.cpp | 91 + cervisia/cvsinitdlg.h | 52 + cervisia/cvsservice/DESIGN | 108 + cervisia/cvsservice/Makefile.am | 54 + cervisia/cvsservice/TODO | 12 + cervisia/cvsservice/cvsaskpass.cpp | 77 + cervisia/cvsservice/cvsjob.cpp | 236 + cervisia/cvsservice/cvsjob.h | 83 + cervisia/cvsservice/cvsloginjob.cpp | 156 + cervisia/cvsservice/cvsloginjob.h | 58 + cervisia/cvsservice/cvsservice.cpp | 1008 ++++ cervisia/cvsservice/cvsservice.desktop | 73 + cervisia/cvsservice/cvsservice.h | 375 ++ cervisia/cvsservice/cvsserviceutils.cpp | 45 + cervisia/cvsservice/cvsserviceutils.h | 40 + cervisia/cvsservice/main.cpp | 46 + cervisia/cvsservice/repository.cpp | 266 + cervisia/cvsservice/repository.h | 109 + cervisia/cvsservice/sshagent.cpp | 237 + cervisia/cvsservice/sshagent.h | 64 + cervisia/diffdlg.cpp | 509 ++ cervisia/diffdlg.h | 87 + cervisia/diffview.cpp | 507 ++ cervisia/diffview.h | 132 + cervisia/dirignorelist.cpp | 47 + cervisia/dirignorelist.h | 50 + cervisia/editwithmenu.cpp | 78 + cervisia/editwithmenu.h | 55 + cervisia/entry.cpp | 34 + cervisia/entry.h | 85 + cervisia/entry_status.cpp | 81 + cervisia/entry_status.h | 64 + cervisia/entry_status_change.h | 54 + cervisia/eventsrc | 88 + cervisia/globalignorelist.cpp | 100 + cervisia/globalignorelist.h | 55 + cervisia/hi16-app-cervisia.png | Bin 0 -> 963 bytes cervisia/hi22-app-cervisia.png | Bin 0 -> 1210 bytes cervisia/hi32-app-cervisia.png | Bin 0 -> 2300 bytes cervisia/hi48-app-cervisia.png | Bin 0 -> 4074 bytes cervisia/historydlg.cpp | 397 ++ cervisia/historydlg.h | 62 + cervisia/ignorelistbase.cpp | 49 + cervisia/ignorelistbase.h | 49 + cervisia/logdlg.cpp | 620 +++ cervisia/logdlg.h | 105 + cervisia/loginfo.cpp | 140 + cervisia/loginfo.h | 163 + cervisia/loglist.cpp | 233 + cervisia/loglist.h | 71 + cervisia/logmessageedit.cpp | 204 + cervisia/logmessageedit.h | 61 + cervisia/logplainview.cpp | 203 + cervisia/logplainview.h | 62 + cervisia/logtree.cpp | 501 ++ cervisia/logtree.h | 91 + cervisia/main.cpp | 213 + cervisia/mergedlg.cpp | 164 + cervisia/mergedlg.h | 65 + cervisia/misc.cpp | 350 ++ cervisia/misc.h | 98 + cervisia/move_repositories.pl | 42 + cervisia/overview.h | 15 + cervisia/patchoptiondlg.cpp | 115 + cervisia/patchoptiondlg.h | 60 + cervisia/pics/Makefile.am | 1 + cervisia/pics/README | 9 + cervisia/pics/cr16-action-vcs_add.png | Bin 0 -> 904 bytes cervisia/pics/cr16-action-vcs_commit.png | Bin 0 -> 908 bytes cervisia/pics/cr16-action-vcs_diff.png | Bin 0 -> 950 bytes cervisia/pics/cr16-action-vcs_remove.png | Bin 0 -> 812 bytes cervisia/pics/cr16-action-vcs_status.png | Bin 0 -> 942 bytes cervisia/pics/cr16-action-vcs_update.png | Bin 0 -> 869 bytes cervisia/pics/cr22-action-vcs_add.png | Bin 0 -> 1337 bytes cervisia/pics/cr22-action-vcs_commit.png | Bin 0 -> 1355 bytes cervisia/pics/cr22-action-vcs_diff.png | Bin 0 -> 1576 bytes cervisia/pics/cr22-action-vcs_remove.png | Bin 0 -> 1186 bytes cervisia/pics/cr22-action-vcs_status.png | Bin 0 -> 1443 bytes cervisia/pics/cr22-action-vcs_update.png | Bin 0 -> 1277 bytes cervisia/pics/cr32-action-vcs_add.png | Bin 0 -> 2135 bytes cervisia/pics/cr32-action-vcs_commit.png | Bin 0 -> 2172 bytes cervisia/pics/cr32-action-vcs_diff.png | Bin 0 -> 2833 bytes cervisia/pics/cr32-action-vcs_remove.png | Bin 0 -> 1901 bytes cervisia/pics/cr32-action-vcs_status.png | Bin 0 -> 2400 bytes cervisia/pics/cr32-action-vcs_update.png | Bin 0 -> 2046 bytes cervisia/pics/cr48-action-vcs_add.png | Bin 0 -> 3617 bytes cervisia/pics/cr48-action-vcs_commit.png | Bin 0 -> 3712 bytes cervisia/pics/cr48-action-vcs_diff.png | Bin 0 -> 5241 bytes cervisia/pics/cr48-action-vcs_remove.png | Bin 0 -> 3212 bytes cervisia/pics/cr48-action-vcs_status.png | Bin 0 -> 4265 bytes cervisia/pics/cr48-action-vcs_update.png | Bin 0 -> 3554 bytes cervisia/pics/crsc-action-vcs_add.svgz | Bin 0 -> 6021 bytes cervisia/pics/crsc-action-vcs_commit.svgz | Bin 0 -> 6954 bytes cervisia/pics/crsc-action-vcs_diff.svgz | Bin 0 -> 6562 bytes cervisia/pics/crsc-action-vcs_remove.svgz | Bin 0 -> 5759 bytes cervisia/pics/crsc-action-vcs_status.svgz | Bin 0 -> 7371 bytes cervisia/pics/crsc-action-vcs_update.svgz | Bin 0 -> 6887 bytes cervisia/progressdlg.cpp | 276 ++ cervisia/progressdlg.h | 68 + cervisia/protocolview.cpp | 196 + cervisia/protocolview.h | 78 + cervisia/qttableview.cpp | 2278 +++++++++ cervisia/qttableview.h | 252 + cervisia/repositories.cpp | 129 + cervisia/repositories.h | 38 + cervisia/repositorydlg.cpp | 504 ++ cervisia/repositorydlg.h | 77 + cervisia/resolvedlg.cpp | 634 +++ cervisia/resolvedlg.h | 98 + cervisia/resolvedlg_p.cpp | 59 + cervisia/resolvedlg_p.h | 51 + cervisia/settingsdlg.cpp | 385 ++ cervisia/settingsdlg.h | 104 + cervisia/settingsdlg_advanced.ui | 97 + cervisia/stringmatcher.cpp | 146 + cervisia/stringmatcher.h | 77 + cervisia/tagdlg.cpp | 147 + cervisia/tagdlg.h | 74 + cervisia/tests/resolvedlg-BR74903-test-01.txt | 4 + cervisia/tests/resolvedlg-conflict-test-01.txt | 5 + cervisia/tests/resolvedlg-conflict-test-02.txt | 9 + cervisia/tooltip.cpp | 111 + cervisia/tooltip.h | 75 + cervisia/updatedlg.cpp | 161 + cervisia/updatedlg.h | 66 + cervisia/updateview.cpp | 629 +++ cervisia/updateview.h | 117 + cervisia/updateview_items.cpp | 826 ++++ cervisia/updateview_items.h | 169 + cervisia/updateview_visitors.cpp | 98 + cervisia/updateview_visitors.h | 69 + cervisia/version.h | 2 + cervisia/watchdlg.cpp | 107 + cervisia/watchdlg.h | 52 + cervisia/watchersdlg.cpp | 121 + cervisia/watchersdlg.h | 45 + configure.in.in | 5 + doc/Makefile.am | 5 + doc/cervisia/Makefile.am | 2 + doc/cervisia/annotate.png | Bin 0 -> 8728 bytes doc/cervisia/checkout.png | Bin 0 -> 8672 bytes doc/cervisia/commit.png | Bin 0 -> 10511 bytes doc/cervisia/diff.png | Bin 0 -> 15331 bytes doc/cervisia/history.png | Bin 0 -> 10831 bytes doc/cervisia/import.png | Bin 0 -> 7167 bytes doc/cervisia/index.docbook | 3224 +++++++++++++ doc/cervisia/logtree.png | Bin 0 -> 13288 bytes doc/cervisia/mainview.png | Bin 0 -> 26607 bytes doc/cervisia/patch.png | Bin 0 -> 7065 bytes doc/cervisia/popup.png | Bin 0 -> 8025 bytes doc/cervisia/repositories.png | Bin 0 -> 16964 bytes doc/cervisia/resolve.png | Bin 0 -> 14074 bytes doc/cervisia/updatetag.png | Bin 0 -> 5939 bytes doc/kapptemplate/Makefile.am | 2 + doc/kapptemplate/man-kapptemplate.1.docbook | 115 + doc/kbabel/Makefile.am | 3 + doc/kbabel/TODO | 4 + doc/kbabel/back.png | Bin 0 -> 1188 bytes doc/kbabel/bottom.png | Bin 0 -> 599 bytes doc/kbabel/catalogmanager.png | Bin 0 -> 1540 bytes doc/kbabel/catalogmanager_broken.png | Bin 0 -> 188 bytes doc/kbabel/catalogmanager_missing.png | Bin 0 -> 215 bytes doc/kbabel/catalogmanager_needwork.png | Bin 0 -> 189 bytes doc/kbabel/catalogmanager_nopot.png | Bin 0 -> 169 bytes doc/kbabel/catalogmanager_nopot_ok.png | Bin 0 -> 267 bytes doc/kbabel/catalogmanager_ok.png | Bin 0 -> 219 bytes doc/kbabel/catalogmanager_reload.png | Bin 0 -> 1203 bytes doc/kbabel/catman.docbook | 214 + doc/kbabel/dbcan.png | Bin 0 -> 17645 bytes doc/kbabel/dictionaries.docbook | 517 ++ doc/kbabel/editcopy.png | Bin 0 -> 799 bytes doc/kbabel/editcut.png | Bin 0 -> 833 bytes doc/kbabel/editpaste.png | Bin 0 -> 979 bytes doc/kbabel/faq.docbook | 66 + doc/kbabel/fileopen.png | Bin 0 -> 554 bytes doc/kbabel/filesave.png | Bin 0 -> 728 bytes doc/kbabel/find.png | Bin 0 -> 1174 bytes doc/kbabel/forward.png | Bin 0 -> 1173 bytes doc/kbabel/glossary.docbook | 211 + doc/kbabel/index.docbook | 174 + doc/kbabel/kbabeldict.docbook | 85 + doc/kbabel/man-catalogmanager.1.docbook | 77 + doc/kbabel/menu.docbook | 2320 +++++++++ doc/kbabel/msgid2msgstr.png | Bin 0 -> 391 bytes doc/kbabel/next.png | Bin 0 -> 247 bytes doc/kbabel/nexterror.png | Bin 0 -> 375 bytes doc/kbabel/nextfuzzy.png | Bin 0 -> 343 bytes doc/kbabel/nextfuzzyuntrans.png | Bin 0 -> 351 bytes doc/kbabel/nextuntranslated.png | Bin 0 -> 309 bytes doc/kbabel/pref_diff.png | Bin 0 -> 9155 bytes doc/kbabel/pref_edit_appearance.png | Bin 0 -> 9383 bytes doc/kbabel/pref_edit_general.png | Bin 0 -> 12868 bytes doc/kbabel/pref_fonts.png | Bin 0 -> 12317 bytes doc/kbabel/pref_proj_catman.png | Bin 0 -> 10571 bytes doc/kbabel/pref_proj_diff.png | Bin 0 -> 10616 bytes doc/kbabel/pref_proj_file_commands.png | Bin 0 -> 13864 bytes doc/kbabel/pref_proj_folder_commands.png | Bin 0 -> 12319 bytes doc/kbabel/pref_proj_source.png | Bin 0 -> 14305 bytes doc/kbabel/pref_search.png | Bin 0 -> 7939 bytes doc/kbabel/pref_wizard_page1.png | Bin 0 -> 7117 bytes doc/kbabel/pref_wizard_page2.png | Bin 0 -> 6475 bytes doc/kbabel/preferences.docbook | 1418 ++++++ doc/kbabel/preverror.png | Bin 0 -> 384 bytes doc/kbabel/prevfuzzy.png | Bin 0 -> 341 bytes doc/kbabel/prevfuzzyuntrans.png | Bin 0 -> 352 bytes doc/kbabel/previous.png | Bin 0 -> 260 bytes doc/kbabel/prevuntranslated.png | Bin 0 -> 308 bytes doc/kbabel/redo.png | Bin 0 -> 632 bytes doc/kbabel/roughtranslation.png | Bin 0 -> 16106 bytes doc/kbabel/snap1.png | Bin 0 -> 37768 bytes doc/kbabel/snap_catalogmanager.png | Bin 0 -> 38898 bytes doc/kbabel/snap_kbabeldict.png | Bin 0 -> 19942 bytes doc/kbabel/snap_kbabeldict2.png | Bin 0 -> 26772 bytes doc/kbabel/stop.png | Bin 0 -> 821 bytes doc/kbabel/top.png | Bin 0 -> 657 bytes doc/kbabel/transsearch.png | Bin 0 -> 1357 bytes doc/kbabel/undo.png | Bin 0 -> 578 bytes doc/kbabel/using.docbook | 791 +++ doc/kbugbuster/Makefile.am | 4 + doc/kbugbuster/index.docbook | 78 + doc/kcachegrind/Makefile.am | 4 + doc/kcachegrind/index.docbook | 962 ++++ doc/kompare/Makefile.am | 4 + doc/kompare/index.docbook | 910 ++++ doc/kompare/settings-diff1.png | Bin 0 -> 19714 bytes doc/kompare/settings-diff2.png | Bin 0 -> 26853 bytes doc/kompare/settings-diff3.png | Bin 0 -> 37895 bytes doc/kompare/settings-diff4.png | Bin 0 -> 24142 bytes doc/kompare/settings-view1.png | Bin 0 -> 30402 bytes doc/kompare/settings-view2.png | Bin 0 -> 19989 bytes doc/scripts/Makefile.am | 3 + doc/scripts/kdesvn-build/Makefile.am | 2 + doc/scripts/kdesvn-build/index.docbook | 1324 +++++ doc/scripts/man-adddebug.1.docbook | 66 + doc/scripts/man-cheatmake.1.docbook | 103 + doc/scripts/man-create_cvsignore.1.docbook | 50 + doc/scripts/man-create_makefile.1.docbook | 94 + doc/scripts/man-create_makefiles.1.docbook | 92 + doc/scripts/man-cvscheck.1.docbook | 134 + doc/scripts/man-cvslastchange.1.docbook | 52 + doc/scripts/man-cvslastlog.1.docbook | 42 + doc/scripts/man-cvsrevertlast.1.docbook | 48 + doc/scripts/man-cxxmetric.1.docbook | 43 + doc/scripts/man-demangle.1.docbook | 63 + doc/scripts/man-extend_dmalloc.1.docbook | 54 + doc/scripts/man-extractrc.1.docbook | 46 + doc/scripts/man-fixincludes.1.docbook | 98 + doc/scripts/man-po2xml.1.docbook | 58 + doc/scripts/man-pruneemptydirs.1.docbook | 69 + doc/scripts/man-qtdoc.1.docbook | 75 + doc/scripts/man-reportview.1.docbook | 73 + doc/scripts/man-split2po.1.docbook | 64 + doc/scripts/man-swappo.1.docbook | 59 + doc/scripts/man-transxx.1.docbook | 55 + doc/scripts/man-xml2pot.1.docbook | 63 + doc/scripts/man-zonetab2pot.1.docbook | 56 + doc/umbrello/Makefile.am | 2 + doc/umbrello/activity-diagram.png | Bin 0 -> 62231 bytes doc/umbrello/add-remove-languages.png | Bin 0 -> 41025 bytes doc/umbrello/aggregation.png | Bin 0 -> 1502 bytes doc/umbrello/association.png | Bin 0 -> 2102 bytes doc/umbrello/authors.docbook | 41 + doc/umbrello/class-diagram.png | Bin 0 -> 44071 bytes doc/umbrello/class.png | Bin 0 -> 4323 bytes doc/umbrello/code-import.png | Bin 0 -> 28877 bytes doc/umbrello/code_import_and_generation.docbook | 170 + doc/umbrello/collaboration-diagram.png | Bin 0 -> 70040 bytes doc/umbrello/composition.png | Bin 0 -> 1971 bytes doc/umbrello/credits.docbook | 12 + doc/umbrello/folders.png | Bin 0 -> 57871 bytes doc/umbrello/generalization.png | Bin 0 -> 1805 bytes doc/umbrello/generation-options.png | Bin 0 -> 47137 bytes doc/umbrello/index.docbook | 69 + doc/umbrello/introduction.docbook | 43 + doc/umbrello/other_features.docbook | 61 + doc/umbrello/sequence-diagram.png | Bin 0 -> 49267 bytes doc/umbrello/state-diagram.png | Bin 0 -> 44030 bytes doc/umbrello/umbrello-main-screen.png | Bin 0 -> 29106 bytes doc/umbrello/umbrello-ui-clean.png | Bin 0 -> 28962 bytes doc/umbrello/umbrello-ui.png | Bin 0 -> 31697 bytes doc/umbrello/uml_basics.docbook | 616 +++ doc/umbrello/use-case-diagram.png | Bin 0 -> 44945 bytes doc/umbrello/working_with_umbrello.docbook | 448 ++ kapptemplate/ChangeLog | 200 + kapptemplate/Makefile.am | 19 + kapptemplate/Makefile.cvs | 12 + kapptemplate/README | 68 + kapptemplate/VERSION | 1 + kapptemplate/admin/Makefile.am | 12 + kapptemplate/appframework/AUTHORS | 3 + kapptemplate/appframework/COPYING | 2 + kapptemplate/appframework/ChangeLog | 4 + kapptemplate/appframework/INSTALL | 2 + kapptemplate/appframework/Makefile.am | 6 + kapptemplate/appframework/NEWS | 3 + kapptemplate/appframework/README | 6 + kapptemplate/appframework/VERSION | 3 + kapptemplate/appframework/app.lsm | 18 + kapptemplate/appframework/app.spec | 44 + kapptemplate/appframework/base-Makefile.am | 11 + kapptemplate/appframework/base-Makefile.cvs | 12 + kapptemplate/appframework/configure.in.in.in | 3 + kapptemplate/appframework/no-exe/COPYING | 339 ++ kapptemplate/appframework/no-exe/INSTALL | 181 + kapptemplate/appframework/no-exe/Makefile.am | 2 + kapptemplate/appframework/po-Makefile.am | 3 + kapptemplate/existing.module | 118 + kapptemplate/existing/Makefile.am | 2 + kapptemplate/existing/app-Makefile.am | 34 + kapptemplate/existing/app-desktop | 9 + kapptemplate/kapp/Makefile.am | 9 + kapptemplate/kapp/app-Makefile.am | 47 + kapptemplate/kapp/app-configure.in.in | 14 + kapptemplate/kapp/app-desktop | 11 + kapptemplate/kapp/app.cpp | 263 + kapptemplate/kapp/app.h | 90 + kapptemplate/kapp/app_client.cpp | 26 + kapptemplate/kapp/appiface.h | 17 + kapptemplate/kapp/apppref.cpp | 42 + kapptemplate/kapp/apppref.h | 37 + kapptemplate/kapp/appui.rc | 10 + kapptemplate/kapp/appview.cpp | 106 + kapptemplate/kapp/appview.h | 77 + kapptemplate/kapp/doc-Makefile.am | 8 + kapptemplate/kapp/doc-app-Makefile.am | 9 + kapptemplate/kapp/hi16-app-app.png | 2 + kapptemplate/kapp/hi32-app-app.png | 2 + kapptemplate/kapp/hi48-app-app.png | 2 + kapptemplate/kapp/index.docbook | 106 + kapptemplate/kapp/lo16-app-app.png | 2 + kapptemplate/kapp/lo32-app-app.png | 2 + kapptemplate/kapp/main.cpp | 58 + kapptemplate/kapp/no-exe/Makefile.am | 3 + kapptemplate/kapp/no-exe/hi16-app-app.png | Bin 0 -> 845 bytes kapptemplate/kapp/no-exe/hi32-app-app.png | Bin 0 -> 2595 bytes kapptemplate/kapp/no-exe/hi48-app-app.png | Bin 0 -> 4954 bytes kapptemplate/kapp/no-exe/lo16-app-app.png | Bin 0 -> 628 bytes kapptemplate/kapp/no-exe/lo32-app-app.png | Bin 0 -> 1335 bytes kapptemplate/kapp/pics-Makefile.am | 8 + kapptemplate/kapptemplate.common | 496 ++ kapptemplate/kapptemplate.in | 131 + kapptemplate/kapptemplate.lsm | 18 + kapptemplate/kapptemplate.module | 68 + kapptemplate/kpartapp.module | 70 + kapptemplate/kpartapp/Makefile.am | 10 + kapptemplate/kpartapp/app-Makefile.am | 57 + kapptemplate/kpartapp/app-configure.in.in | 14 + kapptemplate/kpartapp/app-desktop | 11 + kapptemplate/kpartapp/app.cpp | 176 + kapptemplate/kpartapp/app.h | 70 + kapptemplate/kpartapp/app_part-desktop | 9 + kapptemplate/kpartapp/app_part.cpp | 148 + kapptemplate/kpartapp/app_part.h | 69 + kapptemplate/kpartapp/app_part.rc | 17 + kapptemplate/kpartapp/app_shell.rc | 32 + kapptemplate/kpartapp/doc-Makefile.am | 8 + kapptemplate/kpartapp/doc-app-Makefile.am | 9 + kapptemplate/kpartapp/hi16-app-app.png | 2 + kapptemplate/kpartapp/hi32-app-app.png | 2 + kapptemplate/kpartapp/hi48-app-app.png | 2 + kapptemplate/kpartapp/index.docbook | 106 + kapptemplate/kpartapp/lo16-app-app.png | 2 + kapptemplate/kpartapp/lo32-app-app.png | 2 + kapptemplate/kpartapp/main.cpp | 55 + kapptemplate/kpartapp/no-exe/Makefile.am | 3 + kapptemplate/kpartapp/no-exe/hi16-app-app.png | Bin 0 -> 845 bytes kapptemplate/kpartapp/no-exe/hi32-app-app.png | Bin 0 -> 2595 bytes kapptemplate/kpartapp/no-exe/hi48-app-app.png | Bin 0 -> 4954 bytes kapptemplate/kpartapp/no-exe/lo16-app-app.png | Bin 0 -> 628 bytes kapptemplate/kpartapp/no-exe/lo32-app-app.png | Bin 0 -> 1335 bytes kapptemplate/kpartplugin.module | 69 + kapptemplate/kpartplugin/Makefile.am | 5 + kapptemplate/kpartplugin/hi16-action-plugin.png | 2 + kapptemplate/kpartplugin/hi22-action-plugin.png | 2 + kapptemplate/kpartplugin/no-exe/Makefile.am | 2 + .../kpartplugin/no-exe/hi16-action-plugin.png | Bin 0 -> 292 bytes .../kpartplugin/no-exe/hi22-action-plugin.png | Bin 0 -> 1151 bytes kapptemplate/kpartplugin/plugin-Makefile.am | 20 + kapptemplate/kpartplugin/plugin_app.cpp | 81 + kapptemplate/kpartplugin/plugin_app.h | 20 + kapptemplate/kpartplugin/plugin_app.rc | 13 + kapptemplate/mkinstalldirs | 40 + kbabel/AUTHORS | 7 + kbabel/COPYING | 340 ++ kbabel/ChangeLog | 287 ++ kbabel/Makefile.am | 8 + kbabel/README | 65 + kbabel/TODO | 22 + kbabel/VERSION | 1 + kbabel/addons/Makefile.am | 3 + kbabel/addons/kfile-plugins/Makefile.am | 22 + kbabel/addons/kfile-plugins/kfile_po.cpp | 81 + kbabel/addons/kfile-plugins/kfile_po.desktop | 61 + kbabel/addons/kfile-plugins/kfile_po.h | 49 + kbabel/addons/preview/Makefile.am | 15 + kbabel/addons/preview/pothumbcreator.cpp | 360 ++ kbabel/addons/preview/pothumbcreator.h | 54 + kbabel/addons/preview/pothumbnail.desktop | 68 + kbabel/catalogmanager/Makefile.am | 64 + kbabel/catalogmanager/catalogmanager.cpp | 1371 ++++++ kbabel/catalogmanager/catalogmanager.desktop | 98 + kbabel/catalogmanager/catalogmanager.h | 218 + kbabel/catalogmanager/catalogmanagerapp.h | 73 + kbabel/catalogmanager/catalogmanageriface.h | 67 + kbabel/catalogmanager/catalogmanagerui.rc | 262 + kbabel/catalogmanager/catalogmanagerview.cpp | 3132 ++++++++++++ kbabel/catalogmanager/catalogmanagerview.h | 474 ++ kbabel/catalogmanager/catmanlistitem.cpp | 932 ++++ kbabel/catalogmanager/catmanlistitem.h | 238 + kbabel/catalogmanager/catmanresource.h | 73 + kbabel/catalogmanager/findinfilesdialog.cpp | 229 + kbabel/catalogmanager/findinfilesdialog.h | 85 + kbabel/catalogmanager/future.cpp | 17 + kbabel/catalogmanager/hi16-app-catalogmanager.png | Bin 0 -> 865 bytes kbabel/catalogmanager/hi22-app-catalogmanager.png | Bin 0 -> 1396 bytes kbabel/catalogmanager/hi32-app-catalogmanager.png | Bin 0 -> 2446 bytes kbabel/catalogmanager/hi48-app-catalogmanager.png | Bin 0 -> 4498 bytes kbabel/catalogmanager/icons/Makefile.am | 5 + .../icons/hi16-action-nextmarked.png | Bin 0 -> 329 bytes kbabel/catalogmanager/icons/hi16-action-nextpo.png | Bin 0 -> 348 bytes .../icons/hi16-action-nexttemplate.png | Bin 0 -> 354 bytes .../icons/hi16-action-prevmarked.png | Bin 0 -> 341 bytes kbabel/catalogmanager/icons/hi16-action-prevpo.png | Bin 0 -> 356 bytes .../icons/hi16-action-prevtemplate.png | Bin 0 -> 363 bytes .../icons/hi16-action-statistics.png | Bin 0 -> 702 bytes kbabel/catalogmanager/icons/hi16-action-syntax.png | Bin 0 -> 623 bytes .../icons/hi22-action-nextmarked.png | Bin 0 -> 566 bytes kbabel/catalogmanager/icons/hi22-action-nextpo.png | Bin 0 -> 454 bytes .../icons/hi22-action-nexttemplate.png | Bin 0 -> 447 bytes .../icons/hi22-action-prevmarked.png | Bin 0 -> 560 bytes kbabel/catalogmanager/icons/hi22-action-prevpo.png | Bin 0 -> 440 bytes .../icons/hi22-action-prevtemplate.png | Bin 0 -> 436 bytes .../icons/hi22-action-statistics.png | Bin 0 -> 1180 bytes kbabel/catalogmanager/icons/hi22-action-syntax.png | Bin 0 -> 1378 bytes .../icons/hi32-action-nextmarked.png | Bin 0 -> 706 bytes kbabel/catalogmanager/icons/hi32-action-nextpo.png | Bin 0 -> 593 bytes .../icons/hi32-action-nexttemplate.png | Bin 0 -> 568 bytes .../icons/hi32-action-prevmarked.png | Bin 0 -> 726 bytes kbabel/catalogmanager/icons/hi32-action-prevpo.png | Bin 0 -> 624 bytes .../icons/hi32-action-prevtemplate.png | Bin 0 -> 627 bytes .../icons/hi32-action-statistics.png | Bin 0 -> 1456 bytes kbabel/catalogmanager/icons/hi32-action-syntax.png | Bin 0 -> 1332 bytes .../icons/lo16-action-nextmarked.png | Bin 0 -> 315 bytes kbabel/catalogmanager/icons/lo16-action-nextpo.png | Bin 0 -> 353 bytes .../icons/lo16-action-nexttemplate.png | Bin 0 -> 325 bytes .../icons/lo16-action-prevmarked.png | Bin 0 -> 319 bytes kbabel/catalogmanager/icons/lo16-action-prevpo.png | Bin 0 -> 362 bytes .../icons/lo16-action-prevtemplate.png | Bin 0 -> 340 bytes .../icons/lo16-action-statistics.png | Bin 0 -> 362 bytes kbabel/catalogmanager/icons/lo16-action-syntax.png | Bin 0 -> 311 bytes .../icons/lo22-action-statistics.png | Bin 0 -> 420 bytes kbabel/catalogmanager/icons/lo22-action-syntax.png | Bin 0 -> 402 bytes .../icons/lo32-action-nextmarked.png | Bin 0 -> 582 bytes kbabel/catalogmanager/icons/lo32-action-nextpo.png | Bin 0 -> 593 bytes .../icons/lo32-action-nexttemplate.png | Bin 0 -> 568 bytes .../icons/lo32-action-prevmarked.png | Bin 0 -> 502 bytes kbabel/catalogmanager/icons/lo32-action-prevpo.png | Bin 0 -> 497 bytes .../icons/lo32-action-prevtemplate.png | Bin 0 -> 484 bytes .../icons/lo32-action-statistics.png | Bin 0 -> 471 bytes kbabel/catalogmanager/icons/lo32-action-syntax.png | Bin 0 -> 497 bytes kbabel/catalogmanager/libcvs/Makefile.am | 11 + kbabel/catalogmanager/libcvs/cvsdialog.cpp | 423 ++ kbabel/catalogmanager/libcvs/cvsdialog.h | 158 + kbabel/catalogmanager/libcvs/cvshandler.cpp | 398 ++ kbabel/catalogmanager/libcvs/cvshandler.h | 114 + kbabel/catalogmanager/libcvs/cvsresources.h | 41 + kbabel/catalogmanager/libsvn/Makefile.am | 11 + kbabel/catalogmanager/libsvn/svndialog.cpp | 400 ++ kbabel/catalogmanager/libsvn/svndialog.h | 151 + kbabel/catalogmanager/libsvn/svnhandler.cpp | 544 +++ kbabel/catalogmanager/libsvn/svnhandler.h | 138 + kbabel/catalogmanager/libsvn/svnresources.h | 50 + kbabel/catalogmanager/lo16-app-catalogmanager.png | Bin 0 -> 323 bytes kbabel/catalogmanager/lo32-app-catalogmanager.png | Bin 0 -> 554 bytes kbabel/catalogmanager/main.cpp | 232 + kbabel/catalogmanager/markpatterndialog.cpp | 172 + kbabel/catalogmanager/markpatterndialog.h | 75 + kbabel/catalogmanager/markpatternwidget.ui | 127 + kbabel/catalogmanager/multiroughtransdlg.cpp | 148 + kbabel/catalogmanager/multiroughtransdlg.h | 63 + kbabel/catalogmanager/validateprogress.cpp | 313 ++ kbabel/catalogmanager/validateprogress.h | 109 + kbabel/catalogmanager/validateprogresswidget.ui | 129 + kbabel/catalogmanager/validateprogresswidget.ui.h | 48 + kbabel/catalogmanager/validationoptions.ui | 60 + kbabel/common/Makefile.am | 67 + kbabel/common/argextractor.cpp | 70 + kbabel/common/argextractor.h | 83 + kbabel/common/catalog.cpp | 3509 ++++++++++++++ kbabel/common/catalog.h | 698 +++ kbabel/common/catalog_private.h | 147 + kbabel/common/catalogfileplugin.h | 205 + kbabel/common/catalogitem.cpp | 521 ++ kbabel/common/catalogitem.h | 220 + kbabel/common/catalogitem_private.h | 90 + kbabel/common/catalogsettings.cpp | 258 + kbabel/common/catalogsettings.h | 176 + kbabel/common/catalogview.h | 64 + kbabel/common/diff.cpp | 465 ++ kbabel/common/diff.h | 88 + kbabel/common/editcmd.cpp | 106 + kbabel/common/editcmd.h | 112 + kbabel/common/exportplugin.cpp | 81 + kbabel/common/findoptions.h | 86 + kbabel/common/importplugin.cpp | 194 + kbabel/common/importplugin_private.h | 69 + kbabel/common/itempart.h | 37 + kbabel/common/kbabel-projectrename.upd | 13 + kbabel/common/kbabeldatatool.h | 63 + kbabel/common/kbabelfilter.desktop | 60 + kbabel/common/kbmailer.cpp | 244 + kbabel/common/kbmailer.h | 166 + kbabel/common/kbproject.cpp | 477 ++ kbabel/common/kbproject.h | 109 + kbabel/common/kbprojectmanager.cpp | 101 + kbabel/common/kbprojectmanager.h | 62 + kbabel/common/kbprojectsettings.kcfg | 354 ++ kbabel/common/kbprojectsettings.kcfgc | 7 + kbabel/common/libgettext/Makefile.am | 15 + kbabel/common/libgettext/pofiles.h | 52 + kbabel/common/libgettext/pofiles.ll | 107 + kbabel/common/libgettext/tokens.h | 45 + kbabel/common/msgfmt.cpp | 147 + kbabel/common/msgfmt.h | 65 + kbabel/common/pluralforms.h | 38 + kbabel/common/poinfo.cpp | 781 +++ kbabel/common/poinfo.h | 157 + kbabel/common/projectsettings.cpp | 126 + kbabel/common/projectsettings.h | 142 + kbabel/common/regexpextractor.cpp | 271 ++ kbabel/common/regexpextractor.h | 158 + kbabel/common/resources.h | 43 + kbabel/common/stringdistance.cpp | 182 + kbabel/common/stringdistance.h | 131 + kbabel/common/tagextractor.cpp | 54 + kbabel/common/tagextractor.h | 58 + kbabel/commonui/Makefile.am | 40 + kbabel/commonui/cmdedit.cpp | 298 ++ kbabel/commonui/cmdedit.h | 94 + kbabel/commonui/context.cpp | 305 ++ kbabel/commonui/context.h | 123 + kbabel/commonui/diffpreferences.ui | 141 + kbabel/commonui/diffpreferences.ui.h | 13 + kbabel/commonui/finddialog.cpp | 553 +++ kbabel/commonui/finddialog.h | 136 + kbabel/commonui/kactionselector.cpp | 562 +++ kbabel/commonui/kactionselector.h | 410 ++ kbabel/commonui/kbabel_tool.desktop | 9 + kbabel/commonui/kbabel_validator.desktop | 56 + kbabel/commonui/klisteditor.ui | 261 + kbabel/commonui/klisteditor.ui.h | 122 + kbabel/commonui/projectpref.cpp | 278 ++ kbabel/commonui/projectpref.h | 93 + kbabel/commonui/projectprefwidgets.cpp | 1209 +++++ kbabel/commonui/projectprefwidgets.h | 285 ++ kbabel/commonui/projectwizard.cpp | 172 + kbabel/commonui/projectwizard.h | 74 + kbabel/commonui/projectwizardwidget.ui | 266 + kbabel/commonui/projectwizardwidget.ui.h | 40 + kbabel/commonui/projectwizardwidget2.ui | 157 + kbabel/commonui/roughtransdlg.cpp | 762 +++ kbabel/commonui/roughtransdlg.h | 110 + kbabel/commonui/toolaction.cpp | 108 + kbabel/commonui/toolaction.h | 72 + kbabel/commonui/toolselectionwidget.cpp | 105 + kbabel/commonui/toolselectionwidget.h | 57 + kbabel/configure.in.in | 12 + kbabel/datatools/Makefile.am | 39 + kbabel/datatools/accelerators/Makefile.am | 19 + .../accelerators/kbabel_accelstool.desktop | 105 + kbabel/datatools/accelerators/main.cc | 137 + kbabel/datatools/accelerators/main.h | 56 + kbabel/datatools/arguments/Makefile.am | 19 + kbabel/datatools/arguments/kbabel_argstool.desktop | 106 + kbabel/datatools/arguments/main.cc | 277 ++ kbabel/datatools/arguments/main.h | 55 + kbabel/datatools/context/Makefile.am | 19 + .../datatools/context/kbabel_contexttool.desktop | 100 + kbabel/datatools/context/main.cc | 115 + kbabel/datatools/context/main.h | 52 + kbabel/datatools/equations/Makefile.am | 19 + .../equations/kbabel_equationstool.desktop | 104 + kbabel/datatools/equations/main.cc | 113 + kbabel/datatools/equations/main.h | 50 + kbabel/datatools/length/Makefile.am | 19 + kbabel/datatools/length/kbabel_lengthtool.desktop | 98 + kbabel/datatools/length/main.cc | 142 + kbabel/datatools/length/main.h | 54 + kbabel/datatools/length/test.po | 46 + kbabel/datatools/not-translated/Makefile.am | 19 + .../kbabel_nottranslatedtool.desktop | 97 + kbabel/datatools/not-translated/main.cc | 129 + kbabel/datatools/not-translated/main.h | 55 + kbabel/datatools/not-translated/test.po | 48 + kbabel/datatools/pluralforms/Makefile.am | 19 + .../pluralforms/kbabel_pluralformstool.desktop | 101 + kbabel/datatools/pluralforms/main.cc | 129 + kbabel/datatools/pluralforms/main.h | 53 + kbabel/datatools/punctuation/Makefile.am | 19 + .../punctuation/kbabel_punctuationtool.desktop | 101 + kbabel/datatools/punctuation/main.cc | 157 + kbabel/datatools/punctuation/main.h | 47 + kbabel/datatools/regexp/Makefile.am | 21 + kbabel/datatools/regexp/kbabel_regexptool.desktop | 92 + kbabel/datatools/regexp/main.cc | 181 + kbabel/datatools/regexp/main.h | 73 + kbabel/datatools/regexp/regexplist.xml | 303 ++ kbabel/datatools/setfuzzy/Makefile.am | 19 + .../datatools/setfuzzy/kbabel_setfuzzytool.desktop | 98 + kbabel/datatools/setfuzzy/main.cc | 98 + kbabel/datatools/setfuzzy/main.h | 47 + kbabel/datatools/whitespace/Makefile.am | 19 + .../whitespace/kbabel_whitespacetool.desktop | 98 + kbabel/datatools/whitespace/main.cc | 144 + kbabel/datatools/whitespace/main.h | 55 + kbabel/datatools/whitespace/test.po | 92 + kbabel/datatools/xml/Makefile.am | 19 + kbabel/datatools/xml/kbabel_xmltool.desktop | 105 + kbabel/datatools/xml/main.cc | 206 + kbabel/datatools/xml/main.h | 59 + kbabel/filters/Makefile.am | 3 + kbabel/filters/gettext/Makefile.am | 20 + kbabel/filters/gettext/gettextexport.cpp | 352 ++ kbabel/filters/gettext/gettextexport.h | 88 + kbabel/filters/gettext/gettextimport.cpp | 821 ++++ kbabel/filters/gettext/gettextimport.h | 70 + .../filters/gettext/kbabel_gettext_export.desktop | 53 + .../filters/gettext/kbabel_gettext_import.desktop | 53 + kbabel/filters/linguist/Makefile.am | 18 + .../linguist/kbabel_linguist_export.desktop | 53 + .../linguist/kbabel_linguist_import.desktop | 53 + kbabel/filters/linguist/linguistexport.cpp | 214 + kbabel/filters/linguist/linguistexport.h | 68 + kbabel/filters/linguist/linguistimport.cpp | 193 + kbabel/filters/linguist/linguistimport.h | 68 + kbabel/filters/xliff/Makefile.am | 18 + kbabel/filters/xliff/kbabel_xliff_export.desktop | 47 + kbabel/filters/xliff/kbabel_xliff_import.desktop | 47 + kbabel/filters/xliff/xliffexport.cpp | 223 + kbabel/filters/xliff/xliffexport.h | 66 + kbabel/filters/xliff/xliffimport.cpp | 180 + kbabel/filters/xliff/xliffimport.h | 68 + kbabel/kbabel/Makefile.am | 92 + kbabel/kbabel/charselectview.cpp | 115 + kbabel/kbabel/charselectview.h | 71 + kbabel/kbabel/colorpreferences.ui | 188 + kbabel/kbabel/commentview.cpp | 221 + kbabel/kbabel/commentview.h | 96 + kbabel/kbabel/contextview.cpp | 158 + kbabel/kbabel/contextview.h | 56 + kbabel/kbabel/editordiffpreferences.ui | 192 + kbabel/kbabel/editorpreferences.ui | 353 ++ kbabel/kbabel/editorpreferences.ui.h | 23 + kbabel/kbabel/errorlistview.cpp | 76 + kbabel/kbabel/errorlistview.h | 55 + kbabel/kbabel/fontpreferences.ui | 63 + kbabel/kbabel/fontpreferences.ui.h | 14 + kbabel/kbabel/gotodialog.cpp | 74 + kbabel/kbabel/gotodialog.h | 63 + kbabel/kbabel/headereditor.cpp | 214 + kbabel/kbabel/headereditor.h | 85 + kbabel/kbabel/headerwidget.ui | 64 + kbabel/kbabel/hi16-app-kbabel.png | Bin 0 -> 952 bytes kbabel/kbabel/hi32-app-kbabel.png | Bin 0 -> 2561 bytes kbabel/kbabel/hi48-app-kbabel.png | Bin 0 -> 4753 bytes kbabel/kbabel/hidingmsgedit.cpp | 426 ++ kbabel/kbabel/hidingmsgedit.h | 161 + kbabel/kbabel/icons/Makefile.am | 5 + kbabel/kbabel/icons/hi16-action-autodiff.png | Bin 0 -> 242 bytes kbabel/kbabel/icons/hi16-action-catalogmanager.png | Bin 0 -> 791 bytes kbabel/kbabel/icons/hi16-action-diff.png | Bin 0 -> 203 bytes kbabel/kbabel/icons/hi16-action-insert_arg.png | Bin 0 -> 303 bytes kbabel/kbabel/icons/hi16-action-insert_tag.png | Bin 0 -> 329 bytes kbabel/kbabel/icons/hi16-action-msgid2msgstr.png | Bin 0 -> 298 bytes kbabel/kbabel/icons/hi16-action-nexterror.png | Bin 0 -> 416 bytes kbabel/kbabel/icons/hi16-action-nextfuzzy.png | Bin 0 -> 299 bytes .../kbabel/icons/hi16-action-nextfuzzyuntrans.png | Bin 0 -> 422 bytes .../kbabel/icons/hi16-action-nextuntranslated.png | Bin 0 -> 323 bytes kbabel/kbabel/icons/hi16-action-preverror.png | Bin 0 -> 443 bytes kbabel/kbabel/icons/hi16-action-prevfuzzy.png | Bin 0 -> 346 bytes .../kbabel/icons/hi16-action-prevfuzzyuntrans.png | Bin 0 -> 469 bytes .../kbabel/icons/hi16-action-prevuntranslated.png | Bin 0 -> 350 bytes kbabel/kbabel/icons/hi16-action-search2msgstr.png | Bin 0 -> 374 bytes .../kbabel/icons/hi16-action-spellcheck_actual.png | Bin 0 -> 223 bytes kbabel/kbabel/icons/hi16-action-spellcheck_all.png | Bin 0 -> 204 bytes .../icons/hi16-action-spellcheck_from_cursor.png | Bin 0 -> 216 bytes .../icons/hi16-action-spellcheck_selected.png | Bin 0 -> 214 bytes kbabel/kbabel/icons/hi16-action-togglefuzzy.png | Bin 0 -> 316 bytes kbabel/kbabel/icons/hi16-action-transsearch.png | Bin 0 -> 694 bytes kbabel/kbabel/icons/hi22-action-autodiff.png | Bin 0 -> 314 bytes kbabel/kbabel/icons/hi22-action-catalogmanager.png | Bin 0 -> 1540 bytes kbabel/kbabel/icons/hi22-action-diff.png | Bin 0 -> 227 bytes kbabel/kbabel/icons/hi22-action-insert_arg.png | Bin 0 -> 316 bytes kbabel/kbabel/icons/hi22-action-insert_tag.png | Bin 0 -> 332 bytes kbabel/kbabel/icons/hi22-action-msgid2msgstr.png | Bin 0 -> 277 bytes kbabel/kbabel/icons/hi22-action-nexterror.png | Bin 0 -> 530 bytes kbabel/kbabel/icons/hi22-action-nextfuzzy.png | Bin 0 -> 471 bytes .../kbabel/icons/hi22-action-nextfuzzyuntrans.png | Bin 0 -> 561 bytes .../kbabel/icons/hi22-action-nextuntranslated.png | Bin 0 -> 459 bytes kbabel/kbabel/icons/hi22-action-preverror.png | Bin 0 -> 465 bytes kbabel/kbabel/icons/hi22-action-prevfuzzy.png | Bin 0 -> 413 bytes .../kbabel/icons/hi22-action-prevfuzzyuntrans.png | Bin 0 -> 490 bytes .../kbabel/icons/hi22-action-prevuntranslated.png | Bin 0 -> 399 bytes kbabel/kbabel/icons/hi22-action-search2msgstr.png | Bin 0 -> 338 bytes kbabel/kbabel/icons/hi22-action-togglefuzzy.png | Bin 0 -> 386 bytes kbabel/kbabel/icons/hi22-action-transsearch.png | Bin 0 -> 1357 bytes kbabel/kbabel/icons/hi32-action-autodiff.png | Bin 0 -> 571 bytes kbabel/kbabel/icons/hi32-action-catalogmanager.png | Bin 0 -> 2201 bytes kbabel/kbabel/icons/hi32-action-diff.png | Bin 0 -> 296 bytes kbabel/kbabel/icons/hi32-action-insert_arg.png | Bin 0 -> 537 bytes kbabel/kbabel/icons/hi32-action-insert_tag.png | Bin 0 -> 602 bytes kbabel/kbabel/icons/hi32-action-msgid2msgstr.png | Bin 0 -> 531 bytes kbabel/kbabel/icons/hi32-action-nexterror.png | Bin 0 -> 836 bytes kbabel/kbabel/icons/hi32-action-nextfuzzy.png | Bin 0 -> 597 bytes .../kbabel/icons/hi32-action-nextfuzzyuntrans.png | Bin 0 -> 720 bytes .../kbabel/icons/hi32-action-nextuntranslated.png | Bin 0 -> 583 bytes kbabel/kbabel/icons/hi32-action-preverror.png | Bin 0 -> 1334 bytes kbabel/kbabel/icons/hi32-action-prevfuzzy.png | Bin 0 -> 1012 bytes .../kbabel/icons/hi32-action-prevfuzzyuntrans.png | Bin 0 -> 1140 bytes .../kbabel/icons/hi32-action-prevuntranslated.png | Bin 0 -> 1000 bytes kbabel/kbabel/icons/hi32-action-search2msgstr.png | Bin 0 -> 756 bytes kbabel/kbabel/icons/hi32-action-togglefuzzy.png | Bin 0 -> 854 bytes kbabel/kbabel/icons/hi32-action-transsearch.png | Bin 0 -> 1807 bytes kbabel/kbabel/icons/lo16-action-autodiff.png | Bin 0 -> 361 bytes kbabel/kbabel/icons/lo16-action-catalogmanager.png | Bin 0 -> 588 bytes kbabel/kbabel/icons/lo16-action-diff.png | Bin 0 -> 349 bytes kbabel/kbabel/icons/lo16-action-insert_arg.png | Bin 0 -> 341 bytes kbabel/kbabel/icons/lo16-action-insert_tag.png | Bin 0 -> 358 bytes kbabel/kbabel/icons/lo16-action-msgid2msgstr.png | Bin 0 -> 342 bytes kbabel/kbabel/icons/lo16-action-nexterror.png | Bin 0 -> 390 bytes kbabel/kbabel/icons/lo16-action-nextfuzzy.png | Bin 0 -> 363 bytes .../kbabel/icons/lo16-action-nextfuzzyuntrans.png | Bin 0 -> 373 bytes .../kbabel/icons/lo16-action-nextuntranslated.png | Bin 0 -> 360 bytes kbabel/kbabel/icons/lo16-action-preverror.png | Bin 0 -> 393 bytes kbabel/kbabel/icons/lo16-action-prevfuzzy.png | Bin 0 -> 366 bytes .../kbabel/icons/lo16-action-prevfuzzyuntrans.png | Bin 0 -> 377 bytes .../kbabel/icons/lo16-action-prevuntranslated.png | Bin 0 -> 363 bytes kbabel/kbabel/icons/lo16-action-search2msgstr.png | Bin 0 -> 370 bytes .../kbabel/icons/lo16-action-spellcheck_actual.png | Bin 0 -> 223 bytes kbabel/kbabel/icons/lo16-action-spellcheck_all.png | Bin 0 -> 204 bytes .../icons/lo16-action-spellcheck_from_cursor.png | Bin 0 -> 216 bytes .../icons/lo16-action-spellcheck_selected.png | Bin 0 -> 214 bytes kbabel/kbabel/icons/lo16-action-togglefuzzy.png | Bin 0 -> 316 bytes kbabel/kbabel/icons/lo16-action-transsearch.png | Bin 0 -> 435 bytes kbabel/kbabel/icons/lo32-action-autodiff.png | Bin 0 -> 478 bytes kbabel/kbabel/icons/lo32-action-catalogmanager.png | Bin 0 -> 828 bytes kbabel/kbabel/icons/lo32-action-diff.png | Bin 0 -> 390 bytes kbabel/kbabel/icons/lo32-action-insert_arg.png | Bin 0 -> 409 bytes kbabel/kbabel/icons/lo32-action-insert_tag.png | Bin 0 -> 431 bytes kbabel/kbabel/icons/lo32-action-msgid2msgstr.png | Bin 0 -> 403 bytes kbabel/kbabel/icons/lo32-action-nexterror.png | Bin 0 -> 539 bytes kbabel/kbabel/icons/lo32-action-nextfuzzy.png | Bin 0 -> 430 bytes .../kbabel/icons/lo32-action-nextfuzzyuntrans.png | Bin 0 -> 472 bytes .../kbabel/icons/lo32-action-nextuntranslated.png | Bin 0 -> 429 bytes kbabel/kbabel/icons/lo32-action-preverror.png | Bin 0 -> 547 bytes kbabel/kbabel/icons/lo32-action-prevfuzzy.png | Bin 0 -> 428 bytes .../kbabel/icons/lo32-action-prevfuzzyuntrans.png | Bin 0 -> 474 bytes .../kbabel/icons/lo32-action-prevuntranslated.png | Bin 0 -> 427 bytes kbabel/kbabel/icons/lo32-action-search2msgstr.png | Bin 0 -> 478 bytes kbabel/kbabel/icons/lo32-action-togglefuzzy.png | Bin 0 -> 854 bytes kbabel/kbabel/icons/lo32-action-transsearch.png | Bin 0 -> 1267 bytes kbabel/kbabel/kbabel-difftoproject.upd | 6 + kbabel/kbabel/kbabel-project.upd | 11 + kbabel/kbabel/kbabel.cpp | 1825 +++++++ kbabel/kbabel/kbabel.desktop | 83 + kbabel/kbabel/kbabel.h | 325 ++ kbabel/kbabel/kbabel.kcfg | 304 ++ kbabel/kbabel/kbabeliface.h | 81 + kbabel/kbabel/kbabelpref.cpp | 198 + kbabel/kbabel/kbabelpref.h | 69 + kbabel/kbabel/kbabelsettings.kcfgc | 5 + kbabel/kbabel/kbabelsplash.cpp | 73 + kbabel/kbabel/kbabelsplash.h | 55 + kbabel/kbabel/kbabelui.rc | 235 + kbabel/kbabel/kbabelview.cpp | 4473 +++++++++++++++++ kbabel/kbabel/kbabelview.h | 712 +++ kbabel/kbabel/kbabelview2.cpp | 1025 ++++ kbabel/kbabel/kbbookmarkhandler.cpp | 131 + kbabel/kbabel/kbbookmarkhandler.h | 155 + kbabel/kbabel/kbcatalog.cpp | 63 + kbabel/kbabel/kbcatalog.h | 61 + kbabel/kbabel/kbcataloglistview.cpp | 136 + kbabel/kbabel/kbcataloglistview.h | 82 + kbabel/kbabel/kbcataloglistviewitem.cpp | 213 + kbabel/kbabel/kbcataloglistviewitem.h | 72 + kbabel/kbabel/kbcatalogview.cpp | 137 + kbabel/kbabel/kbcatalogview.h | 102 + kbabel/kbabel/kbcharselect.cpp | 94 + kbabel/kbabel/kbcharselect.h | 65 + kbabel/kbabel/kbhighlighting.cpp | 316 ++ kbabel/kbabel/kbhighlighting.h | 104 + kbabel/kbabel/lo16-app-kbabel.png | Bin 0 -> 352 bytes kbabel/kbabel/lo32-app-kbabel.png | Bin 0 -> 546 bytes kbabel/kbabel/main.cpp | 612 +++ kbabel/kbabel/mymultilineedit.cpp | 1668 +++++++ kbabel/kbabel/mymultilineedit.h | 307 ++ kbabel/kbabel/pics/Makefile.am | 6 + kbabel/kbabel/pics/broken.png | Bin 0 -> 188 bytes kbabel/kbabel/pics/missing.png | Bin 0 -> 215 bytes kbabel/kbabel/pics/needwork.png | Bin 0 -> 189 bytes kbabel/kbabel/pics/noflag.png | Bin 0 -> 159 bytes kbabel/kbabel/pics/ok.png | Bin 0 -> 219 bytes kbabel/kbabel/pics/pref_identity.png | Bin 0 -> 1369 bytes kbabel/kbabel/pics/splash.png | Bin 0 -> 24019 bytes kbabel/kbabel/searchpreferences.ui | 109 + kbabel/kbabel/sourceview.cpp | 75 + kbabel/kbabel/sourceview.h | 57 + kbabel/kbabel/spelldlg.cpp | 142 + kbabel/kbabel/spelldlg.h | 63 + kbabel/kbabel/spelldlgwidget.ui | 126 + kbabel/kbabel/taglistview.cpp | 78 + kbabel/kbabel/taglistview.h | 61 + kbabel/kbabeldict/Makefile.am | 66 + kbabel/kbabeldict/README | 5 + kbabel/kbabeldict/README.modules | 34 + kbabel/kbabeldict/aboutmoduledlg.cpp | 54 + kbabel/kbabeldict/aboutmoduledlg.h | 55 + kbabel/kbabeldict/dictchooser.cpp | 335 ++ kbabel/kbabeldict/dictchooser.h | 94 + kbabel/kbabeldict/dictionarymenu.cpp | 154 + kbabel/kbabeldict/dictionarymenu.h | 81 + kbabel/kbabeldict/hi16-app-kbabeldict.png | Bin 0 -> 829 bytes kbabel/kbabeldict/hi32-app-kbabeldict.png | Bin 0 -> 2278 bytes kbabel/kbabeldict/hi48-app-kbabeldict.png | Bin 0 -> 4127 bytes kbabel/kbabeldict/kbabeldict.cpp | 113 + kbabel/kbabeldict/kbabeldict.desktop | 96 + kbabel/kbabeldict/kbabeldict.h | 62 + kbabel/kbabeldict/kbabeldict_module.desktop | 54 + kbabel/kbabeldict/kbabeldictbox.cpp | 1767 +++++++ kbabel/kbabeldict/kbabeldictbox.h | 282 ++ kbabel/kbabeldict/kbabeldictiface.h | 56 + kbabel/kbabeldict/kbabeldictview.cpp | 294 ++ kbabel/kbabeldict/kbabeldictview.h | 92 + kbabel/kbabeldict/kbabelsplash.cpp | 73 + kbabel/kbabeldict/kbabelsplash.h | 55 + kbabel/kbabeldict/lo16-app-kbabeldict.png | Bin 0 -> 293 bytes kbabel/kbabeldict/lo32-app-kbabeldict.png | Bin 0 -> 497 bytes kbabel/kbabeldict/main.cpp | 136 + kbabel/kbabeldict/modules/Makefile.am | 7 + kbabel/kbabeldict/modules/dbsearchengine/AUTHOR | 1 + .../modules/dbsearchengine/KDBSearchEngine.cpp | 1899 ++++++++ .../modules/dbsearchengine/KDBSearchEngine.h | 333 ++ .../kbabeldict/modules/dbsearchengine/Makefile.am | 34 + kbabel/kbabeldict/modules/dbsearchengine/STRUCTURE | 25 + kbabel/kbabeldict/modules/dbsearchengine/TODO | 27 + .../modules/dbsearchengine/configure.in.bot | 5 + .../modules/dbsearchengine/configure.in.in | 143 + .../kbabeldict/modules/dbsearchengine/database.cpp | 1533 ++++++ .../kbabeldict/modules/dbsearchengine/database.h | 329 ++ .../kbabeldict/modules/dbsearchengine/dbscan.cpp | 197 + kbabel/kbabeldict/modules/dbsearchengine/dbscan.h | 86 + .../modules/dbsearchengine/dbse_factory.cpp | 82 + .../modules/dbsearchengine/dbse_factory.h | 26 + .../modules/dbsearchengine/dbsearchengine.desktop | 52 + .../modules/dbsearchengine/dbseprefwidget.ui | 1036 ++++ .../kbabeldict/modules/dbsearchengine/makemsgdb.C | 327 ++ .../modules/dbsearchengine/preferenceswidget.cpp | 111 + .../modules/dbsearchengine/preferenceswidget.h | 28 + kbabel/kbabeldict/modules/dbsearchengine2/AUTHOR | 1 + .../modules/dbsearchengine2/KDBSearchEngine2.cpp | 686 +++ .../modules/dbsearchengine2/KDBSearchEngine2.h | 202 + .../kbabeldict/modules/dbsearchengine2/Makefile.am | 34 + kbabel/kbabeldict/modules/dbsearchengine2/README | 21 + .../modules/dbsearchengine2/algorithms.cpp | 425 ++ .../modules/dbsearchengine2/algorithms.h | 157 + .../kbabeldict/modules/dbsearchengine2/chunk.cpp | 203 + kbabel/kbabeldict/modules/dbsearchengine2/chunk.h | 151 + .../modules/dbsearchengine2/database.cpp | 752 +++ .../kbabeldict/modules/dbsearchengine2/database.h | 237 + .../modules/dbsearchengine2/dbentries.cpp | 171 + .../kbabeldict/modules/dbsearchengine2/dbentries.h | 170 + .../kbabeldict/modules/dbsearchengine2/dbscan.cpp | 280 ++ kbabel/kbabeldict/modules/dbsearchengine2/dbscan.h | 120 + kbabel/kbabeldict/modules/dbsearchengine2/dbse2.ui | 732 +++ .../modules/dbsearchengine2/dbse2_factory.cpp | 83 + .../modules/dbsearchengine2/dbse2_factory.h | 26 + .../dbsearchengine2/dbsearchengine2.desktop | 52 + .../modules/dbsearchengine2/dbseprefwidget.ui | 1039 ++++ .../modules/dbsearchengine2/preferenceswidget.cpp | 98 + .../modules/dbsearchengine2/preferenceswidget.h | 26 + .../modules/dbsearchengine2/sourcedialog.ui | 266 + kbabel/kbabeldict/modules/poauxiliary/Makefile.am | 36 + .../kbabeldict/modules/poauxiliary/pa_factory.cpp | 110 + kbabel/kbabeldict/modules/poauxiliary/pa_factory.h | 60 + .../kbabeldict/modules/poauxiliary/poauxiliary.cpp | 554 +++ .../modules/poauxiliary/poauxiliary.desktop | 51 + .../kbabeldict/modules/poauxiliary/poauxiliary.h | 136 + .../modules/poauxiliary/preferenceswidget.cpp | 115 + .../modules/poauxiliary/preferenceswidget.h | 77 + kbabel/kbabeldict/modules/poauxiliary/pwidget.ui | 133 + kbabel/kbabeldict/modules/pocompendium/Makefile.am | 39 + .../modules/pocompendium/compendiumdata.cpp | 261 + .../modules/pocompendium/compendiumdata.h | 105 + .../kbabeldict/modules/pocompendium/pc_factory.cpp | 110 + .../kbabeldict/modules/pocompendium/pc_factory.h | 60 + .../modules/pocompendium/pocompendium.cpp | 1246 +++++ .../modules/pocompendium/pocompendium.desktop | 50 + .../kbabeldict/modules/pocompendium/pocompendium.h | 147 + .../modules/pocompendium/preferenceswidget.cpp | 352 ++ .../modules/pocompendium/preferenceswidget.h | 97 + kbabel/kbabeldict/modules/pocompendium/pwidget.ui | 280 ++ kbabel/kbabeldict/modules/tmx/Makefile.am | 34 + kbabel/kbabeldict/modules/tmx/pc_factory.cpp | 111 + kbabel/kbabeldict/modules/tmx/pc_factory.h | 62 + .../kbabeldict/modules/tmx/preferenceswidget.cpp | 334 ++ kbabel/kbabeldict/modules/tmx/preferenceswidget.h | 97 + kbabel/kbabeldict/modules/tmx/pwidget.ui | 216 + kbabel/kbabeldict/modules/tmx/tmxcompendium.cpp | 1007 ++++ .../kbabeldict/modules/tmx/tmxcompendium.desktop | 50 + kbabel/kbabeldict/modules/tmx/tmxcompendium.h | 137 + .../kbabeldict/modules/tmx/tmxcompendiumdata.cpp | 308 ++ kbabel/kbabeldict/modules/tmx/tmxcompendiumdata.h | 106 + kbabel/kbabeldict/searchengine.cpp | 283 ++ kbabel/kbabeldict/searchengine.h | 534 +++ kbugbuster/AUTHORS | 4 + kbugbuster/COPYING | 340 ++ kbugbuster/ChangeLog | 4 + kbugbuster/INSTALL | 167 + kbugbuster/Makefile.am | 28 + kbugbuster/README | 2 + kbugbuster/TODO | 4 + kbugbuster/backend/Makefile.am | 16 + kbugbuster/backend/bug.cpp | 240 + kbugbuster/backend/bug.h | 92 + kbugbuster/backend/bugcache.cpp | 289 ++ kbugbuster/backend/bugcache.h | 47 + kbugbuster/backend/bugcommand.cpp | 317 ++ kbugbuster/backend/bugcommand.h | 217 + kbugbuster/backend/bugdetails.cpp | 268 ++ kbugbuster/backend/bugdetails.h | 55 + kbugbuster/backend/bugdetailsimpl.h | 40 + kbugbuster/backend/bugdetailsjob.cpp | 50 + kbugbuster/backend/bugdetailsjob.h | 32 + kbugbuster/backend/bugdetailspart.h | 21 + kbugbuster/backend/bugimpl.h | 36 + kbugbuster/backend/bugjob.cpp | 97 + kbugbuster/backend/bugjob.h | 45 + kbugbuster/backend/buglistjob.cpp | 75 + kbugbuster/backend/buglistjob.h | 58 + kbugbuster/backend/bugmybugsjob.cpp | 78 + kbugbuster/backend/bugmybugsjob.h | 52 + kbugbuster/backend/bugserver.cpp | 412 ++ kbugbuster/backend/bugserver.h | 152 + kbugbuster/backend/bugserverconfig.cpp | 150 + kbugbuster/backend/bugserverconfig.h | 91 + kbugbuster/backend/bugsystem.cpp | 436 ++ kbugbuster/backend/bugsystem.h | 146 + kbugbuster/backend/domprocessor.cpp | 407 ++ kbugbuster/backend/domprocessor.h | 69 + kbugbuster/backend/error.h | 43 + kbugbuster/backend/htmlparser.cpp | 294 ++ kbugbuster/backend/htmlparser.h | 116 + kbugbuster/backend/kbbprefs.cpp | 170 + kbugbuster/backend/kbbprefs.h | 82 + kbugbuster/backend/mailsender.cpp | 212 + kbugbuster/backend/mailsender.h | 50 + kbugbuster/backend/package.cpp | 82 + kbugbuster/backend/package.h | 43 + kbugbuster/backend/packageimpl.h | 31 + kbugbuster/backend/packagelistjob.cpp | 68 + kbugbuster/backend/packagelistjob.h | 55 + kbugbuster/backend/person.cpp | 74 + kbugbuster/backend/person.h | 26 + kbugbuster/backend/processor.cpp | 78 + kbugbuster/backend/processor.h | 63 + kbugbuster/backend/rdfprocessor.cpp | 107 + kbugbuster/backend/rdfprocessor.h | 43 + kbugbuster/backend/smtp.cpp | 181 + kbugbuster/backend/smtp.h | 67 + kbugbuster/configure.in.in | 8 + kbugbuster/gui/Makefile.am | 24 + kbugbuster/gui/README | 21 + kbugbuster/gui/buglvi.cpp | 109 + kbugbuster/gui/buglvi.h | 57 + kbugbuster/gui/centralwidget.cpp | 507 ++ kbugbuster/gui/centralwidget.h | 142 + kbugbuster/gui/centralwidget_base.ui | 236 + kbugbuster/gui/cwbugdetails.cpp | 212 + kbugbuster/gui/cwbugdetails.h | 65 + kbugbuster/gui/cwbugdetailscontainer.cpp | 269 ++ kbugbuster/gui/cwbugdetailscontainer.h | 88 + kbugbuster/gui/cwbugdetailscontainer_base.ui | 244 + kbugbuster/gui/cwbuglistcontainer.cpp | 328 ++ kbugbuster/gui/cwbuglistcontainer.h | 99 + kbugbuster/gui/cwloadingwidget.cpp | 256 + kbugbuster/gui/cwloadingwidget.h | 87 + kbugbuster/gui/cwsearchwidget.cpp | 69 + kbugbuster/gui/cwsearchwidget.h | 46 + kbugbuster/gui/cwsearchwidget_base.ui | 198 + kbugbuster/gui/kbbbookmarkmanager.h | 23 + kbugbuster/gui/kbbmainwindow.cpp | 504 ++ kbugbuster/gui/kbbmainwindow.h | 144 + kbugbuster/gui/kbugbusterui.rc | 78 + kbugbuster/gui/loadallbugsdlg.cpp | 92 + kbugbuster/gui/loadallbugsdlg.h | 43 + kbugbuster/gui/messageeditor.cpp | 117 + kbugbuster/gui/messageeditor.h | 33 + kbugbuster/gui/msginputdialog.cpp | 226 + kbugbuster/gui/msginputdialog.h | 55 + kbugbuster/gui/packagelvi.cpp | 38 + kbugbuster/gui/packagelvi.h | 51 + kbugbuster/gui/packageselectdialog.cpp | 214 + kbugbuster/gui/packageselectdialog.h | 64 + kbugbuster/gui/preferencesdialog.cpp | 306 ++ kbugbuster/gui/preferencesdialog.h | 76 + kbugbuster/gui/serverconfigdialog.cpp | 82 + kbugbuster/gui/serverconfigdialog.h | 28 + kbugbuster/gui/severityselectdialog.cpp | 40 + kbugbuster/gui/severityselectdialog.h | 23 + kbugbuster/hi128-app-kbugbuster.png | Bin 0 -> 13453 bytes kbugbuster/hi16-app-kbugbuster.png | Bin 0 -> 1107 bytes kbugbuster/hi22-app-kbugbuster.png | Bin 0 -> 1914 bytes kbugbuster/hi32-app-kbugbuster.png | Bin 0 -> 2994 bytes kbugbuster/hi48-app-kbugbuster.png | Bin 0 -> 5091 bytes kbugbuster/hi64-app-kbugbuster.png | Bin 0 -> 7630 bytes kbugbuster/kbugbuster.desktop | 78 + kbugbuster/kresources/Makefile.am | 16 + kbugbuster/kresources/bugzilla.desktop | 48 + kbugbuster/kresources/kcalresource.cpp | 316 ++ kbugbuster/kresources/kcalresource.h | 123 + kbugbuster/kresources/kcalresource_plugin.cpp | 37 + kbugbuster/kresources/kcalresourceconfig.cpp | 92 + kbugbuster/kresources/kcalresourceconfig.h | 53 + .../kresources/kresources_kcal_bugzilla.kcfg | 20 + kbugbuster/kresources/resourceprefs.kcfgc | 9 + kbugbuster/lo16-app-kbugbuster.png | Bin 0 -> 305 bytes kbugbuster/lo32-app-kbugbuster.png | Bin 0 -> 493 bytes kbugbuster/main.cpp | 83 + kbugbuster/pics/Makefile.am | 4 + kbugbuster/pics/bars.png | Bin 0 -> 258 bytes kbugbuster/pics/logo.png | Bin 0 -> 15734 bytes kbugbuster/pics/tools.png | Bin 0 -> 76482 bytes kbugbuster/pics/top-right.png | Bin 0 -> 13794 bytes kcachegrind/AUTHORS | 1 + kcachegrind/COPYING | 340 ++ kcachegrind/ChangeLog | 89 + kcachegrind/INSTALL | 167 + kcachegrind/Makefile.am | 6 + kcachegrind/NEWS | 0 kcachegrind/README | 62 + kcachegrind/TODO | 100 + kcachegrind/configure.in.in | 8 + kcachegrind/converters/Makefile.am | 2 + kcachegrind/converters/README | 24 + kcachegrind/converters/dprof2calltree | 199 + kcachegrind/converters/hotshot2calltree | 394 ++ kcachegrind/converters/memprof2calltree | 38 + kcachegrind/converters/op2calltree | 238 + kcachegrind/converters/pprof2calltree | 218 + kcachegrind/kcachegrind.lsm.in | 11 + kcachegrind/kcachegrind.spec.in | 55 + kcachegrind/kcachegrind/Doxyfile | 157 + kcachegrind/kcachegrind/Makefile.am | 62 + kcachegrind/kcachegrind/cachegrindloader.cpp | 1323 +++++ kcachegrind/kcachegrind/callgraphview.cpp | 2734 +++++++++++ kcachegrind/kcachegrind/callgraphview.h | 499 ++ kcachegrind/kcachegrind/callitem.cpp | 185 + kcachegrind/kcachegrind/callitem.h | 50 + kcachegrind/kcachegrind/callmapview.cpp | 999 ++++ kcachegrind/kcachegrind/callmapview.h | 129 + kcachegrind/kcachegrind/callview.cpp | 256 + kcachegrind/kcachegrind/callview.h | 55 + kcachegrind/kcachegrind/configdlg.cpp | 398 ++ kcachegrind/kcachegrind/configdlg.h | 64 + kcachegrind/kcachegrind/configdlgbase.ui | 653 +++ kcachegrind/kcachegrind/configuration.cpp | 490 ++ kcachegrind/kcachegrind/configuration.h | 101 + kcachegrind/kcachegrind/costlistitem.cpp | 136 + kcachegrind/kcachegrind/costlistitem.h | 52 + kcachegrind/kcachegrind/costtypeitem.cpp | 149 + kcachegrind/kcachegrind/costtypeitem.h | 50 + kcachegrind/kcachegrind/costtypeview.cpp | 310 ++ kcachegrind/kcachegrind/costtypeview.h | 53 + kcachegrind/kcachegrind/coverage.cpp | 329 ++ kcachegrind/kcachegrind/coverage.h | 102 + kcachegrind/kcachegrind/coverageitem.cpp | 343 ++ kcachegrind/kcachegrind/coverageitem.h | 82 + kcachegrind/kcachegrind/coverageview.cpp | 321 ++ kcachegrind/kcachegrind/coverageview.h | 56 + kcachegrind/kcachegrind/dumpmanager.cpp | 50 + kcachegrind/kcachegrind/dumpmanager.h | 59 + kcachegrind/kcachegrind/dumpselection.cpp | 33 + kcachegrind/kcachegrind/dumpselection.h | 29 + kcachegrind/kcachegrind/dumpselectionbase.ui | 1082 +++++ kcachegrind/kcachegrind/fixcost.cpp | 174 + kcachegrind/kcachegrind/fixcost.h | 171 + kcachegrind/kcachegrind/functionitem.cpp | 236 + kcachegrind/kcachegrind/functionitem.h | 58 + kcachegrind/kcachegrind/functionselection.cpp | 871 ++++ kcachegrind/kcachegrind/functionselection.h | 85 + kcachegrind/kcachegrind/functionselectionbase.ui | 163 + kcachegrind/kcachegrind/hi32-app-kcachegrind.png | Bin 0 -> 2411 bytes kcachegrind/kcachegrind/hi48-app-kcachegrind.png | Bin 0 -> 9010 bytes kcachegrind/kcachegrind/instritem.cpp | 469 ++ kcachegrind/kcachegrind/instritem.h | 86 + kcachegrind/kcachegrind/instrview.cpp | 949 ++++ kcachegrind/kcachegrind/instrview.h | 82 + kcachegrind/kcachegrind/kcachegrind.desktop | 103 + kcachegrind/kcachegrind/kcachegrindui.rc | 57 + kcachegrind/kcachegrind/listutils.cpp | 266 + kcachegrind/kcachegrind/listutils.h | 65 + kcachegrind/kcachegrind/lo16-app-kcachegrind.png | Bin 0 -> 377 bytes kcachegrind/kcachegrind/lo32-app-kcachegrind.png | Bin 0 -> 576 bytes kcachegrind/kcachegrind/loader.cpp | 85 + kcachegrind/kcachegrind/loader.h | 79 + kcachegrind/kcachegrind/main.cpp | 95 + kcachegrind/kcachegrind/multiview.cpp | 224 + kcachegrind/kcachegrind/multiview.h | 66 + kcachegrind/kcachegrind/partgraph.cpp | 534 +++ kcachegrind/kcachegrind/partgraph.h | 131 + kcachegrind/kcachegrind/partlistitem.cpp | 189 + kcachegrind/kcachegrind/partlistitem.h | 54 + kcachegrind/kcachegrind/partselection.cpp | 567 +++ kcachegrind/kcachegrind/partselection.h | 95 + kcachegrind/kcachegrind/partselectionbase.ui | 89 + kcachegrind/kcachegrind/partview.cpp | 235 + kcachegrind/kcachegrind/partview.h | 54 + kcachegrind/kcachegrind/pool.cpp | 258 + kcachegrind/kcachegrind/pool.h | 107 + kcachegrind/kcachegrind/sourceitem.cpp | 444 ++ kcachegrind/kcachegrind/sourceitem.h | 84 + kcachegrind/kcachegrind/sourceview.cpp | 813 ++++ kcachegrind/kcachegrind/sourceview.h | 70 + kcachegrind/kcachegrind/stackbrowser.cpp | 417 ++ kcachegrind/kcachegrind/stackbrowser.h | 109 + kcachegrind/kcachegrind/stackitem.cpp | 116 + kcachegrind/kcachegrind/stackitem.h | 56 + kcachegrind/kcachegrind/stackselection.cpp | 230 + kcachegrind/kcachegrind/stackselection.h | 80 + kcachegrind/kcachegrind/stackselectionbase.ui | 80 + kcachegrind/kcachegrind/subcost.cpp | 62 + kcachegrind/kcachegrind/subcost.h | 66 + kcachegrind/kcachegrind/tabview.cpp | 890 ++++ kcachegrind/kcachegrind/tabview.h | 170 + kcachegrind/kcachegrind/tips | 141 + kcachegrind/kcachegrind/toplevel.cpp | 2399 +++++++++ kcachegrind/kcachegrind/toplevel.h | 274 ++ kcachegrind/kcachegrind/tracedata.cpp | 5072 ++++++++++++++++++++ kcachegrind/kcachegrind/tracedata.h | 1967 ++++++++ kcachegrind/kcachegrind/traceitemview.cpp | 443 ++ kcachegrind/kcachegrind/traceitemview.h | 206 + kcachegrind/kcachegrind/treemap.cpp | 3214 +++++++++++++ kcachegrind/kcachegrind/treemap.h | 758 +++ kcachegrind/kcachegrind/utils.cpp | 483 ++ kcachegrind/kcachegrind/utils.h | 164 + kcachegrind/kcachegrind/x-kcachegrind.desktop | 44 + kcachegrind/pics/Makefile.am | 3 + kcachegrind/pics/hicolor/Makefile.am | 2 + kcachegrind/pics/hicolor/hi16-action-fromrec.png | Bin 0 -> 226 bytes kcachegrind/pics/hicolor/hi16-action-percent.png | Bin 0 -> 261 bytes kcachegrind/pics/hicolor/hi16-action-recrec.png | Bin 0 -> 266 bytes kcachegrind/pics/hicolor/hi16-action-torec.png | Bin 0 -> 238 bytes kcachegrind/pics/hicolor/hi22-action-percent.png | Bin 0 -> 351 bytes kcachegrind/pics/hicolor/hi32-action-percent.png | Bin 0 -> 417 bytes kcachegrind/tests/cg-badcompression1 | 17 + kcachegrind/tests/cg-badcostline1 | 11 + kcachegrind/tests/cg-badposition | 15 + kcachegrind/version.h.in | 1 + kdeaccounts-plugin/Makefile.am | 17 + kdeaccounts-plugin/README | 15 + kdeaccounts-plugin/kdeaccountsformat.cpp | 90 + kdeaccounts-plugin/kdeaccountsformat.h | 51 + kdeaccounts-plugin/kdeaccountsplugin.desktop | 44 + kdepalettes/KDE_Gimp | 44 + kdepalettes/README | 16 + kdepalettes/kde_xpaintrc | 128 + kdesdk.lsm | 11 + kfile-plugins/Makefile.am | 1 + kfile-plugins/c++/Makefile.am | 22 + kfile-plugins/c++/kfile_cpp.cpp | 130 + kfile-plugins/c++/kfile_cpp.desktop | 60 + kfile-plugins/c++/kfile_cpp.h | 40 + kfile-plugins/c++/kfile_h.desktop | 58 + kfile-plugins/diff/Makefile.am | 22 + kfile-plugins/diff/kfile_diff.cpp | 610 +++ kfile-plugins/diff/kfile_diff.desktop | 57 + kfile-plugins/diff/kfile_diff.h | 52 + kfile-plugins/ts/Makefile.am | 21 + kfile-plugins/ts/kfile_ts.cpp | 93 + kfile-plugins/ts/kfile_ts.desktop | 54 + kfile-plugins/ts/kfile_ts.h | 39 + kioslave/Makefile.am | 6 + kioslave/svn/AUTHORS | 1 + kioslave/svn/COPYING | 481 ++ kioslave/svn/Makefile.am | 15 + kioslave/svn/README | 40 + kioslave/svn/TODO | 74 + kioslave/svn/configure.in.bot | 9 + kioslave/svn/configure.in.in | 157 + kioslave/svn/icons/Makefile.am | 1 + kioslave/svn/icons/cr128-action-svn_add.png | Bin 0 -> 12287 bytes kioslave/svn/icons/cr128-action-svn_branch.png | Bin 0 -> 12256 bytes kioslave/svn/icons/cr128-action-svn_merge.png | Bin 0 -> 5183 bytes kioslave/svn/icons/cr128-action-svn_remove.png | Bin 0 -> 12607 bytes kioslave/svn/icons/cr128-action-svn_status.png | Bin 0 -> 14500 bytes kioslave/svn/icons/cr128-action-svn_switch.png | Bin 0 -> 12428 bytes kioslave/svn/icons/cr16-action-svn_add.png | Bin 0 -> 801 bytes kioslave/svn/icons/cr16-action-svn_branch.png | Bin 0 -> 858 bytes kioslave/svn/icons/cr16-action-svn_merge.png | Bin 0 -> 565 bytes kioslave/svn/icons/cr16-action-svn_remove.png | Bin 0 -> 660 bytes kioslave/svn/icons/cr16-action-svn_status.png | Bin 0 -> 926 bytes kioslave/svn/icons/cr16-action-svn_switch.png | Bin 0 -> 752 bytes kioslave/svn/icons/cr22-action-svn_add.png | Bin 0 -> 1232 bytes kioslave/svn/icons/cr22-action-svn_branch.png | Bin 0 -> 1261 bytes kioslave/svn/icons/cr22-action-svn_merge.png | Bin 0 -> 924 bytes kioslave/svn/icons/cr22-action-svn_remove.png | Bin 0 -> 1041 bytes kioslave/svn/icons/cr22-action-svn_status.png | Bin 0 -> 1392 bytes kioslave/svn/icons/cr22-action-svn_switch.png | Bin 0 -> 1257 bytes kioslave/svn/icons/cr32-action-svn_add.png | Bin 0 -> 1888 bytes kioslave/svn/icons/cr32-action-svn_branch.png | Bin 0 -> 2093 bytes kioslave/svn/icons/cr32-action-svn_merge.png | Bin 0 -> 1347 bytes kioslave/svn/icons/cr32-action-svn_remove.png | Bin 0 -> 1694 bytes kioslave/svn/icons/cr32-action-svn_status.png | Bin 0 -> 2326 bytes kioslave/svn/icons/cr32-action-svn_switch.png | Bin 0 -> 2048 bytes kioslave/svn/icons/cr48-action-svn_add.png | Bin 0 -> 3318 bytes kioslave/svn/icons/cr48-action-svn_branch.png | Bin 0 -> 3527 bytes kioslave/svn/icons/cr48-action-svn_merge.png | Bin 0 -> 2004 bytes kioslave/svn/icons/cr48-action-svn_remove.png | Bin 0 -> 3064 bytes kioslave/svn/icons/cr48-action-svn_status.png | Bin 0 -> 4072 bytes kioslave/svn/icons/cr48-action-svn_switch.png | Bin 0 -> 3524 bytes kioslave/svn/icons/cr64-action-svn_add.png | Bin 0 -> 4653 bytes kioslave/svn/icons/cr64-action-svn_branch.png | Bin 0 -> 5140 bytes kioslave/svn/icons/cr64-action-svn_merge.png | Bin 0 -> 2624 bytes kioslave/svn/icons/cr64-action-svn_remove.png | Bin 0 -> 4472 bytes kioslave/svn/icons/cr64-action-svn_status.png | Bin 0 -> 6036 bytes kioslave/svn/icons/cr64-action-svn_switch.png | Bin 0 -> 5161 bytes kioslave/svn/icons/crsc-action-svn_add.svgz | Bin 0 -> 81728 bytes kioslave/svn/icons/crsc-action-svn_branch.svgz | Bin 0 -> 7200 bytes kioslave/svn/icons/crsc-action-svn_merge.svgz | Bin 0 -> 9170 bytes kioslave/svn/icons/crsc-action-svn_remove.svgz | Bin 0 -> 82210 bytes kioslave/svn/icons/crsc-action-svn_status.svgz | Bin 0 -> 3590 bytes kioslave/svn/icons/crsc-action-svn_switch.svgz | Bin 0 -> 8258 bytes kioslave/svn/ksvnd/Makefile.am | 13 + kioslave/svn/ksvnd/commitdlg.ui | 116 + kioslave/svn/ksvnd/commitdlg.ui.h | 30 + kioslave/svn/ksvnd/ksvnd.cpp | 351 ++ kioslave/svn/ksvnd/ksvnd.desktop | 50 + kioslave/svn/ksvnd/ksvnd.h | 69 + kioslave/svn/svn+file.protocol | 43 + kioslave/svn/svn+http.protocol | 43 + kioslave/svn/svn+https.protocol | 43 + kioslave/svn/svn+ssh.protocol | 43 + kioslave/svn/svn.cpp | 1593 ++++++ kioslave/svn/svn.h | 140 + kioslave/svn/svn.protocol | 43 + kioslave/svn/svnhelper/Makefile.am | 18 + kioslave/svn/svnhelper/apply_patch.desktop | 94 + kioslave/svn/svnhelper/kio_svn_helper.cpp | 292 ++ kioslave/svn/svnhelper/kio_svn_helper.h | 41 + kioslave/svn/svnhelper/subversion.desktop | 919 ++++ kioslave/svn/svnhelper/subversion_toplevel.desktop | 97 + kioslave/svn/svnhelper/subversioncheckout.ui | 204 + kioslave/svn/svnhelper/subversiondiff.ui | 100 + kioslave/svn/svnhelper/subversionlog.ui | 100 + kioslave/svn/svnhelper/subversionswitch.ui | 204 + kmtrace/Makefile.am | 50 + kmtrace/README | 110 + kmtrace/configure.in.in | 18 + kmtrace/demangle.cpp | 60 + kmtrace/kde.excludes | 49 + kmtrace/kminspector.in | 9 + kmtrace/kmtrace.cpp | 721 +++ kmtrace/ksotrace.cpp | 11 + kmtrace/ktrace.c | 840 ++++ kmtrace/ktrace.h | 10 + kmtrace/match.cpp | 73 + kmtrace/mtrace.c | 383 ++ kompare/AUTHORS | 2 + kompare/COPYING | 340 ++ kompare/ChangeLog | 423 ++ kompare/DESIGN | 35 + kompare/Makefile.am | 43 + kompare/README | 16 + kompare/TODO | 34 + kompare/interfaces/Makefile.am | 8 + kompare/interfaces/kompareinterface.cpp | 60 + kompare/interfaces/kompareinterface.h | 105 + kompare/kompare.desktop | 74 + kompare/kompare_shell.cpp | 487 ++ kompare/kompare_shell.h | 149 + kompare/komparenavigationpart.desktop | 4 + kompare/komparenavtreepart/Makefile.am | 29 + kompare/komparenavtreepart/komparenavtreepart.cpp | 710 +++ .../komparenavtreepart/komparenavtreepart.desktop | 11 + kompare/komparenavtreepart/komparenavtreepart.h | 191 + kompare/komparepart/Makefile.am | 49 + kompare/komparepart/kompare_part.cpp | 759 +++ kompare/komparepart/kompare_part.h | 223 + kompare/komparepart/kompare_qsplitter.h | 213 + kompare/komparepart/kompareconnectwidget.cpp | 285 ++ kompare/komparepart/kompareconnectwidget.h | 95 + kompare/komparepart/komparelistview.cpp | 783 +++ kompare/komparepart/komparelistview.h | 225 + kompare/komparepart/komparepart.desktop | 24 + kompare/komparepart/komparepartui.rc | 44 + kompare/komparepart/kompareprefdlg.cpp | 106 + kompare/komparepart/kompareprefdlg.h | 54 + kompare/komparepart/komparesaveoptionsbase.ui | 336 ++ kompare/komparepart/komparesaveoptionswidget.cpp | 215 + kompare/komparepart/komparesaveoptionswidget.h | 51 + kompare/komparepart/komparesplitter.cpp | 712 +++ kompare/komparepart/komparesplitter.h | 115 + kompare/kompareui.rc | 33 + kompare/kompareurldialog.cpp | 143 + kompare/kompareurldialog.h | 76 + kompare/kompareviewpart.desktop | 4 + kompare/libdialogpages/Makefile.am | 32 + kompare/libdialogpages/diffpage.cpp | 355 ++ kompare/libdialogpages/diffpage.h | 100 + kompare/libdialogpages/diffsettings.cpp | 108 + kompare/libdialogpages/diffsettings.h | 66 + kompare/libdialogpages/filespage.cpp | 170 + kompare/libdialogpages/filespage.h | 83 + kompare/libdialogpages/filessettings.cpp | 60 + kompare/libdialogpages/filessettings.h | 53 + kompare/libdialogpages/pagebase.cpp | 104 + kompare/libdialogpages/pagebase.h | 49 + kompare/libdialogpages/settingsbase.cpp | 42 + kompare/libdialogpages/settingsbase.h | 42 + kompare/libdialogpages/viewpage.cpp | 179 + kompare/libdialogpages/viewpage.h | 64 + kompare/libdialogpages/viewsettings.cpp | 101 + kompare/libdialogpages/viewsettings.h | 61 + kompare/libdiff2/Makefile.am | 37 + kompare/libdiff2/cvsdiffparser.cpp | 160 + kompare/libdiff2/cvsdiffparser.h | 61 + kompare/libdiff2/difference.cpp | 137 + kompare/libdiff2/difference.h | 223 + kompare/libdiff2/diffhunk.cpp | 115 + kompare/libdiff2/diffhunk.h | 69 + kompare/libdiff2/diffmodel.cpp | 409 ++ kompare/libdiff2/diffmodel.h | 150 + kompare/libdiff2/diffmodellist.cpp | 29 + kompare/libdiff2/diffmodellist.h | 49 + kompare/libdiff2/diffparser.cpp | 81 + kompare/libdiff2/diffparser.h | 38 + kompare/libdiff2/kompare.h | 144 + kompare/libdiff2/komparemodellist.cpp | 1423 ++++++ kompare/libdiff2/komparemodellist.h | 213 + kompare/libdiff2/kompareprocess.cpp | 269 ++ kompare/libdiff2/kompareprocess.h | 67 + kompare/libdiff2/levenshteintable.cpp | 332 ++ kompare/libdiff2/levenshteintable.h | 69 + kompare/libdiff2/parser.cpp | 139 + kompare/libdiff2/parser.h | 58 + kompare/libdiff2/parserbase.cpp | 739 +++ kompare/libdiff2/parserbase.h | 133 + kompare/libdiff2/perforceparser.cpp | 223 + kompare/libdiff2/perforceparser.h | 44 + kompare/main.cpp | 217 + kompare/pics/Makefile.am | 3 + kompare/pics/hi128-app-kompare.png | Bin 0 -> 19472 bytes kompare/pics/hi16-app-kompare.png | Bin 0 -> 917 bytes kompare/pics/hi22-app-kompare.png | Bin 0 -> 1237 bytes kompare/pics/hi32-app-kompare.png | Bin 0 -> 2771 bytes kompare/pics/hi48-app-kompare.png | Bin 0 -> 4780 bytes kompare/pics/hisc-app-kompare.svgz | Bin 0 -> 1474 bytes kompare/tests/cvsdiff/context.diff | 83 + kompare/tests/cvsdiff/contextm.diff | 1046 ++++ kompare/tests/cvsdiff/ed.diff | 24 + kompare/tests/cvsdiff/edm.diff | 692 +++ kompare/tests/cvsdiff/normal.diff | 29 + kompare/tests/cvsdiff/normalm.diff | 861 ++++ kompare/tests/cvsdiff/rcs.diff | 24 + kompare/tests/cvsdiff/rcsm.diff | 683 +++ kompare/tests/cvsdiff/unified.diff | 50 + kompare/tests/cvsdiff/unifiedm.diff | 924 ++++ kompare/tests/diff/context.diff | 27 + kompare/tests/diff/contextm.diff | 1032 ++++ kompare/tests/diff/contextp.diff | 27 + kompare/tests/diff/ed.diff | 10 + kompare/tests/diff/edm.diff | 680 +++ kompare/tests/diff/normal.diff | 12 + kompare/tests/diff/normalm.diff | 849 ++++ kompare/tests/diff/rcs.diff | 9 + kompare/tests/diff/rcsm.diff | 671 +++ kompare/tests/diff/unified.diff | 19 + kompare/tests/diff/unifiedm.diff | 911 ++++ kompare/tests/diff/unifiedp.diff | 19 + kompare/tests/perforce/context.diff | 8 + kompare/tests/perforce/contextm.diff | 23 + kompare/tests/perforce/rcs.diff | 3 + kompare/tests/perforce/rcsm.diff | 12 + kompare/tests/perforce/unified.diff | 6 + kompare/tests/perforce/unifiedm.diff | 19 + kompare/tests/subversion/context.diff | 9 + kompare/tests/subversion/contextm.diff | 180 + kompare/tests/subversion/ed.diff | 5 + kompare/tests/subversion/edm.diff | 57 + kompare/tests/subversion/normal.diff | 6 + kompare/tests/subversion/normalm.diff | 170 + kompare/tests/subversion/rcs.diff | 5 + kompare/tests/subversion/rcsm.diff | 55 + kompare/tests/subversion/unified.diff | 7 + kompare/tests/subversion/unifiedm.diff | 173 + kprofilemethod/Makefile.am | 2 + kprofilemethod/README | 21 + kprofilemethod/kprofilemethod.h | 51 + kspy/Makefile.am | 30 + kspy/README | 12 + kspy/classinfoview.cpp | 58 + kspy/classinfoview.h | 40 + kspy/kspy.h | 44 + kspy/main.cpp | 52 + kspy/navview.cpp | 88 + kspy/navview.h | 57 + kspy/navviewitem.cpp | 40 + kspy/navviewitem.h | 38 + kspy/propsview.cpp | 196 + kspy/propsview.h | 40 + kspy/receiversview.cpp | 80 + kspy/receiversview.h | 40 + kspy/sigslotview.cpp | 73 + kspy/sigslotview.h | 40 + kspy/spy.cpp | 115 + kspy/spy.h | 59 + kstartperf/Makefile.am | 14 + kstartperf/README | 31 + kstartperf/kstartperf.cpp | 124 + kstartperf/libkstartperf.c | 122 + kuiviewer/Makefile.am | 70 + kuiviewer/designerthumbnail.desktop | 53 + kuiviewer/hi16-app-kuiviewer.png | Bin 0 -> 845 bytes kuiviewer/hi32-app-kuiviewer.png | Bin 0 -> 2595 bytes kuiviewer/hi48-app-kuiviewer.png | Bin 0 -> 4954 bytes kuiviewer/kuiviewer.cpp | 165 + kuiviewer/kuiviewer.desktop | 58 + kuiviewer/kuiviewer.h | 103 + kuiviewer/kuiviewer_part.cpp | 211 + kuiviewer/kuiviewer_part.desktop | 9 + kuiviewer/kuiviewer_part.h | 80 + kuiviewer/kuiviewer_part.rc | 17 + kuiviewer/kuiviewerui.rc | 8 + kuiviewer/lo16-app-kuiviewer.png | Bin 0 -> 309 bytes kuiviewer/lo32-app-kuiviewer.png | Bin 0 -> 497 bytes kuiviewer/main.cpp | 88 + kuiviewer/quicreator.cpp | 61 + kuiviewer/quicreator.h | 36 + kunittest/Makefile.am | 21 + kunittest/dcopinterface.h | 38 + kunittest/example/Makefile.am | 1 + kunittest/example/module/Makefile.am | 20 + kunittest/example/module/sampleextra.cpp | 37 + kunittest/example/module/sampleextra.h | 36 + kunittest/example/module/samplemodule.cpp | 37 + kunittest/example/module/samplemodule.h | 43 + kunittest/example/module/samplemodule2.cpp | 61 + kunittest/example/module/samplemodule2.h | 43 + kunittest/example/module/sampletests.cpp | 82 + kunittest/example/module/sampletests.h | 53 + kunittest/example/simple/Makefile.am | 22 + kunittest/example/simple/main.cpp | 32 + kunittest/example/simple/maingui.cpp | 60 + kunittest/example/simple/sampletest.cpp | 55 + kunittest/example/simple/sampletest.h | 42 + kunittest/guimodrunner.cpp | 72 + kunittest/kunittest | 19 + kunittest/kunittest_debughelper | 107 + kunittest/kunittestmod | 37 + kunittest/runnergui.cpp | 433 ++ kunittest/runnergui.h | 78 + kunittest/testerwidget.ui | 197 + kunittest/testerwidget.ui.h | 46 + poxml/GettextLexer.cpp | 550 +++ poxml/GettextLexer.hpp | 47 + poxml/GettextParser.cpp | 414 ++ poxml/GettextParser.hpp | 53 + poxml/GettextParserTokenTypes.hpp | 22 + poxml/GettextParserTokenTypes.txt | 13 + poxml/Makefile.am | 48 + poxml/antlr/AUTHORS | 2 + poxml/antlr/COPYING | 32 + poxml/antlr/ChangeLog | 293 ++ poxml/antlr/INSTALL | 183 + poxml/antlr/Makefile.am | 2 + poxml/antlr/README | 72 + poxml/antlr/TODO | 34 + poxml/antlr/antlr/ANTLRException.hpp | 60 + poxml/antlr/antlr/AST.hpp | 108 + poxml/antlr/antlr/ASTArray.hpp | 63 + poxml/antlr/antlr/ASTFactory.hpp | 113 + poxml/antlr/antlr/ASTNULLType.hpp | 72 + poxml/antlr/antlr/ASTPair.hpp | 77 + poxml/antlr/antlr/ASTRefCount.hpp | 104 + poxml/antlr/antlr/BaseAST.hpp | 106 + poxml/antlr/antlr/BitSet.hpp | 50 + poxml/antlr/antlr/CharBuffer.hpp | 75 + poxml/antlr/antlr/CharScanner.hpp | 265 + poxml/antlr/antlr/CharStreamException.hpp | 18 + poxml/antlr/antlr/CharStreamIOException.hpp | 20 + poxml/antlr/antlr/CircularQueue.hpp | 88 + poxml/antlr/antlr/CommonAST.hpp | 68 + poxml/antlr/antlr/CommonASTWithHiddenTokens.hpp | 41 + poxml/antlr/antlr/CommonHiddenStreamToken.hpp | 30 + poxml/antlr/antlr/CommonToken.hpp | 77 + poxml/antlr/antlr/InputBuffer.hpp | 158 + poxml/antlr/antlr/LLkParser.hpp | 82 + poxml/antlr/antlr/LexerSharedInputState.hpp | 49 + poxml/antlr/antlr/Makefile.am | 45 + poxml/antlr/antlr/MismatchedCharException.hpp | 127 + poxml/antlr/antlr/MismatchedTokenException.hpp | 167 + poxml/antlr/antlr/NoViableAltException.hpp | 71 + poxml/antlr/antlr/NoViableAltForCharException.hpp | 64 + poxml/antlr/antlr/Parser.hpp | 213 + poxml/antlr/antlr/ParserSharedInputState.hpp | 42 + poxml/antlr/antlr/RecognitionException.hpp | 78 + poxml/antlr/antlr/RefCount.hpp | 87 + poxml/antlr/antlr/SemanticException.hpp | 52 + poxml/antlr/antlr/String.hpp | 47 + poxml/antlr/antlr/Token.hpp | 106 + poxml/antlr/antlr/TokenBuffer.hpp | 141 + poxml/antlr/antlr/TokenStream.hpp | 54 + poxml/antlr/antlr/TokenStreamBasicFilter.hpp | 35 + poxml/antlr/antlr/TokenStreamException.hpp | 19 + poxml/antlr/antlr/TokenStreamHiddenTokenFilter.hpp | 84 + poxml/antlr/antlr/TokenStreamIOException.hpp | 22 + .../antlr/TokenStreamRecognitionException.hpp | 21 + poxml/antlr/antlr/TokenStreamRetryException.hpp | 17 + poxml/antlr/antlr/TokenStreamSelector.hpp | 78 + poxml/antlr/antlr/TreeParser.hpp | 159 + poxml/antlr/antlr/TreeParserSharedInputState.hpp | 34 + poxml/antlr/antlr/config.hpp | 168 + poxml/antlr/configure.in | 22 + poxml/antlr/src/ANTLRException.cpp | 57 + poxml/antlr/src/ASTFactory.cpp | 218 + poxml/antlr/src/ASTRefCount.cpp | 74 + poxml/antlr/src/BaseAST.cpp | 320 ++ poxml/antlr/src/BitSet.cpp | 76 + poxml/antlr/src/CharBuffer.cpp | 67 + poxml/antlr/src/CharScanner.cpp | 430 ++ poxml/antlr/src/CommonAST.cpp | 100 + poxml/antlr/src/CommonASTWithHiddenTokens.cpp | 29 + poxml/antlr/src/CommonHiddenStreamToken.cpp | 46 + poxml/antlr/src/CommonToken.cpp | 81 + poxml/antlr/src/InputBuffer.cpp | 109 + poxml/antlr/src/LLkParser.cpp | 105 + poxml/antlr/src/LexerSharedInputState.cpp | 55 + poxml/antlr/src/Makefile.am | 39 + poxml/antlr/src/MismatchedCharException.cpp | 153 + poxml/antlr/src/MismatchedTokenException.cpp | 223 + poxml/antlr/src/NoViableAltException.cpp | 82 + poxml/antlr/src/NoViableAltForCharException.cpp | 71 + poxml/antlr/src/Parser.cpp | 304 ++ poxml/antlr/src/ParserSharedInputState.cpp | 37 + poxml/antlr/src/RecognitionException.cpp | 87 + poxml/antlr/src/String.cpp | 61 + poxml/antlr/src/Token.cpp | 108 + poxml/antlr/src/TokenBuffer.cpp | 107 + poxml/antlr/src/TokenStreamBasicFilter.cpp | 34 + poxml/antlr/src/TokenStreamHiddenTokenFilter.cpp | 146 + poxml/antlr/src/TokenStreamSelector.cpp | 97 + poxml/antlr/src/TreeParser.cpp | 165 + poxml/antlr/src/TreeParserSharedInputState.cpp | 22 + poxml/gettext.g | 168 + poxml/lauri.po | 442 ++ poxml/lauri.xml | 241 + poxml/parser.cpp | 1008 ++++ poxml/parser.h | 124 + poxml/po2xml.cpp | 261 + poxml/split.cpp | 162 + poxml/swappo.cpp | 38 + poxml/transxx.cpp | 130 + poxml/xml2pot.cpp | 77 + scheck/Makefile.am | 33 + scheck/README | 22 + scheck/bitmaps.h | 84 + scheck/scheck.cpp | 2762 +++++++++++ scheck/scheck.h | 173 + scheck/scheck.themerc | 55 + scheck/status.txt | 41 + scripts/Makefile.am | 57 + scripts/README | 186 + scripts/add_trace.pl | 124 + scripts/adddebug | 63 + scripts/alldcop.rb | 91 + scripts/authors2xml.pl | 27 + scripts/build-progress.sh | 22 + scripts/cheatmake | 80 + scripts/check_licenses | 88 + scripts/colorcvs | 175 + scripts/colorcvsrc-sample | 36 + scripts/colorsvn | 201 + scripts/completions/bash/dcop | 52 + scripts/completions/zsh/_dcop | 11 + scripts/completions/zsh/_kcmshell | 16 + scripts/completions/zsh/_kdeinit_wrapper | 6 + scripts/completions/zsh/_kdekillall | 8 + scripts/completions/zsh/_makeobj | 30 + scripts/create_cvsignore | 55 + scripts/create_makefile | 110 + scripts/create_makefiles | 34 + scripts/create_svnignore | 82 + scripts/cvs-clean | 90 + scripts/cvs2dist | 626 +++ scripts/cvsaddcurrentdir | 30 + scripts/cvsbackport | 32 + scripts/cvsblame | 252 + scripts/cvscheck | 385 ++ scripts/cvsforwardport | 32 + scripts/cvsgettags | 38 + scripts/cvslastchange | 55 + scripts/cvslastlog | 8 + scripts/cvslastreferenced | 64 + scripts/cvsrevertlast | 17 + scripts/cvsversion | 30 + scripts/cxxmetric | 223 + scripts/extend_dmalloc | 159 + scripts/extractattr | 158 + scripts/extractrc | 174 + scripts/findmissingcrystal | 32 + scripts/fixfsfaddr.sed | 30 + scripts/fixheaders | 214 + scripts/fixkdeincludes | 756 +++ scripts/fixuifiles | 293 ++ scripts/gettext.patch | 194 + scripts/includemocs | 102 + scripts/kDebug2kdDebug.sh | 154 + scripts/kde-build | 898 ++++ scripts/kde-buildrc | 205 + scripts/kde-devel-emacs.el | 1890 ++++++++ scripts/kde-devel-gdb | 299 ++ scripts/kde-devel-vim.vim | 428 ++ scripts/kde-emacs/HACKING | 7 + scripts/kde-emacs/dirvars.el | 200 + scripts/kde-emacs/kde-emacs-bindings.el | 185 + scripts/kde-emacs/kde-emacs-compat.el | 77 + scripts/kde-emacs/kde-emacs-core.el | 3823 +++++++++++++++ scripts/kde-emacs/kde-emacs-doc.el | 322 ++ scripts/kde-emacs/kde-emacs-general.el | 179 + scripts/kde-emacs/kde-emacs-semantic.el | 456 ++ scripts/kde-emacs/kde-emacs-tips.texi | 257 + scripts/kde-emacs/kde-emacs-utils.el | 894 ++++ scripts/kde-emacs/kde-emacs-vars.el | 147 + scripts/kde-emacs/kde-emacs.el | 66 + scripts/kde-emacs/klaralv.el | 422 ++ scripts/kde-spellcheck.pl | 1537 ++++++ scripts/kde.supp | 155 + scripts/kdedoc | 48 + scripts/kdekillall | 28 + scripts/kdelnk2desktop.py | 21 + scripts/kdemangen.pl | 250 + scripts/kdesvn-build | 4286 +++++++++++++++++ scripts/kdesvn-buildrc-sample | 246 + scripts/kdesvn-buildrc.xml | 152 + scripts/licensecheck | 63 + scripts/makeobj | 96 + scripts/noncvslist | 127 + scripts/nonsvnlist | 4 + scripts/package_crystalsvg | 185 + scripts/png2mng.pl | 193 + scripts/pruneemptydirs | 46 + scripts/qtdoc | 19 + scripts/rc2kcfgxt.pl | 95 + scripts/svn-clean | 113 + scripts/svn2dist | 638 +++ scripts/svnaddcurrentdir | 30 + scripts/svnbackport | 64 + scripts/svnchangesince | 63 + scripts/svnforwardport | 64 + scripts/svngettags | 3 + scripts/svnlastchange | 172 + scripts/svnlastlog | 4 + scripts/svnrevertlast | 9 + scripts/svnversions | 51 + scripts/zonetab2pot.py | 24 + umbrello/AUTHORS | 14 + umbrello/COPYING | 340 ++ umbrello/ChangeLog | 644 +++ umbrello/INSTALL | 179 + umbrello/Makefile.am | 12 + umbrello/README | 10 + umbrello/THANKS | 93 + umbrello/TODO | 22 + umbrello/VERSION | 1 + umbrello/configure.in.in | 112 + umbrello/make-umbrello-release.sh | 60 + umbrello/umbrello/Makefile.am | 155 + umbrello/umbrello/activitywidget.cpp | 235 + umbrello/umbrello/activitywidget.h | 127 + umbrello/umbrello/actor.cpp | 40 + umbrello/umbrello/actor.h | 66 + umbrello/umbrello/actorwidget.cpp | 79 + umbrello/umbrello/actorwidget.h | 78 + umbrello/umbrello/aligntoolbar.cpp | 391 ++ umbrello/umbrello/aligntoolbar.h | 224 + umbrello/umbrello/artifact.cpp | 57 + umbrello/umbrello/artifact.h | 97 + umbrello/umbrello/artifactwidget.cpp | 259 + umbrello/umbrello/artifactwidget.h | 107 + umbrello/umbrello/association.cpp | 574 +++ umbrello/umbrello/association.h | 290 ++ umbrello/umbrello/associationwidget.cpp | 3614 ++++++++++++++ umbrello/umbrello/associationwidget.h | 1045 ++++ umbrello/umbrello/associationwidgetlist.h | 29 + umbrello/umbrello/assocrules.cpp | 386 ++ umbrello/umbrello/assocrules.h | 106 + umbrello/umbrello/attribute.cpp | 327 ++ umbrello/umbrello/attribute.h | 157 + umbrello/umbrello/autolayout/Makefile.am | 20 + umbrello/umbrello/autolayout/_graph.h | 34 + umbrello/umbrello/autolayout/autolayout.h | 45 + umbrello/umbrello/autolayout/autolayoutdlg.cpp | 184 + umbrello/umbrello/autolayout/autolayoutdlg.h | 63 + umbrello/umbrello/autolayout/autolayouter.cpp | 29 + umbrello/umbrello/autolayout/autolayouter.h | 64 + .../umbrello/autolayout/autolayouteradapter.cpp | 191 + umbrello/umbrello/autolayout/autolayouteradapter.h | 87 + umbrello/umbrello/autolayout/baseinclude.h | 29 + umbrello/umbrello/autolayout/canvas.h | 34 + umbrello/umbrello/autolayout/diagram.h | 50 + umbrello/umbrello/autolayout/diagram_interface.h | 31 + umbrello/umbrello/autolayout/dotautolayouter.cpp | 43 + umbrello/umbrello/autolayout/dotautolayouter.h | 38 + .../umbrello/autolayout/graphvizautolayouter.cpp | 54 + .../umbrello/autolayout/graphvizautolayouter.h | 50 + umbrello/umbrello/autolayout/graphvizgraph.cpp | 150 + umbrello/umbrello/autolayout/graphvizgraph.h | 58 + umbrello/umbrello/autolayout/graphviznode.cpp | 46 + umbrello/umbrello/autolayout/graphviznode.h | 42 + .../umbrello/autolayout/newautolayoutdialog.ui | 554 +++ umbrello/umbrello/autolayout/node.h | 37 + umbrello/umbrello/autolayout/simplecanvas.cpp | 20 + umbrello/umbrello/autolayout/simplecanvas.h | 39 + umbrello/umbrello/boxwidget.cpp | 44 + umbrello/umbrello/boxwidget.h | 60 + umbrello/umbrello/classifier.cpp | 1023 ++++ umbrello/umbrello/classifier.h | 496 ++ umbrello/umbrello/classifiercodedocument.cpp | 742 +++ umbrello/umbrello/classifiercodedocument.h | 251 + umbrello/umbrello/classifierlistitem.cpp | 98 + umbrello/umbrello/classifierlistitem.h | 126 + umbrello/umbrello/classifierwidget.cpp | 803 ++++ umbrello/umbrello/classifierwidget.h | 390 ++ umbrello/umbrello/clipboard/Makefile.am | 6 + umbrello/umbrello/clipboard/idchangelog.cpp | 97 + umbrello/umbrello/clipboard/idchangelog.h | 126 + umbrello/umbrello/clipboard/umlclipboard.cpp | 694 +++ umbrello/umbrello/clipboard/umlclipboard.h | 194 + umbrello/umbrello/clipboard/umldrag.cpp | 773 +++ umbrello/umbrello/clipboard/umldrag.h | 223 + umbrello/umbrello/cmdlineexportallviewsevent.cpp | 48 + umbrello/umbrello/cmdlineexportallviewsevent.h | 78 + umbrello/umbrello/codeaccessormethod.cpp | 195 + umbrello/umbrello/codeaccessormethod.h | 118 + umbrello/umbrello/codeaccessormethodlist.h | 23 + umbrello/umbrello/codeblock.cpp | 143 + umbrello/umbrello/codeblock.h | 121 + umbrello/umbrello/codeblockwithcomments.cpp | 181 + umbrello/umbrello/codeblockwithcomments.h | 107 + umbrello/umbrello/codeclassfield.cpp | 617 +++ umbrello/umbrello/codeclassfield.h | 237 + .../umbrello/codeclassfielddeclarationblock.cpp | 174 + umbrello/umbrello/codeclassfielddeclarationblock.h | 112 + umbrello/umbrello/codeclassfieldlist.h | 23 + umbrello/umbrello/codecomment.cpp | 62 + umbrello/umbrello/codecomment.h | 68 + umbrello/umbrello/codedocument.cpp | 502 ++ umbrello/umbrello/codedocument.h | 273 ++ umbrello/umbrello/codedocumentlist.h | 23 + umbrello/umbrello/codegenerationpolicy.cpp | 587 +++ umbrello/umbrello/codegenerationpolicy.h | 384 ++ umbrello/umbrello/codegenerator.cpp | 723 +++ umbrello/umbrello/codegenerator.h | 416 ++ umbrello/umbrello/codegenerators/Makefile.am | 38 + umbrello/umbrello/codegenerators/adawriter.cpp | 686 +++ umbrello/umbrello/codegenerators/adawriter.h | 105 + umbrello/umbrello/codegenerators/aswriter.cpp | 775 +++ umbrello/umbrello/codegenerators/aswriter.h | 80 + .../umbrello/codegenerators/classifierinfo.cpp | 140 + umbrello/umbrello/codegenerators/classifierinfo.h | 126 + umbrello/umbrello/codegenerators/codegen_utils.cpp | 413 ++ umbrello/umbrello/codegenerators/codegen_utils.h | 42 + .../umbrello/codegenerators/codegenfactory.cpp | 360 ++ umbrello/umbrello/codegenerators/codegenfactory.h | 119 + .../umbrello/codegenerators/codegenpolicyext.h | 52 + .../umbrello/codegenerators/cppcodeclassfield.cpp | 108 + .../umbrello/codegenerators/cppcodeclassfield.h | 62 + .../umbrello/codegenerators/cppcodecomment.cpp | 98 + umbrello/umbrello/codegenerators/cppcodecomment.h | 83 + .../codegenerators/cppcodedocumentation.cpp | 141 + .../umbrello/codegenerators/cppcodedocumentation.h | 95 + .../codegenerators/cppcodegenerationform.cpp | 305 ++ .../codegenerators/cppcodegenerationform.h | 150 + .../codegenerators/cppcodegenerationformbase.ui | 481 ++ .../codegenerators/cppcodegenerationpolicy.cpp | 390 ++ .../codegenerators/cppcodegenerationpolicy.h | 227 + .../codegenerators/cppcodegenerationpolicypage.cpp | 94 + .../codegenerators/cppcodegenerationpolicypage.h | 48 + .../umbrello/codegenerators/cppcodegenerator.cpp | 366 ++ .../umbrello/codegenerators/cppcodegenerator.h | 167 + .../cppheaderclassdeclarationblock.cpp | 160 + .../cppheaderclassdeclarationblock.h | 67 + .../codegenerators/cppheadercodeaccessormethod.cpp | 170 + .../codegenerators/cppheadercodeaccessormethod.h | 57 + .../cppheadercodeclassfielddeclarationblock.cpp | 78 + .../cppheadercodeclassfielddeclarationblock.h | 51 + .../codegenerators/cppheadercodedocument.cpp | 813 ++++ .../codegenerators/cppheadercodedocument.h | 107 + .../codegenerators/cppheadercodeoperation.cpp | 175 + .../codegenerators/cppheadercodeoperation.h | 59 + .../codegenerators/cppmakecodedocument.cpp | 78 + .../umbrello/codegenerators/cppmakecodedocument.h | 67 + .../codegenerators/cppsourcecodeaccessormethod.cpp | 169 + .../codegenerators/cppsourcecodeaccessormethod.h | 57 + .../cppsourcecodeclassfielddeclarationblock.cpp | 74 + .../cppsourcecodeclassfielddeclarationblock.h | 51 + .../codegenerators/cppsourcecodedocument.cpp | 168 + .../codegenerators/cppsourcecodedocument.h | 73 + .../codegenerators/cppsourcecodeoperation.cpp | 193 + .../codegenerators/cppsourcecodeoperation.h | 51 + umbrello/umbrello/codegenerators/cppwriter.cpp | 1283 +++++ umbrello/umbrello/codegenerators/cppwriter.h | 293 ++ umbrello/umbrello/codegenerators/csharpwriter.cpp | 725 +++ umbrello/umbrello/codegenerators/csharpwriter.h | 163 + umbrello/umbrello/codegenerators/dwriter.cpp | 970 ++++ umbrello/umbrello/codegenerators/dwriter.h | 276 ++ umbrello/umbrello/codegenerators/idlwriter.cpp | 482 ++ umbrello/umbrello/codegenerators/idlwriter.h | 74 + .../codegenerators/javaantcodedocument.cpp | 312 ++ .../umbrello/codegenerators/javaantcodedocument.h | 100 + .../codegenerators/javaclassdeclarationblock.cpp | 169 + .../codegenerators/javaclassdeclarationblock.h | 66 + .../codegenerators/javaclassifiercodedocument.cpp | 583 +++ .../codegenerators/javaclassifiercodedocument.h | 110 + .../codegenerators/javacodeaccessormethod.cpp | 223 + .../codegenerators/javacodeaccessormethod.h | 65 + .../umbrello/codegenerators/javacodeclassfield.cpp | 111 + .../umbrello/codegenerators/javacodeclassfield.h | 59 + .../javacodeclassfielddeclarationblock.cpp | 118 + .../javacodeclassfielddeclarationblock.h | 51 + .../umbrello/codegenerators/javacodecomment.cpp | 84 + umbrello/umbrello/codegenerators/javacodecomment.h | 77 + .../codegenerators/javacodedocumentation.cpp | 143 + .../codegenerators/javacodedocumentation.h | 95 + .../codegenerators/javacodegenerationformbase.ui | 277 ++ .../codegenerators/javacodegenerationpolicy.cpp | 188 + .../codegenerators/javacodegenerationpolicy.h | 116 + .../javacodegenerationpolicypage.cpp | 84 + .../codegenerators/javacodegenerationpolicypage.h | 48 + .../umbrello/codegenerators/javacodegenerator.cpp | 339 ++ .../umbrello/codegenerators/javacodegenerator.h | 150 + .../umbrello/codegenerators/javacodeoperation.cpp | 132 + .../umbrello/codegenerators/javacodeoperation.h | 52 + umbrello/umbrello/codegenerators/javawriter.cpp | 936 ++++ umbrello/umbrello/codegenerators/javawriter.h | 236 + umbrello/umbrello/codegenerators/jswriter.cpp | 308 ++ umbrello/umbrello/codegenerators/jswriter.h | 78 + umbrello/umbrello/codegenerators/pascalwriter.cpp | 542 +++ umbrello/umbrello/codegenerators/pascalwriter.h | 86 + umbrello/umbrello/codegenerators/perlwriter.cpp | 716 +++ umbrello/umbrello/codegenerators/perlwriter.h | 101 + umbrello/umbrello/codegenerators/php5writer.cpp | 3418 +++++++++++++ umbrello/umbrello/codegenerators/php5writer.h | 95 + umbrello/umbrello/codegenerators/phpwriter.cpp | 3339 +++++++++++++ umbrello/umbrello/codegenerators/phpwriter.h | 90 + umbrello/umbrello/codegenerators/pythonwriter.cpp | 438 ++ umbrello/umbrello/codegenerators/pythonwriter.h | 86 + .../codegenerators/rubyclassdeclarationblock.cpp | 147 + .../codegenerators/rubyclassdeclarationblock.h | 74 + .../codegenerators/rubyclassifiercodedocument.cpp | 646 +++ .../codegenerators/rubyclassifiercodedocument.h | 126 + .../codegenerators/rubycodeaccessormethod.cpp | 233 + .../codegenerators/rubycodeaccessormethod.h | 73 + .../umbrello/codegenerators/rubycodeclassfield.cpp | 113 + .../umbrello/codegenerators/rubycodeclassfield.h | 61 + .../rubycodeclassfielddeclarationblock.cpp | 112 + .../rubycodeclassfielddeclarationblock.h | 55 + .../umbrello/codegenerators/rubycodecomment.cpp | 85 + umbrello/umbrello/codegenerators/rubycodecomment.h | 69 + .../codegenerators/rubycodedocumentation.cpp | 145 + .../codegenerators/rubycodedocumentation.h | 97 + .../codegenerators/rubycodegenerationformbase.ui | 248 + .../codegenerators/rubycodegenerationpolicy.cpp | 170 + .../codegenerators/rubycodegenerationpolicy.h | 117 + .../rubycodegenerationpolicypage.cpp | 75 + .../codegenerators/rubycodegenerationpolicypage.h | 51 + .../umbrello/codegenerators/rubycodegenerator.cpp | 186 + .../umbrello/codegenerators/rubycodegenerator.h | 129 + .../umbrello/codegenerators/rubycodeoperation.cpp | 226 + .../umbrello/codegenerators/rubycodeoperation.h | 54 + umbrello/umbrello/codegenerators/rubywriter.cpp | 448 ++ umbrello/umbrello/codegenerators/rubywriter.h | 113 + .../codegenerators/simplecodegenerator.cpp | 292 ++ .../umbrello/codegenerators/simplecodegenerator.h | 120 + umbrello/umbrello/codegenerators/sqlwriter.cpp | 394 ++ umbrello/umbrello/codegenerators/sqlwriter.h | 77 + umbrello/umbrello/codegenerators/tclwriter.cpp | 951 ++++ umbrello/umbrello/codegenerators/tclwriter.h | 174 + .../umbrello/codegenerators/xmlcodecomment.cpp | 67 + umbrello/umbrello/codegenerators/xmlcodecomment.h | 68 + .../codegenerators/xmlelementcodeblock.cpp | 166 + .../umbrello/codegenerators/xmlelementcodeblock.h | 88 + .../umbrello/codegenerators/xmlschemawriter.cpp | 809 ++++ umbrello/umbrello/codegenerators/xmlschemawriter.h | 249 + umbrello/umbrello/codegenobjectwithtextblocks.cpp | 517 ++ umbrello/umbrello/codegenobjectwithtextblocks.h | 192 + umbrello/umbrello/codeimport/Makefile.am | 6 + umbrello/umbrello/codeimport/adaimport.cpp | 588 +++ umbrello/umbrello/codeimport/adaimport.h | 88 + umbrello/umbrello/codeimport/classimport.cpp | 58 + umbrello/umbrello/codeimport/classimport.h | 61 + umbrello/umbrello/codeimport/cppimport.cpp | 109 + umbrello/umbrello/codeimport/cppimport.h | 59 + umbrello/umbrello/codeimport/idlimport.cpp | 356 ++ umbrello/umbrello/codeimport/idlimport.h | 54 + umbrello/umbrello/codeimport/import_utils.cpp | 464 ++ umbrello/umbrello/codeimport/import_utils.h | 175 + umbrello/umbrello/codeimport/javaimport.cpp | 549 +++ umbrello/umbrello/codeimport/javaimport.h | 106 + .../umbrello/codeimport/kdevcppparser/Makefile.am | 5 + umbrello/umbrello/codeimport/kdevcppparser/README | 56 + umbrello/umbrello/codeimport/kdevcppparser/ast.cpp | 1183 +++++ umbrello/umbrello/codeimport/kdevcppparser/ast.h | 1449 ++++++ .../codeimport/kdevcppparser/ast_utils.cpp | 176 + .../umbrello/codeimport/kdevcppparser/ast_utils.h | 29 + .../codeimport/kdevcppparser/cpptree2uml.cpp | 640 +++ .../codeimport/kdevcppparser/cpptree2uml.h | 98 + .../umbrello/codeimport/kdevcppparser/driver.cpp | 435 ++ .../umbrello/codeimport/kdevcppparser/driver.h | 230 + .../umbrello/codeimport/kdevcppparser/errors.cpp | 25 + .../umbrello/codeimport/kdevcppparser/errors.h | 45 + .../codeimport/kdevcppparser/keywords.lut.h | 123 + .../umbrello/codeimport/kdevcppparser/lexer.cpp | 1002 ++++ umbrello/umbrello/codeimport/kdevcppparser/lexer.h | 791 +++ .../umbrello/codeimport/kdevcppparser/lookup.cpp | 113 + .../umbrello/codeimport/kdevcppparser/lookup.h | 119 + .../umbrello/codeimport/kdevcppparser/parser.cpp | 4238 ++++++++++++++++ .../umbrello/codeimport/kdevcppparser/parser.h | 221 + .../codeimport/kdevcppparser/tree_parser.cpp | 207 + .../codeimport/kdevcppparser/tree_parser.h | 59 + .../umbrello/codeimport/kdevcppparser/urlutil.cpp | 310 ++ .../umbrello/codeimport/kdevcppparser/urlutil.h | 132 + umbrello/umbrello/codeimport/nativeimportbase.cpp | 340 ++ umbrello/umbrello/codeimport/nativeimportbase.h | 227 + umbrello/umbrello/codeimport/pascalimport.cpp | 413 ++ umbrello/umbrello/codeimport/pascalimport.h | 66 + umbrello/umbrello/codeimport/pythonimport.cpp | 190 + umbrello/umbrello/codeimport/pythonimport.h | 76 + umbrello/umbrello/codemethodblock.cpp | 191 + umbrello/umbrello/codemethodblock.h | 133 + umbrello/umbrello/codeoperation.cpp | 177 + umbrello/umbrello/codeoperation.h | 94 + umbrello/umbrello/codeparameter.cpp | 287 ++ umbrello/umbrello/codeparameter.h | 160 + umbrello/umbrello/codeviewerstate.h | 38 + umbrello/umbrello/component.cpp | 100 + umbrello/umbrello/component.h | 86 + umbrello/umbrello/componentwidget.cpp | 141 + umbrello/umbrello/componentwidget.h | 74 + umbrello/umbrello/configurable.cpp | 81 + umbrello/umbrello/configurable.h | 118 + umbrello/umbrello/cr128-mime-umbrellofile.png | Bin 0 -> 6767 bytes umbrello/umbrello/cr16-mime-umbrellofile.png | Bin 0 -> 835 bytes umbrello/umbrello/cr22-mime-umbrellofile.png | Bin 0 -> 1134 bytes umbrello/umbrello/cr32-mime-umbrellofile.png | Bin 0 -> 1717 bytes umbrello/umbrello/cr48-mime-umbrellofile.png | Bin 0 -> 2665 bytes umbrello/umbrello/cr64-mime-umbrellofile.png | Bin 0 -> 3515 bytes umbrello/umbrello/crsc-mime-umbrellofile.svgz | Bin 0 -> 2860 bytes umbrello/umbrello/datatypewidget.cpp | 114 + umbrello/umbrello/datatypewidget.h | 81 + umbrello/umbrello/dialog_utils.cpp | 60 + umbrello/umbrello/dialog_utils.h | 63 + umbrello/umbrello/dialogs/Makefile.am | 54 + umbrello/umbrello/dialogs/activitydialog.cpp | 125 + umbrello/umbrello/dialogs/activitydialog.h | 136 + umbrello/umbrello/dialogs/activitypage.cpp | 325 ++ umbrello/umbrello/dialogs/activitypage.h | 94 + umbrello/umbrello/dialogs/assocgenpage.cpp | 131 + umbrello/umbrello/dialogs/assocgenpage.h | 93 + umbrello/umbrello/dialogs/assocpage.cpp | 127 + umbrello/umbrello/dialogs/assocpage.h | 76 + umbrello/umbrello/dialogs/assocpropdlg.cpp | 115 + umbrello/umbrello/dialogs/assocpropdlg.h | 72 + umbrello/umbrello/dialogs/assocrolepage.cpp | 293 ++ umbrello/umbrello/dialogs/assocrolepage.h | 85 + umbrello/umbrello/dialogs/classgenpage.cpp | 472 ++ umbrello/umbrello/dialogs/classgenpage.h | 109 + umbrello/umbrello/dialogs/classifierlistpage.cpp | 607 +++ umbrello/umbrello/dialogs/classifierlistpage.h | 191 + umbrello/umbrello/dialogs/classoptionspage.cpp | 195 + umbrello/umbrello/dialogs/classoptionspage.h | 106 + umbrello/umbrello/dialogs/classpropdlg.cpp | 250 + umbrello/umbrello/dialogs/classpropdlg.h | 129 + umbrello/umbrello/dialogs/classwizard.cpp | 107 + umbrello/umbrello/dialogs/classwizard.h | 104 + umbrello/umbrello/dialogs/codeeditor.cpp | 1231 +++++ umbrello/umbrello/dialogs/codeeditor.h | 191 + .../umbrello/dialogs/codegenerationoptionsbase.ui | 533 ++ .../umbrello/dialogs/codegenerationoptionspage.cpp | 188 + .../umbrello/dialogs/codegenerationoptionspage.h | 74 + .../umbrello/dialogs/codegenerationpolicybase.ui | 39 + .../umbrello/dialogs/codegenerationpolicypage.cpp | 50 + .../umbrello/dialogs/codegenerationpolicypage.h | 55 + umbrello/umbrello/dialogs/codegenerationwizard.cpp | 258 + umbrello/umbrello/dialogs/codegenerationwizard.h | 88 + .../umbrello/dialogs/codegenerationwizardbase.ui | 309 ++ umbrello/umbrello/dialogs/codeviewerdialog.cpp | 122 + umbrello/umbrello/dialogs/codeviewerdialog.h | 80 + umbrello/umbrello/dialogs/codeviewerdialogbase.ui | 113 + umbrello/umbrello/dialogs/codevieweroptionsbase.ui | 369 ++ .../umbrello/dialogs/codevieweroptionspage.cpp | 70 + umbrello/umbrello/dialogs/codevieweroptionspage.h | 52 + .../umbrello/dialogs/defaultcodegenpolicypage.cpp | 41 + .../umbrello/dialogs/defaultcodegenpolicypage.h | 42 + umbrello/umbrello/dialogs/diagramprintpage.cpp | 225 + umbrello/umbrello/dialogs/diagramprintpage.h | 101 + umbrello/umbrello/dialogs/diagrampropertiespage.ui | 410 ++ umbrello/umbrello/dialogs/exportallviewsdialog.cpp | 55 + umbrello/umbrello/dialogs/exportallviewsdialog.h | 72 + .../umbrello/dialogs/exportallviewsdialogbase.ui | 216 + umbrello/umbrello/dialogs/notedialog.cpp | 53 + umbrello/umbrello/dialogs/notedialog.h | 55 + umbrello/umbrello/dialogs/overwritedialogue.cpp | 62 + umbrello/umbrello/dialogs/overwritedialogue.h | 72 + umbrello/umbrello/dialogs/parmpropdlg.cpp | 257 + umbrello/umbrello/dialogs/parmpropdlg.h | 125 + umbrello/umbrello/dialogs/pkgcontentspage.cpp | 127 + umbrello/umbrello/dialogs/pkgcontentspage.h | 62 + umbrello/umbrello/dialogs/selectopdlg.cpp | 141 + umbrello/umbrello/dialogs/selectopdlg.h | 120 + umbrello/umbrello/dialogs/settingsdlg.cpp | 443 ++ umbrello/umbrello/dialogs/settingsdlg.h | 177 + umbrello/umbrello/dialogs/statedialog.cpp | 147 + umbrello/umbrello/dialogs/statedialog.h | 144 + umbrello/umbrello/dialogs/umlattributedialog.cpp | 235 + umbrello/umbrello/dialogs/umlattributedialog.h | 81 + .../umbrello/dialogs/umlentityattributedialog.cpp | 262 + .../umbrello/dialogs/umlentityattributedialog.h | 87 + umbrello/umbrello/dialogs/umloperationdialog.cpp | 530 ++ umbrello/umbrello/dialogs/umloperationdialog.h | 135 + umbrello/umbrello/dialogs/umlroledialog.cpp | 63 + umbrello/umbrello/dialogs/umlroledialog.h | 75 + umbrello/umbrello/dialogs/umlroleproperties.cpp | 114 + umbrello/umbrello/dialogs/umlroleproperties.h | 59 + umbrello/umbrello/dialogs/umlrolepropertiesbase.ui | 203 + umbrello/umbrello/dialogs/umltemplatedialog.cpp | 161 + umbrello/umbrello/dialogs/umltemplatedialog.h | 81 + umbrello/umbrello/dialogs/umlviewdialog.cpp | 184 + umbrello/umbrello/dialogs/umlviewdialog.h | 119 + umbrello/umbrello/dialogs/umlwidgetcolorpage.cpp | 114 + umbrello/umbrello/dialogs/umlwidgetcolorpage.h | 86 + umbrello/umbrello/docgenerators/Makefile.am | 21 + umbrello/umbrello/docgenerators/common.ent | 19 + umbrello/umbrello/docgenerators/docbook2xhtml.xsl | 7 + .../umbrello/docgenerators/docbookgenerator.cpp | 145 + umbrello/umbrello/docgenerators/docbookgenerator.h | 78 + umbrello/umbrello/docgenerators/main.cpp | 94 + umbrello/umbrello/docgenerators/xhtmlgenerator.cpp | 171 + umbrello/umbrello/docgenerators/xhtmlgenerator.h | 104 + umbrello/umbrello/docgenerators/xmi.css | 170 + umbrello/umbrello/docgenerators/xmi2docbook.sh | 9 + umbrello/umbrello/docgenerators/xmi2docbook.xsl | 998 ++++ umbrello/umbrello/docwindow.cpp | 223 + umbrello/umbrello/docwindow.h | 168 + umbrello/umbrello/entity.cpp | 217 + umbrello/umbrello/entity.h | 155 + umbrello/umbrello/entityattribute.cpp | 178 + umbrello/umbrello/entityattribute.h | 183 + umbrello/umbrello/entitywidget.cpp | 203 + umbrello/umbrello/entitywidget.h | 82 + umbrello/umbrello/enum.cpp | 206 + umbrello/umbrello/enum.h | 145 + umbrello/umbrello/enumliteral.cpp | 74 + umbrello/umbrello/enumliteral.h | 85 + umbrello/umbrello/enumwidget.cpp | 218 + umbrello/umbrello/enumwidget.h | 109 + umbrello/umbrello/floatingtextwidget.cpp | 453 ++ umbrello/umbrello/floatingtextwidget.h | 303 ++ umbrello/umbrello/floatingtextwidgetcontroller.cpp | 119 + umbrello/umbrello/floatingtextwidgetcontroller.h | 152 + umbrello/umbrello/folder.cpp | 412 ++ umbrello/umbrello/folder.h | 199 + umbrello/umbrello/forkjoinwidget.cpp | 112 + umbrello/umbrello/forkjoinwidget.h | 103 + umbrello/umbrello/headings/Makefile.am | 17 + umbrello/umbrello/headings/heading.adb | 29 + umbrello/umbrello/headings/heading.ads | 30 + umbrello/umbrello/headings/heading.as | 29 + umbrello/umbrello/headings/heading.cpp | 29 + umbrello/umbrello/headings/heading.cs | 29 + umbrello/umbrello/headings/heading.d | 29 + umbrello/umbrello/headings/heading.h | 29 + umbrello/umbrello/headings/heading.idl | 29 + umbrello/umbrello/headings/heading.java | 29 + umbrello/umbrello/headings/heading.js | 29 + umbrello/umbrello/headings/heading.php | 30 + umbrello/umbrello/headings/heading.pm | 29 + umbrello/umbrello/headings/heading.py | 29 + umbrello/umbrello/headings/heading.rb | 29 + umbrello/umbrello/headings/heading.sql | 28 + umbrello/umbrello/headings/heading.xsd | 4 + umbrello/umbrello/hi128-app-umbrello.png | Bin 0 -> 4254 bytes umbrello/umbrello/hi16-app-umbrello.png | Bin 0 -> 618 bytes umbrello/umbrello/hi16-mime-umbrellofile.png | Bin 0 -> 554 bytes umbrello/umbrello/hi22-app-umbrello.png | Bin 0 -> 794 bytes umbrello/umbrello/hi32-app-umbrello.png | Bin 0 -> 1071 bytes umbrello/umbrello/hi32-mime-umbrellofile.png | Bin 0 -> 1720 bytes umbrello/umbrello/hi48-app-umbrello.png | Bin 0 -> 1625 bytes umbrello/umbrello/hi64-app-umbrello.png | Bin 0 -> 2132 bytes umbrello/umbrello/hierarchicalcodeblock.cpp | 386 ++ umbrello/umbrello/hierarchicalcodeblock.h | 155 + umbrello/umbrello/hisc-app-umbrello.svgz | Bin 0 -> 1392 bytes umbrello/umbrello/import_rose.cpp | 391 ++ umbrello/umbrello/import_rose.h | 35 + umbrello/umbrello/kplayerslideraction.cpp | 412 ++ umbrello/umbrello/kplayerslideraction.h | 202 + umbrello/umbrello/kstartuplogo.cpp | 55 + umbrello/umbrello/kstartuplogo.h | 49 + umbrello/umbrello/linepath.cpp | 957 ++++ umbrello/umbrello/linepath.h | 383 ++ umbrello/umbrello/linkwidget.cpp | 61 + umbrello/umbrello/linkwidget.h | 128 + umbrello/umbrello/listpopupmenu.cpp | 1348 ++++++ umbrello/umbrello/listpopupmenu.h | 330 ++ umbrello/umbrello/main.cpp | 208 + umbrello/umbrello/messagewidget.cpp | 792 +++ umbrello/umbrello/messagewidget.h | 401 ++ umbrello/umbrello/messagewidgetcontroller.cpp | 96 + umbrello/umbrello/messagewidgetcontroller.h | 154 + umbrello/umbrello/messagewidgetlist.h | 22 + umbrello/umbrello/model_utils.cpp | 1221 +++++ umbrello/umbrello/model_utils.h | 328 ++ umbrello/umbrello/node.cpp | 43 + umbrello/umbrello/node.h | 67 + umbrello/umbrello/nodewidget.cpp | 132 + umbrello/umbrello/nodewidget.h | 64 + umbrello/umbrello/notewidget.cpp | 316 ++ umbrello/umbrello/notewidget.h | 146 + umbrello/umbrello/notewidgetcontroller.cpp | 49 + umbrello/umbrello/notewidgetcontroller.h | 81 + umbrello/umbrello/object_factory.cpp | 283 ++ umbrello/umbrello/object_factory.h | 84 + umbrello/umbrello/objectwidget.cpp | 403 ++ umbrello/umbrello/objectwidget.h | 331 ++ umbrello/umbrello/objectwidgetcontroller.cpp | 44 + umbrello/umbrello/objectwidgetcontroller.h | 92 + umbrello/umbrello/operation.cpp | 431 ++ umbrello/umbrello/operation.h | 209 + umbrello/umbrello/optionstate.cpp | 27 + umbrello/umbrello/optionstate.h | 81 + umbrello/umbrello/ownedcodeblock.cpp | 180 + umbrello/umbrello/ownedcodeblock.h | 100 + umbrello/umbrello/ownedhierarchicalcodeblock.cpp | 113 + umbrello/umbrello/ownedhierarchicalcodeblock.h | 98 + umbrello/umbrello/package.cpp | 298 ++ umbrello/umbrello/package.h | 198 + umbrello/umbrello/packagewidget.cpp | 133 + umbrello/umbrello/packagewidget.h | 74 + umbrello/umbrello/petalnode.cpp | 61 + umbrello/umbrello/petalnode.h | 86 + umbrello/umbrello/petaltree2uml.cpp | 631 +++ umbrello/umbrello/petaltree2uml.h | 39 + umbrello/umbrello/pics/COPYING | 3 + umbrello/umbrello/pics/CVglobal_meth.png | Bin 0 -> 342 bytes umbrello/umbrello/pics/CVglobal_var.png | Bin 0 -> 313 bytes umbrello/umbrello/pics/CVimplementation_meth.png | Bin 0 -> 372 bytes umbrello/umbrello/pics/CVimplementation_signal.png | Bin 0 -> 391 bytes umbrello/umbrello/pics/CVimplementation_slot.png | Bin 0 -> 392 bytes umbrello/umbrello/pics/CVimplementation_var.png | Bin 0 -> 309 bytes umbrello/umbrello/pics/CVnamespace.png | Bin 0 -> 451 bytes umbrello/umbrello/pics/CVprivate_meth.png | Bin 0 -> 347 bytes umbrello/umbrello/pics/CVprivate_signal.png | Bin 0 -> 354 bytes umbrello/umbrello/pics/CVprivate_slot.png | Bin 0 -> 356 bytes umbrello/umbrello/pics/CVprivate_var.png | Bin 0 -> 314 bytes umbrello/umbrello/pics/CVprotected_meth.png | Bin 0 -> 346 bytes umbrello/umbrello/pics/CVprotected_signal.png | Bin 0 -> 352 bytes umbrello/umbrello/pics/CVprotected_slot.png | Bin 0 -> 353 bytes umbrello/umbrello/pics/CVprotected_var.png | Bin 0 -> 321 bytes umbrello/umbrello/pics/CVpublic_meth.png | Bin 0 -> 320 bytes umbrello/umbrello/pics/CVpublic_signal.png | Bin 0 -> 327 bytes umbrello/umbrello/pics/CVpublic_slot.png | Bin 0 -> 329 bytes umbrello/umbrello/pics/CVpublic_var.png | Bin 0 -> 293 bytes umbrello/umbrello/pics/CVstruct.png | Bin 0 -> 355 bytes umbrello/umbrello/pics/Makefile.am | 112 + umbrello/umbrello/pics/actor.png | Bin 0 -> 1081 bytes umbrello/umbrello/pics/aggregation.png | Bin 0 -> 439 bytes umbrello/umbrello/pics/align_bottom.png | Bin 0 -> 247 bytes umbrello/umbrello/pics/align_hori_distribute.png | Bin 0 -> 297 bytes umbrello/umbrello/pics/align_hori_middle.png | Bin 0 -> 295 bytes umbrello/umbrello/pics/align_left.png | Bin 0 -> 248 bytes umbrello/umbrello/pics/align_right.png | Bin 0 -> 249 bytes umbrello/umbrello/pics/align_top.png | Bin 0 -> 246 bytes umbrello/umbrello/pics/align_vert_distribute.png | Bin 0 -> 305 bytes umbrello/umbrello/pics/align_vert_middle.png | Bin 0 -> 289 bytes umbrello/umbrello/pics/anchor.png | Bin 0 -> 859 bytes umbrello/umbrello/pics/andline.png | Bin 0 -> 788 bytes umbrello/umbrello/pics/arrow.png | Bin 0 -> 436 bytes umbrello/umbrello/pics/artifact.png | Bin 0 -> 726 bytes umbrello/umbrello/pics/association.png | Bin 0 -> 167 bytes umbrello/umbrello/pics/box.png | Bin 0 -> 184 bytes umbrello/umbrello/pics/branch.png | Bin 0 -> 863 bytes umbrello/umbrello/pics/choice-rhomb.png | Bin 0 -> 894 bytes umbrello/umbrello/pics/choice-round.png | Bin 0 -> 971 bytes umbrello/umbrello/pics/class.png | Bin 0 -> 827 bytes umbrello/umbrello/pics/component.png | Bin 0 -> 577 bytes umbrello/umbrello/pics/composition.png | Bin 0 -> 540 bytes umbrello/umbrello/pics/containment.png | Bin 0 -> 447 bytes .../pics/cr16-action-umbrello_diagram_activity.png | Bin 0 -> 789 bytes .../pics/cr16-action-umbrello_diagram_class.png | Bin 0 -> 764 bytes .../cr16-action-umbrello_diagram_collaboration.png | Bin 0 -> 799 bytes .../cr16-action-umbrello_diagram_component.png | Bin 0 -> 754 bytes .../cr16-action-umbrello_diagram_deployment.png | Bin 0 -> 815 bytes .../pics/cr16-action-umbrello_diagram_sequence.png | Bin 0 -> 821 bytes .../pics/cr16-action-umbrello_diagram_state.png | Bin 0 -> 816 bytes .../pics/cr16-action-umbrello_diagram_usecase.png | Bin 0 -> 823 bytes .../pics/cr22-action-umbrello_diagram_activity.png | Bin 0 -> 1211 bytes .../pics/cr22-action-umbrello_diagram_class.png | Bin 0 -> 1124 bytes .../cr22-action-umbrello_diagram_collaboration.png | Bin 0 -> 1169 bytes .../cr22-action-umbrello_diagram_component.png | Bin 0 -> 1090 bytes .../cr22-action-umbrello_diagram_deployment.png | Bin 0 -> 1177 bytes ...-action-umbrello_diagram_entityrelationship.png | Bin 0 -> 1039 bytes .../pics/cr22-action-umbrello_diagram_sequence.png | Bin 0 -> 1225 bytes .../pics/cr22-action-umbrello_diagram_state.png | Bin 0 -> 1258 bytes .../pics/cr22-action-umbrello_diagram_usecase.png | Bin 0 -> 1247 bytes umbrello/umbrello/pics/cursor-actor.png | Bin 0 -> 1055 bytes umbrello/umbrello/pics/cursor-aggregation.png | Bin 0 -> 880 bytes umbrello/umbrello/pics/cursor-anchor.png | Bin 0 -> 1157 bytes umbrello/umbrello/pics/cursor-andline.png | Bin 0 -> 1018 bytes umbrello/umbrello/pics/cursor-artifact.png | Bin 0 -> 813 bytes umbrello/umbrello/pics/cursor-association.png | Bin 0 -> 775 bytes umbrello/umbrello/pics/cursor-box.png | Bin 0 -> 716 bytes umbrello/umbrello/pics/cursor-branch.png | Bin 0 -> 1304 bytes umbrello/umbrello/pics/cursor-choice-rhomb.png | Bin 0 -> 1179 bytes umbrello/umbrello/pics/cursor-choice-round.png | Bin 0 -> 1042 bytes umbrello/umbrello/pics/cursor-class.png | Bin 0 -> 852 bytes umbrello/umbrello/pics/cursor-component.png | Bin 0 -> 678 bytes umbrello/umbrello/pics/cursor-composition.png | Bin 0 -> 990 bytes umbrello/umbrello/pics/cursor-containment.png | Bin 0 -> 888 bytes umbrello/umbrello/pics/cursor-datatype.png | Bin 0 -> 1080 bytes umbrello/umbrello/pics/cursor-deep-history.png | Bin 0 -> 826 bytes umbrello/umbrello/pics/cursor-dependency.png | Bin 0 -> 1089 bytes umbrello/umbrello/pics/cursor-end_state.png | Bin 0 -> 1250 bytes umbrello/umbrello/pics/cursor-entity.png | Bin 0 -> 878 bytes umbrello/umbrello/pics/cursor-enum.png | Bin 0 -> 1050 bytes umbrello/umbrello/pics/cursor-fork.png | Bin 0 -> 860 bytes umbrello/umbrello/pics/cursor-generalisation.png | Bin 0 -> 890 bytes umbrello/umbrello/pics/cursor-initial_state.png | Bin 0 -> 1105 bytes umbrello/umbrello/pics/cursor-interface.png | Bin 0 -> 1056 bytes umbrello/umbrello/pics/cursor-join.png | Bin 0 -> 442 bytes umbrello/umbrello/pics/cursor-junction.png | Bin 0 -> 606 bytes .../umbrello/pics/cursor-message-asynchronous.png | Bin 0 -> 1065 bytes .../umbrello/pics/cursor-message-synchronous.png | Bin 0 -> 1014 bytes umbrello/umbrello/pics/cursor-node.png | Bin 0 -> 1137 bytes umbrello/umbrello/pics/cursor-note.png | Bin 0 -> 1161 bytes umbrello/umbrello/pics/cursor-object.png | Bin 0 -> 1013 bytes umbrello/umbrello/pics/cursor-package.png | Bin 0 -> 1067 bytes umbrello/umbrello/pics/cursor-relationship.png | Bin 0 -> 935 bytes umbrello/umbrello/pics/cursor-shallow-history.png | Bin 0 -> 762 bytes umbrello/umbrello/pics/cursor-state-fork.png | Bin 0 -> 390 bytes umbrello/umbrello/pics/cursor-text.png | Bin 0 -> 1049 bytes umbrello/umbrello/pics/cursor-uniassociation.png | Bin 0 -> 888 bytes umbrello/umbrello/pics/cursor-usecase.png | Bin 0 -> 1189 bytes umbrello/umbrello/pics/datatype.png | Bin 0 -> 752 bytes umbrello/umbrello/pics/deep-history.png | Bin 0 -> 703 bytes umbrello/umbrello/pics/dependency.png | Bin 0 -> 335 bytes umbrello/umbrello/pics/end_state.png | Bin 0 -> 1186 bytes umbrello/umbrello/pics/entity.png | Bin 0 -> 827 bytes umbrello/umbrello/pics/enum.png | Bin 0 -> 999 bytes umbrello/umbrello/pics/fork.png | Bin 0 -> 213 bytes umbrello/umbrello/pics/generalisation.png | Bin 0 -> 346 bytes umbrello/umbrello/pics/initial_state.png | Bin 0 -> 893 bytes umbrello/umbrello/pics/interface.png | Bin 0 -> 883 bytes umbrello/umbrello/pics/join.png | Bin 0 -> 281 bytes umbrello/umbrello/pics/junction.png | Bin 0 -> 412 bytes umbrello/umbrello/pics/message-asynchronous.png | Bin 0 -> 556 bytes umbrello/umbrello/pics/message-synchronous.png | Bin 0 -> 594 bytes umbrello/umbrello/pics/node.png | Bin 0 -> 1071 bytes umbrello/umbrello/pics/note.png | Bin 0 -> 939 bytes umbrello/umbrello/pics/object.png | Bin 0 -> 727 bytes umbrello/umbrello/pics/package.png | Bin 0 -> 939 bytes umbrello/umbrello/pics/relationship.png | Bin 0 -> 297 bytes umbrello/umbrello/pics/shallow-history.png | Bin 0 -> 669 bytes umbrello/umbrello/pics/sources/actor.svg | 172 + umbrello/umbrello/pics/sources/aggregation.svg | 130 + umbrello/umbrello/pics/sources/align_bottom.svg | 261 + .../pics/sources/align_hori_distribute.svg | 273 ++ .../umbrello/pics/sources/align_hori_middle.svg | 257 + umbrello/umbrello/pics/sources/align_left.svg | 260 + umbrello/umbrello/pics/sources/align_right.svg | 260 + umbrello/umbrello/pics/sources/align_top.svg | 251 + .../pics/sources/align_vert_distribute.svg | 272 ++ .../umbrello/pics/sources/align_vert_middle.svg | 254 + umbrello/umbrello/pics/sources/anchor.svg | 279 ++ umbrello/umbrello/pics/sources/andline.svg | 102 + umbrello/umbrello/pics/sources/artifact.svg | 153 + umbrello/umbrello/pics/sources/association.svg | 115 + umbrello/umbrello/pics/sources/asynchro.svg | 133 + umbrello/umbrello/pics/sources/box.svg | 85 + umbrello/umbrello/pics/sources/branch.svg | 128 + umbrello/umbrello/pics/sources/choice-rhomb.svg | 160 + umbrello/umbrello/pics/sources/choice-round.svg | 178 + umbrello/umbrello/pics/sources/class.svg | 131 + umbrello/umbrello/pics/sources/component.svg | 123 + umbrello/umbrello/pics/sources/composition.svg | 128 + umbrello/umbrello/pics/sources/containment.svg | 141 + umbrello/umbrello/pics/sources/cursor-andline.svg | 496 ++ .../umbrello/pics/sources/cursor-choice-rhomb.svg | 439 ++ .../umbrello/pics/sources/cursor-choice-round.svg | 356 ++ .../umbrello/pics/sources/cursor-deep-history.svg | 206 + umbrello/umbrello/pics/sources/cursor-join.svg | 289 ++ umbrello/umbrello/pics/sources/cursor-junction.svg | 279 ++ .../pics/sources/cursor-shallow-history.svg | 195 + .../umbrello/pics/sources/cursor-state-fork.svg | 289 ++ umbrello/umbrello/pics/sources/datatype.svg | 121 + umbrello/umbrello/pics/sources/deep-history.svg | 84 + umbrello/umbrello/pics/sources/dependency.svg | 216 + umbrello/umbrello/pics/sources/diag_activity.svg | 571 +++ umbrello/umbrello/pics/sources/diag_class.svg | 360 ++ .../umbrello/pics/sources/diag_collaboration.svg | 1111 +++++ umbrello/umbrello/pics/sources/diag_component.svg | 732 +++ umbrello/umbrello/pics/sources/diag_deployment.svg | 676 +++ .../pics/sources/diag_entityrelationship.svg | 715 +++ umbrello/umbrello/pics/sources/diag_sequence.svg | 729 +++ umbrello/umbrello/pics/sources/diag_state.svg | 629 +++ umbrello/umbrello/pics/sources/diag_usecase.svg | 489 ++ umbrello/umbrello/pics/sources/diagbase.svg | 189 + umbrello/umbrello/pics/sources/end_state.svg | 136 + umbrello/umbrello/pics/sources/entity.svg | 151 + umbrello/umbrello/pics/sources/enum.svg | 114 + umbrello/umbrello/pics/sources/fork.svg | 115 + umbrello/umbrello/pics/sources/generalise.svg | 127 + umbrello/umbrello/pics/sources/initial.svg | 126 + umbrello/umbrello/pics/sources/interface.svg | 130 + umbrello/umbrello/pics/sources/join.svg | 175 + umbrello/umbrello/pics/sources/junction.svg | 256 + .../umbrello/pics/sources/message-asynchronous.svg | 133 + .../umbrello/pics/sources/message-synchronous.svg | 169 + umbrello/umbrello/pics/sources/node.svg | 114 + umbrello/umbrello/pics/sources/note.svg | 164 + umbrello/umbrello/pics/sources/object.svg | 110 + umbrello/umbrello/pics/sources/package.svg | 121 + umbrello/umbrello/pics/sources/relationship.svg | 243 + umbrello/umbrello/pics/sources/shallow-history.svg | 73 + umbrello/umbrello/pics/sources/state-fork.svg | 172 + umbrello/umbrello/pics/sources/subsystem.svg | 135 + umbrello/umbrello/pics/sources/template.svg | 146 + umbrello/umbrello/pics/sources/text.svg | 91 + umbrello/umbrello/pics/sources/uniassociation.svg | 221 + umbrello/umbrello/pics/sources/usecase.svg | 131 + umbrello/umbrello/pics/startlogo.png | Bin 0 -> 19738 bytes umbrello/umbrello/pics/state-fork.png | Bin 0 -> 324 bytes umbrello/umbrello/pics/subsystem.png | Bin 0 -> 629 bytes umbrello/umbrello/pics/template.png | Bin 0 -> 577 bytes umbrello/umbrello/pics/text.png | Bin 0 -> 484 bytes umbrello/umbrello/pics/uniassociation.png | Bin 0 -> 307 bytes umbrello/umbrello/pics/usecase.png | Bin 0 -> 751 bytes umbrello/umbrello/plugin.cpp | 167 + umbrello/umbrello/plugin.h | 163 + umbrello/umbrello/pluginloader.cpp | 177 + umbrello/umbrello/pluginloader.h | 132 + umbrello/umbrello/refactoring/Makefile.am | 8 + .../umbrello/refactoring/refactoringassistant.cpp | 701 +++ .../umbrello/refactoring/refactoringassistant.h | 89 + umbrello/umbrello/seqlinewidget.cpp | 124 + umbrello/umbrello/seqlinewidget.h | 135 + umbrello/umbrello/statewidget.cpp | 297 ++ umbrello/umbrello/statewidget.h | 163 + umbrello/umbrello/stereotype.cpp | 94 + umbrello/umbrello/stereotype.h | 103 + umbrello/umbrello/template.cpp | 95 + umbrello/umbrello/template.h | 107 + umbrello/umbrello/textblock.cpp | 320 ++ umbrello/umbrello/textblock.h | 241 + umbrello/umbrello/textblocklist.h | 23 + umbrello/umbrello/tips | 129 + umbrello/umbrello/toolbarstate.cpp | 260 + umbrello/umbrello/toolbarstate.h | 365 ++ umbrello/umbrello/toolbarstatearrow.cpp | 134 + umbrello/umbrello/toolbarstatearrow.h | 158 + umbrello/umbrello/toolbarstateassociation.cpp | 232 + umbrello/umbrello/toolbarstateassociation.h | 168 + umbrello/umbrello/toolbarstatefactory.cpp | 96 + umbrello/umbrello/toolbarstatefactory.h | 53 + umbrello/umbrello/toolbarstatemessages.cpp | 169 + umbrello/umbrello/toolbarstatemessages.h | 187 + umbrello/umbrello/toolbarstateother.cpp | 163 + umbrello/umbrello/toolbarstateother.h | 83 + umbrello/umbrello/toolbarstatepool.cpp | 30 + umbrello/umbrello/toolbarstatepool.h | 71 + umbrello/umbrello/umbrello.desktop | 57 + umbrello/umbrello/umbrelloui.rc | 58 + umbrello/umbrello/uml.cpp | 1708 +++++++ umbrello/umbrello/uml.h | 1026 ++++ umbrello/umbrello/umlassociationlist.h | 23 + umbrello/umbrello/umlattributelist.cpp | 39 + umbrello/umbrello/umlattributelist.h | 43 + umbrello/umbrello/umlcanvasobject.cpp | 322 ++ umbrello/umbrello/umlcanvasobject.h | 249 + umbrello/umbrello/umlclassifierlist.h | 23 + umbrello/umbrello/umlclassifierlistitemlist.cpp | 42 + umbrello/umbrello/umlclassifierlistitemlist.h | 45 + umbrello/umbrello/umldoc.cpp | 2356 +++++++++ umbrello/umbrello/umldoc.h | 920 ++++ umbrello/umbrello/umlentityattributelist.cpp | 38 + umbrello/umbrello/umlentityattributelist.h | 44 + umbrello/umbrello/umlenumliterallist.h | 23 + umbrello/umbrello/umllistview.cpp | 2703 +++++++++++ umbrello/umbrello/umllistview.h | 473 ++ umbrello/umbrello/umllistviewitem.cpp | 696 +++ umbrello/umbrello/umllistviewitem.h | 285 ++ umbrello/umbrello/umllistviewitemlist.h | 29 + umbrello/umbrello/umlnamespace.cpp | 76 + umbrello/umbrello/umlnamespace.h | 353 ++ umbrello/umbrello/umlobject.cpp | 753 +++ umbrello/umbrello/umlobject.h | 495 ++ umbrello/umbrello/umlobjectlist.cpp | 42 + umbrello/umbrello/umlobjectlist.h | 53 + umbrello/umbrello/umloperationlist.h | 23 + umbrello/umbrello/umlpackagelist.h | 22 + umbrello/umbrello/umlrole.cpp | 339 ++ umbrello/umbrello/umlrole.h | 128 + umbrello/umbrello/umlstereotypelist.h | 23 + umbrello/umbrello/umltemplatelist.h | 23 + umbrello/umbrello/umlview.cpp | 3352 +++++++++++++ umbrello/umbrello/umlview.h | 1255 +++++ umbrello/umbrello/umlviewcanvas.cpp | 41 + umbrello/umbrello/umlviewcanvas.h | 48 + umbrello/umbrello/umlviewimageexporter.cpp | 128 + umbrello/umbrello/umlviewimageexporter.h | 122 + umbrello/umbrello/umlviewimageexporterall.cpp | 74 + umbrello/umbrello/umlviewimageexporterall.h | 58 + umbrello/umbrello/umlviewimageexportermodel.cpp | 366 ++ umbrello/umbrello/umlviewimageexportermodel.h | 214 + umbrello/umbrello/umlviewlist.h | 29 + umbrello/umbrello/umlwidget.cpp | 1025 ++++ umbrello/umbrello/umlwidget.h | 728 +++ umbrello/umbrello/umlwidgetcontroller.cpp | 540 +++ umbrello/umbrello/umlwidgetcontroller.h | 476 ++ umbrello/umbrello/umlwidgetlist.h | 29 + umbrello/umbrello/uniqueid.cpp | 57 + umbrello/umbrello/uniqueid.h | 50 + umbrello/umbrello/usecase.cpp | 40 + umbrello/umbrello/usecase.h | 63 + umbrello/umbrello/usecasewidget.cpp | 72 + umbrello/umbrello/usecasewidget.h | 74 + umbrello/umbrello/widget_factory.cpp | 241 + umbrello/umbrello/widget_factory.h | 41 + umbrello/umbrello/widget_utils.cpp | 110 + umbrello/umbrello/widget_utils.h | 52 + umbrello/umbrello/widgetbase.cpp | 136 + umbrello/umbrello/widgetbase.h | 201 + umbrello/umbrello/worktoolbar.cpp | 315 ++ umbrello/umbrello/worktoolbar.h | 183 + umbrello/umbrello/x-umbrello.desktop | 53 + umbrello/uml.kdevprj | 2154 +++++++++ umbrello/uml.lsm | 14 + 2490 files changed, 411231 insertions(+) create mode 100644 COPYING create mode 100644 COPYING-DOCS create mode 100644 Makefile.am.in create mode 100644 Makefile.cvs create mode 100644 README create mode 100644 cervisia/COPYING create mode 100644 cervisia/ChangeLog create mode 100644 cervisia/HACKING create mode 100644 cervisia/Makefile.am create mode 100644 cervisia/README create mode 100644 cervisia/TODO create mode 100644 cervisia/addremovedlg.cpp create mode 100644 cervisia/addremovedlg.h create mode 100644 cervisia/addrepositorydlg.cpp create mode 100644 cervisia/addrepositorydlg.h create mode 100644 cervisia/annotatectl.cpp create mode 100644 cervisia/annotatectl.h create mode 100644 cervisia/annotatedlg.cpp create mode 100644 cervisia/annotatedlg.h create mode 100644 cervisia/annotateview.cpp create mode 100644 cervisia/annotateview.h create mode 100644 cervisia/cervisia-change_repos_list.pl create mode 100644 cervisia/cervisia-normalize_cvsroot.pl create mode 100644 cervisia/cervisia.1.in create mode 100644 cervisia/cervisia.desktop create mode 100644 cervisia/cervisia.pod create mode 100644 cervisia/cervisia.upd create mode 100644 cervisia/cervisiapart.cpp create mode 100644 cervisia/cervisiapart.h create mode 100644 cervisia/cervisiapart.kcfg create mode 100644 cervisia/cervisiasettings.kcfgc create mode 100644 cervisia/cervisiashell.cpp create mode 100644 cervisia/cervisiashell.h create mode 100644 cervisia/cervisiashellui.rc create mode 100644 cervisia/cervisiaui.rc create mode 100644 cervisia/change_colors.pl create mode 100644 cervisia/changelogdlg.cpp create mode 100644 cervisia/changelogdlg.h create mode 100644 cervisia/checkoutdlg.cpp create mode 100644 cervisia/checkoutdlg.h create mode 100644 cervisia/commitdlg.cpp create mode 100644 cervisia/commitdlg.h create mode 100644 cervisia/cvsdir.cpp create mode 100644 cervisia/cvsdir.h create mode 100644 cervisia/cvsinitdlg.cpp create mode 100644 cervisia/cvsinitdlg.h create mode 100644 cervisia/cvsservice/DESIGN create mode 100644 cervisia/cvsservice/Makefile.am create mode 100644 cervisia/cvsservice/TODO create mode 100644 cervisia/cvsservice/cvsaskpass.cpp create mode 100644 cervisia/cvsservice/cvsjob.cpp create mode 100644 cervisia/cvsservice/cvsjob.h create mode 100644 cervisia/cvsservice/cvsloginjob.cpp create mode 100644 cervisia/cvsservice/cvsloginjob.h create mode 100644 cervisia/cvsservice/cvsservice.cpp create mode 100644 cervisia/cvsservice/cvsservice.desktop create mode 100644 cervisia/cvsservice/cvsservice.h create mode 100644 cervisia/cvsservice/cvsserviceutils.cpp create mode 100644 cervisia/cvsservice/cvsserviceutils.h create mode 100644 cervisia/cvsservice/main.cpp create mode 100644 cervisia/cvsservice/repository.cpp create mode 100644 cervisia/cvsservice/repository.h create mode 100644 cervisia/cvsservice/sshagent.cpp create mode 100644 cervisia/cvsservice/sshagent.h create mode 100644 cervisia/diffdlg.cpp create mode 100644 cervisia/diffdlg.h create mode 100644 cervisia/diffview.cpp create mode 100644 cervisia/diffview.h create mode 100644 cervisia/dirignorelist.cpp create mode 100644 cervisia/dirignorelist.h create mode 100644 cervisia/editwithmenu.cpp create mode 100644 cervisia/editwithmenu.h create mode 100644 cervisia/entry.cpp create mode 100644 cervisia/entry.h create mode 100644 cervisia/entry_status.cpp create mode 100644 cervisia/entry_status.h create mode 100644 cervisia/entry_status_change.h create mode 100644 cervisia/eventsrc create mode 100644 cervisia/globalignorelist.cpp create mode 100644 cervisia/globalignorelist.h create mode 100644 cervisia/hi16-app-cervisia.png create mode 100644 cervisia/hi22-app-cervisia.png create mode 100644 cervisia/hi32-app-cervisia.png create mode 100644 cervisia/hi48-app-cervisia.png create mode 100644 cervisia/historydlg.cpp create mode 100644 cervisia/historydlg.h create mode 100644 cervisia/ignorelistbase.cpp create mode 100644 cervisia/ignorelistbase.h create mode 100644 cervisia/logdlg.cpp create mode 100644 cervisia/logdlg.h create mode 100644 cervisia/loginfo.cpp create mode 100644 cervisia/loginfo.h create mode 100644 cervisia/loglist.cpp create mode 100644 cervisia/loglist.h create mode 100644 cervisia/logmessageedit.cpp create mode 100644 cervisia/logmessageedit.h create mode 100644 cervisia/logplainview.cpp create mode 100644 cervisia/logplainview.h create mode 100644 cervisia/logtree.cpp create mode 100644 cervisia/logtree.h create mode 100644 cervisia/main.cpp create mode 100644 cervisia/mergedlg.cpp create mode 100644 cervisia/mergedlg.h create mode 100644 cervisia/misc.cpp create mode 100644 cervisia/misc.h create mode 100644 cervisia/move_repositories.pl create mode 100644 cervisia/overview.h create mode 100644 cervisia/patchoptiondlg.cpp create mode 100644 cervisia/patchoptiondlg.h create mode 100644 cervisia/pics/Makefile.am create mode 100644 cervisia/pics/README create mode 100644 cervisia/pics/cr16-action-vcs_add.png create mode 100644 cervisia/pics/cr16-action-vcs_commit.png create mode 100644 cervisia/pics/cr16-action-vcs_diff.png create mode 100644 cervisia/pics/cr16-action-vcs_remove.png create mode 100644 cervisia/pics/cr16-action-vcs_status.png create mode 100644 cervisia/pics/cr16-action-vcs_update.png create mode 100644 cervisia/pics/cr22-action-vcs_add.png create mode 100644 cervisia/pics/cr22-action-vcs_commit.png create mode 100644 cervisia/pics/cr22-action-vcs_diff.png create mode 100644 cervisia/pics/cr22-action-vcs_remove.png create mode 100644 cervisia/pics/cr22-action-vcs_status.png create mode 100644 cervisia/pics/cr22-action-vcs_update.png create mode 100644 cervisia/pics/cr32-action-vcs_add.png create mode 100644 cervisia/pics/cr32-action-vcs_commit.png create mode 100644 cervisia/pics/cr32-action-vcs_diff.png create mode 100644 cervisia/pics/cr32-action-vcs_remove.png create mode 100644 cervisia/pics/cr32-action-vcs_status.png create mode 100644 cervisia/pics/cr32-action-vcs_update.png create mode 100644 cervisia/pics/cr48-action-vcs_add.png create mode 100644 cervisia/pics/cr48-action-vcs_commit.png create mode 100644 cervisia/pics/cr48-action-vcs_diff.png create mode 100644 cervisia/pics/cr48-action-vcs_remove.png create mode 100644 cervisia/pics/cr48-action-vcs_status.png create mode 100644 cervisia/pics/cr48-action-vcs_update.png create mode 100644 cervisia/pics/crsc-action-vcs_add.svgz create mode 100644 cervisia/pics/crsc-action-vcs_commit.svgz create mode 100644 cervisia/pics/crsc-action-vcs_diff.svgz create mode 100644 cervisia/pics/crsc-action-vcs_remove.svgz create mode 100644 cervisia/pics/crsc-action-vcs_status.svgz create mode 100644 cervisia/pics/crsc-action-vcs_update.svgz create mode 100644 cervisia/progressdlg.cpp create mode 100644 cervisia/progressdlg.h create mode 100644 cervisia/protocolview.cpp create mode 100644 cervisia/protocolview.h create mode 100644 cervisia/qttableview.cpp create mode 100644 cervisia/qttableview.h create mode 100644 cervisia/repositories.cpp create mode 100644 cervisia/repositories.h create mode 100644 cervisia/repositorydlg.cpp create mode 100644 cervisia/repositorydlg.h create mode 100644 cervisia/resolvedlg.cpp create mode 100644 cervisia/resolvedlg.h create mode 100644 cervisia/resolvedlg_p.cpp create mode 100644 cervisia/resolvedlg_p.h create mode 100644 cervisia/settingsdlg.cpp create mode 100644 cervisia/settingsdlg.h create mode 100644 cervisia/settingsdlg_advanced.ui create mode 100644 cervisia/stringmatcher.cpp create mode 100644 cervisia/stringmatcher.h create mode 100644 cervisia/tagdlg.cpp create mode 100644 cervisia/tagdlg.h create mode 100644 cervisia/tests/resolvedlg-BR74903-test-01.txt create mode 100644 cervisia/tests/resolvedlg-conflict-test-01.txt create mode 100644 cervisia/tests/resolvedlg-conflict-test-02.txt create mode 100644 cervisia/tooltip.cpp create mode 100644 cervisia/tooltip.h create mode 100644 cervisia/updatedlg.cpp create mode 100644 cervisia/updatedlg.h create mode 100644 cervisia/updateview.cpp create mode 100644 cervisia/updateview.h create mode 100644 cervisia/updateview_items.cpp create mode 100644 cervisia/updateview_items.h create mode 100644 cervisia/updateview_visitors.cpp create mode 100644 cervisia/updateview_visitors.h create mode 100644 cervisia/version.h create mode 100644 cervisia/watchdlg.cpp create mode 100644 cervisia/watchdlg.h create mode 100644 cervisia/watchersdlg.cpp create mode 100644 cervisia/watchersdlg.h create mode 100644 configure.in.in create mode 100644 doc/Makefile.am create mode 100644 doc/cervisia/Makefile.am create mode 100644 doc/cervisia/annotate.png create mode 100644 doc/cervisia/checkout.png create mode 100644 doc/cervisia/commit.png create mode 100644 doc/cervisia/diff.png create mode 100644 doc/cervisia/history.png create mode 100644 doc/cervisia/import.png create mode 100644 doc/cervisia/index.docbook create mode 100644 doc/cervisia/logtree.png create mode 100644 doc/cervisia/mainview.png create mode 100644 doc/cervisia/patch.png create mode 100644 doc/cervisia/popup.png create mode 100644 doc/cervisia/repositories.png create mode 100644 doc/cervisia/resolve.png create mode 100644 doc/cervisia/updatetag.png create mode 100644 doc/kapptemplate/Makefile.am create mode 100644 doc/kapptemplate/man-kapptemplate.1.docbook create mode 100644 doc/kbabel/Makefile.am create mode 100644 doc/kbabel/TODO create mode 100644 doc/kbabel/back.png create mode 100644 doc/kbabel/bottom.png create mode 100644 doc/kbabel/catalogmanager.png create mode 100644 doc/kbabel/catalogmanager_broken.png create mode 100644 doc/kbabel/catalogmanager_missing.png create mode 100644 doc/kbabel/catalogmanager_needwork.png create mode 100644 doc/kbabel/catalogmanager_nopot.png create mode 100644 doc/kbabel/catalogmanager_nopot_ok.png create mode 100644 doc/kbabel/catalogmanager_ok.png create mode 100644 doc/kbabel/catalogmanager_reload.png create mode 100644 doc/kbabel/catman.docbook create mode 100644 doc/kbabel/dbcan.png create mode 100644 doc/kbabel/dictionaries.docbook create mode 100644 doc/kbabel/editcopy.png create mode 100644 doc/kbabel/editcut.png create mode 100644 doc/kbabel/editpaste.png create mode 100644 doc/kbabel/faq.docbook create mode 100644 doc/kbabel/fileopen.png create mode 100644 doc/kbabel/filesave.png create mode 100644 doc/kbabel/find.png create mode 100644 doc/kbabel/forward.png create mode 100644 doc/kbabel/glossary.docbook create mode 100644 doc/kbabel/index.docbook create mode 100644 doc/kbabel/kbabeldict.docbook create mode 100644 doc/kbabel/man-catalogmanager.1.docbook create mode 100644 doc/kbabel/menu.docbook create mode 100644 doc/kbabel/msgid2msgstr.png create mode 100644 doc/kbabel/next.png create mode 100644 doc/kbabel/nexterror.png create mode 100644 doc/kbabel/nextfuzzy.png create mode 100644 doc/kbabel/nextfuzzyuntrans.png create mode 100644 doc/kbabel/nextuntranslated.png create mode 100644 doc/kbabel/pref_diff.png create mode 100644 doc/kbabel/pref_edit_appearance.png create mode 100644 doc/kbabel/pref_edit_general.png create mode 100644 doc/kbabel/pref_fonts.png create mode 100644 doc/kbabel/pref_proj_catman.png create mode 100644 doc/kbabel/pref_proj_diff.png create mode 100644 doc/kbabel/pref_proj_file_commands.png create mode 100644 doc/kbabel/pref_proj_folder_commands.png create mode 100644 doc/kbabel/pref_proj_source.png create mode 100644 doc/kbabel/pref_search.png create mode 100644 doc/kbabel/pref_wizard_page1.png create mode 100644 doc/kbabel/pref_wizard_page2.png create mode 100644 doc/kbabel/preferences.docbook create mode 100644 doc/kbabel/preverror.png create mode 100644 doc/kbabel/prevfuzzy.png create mode 100644 doc/kbabel/prevfuzzyuntrans.png create mode 100644 doc/kbabel/previous.png create mode 100644 doc/kbabel/prevuntranslated.png create mode 100644 doc/kbabel/redo.png create mode 100644 doc/kbabel/roughtranslation.png create mode 100644 doc/kbabel/snap1.png create mode 100644 doc/kbabel/snap_catalogmanager.png create mode 100644 doc/kbabel/snap_kbabeldict.png create mode 100644 doc/kbabel/snap_kbabeldict2.png create mode 100644 doc/kbabel/stop.png create mode 100644 doc/kbabel/top.png create mode 100644 doc/kbabel/transsearch.png create mode 100644 doc/kbabel/undo.png create mode 100644 doc/kbabel/using.docbook create mode 100644 doc/kbugbuster/Makefile.am create mode 100644 doc/kbugbuster/index.docbook create mode 100644 doc/kcachegrind/Makefile.am create mode 100644 doc/kcachegrind/index.docbook create mode 100644 doc/kompare/Makefile.am create mode 100644 doc/kompare/index.docbook create mode 100644 doc/kompare/settings-diff1.png create mode 100644 doc/kompare/settings-diff2.png create mode 100644 doc/kompare/settings-diff3.png create mode 100644 doc/kompare/settings-diff4.png create mode 100644 doc/kompare/settings-view1.png create mode 100644 doc/kompare/settings-view2.png create mode 100644 doc/scripts/Makefile.am create mode 100644 doc/scripts/kdesvn-build/Makefile.am create mode 100644 doc/scripts/kdesvn-build/index.docbook create mode 100644 doc/scripts/man-adddebug.1.docbook create mode 100644 doc/scripts/man-cheatmake.1.docbook create mode 100644 doc/scripts/man-create_cvsignore.1.docbook create mode 100644 doc/scripts/man-create_makefile.1.docbook create mode 100644 doc/scripts/man-create_makefiles.1.docbook create mode 100644 doc/scripts/man-cvscheck.1.docbook create mode 100644 doc/scripts/man-cvslastchange.1.docbook create mode 100644 doc/scripts/man-cvslastlog.1.docbook create mode 100644 doc/scripts/man-cvsrevertlast.1.docbook create mode 100644 doc/scripts/man-cxxmetric.1.docbook create mode 100644 doc/scripts/man-demangle.1.docbook create mode 100644 doc/scripts/man-extend_dmalloc.1.docbook create mode 100644 doc/scripts/man-extractrc.1.docbook create mode 100644 doc/scripts/man-fixincludes.1.docbook create mode 100644 doc/scripts/man-po2xml.1.docbook create mode 100644 doc/scripts/man-pruneemptydirs.1.docbook create mode 100644 doc/scripts/man-qtdoc.1.docbook create mode 100644 doc/scripts/man-reportview.1.docbook create mode 100644 doc/scripts/man-split2po.1.docbook create mode 100644 doc/scripts/man-swappo.1.docbook create mode 100644 doc/scripts/man-transxx.1.docbook create mode 100644 doc/scripts/man-xml2pot.1.docbook create mode 100644 doc/scripts/man-zonetab2pot.1.docbook create mode 100644 doc/umbrello/Makefile.am create mode 100644 doc/umbrello/activity-diagram.png create mode 100644 doc/umbrello/add-remove-languages.png create mode 100644 doc/umbrello/aggregation.png create mode 100644 doc/umbrello/association.png create mode 100644 doc/umbrello/authors.docbook create mode 100644 doc/umbrello/class-diagram.png create mode 100644 doc/umbrello/class.png create mode 100644 doc/umbrello/code-import.png create mode 100644 doc/umbrello/code_import_and_generation.docbook create mode 100644 doc/umbrello/collaboration-diagram.png create mode 100644 doc/umbrello/composition.png create mode 100644 doc/umbrello/credits.docbook create mode 100644 doc/umbrello/folders.png create mode 100644 doc/umbrello/generalization.png create mode 100644 doc/umbrello/generation-options.png create mode 100644 doc/umbrello/index.docbook create mode 100644 doc/umbrello/introduction.docbook create mode 100644 doc/umbrello/other_features.docbook create mode 100644 doc/umbrello/sequence-diagram.png create mode 100644 doc/umbrello/state-diagram.png create mode 100644 doc/umbrello/umbrello-main-screen.png create mode 100644 doc/umbrello/umbrello-ui-clean.png create mode 100644 doc/umbrello/umbrello-ui.png create mode 100644 doc/umbrello/uml_basics.docbook create mode 100644 doc/umbrello/use-case-diagram.png create mode 100644 doc/umbrello/working_with_umbrello.docbook create mode 100644 kapptemplate/ChangeLog create mode 100644 kapptemplate/Makefile.am create mode 100644 kapptemplate/Makefile.cvs create mode 100644 kapptemplate/README create mode 100644 kapptemplate/VERSION create mode 100644 kapptemplate/admin/Makefile.am create mode 100644 kapptemplate/appframework/AUTHORS create mode 100644 kapptemplate/appframework/COPYING create mode 100644 kapptemplate/appframework/ChangeLog create mode 100644 kapptemplate/appframework/INSTALL create mode 100644 kapptemplate/appframework/Makefile.am create mode 100644 kapptemplate/appframework/NEWS create mode 100644 kapptemplate/appframework/README create mode 100644 kapptemplate/appframework/VERSION create mode 100644 kapptemplate/appframework/app.lsm create mode 100644 kapptemplate/appframework/app.spec create mode 100644 kapptemplate/appframework/base-Makefile.am create mode 100644 kapptemplate/appframework/base-Makefile.cvs create mode 100644 kapptemplate/appframework/configure.in.in.in create mode 100644 kapptemplate/appframework/no-exe/COPYING create mode 100644 kapptemplate/appframework/no-exe/INSTALL create mode 100644 kapptemplate/appframework/no-exe/Makefile.am create mode 100644 kapptemplate/appframework/po-Makefile.am create mode 100644 kapptemplate/existing.module create mode 100644 kapptemplate/existing/Makefile.am create mode 100644 kapptemplate/existing/app-Makefile.am create mode 100644 kapptemplate/existing/app-desktop create mode 100644 kapptemplate/kapp/Makefile.am create mode 100644 kapptemplate/kapp/app-Makefile.am create mode 100644 kapptemplate/kapp/app-configure.in.in create mode 100644 kapptemplate/kapp/app-desktop create mode 100644 kapptemplate/kapp/app.cpp create mode 100644 kapptemplate/kapp/app.h create mode 100644 kapptemplate/kapp/app_client.cpp create mode 100644 kapptemplate/kapp/appiface.h create mode 100644 kapptemplate/kapp/apppref.cpp create mode 100644 kapptemplate/kapp/apppref.h create mode 100644 kapptemplate/kapp/appui.rc create mode 100644 kapptemplate/kapp/appview.cpp create mode 100644 kapptemplate/kapp/appview.h create mode 100644 kapptemplate/kapp/doc-Makefile.am create mode 100644 kapptemplate/kapp/doc-app-Makefile.am create mode 100644 kapptemplate/kapp/hi16-app-app.png create mode 100644 kapptemplate/kapp/hi32-app-app.png create mode 100644 kapptemplate/kapp/hi48-app-app.png create mode 100644 kapptemplate/kapp/index.docbook create mode 100644 kapptemplate/kapp/lo16-app-app.png create mode 100644 kapptemplate/kapp/lo32-app-app.png create mode 100644 kapptemplate/kapp/main.cpp create mode 100644 kapptemplate/kapp/no-exe/Makefile.am create mode 100644 kapptemplate/kapp/no-exe/hi16-app-app.png create mode 100644 kapptemplate/kapp/no-exe/hi32-app-app.png create mode 100644 kapptemplate/kapp/no-exe/hi48-app-app.png create mode 100644 kapptemplate/kapp/no-exe/lo16-app-app.png create mode 100644 kapptemplate/kapp/no-exe/lo32-app-app.png create mode 100644 kapptemplate/kapp/pics-Makefile.am create mode 100644 kapptemplate/kapptemplate.common create mode 100644 kapptemplate/kapptemplate.in create mode 100644 kapptemplate/kapptemplate.lsm create mode 100644 kapptemplate/kapptemplate.module create mode 100644 kapptemplate/kpartapp.module create mode 100644 kapptemplate/kpartapp/Makefile.am create mode 100644 kapptemplate/kpartapp/app-Makefile.am create mode 100644 kapptemplate/kpartapp/app-configure.in.in create mode 100644 kapptemplate/kpartapp/app-desktop create mode 100644 kapptemplate/kpartapp/app.cpp create mode 100644 kapptemplate/kpartapp/app.h create mode 100644 kapptemplate/kpartapp/app_part-desktop create mode 100644 kapptemplate/kpartapp/app_part.cpp create mode 100644 kapptemplate/kpartapp/app_part.h create mode 100644 kapptemplate/kpartapp/app_part.rc create mode 100644 kapptemplate/kpartapp/app_shell.rc create mode 100644 kapptemplate/kpartapp/doc-Makefile.am create mode 100644 kapptemplate/kpartapp/doc-app-Makefile.am create mode 100644 kapptemplate/kpartapp/hi16-app-app.png create mode 100644 kapptemplate/kpartapp/hi32-app-app.png create mode 100644 kapptemplate/kpartapp/hi48-app-app.png create mode 100644 kapptemplate/kpartapp/index.docbook create mode 100644 kapptemplate/kpartapp/lo16-app-app.png create mode 100644 kapptemplate/kpartapp/lo32-app-app.png create mode 100644 kapptemplate/kpartapp/main.cpp create mode 100644 kapptemplate/kpartapp/no-exe/Makefile.am create mode 100644 kapptemplate/kpartapp/no-exe/hi16-app-app.png create mode 100644 kapptemplate/kpartapp/no-exe/hi32-app-app.png create mode 100644 kapptemplate/kpartapp/no-exe/hi48-app-app.png create mode 100644 kapptemplate/kpartapp/no-exe/lo16-app-app.png create mode 100644 kapptemplate/kpartapp/no-exe/lo32-app-app.png create mode 100644 kapptemplate/kpartplugin.module create mode 100644 kapptemplate/kpartplugin/Makefile.am create mode 100644 kapptemplate/kpartplugin/hi16-action-plugin.png create mode 100644 kapptemplate/kpartplugin/hi22-action-plugin.png create mode 100644 kapptemplate/kpartplugin/no-exe/Makefile.am create mode 100644 kapptemplate/kpartplugin/no-exe/hi16-action-plugin.png create mode 100644 kapptemplate/kpartplugin/no-exe/hi22-action-plugin.png create mode 100644 kapptemplate/kpartplugin/plugin-Makefile.am create mode 100644 kapptemplate/kpartplugin/plugin_app.cpp create mode 100644 kapptemplate/kpartplugin/plugin_app.h create mode 100644 kapptemplate/kpartplugin/plugin_app.rc create mode 100755 kapptemplate/mkinstalldirs create mode 100644 kbabel/AUTHORS create mode 100644 kbabel/COPYING create mode 100644 kbabel/ChangeLog create mode 100644 kbabel/Makefile.am create mode 100644 kbabel/README create mode 100644 kbabel/TODO create mode 100644 kbabel/VERSION create mode 100644 kbabel/addons/Makefile.am create mode 100644 kbabel/addons/kfile-plugins/Makefile.am create mode 100644 kbabel/addons/kfile-plugins/kfile_po.cpp create mode 100644 kbabel/addons/kfile-plugins/kfile_po.desktop create mode 100644 kbabel/addons/kfile-plugins/kfile_po.h create mode 100644 kbabel/addons/preview/Makefile.am create mode 100644 kbabel/addons/preview/pothumbcreator.cpp create mode 100644 kbabel/addons/preview/pothumbcreator.h create mode 100644 kbabel/addons/preview/pothumbnail.desktop create mode 100644 kbabel/catalogmanager/Makefile.am create mode 100644 kbabel/catalogmanager/catalogmanager.cpp create mode 100644 kbabel/catalogmanager/catalogmanager.desktop create mode 100644 kbabel/catalogmanager/catalogmanager.h create mode 100644 kbabel/catalogmanager/catalogmanagerapp.h create mode 100644 kbabel/catalogmanager/catalogmanageriface.h create mode 100644 kbabel/catalogmanager/catalogmanagerui.rc create mode 100644 kbabel/catalogmanager/catalogmanagerview.cpp create mode 100644 kbabel/catalogmanager/catalogmanagerview.h create mode 100644 kbabel/catalogmanager/catmanlistitem.cpp create mode 100644 kbabel/catalogmanager/catmanlistitem.h create mode 100644 kbabel/catalogmanager/catmanresource.h create mode 100644 kbabel/catalogmanager/findinfilesdialog.cpp create mode 100644 kbabel/catalogmanager/findinfilesdialog.h create mode 100644 kbabel/catalogmanager/future.cpp create mode 100644 kbabel/catalogmanager/hi16-app-catalogmanager.png create mode 100644 kbabel/catalogmanager/hi22-app-catalogmanager.png create mode 100644 kbabel/catalogmanager/hi32-app-catalogmanager.png create mode 100644 kbabel/catalogmanager/hi48-app-catalogmanager.png create mode 100644 kbabel/catalogmanager/icons/Makefile.am create mode 100644 kbabel/catalogmanager/icons/hi16-action-nextmarked.png create mode 100644 kbabel/catalogmanager/icons/hi16-action-nextpo.png create mode 100644 kbabel/catalogmanager/icons/hi16-action-nexttemplate.png create mode 100644 kbabel/catalogmanager/icons/hi16-action-prevmarked.png create mode 100644 kbabel/catalogmanager/icons/hi16-action-prevpo.png create mode 100644 kbabel/catalogmanager/icons/hi16-action-prevtemplate.png create mode 100644 kbabel/catalogmanager/icons/hi16-action-statistics.png create mode 100644 kbabel/catalogmanager/icons/hi16-action-syntax.png create mode 100644 kbabel/catalogmanager/icons/hi22-action-nextmarked.png create mode 100644 kbabel/catalogmanager/icons/hi22-action-nextpo.png create mode 100644 kbabel/catalogmanager/icons/hi22-action-nexttemplate.png create mode 100644 kbabel/catalogmanager/icons/hi22-action-prevmarked.png create mode 100644 kbabel/catalogmanager/icons/hi22-action-prevpo.png create mode 100644 kbabel/catalogmanager/icons/hi22-action-prevtemplate.png create mode 100644 kbabel/catalogmanager/icons/hi22-action-statistics.png create mode 100644 kbabel/catalogmanager/icons/hi22-action-syntax.png create mode 100644 kbabel/catalogmanager/icons/hi32-action-nextmarked.png create mode 100644 kbabel/catalogmanager/icons/hi32-action-nextpo.png create mode 100644 kbabel/catalogmanager/icons/hi32-action-nexttemplate.png create mode 100644 kbabel/catalogmanager/icons/hi32-action-prevmarked.png create mode 100644 kbabel/catalogmanager/icons/hi32-action-prevpo.png create mode 100644 kbabel/catalogmanager/icons/hi32-action-prevtemplate.png create mode 100644 kbabel/catalogmanager/icons/hi32-action-statistics.png create mode 100644 kbabel/catalogmanager/icons/hi32-action-syntax.png create mode 100644 kbabel/catalogmanager/icons/lo16-action-nextmarked.png create mode 100644 kbabel/catalogmanager/icons/lo16-action-nextpo.png create mode 100644 kbabel/catalogmanager/icons/lo16-action-nexttemplate.png create mode 100644 kbabel/catalogmanager/icons/lo16-action-prevmarked.png create mode 100644 kbabel/catalogmanager/icons/lo16-action-prevpo.png create mode 100644 kbabel/catalogmanager/icons/lo16-action-prevtemplate.png create mode 100644 kbabel/catalogmanager/icons/lo16-action-statistics.png create mode 100644 kbabel/catalogmanager/icons/lo16-action-syntax.png create mode 100644 kbabel/catalogmanager/icons/lo22-action-statistics.png create mode 100644 kbabel/catalogmanager/icons/lo22-action-syntax.png create mode 100644 kbabel/catalogmanager/icons/lo32-action-nextmarked.png create mode 100644 kbabel/catalogmanager/icons/lo32-action-nextpo.png create mode 100644 kbabel/catalogmanager/icons/lo32-action-nexttemplate.png create mode 100644 kbabel/catalogmanager/icons/lo32-action-prevmarked.png create mode 100644 kbabel/catalogmanager/icons/lo32-action-prevpo.png create mode 100644 kbabel/catalogmanager/icons/lo32-action-prevtemplate.png create mode 100644 kbabel/catalogmanager/icons/lo32-action-statistics.png create mode 100644 kbabel/catalogmanager/icons/lo32-action-syntax.png create mode 100644 kbabel/catalogmanager/libcvs/Makefile.am create mode 100644 kbabel/catalogmanager/libcvs/cvsdialog.cpp create mode 100644 kbabel/catalogmanager/libcvs/cvsdialog.h create mode 100644 kbabel/catalogmanager/libcvs/cvshandler.cpp create mode 100644 kbabel/catalogmanager/libcvs/cvshandler.h create mode 100644 kbabel/catalogmanager/libcvs/cvsresources.h create mode 100644 kbabel/catalogmanager/libsvn/Makefile.am create mode 100644 kbabel/catalogmanager/libsvn/svndialog.cpp create mode 100644 kbabel/catalogmanager/libsvn/svndialog.h create mode 100644 kbabel/catalogmanager/libsvn/svnhandler.cpp create mode 100644 kbabel/catalogmanager/libsvn/svnhandler.h create mode 100644 kbabel/catalogmanager/libsvn/svnresources.h create mode 100644 kbabel/catalogmanager/lo16-app-catalogmanager.png create mode 100644 kbabel/catalogmanager/lo32-app-catalogmanager.png create mode 100644 kbabel/catalogmanager/main.cpp create mode 100644 kbabel/catalogmanager/markpatterndialog.cpp create mode 100644 kbabel/catalogmanager/markpatterndialog.h create mode 100644 kbabel/catalogmanager/markpatternwidget.ui create mode 100644 kbabel/catalogmanager/multiroughtransdlg.cpp create mode 100644 kbabel/catalogmanager/multiroughtransdlg.h create mode 100644 kbabel/catalogmanager/validateprogress.cpp create mode 100644 kbabel/catalogmanager/validateprogress.h create mode 100644 kbabel/catalogmanager/validateprogresswidget.ui create mode 100644 kbabel/catalogmanager/validateprogresswidget.ui.h create mode 100644 kbabel/catalogmanager/validationoptions.ui create mode 100644 kbabel/common/Makefile.am create mode 100644 kbabel/common/argextractor.cpp create mode 100644 kbabel/common/argextractor.h create mode 100644 kbabel/common/catalog.cpp create mode 100644 kbabel/common/catalog.h create mode 100644 kbabel/common/catalog_private.h create mode 100644 kbabel/common/catalogfileplugin.h create mode 100644 kbabel/common/catalogitem.cpp create mode 100644 kbabel/common/catalogitem.h create mode 100644 kbabel/common/catalogitem_private.h create mode 100644 kbabel/common/catalogsettings.cpp create mode 100644 kbabel/common/catalogsettings.h create mode 100644 kbabel/common/catalogview.h create mode 100644 kbabel/common/diff.cpp create mode 100644 kbabel/common/diff.h create mode 100644 kbabel/common/editcmd.cpp create mode 100644 kbabel/common/editcmd.h create mode 100644 kbabel/common/exportplugin.cpp create mode 100644 kbabel/common/findoptions.h create mode 100644 kbabel/common/importplugin.cpp create mode 100644 kbabel/common/importplugin_private.h create mode 100644 kbabel/common/itempart.h create mode 100644 kbabel/common/kbabel-projectrename.upd create mode 100644 kbabel/common/kbabeldatatool.h create mode 100644 kbabel/common/kbabelfilter.desktop create mode 100644 kbabel/common/kbmailer.cpp create mode 100644 kbabel/common/kbmailer.h create mode 100644 kbabel/common/kbproject.cpp create mode 100644 kbabel/common/kbproject.h create mode 100644 kbabel/common/kbprojectmanager.cpp create mode 100644 kbabel/common/kbprojectmanager.h create mode 100644 kbabel/common/kbprojectsettings.kcfg create mode 100644 kbabel/common/kbprojectsettings.kcfgc create mode 100644 kbabel/common/libgettext/Makefile.am create mode 100644 kbabel/common/libgettext/pofiles.h create mode 100644 kbabel/common/libgettext/pofiles.ll create mode 100644 kbabel/common/libgettext/tokens.h create mode 100644 kbabel/common/msgfmt.cpp create mode 100644 kbabel/common/msgfmt.h create mode 100644 kbabel/common/pluralforms.h create mode 100644 kbabel/common/poinfo.cpp create mode 100644 kbabel/common/poinfo.h create mode 100644 kbabel/common/projectsettings.cpp create mode 100644 kbabel/common/projectsettings.h create mode 100644 kbabel/common/regexpextractor.cpp create mode 100644 kbabel/common/regexpextractor.h create mode 100644 kbabel/common/resources.h create mode 100644 kbabel/common/stringdistance.cpp create mode 100644 kbabel/common/stringdistance.h create mode 100644 kbabel/common/tagextractor.cpp create mode 100644 kbabel/common/tagextractor.h create mode 100644 kbabel/commonui/Makefile.am create mode 100644 kbabel/commonui/cmdedit.cpp create mode 100644 kbabel/commonui/cmdedit.h create mode 100644 kbabel/commonui/context.cpp create mode 100644 kbabel/commonui/context.h create mode 100644 kbabel/commonui/diffpreferences.ui create mode 100644 kbabel/commonui/diffpreferences.ui.h create mode 100644 kbabel/commonui/finddialog.cpp create mode 100644 kbabel/commonui/finddialog.h create mode 100644 kbabel/commonui/kactionselector.cpp create mode 100644 kbabel/commonui/kactionselector.h create mode 100644 kbabel/commonui/kbabel_tool.desktop create mode 100644 kbabel/commonui/kbabel_validator.desktop create mode 100644 kbabel/commonui/klisteditor.ui create mode 100644 kbabel/commonui/klisteditor.ui.h create mode 100644 kbabel/commonui/projectpref.cpp create mode 100644 kbabel/commonui/projectpref.h create mode 100644 kbabel/commonui/projectprefwidgets.cpp create mode 100644 kbabel/commonui/projectprefwidgets.h create mode 100644 kbabel/commonui/projectwizard.cpp create mode 100644 kbabel/commonui/projectwizard.h create mode 100644 kbabel/commonui/projectwizardwidget.ui create mode 100644 kbabel/commonui/projectwizardwidget.ui.h create mode 100644 kbabel/commonui/projectwizardwidget2.ui create mode 100644 kbabel/commonui/roughtransdlg.cpp create mode 100644 kbabel/commonui/roughtransdlg.h create mode 100644 kbabel/commonui/toolaction.cpp create mode 100644 kbabel/commonui/toolaction.h create mode 100644 kbabel/commonui/toolselectionwidget.cpp create mode 100644 kbabel/commonui/toolselectionwidget.h create mode 100644 kbabel/configure.in.in create mode 100644 kbabel/datatools/Makefile.am create mode 100644 kbabel/datatools/accelerators/Makefile.am create mode 100644 kbabel/datatools/accelerators/kbabel_accelstool.desktop create mode 100644 kbabel/datatools/accelerators/main.cc create mode 100644 kbabel/datatools/accelerators/main.h create mode 100644 kbabel/datatools/arguments/Makefile.am create mode 100644 kbabel/datatools/arguments/kbabel_argstool.desktop create mode 100644 kbabel/datatools/arguments/main.cc create mode 100644 kbabel/datatools/arguments/main.h create mode 100644 kbabel/datatools/context/Makefile.am create mode 100644 kbabel/datatools/context/kbabel_contexttool.desktop create mode 100644 kbabel/datatools/context/main.cc create mode 100644 kbabel/datatools/context/main.h create mode 100644 kbabel/datatools/equations/Makefile.am create mode 100644 kbabel/datatools/equations/kbabel_equationstool.desktop create mode 100644 kbabel/datatools/equations/main.cc create mode 100644 kbabel/datatools/equations/main.h create mode 100644 kbabel/datatools/length/Makefile.am create mode 100644 kbabel/datatools/length/kbabel_lengthtool.desktop create mode 100644 kbabel/datatools/length/main.cc create mode 100644 kbabel/datatools/length/main.h create mode 100644 kbabel/datatools/length/test.po create mode 100644 kbabel/datatools/not-translated/Makefile.am create mode 100644 kbabel/datatools/not-translated/kbabel_nottranslatedtool.desktop create mode 100644 kbabel/datatools/not-translated/main.cc create mode 100644 kbabel/datatools/not-translated/main.h create mode 100644 kbabel/datatools/not-translated/test.po create mode 100644 kbabel/datatools/pluralforms/Makefile.am create mode 100644 kbabel/datatools/pluralforms/kbabel_pluralformstool.desktop create mode 100644 kbabel/datatools/pluralforms/main.cc create mode 100644 kbabel/datatools/pluralforms/main.h create mode 100644 kbabel/datatools/punctuation/Makefile.am create mode 100644 kbabel/datatools/punctuation/kbabel_punctuationtool.desktop create mode 100644 kbabel/datatools/punctuation/main.cc create mode 100644 kbabel/datatools/punctuation/main.h create mode 100644 kbabel/datatools/regexp/Makefile.am create mode 100644 kbabel/datatools/regexp/kbabel_regexptool.desktop create mode 100644 kbabel/datatools/regexp/main.cc create mode 100644 kbabel/datatools/regexp/main.h create mode 100644 kbabel/datatools/regexp/regexplist.xml create mode 100644 kbabel/datatools/setfuzzy/Makefile.am create mode 100644 kbabel/datatools/setfuzzy/kbabel_setfuzzytool.desktop create mode 100644 kbabel/datatools/setfuzzy/main.cc create mode 100644 kbabel/datatools/setfuzzy/main.h create mode 100644 kbabel/datatools/whitespace/Makefile.am create mode 100644 kbabel/datatools/whitespace/kbabel_whitespacetool.desktop create mode 100644 kbabel/datatools/whitespace/main.cc create mode 100644 kbabel/datatools/whitespace/main.h create mode 100644 kbabel/datatools/whitespace/test.po create mode 100644 kbabel/datatools/xml/Makefile.am create mode 100644 kbabel/datatools/xml/kbabel_xmltool.desktop create mode 100644 kbabel/datatools/xml/main.cc create mode 100644 kbabel/datatools/xml/main.h create mode 100644 kbabel/filters/Makefile.am create mode 100644 kbabel/filters/gettext/Makefile.am create mode 100644 kbabel/filters/gettext/gettextexport.cpp create mode 100644 kbabel/filters/gettext/gettextexport.h create mode 100644 kbabel/filters/gettext/gettextimport.cpp create mode 100644 kbabel/filters/gettext/gettextimport.h create mode 100644 kbabel/filters/gettext/kbabel_gettext_export.desktop create mode 100644 kbabel/filters/gettext/kbabel_gettext_import.desktop create mode 100644 kbabel/filters/linguist/Makefile.am create mode 100644 kbabel/filters/linguist/kbabel_linguist_export.desktop create mode 100644 kbabel/filters/linguist/kbabel_linguist_import.desktop create mode 100644 kbabel/filters/linguist/linguistexport.cpp create mode 100644 kbabel/filters/linguist/linguistexport.h create mode 100644 kbabel/filters/linguist/linguistimport.cpp create mode 100644 kbabel/filters/linguist/linguistimport.h create mode 100644 kbabel/filters/xliff/Makefile.am create mode 100644 kbabel/filters/xliff/kbabel_xliff_export.desktop create mode 100644 kbabel/filters/xliff/kbabel_xliff_import.desktop create mode 100644 kbabel/filters/xliff/xliffexport.cpp create mode 100644 kbabel/filters/xliff/xliffexport.h create mode 100644 kbabel/filters/xliff/xliffimport.cpp create mode 100644 kbabel/filters/xliff/xliffimport.h create mode 100644 kbabel/kbabel/Makefile.am create mode 100644 kbabel/kbabel/charselectview.cpp create mode 100644 kbabel/kbabel/charselectview.h create mode 100644 kbabel/kbabel/colorpreferences.ui create mode 100644 kbabel/kbabel/commentview.cpp create mode 100644 kbabel/kbabel/commentview.h create mode 100644 kbabel/kbabel/contextview.cpp create mode 100644 kbabel/kbabel/contextview.h create mode 100644 kbabel/kbabel/editordiffpreferences.ui create mode 100644 kbabel/kbabel/editorpreferences.ui create mode 100644 kbabel/kbabel/editorpreferences.ui.h create mode 100644 kbabel/kbabel/errorlistview.cpp create mode 100644 kbabel/kbabel/errorlistview.h create mode 100644 kbabel/kbabel/fontpreferences.ui create mode 100644 kbabel/kbabel/fontpreferences.ui.h create mode 100644 kbabel/kbabel/gotodialog.cpp create mode 100644 kbabel/kbabel/gotodialog.h create mode 100644 kbabel/kbabel/headereditor.cpp create mode 100644 kbabel/kbabel/headereditor.h create mode 100644 kbabel/kbabel/headerwidget.ui create mode 100644 kbabel/kbabel/hi16-app-kbabel.png create mode 100644 kbabel/kbabel/hi32-app-kbabel.png create mode 100644 kbabel/kbabel/hi48-app-kbabel.png create mode 100644 kbabel/kbabel/hidingmsgedit.cpp create mode 100644 kbabel/kbabel/hidingmsgedit.h create mode 100644 kbabel/kbabel/icons/Makefile.am create mode 100644 kbabel/kbabel/icons/hi16-action-autodiff.png create mode 100644 kbabel/kbabel/icons/hi16-action-catalogmanager.png create mode 100644 kbabel/kbabel/icons/hi16-action-diff.png create mode 100644 kbabel/kbabel/icons/hi16-action-insert_arg.png create mode 100644 kbabel/kbabel/icons/hi16-action-insert_tag.png create mode 100644 kbabel/kbabel/icons/hi16-action-msgid2msgstr.png create mode 100644 kbabel/kbabel/icons/hi16-action-nexterror.png create mode 100644 kbabel/kbabel/icons/hi16-action-nextfuzzy.png create mode 100644 kbabel/kbabel/icons/hi16-action-nextfuzzyuntrans.png create mode 100644 kbabel/kbabel/icons/hi16-action-nextuntranslated.png create mode 100644 kbabel/kbabel/icons/hi16-action-preverror.png create mode 100644 kbabel/kbabel/icons/hi16-action-prevfuzzy.png create mode 100644 kbabel/kbabel/icons/hi16-action-prevfuzzyuntrans.png create mode 100644 kbabel/kbabel/icons/hi16-action-prevuntranslated.png create mode 100644 kbabel/kbabel/icons/hi16-action-search2msgstr.png create mode 100644 kbabel/kbabel/icons/hi16-action-spellcheck_actual.png create mode 100644 kbabel/kbabel/icons/hi16-action-spellcheck_all.png create mode 100644 kbabel/kbabel/icons/hi16-action-spellcheck_from_cursor.png create mode 100644 kbabel/kbabel/icons/hi16-action-spellcheck_selected.png create mode 100644 kbabel/kbabel/icons/hi16-action-togglefuzzy.png create mode 100644 kbabel/kbabel/icons/hi16-action-transsearch.png create mode 100644 kbabel/kbabel/icons/hi22-action-autodiff.png create mode 100644 kbabel/kbabel/icons/hi22-action-catalogmanager.png create mode 100644 kbabel/kbabel/icons/hi22-action-diff.png create mode 100644 kbabel/kbabel/icons/hi22-action-insert_arg.png create mode 100644 kbabel/kbabel/icons/hi22-action-insert_tag.png create mode 100644 kbabel/kbabel/icons/hi22-action-msgid2msgstr.png create mode 100644 kbabel/kbabel/icons/hi22-action-nexterror.png create mode 100644 kbabel/kbabel/icons/hi22-action-nextfuzzy.png create mode 100644 kbabel/kbabel/icons/hi22-action-nextfuzzyuntrans.png create mode 100644 kbabel/kbabel/icons/hi22-action-nextuntranslated.png create mode 100644 kbabel/kbabel/icons/hi22-action-preverror.png create mode 100644 kbabel/kbabel/icons/hi22-action-prevfuzzy.png create mode 100644 kbabel/kbabel/icons/hi22-action-prevfuzzyuntrans.png create mode 100644 kbabel/kbabel/icons/hi22-action-prevuntranslated.png create mode 100644 kbabel/kbabel/icons/hi22-action-search2msgstr.png create mode 100644 kbabel/kbabel/icons/hi22-action-togglefuzzy.png create mode 100644 kbabel/kbabel/icons/hi22-action-transsearch.png create mode 100644 kbabel/kbabel/icons/hi32-action-autodiff.png create mode 100644 kbabel/kbabel/icons/hi32-action-catalogmanager.png create mode 100644 kbabel/kbabel/icons/hi32-action-diff.png create mode 100644 kbabel/kbabel/icons/hi32-action-insert_arg.png create mode 100644 kbabel/kbabel/icons/hi32-action-insert_tag.png create mode 100644 kbabel/kbabel/icons/hi32-action-msgid2msgstr.png create mode 100644 kbabel/kbabel/icons/hi32-action-nexterror.png create mode 100644 kbabel/kbabel/icons/hi32-action-nextfuzzy.png create mode 100644 kbabel/kbabel/icons/hi32-action-nextfuzzyuntrans.png create mode 100644 kbabel/kbabel/icons/hi32-action-nextuntranslated.png create mode 100644 kbabel/kbabel/icons/hi32-action-preverror.png create mode 100644 kbabel/kbabel/icons/hi32-action-prevfuzzy.png create mode 100644 kbabel/kbabel/icons/hi32-action-prevfuzzyuntrans.png create mode 100644 kbabel/kbabel/icons/hi32-action-prevuntranslated.png create mode 100644 kbabel/kbabel/icons/hi32-action-search2msgstr.png create mode 100644 kbabel/kbabel/icons/hi32-action-togglefuzzy.png create mode 100644 kbabel/kbabel/icons/hi32-action-transsearch.png create mode 100644 kbabel/kbabel/icons/lo16-action-autodiff.png create mode 100644 kbabel/kbabel/icons/lo16-action-catalogmanager.png create mode 100644 kbabel/kbabel/icons/lo16-action-diff.png create mode 100644 kbabel/kbabel/icons/lo16-action-insert_arg.png create mode 100644 kbabel/kbabel/icons/lo16-action-insert_tag.png create mode 100644 kbabel/kbabel/icons/lo16-action-msgid2msgstr.png create mode 100644 kbabel/kbabel/icons/lo16-action-nexterror.png create mode 100644 kbabel/kbabel/icons/lo16-action-nextfuzzy.png create mode 100644 kbabel/kbabel/icons/lo16-action-nextfuzzyuntrans.png create mode 100644 kbabel/kbabel/icons/lo16-action-nextuntranslated.png create mode 100644 kbabel/kbabel/icons/lo16-action-preverror.png create mode 100644 kbabel/kbabel/icons/lo16-action-prevfuzzy.png create mode 100644 kbabel/kbabel/icons/lo16-action-prevfuzzyuntrans.png create mode 100644 kbabel/kbabel/icons/lo16-action-prevuntranslated.png create mode 100644 kbabel/kbabel/icons/lo16-action-search2msgstr.png create mode 100644 kbabel/kbabel/icons/lo16-action-spellcheck_actual.png create mode 100644 kbabel/kbabel/icons/lo16-action-spellcheck_all.png create mode 100644 kbabel/kbabel/icons/lo16-action-spellcheck_from_cursor.png create mode 100644 kbabel/kbabel/icons/lo16-action-spellcheck_selected.png create mode 100644 kbabel/kbabel/icons/lo16-action-togglefuzzy.png create mode 100644 kbabel/kbabel/icons/lo16-action-transsearch.png create mode 100644 kbabel/kbabel/icons/lo32-action-autodiff.png create mode 100644 kbabel/kbabel/icons/lo32-action-catalogmanager.png create mode 100644 kbabel/kbabel/icons/lo32-action-diff.png create mode 100644 kbabel/kbabel/icons/lo32-action-insert_arg.png create mode 100644 kbabel/kbabel/icons/lo32-action-insert_tag.png create mode 100644 kbabel/kbabel/icons/lo32-action-msgid2msgstr.png create mode 100644 kbabel/kbabel/icons/lo32-action-nexterror.png create mode 100644 kbabel/kbabel/icons/lo32-action-nextfuzzy.png create mode 100644 kbabel/kbabel/icons/lo32-action-nextfuzzyuntrans.png create mode 100644 kbabel/kbabel/icons/lo32-action-nextuntranslated.png create mode 100644 kbabel/kbabel/icons/lo32-action-preverror.png create mode 100644 kbabel/kbabel/icons/lo32-action-prevfuzzy.png create mode 100644 kbabel/kbabel/icons/lo32-action-prevfuzzyuntrans.png create mode 100644 kbabel/kbabel/icons/lo32-action-prevuntranslated.png create mode 100644 kbabel/kbabel/icons/lo32-action-search2msgstr.png create mode 100644 kbabel/kbabel/icons/lo32-action-togglefuzzy.png create mode 100644 kbabel/kbabel/icons/lo32-action-transsearch.png create mode 100644 kbabel/kbabel/kbabel-difftoproject.upd create mode 100644 kbabel/kbabel/kbabel-project.upd create mode 100644 kbabel/kbabel/kbabel.cpp create mode 100644 kbabel/kbabel/kbabel.desktop create mode 100644 kbabel/kbabel/kbabel.h create mode 100644 kbabel/kbabel/kbabel.kcfg create mode 100644 kbabel/kbabel/kbabeliface.h create mode 100644 kbabel/kbabel/kbabelpref.cpp create mode 100644 kbabel/kbabel/kbabelpref.h create mode 100644 kbabel/kbabel/kbabelsettings.kcfgc create mode 100644 kbabel/kbabel/kbabelsplash.cpp create mode 100644 kbabel/kbabel/kbabelsplash.h create mode 100644 kbabel/kbabel/kbabelui.rc create mode 100644 kbabel/kbabel/kbabelview.cpp create mode 100644 kbabel/kbabel/kbabelview.h create mode 100644 kbabel/kbabel/kbabelview2.cpp create mode 100644 kbabel/kbabel/kbbookmarkhandler.cpp create mode 100644 kbabel/kbabel/kbbookmarkhandler.h create mode 100644 kbabel/kbabel/kbcatalog.cpp create mode 100644 kbabel/kbabel/kbcatalog.h create mode 100644 kbabel/kbabel/kbcataloglistview.cpp create mode 100644 kbabel/kbabel/kbcataloglistview.h create mode 100644 kbabel/kbabel/kbcataloglistviewitem.cpp create mode 100644 kbabel/kbabel/kbcataloglistviewitem.h create mode 100644 kbabel/kbabel/kbcatalogview.cpp create mode 100644 kbabel/kbabel/kbcatalogview.h create mode 100644 kbabel/kbabel/kbcharselect.cpp create mode 100644 kbabel/kbabel/kbcharselect.h create mode 100644 kbabel/kbabel/kbhighlighting.cpp create mode 100644 kbabel/kbabel/kbhighlighting.h create mode 100644 kbabel/kbabel/lo16-app-kbabel.png create mode 100644 kbabel/kbabel/lo32-app-kbabel.png create mode 100644 kbabel/kbabel/main.cpp create mode 100644 kbabel/kbabel/mymultilineedit.cpp create mode 100644 kbabel/kbabel/mymultilineedit.h create mode 100644 kbabel/kbabel/pics/Makefile.am create mode 100644 kbabel/kbabel/pics/broken.png create mode 100644 kbabel/kbabel/pics/missing.png create mode 100644 kbabel/kbabel/pics/needwork.png create mode 100644 kbabel/kbabel/pics/noflag.png create mode 100644 kbabel/kbabel/pics/ok.png create mode 100644 kbabel/kbabel/pics/pref_identity.png create mode 100644 kbabel/kbabel/pics/splash.png create mode 100644 kbabel/kbabel/searchpreferences.ui create mode 100644 kbabel/kbabel/sourceview.cpp create mode 100644 kbabel/kbabel/sourceview.h create mode 100644 kbabel/kbabel/spelldlg.cpp create mode 100644 kbabel/kbabel/spelldlg.h create mode 100644 kbabel/kbabel/spelldlgwidget.ui create mode 100644 kbabel/kbabel/taglistview.cpp create mode 100644 kbabel/kbabel/taglistview.h create mode 100644 kbabel/kbabeldict/Makefile.am create mode 100644 kbabel/kbabeldict/README create mode 100644 kbabel/kbabeldict/README.modules create mode 100644 kbabel/kbabeldict/aboutmoduledlg.cpp create mode 100644 kbabel/kbabeldict/aboutmoduledlg.h create mode 100644 kbabel/kbabeldict/dictchooser.cpp create mode 100644 kbabel/kbabeldict/dictchooser.h create mode 100644 kbabel/kbabeldict/dictionarymenu.cpp create mode 100644 kbabel/kbabeldict/dictionarymenu.h create mode 100644 kbabel/kbabeldict/hi16-app-kbabeldict.png create mode 100644 kbabel/kbabeldict/hi32-app-kbabeldict.png create mode 100644 kbabel/kbabeldict/hi48-app-kbabeldict.png create mode 100644 kbabel/kbabeldict/kbabeldict.cpp create mode 100644 kbabel/kbabeldict/kbabeldict.desktop create mode 100644 kbabel/kbabeldict/kbabeldict.h create mode 100644 kbabel/kbabeldict/kbabeldict_module.desktop create mode 100644 kbabel/kbabeldict/kbabeldictbox.cpp create mode 100644 kbabel/kbabeldict/kbabeldictbox.h create mode 100644 kbabel/kbabeldict/kbabeldictiface.h create mode 100644 kbabel/kbabeldict/kbabeldictview.cpp create mode 100644 kbabel/kbabeldict/kbabeldictview.h create mode 100644 kbabel/kbabeldict/kbabelsplash.cpp create mode 100644 kbabel/kbabeldict/kbabelsplash.h create mode 100644 kbabel/kbabeldict/lo16-app-kbabeldict.png create mode 100644 kbabel/kbabeldict/lo32-app-kbabeldict.png create mode 100644 kbabel/kbabeldict/main.cpp create mode 100644 kbabel/kbabeldict/modules/Makefile.am create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/AUTHOR create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/KDBSearchEngine.cpp create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/KDBSearchEngine.h create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/Makefile.am create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/STRUCTURE create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/TODO create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/configure.in.bot create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/configure.in.in create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/database.cpp create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/database.h create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/dbscan.cpp create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/dbscan.h create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/dbse_factory.cpp create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/dbse_factory.h create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/dbsearchengine.desktop create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/dbseprefwidget.ui create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/makemsgdb.C create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/preferenceswidget.cpp create mode 100644 kbabel/kbabeldict/modules/dbsearchengine/preferenceswidget.h create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/AUTHOR create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/KDBSearchEngine2.cpp create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/KDBSearchEngine2.h create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/Makefile.am create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/README create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/algorithms.cpp create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/algorithms.h create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/chunk.cpp create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/chunk.h create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/database.cpp create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/database.h create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/dbentries.cpp create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/dbentries.h create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/dbscan.cpp create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/dbscan.h create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/dbse2.ui create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/dbse2_factory.cpp create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/dbse2_factory.h create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/dbsearchengine2.desktop create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/dbseprefwidget.ui create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/preferenceswidget.cpp create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/preferenceswidget.h create mode 100644 kbabel/kbabeldict/modules/dbsearchengine2/sourcedialog.ui create mode 100644 kbabel/kbabeldict/modules/poauxiliary/Makefile.am create mode 100644 kbabel/kbabeldict/modules/poauxiliary/pa_factory.cpp create mode 100644 kbabel/kbabeldict/modules/poauxiliary/pa_factory.h create mode 100644 kbabel/kbabeldict/modules/poauxiliary/poauxiliary.cpp create mode 100644 kbabel/kbabeldict/modules/poauxiliary/poauxiliary.desktop create mode 100644 kbabel/kbabeldict/modules/poauxiliary/poauxiliary.h create mode 100644 kbabel/kbabeldict/modules/poauxiliary/preferenceswidget.cpp create mode 100644 kbabel/kbabeldict/modules/poauxiliary/preferenceswidget.h create mode 100644 kbabel/kbabeldict/modules/poauxiliary/pwidget.ui create mode 100644 kbabel/kbabeldict/modules/pocompendium/Makefile.am create mode 100644 kbabel/kbabeldict/modules/pocompendium/compendiumdata.cpp create mode 100644 kbabel/kbabeldict/modules/pocompendium/compendiumdata.h create mode 100644 kbabel/kbabeldict/modules/pocompendium/pc_factory.cpp create mode 100644 kbabel/kbabeldict/modules/pocompendium/pc_factory.h create mode 100644 kbabel/kbabeldict/modules/pocompendium/pocompendium.cpp create mode 100644 kbabel/kbabeldict/modules/pocompendium/pocompendium.desktop create mode 100644 kbabel/kbabeldict/modules/pocompendium/pocompendium.h create mode 100644 kbabel/kbabeldict/modules/pocompendium/preferenceswidget.cpp create mode 100644 kbabel/kbabeldict/modules/pocompendium/preferenceswidget.h create mode 100644 kbabel/kbabeldict/modules/pocompendium/pwidget.ui create mode 100644 kbabel/kbabeldict/modules/tmx/Makefile.am create mode 100644 kbabel/kbabeldict/modules/tmx/pc_factory.cpp create mode 100644 kbabel/kbabeldict/modules/tmx/pc_factory.h create mode 100644 kbabel/kbabeldict/modules/tmx/preferenceswidget.cpp create mode 100644 kbabel/kbabeldict/modules/tmx/preferenceswidget.h create mode 100644 kbabel/kbabeldict/modules/tmx/pwidget.ui create mode 100644 kbabel/kbabeldict/modules/tmx/tmxcompendium.cpp create mode 100644 kbabel/kbabeldict/modules/tmx/tmxcompendium.desktop create mode 100644 kbabel/kbabeldict/modules/tmx/tmxcompendium.h create mode 100644 kbabel/kbabeldict/modules/tmx/tmxcompendiumdata.cpp create mode 100644 kbabel/kbabeldict/modules/tmx/tmxcompendiumdata.h create mode 100644 kbabel/kbabeldict/searchengine.cpp create mode 100644 kbabel/kbabeldict/searchengine.h create mode 100644 kbugbuster/AUTHORS create mode 100644 kbugbuster/COPYING create mode 100644 kbugbuster/ChangeLog create mode 100644 kbugbuster/INSTALL create mode 100644 kbugbuster/Makefile.am create mode 100644 kbugbuster/README create mode 100644 kbugbuster/TODO create mode 100644 kbugbuster/backend/Makefile.am create mode 100644 kbugbuster/backend/bug.cpp create mode 100644 kbugbuster/backend/bug.h create mode 100644 kbugbuster/backend/bugcache.cpp create mode 100644 kbugbuster/backend/bugcache.h create mode 100644 kbugbuster/backend/bugcommand.cpp create mode 100644 kbugbuster/backend/bugcommand.h create mode 100644 kbugbuster/backend/bugdetails.cpp create mode 100644 kbugbuster/backend/bugdetails.h create mode 100644 kbugbuster/backend/bugdetailsimpl.h create mode 100644 kbugbuster/backend/bugdetailsjob.cpp create mode 100644 kbugbuster/backend/bugdetailsjob.h create mode 100644 kbugbuster/backend/bugdetailspart.h create mode 100644 kbugbuster/backend/bugimpl.h create mode 100644 kbugbuster/backend/bugjob.cpp create mode 100644 kbugbuster/backend/bugjob.h create mode 100644 kbugbuster/backend/buglistjob.cpp create mode 100644 kbugbuster/backend/buglistjob.h create mode 100644 kbugbuster/backend/bugmybugsjob.cpp create mode 100644 kbugbuster/backend/bugmybugsjob.h create mode 100644 kbugbuster/backend/bugserver.cpp create mode 100644 kbugbuster/backend/bugserver.h create mode 100644 kbugbuster/backend/bugserverconfig.cpp create mode 100644 kbugbuster/backend/bugserverconfig.h create mode 100644 kbugbuster/backend/bugsystem.cpp create mode 100644 kbugbuster/backend/bugsystem.h create mode 100644 kbugbuster/backend/domprocessor.cpp create mode 100644 kbugbuster/backend/domprocessor.h create mode 100644 kbugbuster/backend/error.h create mode 100644 kbugbuster/backend/htmlparser.cpp create mode 100644 kbugbuster/backend/htmlparser.h create mode 100644 kbugbuster/backend/kbbprefs.cpp create mode 100644 kbugbuster/backend/kbbprefs.h create mode 100644 kbugbuster/backend/mailsender.cpp create mode 100644 kbugbuster/backend/mailsender.h create mode 100644 kbugbuster/backend/package.cpp create mode 100644 kbugbuster/backend/package.h create mode 100644 kbugbuster/backend/packageimpl.h create mode 100644 kbugbuster/backend/packagelistjob.cpp create mode 100644 kbugbuster/backend/packagelistjob.h create mode 100644 kbugbuster/backend/person.cpp create mode 100644 kbugbuster/backend/person.h create mode 100644 kbugbuster/backend/processor.cpp create mode 100644 kbugbuster/backend/processor.h create mode 100644 kbugbuster/backend/rdfprocessor.cpp create mode 100644 kbugbuster/backend/rdfprocessor.h create mode 100644 kbugbuster/backend/smtp.cpp create mode 100644 kbugbuster/backend/smtp.h create mode 100644 kbugbuster/configure.in.in create mode 100644 kbugbuster/gui/Makefile.am create mode 100644 kbugbuster/gui/README create mode 100644 kbugbuster/gui/buglvi.cpp create mode 100644 kbugbuster/gui/buglvi.h create mode 100644 kbugbuster/gui/centralwidget.cpp create mode 100644 kbugbuster/gui/centralwidget.h create mode 100644 kbugbuster/gui/centralwidget_base.ui create mode 100644 kbugbuster/gui/cwbugdetails.cpp create mode 100644 kbugbuster/gui/cwbugdetails.h create mode 100644 kbugbuster/gui/cwbugdetailscontainer.cpp create mode 100644 kbugbuster/gui/cwbugdetailscontainer.h create mode 100644 kbugbuster/gui/cwbugdetailscontainer_base.ui create mode 100644 kbugbuster/gui/cwbuglistcontainer.cpp create mode 100644 kbugbuster/gui/cwbuglistcontainer.h create mode 100644 kbugbuster/gui/cwloadingwidget.cpp create mode 100644 kbugbuster/gui/cwloadingwidget.h create mode 100644 kbugbuster/gui/cwsearchwidget.cpp create mode 100644 kbugbuster/gui/cwsearchwidget.h create mode 100644 kbugbuster/gui/cwsearchwidget_base.ui create mode 100644 kbugbuster/gui/kbbbookmarkmanager.h create mode 100644 kbugbuster/gui/kbbmainwindow.cpp create mode 100644 kbugbuster/gui/kbbmainwindow.h create mode 100644 kbugbuster/gui/kbugbusterui.rc create mode 100644 kbugbuster/gui/loadallbugsdlg.cpp create mode 100644 kbugbuster/gui/loadallbugsdlg.h create mode 100644 kbugbuster/gui/messageeditor.cpp create mode 100644 kbugbuster/gui/messageeditor.h create mode 100644 kbugbuster/gui/msginputdialog.cpp create mode 100644 kbugbuster/gui/msginputdialog.h create mode 100644 kbugbuster/gui/packagelvi.cpp create mode 100644 kbugbuster/gui/packagelvi.h create mode 100644 kbugbuster/gui/packageselectdialog.cpp create mode 100644 kbugbuster/gui/packageselectdialog.h create mode 100644 kbugbuster/gui/preferencesdialog.cpp create mode 100644 kbugbuster/gui/preferencesdialog.h create mode 100644 kbugbuster/gui/serverconfigdialog.cpp create mode 100644 kbugbuster/gui/serverconfigdialog.h create mode 100644 kbugbuster/gui/severityselectdialog.cpp create mode 100644 kbugbuster/gui/severityselectdialog.h create mode 100644 kbugbuster/hi128-app-kbugbuster.png create mode 100644 kbugbuster/hi16-app-kbugbuster.png create mode 100644 kbugbuster/hi22-app-kbugbuster.png create mode 100644 kbugbuster/hi32-app-kbugbuster.png create mode 100644 kbugbuster/hi48-app-kbugbuster.png create mode 100644 kbugbuster/hi64-app-kbugbuster.png create mode 100644 kbugbuster/kbugbuster.desktop create mode 100644 kbugbuster/kresources/Makefile.am create mode 100644 kbugbuster/kresources/bugzilla.desktop create mode 100644 kbugbuster/kresources/kcalresource.cpp create mode 100644 kbugbuster/kresources/kcalresource.h create mode 100644 kbugbuster/kresources/kcalresource_plugin.cpp create mode 100644 kbugbuster/kresources/kcalresourceconfig.cpp create mode 100644 kbugbuster/kresources/kcalresourceconfig.h create mode 100644 kbugbuster/kresources/kresources_kcal_bugzilla.kcfg create mode 100644 kbugbuster/kresources/resourceprefs.kcfgc create mode 100644 kbugbuster/lo16-app-kbugbuster.png create mode 100644 kbugbuster/lo32-app-kbugbuster.png create mode 100644 kbugbuster/main.cpp create mode 100644 kbugbuster/pics/Makefile.am create mode 100644 kbugbuster/pics/bars.png create mode 100644 kbugbuster/pics/logo.png create mode 100644 kbugbuster/pics/tools.png create mode 100644 kbugbuster/pics/top-right.png create mode 100644 kcachegrind/AUTHORS create mode 100644 kcachegrind/COPYING create mode 100644 kcachegrind/ChangeLog create mode 100644 kcachegrind/INSTALL create mode 100644 kcachegrind/Makefile.am create mode 100644 kcachegrind/NEWS create mode 100644 kcachegrind/README create mode 100644 kcachegrind/TODO create mode 100644 kcachegrind/configure.in.in create mode 100644 kcachegrind/converters/Makefile.am create mode 100644 kcachegrind/converters/README create mode 100644 kcachegrind/converters/dprof2calltree create mode 100644 kcachegrind/converters/hotshot2calltree create mode 100755 kcachegrind/converters/memprof2calltree create mode 100755 kcachegrind/converters/op2calltree create mode 100644 kcachegrind/converters/pprof2calltree create mode 100644 kcachegrind/kcachegrind.lsm.in create mode 100644 kcachegrind/kcachegrind.spec.in create mode 100644 kcachegrind/kcachegrind/Doxyfile create mode 100644 kcachegrind/kcachegrind/Makefile.am create mode 100644 kcachegrind/kcachegrind/cachegrindloader.cpp create mode 100644 kcachegrind/kcachegrind/callgraphview.cpp create mode 100644 kcachegrind/kcachegrind/callgraphview.h create mode 100644 kcachegrind/kcachegrind/callitem.cpp create mode 100644 kcachegrind/kcachegrind/callitem.h create mode 100644 kcachegrind/kcachegrind/callmapview.cpp create mode 100644 kcachegrind/kcachegrind/callmapview.h create mode 100644 kcachegrind/kcachegrind/callview.cpp create mode 100644 kcachegrind/kcachegrind/callview.h create mode 100644 kcachegrind/kcachegrind/configdlg.cpp create mode 100644 kcachegrind/kcachegrind/configdlg.h create mode 100644 kcachegrind/kcachegrind/configdlgbase.ui create mode 100644 kcachegrind/kcachegrind/configuration.cpp create mode 100644 kcachegrind/kcachegrind/configuration.h create mode 100644 kcachegrind/kcachegrind/costlistitem.cpp create mode 100644 kcachegrind/kcachegrind/costlistitem.h create mode 100644 kcachegrind/kcachegrind/costtypeitem.cpp create mode 100644 kcachegrind/kcachegrind/costtypeitem.h create mode 100644 kcachegrind/kcachegrind/costtypeview.cpp create mode 100644 kcachegrind/kcachegrind/costtypeview.h create mode 100644 kcachegrind/kcachegrind/coverage.cpp create mode 100644 kcachegrind/kcachegrind/coverage.h create mode 100644 kcachegrind/kcachegrind/coverageitem.cpp create mode 100644 kcachegrind/kcachegrind/coverageitem.h create mode 100644 kcachegrind/kcachegrind/coverageview.cpp create mode 100644 kcachegrind/kcachegrind/coverageview.h create mode 100644 kcachegrind/kcachegrind/dumpmanager.cpp create mode 100644 kcachegrind/kcachegrind/dumpmanager.h create mode 100644 kcachegrind/kcachegrind/dumpselection.cpp create mode 100644 kcachegrind/kcachegrind/dumpselection.h create mode 100644 kcachegrind/kcachegrind/dumpselectionbase.ui create mode 100644 kcachegrind/kcachegrind/fixcost.cpp create mode 100644 kcachegrind/kcachegrind/fixcost.h create mode 100644 kcachegrind/kcachegrind/functionitem.cpp create mode 100644 kcachegrind/kcachegrind/functionitem.h create mode 100644 kcachegrind/kcachegrind/functionselection.cpp create mode 100644 kcachegrind/kcachegrind/functionselection.h create mode 100644 kcachegrind/kcachegrind/functionselectionbase.ui create mode 100644 kcachegrind/kcachegrind/hi32-app-kcachegrind.png create mode 100644 kcachegrind/kcachegrind/hi48-app-kcachegrind.png create mode 100644 kcachegrind/kcachegrind/instritem.cpp create mode 100644 kcachegrind/kcachegrind/instritem.h create mode 100644 kcachegrind/kcachegrind/instrview.cpp create mode 100644 kcachegrind/kcachegrind/instrview.h create mode 100644 kcachegrind/kcachegrind/kcachegrind.desktop create mode 100644 kcachegrind/kcachegrind/kcachegrindui.rc create mode 100644 kcachegrind/kcachegrind/listutils.cpp create mode 100644 kcachegrind/kcachegrind/listutils.h create mode 100644 kcachegrind/kcachegrind/lo16-app-kcachegrind.png create mode 100644 kcachegrind/kcachegrind/lo32-app-kcachegrind.png create mode 100644 kcachegrind/kcachegrind/loader.cpp create mode 100644 kcachegrind/kcachegrind/loader.h create mode 100644 kcachegrind/kcachegrind/main.cpp create mode 100644 kcachegrind/kcachegrind/multiview.cpp create mode 100644 kcachegrind/kcachegrind/multiview.h create mode 100644 kcachegrind/kcachegrind/partgraph.cpp create mode 100644 kcachegrind/kcachegrind/partgraph.h create mode 100644 kcachegrind/kcachegrind/partlistitem.cpp create mode 100644 kcachegrind/kcachegrind/partlistitem.h create mode 100644 kcachegrind/kcachegrind/partselection.cpp create mode 100644 kcachegrind/kcachegrind/partselection.h create mode 100644 kcachegrind/kcachegrind/partselectionbase.ui create mode 100644 kcachegrind/kcachegrind/partview.cpp create mode 100644 kcachegrind/kcachegrind/partview.h create mode 100644 kcachegrind/kcachegrind/pool.cpp create mode 100644 kcachegrind/kcachegrind/pool.h create mode 100644 kcachegrind/kcachegrind/sourceitem.cpp create mode 100644 kcachegrind/kcachegrind/sourceitem.h create mode 100644 kcachegrind/kcachegrind/sourceview.cpp create mode 100644 kcachegrind/kcachegrind/sourceview.h create mode 100644 kcachegrind/kcachegrind/stackbrowser.cpp create mode 100644 kcachegrind/kcachegrind/stackbrowser.h create mode 100644 kcachegrind/kcachegrind/stackitem.cpp create mode 100644 kcachegrind/kcachegrind/stackitem.h create mode 100644 kcachegrind/kcachegrind/stackselection.cpp create mode 100644 kcachegrind/kcachegrind/stackselection.h create mode 100644 kcachegrind/kcachegrind/stackselectionbase.ui create mode 100644 kcachegrind/kcachegrind/subcost.cpp create mode 100644 kcachegrind/kcachegrind/subcost.h create mode 100644 kcachegrind/kcachegrind/tabview.cpp create mode 100644 kcachegrind/kcachegrind/tabview.h create mode 100644 kcachegrind/kcachegrind/tips create mode 100644 kcachegrind/kcachegrind/toplevel.cpp create mode 100644 kcachegrind/kcachegrind/toplevel.h create mode 100644 kcachegrind/kcachegrind/tracedata.cpp create mode 100644 kcachegrind/kcachegrind/tracedata.h create mode 100644 kcachegrind/kcachegrind/traceitemview.cpp create mode 100644 kcachegrind/kcachegrind/traceitemview.h create mode 100644 kcachegrind/kcachegrind/treemap.cpp create mode 100644 kcachegrind/kcachegrind/treemap.h create mode 100644 kcachegrind/kcachegrind/utils.cpp create mode 100644 kcachegrind/kcachegrind/utils.h create mode 100644 kcachegrind/kcachegrind/x-kcachegrind.desktop create mode 100644 kcachegrind/pics/Makefile.am create mode 100644 kcachegrind/pics/hicolor/Makefile.am create mode 100644 kcachegrind/pics/hicolor/hi16-action-fromrec.png create mode 100644 kcachegrind/pics/hicolor/hi16-action-percent.png create mode 100644 kcachegrind/pics/hicolor/hi16-action-recrec.png create mode 100644 kcachegrind/pics/hicolor/hi16-action-torec.png create mode 100644 kcachegrind/pics/hicolor/hi22-action-percent.png create mode 100644 kcachegrind/pics/hicolor/hi32-action-percent.png create mode 100644 kcachegrind/tests/cg-badcompression1 create mode 100644 kcachegrind/tests/cg-badcostline1 create mode 100644 kcachegrind/tests/cg-badposition create mode 100644 kcachegrind/version.h.in create mode 100644 kdeaccounts-plugin/Makefile.am create mode 100644 kdeaccounts-plugin/README create mode 100644 kdeaccounts-plugin/kdeaccountsformat.cpp create mode 100644 kdeaccounts-plugin/kdeaccountsformat.h create mode 100644 kdeaccounts-plugin/kdeaccountsplugin.desktop create mode 100644 kdepalettes/KDE_Gimp create mode 100644 kdepalettes/README create mode 100644 kdepalettes/kde_xpaintrc create mode 100644 kdesdk.lsm create mode 100644 kfile-plugins/Makefile.am create mode 100644 kfile-plugins/c++/Makefile.am create mode 100644 kfile-plugins/c++/kfile_cpp.cpp create mode 100644 kfile-plugins/c++/kfile_cpp.desktop create mode 100644 kfile-plugins/c++/kfile_cpp.h create mode 100644 kfile-plugins/c++/kfile_h.desktop create mode 100644 kfile-plugins/diff/Makefile.am create mode 100644 kfile-plugins/diff/kfile_diff.cpp create mode 100644 kfile-plugins/diff/kfile_diff.desktop create mode 100644 kfile-plugins/diff/kfile_diff.h create mode 100644 kfile-plugins/ts/Makefile.am create mode 100644 kfile-plugins/ts/kfile_ts.cpp create mode 100644 kfile-plugins/ts/kfile_ts.desktop create mode 100644 kfile-plugins/ts/kfile_ts.h create mode 100644 kioslave/Makefile.am create mode 100644 kioslave/svn/AUTHORS create mode 100644 kioslave/svn/COPYING create mode 100644 kioslave/svn/Makefile.am create mode 100644 kioslave/svn/README create mode 100644 kioslave/svn/TODO create mode 100644 kioslave/svn/configure.in.bot create mode 100644 kioslave/svn/configure.in.in create mode 100644 kioslave/svn/icons/Makefile.am create mode 100644 kioslave/svn/icons/cr128-action-svn_add.png create mode 100644 kioslave/svn/icons/cr128-action-svn_branch.png create mode 100644 kioslave/svn/icons/cr128-action-svn_merge.png create mode 100644 kioslave/svn/icons/cr128-action-svn_remove.png create mode 100644 kioslave/svn/icons/cr128-action-svn_status.png create mode 100644 kioslave/svn/icons/cr128-action-svn_switch.png create mode 100644 kioslave/svn/icons/cr16-action-svn_add.png create mode 100644 kioslave/svn/icons/cr16-action-svn_branch.png create mode 100644 kioslave/svn/icons/cr16-action-svn_merge.png create mode 100644 kioslave/svn/icons/cr16-action-svn_remove.png create mode 100644 kioslave/svn/icons/cr16-action-svn_status.png create mode 100644 kioslave/svn/icons/cr16-action-svn_switch.png create mode 100644 kioslave/svn/icons/cr22-action-svn_add.png create mode 100644 kioslave/svn/icons/cr22-action-svn_branch.png create mode 100644 kioslave/svn/icons/cr22-action-svn_merge.png create mode 100644 kioslave/svn/icons/cr22-action-svn_remove.png create mode 100644 kioslave/svn/icons/cr22-action-svn_status.png create mode 100644 kioslave/svn/icons/cr22-action-svn_switch.png create mode 100644 kioslave/svn/icons/cr32-action-svn_add.png create mode 100644 kioslave/svn/icons/cr32-action-svn_branch.png create mode 100644 kioslave/svn/icons/cr32-action-svn_merge.png create mode 100644 kioslave/svn/icons/cr32-action-svn_remove.png create mode 100644 kioslave/svn/icons/cr32-action-svn_status.png create mode 100644 kioslave/svn/icons/cr32-action-svn_switch.png create mode 100644 kioslave/svn/icons/cr48-action-svn_add.png create mode 100644 kioslave/svn/icons/cr48-action-svn_branch.png create mode 100644 kioslave/svn/icons/cr48-action-svn_merge.png create mode 100644 kioslave/svn/icons/cr48-action-svn_remove.png create mode 100644 kioslave/svn/icons/cr48-action-svn_status.png create mode 100644 kioslave/svn/icons/cr48-action-svn_switch.png create mode 100644 kioslave/svn/icons/cr64-action-svn_add.png create mode 100644 kioslave/svn/icons/cr64-action-svn_branch.png create mode 100644 kioslave/svn/icons/cr64-action-svn_merge.png create mode 100644 kioslave/svn/icons/cr64-action-svn_remove.png create mode 100644 kioslave/svn/icons/cr64-action-svn_status.png create mode 100644 kioslave/svn/icons/cr64-action-svn_switch.png create mode 100644 kioslave/svn/icons/crsc-action-svn_add.svgz create mode 100644 kioslave/svn/icons/crsc-action-svn_branch.svgz create mode 100644 kioslave/svn/icons/crsc-action-svn_merge.svgz create mode 100644 kioslave/svn/icons/crsc-action-svn_remove.svgz create mode 100644 kioslave/svn/icons/crsc-action-svn_status.svgz create mode 100644 kioslave/svn/icons/crsc-action-svn_switch.svgz create mode 100644 kioslave/svn/ksvnd/Makefile.am create mode 100644 kioslave/svn/ksvnd/commitdlg.ui create mode 100644 kioslave/svn/ksvnd/commitdlg.ui.h create mode 100644 kioslave/svn/ksvnd/ksvnd.cpp create mode 100644 kioslave/svn/ksvnd/ksvnd.desktop create mode 100644 kioslave/svn/ksvnd/ksvnd.h create mode 100644 kioslave/svn/svn+file.protocol create mode 100644 kioslave/svn/svn+http.protocol create mode 100644 kioslave/svn/svn+https.protocol create mode 100644 kioslave/svn/svn+ssh.protocol create mode 100644 kioslave/svn/svn.cpp create mode 100644 kioslave/svn/svn.h create mode 100644 kioslave/svn/svn.protocol create mode 100644 kioslave/svn/svnhelper/Makefile.am create mode 100644 kioslave/svn/svnhelper/apply_patch.desktop create mode 100644 kioslave/svn/svnhelper/kio_svn_helper.cpp create mode 100644 kioslave/svn/svnhelper/kio_svn_helper.h create mode 100644 kioslave/svn/svnhelper/subversion.desktop create mode 100644 kioslave/svn/svnhelper/subversion_toplevel.desktop create mode 100644 kioslave/svn/svnhelper/subversioncheckout.ui create mode 100644 kioslave/svn/svnhelper/subversiondiff.ui create mode 100644 kioslave/svn/svnhelper/subversionlog.ui create mode 100644 kioslave/svn/svnhelper/subversionswitch.ui create mode 100644 kmtrace/Makefile.am create mode 100644 kmtrace/README create mode 100644 kmtrace/configure.in.in create mode 100644 kmtrace/demangle.cpp create mode 100644 kmtrace/kde.excludes create mode 100755 kmtrace/kminspector.in create mode 100644 kmtrace/kmtrace.cpp create mode 100644 kmtrace/ksotrace.cpp create mode 100644 kmtrace/ktrace.c create mode 100644 kmtrace/ktrace.h create mode 100644 kmtrace/match.cpp create mode 100644 kmtrace/mtrace.c create mode 100644 kompare/AUTHORS create mode 100644 kompare/COPYING create mode 100644 kompare/ChangeLog create mode 100644 kompare/DESIGN create mode 100644 kompare/Makefile.am create mode 100644 kompare/README create mode 100644 kompare/TODO create mode 100644 kompare/interfaces/Makefile.am create mode 100644 kompare/interfaces/kompareinterface.cpp create mode 100644 kompare/interfaces/kompareinterface.h create mode 100644 kompare/kompare.desktop create mode 100644 kompare/kompare_shell.cpp create mode 100644 kompare/kompare_shell.h create mode 100644 kompare/komparenavigationpart.desktop create mode 100644 kompare/komparenavtreepart/Makefile.am create mode 100644 kompare/komparenavtreepart/komparenavtreepart.cpp create mode 100644 kompare/komparenavtreepart/komparenavtreepart.desktop create mode 100644 kompare/komparenavtreepart/komparenavtreepart.h create mode 100644 kompare/komparepart/Makefile.am create mode 100644 kompare/komparepart/kompare_part.cpp create mode 100644 kompare/komparepart/kompare_part.h create mode 100644 kompare/komparepart/kompare_qsplitter.h create mode 100644 kompare/komparepart/kompareconnectwidget.cpp create mode 100644 kompare/komparepart/kompareconnectwidget.h create mode 100644 kompare/komparepart/komparelistview.cpp create mode 100644 kompare/komparepart/komparelistview.h create mode 100644 kompare/komparepart/komparepart.desktop create mode 100644 kompare/komparepart/komparepartui.rc create mode 100644 kompare/komparepart/kompareprefdlg.cpp create mode 100644 kompare/komparepart/kompareprefdlg.h create mode 100644 kompare/komparepart/komparesaveoptionsbase.ui create mode 100644 kompare/komparepart/komparesaveoptionswidget.cpp create mode 100644 kompare/komparepart/komparesaveoptionswidget.h create mode 100644 kompare/komparepart/komparesplitter.cpp create mode 100644 kompare/komparepart/komparesplitter.h create mode 100644 kompare/kompareui.rc create mode 100644 kompare/kompareurldialog.cpp create mode 100644 kompare/kompareurldialog.h create mode 100644 kompare/kompareviewpart.desktop create mode 100644 kompare/libdialogpages/Makefile.am create mode 100644 kompare/libdialogpages/diffpage.cpp create mode 100644 kompare/libdialogpages/diffpage.h create mode 100644 kompare/libdialogpages/diffsettings.cpp create mode 100644 kompare/libdialogpages/diffsettings.h create mode 100644 kompare/libdialogpages/filespage.cpp create mode 100644 kompare/libdialogpages/filespage.h create mode 100644 kompare/libdialogpages/filessettings.cpp create mode 100644 kompare/libdialogpages/filessettings.h create mode 100644 kompare/libdialogpages/pagebase.cpp create mode 100644 kompare/libdialogpages/pagebase.h create mode 100644 kompare/libdialogpages/settingsbase.cpp create mode 100644 kompare/libdialogpages/settingsbase.h create mode 100644 kompare/libdialogpages/viewpage.cpp create mode 100644 kompare/libdialogpages/viewpage.h create mode 100644 kompare/libdialogpages/viewsettings.cpp create mode 100644 kompare/libdialogpages/viewsettings.h create mode 100644 kompare/libdiff2/Makefile.am create mode 100644 kompare/libdiff2/cvsdiffparser.cpp create mode 100644 kompare/libdiff2/cvsdiffparser.h create mode 100644 kompare/libdiff2/difference.cpp create mode 100644 kompare/libdiff2/difference.h create mode 100644 kompare/libdiff2/diffhunk.cpp create mode 100644 kompare/libdiff2/diffhunk.h create mode 100644 kompare/libdiff2/diffmodel.cpp create mode 100644 kompare/libdiff2/diffmodel.h create mode 100644 kompare/libdiff2/diffmodellist.cpp create mode 100644 kompare/libdiff2/diffmodellist.h create mode 100644 kompare/libdiff2/diffparser.cpp create mode 100644 kompare/libdiff2/diffparser.h create mode 100644 kompare/libdiff2/kompare.h create mode 100644 kompare/libdiff2/komparemodellist.cpp create mode 100644 kompare/libdiff2/komparemodellist.h create mode 100644 kompare/libdiff2/kompareprocess.cpp create mode 100644 kompare/libdiff2/kompareprocess.h create mode 100644 kompare/libdiff2/levenshteintable.cpp create mode 100644 kompare/libdiff2/levenshteintable.h create mode 100644 kompare/libdiff2/parser.cpp create mode 100644 kompare/libdiff2/parser.h create mode 100644 kompare/libdiff2/parserbase.cpp create mode 100644 kompare/libdiff2/parserbase.h create mode 100644 kompare/libdiff2/perforceparser.cpp create mode 100644 kompare/libdiff2/perforceparser.h create mode 100644 kompare/main.cpp create mode 100644 kompare/pics/Makefile.am create mode 100644 kompare/pics/hi128-app-kompare.png create mode 100644 kompare/pics/hi16-app-kompare.png create mode 100644 kompare/pics/hi22-app-kompare.png create mode 100644 kompare/pics/hi32-app-kompare.png create mode 100644 kompare/pics/hi48-app-kompare.png create mode 100644 kompare/pics/hisc-app-kompare.svgz create mode 100644 kompare/tests/cvsdiff/context.diff create mode 100644 kompare/tests/cvsdiff/contextm.diff create mode 100644 kompare/tests/cvsdiff/ed.diff create mode 100644 kompare/tests/cvsdiff/edm.diff create mode 100644 kompare/tests/cvsdiff/normal.diff create mode 100644 kompare/tests/cvsdiff/normalm.diff create mode 100644 kompare/tests/cvsdiff/rcs.diff create mode 100644 kompare/tests/cvsdiff/rcsm.diff create mode 100644 kompare/tests/cvsdiff/unified.diff create mode 100644 kompare/tests/cvsdiff/unifiedm.diff create mode 100644 kompare/tests/diff/context.diff create mode 100644 kompare/tests/diff/contextm.diff create mode 100644 kompare/tests/diff/contextp.diff create mode 100644 kompare/tests/diff/ed.diff create mode 100644 kompare/tests/diff/edm.diff create mode 100644 kompare/tests/diff/normal.diff create mode 100644 kompare/tests/diff/normalm.diff create mode 100644 kompare/tests/diff/rcs.diff create mode 100644 kompare/tests/diff/rcsm.diff create mode 100644 kompare/tests/diff/unified.diff create mode 100644 kompare/tests/diff/unifiedm.diff create mode 100644 kompare/tests/diff/unifiedp.diff create mode 100644 kompare/tests/perforce/context.diff create mode 100644 kompare/tests/perforce/contextm.diff create mode 100644 kompare/tests/perforce/rcs.diff create mode 100644 kompare/tests/perforce/rcsm.diff create mode 100644 kompare/tests/perforce/unified.diff create mode 100644 kompare/tests/perforce/unifiedm.diff create mode 100644 kompare/tests/subversion/context.diff create mode 100644 kompare/tests/subversion/contextm.diff create mode 100644 kompare/tests/subversion/ed.diff create mode 100644 kompare/tests/subversion/edm.diff create mode 100644 kompare/tests/subversion/normal.diff create mode 100644 kompare/tests/subversion/normalm.diff create mode 100644 kompare/tests/subversion/rcs.diff create mode 100644 kompare/tests/subversion/rcsm.diff create mode 100644 kompare/tests/subversion/unified.diff create mode 100644 kompare/tests/subversion/unifiedm.diff create mode 100644 kprofilemethod/Makefile.am create mode 100644 kprofilemethod/README create mode 100644 kprofilemethod/kprofilemethod.h create mode 100644 kspy/Makefile.am create mode 100644 kspy/README create mode 100644 kspy/classinfoview.cpp create mode 100644 kspy/classinfoview.h create mode 100644 kspy/kspy.h create mode 100644 kspy/main.cpp create mode 100644 kspy/navview.cpp create mode 100644 kspy/navview.h create mode 100644 kspy/navviewitem.cpp create mode 100644 kspy/navviewitem.h create mode 100644 kspy/propsview.cpp create mode 100644 kspy/propsview.h create mode 100644 kspy/receiversview.cpp create mode 100644 kspy/receiversview.h create mode 100644 kspy/sigslotview.cpp create mode 100644 kspy/sigslotview.h create mode 100644 kspy/spy.cpp create mode 100644 kspy/spy.h create mode 100644 kstartperf/Makefile.am create mode 100644 kstartperf/README create mode 100644 kstartperf/kstartperf.cpp create mode 100644 kstartperf/libkstartperf.c create mode 100644 kuiviewer/Makefile.am create mode 100644 kuiviewer/designerthumbnail.desktop create mode 100644 kuiviewer/hi16-app-kuiviewer.png create mode 100644 kuiviewer/hi32-app-kuiviewer.png create mode 100644 kuiviewer/hi48-app-kuiviewer.png create mode 100644 kuiviewer/kuiviewer.cpp create mode 100644 kuiviewer/kuiviewer.desktop create mode 100644 kuiviewer/kuiviewer.h create mode 100644 kuiviewer/kuiviewer_part.cpp create mode 100644 kuiviewer/kuiviewer_part.desktop create mode 100644 kuiviewer/kuiviewer_part.h create mode 100644 kuiviewer/kuiviewer_part.rc create mode 100644 kuiviewer/kuiviewerui.rc create mode 100644 kuiviewer/lo16-app-kuiviewer.png create mode 100644 kuiviewer/lo32-app-kuiviewer.png create mode 100644 kuiviewer/main.cpp create mode 100644 kuiviewer/quicreator.cpp create mode 100644 kuiviewer/quicreator.h create mode 100644 kunittest/Makefile.am create mode 100644 kunittest/dcopinterface.h create mode 100644 kunittest/example/Makefile.am create mode 100644 kunittest/example/module/Makefile.am create mode 100644 kunittest/example/module/sampleextra.cpp create mode 100644 kunittest/example/module/sampleextra.h create mode 100644 kunittest/example/module/samplemodule.cpp create mode 100644 kunittest/example/module/samplemodule.h create mode 100644 kunittest/example/module/samplemodule2.cpp create mode 100644 kunittest/example/module/samplemodule2.h create mode 100644 kunittest/example/module/sampletests.cpp create mode 100644 kunittest/example/module/sampletests.h create mode 100644 kunittest/example/simple/Makefile.am create mode 100644 kunittest/example/simple/main.cpp create mode 100644 kunittest/example/simple/maingui.cpp create mode 100644 kunittest/example/simple/sampletest.cpp create mode 100644 kunittest/example/simple/sampletest.h create mode 100644 kunittest/guimodrunner.cpp create mode 100755 kunittest/kunittest create mode 100755 kunittest/kunittest_debughelper create mode 100755 kunittest/kunittestmod create mode 100644 kunittest/runnergui.cpp create mode 100644 kunittest/runnergui.h create mode 100644 kunittest/testerwidget.ui create mode 100644 kunittest/testerwidget.ui.h create mode 100644 poxml/GettextLexer.cpp create mode 100644 poxml/GettextLexer.hpp create mode 100644 poxml/GettextParser.cpp create mode 100644 poxml/GettextParser.hpp create mode 100644 poxml/GettextParserTokenTypes.hpp create mode 100644 poxml/GettextParserTokenTypes.txt create mode 100644 poxml/Makefile.am create mode 100644 poxml/antlr/AUTHORS create mode 100644 poxml/antlr/COPYING create mode 100644 poxml/antlr/ChangeLog create mode 100644 poxml/antlr/INSTALL create mode 100644 poxml/antlr/Makefile.am create mode 100644 poxml/antlr/README create mode 100644 poxml/antlr/TODO create mode 100644 poxml/antlr/antlr/ANTLRException.hpp create mode 100644 poxml/antlr/antlr/AST.hpp create mode 100644 poxml/antlr/antlr/ASTArray.hpp create mode 100644 poxml/antlr/antlr/ASTFactory.hpp create mode 100644 poxml/antlr/antlr/ASTNULLType.hpp create mode 100644 poxml/antlr/antlr/ASTPair.hpp create mode 100644 poxml/antlr/antlr/ASTRefCount.hpp create mode 100644 poxml/antlr/antlr/BaseAST.hpp create mode 100644 poxml/antlr/antlr/BitSet.hpp create mode 100644 poxml/antlr/antlr/CharBuffer.hpp create mode 100644 poxml/antlr/antlr/CharScanner.hpp create mode 100644 poxml/antlr/antlr/CharStreamException.hpp create mode 100644 poxml/antlr/antlr/CharStreamIOException.hpp create mode 100644 poxml/antlr/antlr/CircularQueue.hpp create mode 100644 poxml/antlr/antlr/CommonAST.hpp create mode 100644 poxml/antlr/antlr/CommonASTWithHiddenTokens.hpp create mode 100644 poxml/antlr/antlr/CommonHiddenStreamToken.hpp create mode 100644 poxml/antlr/antlr/CommonToken.hpp create mode 100644 poxml/antlr/antlr/InputBuffer.hpp create mode 100644 poxml/antlr/antlr/LLkParser.hpp create mode 100644 poxml/antlr/antlr/LexerSharedInputState.hpp create mode 100644 poxml/antlr/antlr/Makefile.am create mode 100644 poxml/antlr/antlr/MismatchedCharException.hpp create mode 100644 poxml/antlr/antlr/MismatchedTokenException.hpp create mode 100644 poxml/antlr/antlr/NoViableAltException.hpp create mode 100644 poxml/antlr/antlr/NoViableAltForCharException.hpp create mode 100644 poxml/antlr/antlr/Parser.hpp create mode 100644 poxml/antlr/antlr/ParserSharedInputState.hpp create mode 100644 poxml/antlr/antlr/RecognitionException.hpp create mode 100644 poxml/antlr/antlr/RefCount.hpp create mode 100644 poxml/antlr/antlr/SemanticException.hpp create mode 100644 poxml/antlr/antlr/String.hpp create mode 100644 poxml/antlr/antlr/Token.hpp create mode 100644 poxml/antlr/antlr/TokenBuffer.hpp create mode 100644 poxml/antlr/antlr/TokenStream.hpp create mode 100644 poxml/antlr/antlr/TokenStreamBasicFilter.hpp create mode 100644 poxml/antlr/antlr/TokenStreamException.hpp create mode 100644 poxml/antlr/antlr/TokenStreamHiddenTokenFilter.hpp create mode 100644 poxml/antlr/antlr/TokenStreamIOException.hpp create mode 100644 poxml/antlr/antlr/TokenStreamRecognitionException.hpp create mode 100644 poxml/antlr/antlr/TokenStreamRetryException.hpp create mode 100644 poxml/antlr/antlr/TokenStreamSelector.hpp create mode 100644 poxml/antlr/antlr/TreeParser.hpp create mode 100644 poxml/antlr/antlr/TreeParserSharedInputState.hpp create mode 100644 poxml/antlr/antlr/config.hpp create mode 100644 poxml/antlr/configure.in create mode 100644 poxml/antlr/src/ANTLRException.cpp create mode 100644 poxml/antlr/src/ASTFactory.cpp create mode 100644 poxml/antlr/src/ASTRefCount.cpp create mode 100644 poxml/antlr/src/BaseAST.cpp create mode 100644 poxml/antlr/src/BitSet.cpp create mode 100644 poxml/antlr/src/CharBuffer.cpp create mode 100644 poxml/antlr/src/CharScanner.cpp create mode 100644 poxml/antlr/src/CommonAST.cpp create mode 100644 poxml/antlr/src/CommonASTWithHiddenTokens.cpp create mode 100644 poxml/antlr/src/CommonHiddenStreamToken.cpp create mode 100644 poxml/antlr/src/CommonToken.cpp create mode 100644 poxml/antlr/src/InputBuffer.cpp create mode 100644 poxml/antlr/src/LLkParser.cpp create mode 100644 poxml/antlr/src/LexerSharedInputState.cpp create mode 100644 poxml/antlr/src/Makefile.am create mode 100644 poxml/antlr/src/MismatchedCharException.cpp create mode 100644 poxml/antlr/src/MismatchedTokenException.cpp create mode 100644 poxml/antlr/src/NoViableAltException.cpp create mode 100644 poxml/antlr/src/NoViableAltForCharException.cpp create mode 100644 poxml/antlr/src/Parser.cpp create mode 100644 poxml/antlr/src/ParserSharedInputState.cpp create mode 100644 poxml/antlr/src/RecognitionException.cpp create mode 100644 poxml/antlr/src/String.cpp create mode 100644 poxml/antlr/src/Token.cpp create mode 100644 poxml/antlr/src/TokenBuffer.cpp create mode 100644 poxml/antlr/src/TokenStreamBasicFilter.cpp create mode 100644 poxml/antlr/src/TokenStreamHiddenTokenFilter.cpp create mode 100644 poxml/antlr/src/TokenStreamSelector.cpp create mode 100644 poxml/antlr/src/TreeParser.cpp create mode 100644 poxml/antlr/src/TreeParserSharedInputState.cpp create mode 100644 poxml/gettext.g create mode 100644 poxml/lauri.po create mode 100644 poxml/lauri.xml create mode 100644 poxml/parser.cpp create mode 100644 poxml/parser.h create mode 100644 poxml/po2xml.cpp create mode 100644 poxml/split.cpp create mode 100644 poxml/swappo.cpp create mode 100644 poxml/transxx.cpp create mode 100644 poxml/xml2pot.cpp create mode 100644 scheck/Makefile.am create mode 100644 scheck/README create mode 100644 scheck/bitmaps.h create mode 100644 scheck/scheck.cpp create mode 100644 scheck/scheck.h create mode 100644 scheck/scheck.themerc create mode 100644 scheck/status.txt create mode 100644 scripts/Makefile.am create mode 100644 scripts/README create mode 100644 scripts/add_trace.pl create mode 100755 scripts/adddebug create mode 100755 scripts/alldcop.rb create mode 100755 scripts/authors2xml.pl create mode 100644 scripts/build-progress.sh create mode 100755 scripts/cheatmake create mode 100755 scripts/check_licenses create mode 100755 scripts/colorcvs create mode 100644 scripts/colorcvsrc-sample create mode 100755 scripts/colorsvn create mode 100644 scripts/completions/bash/dcop create mode 100644 scripts/completions/zsh/_dcop create mode 100644 scripts/completions/zsh/_kcmshell create mode 100644 scripts/completions/zsh/_kdeinit_wrapper create mode 100644 scripts/completions/zsh/_kdekillall create mode 100644 scripts/completions/zsh/_makeobj create mode 100755 scripts/create_cvsignore create mode 100755 scripts/create_makefile create mode 100755 scripts/create_makefiles create mode 100755 scripts/create_svnignore create mode 100755 scripts/cvs-clean create mode 100755 scripts/cvs2dist create mode 100755 scripts/cvsaddcurrentdir create mode 100755 scripts/cvsbackport create mode 100755 scripts/cvsblame create mode 100755 scripts/cvscheck create mode 100755 scripts/cvsforwardport create mode 100755 scripts/cvsgettags create mode 100755 scripts/cvslastchange create mode 100755 scripts/cvslastlog create mode 100755 scripts/cvslastreferenced create mode 100755 scripts/cvsrevertlast create mode 100755 scripts/cvsversion create mode 100755 scripts/cxxmetric create mode 100755 scripts/extend_dmalloc create mode 100755 scripts/extractattr create mode 100755 scripts/extractrc create mode 100755 scripts/findmissingcrystal create mode 100644 scripts/fixfsfaddr.sed create mode 100644 scripts/fixheaders create mode 100644 scripts/fixkdeincludes create mode 100755 scripts/fixuifiles create mode 100644 scripts/gettext.patch create mode 100755 scripts/includemocs create mode 100755 scripts/kDebug2kdDebug.sh create mode 100755 scripts/kde-build create mode 100644 scripts/kde-buildrc create mode 100644 scripts/kde-devel-emacs.el create mode 100644 scripts/kde-devel-gdb create mode 100644 scripts/kde-devel-vim.vim create mode 100644 scripts/kde-emacs/HACKING create mode 100644 scripts/kde-emacs/dirvars.el create mode 100644 scripts/kde-emacs/kde-emacs-bindings.el create mode 100644 scripts/kde-emacs/kde-emacs-compat.el create mode 100644 scripts/kde-emacs/kde-emacs-core.el create mode 100644 scripts/kde-emacs/kde-emacs-doc.el create mode 100644 scripts/kde-emacs/kde-emacs-general.el create mode 100644 scripts/kde-emacs/kde-emacs-semantic.el create mode 100644 scripts/kde-emacs/kde-emacs-tips.texi create mode 100644 scripts/kde-emacs/kde-emacs-utils.el create mode 100644 scripts/kde-emacs/kde-emacs-vars.el create mode 100644 scripts/kde-emacs/kde-emacs.el create mode 100644 scripts/kde-emacs/klaralv.el create mode 100755 scripts/kde-spellcheck.pl create mode 100644 scripts/kde.supp create mode 100755 scripts/kdedoc create mode 100755 scripts/kdekillall create mode 100755 scripts/kdelnk2desktop.py create mode 100755 scripts/kdemangen.pl create mode 100755 scripts/kdesvn-build create mode 100644 scripts/kdesvn-buildrc-sample create mode 100644 scripts/kdesvn-buildrc.xml create mode 100755 scripts/licensecheck create mode 100755 scripts/makeobj create mode 100755 scripts/noncvslist create mode 100755 scripts/nonsvnlist create mode 100755 scripts/package_crystalsvg create mode 100644 scripts/png2mng.pl create mode 100755 scripts/pruneemptydirs create mode 100755 scripts/qtdoc create mode 100644 scripts/rc2kcfgxt.pl create mode 100755 scripts/svn-clean create mode 100755 scripts/svn2dist create mode 100755 scripts/svnaddcurrentdir create mode 100755 scripts/svnbackport create mode 100755 scripts/svnchangesince create mode 100755 scripts/svnforwardport create mode 100755 scripts/svngettags create mode 100755 scripts/svnlastchange create mode 100755 scripts/svnlastlog create mode 100755 scripts/svnrevertlast create mode 100755 scripts/svnversions create mode 100755 scripts/zonetab2pot.py create mode 100644 umbrello/AUTHORS create mode 100644 umbrello/COPYING create mode 100644 umbrello/ChangeLog create mode 100644 umbrello/INSTALL create mode 100644 umbrello/Makefile.am create mode 100644 umbrello/README create mode 100644 umbrello/THANKS create mode 100644 umbrello/TODO create mode 100644 umbrello/VERSION create mode 100644 umbrello/configure.in.in create mode 100755 umbrello/make-umbrello-release.sh create mode 100644 umbrello/umbrello/Makefile.am create mode 100644 umbrello/umbrello/activitywidget.cpp create mode 100644 umbrello/umbrello/activitywidget.h create mode 100644 umbrello/umbrello/actor.cpp create mode 100644 umbrello/umbrello/actor.h create mode 100644 umbrello/umbrello/actorwidget.cpp create mode 100644 umbrello/umbrello/actorwidget.h create mode 100644 umbrello/umbrello/aligntoolbar.cpp create mode 100644 umbrello/umbrello/aligntoolbar.h create mode 100644 umbrello/umbrello/artifact.cpp create mode 100644 umbrello/umbrello/artifact.h create mode 100644 umbrello/umbrello/artifactwidget.cpp create mode 100644 umbrello/umbrello/artifactwidget.h create mode 100644 umbrello/umbrello/association.cpp create mode 100644 umbrello/umbrello/association.h create mode 100644 umbrello/umbrello/associationwidget.cpp create mode 100644 umbrello/umbrello/associationwidget.h create mode 100644 umbrello/umbrello/associationwidgetlist.h create mode 100644 umbrello/umbrello/assocrules.cpp create mode 100644 umbrello/umbrello/assocrules.h create mode 100644 umbrello/umbrello/attribute.cpp create mode 100644 umbrello/umbrello/attribute.h create mode 100644 umbrello/umbrello/autolayout/Makefile.am create mode 100644 umbrello/umbrello/autolayout/_graph.h create mode 100644 umbrello/umbrello/autolayout/autolayout.h create mode 100644 umbrello/umbrello/autolayout/autolayoutdlg.cpp create mode 100644 umbrello/umbrello/autolayout/autolayoutdlg.h create mode 100644 umbrello/umbrello/autolayout/autolayouter.cpp create mode 100644 umbrello/umbrello/autolayout/autolayouter.h create mode 100644 umbrello/umbrello/autolayout/autolayouteradapter.cpp create mode 100644 umbrello/umbrello/autolayout/autolayouteradapter.h create mode 100644 umbrello/umbrello/autolayout/baseinclude.h create mode 100644 umbrello/umbrello/autolayout/canvas.h create mode 100644 umbrello/umbrello/autolayout/diagram.h create mode 100644 umbrello/umbrello/autolayout/diagram_interface.h create mode 100644 umbrello/umbrello/autolayout/dotautolayouter.cpp create mode 100644 umbrello/umbrello/autolayout/dotautolayouter.h create mode 100644 umbrello/umbrello/autolayout/graphvizautolayouter.cpp create mode 100644 umbrello/umbrello/autolayout/graphvizautolayouter.h create mode 100644 umbrello/umbrello/autolayout/graphvizgraph.cpp create mode 100644 umbrello/umbrello/autolayout/graphvizgraph.h create mode 100644 umbrello/umbrello/autolayout/graphviznode.cpp create mode 100644 umbrello/umbrello/autolayout/graphviznode.h create mode 100644 umbrello/umbrello/autolayout/newautolayoutdialog.ui create mode 100644 umbrello/umbrello/autolayout/node.h create mode 100644 umbrello/umbrello/autolayout/simplecanvas.cpp create mode 100644 umbrello/umbrello/autolayout/simplecanvas.h create mode 100644 umbrello/umbrello/boxwidget.cpp create mode 100644 umbrello/umbrello/boxwidget.h create mode 100644 umbrello/umbrello/classifier.cpp create mode 100644 umbrello/umbrello/classifier.h create mode 100644 umbrello/umbrello/classifiercodedocument.cpp create mode 100644 umbrello/umbrello/classifiercodedocument.h create mode 100644 umbrello/umbrello/classifierlistitem.cpp create mode 100644 umbrello/umbrello/classifierlistitem.h create mode 100644 umbrello/umbrello/classifierwidget.cpp create mode 100644 umbrello/umbrello/classifierwidget.h create mode 100644 umbrello/umbrello/clipboard/Makefile.am create mode 100644 umbrello/umbrello/clipboard/idchangelog.cpp create mode 100644 umbrello/umbrello/clipboard/idchangelog.h create mode 100644 umbrello/umbrello/clipboard/umlclipboard.cpp create mode 100644 umbrello/umbrello/clipboard/umlclipboard.h create mode 100644 umbrello/umbrello/clipboard/umldrag.cpp create mode 100644 umbrello/umbrello/clipboard/umldrag.h create mode 100644 umbrello/umbrello/cmdlineexportallviewsevent.cpp create mode 100644 umbrello/umbrello/cmdlineexportallviewsevent.h create mode 100644 umbrello/umbrello/codeaccessormethod.cpp create mode 100644 umbrello/umbrello/codeaccessormethod.h create mode 100644 umbrello/umbrello/codeaccessormethodlist.h create mode 100644 umbrello/umbrello/codeblock.cpp create mode 100644 umbrello/umbrello/codeblock.h create mode 100644 umbrello/umbrello/codeblockwithcomments.cpp create mode 100644 umbrello/umbrello/codeblockwithcomments.h create mode 100644 umbrello/umbrello/codeclassfield.cpp create mode 100644 umbrello/umbrello/codeclassfield.h create mode 100644 umbrello/umbrello/codeclassfielddeclarationblock.cpp create mode 100644 umbrello/umbrello/codeclassfielddeclarationblock.h create mode 100644 umbrello/umbrello/codeclassfieldlist.h create mode 100644 umbrello/umbrello/codecomment.cpp create mode 100644 umbrello/umbrello/codecomment.h create mode 100644 umbrello/umbrello/codedocument.cpp create mode 100644 umbrello/umbrello/codedocument.h create mode 100644 umbrello/umbrello/codedocumentlist.h create mode 100644 umbrello/umbrello/codegenerationpolicy.cpp create mode 100644 umbrello/umbrello/codegenerationpolicy.h create mode 100644 umbrello/umbrello/codegenerator.cpp create mode 100644 umbrello/umbrello/codegenerator.h create mode 100644 umbrello/umbrello/codegenerators/Makefile.am create mode 100644 umbrello/umbrello/codegenerators/adawriter.cpp create mode 100644 umbrello/umbrello/codegenerators/adawriter.h create mode 100644 umbrello/umbrello/codegenerators/aswriter.cpp create mode 100644 umbrello/umbrello/codegenerators/aswriter.h create mode 100644 umbrello/umbrello/codegenerators/classifierinfo.cpp create mode 100644 umbrello/umbrello/codegenerators/classifierinfo.h create mode 100644 umbrello/umbrello/codegenerators/codegen_utils.cpp create mode 100644 umbrello/umbrello/codegenerators/codegen_utils.h create mode 100644 umbrello/umbrello/codegenerators/codegenfactory.cpp create mode 100644 umbrello/umbrello/codegenerators/codegenfactory.h create mode 100644 umbrello/umbrello/codegenerators/codegenpolicyext.h create mode 100644 umbrello/umbrello/codegenerators/cppcodeclassfield.cpp create mode 100644 umbrello/umbrello/codegenerators/cppcodeclassfield.h create mode 100644 umbrello/umbrello/codegenerators/cppcodecomment.cpp create mode 100644 umbrello/umbrello/codegenerators/cppcodecomment.h create mode 100644 umbrello/umbrello/codegenerators/cppcodedocumentation.cpp create mode 100644 umbrello/umbrello/codegenerators/cppcodedocumentation.h create mode 100644 umbrello/umbrello/codegenerators/cppcodegenerationform.cpp create mode 100644 umbrello/umbrello/codegenerators/cppcodegenerationform.h create mode 100644 umbrello/umbrello/codegenerators/cppcodegenerationformbase.ui create mode 100644 umbrello/umbrello/codegenerators/cppcodegenerationpolicy.cpp create mode 100644 umbrello/umbrello/codegenerators/cppcodegenerationpolicy.h create mode 100644 umbrello/umbrello/codegenerators/cppcodegenerationpolicypage.cpp create mode 100644 umbrello/umbrello/codegenerators/cppcodegenerationpolicypage.h create mode 100644 umbrello/umbrello/codegenerators/cppcodegenerator.cpp create mode 100644 umbrello/umbrello/codegenerators/cppcodegenerator.h create mode 100644 umbrello/umbrello/codegenerators/cppheaderclassdeclarationblock.cpp create mode 100644 umbrello/umbrello/codegenerators/cppheaderclassdeclarationblock.h create mode 100644 umbrello/umbrello/codegenerators/cppheadercodeaccessormethod.cpp create mode 100644 umbrello/umbrello/codegenerators/cppheadercodeaccessormethod.h create mode 100644 umbrello/umbrello/codegenerators/cppheadercodeclassfielddeclarationblock.cpp create mode 100644 umbrello/umbrello/codegenerators/cppheadercodeclassfielddeclarationblock.h create mode 100644 umbrello/umbrello/codegenerators/cppheadercodedocument.cpp create mode 100644 umbrello/umbrello/codegenerators/cppheadercodedocument.h create mode 100644 umbrello/umbrello/codegenerators/cppheadercodeoperation.cpp create mode 100644 umbrello/umbrello/codegenerators/cppheadercodeoperation.h create mode 100644 umbrello/umbrello/codegenerators/cppmakecodedocument.cpp create mode 100644 umbrello/umbrello/codegenerators/cppmakecodedocument.h create mode 100644 umbrello/umbrello/codegenerators/cppsourcecodeaccessormethod.cpp create mode 100644 umbrello/umbrello/codegenerators/cppsourcecodeaccessormethod.h create mode 100644 umbrello/umbrello/codegenerators/cppsourcecodeclassfielddeclarationblock.cpp create mode 100644 umbrello/umbrello/codegenerators/cppsourcecodeclassfielddeclarationblock.h create mode 100644 umbrello/umbrello/codegenerators/cppsourcecodedocument.cpp create mode 100644 umbrello/umbrello/codegenerators/cppsourcecodedocument.h create mode 100644 umbrello/umbrello/codegenerators/cppsourcecodeoperation.cpp create mode 100644 umbrello/umbrello/codegenerators/cppsourcecodeoperation.h create mode 100644 umbrello/umbrello/codegenerators/cppwriter.cpp create mode 100644 umbrello/umbrello/codegenerators/cppwriter.h create mode 100644 umbrello/umbrello/codegenerators/csharpwriter.cpp create mode 100644 umbrello/umbrello/codegenerators/csharpwriter.h create mode 100644 umbrello/umbrello/codegenerators/dwriter.cpp create mode 100644 umbrello/umbrello/codegenerators/dwriter.h create mode 100644 umbrello/umbrello/codegenerators/idlwriter.cpp create mode 100644 umbrello/umbrello/codegenerators/idlwriter.h create mode 100644 umbrello/umbrello/codegenerators/javaantcodedocument.cpp create mode 100644 umbrello/umbrello/codegenerators/javaantcodedocument.h create mode 100644 umbrello/umbrello/codegenerators/javaclassdeclarationblock.cpp create mode 100644 umbrello/umbrello/codegenerators/javaclassdeclarationblock.h create mode 100644 umbrello/umbrello/codegenerators/javaclassifiercodedocument.cpp create mode 100644 umbrello/umbrello/codegenerators/javaclassifiercodedocument.h create mode 100644 umbrello/umbrello/codegenerators/javacodeaccessormethod.cpp create mode 100644 umbrello/umbrello/codegenerators/javacodeaccessormethod.h create mode 100644 umbrello/umbrello/codegenerators/javacodeclassfield.cpp create mode 100644 umbrello/umbrello/codegenerators/javacodeclassfield.h create mode 100644 umbrello/umbrello/codegenerators/javacodeclassfielddeclarationblock.cpp create mode 100644 umbrello/umbrello/codegenerators/javacodeclassfielddeclarationblock.h create mode 100644 umbrello/umbrello/codegenerators/javacodecomment.cpp create mode 100644 umbrello/umbrello/codegenerators/javacodecomment.h create mode 100644 umbrello/umbrello/codegenerators/javacodedocumentation.cpp create mode 100644 umbrello/umbrello/codegenerators/javacodedocumentation.h create mode 100644 umbrello/umbrello/codegenerators/javacodegenerationformbase.ui create mode 100644 umbrello/umbrello/codegenerators/javacodegenerationpolicy.cpp create mode 100644 umbrello/umbrello/codegenerators/javacodegenerationpolicy.h create mode 100644 umbrello/umbrello/codegenerators/javacodegenerationpolicypage.cpp create mode 100644 umbrello/umbrello/codegenerators/javacodegenerationpolicypage.h create mode 100644 umbrello/umbrello/codegenerators/javacodegenerator.cpp create mode 100644 umbrello/umbrello/codegenerators/javacodegenerator.h create mode 100644 umbrello/umbrello/codegenerators/javacodeoperation.cpp create mode 100644 umbrello/umbrello/codegenerators/javacodeoperation.h create mode 100644 umbrello/umbrello/codegenerators/javawriter.cpp create mode 100644 umbrello/umbrello/codegenerators/javawriter.h create mode 100644 umbrello/umbrello/codegenerators/jswriter.cpp create mode 100644 umbrello/umbrello/codegenerators/jswriter.h create mode 100644 umbrello/umbrello/codegenerators/pascalwriter.cpp create mode 100644 umbrello/umbrello/codegenerators/pascalwriter.h create mode 100644 umbrello/umbrello/codegenerators/perlwriter.cpp create mode 100644 umbrello/umbrello/codegenerators/perlwriter.h create mode 100644 umbrello/umbrello/codegenerators/php5writer.cpp create mode 100644 umbrello/umbrello/codegenerators/php5writer.h create mode 100644 umbrello/umbrello/codegenerators/phpwriter.cpp create mode 100644 umbrello/umbrello/codegenerators/phpwriter.h create mode 100644 umbrello/umbrello/codegenerators/pythonwriter.cpp create mode 100644 umbrello/umbrello/codegenerators/pythonwriter.h create mode 100644 umbrello/umbrello/codegenerators/rubyclassdeclarationblock.cpp create mode 100644 umbrello/umbrello/codegenerators/rubyclassdeclarationblock.h create mode 100644 umbrello/umbrello/codegenerators/rubyclassifiercodedocument.cpp create mode 100644 umbrello/umbrello/codegenerators/rubyclassifiercodedocument.h create mode 100644 umbrello/umbrello/codegenerators/rubycodeaccessormethod.cpp create mode 100644 umbrello/umbrello/codegenerators/rubycodeaccessormethod.h create mode 100644 umbrello/umbrello/codegenerators/rubycodeclassfield.cpp create mode 100644 umbrello/umbrello/codegenerators/rubycodeclassfield.h create mode 100644 umbrello/umbrello/codegenerators/rubycodeclassfielddeclarationblock.cpp create mode 100644 umbrello/umbrello/codegenerators/rubycodeclassfielddeclarationblock.h create mode 100644 umbrello/umbrello/codegenerators/rubycodecomment.cpp create mode 100644 umbrello/umbrello/codegenerators/rubycodecomment.h create mode 100644 umbrello/umbrello/codegenerators/rubycodedocumentation.cpp create mode 100644 umbrello/umbrello/codegenerators/rubycodedocumentation.h create mode 100644 umbrello/umbrello/codegenerators/rubycodegenerationformbase.ui create mode 100644 umbrello/umbrello/codegenerators/rubycodegenerationpolicy.cpp create mode 100644 umbrello/umbrello/codegenerators/rubycodegenerationpolicy.h create mode 100644 umbrello/umbrello/codegenerators/rubycodegenerationpolicypage.cpp create mode 100644 umbrello/umbrello/codegenerators/rubycodegenerationpolicypage.h create mode 100644 umbrello/umbrello/codegenerators/rubycodegenerator.cpp create mode 100644 umbrello/umbrello/codegenerators/rubycodegenerator.h create mode 100644 umbrello/umbrello/codegenerators/rubycodeoperation.cpp create mode 100644 umbrello/umbrello/codegenerators/rubycodeoperation.h create mode 100644 umbrello/umbrello/codegenerators/rubywriter.cpp create mode 100644 umbrello/umbrello/codegenerators/rubywriter.h create mode 100644 umbrello/umbrello/codegenerators/simplecodegenerator.cpp create mode 100644 umbrello/umbrello/codegenerators/simplecodegenerator.h create mode 100644 umbrello/umbrello/codegenerators/sqlwriter.cpp create mode 100644 umbrello/umbrello/codegenerators/sqlwriter.h create mode 100644 umbrello/umbrello/codegenerators/tclwriter.cpp create mode 100644 umbrello/umbrello/codegenerators/tclwriter.h create mode 100644 umbrello/umbrello/codegenerators/xmlcodecomment.cpp create mode 100644 umbrello/umbrello/codegenerators/xmlcodecomment.h create mode 100644 umbrello/umbrello/codegenerators/xmlelementcodeblock.cpp create mode 100644 umbrello/umbrello/codegenerators/xmlelementcodeblock.h create mode 100644 umbrello/umbrello/codegenerators/xmlschemawriter.cpp create mode 100644 umbrello/umbrello/codegenerators/xmlschemawriter.h create mode 100644 umbrello/umbrello/codegenobjectwithtextblocks.cpp create mode 100644 umbrello/umbrello/codegenobjectwithtextblocks.h create mode 100644 umbrello/umbrello/codeimport/Makefile.am create mode 100644 umbrello/umbrello/codeimport/adaimport.cpp create mode 100644 umbrello/umbrello/codeimport/adaimport.h create mode 100644 umbrello/umbrello/codeimport/classimport.cpp create mode 100644 umbrello/umbrello/codeimport/classimport.h create mode 100644 umbrello/umbrello/codeimport/cppimport.cpp create mode 100644 umbrello/umbrello/codeimport/cppimport.h create mode 100644 umbrello/umbrello/codeimport/idlimport.cpp create mode 100644 umbrello/umbrello/codeimport/idlimport.h create mode 100644 umbrello/umbrello/codeimport/import_utils.cpp create mode 100644 umbrello/umbrello/codeimport/import_utils.h create mode 100644 umbrello/umbrello/codeimport/javaimport.cpp create mode 100644 umbrello/umbrello/codeimport/javaimport.h create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/Makefile.am create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/README create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/ast.cpp create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/ast.h create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/ast_utils.cpp create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/ast_utils.h create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/cpptree2uml.cpp create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/cpptree2uml.h create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/driver.cpp create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/driver.h create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/errors.cpp create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/errors.h create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/keywords.lut.h create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/lexer.cpp create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/lexer.h create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/lookup.cpp create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/lookup.h create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/parser.cpp create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/parser.h create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/tree_parser.cpp create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/tree_parser.h create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/urlutil.cpp create mode 100644 umbrello/umbrello/codeimport/kdevcppparser/urlutil.h create mode 100644 umbrello/umbrello/codeimport/nativeimportbase.cpp create mode 100644 umbrello/umbrello/codeimport/nativeimportbase.h create mode 100644 umbrello/umbrello/codeimport/pascalimport.cpp create mode 100644 umbrello/umbrello/codeimport/pascalimport.h create mode 100644 umbrello/umbrello/codeimport/pythonimport.cpp create mode 100644 umbrello/umbrello/codeimport/pythonimport.h create mode 100644 umbrello/umbrello/codemethodblock.cpp create mode 100644 umbrello/umbrello/codemethodblock.h create mode 100644 umbrello/umbrello/codeoperation.cpp create mode 100644 umbrello/umbrello/codeoperation.h create mode 100644 umbrello/umbrello/codeparameter.cpp create mode 100644 umbrello/umbrello/codeparameter.h create mode 100644 umbrello/umbrello/codeviewerstate.h create mode 100644 umbrello/umbrello/component.cpp create mode 100644 umbrello/umbrello/component.h create mode 100644 umbrello/umbrello/componentwidget.cpp create mode 100644 umbrello/umbrello/componentwidget.h create mode 100644 umbrello/umbrello/configurable.cpp create mode 100644 umbrello/umbrello/configurable.h create mode 100644 umbrello/umbrello/cr128-mime-umbrellofile.png create mode 100644 umbrello/umbrello/cr16-mime-umbrellofile.png create mode 100644 umbrello/umbrello/cr22-mime-umbrellofile.png create mode 100644 umbrello/umbrello/cr32-mime-umbrellofile.png create mode 100644 umbrello/umbrello/cr48-mime-umbrellofile.png create mode 100644 umbrello/umbrello/cr64-mime-umbrellofile.png create mode 100644 umbrello/umbrello/crsc-mime-umbrellofile.svgz create mode 100644 umbrello/umbrello/datatypewidget.cpp create mode 100644 umbrello/umbrello/datatypewidget.h create mode 100644 umbrello/umbrello/dialog_utils.cpp create mode 100644 umbrello/umbrello/dialog_utils.h create mode 100644 umbrello/umbrello/dialogs/Makefile.am create mode 100644 umbrello/umbrello/dialogs/activitydialog.cpp create mode 100644 umbrello/umbrello/dialogs/activitydialog.h create mode 100644 umbrello/umbrello/dialogs/activitypage.cpp create mode 100644 umbrello/umbrello/dialogs/activitypage.h create mode 100644 umbrello/umbrello/dialogs/assocgenpage.cpp create mode 100644 umbrello/umbrello/dialogs/assocgenpage.h create mode 100644 umbrello/umbrello/dialogs/assocpage.cpp create mode 100644 umbrello/umbrello/dialogs/assocpage.h create mode 100644 umbrello/umbrello/dialogs/assocpropdlg.cpp create mode 100644 umbrello/umbrello/dialogs/assocpropdlg.h create mode 100644 umbrello/umbrello/dialogs/assocrolepage.cpp create mode 100644 umbrello/umbrello/dialogs/assocrolepage.h create mode 100644 umbrello/umbrello/dialogs/classgenpage.cpp create mode 100644 umbrello/umbrello/dialogs/classgenpage.h create mode 100644 umbrello/umbrello/dialogs/classifierlistpage.cpp create mode 100644 umbrello/umbrello/dialogs/classifierlistpage.h create mode 100644 umbrello/umbrello/dialogs/classoptionspage.cpp create mode 100644 umbrello/umbrello/dialogs/classoptionspage.h create mode 100644 umbrello/umbrello/dialogs/classpropdlg.cpp create mode 100644 umbrello/umbrello/dialogs/classpropdlg.h create mode 100644 umbrello/umbrello/dialogs/classwizard.cpp create mode 100644 umbrello/umbrello/dialogs/classwizard.h create mode 100644 umbrello/umbrello/dialogs/codeeditor.cpp create mode 100644 umbrello/umbrello/dialogs/codeeditor.h create mode 100644 umbrello/umbrello/dialogs/codegenerationoptionsbase.ui create mode 100644 umbrello/umbrello/dialogs/codegenerationoptionspage.cpp create mode 100644 umbrello/umbrello/dialogs/codegenerationoptionspage.h create mode 100644 umbrello/umbrello/dialogs/codegenerationpolicybase.ui create mode 100644 umbrello/umbrello/dialogs/codegenerationpolicypage.cpp create mode 100644 umbrello/umbrello/dialogs/codegenerationpolicypage.h create mode 100644 umbrello/umbrello/dialogs/codegenerationwizard.cpp create mode 100644 umbrello/umbrello/dialogs/codegenerationwizard.h create mode 100644 umbrello/umbrello/dialogs/codegenerationwizardbase.ui create mode 100644 umbrello/umbrello/dialogs/codeviewerdialog.cpp create mode 100644 umbrello/umbrello/dialogs/codeviewerdialog.h create mode 100644 umbrello/umbrello/dialogs/codeviewerdialogbase.ui create mode 100644 umbrello/umbrello/dialogs/codevieweroptionsbase.ui create mode 100644 umbrello/umbrello/dialogs/codevieweroptionspage.cpp create mode 100644 umbrello/umbrello/dialogs/codevieweroptionspage.h create mode 100644 umbrello/umbrello/dialogs/defaultcodegenpolicypage.cpp create mode 100644 umbrello/umbrello/dialogs/defaultcodegenpolicypage.h create mode 100644 umbrello/umbrello/dialogs/diagramprintpage.cpp create mode 100644 umbrello/umbrello/dialogs/diagramprintpage.h create mode 100644 umbrello/umbrello/dialogs/diagrampropertiespage.ui create mode 100644 umbrello/umbrello/dialogs/exportallviewsdialog.cpp create mode 100644 umbrello/umbrello/dialogs/exportallviewsdialog.h create mode 100644 umbrello/umbrello/dialogs/exportallviewsdialogbase.ui create mode 100644 umbrello/umbrello/dialogs/notedialog.cpp create mode 100644 umbrello/umbrello/dialogs/notedialog.h create mode 100644 umbrello/umbrello/dialogs/overwritedialogue.cpp create mode 100644 umbrello/umbrello/dialogs/overwritedialogue.h create mode 100644 umbrello/umbrello/dialogs/parmpropdlg.cpp create mode 100644 umbrello/umbrello/dialogs/parmpropdlg.h create mode 100644 umbrello/umbrello/dialogs/pkgcontentspage.cpp create mode 100644 umbrello/umbrello/dialogs/pkgcontentspage.h create mode 100644 umbrello/umbrello/dialogs/selectopdlg.cpp create mode 100644 umbrello/umbrello/dialogs/selectopdlg.h create mode 100644 umbrello/umbrello/dialogs/settingsdlg.cpp create mode 100644 umbrello/umbrello/dialogs/settingsdlg.h create mode 100644 umbrello/umbrello/dialogs/statedialog.cpp create mode 100644 umbrello/umbrello/dialogs/statedialog.h create mode 100644 umbrello/umbrello/dialogs/umlattributedialog.cpp create mode 100644 umbrello/umbrello/dialogs/umlattributedialog.h create mode 100644 umbrello/umbrello/dialogs/umlentityattributedialog.cpp create mode 100644 umbrello/umbrello/dialogs/umlentityattributedialog.h create mode 100644 umbrello/umbrello/dialogs/umloperationdialog.cpp create mode 100644 umbrello/umbrello/dialogs/umloperationdialog.h create mode 100644 umbrello/umbrello/dialogs/umlroledialog.cpp create mode 100644 umbrello/umbrello/dialogs/umlroledialog.h create mode 100644 umbrello/umbrello/dialogs/umlroleproperties.cpp create mode 100644 umbrello/umbrello/dialogs/umlroleproperties.h create mode 100644 umbrello/umbrello/dialogs/umlrolepropertiesbase.ui create mode 100644 umbrello/umbrello/dialogs/umltemplatedialog.cpp create mode 100644 umbrello/umbrello/dialogs/umltemplatedialog.h create mode 100644 umbrello/umbrello/dialogs/umlviewdialog.cpp create mode 100644 umbrello/umbrello/dialogs/umlviewdialog.h create mode 100644 umbrello/umbrello/dialogs/umlwidgetcolorpage.cpp create mode 100644 umbrello/umbrello/dialogs/umlwidgetcolorpage.h create mode 100644 umbrello/umbrello/docgenerators/Makefile.am create mode 100644 umbrello/umbrello/docgenerators/common.ent create mode 100644 umbrello/umbrello/docgenerators/docbook2xhtml.xsl create mode 100644 umbrello/umbrello/docgenerators/docbookgenerator.cpp create mode 100644 umbrello/umbrello/docgenerators/docbookgenerator.h create mode 100644 umbrello/umbrello/docgenerators/main.cpp create mode 100644 umbrello/umbrello/docgenerators/xhtmlgenerator.cpp create mode 100644 umbrello/umbrello/docgenerators/xhtmlgenerator.h create mode 100644 umbrello/umbrello/docgenerators/xmi.css create mode 100644 umbrello/umbrello/docgenerators/xmi2docbook.sh create mode 100644 umbrello/umbrello/docgenerators/xmi2docbook.xsl create mode 100644 umbrello/umbrello/docwindow.cpp create mode 100644 umbrello/umbrello/docwindow.h create mode 100644 umbrello/umbrello/entity.cpp create mode 100644 umbrello/umbrello/entity.h create mode 100644 umbrello/umbrello/entityattribute.cpp create mode 100644 umbrello/umbrello/entityattribute.h create mode 100644 umbrello/umbrello/entitywidget.cpp create mode 100644 umbrello/umbrello/entitywidget.h create mode 100644 umbrello/umbrello/enum.cpp create mode 100644 umbrello/umbrello/enum.h create mode 100644 umbrello/umbrello/enumliteral.cpp create mode 100644 umbrello/umbrello/enumliteral.h create mode 100644 umbrello/umbrello/enumwidget.cpp create mode 100644 umbrello/umbrello/enumwidget.h create mode 100644 umbrello/umbrello/floatingtextwidget.cpp create mode 100644 umbrello/umbrello/floatingtextwidget.h create mode 100644 umbrello/umbrello/floatingtextwidgetcontroller.cpp create mode 100644 umbrello/umbrello/floatingtextwidgetcontroller.h create mode 100644 umbrello/umbrello/folder.cpp create mode 100644 umbrello/umbrello/folder.h create mode 100644 umbrello/umbrello/forkjoinwidget.cpp create mode 100644 umbrello/umbrello/forkjoinwidget.h create mode 100644 umbrello/umbrello/headings/Makefile.am create mode 100644 umbrello/umbrello/headings/heading.adb create mode 100644 umbrello/umbrello/headings/heading.ads create mode 100644 umbrello/umbrello/headings/heading.as create mode 100644 umbrello/umbrello/headings/heading.cpp create mode 100644 umbrello/umbrello/headings/heading.cs create mode 100644 umbrello/umbrello/headings/heading.d create mode 100644 umbrello/umbrello/headings/heading.h create mode 100644 umbrello/umbrello/headings/heading.idl create mode 100644 umbrello/umbrello/headings/heading.java create mode 100644 umbrello/umbrello/headings/heading.js create mode 100644 umbrello/umbrello/headings/heading.php create mode 100644 umbrello/umbrello/headings/heading.pm create mode 100644 umbrello/umbrello/headings/heading.py create mode 100644 umbrello/umbrello/headings/heading.rb create mode 100644 umbrello/umbrello/headings/heading.sql create mode 100644 umbrello/umbrello/headings/heading.xsd create mode 100644 umbrello/umbrello/hi128-app-umbrello.png create mode 100644 umbrello/umbrello/hi16-app-umbrello.png create mode 100644 umbrello/umbrello/hi16-mime-umbrellofile.png create mode 100644 umbrello/umbrello/hi22-app-umbrello.png create mode 100644 umbrello/umbrello/hi32-app-umbrello.png create mode 100644 umbrello/umbrello/hi32-mime-umbrellofile.png create mode 100644 umbrello/umbrello/hi48-app-umbrello.png create mode 100644 umbrello/umbrello/hi64-app-umbrello.png create mode 100644 umbrello/umbrello/hierarchicalcodeblock.cpp create mode 100644 umbrello/umbrello/hierarchicalcodeblock.h create mode 100644 umbrello/umbrello/hisc-app-umbrello.svgz create mode 100644 umbrello/umbrello/import_rose.cpp create mode 100644 umbrello/umbrello/import_rose.h create mode 100644 umbrello/umbrello/kplayerslideraction.cpp create mode 100644 umbrello/umbrello/kplayerslideraction.h create mode 100644 umbrello/umbrello/kstartuplogo.cpp create mode 100644 umbrello/umbrello/kstartuplogo.h create mode 100644 umbrello/umbrello/linepath.cpp create mode 100644 umbrello/umbrello/linepath.h create mode 100644 umbrello/umbrello/linkwidget.cpp create mode 100644 umbrello/umbrello/linkwidget.h create mode 100644 umbrello/umbrello/listpopupmenu.cpp create mode 100644 umbrello/umbrello/listpopupmenu.h create mode 100644 umbrello/umbrello/main.cpp create mode 100644 umbrello/umbrello/messagewidget.cpp create mode 100644 umbrello/umbrello/messagewidget.h create mode 100644 umbrello/umbrello/messagewidgetcontroller.cpp create mode 100644 umbrello/umbrello/messagewidgetcontroller.h create mode 100644 umbrello/umbrello/messagewidgetlist.h create mode 100644 umbrello/umbrello/model_utils.cpp create mode 100644 umbrello/umbrello/model_utils.h create mode 100644 umbrello/umbrello/node.cpp create mode 100644 umbrello/umbrello/node.h create mode 100644 umbrello/umbrello/nodewidget.cpp create mode 100644 umbrello/umbrello/nodewidget.h create mode 100644 umbrello/umbrello/notewidget.cpp create mode 100644 umbrello/umbrello/notewidget.h create mode 100644 umbrello/umbrello/notewidgetcontroller.cpp create mode 100644 umbrello/umbrello/notewidgetcontroller.h create mode 100644 umbrello/umbrello/object_factory.cpp create mode 100644 umbrello/umbrello/object_factory.h create mode 100644 umbrello/umbrello/objectwidget.cpp create mode 100644 umbrello/umbrello/objectwidget.h create mode 100644 umbrello/umbrello/objectwidgetcontroller.cpp create mode 100644 umbrello/umbrello/objectwidgetcontroller.h create mode 100644 umbrello/umbrello/operation.cpp create mode 100644 umbrello/umbrello/operation.h create mode 100644 umbrello/umbrello/optionstate.cpp create mode 100644 umbrello/umbrello/optionstate.h create mode 100644 umbrello/umbrello/ownedcodeblock.cpp create mode 100644 umbrello/umbrello/ownedcodeblock.h create mode 100644 umbrello/umbrello/ownedhierarchicalcodeblock.cpp create mode 100644 umbrello/umbrello/ownedhierarchicalcodeblock.h create mode 100644 umbrello/umbrello/package.cpp create mode 100644 umbrello/umbrello/package.h create mode 100644 umbrello/umbrello/packagewidget.cpp create mode 100644 umbrello/umbrello/packagewidget.h create mode 100644 umbrello/umbrello/petalnode.cpp create mode 100644 umbrello/umbrello/petalnode.h create mode 100644 umbrello/umbrello/petaltree2uml.cpp create mode 100644 umbrello/umbrello/petaltree2uml.h create mode 100644 umbrello/umbrello/pics/COPYING create mode 100644 umbrello/umbrello/pics/CVglobal_meth.png create mode 100644 umbrello/umbrello/pics/CVglobal_var.png create mode 100644 umbrello/umbrello/pics/CVimplementation_meth.png create mode 100644 umbrello/umbrello/pics/CVimplementation_signal.png create mode 100644 umbrello/umbrello/pics/CVimplementation_slot.png create mode 100644 umbrello/umbrello/pics/CVimplementation_var.png create mode 100644 umbrello/umbrello/pics/CVnamespace.png create mode 100644 umbrello/umbrello/pics/CVprivate_meth.png create mode 100644 umbrello/umbrello/pics/CVprivate_signal.png create mode 100644 umbrello/umbrello/pics/CVprivate_slot.png create mode 100644 umbrello/umbrello/pics/CVprivate_var.png create mode 100644 umbrello/umbrello/pics/CVprotected_meth.png create mode 100644 umbrello/umbrello/pics/CVprotected_signal.png create mode 100644 umbrello/umbrello/pics/CVprotected_slot.png create mode 100644 umbrello/umbrello/pics/CVprotected_var.png create mode 100644 umbrello/umbrello/pics/CVpublic_meth.png create mode 100644 umbrello/umbrello/pics/CVpublic_signal.png create mode 100644 umbrello/umbrello/pics/CVpublic_slot.png create mode 100644 umbrello/umbrello/pics/CVpublic_var.png create mode 100644 umbrello/umbrello/pics/CVstruct.png create mode 100644 umbrello/umbrello/pics/Makefile.am create mode 100644 umbrello/umbrello/pics/actor.png create mode 100644 umbrello/umbrello/pics/aggregation.png create mode 100644 umbrello/umbrello/pics/align_bottom.png create mode 100644 umbrello/umbrello/pics/align_hori_distribute.png create mode 100644 umbrello/umbrello/pics/align_hori_middle.png create mode 100644 umbrello/umbrello/pics/align_left.png create mode 100644 umbrello/umbrello/pics/align_right.png create mode 100644 umbrello/umbrello/pics/align_top.png create mode 100644 umbrello/umbrello/pics/align_vert_distribute.png create mode 100644 umbrello/umbrello/pics/align_vert_middle.png create mode 100644 umbrello/umbrello/pics/anchor.png create mode 100644 umbrello/umbrello/pics/andline.png create mode 100644 umbrello/umbrello/pics/arrow.png create mode 100644 umbrello/umbrello/pics/artifact.png create mode 100644 umbrello/umbrello/pics/association.png create mode 100644 umbrello/umbrello/pics/box.png create mode 100644 umbrello/umbrello/pics/branch.png create mode 100644 umbrello/umbrello/pics/choice-rhomb.png create mode 100644 umbrello/umbrello/pics/choice-round.png create mode 100644 umbrello/umbrello/pics/class.png create mode 100644 umbrello/umbrello/pics/component.png create mode 100644 umbrello/umbrello/pics/composition.png create mode 100644 umbrello/umbrello/pics/containment.png create mode 100644 umbrello/umbrello/pics/cr16-action-umbrello_diagram_activity.png create mode 100644 umbrello/umbrello/pics/cr16-action-umbrello_diagram_class.png create mode 100644 umbrello/umbrello/pics/cr16-action-umbrello_diagram_collaboration.png create mode 100644 umbrello/umbrello/pics/cr16-action-umbrello_diagram_component.png create mode 100644 umbrello/umbrello/pics/cr16-action-umbrello_diagram_deployment.png create mode 100644 umbrello/umbrello/pics/cr16-action-umbrello_diagram_sequence.png create mode 100644 umbrello/umbrello/pics/cr16-action-umbrello_diagram_state.png create mode 100644 umbrello/umbrello/pics/cr16-action-umbrello_diagram_usecase.png create mode 100644 umbrello/umbrello/pics/cr22-action-umbrello_diagram_activity.png create mode 100644 umbrello/umbrello/pics/cr22-action-umbrello_diagram_class.png create mode 100644 umbrello/umbrello/pics/cr22-action-umbrello_diagram_collaboration.png create mode 100644 umbrello/umbrello/pics/cr22-action-umbrello_diagram_component.png create mode 100644 umbrello/umbrello/pics/cr22-action-umbrello_diagram_deployment.png create mode 100644 umbrello/umbrello/pics/cr22-action-umbrello_diagram_entityrelationship.png create mode 100644 umbrello/umbrello/pics/cr22-action-umbrello_diagram_sequence.png create mode 100644 umbrello/umbrello/pics/cr22-action-umbrello_diagram_state.png create mode 100644 umbrello/umbrello/pics/cr22-action-umbrello_diagram_usecase.png create mode 100644 umbrello/umbrello/pics/cursor-actor.png create mode 100644 umbrello/umbrello/pics/cursor-aggregation.png create mode 100644 umbrello/umbrello/pics/cursor-anchor.png create mode 100644 umbrello/umbrello/pics/cursor-andline.png create mode 100644 umbrello/umbrello/pics/cursor-artifact.png create mode 100644 umbrello/umbrello/pics/cursor-association.png create mode 100644 umbrello/umbrello/pics/cursor-box.png create mode 100644 umbrello/umbrello/pics/cursor-branch.png create mode 100644 umbrello/umbrello/pics/cursor-choice-rhomb.png create mode 100644 umbrello/umbrello/pics/cursor-choice-round.png create mode 100644 umbrello/umbrello/pics/cursor-class.png create mode 100644 umbrello/umbrello/pics/cursor-component.png create mode 100644 umbrello/umbrello/pics/cursor-composition.png create mode 100644 umbrello/umbrello/pics/cursor-containment.png create mode 100644 umbrello/umbrello/pics/cursor-datatype.png create mode 100644 umbrello/umbrello/pics/cursor-deep-history.png create mode 100644 umbrello/umbrello/pics/cursor-dependency.png create mode 100644 umbrello/umbrello/pics/cursor-end_state.png create mode 100644 umbrello/umbrello/pics/cursor-entity.png create mode 100644 umbrello/umbrello/pics/cursor-enum.png create mode 100644 umbrello/umbrello/pics/cursor-fork.png create mode 100644 umbrello/umbrello/pics/cursor-generalisation.png create mode 100644 umbrello/umbrello/pics/cursor-initial_state.png create mode 100644 umbrello/umbrello/pics/cursor-interface.png create mode 100644 umbrello/umbrello/pics/cursor-join.png create mode 100644 umbrello/umbrello/pics/cursor-junction.png create mode 100644 umbrello/umbrello/pics/cursor-message-asynchronous.png create mode 100644 umbrello/umbrello/pics/cursor-message-synchronous.png create mode 100644 umbrello/umbrello/pics/cursor-node.png create mode 100644 umbrello/umbrello/pics/cursor-note.png create mode 100644 umbrello/umbrello/pics/cursor-object.png create mode 100644 umbrello/umbrello/pics/cursor-package.png create mode 100644 umbrello/umbrello/pics/cursor-relationship.png create mode 100644 umbrello/umbrello/pics/cursor-shallow-history.png create mode 100644 umbrello/umbrello/pics/cursor-state-fork.png create mode 100644 umbrello/umbrello/pics/cursor-text.png create mode 100644 umbrello/umbrello/pics/cursor-uniassociation.png create mode 100644 umbrello/umbrello/pics/cursor-usecase.png create mode 100644 umbrello/umbrello/pics/datatype.png create mode 100644 umbrello/umbrello/pics/deep-history.png create mode 100644 umbrello/umbrello/pics/dependency.png create mode 100644 umbrello/umbrello/pics/end_state.png create mode 100644 umbrello/umbrello/pics/entity.png create mode 100644 umbrello/umbrello/pics/enum.png create mode 100644 umbrello/umbrello/pics/fork.png create mode 100644 umbrello/umbrello/pics/generalisation.png create mode 100644 umbrello/umbrello/pics/initial_state.png create mode 100644 umbrello/umbrello/pics/interface.png create mode 100644 umbrello/umbrello/pics/join.png create mode 100644 umbrello/umbrello/pics/junction.png create mode 100644 umbrello/umbrello/pics/message-asynchronous.png create mode 100644 umbrello/umbrello/pics/message-synchronous.png create mode 100644 umbrello/umbrello/pics/node.png create mode 100644 umbrello/umbrello/pics/note.png create mode 100644 umbrello/umbrello/pics/object.png create mode 100644 umbrello/umbrello/pics/package.png create mode 100644 umbrello/umbrello/pics/relationship.png create mode 100644 umbrello/umbrello/pics/shallow-history.png create mode 100644 umbrello/umbrello/pics/sources/actor.svg create mode 100644 umbrello/umbrello/pics/sources/aggregation.svg create mode 100644 umbrello/umbrello/pics/sources/align_bottom.svg create mode 100644 umbrello/umbrello/pics/sources/align_hori_distribute.svg create mode 100644 umbrello/umbrello/pics/sources/align_hori_middle.svg create mode 100644 umbrello/umbrello/pics/sources/align_left.svg create mode 100644 umbrello/umbrello/pics/sources/align_right.svg create mode 100644 umbrello/umbrello/pics/sources/align_top.svg create mode 100644 umbrello/umbrello/pics/sources/align_vert_distribute.svg create mode 100644 umbrello/umbrello/pics/sources/align_vert_middle.svg create mode 100644 umbrello/umbrello/pics/sources/anchor.svg create mode 100644 umbrello/umbrello/pics/sources/andline.svg create mode 100644 umbrello/umbrello/pics/sources/artifact.svg create mode 100644 umbrello/umbrello/pics/sources/association.svg create mode 100644 umbrello/umbrello/pics/sources/asynchro.svg create mode 100644 umbrello/umbrello/pics/sources/box.svg create mode 100644 umbrello/umbrello/pics/sources/branch.svg create mode 100644 umbrello/umbrello/pics/sources/choice-rhomb.svg create mode 100644 umbrello/umbrello/pics/sources/choice-round.svg create mode 100644 umbrello/umbrello/pics/sources/class.svg create mode 100644 umbrello/umbrello/pics/sources/component.svg create mode 100644 umbrello/umbrello/pics/sources/composition.svg create mode 100644 umbrello/umbrello/pics/sources/containment.svg create mode 100644 umbrello/umbrello/pics/sources/cursor-andline.svg create mode 100644 umbrello/umbrello/pics/sources/cursor-choice-rhomb.svg create mode 100644 umbrello/umbrello/pics/sources/cursor-choice-round.svg create mode 100644 umbrello/umbrello/pics/sources/cursor-deep-history.svg create mode 100644 umbrello/umbrello/pics/sources/cursor-join.svg create mode 100644 umbrello/umbrello/pics/sources/cursor-junction.svg create mode 100644 umbrello/umbrello/pics/sources/cursor-shallow-history.svg create mode 100644 umbrello/umbrello/pics/sources/cursor-state-fork.svg create mode 100644 umbrello/umbrello/pics/sources/datatype.svg create mode 100644 umbrello/umbrello/pics/sources/deep-history.svg create mode 100644 umbrello/umbrello/pics/sources/dependency.svg create mode 100644 umbrello/umbrello/pics/sources/diag_activity.svg create mode 100644 umbrello/umbrello/pics/sources/diag_class.svg create mode 100644 umbrello/umbrello/pics/sources/diag_collaboration.svg create mode 100644 umbrello/umbrello/pics/sources/diag_component.svg create mode 100644 umbrello/umbrello/pics/sources/diag_deployment.svg create mode 100644 umbrello/umbrello/pics/sources/diag_entityrelationship.svg create mode 100644 umbrello/umbrello/pics/sources/diag_sequence.svg create mode 100644 umbrello/umbrello/pics/sources/diag_state.svg create mode 100644 umbrello/umbrello/pics/sources/diag_usecase.svg create mode 100644 umbrello/umbrello/pics/sources/diagbase.svg create mode 100644 umbrello/umbrello/pics/sources/end_state.svg create mode 100644 umbrello/umbrello/pics/sources/entity.svg create mode 100644 umbrello/umbrello/pics/sources/enum.svg create mode 100644 umbrello/umbrello/pics/sources/fork.svg create mode 100644 umbrello/umbrello/pics/sources/generalise.svg create mode 100644 umbrello/umbrello/pics/sources/initial.svg create mode 100644 umbrello/umbrello/pics/sources/interface.svg create mode 100644 umbrello/umbrello/pics/sources/join.svg create mode 100644 umbrello/umbrello/pics/sources/junction.svg create mode 100644 umbrello/umbrello/pics/sources/message-asynchronous.svg create mode 100644 umbrello/umbrello/pics/sources/message-synchronous.svg create mode 100644 umbrello/umbrello/pics/sources/node.svg create mode 100644 umbrello/umbrello/pics/sources/note.svg create mode 100644 umbrello/umbrello/pics/sources/object.svg create mode 100644 umbrello/umbrello/pics/sources/package.svg create mode 100644 umbrello/umbrello/pics/sources/relationship.svg create mode 100644 umbrello/umbrello/pics/sources/shallow-history.svg create mode 100644 umbrello/umbrello/pics/sources/state-fork.svg create mode 100644 umbrello/umbrello/pics/sources/subsystem.svg create mode 100644 umbrello/umbrello/pics/sources/template.svg create mode 100644 umbrello/umbrello/pics/sources/text.svg create mode 100644 umbrello/umbrello/pics/sources/uniassociation.svg create mode 100644 umbrello/umbrello/pics/sources/usecase.svg create mode 100644 umbrello/umbrello/pics/startlogo.png create mode 100644 umbrello/umbrello/pics/state-fork.png create mode 100644 umbrello/umbrello/pics/subsystem.png create mode 100644 umbrello/umbrello/pics/template.png create mode 100644 umbrello/umbrello/pics/text.png create mode 100644 umbrello/umbrello/pics/uniassociation.png create mode 100644 umbrello/umbrello/pics/usecase.png create mode 100644 umbrello/umbrello/plugin.cpp create mode 100644 umbrello/umbrello/plugin.h create mode 100644 umbrello/umbrello/pluginloader.cpp create mode 100644 umbrello/umbrello/pluginloader.h create mode 100644 umbrello/umbrello/refactoring/Makefile.am create mode 100644 umbrello/umbrello/refactoring/refactoringassistant.cpp create mode 100644 umbrello/umbrello/refactoring/refactoringassistant.h create mode 100644 umbrello/umbrello/seqlinewidget.cpp create mode 100644 umbrello/umbrello/seqlinewidget.h create mode 100644 umbrello/umbrello/statewidget.cpp create mode 100644 umbrello/umbrello/statewidget.h create mode 100644 umbrello/umbrello/stereotype.cpp create mode 100644 umbrello/umbrello/stereotype.h create mode 100644 umbrello/umbrello/template.cpp create mode 100644 umbrello/umbrello/template.h create mode 100644 umbrello/umbrello/textblock.cpp create mode 100644 umbrello/umbrello/textblock.h create mode 100644 umbrello/umbrello/textblocklist.h create mode 100644 umbrello/umbrello/tips create mode 100644 umbrello/umbrello/toolbarstate.cpp create mode 100644 umbrello/umbrello/toolbarstate.h create mode 100644 umbrello/umbrello/toolbarstatearrow.cpp create mode 100644 umbrello/umbrello/toolbarstatearrow.h create mode 100644 umbrello/umbrello/toolbarstateassociation.cpp create mode 100644 umbrello/umbrello/toolbarstateassociation.h create mode 100644 umbrello/umbrello/toolbarstatefactory.cpp create mode 100644 umbrello/umbrello/toolbarstatefactory.h create mode 100644 umbrello/umbrello/toolbarstatemessages.cpp create mode 100644 umbrello/umbrello/toolbarstatemessages.h create mode 100644 umbrello/umbrello/toolbarstateother.cpp create mode 100644 umbrello/umbrello/toolbarstateother.h create mode 100644 umbrello/umbrello/toolbarstatepool.cpp create mode 100644 umbrello/umbrello/toolbarstatepool.h create mode 100644 umbrello/umbrello/umbrello.desktop create mode 100644 umbrello/umbrello/umbrelloui.rc create mode 100644 umbrello/umbrello/uml.cpp create mode 100644 umbrello/umbrello/uml.h create mode 100644 umbrello/umbrello/umlassociationlist.h create mode 100644 umbrello/umbrello/umlattributelist.cpp create mode 100644 umbrello/umbrello/umlattributelist.h create mode 100644 umbrello/umbrello/umlcanvasobject.cpp create mode 100644 umbrello/umbrello/umlcanvasobject.h create mode 100644 umbrello/umbrello/umlclassifierlist.h create mode 100644 umbrello/umbrello/umlclassifierlistitemlist.cpp create mode 100644 umbrello/umbrello/umlclassifierlistitemlist.h create mode 100644 umbrello/umbrello/umldoc.cpp create mode 100644 umbrello/umbrello/umldoc.h create mode 100644 umbrello/umbrello/umlentityattributelist.cpp create mode 100644 umbrello/umbrello/umlentityattributelist.h create mode 100644 umbrello/umbrello/umlenumliterallist.h create mode 100644 umbrello/umbrello/umllistview.cpp create mode 100644 umbrello/umbrello/umllistview.h create mode 100644 umbrello/umbrello/umllistviewitem.cpp create mode 100644 umbrello/umbrello/umllistviewitem.h create mode 100644 umbrello/umbrello/umllistviewitemlist.h create mode 100644 umbrello/umbrello/umlnamespace.cpp create mode 100644 umbrello/umbrello/umlnamespace.h create mode 100644 umbrello/umbrello/umlobject.cpp create mode 100644 umbrello/umbrello/umlobject.h create mode 100644 umbrello/umbrello/umlobjectlist.cpp create mode 100644 umbrello/umbrello/umlobjectlist.h create mode 100644 umbrello/umbrello/umloperationlist.h create mode 100644 umbrello/umbrello/umlpackagelist.h create mode 100644 umbrello/umbrello/umlrole.cpp create mode 100644 umbrello/umbrello/umlrole.h create mode 100644 umbrello/umbrello/umlstereotypelist.h create mode 100644 umbrello/umbrello/umltemplatelist.h create mode 100644 umbrello/umbrello/umlview.cpp create mode 100644 umbrello/umbrello/umlview.h create mode 100644 umbrello/umbrello/umlviewcanvas.cpp create mode 100644 umbrello/umbrello/umlviewcanvas.h create mode 100644 umbrello/umbrello/umlviewimageexporter.cpp create mode 100644 umbrello/umbrello/umlviewimageexporter.h create mode 100644 umbrello/umbrello/umlviewimageexporterall.cpp create mode 100644 umbrello/umbrello/umlviewimageexporterall.h create mode 100644 umbrello/umbrello/umlviewimageexportermodel.cpp create mode 100644 umbrello/umbrello/umlviewimageexportermodel.h create mode 100644 umbrello/umbrello/umlviewlist.h create mode 100644 umbrello/umbrello/umlwidget.cpp create mode 100644 umbrello/umbrello/umlwidget.h create mode 100644 umbrello/umbrello/umlwidgetcontroller.cpp create mode 100644 umbrello/umbrello/umlwidgetcontroller.h create mode 100644 umbrello/umbrello/umlwidgetlist.h create mode 100644 umbrello/umbrello/uniqueid.cpp create mode 100644 umbrello/umbrello/uniqueid.h create mode 100644 umbrello/umbrello/usecase.cpp create mode 100644 umbrello/umbrello/usecase.h create mode 100644 umbrello/umbrello/usecasewidget.cpp create mode 100644 umbrello/umbrello/usecasewidget.h create mode 100644 umbrello/umbrello/widget_factory.cpp create mode 100644 umbrello/umbrello/widget_factory.h create mode 100644 umbrello/umbrello/widget_utils.cpp create mode 100644 umbrello/umbrello/widget_utils.h create mode 100644 umbrello/umbrello/widgetbase.cpp create mode 100644 umbrello/umbrello/widgetbase.h create mode 100644 umbrello/umbrello/worktoolbar.cpp create mode 100644 umbrello/umbrello/worktoolbar.h create mode 100644 umbrello/umbrello/x-umbrello.desktop create mode 100644 umbrello/uml.kdevprj create mode 100644 umbrello/uml.lsm diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..5ff207a8 --- /dev/null +++ b/COPYING @@ -0,0 +1,346 @@ +NOTE! The GPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the kde programs) are copyrighted +by the authors who actually wrote it. + +--------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/COPYING-DOCS b/COPYING-DOCS new file mode 100644 index 00000000..4a0fe1c8 --- /dev/null +++ b/COPYING-DOCS @@ -0,0 +1,397 @@ + GNU Free Documentation License + Version 1.2, November 2002 + + + Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + +0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document "free" in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The "Document", below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as "you". You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (Thus, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section "Entitled XYZ" means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as "Acknowledgements", +"Dedications", "Endorsements", or "History".) To "Preserve the Title" +of such a section when you modify the Document means that it remains a +section "Entitled XYZ" according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + + +2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + + +4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +A. Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +B. List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has fewer than five), + unless they release you from this requirement. +C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. +D. Preserve all the copyright notices of the Document. +E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +F. Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +G. Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +H. Include an unaltered copy of this License. +I. Preserve the section Entitled "History", Preserve its Title, and add + to it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section Entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section all + the substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. +N. Do not retitle any existing section to be Entitled "Endorsements" + or to conflict in title with any Invariant Section. +O. Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled "History" +in the various original documents, forming one section Entitled +"History"; likewise combine any sections Entitled "Acknowledgements", +and any sections Entitled "Dedications". You must delete all sections +Entitled "Endorsements". + + +6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + + +7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an "aggregate" if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled "Acknowledgements", +"Dedications", or "History", the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + + +10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + + +ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. diff --git a/Makefile.am.in b/Makefile.am.in new file mode 100644 index 00000000..ac039ee6 --- /dev/null +++ b/Makefile.am.in @@ -0,0 +1,19 @@ +## kdesdk/Makefile.am +## (C) 1997 Stephan Kulow + +AUTOMAKE_OPTIONS = foreign 1.6.1 +DISTCLEANFILES = inst-apps + +MAINTAINERCLEANFILES = subdirs configure.in acinclude.m4 SUBDIRS + +cvs-local: + @for i in *; do \ + if test -f $$i/Makefile.cvs; then \ + echo "creating stuff in $$i"; \ + (cd $$i && $(MAKE) -f Makefile.cvs); \ + fi \ + done + @perl -pi -e 'if (/\[\/\$$\]\*. INSTALL=/) { print $$_ ; $$_ = "\"\") ;;\n"; }' ` ls -1 configure */configure 2>/dev/null` + +include admin/deps.am + diff --git a/Makefile.cvs b/Makefile.cvs new file mode 100644 index 00000000..030b1407 --- /dev/null +++ b/Makefile.cvs @@ -0,0 +1,15 @@ + +all: + @echo "This Makefile is only for the CVS repository" + @echo "This will be deleted before making the distribution" + @echo "" + @if test ! -d admin; then \ + echo "Please recheckout this module!" ;\ + echo "for cvs: use checkout once and after that update again" ;\ + echo "for cvsup: checkout kde-common from cvsup and" ;\ + echo " link kde-common/admin to ./admin" ;\ + exit 1 ;\ + fi + $(MAKE) -f admin/Makefile.common cvs + +.SILENT: diff --git a/README b/README new file mode 100644 index 00000000..fe188f05 --- /dev/null +++ b/README @@ -0,0 +1,26 @@ +In this file: + +* What it is + +What it is +---------- + +This is a collection of applications and tools used by KDE developers. +It also has example code for use in learning KDE programming or starting +a new KDE application. + +* cervisia: CVS client part +* kapptemplate: shell script to easy the beginning of new apps +* kexample: a sample KDE application, heavily documented +* kbabel: an editor for *.po files +* kbugbuster: a graphical frontend for the KDE bug reporting system +* kdepalettes: palettes matching KDE's style for Gimp and XPaint +* kmtrace: converts glibc's mtrace log into a full backtrace +* kspy: displays all used QObjects in an application +* kstartperf: startup time measurement +* poxml: +* scripts: various helper scripts (see scripts/README) +* scheck: An interface style to highlight accel and style guide conflicts +* kuiviewer: A KPart that lets you view .ui files. +* umbrello: A UML modeller. + diff --git a/cervisia/COPYING b/cervisia/COPYING new file mode 100644 index 00000000..96bdc086 --- /dev/null +++ b/cervisia/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/cervisia/ChangeLog b/cervisia/ChangeLog new file mode 100644 index 00000000..6461e67f --- /dev/null +++ b/cervisia/ChangeLog @@ -0,0 +1,1543 @@ +2008-08-15 André Wöbbeking + + * Fix BR #162523: + Allow cvsnt users to login to repositories. + +2008-06-18 André Wöbbeking + + * Fix BR #164216: + Fixed crash when updating the status after adding a directory to a repository. + +2007-09-11 André Wöbbeking + + * Fix BR #148162: + Workaround a regression in Qt 3.3.8 QDateTime::fromString(). + Patch by Martin Koller. + +2007-01-13 André Wöbbeking + + * Fix: + Handle '.' in user name when adding a repository. + +2006-09-02 André Wöbbeking + + * Fix BR #133129: + Dates from cvs history are parsed correctly. + + While at it also made parsing more robust and added event 'P' + "Update, Patched". + +2006-08-12 André Wöbbeking + + * Fix BR #131695: + Broken repository locations don't crash cvsservice anymore. + +2005-08-03 Christian Loose + + * Implemented wish #92938: + Added ability to exclude files in the commit dialog from + the subsequent commit. + +2005-03-30 Christian Loose + + * Change License to GPL. + +2005-03-17 Christian Loose + + * Implemented wish #40760: + New item in context menu to fold/unfold the + selected folder and its subfolders. + +2005-03-07 Christian Loose + + * Added support for commit template message (CVS/Template) in + the commit dialog. Patch by Darrell Esau. + +2005-03-04 Christian Loose + + * Fix BR #97664: + Fix statusbar when embedded in Konqueror. + +2005-01-31 Christian Loose + + * Implemented wish #95257: + Added new setting for a color. It is used to highlight files in + the update view with status "Not in CVS". + +2005-01-09 André Wöbbeking + + * Added an icon for "Diff". Thanks to Jonathan Riddell. + +2004-12-08 Christian Loose + + * Fix BR #90346: + Normalize user-entered CVSROOT specification before adding + a new group to the cvsservicerc configuration file. This + prevents duplicate entries in the repository list. + +2004-11-30 Christian Loose + + * Fix BR #94083: + Don't crash while removing old 'Edit with...' menu item + from the context menu. This can happen after the user + switched tabs in Konqueror. + +2004-11-11 André Wöbbeking + + * Fix BR #92576: + Use correct encoding for status messages. + +2004-10-26 Christian Loose + + * Implemented wish #78696: + Added possibility to get a notification, when a cvs commit job + has finished. + +2004-10-14 André Wöbbeking + + * Added a search line (ala JuK and KMail) to the CVS log list view. + +2004-09-10 Christian Loose + + * Fix bug #89215: + Always make sure that directory entries '.' and '..' + are part of the ignore list to prevent an endless loop + in UpdateDirItem::maybeScanDir(). + +2004-09-09 Christian Loose + + * Added new command-line option -annotate. + +2004-08-30 Christian Loose + + * Added new item 'Properties' to context menu. It + shows the properties dialog for the selected file. + +2004-08-28 Christian Loose + + * Implemented wish #74751: + Added support for non-recursive checkouts to the + checkout dialog. Patch by Sergio Visinoni. + +2004-08-23 Christian Loose + + * Implemented bug #74862: + In the checkout dialog, it's now possible to fetch + the existing branch/tag names for a module from the + cvs server. + * Fix bug #87830: + Always read the cvs client option from the configuration + file even when there is no sandbox open. + +2004-07-13 André Wöbbeking + + * Implemented FR #67805, #67806, #67807, #67809: + Hooray, icons for Cervisia's (main) actions. + Thanks to Marco Martin for the great artwork! + +2004-07-11 Christian Loose + + * Fix bug #83239: + Fixed retrieving author from cvs log output. + +2004-07-09 Christian Loose + + * Implemented wish #75825: + The context menu for the file view gained an + 'Edit With' menu. It's now possible to start a + different application for the selected file. + +2004-07-07 Christian Loose + + * Implemented wish #75017: + Show the patch option dialog when the user called the + 'Create patch against repository' action and pass + the selected options to the 'cvs diff' command. + +2004-07-05 Christian Loose + + * Added a new patch option dialog. This lets you choose + the options passed to the diff command when creating + a patch with the 'Create Patch' button in the log + dialog. + +2004-06-21 Christian Loose + + * Implemented wish #66231: + Added new 'Create Patch' button to the log dialog. This + feature makes it possible to create a patch between + arbitrary CVS revisions. + +2004-06-02 Christian Loose + + * Implemented wish #77894: + Added support for a checkout of a module without + the CVS folder. (cvs export) + Patch by Dermot Daly + + * Implemented wish #80177: + It's now possible to checkout a project under an + alias name. (cvs checkout -d) + Patch by Dermot Daly + +2004-05-29 Christian Loose + + * Implemented wish #63592: + Honor the CVSROOT/cvsignore file by downloading it from the + cvs server and adding it to the global ignore list. + +2004-05-29 André Wöbbeking + + * Fix BR #81665: don't show duplicated files in the file view (did + only occur when the option "Update Recursively" wasn't active). + +2004-05-21 Christian Loose + + * Added a new method to download the CVSROOT/cvsignore file + from the cvs server to the DCOP service. + +2004-05-17 André Wöbbeking + + * Fix BR #58254: honor option "Hide Non-CVS Files" when opening + a branch in the file tree. + +2004-05-17 Christian Loose + + * Implemented wish #41467: + Added possibility to hide files with status Unknown by + extending the current option "Hide Up-To-Date Files". This option + is now called "Hide Unmodified Files". + +2004-05-16 André Wöbbeking + + * Fix BR #81498: handle spaces in the working folder name correctly. + +2004-05-07 Christian Loose + + * Added auto completion to the working folder line edit + in the checkout and the import dialog. + +2004-05-05 Christian Loose + + * Added support for compression levels (-z) above three. + +2004-04-29 Christian Loose + + * Activated spellchecking in changelog dialog. + * Implement BR #79957: + Spellchecking in commit dialog. + (patch by theboywho@ruddyperl.com) + +2004-04-23 Christian Loose + + * Big cleanup - removed KDE 3.1 support + +2004-04-14 Christian Loose + + * Fix session management + +2004-04-13 Christian Loose + + * Implemented BR #74754: + Added support for 'cvs init' to create a new repository. + +2004-04-01 Christian Loose + + * Fix BR #46871: + Preserve file content in resolve dialog: + - don't remove characters + - don't add or remove new line markers + - handle A+B/B+A cases with no new line marker at + the end of the first version correctly + * Fix BR #74903: + Don't choke on conflict markers that are not on a + separate line in the resolve dialog. This happens + when the file didn't end with a new line marker + before CVS encountered the conflict. + * Fix BR #78800: + Lock harder whether a directory really is under CVS control. + Fix by Frerich Raabe. + +2004-03-17 Christian Loose + + * Don't execute shell scripts or .desktop files when the user used + the edit file function. (BR #77440) + +2004-03-02 André Wöbbeking + + * Fix BR #55871: + - truncate the tooltip text if necessary + - use a subclassed QToolTip instead of the own TipLabel. + +2004-03-01 Christian Loose + + * Implemented BR #72861: + Added support for option "use the file's modification time as the time + of import" (-d) to the cvs import function. + +2004-02-24 André Wöbbeking + + * Fix BR #75201: + Prevent crash when you activate a CervisiaPart view with RMB in + a Koqueror with more than one view. + +2004-02-20 Christian Loose + + * Fix remembering the last input values in checkout dialog. + +2004-02-01 Christian Loose + + * LogTree now derives from QTable instead of the deprecated QtTableView + +2004-01-22 Christian Loose + + * Fix BR #70936: + Prevent crash while embedded into Quanta because of + a name conflict between the TagDialog classes. + +2004-01-16 André Wöbbeking + + * Fix BR 72519 (file view): + Don't select hidden files when you select a range of files + with Shift key. This prevents you from evil accidents (i.e. + "Remove from Repository"). + +2003-10-04 Christian Loose + + * Do a cvs logout when a user removes a pserver repository item. + This way the repository isn't re-added because of the .cvspass file. + +2003-10-03 Christian Loose + + * Added support to add or remove watches to the cvs DCOP service. + +2003-09-14 Christian Loose + + * Added new function makePatch() to the cvs DCOP service + to create a patch against the repository. + +2003-09-05 Christian Loose + + * Implemented BR #56716: + Added login/logout functionality for pserver cvs + servers. + +2003-08-30 Christian Loose + + * Added editors() and import() methods to cvs DCOP service + +2003-08-29 Christian Loose + + * Removed the editor option from the settings dialog. We + use KRun now to start the preferred editor for the given + mime-type. + * Fix BR #53815: Prevent the user from changing the directory + with konqueror's tree view while there is a job running + in the protocol view. + +2003-08-28 Christian Loose + + * Added a new button (View) to the log dialog to view + the selected revision of a file in the preferred editor. + * Added method downloadRevision() to the cvs DCOP service + to download a specific revision of a file. + +2003-08-27 Christian Loose + + * Revamped settings dialog: + - Used KJanusWidget::IconList instead of KJanusWidget::Tabbed + - Merged 'Appearance' and 'Colors' page + - Moved creation of option pages in separate methods + +2003-08-09 André Wöbbeking + + * Make file view configurable (column order/widths, sorting). + +2003-08-02 André Wöbbeking + + * Fix/Implement FR 56042: + Use the configured colors as foreground colors in the file AND + protocol view and a bold font to improve readability of the text + for modified, added and removed files. + +2003-07-30 Christian Loose + + * Added lock() and unlock() methods to the DCOP service. + * Replace the lock and unlock implementation in CervisiaPart + by calls to the new methods in the DCOP service. + +2003-07-28 Christian Loose + + * Add search functionality to the plain log view. Now you + can search for a word in the commit messages. + +2003-07-16 Christian Loose + + * Added new view variant for cvs' log output to the log dialog. + This view shows the data in a format that is very similar to + the format of the command-line output of cvs log. + +2003-07-09 Christian Loose + + * New watchers dialog: Cervisia now shows the watchers of + the selected files in a nice dialog instead of just showing cvs' + output in the protocol view. + +2003-07-07 Christian Loose + + * Added createTag() and deleteTag() methods to the DCOP + service and use them in the part. + +2003-07-04 Christian Loose + + * Implement BR #60604: It's now possible to select + revision B in Log dialog with Ctrl key + left mouse button in + addition to the middle mouse button. + +2003-06-26 Christian Loose + + * Start/Stop the ssh-agent process and setup the cvs + job environment to use it. + * Initial version of our own little ssh-askpass program. + * Initial version of the SshAgent class which will later enable + the cvs DCOP service to utilize the ssh-agent program. + +2003-06-19 Christian Loose + + * Added showWatchers() method to the DCOP service. + +2003-06-19 André Wöbbeking + + * Fix BR 59275 (DiffZoomWidget): + o Use QStyle::querySubControlMetrics() to get the exact + geometry of the scroll bar groove. + o Better performance for big files. + +2003-06-14 Christian Loose + + * Implement BR #59644: Change key shortcuts for cvs add and + cvs remove to Insert and Delete. Now you can use the plus and + minus keys for the tree view. + +2003-06-08 Christian Loose + + * Added possibility to DCOP service to retrieve the accumulated + output after cvs command finished. + +2003-06-04 Christian Loose + + * Added new command line option to show a log dialog for + a given file without starting the whole program. + Usage: cervisia -log main.cpp + +2003-06-03 Christian Loose + + * Fix BR #59267: Re-added "clear" command to the + RMB context menu of the ProtocolView + +2003-05-29 Christian Loose + + * Font of ChangeLog dialog now configurable. + +2003-05-25 Christian Loose + + * Added a method to retrieve a list of modules in the repository + to the cvs DCOP service (cvs checkout -c). + * Use the new method in the CheckoutDialog. + +2003-05-22 Christian Loose + + * Added edit() and unedit() method to cvs DCOP service + and used them for the corresponding actions in + Cervisia. + +2003-05-14 Christian Loose + + * Use DCOP service for history action + * Added history() method to cvs DCOP service + +2003-05-10 André Wöbbeking + + * Fix BR 50918: + o Added possibility to diff a selected file against the + newest version in the repository (cvs diff -r HEAD). + o Now External and internal diff show the same differences. + +2003-05-09 Christian Loose + + * Use DCOP service for commit action + (CervisiaPart::slotCommit()) + * Convert CervisiaPart::updateActions() from manually + changing the state of the menu items to using + KXMLGUIClient::stateChanged() with the corresponding + setup in the part's rc-file. + +2003-05-05 André Wöbbeking + + * Diff between any revision and the sandbox (only select + revision A in the log dialog). + +2003-04-18 Christian Loose + + * Fix BR 56042: Better default colors for white + background + * Fix BR 56942: Escape output lines for protocol view + so html tags in commit messages aren't interpreted + +2003-02-28 Christian Loose + + * Fix BR #54382: Display warning message in remove dialog + to make clear that the action will also remove the local + copy of the selected files. + +2003-02-24 Christian Loose + + * Make shortcut keys of actions which are part of CervisiaPart + configurable (#55125). + +2003-02-21 Christian Loose + + * Implemented BR #41263: Added splitters to resolve + dialog. + +2003-02-18 Christian Loose + + * Big cleanup of parseCvsDiff() in diff dialog + +2003-02-17 Christian Loose + + * Use DCOP service for diff dialog. + * Added private method callExternalDiff() in diff + dialog to make parseCvsDiff() more readable. + +2003-02-16 Christian Loose + + * Improved size handling for Repository dialog, + Add repository dialog and Checkout dialog. + +2003-02-12 Christian Loose + + * Bug 54106: Display error message when user tries to + access a remote repository + * Improved size handling for Commit dialog, History dialog, + Resolve dialog and Resolve edit dialog + * Removed code duplication in Commit dialog when + displaying the Diff dialog + +2003-02-11 Christian Loose + + * Improved size handling for Diff dialog + +2003-02-10 Christian Loose + + * Remove CervisiaShell's dependency on CervisiaPart + * Moved filter status indicator to CervisiaPart + * Improve dialog size handling for ChangeLog dialog + (use new KDialogBase methods to save the size into + CervisiaPart's configuration file) + +2003-02-06 Christian Loose + + * Try to get the user name and email address for the + changelog from the control center settings (KEMailSettings) + before asking the system. + +2003-02-02 Christian Loose + + * Added login() and logout() methods to DCOP service. + +2003-01-26 André Wöbbeking + + * Implemented new option "Hide Empty Directories" + +2003-01-23 Christian Loose + + * Implemented wish #41468: Remember last open directory + in KDirSelect dialog + +2003-01-21 Christian Loose + + * Added remove() method to cvs DCOP service + * Use DCOP service for cvs add and cvs remove + +2003-01-18 Christian Loose + + * Remove custom dialog size handling from annotation + dialog. The size is stored globally to simulate + Cervisia's old behaviour. + +2003-01-17 Christian Loose + + * Remove restorePseudo() hack + * We always want to save "Current Directory". So move out + of the session management methods. + * Settings for the part are now handle by the part. This + fixes partly the bug #38235. + +2003-01-16 Christian Loose + + * Changed main window size handling. The window size is now + handled by KMainWindow. + * Added method openURL() to CervisiaShell. Use this method to + open the sandbox which was provided on the command line. + TODO: remove restorePseudo() + +2003-01-11 Christian Loose + + * Added help button to CommitDialog + * Use DCOP service to retrieve Tags and Branches for TagDialog, + MergeDialog und UpdateDialog + * Added new AddRemoveDialog (extracted from CommitDialog) + * Make functionality to view diff in CommitDialog more visible + by adding diff button. + +2003-01-11 André Wöbbeking + + * unfoldTree(): reduced flicker and improved perfomance by disabling updates. + +2003-01-11 André Wöbbeking + + * Moved colors from UpdateViewItem to UpdateView: + o it's faster as you don't need KConfig in UpdateViewItem ctor + o it needs less memory (3 colors per item) + +2003-01-09 Christian Loose + + * Use DCOP stubs to access the methods of the cvs DCOP service + * Added new method update() and checkout() to DCOP service + * Use KProcess::operator<< instead of QString::operator+= to + build the command line + * Make CVS_SERVER configurable in DCOP service + (GUI is missing) + * Break up updateOrStatus() method in cervisiapart.cpp + * Use DCOP service to update the working copy + +2003-01-04 Christian Loose + + * Use DCOP service to update status for UpdateView + +2003-01-02 Christian Loose + + * Added new startJob() method to ProtocolView + that uses the new cvs DCOP service + +2003-01-02 André Wöbbeking + + * Replaced deprecated Qt classes/methods with actual equivalents. + +2002-12-31 Christian Loose + + * Separate GUI from functionality for AnnotateDialog + * Use DCOP service for log dialog + +2002-12-30 André Wöbbeking + + * Use user's settings (locale and timezone) to display dates. + +2002-12-29 André Wöbbeking + + * Fixed sorting in list views. + +2002-12-29 Christian Loose + + * Extract AddRepositoryDialog from repositorydlg.cpp + * Save and read repository configuration to/from the + configuration file of the cvs DCOP service + * Added kconf_update script to copy repository configuration + to cvsservicerc + +2002-12-28 Christian Loose + + * Start and stop cvs DCOP service in CervisiaPart + * Change working copy directory in DCOP service + * Use DCOP service for annotate dialog + * Several changes to the DCOP service + * Added new parseCvsLog() method to LogDialog that + uses the new cvs DCOP service + * Save and read cvs client and global compression level configuration + to/from the configuration file of the cvs DCOP service + * Added kconf_update script + +2002-12-26 André Wöbbeking + + * Fixed parsing of branch and tag names: + o no more trailing spaces + o names with more than 24 chars are identified now + +2002-12-23 André Wöbbeking + + * All dialogs: + - Inherit from KDialogBase instead of QDialog + -> less code and more KDE standard compliant. + - removed layout leftovers from old Qt versions. + - reduced header dependences. + +2002-12-19 Christian Loose + + * Replace deprecated QMultiLineEdit with KTextEdit in + ChangeLog dialog + * Added new parseCvsAnnotate() method to AnnotateDialog + that uses the new cvs DCOP service + +2002-12-18 Christian Loose + + * Added a new progress dialog that will replace + CvsProgressDialog in the near future + +2002-12-17 Christian Loose + + * Fixed "fetch of branch list hangs in update dialog" (#50824) + +2002-12-14 Christian Loose + + * Added first version of cvs DCOP service + +2002-12-14 André Wöbbeking + + * Removed ListViewItem. Use Q/KListViewItem instead. + +2002-12-14 André Wöbbeking + + * ProtocolView::appendLine(): removed trailing
+ as QTextEdit::append() already adds a new paragraph. + Detect "U " as remote changed file. + +2002-12-12 Christian Loose + + * Fix the too small scroll area of diff view when + tabs in source code + +2002-12-08 Christian Loose + + * Make ChangeLog dialog more KDE standard conformant + QDialog -> KDialogBase + +2002-12-05 Christian Loose + + * Don't add a new line to the end of changelog + * Use QFile instead of FILE* and remove unneeded + inheritance in cvsdir.cpp + +2002-12-02 Christian Loose + + * Fixed commit dialog bigger than screen bug (#50735) + +2002-11-29 Christian Loose + + * Fixed automatic cvs edit option which called cvs edit + for more files than necessary + * Added support for new .cvspass format introduced with + cvs 1.11.1 + * Fixed checkout dialog bigger than screen bug + * Fixed sort order of the revision numbers in log view + +2002-07-25 Christian Loose + + * Use KGenericFactory + * Revived old filter status indicator in statusbar + * Preserve empty lines in ChangeLog while adding a + new entry + +2002-06-28 Bernd Gehrmann + + * Removed Qt1-specific layout management stuff + * More latin1 conversions removed + * Command line option --resolve filename + (Stanislav Visnovsky ) + +2002-06-27 Bernd Gehrmann + + * Mark files with option -kb with a binary icon + * Fixed restoration of the last loaded sandbox + * Use different instance names for part and shell, + otherwise KConfig get messed up + +2002-06-26 Bernd Gehrmann + + * Made editor configurable again + * Readded manpage + * Put help buttons in all dialogs, linked to + the online docs + +2002-06-25 Bernd Gehrmann + + * Fixed char buffer to QString conversion in + CvsProgressDialog, resulting in random garbage + inserted in the annotate view. Also a bug + introduced in 2002-06-11 + * Changed annotate view to QListView. Based + on a patch by André Wöbbeking + * Hide custom tooltips in list views when contents + are scrolled + * Simplified history dialog filtering by + using QListViewItem::setVisible() + * #include cleanup + * i18n fixes + * Rewritten repository settings dialog; its + functionality now comprises that of the former + dialog and the former add repository dialog + * Load .ui, .docbook and .xml files with utf8 + encoding. This can be implemented in a cleaner way + * Resolved accelerator conflict in resolve dialog + * Escape all text inserted in richtext tooltips + +2002-06-22 Bernd Gehrmann + + * Fixed diff dialog bug due to changes from 2002-06-11 + +2002-06-18 Roland Krause + + * View Filter is now applied after Fold/Unfold of the + file tree. + +2002-06-11 Bernd Gehrmann + + * Interpret all output from cvs in the user's locale + +2002-04-28 Bernd Gehrmann + + * Little layout fix in log dialog by Christian Loose + * Patch by Andrew Speer : + in tags list (produced by cvs status -v) accept tabs + as delimiter + +2002-04-22 Bernd Gehrmann + + * Patch by Christian Loose : + - Allow to specify a comment when importing a module + - Enable checkout/import when no item is selected + * Always enable folding and unfolding the tree + +2002-04-17 Bernd Gehrmann + + * Patch by Gregory Green : + - Checkout of branches + * Patch by Roland Krause : + - Added "Last change" to context menu + - Added filter for files which are not in cvs + * Ignore stderr in Make Patch (it would produce + invalid patches previously) + +2002-04-03 Bernd Gehrmann + + * Release 1.5rich + +2002-02-04 Bernd Gehrmann + + * Colored ProtocolView output + Based on a patch by Asaf Gery + +2001-09-04 Bernd Gehrmann + + * Added 'Force tag creation' option to tag dialog + Patch by Alessandro Praduroux + + ======> TODO Before Importing + ======================================== + * Remove stale CVS directories and add to main repository + kdesdk/cervisia/CVS + kdesdk/po/cervisia/CVS + kdesdk/doc/cervisia/CVS + + cd kdesdk + cvs add cervisia + cd cervisia + cvs add -kb *.png + cvs add Makefile.am README TODO .cvsignore ChangeLog LICENSE.QPL \ + cervisia.lsm *.cpp *.h *.rc + ======================================== + + +2001-09-01 Richard Moore + + * Added support for KDE/Qt 3. At the moment I've just made the + minimum set of changes required to make it compile. Qt 2.x is + still supported of course. + * Moved README, TODO, ChangeLog, cervisia.lsm and LICENSE.QPL to + the main source directory. + * Moved version string from configure.in.in to version.h + * Updated version string + * Added a kdoc build target to Makefile.am so we can generate some + api documentation + * Imported to kdesdk CVS module + +2001-07-05 Bernd Gehrmann + + * Added missing icons to doc/common directory + +2001-07-04 Richard Moore + + * Converted to KParts + * Created a standalone shell app that embeds the part + * Converted Makefile to use METASOURCES = AUTO + +2001-06-23 Bernd Gehrmann + + * Avoid making NotInCVS files up-to-date + * Don't show attic directories + * DEBUGOUT -> kdDebug() + +2001-06-20 Bernd Gehrmann + + * Patch by Adrian Schroeter : + - Some QString -> char* conversion fixes + - fixes for autoconf 2.50 + * Use Prune Empty Directories settings not only for updates, + but also for checkouts + +2001-06-19 Richard Moore + + * Ported UI implementation to use XMLGUI + * Made UI more style guide compliant + * Added toolbar configuration + * Ported settings dialog to use KDialogBase + +2001-06-19 Bernd Gehrmann + + * Again a patch by Colin Macleod + Compression argument is now configurable per + repository, with a global default. Also, -f + is given to all commands now. + +2001-05-21 Bernd Gehrmann + + * Made compression argument (-z) configurable. + Patch by Colin Macleod + +2001-05-17 Bernd Gehrmann + + * Fixed crash when closing settings dialog + * Fixed first line in annotate dialog disappearing + * Remade html documentation from docbook, bringing + it in line with the kde style + * Release 1.4.1 + +2001-05-16 Bernd Gehrmann + + * Cleanup + * Release 1.4 + +2001-05-14 Bernd Gehrmann + + * Patch by Francisco Jose Blasco Abril + Adds to the resolve dialog buttons A and B the additional + choices A+B and B+A. It is also possible to freely edit + items. + * Use -f option also for cvs log + +2001-04-03 Bernd Gehrmann + + * Support for external diff frontends: Based + on a patch by Scott Moore + +2001-03-27 Bernd Gehrmann + + * Fixed resolve dialog bug: The merged view was + not scrolled to the relevant position + +2001-03-18 Bernd Gehrmann + + * Copied and cleaned up the << and >> button stuff from resolve + dialog to the diff dialog. Now it is possible to jump between + differences by these buttons. Based on ideas and + code by Francois Biot . + * Zoom widget in diff dialog improved. Ditto. + * Added a combo in the diff dialog which allows to jump + directly to a difference region + * Release 1.3 + +2001-03-14 Bernd Gehrmann + + * Ignore symbolic links + +2001-03-12 Bernd Gehrmann + + * Hopefully fixed a crash in the listview which + I could not reproduce myself. + +2001-03-03 Bernd Gehrmann + + * Patch by Wynn Wilkes: Allow to open the editor + with multiple files + * Made tab width in diff view customizable + * Fix in doc/Makefile.am for Solaris. + Thanks to Timo Ruottinen + * Some cleanups wrt constness + +2001-03-02 Bernd Gehrmann + * Patch by Cosmin Smeu : + In diff view, replace tabs by spaces + +2001-02-17 Bernd Gehrmann + + * Fixed opening files by double clicking + * Added small icon to makefile + * Show directory items in file tree with folder icon + * Implemented Fold tree + * Implemented Lock files and Unlock files + * Implemented Edit files, Unedit Files, Show editors + * Implemented Hide removed files + * Added label in the status bar which shows + which items are hidden + * Release 1.2 + +2001-02-09 Bernd Gehrmann + + * Changed behaviour of .cvsignore mechanisms + for directories: now directories are ignored, + _except_ if they appear in CVS/Entries. I'm + now quite sure that it matches the behaviour + of CVS itself :-) + * Release 1.1 + +2001-02-08 Bernd Gehrmann + + * In main view, allow tabbing by keyboard between + file tree and protocol view + * Context menus in main view also by keyboard + +2001-02-07 Bernd Gehrmann + + * Apply ignore rules to files instead of directories + - Yet Another Bug introduced between Betas 2 and 3 + * Ignore CVS directories + * Pressing Return now edits the current file, not one + of the selected files, and never a directory... + * Quote file name in diff dialog + * In Merge dialog, implemented buttons to fetch the + lists of possible tags and branches + +2001-02-06 Bernd Gehrmann + + * Finished filtering in main view - the cast orgy ;-) + +2001-02-04 Bernd Gehrmann + + * Save column sizes of log list + * In main view and history dialog, let the main column + take the remaining space of the list view + * Do not read in contents of directory repeatedly + if it is empty + * Tooltips for menu items + * Options -> Settings, in consistency with other KDE apps + * Rewritten some captions + +2001-02-03 Bernd Gehrmann + + * Implemented Revert - only for cvs 1.11 + +2001-02-01 Bernd Gehrmann + + * In Update to tag dialog, introduced new option + Update to branch; implemented buttons to fetch the + lists of possible tags and branches + * In protocol view, added popup with items + Clear and Select All + +2001-01-31 Bernd Gehrmann + + * Fixed widgets not appearing in the import dialog + * Made several dialogs non-modal again which I + made modal by accident between Beta 2 and Beta 3 + * Added button for fetching the list of possible tags + in Delete Tag dialog + +2001-01-30 Bernd Gehrmann + + * Updated screenshots in documentation + * Close CVS/Root file after reading + * Don't count diagnostic message from cvs server + as errors when making a patch + * Use configured cvs client when making patch + +2001-01-29 Bernd Gehrmann + + * Layout fix in Add Watch dialog + * Use bigger font in ChangeLog dialog, use + KGlobalSettings::fixedFont() for KDE2 + +2001-01-28 Bernd Gehrmann + + * Put sort column indicators in all list views + * New Bonsai-like blame annotation view + * Reduced free space in log tree, cleaned up the calculation + * Tool tips in log tree contain revision, author and date now + and use Qt's richtext engine; improved positioning + * Session-save configuration (order and sort direction) of log list + * Removed KDE1 related legacy classes + * KDE2 conformant command line handling + * Updated manpage to KDE2 + * Updated libtool/autoconf stuff, should support --prefix now, + removed support for --enable-final and --enable-fast-perl + +2001-01-27 Bernd Gehrmann + + * Removed superfluous line breaks in log tree tool tips + * Fixed bug that prevented cvs info from working when kdehelp + was not installed + * Layout fixes in settings dialog + * Fixed i18n issue in log list + * 1.0beta3 branches from here + +2001-01-23 Bernd Gehrmann + + * Now directories are now never ignored via the various .cvsignore mechanisms + +2001-01-21 Bernd Gehrmann + + * Try to find a nice user name for the change log dialog + automatically + * Don't use entry from change log dialog in commit dialog + if the change log dialog was cancelled + * Made close button in history dialog non-autodefault + +2001-01-20 Bernd Gehrmann + + * Made all dialogs maximizable/minimizable where it makes sense + * Layout fix + * Fixed doc/Makefile.am for non-Linux systems (hopefully :-) + * In the Old Messages combo in the commit dialog, remove + duplicate entries + +2001-01-17 Bernd Gehrmann + + * Patch by Florent Pillet : + - In the log dialog, use multi line edits instead of labels + - In the linear log list, additional column with branch + * Korean translation by Yu-Chan, Park + * Patch by Wynn Wilkes : + Making path to the cvs client configurable + * Some cleanup in function argument list order; don't + let dialogs include toplevel.h, instead turned + variable into a function in misc.h... I'm a purist about + this matter ;-) + +2001-01-04 Bernd Gehrmann + + * Always use /bin/sh as shell + * When the progress dialog is canceled, kill the + underlying process instead of destroying the + KProcess object. This avoids a (superfluous) warning + from KProcCtrl + * Removed Ok button from the dialog. + Patch by Guillaume Laurent + * Use KAnimWidget for KDE2 + +2000-12-21 Bernd Gehrmann + + * Release 1.0beta2 + +2000-12-13 Bernd Gehrmann + + * Bug fixes for broken diff dialog in KDE 1 version: + + Do not create modeless dialogs with parent + + Create combo box in commit dialog with minimal size + * Bug fix in log tree: tool tips showed revisions + appearing on several branches with the same root + * Let cvs ignore ~/.cvsrc file when diffing, as that + may lead to conflicting options + +2000-11-24 Bernd Gehrmann + + * Layout improvements in watch dialog + * Implemented 'Create patch' + * More documentation + * Release 1.0beta1 + +2000-11-21 Bernd Gehrmann + + * In Import dialog, added options for ignoring files + and for binary imports + +2000-11-19 Bernd Gehrmann + + * Fixes for srcdir != builddir + +2000-11-11 Bernd Gehrmann + + * Made some fonts and the orientation of the main + window splitter configurable + * Fixed log tree for nested branches + +2000-11-10 Bernd Gehrmann + + * History list can be sorted chronologically + +2000-11-09 Bernd Gehrmann + + * In Last Change dialog, show newer version on the right + * Session management for commit dialog + * Diff options now customizable + +2000-11-08 Bernd Gehrmann + + * Again a patch by Jan Borsodi : + - In Commit dialog, diffs against repository + can be created by selecting files from the + list box + - List of 30 latest log messages is stored + and available via a combo box + - Wheel mouse support in diff view + - Shortcut F5 for Status + Thanks :-) + +2000-10-27 Bernd Gehrmann + + * Disabled dcop support which doesn't make sense + as long as there's no general convention about + an interface for loading files + +2000-10-17 Bernd Gehrmann + + * Ignore empty lines in output of cvs checkout -c + Patch by Jan Borsodi + * Added /usr/local/include and /usr/local/lib + to autoconf-checked directories + +2000-10-01 Bernd Gehrmann + + * Protect file names with whitespace in them + Based on a patch by Jeff Cody + * Allow it to execute a File->Status + automatically when a sandbox is opened. Separate + options for local and remote repositories + +2000-09-19 Bernd Gehrmann + + * Fixed wrong #ifdef statement which disabled + Deselect All and made the selection mechanism + almost unusable with KDE1 + * In diff view, synchronize both horizontal + and vertical scrollbars + * Release 0.7.2 + +2000-09-16 Bernd Gehrmann + + * Added horizontal scrollbars in diff view + * Made number of context lines configurable + * In directory listing, separate directories from files + * Display sticky date tags in a more friendly way + * Adjust columns widths in main view dynamically + * Set focus explicitly in TagDialog, UpdateDialog + and HistoryDialog whenever radio buttons change + * Set initial focus in some dialogs + * Added more accelerators in CheckoutDialog + and WatchDialog + * Fixed recursive removes + * Do no try to read non-existent directories + * More precise coordinate computation on change bar + avoids stripes in certain circumstances + * Fixed highlighted text color + * Show newest revisions first in log message list + * Release 0.7.1 + +2000-09-15 Bernd Gehrmann + + * Avoid -no-rtti because some crap code in + kdelibs crashes without rtti + * Fixed selection by keyboard in log message list + * Avoid exploding status bar width with + long command lines + * Fixed "Deselect all" accelerator + +2000-09-10 Bernd Gehrmann + + * Fixed dist target + * Release 0.7.0 + +2000-09-09 Bernd Gehrmann + + * Fixed spinning gear for KDE2 + * Fixed misinterpretation of mouse events in + log list for KDE2 + +2000-09-08 Bernd Gehrmann + + * Backporting to KDE1. It becomes annoying :-( + * Polished log tree and log list + * Reduced flicker a lot + +2000-09-05 Bernd Gehrmann + + * Implemented change bar in diff frontend + +2000-09-03 Bernd Gehrmann + + * New diff frontend, based on unified diffs instead + of --side-by-side. + * Keybindings for arrow keys in diff and resolve + dialogs. + * Fixed caption/about data for KDE2 + +2000-08-29 Bernd Gehrmann + + * Fixed Remove behaviour, which may be recursive or not + +2000-07-13 Bernd Gehrmann + + * Fixed problems with latest kdelibs2 and gcc 2.96 + (thanks to Stan Bubrouski) + * Error messages don't confuse the 'Fetch list' + item in the check out dialog any more + * Using QFileDialog instead of completely + broken KFileDialog::getExistingDirectory() + +2000-05-28 Bernd Gehrmann + + * Quote file name when calling an editor + +2000-05-17 Bernd Gehrmann + + * Improvements in the logdialog/log tree by + Florent Pillet + * Implemented 'Delete tag' + +2000-05-07 Bernd Gehrmann + + * Implemented Add Watch, Remove watch, + Show watchers + +2000-04-25 Bernd Gehrmann + + * Added option to automatically call 'cvs edit' whenever + a read-only file is edited. Based on a patch by + Steffen Dettmer . Thanks! + +2000-04-15 Bernd Gehrmann + + * Added documentation for repository access + * Release 0.6.0 + +2000-04-14 Bernd Gehrmann + + * Fixed "Open recent" for Qt 2.x + * Fixed caption for KDE 2.x + +2000-04-11 Bernd Gehrmann + + * Made repositories dialog usable. + +2000-04-09 Bernd Gehrmann + + * New quoting mechanism which is reliable also when + log messages/file names contain $ or ' + Thanks for the hint, Walter! + +2000-03-09 Bernd Gehrmann + + * Put ok and cancel buttons in the progress dialog + to avoid confusion + +2000-03-06 Bernd Gehrmann + + * The combo boxes in the log dialog now indicate + that chosing a branch tag selects the branch point + * Repository dialog, unfinished. + +2000-03-05 Bernd Gehrmann + + * Replaced all Done's with Close's + * Moved Add binary from Advanced to File menu + * Enabled tag selectors in log dialog now also for KDE 2 + * Implemented "Open recent" + * Implemented "History" + +2000-03-04 Bernd Gehrmann + + * Added a button in the checkout dialog to obtain + a list of modules + * Improved error message detection + * Show sandbox in caption + +2000-03-02 Bernd Gehrmann + + * Much more detailed pseudo session management. + Please delete your cervisiarc file! + +2000-02-21 Bernd Gehrmann + + * Fixed Checkout and Import which were not working at all + * Check tag names in checkout and import dialog + * Fixed accelerators and added some more + * Implemented "Merge" + * Implemented "Prune empty directories" + * Implemented "Select by tag" in log dialog + * Release 0.5.0 + +2000-02-20 Bernd Gehrmann + + * Fixed bug which caused tooltips for revisions + an branches to disappear in the log tree + * Tags, branches and branchpoints as tooltips in log tree + * Improved log dialog layout + * Implemented "Add binary" + +2000-02-17 Bernd Gehrmann + + * Handle hidden files correctly + * Do not ignore case in files sorting + * Implemented "Last change" + * Release 0.4.0 + +2000-02-15 Bernd Gehrmann + + * Implemented "Update to tag" and "Update to HEAD" + * Implemented "Tag" + +2000-02-14 Bernd Gehrmann + + * Implemented options "Commit recursively" and + "Create directories" + * Ported Checkout/Import dialog to the new scheme + +2000-02-13 Bernd Gehrmann + + * Rewritten the whole UpdateView (well, almost :-). Now it allows + to show and update the revision and tag field. + * Real support for asynchronous operation of Update, Commit, Add + and remove: output goes to new ProtocolView + * Fixed "Unfold Tree" + * Fixed crash caused by too many open files + (.cvsignore files were never closed) + + +2000-02-08 Bernd Gehrmann + + * Ported documentation to docbook, added some + more stuff and adjusted automake system accordingly + +2000-02-06 Bernd Gehrmann + + * Updated autoconf framework to work correctly + with both KDE 1 and KDE 2 + +1999-12-19 Bernd Gehrmann + + * In Checkout dialog, use $CVSROOT as default repository + (if defined) + * Added command line arguments --help and --version + +1999-11-23 Bernd Gehrmann + + * Fixed bug which caused files mistakenly marked as up to date + * Fixed options menu + * Show wait cursor while child process runs in background + * Made Ok button in settings dialog the default + * Release 0.3.1 + +1999-11-21 Bernd Gehrmann + + * ChangeLog editor + * Release 0.3.0 + +1999-11-20 Bernd Gehrmann + + * Solution of the startup problem: pseudo-session management + * Simpler handling of command line argument + * Tooltips in LogTree show log message + +1999-10-31 Bernd Gehrmann + + * DCOP support for KDE HEAD branch + * Settings dialog + * Context menu + * Doubleclick opens file + * 'Open Sandbox' + +1999-09-22 Bernd Gehrmann + + * KDE2 is really a moving target => more porting: + replaced KQuickHelp by QWhatsThis, + #defined Icon BarIcon + * Made editor configurable + +1999-08-07 Bernd Gehrmann + + * Spinning gear for SubProcDialog and CvsProgressDialog + * Reorganized the whole 'mark updated' code. As + a side effect, there is no more confusion about + bogus 'Up to date' files + * Save/load options + * Implemented 'Unfold tree' + * man page + * Release 0.2.1 + +1999-08-04 Bernd Gehrmann + + * Fixed disappearing revision in log tree view + when branch is longer than trunk + * Changed .kdelnk from Applications -> Development + * Release 0.2 + +1999-08-03 Bernd Gehrmann + + * Implemented recursive update + +1999-08-02 Bernd Gehrmann + + * Changed update, diff and annotate to use + the new CvsProgressDialog + * Preparations for multi log dialog + * Fixed memory leaks in modeless dialogs + which didn't delete themselves on closeEvent() + +1999-08-01 Bernd Gehrmann + + * Implemented CvsProgressDialog which will show error + messages for things like 'update' and allow the user + to interrupt the operation if cvs hangs. + It's _very_ smart :-) + +1999-07-31 Bernd Gehrmann + + * Commit/Add/Remove now show SubProcDialog + to give the user feedback + * Preparations for better error handling: + parseXXX routines return bool now + * Commented out some of the DEBUGOUT. + It was just _too_ much ;-) + +1999-07-20 Bernd Gehrmann + + * Ported to KDE 2 + +1999-07-19 Bernd Gehrmann + + * Implemented "Import" + * Release 0.1 + +1999-07-17 Bernd Gehrmann + + * Another trial in the "Startup in non-CVS directories" + game + * Layout corrections in Checkout dialog + * Mark selections in LogTreeView, + synchronized with LogListView + +1999-07-15 Bernd Gehrmann + + * Gave up on learning docbook. Some documentation + in linuxdoc is now available. + +1999-06-01 Bernd Gehrmann + + * Option -askdir + * Update status field for 'up to date' files + * Release 0.0.2 + +1999-05-25 Bernd Gehrmann + + * Set colors in DiffView explicitly + +1999-05-17 Bernd Gehrmann + + * Fixed 'cvs commit' command + * Preparations for i18n + +1999-05-11 Bernd Gehrmann + + * Release 0.0.1 + + + + diff --git a/cervisia/HACKING b/cervisia/HACKING new file mode 100644 index 00000000..dfa61cdd --- /dev/null +++ b/cervisia/HACKING @@ -0,0 +1,79 @@ +Coding Style +============ + +Formatting +---------- + +- No tabs. +- Indent is 4 spaces. +- A line should not exceed 80 chars. +- Brackets are always on separate lines. +- Put spaces between brackets of if, while and + similar statements. + + +Example: + +void MyClass::myFunction(const QString& arg) +{ + if( blah == "halb" ) + { + doSometing(); + } + else + { + varA = varB; + } +} + + + +Header Formatting +----------------- + +- Access modifiers are not indented. +- Double inclusion guard defines are all upper case + letters and are composed of the namespace (if available), + the classname and a H suffix separated by underscores. +- Inside a namespace there is no indentation. + + +Example: + +#ifndef NAMESPACE_MYCLASS_H +#define NAMESPACE_MYCLASS_H + +namespace Namespace +{ + +class MyClass +{ +public: + MyClass(); + +private: + int m_intVar; + KProcess* m_proc; +}; + +} + +#endif + + + +Class and File Names +-------------------- + + + +Class and Variable Names +------------------------ + +- For class, variable and function names separate multiple + words by uppercasing the words preceded by other words. +- Class names start with an uppercase letter. +- Function names start with a lowercase letter. +- Variable names start with a lowercase letter. +- Member Variables of a class start with a 'm_' prefix + followed by an lowercase letter. \ No newline at end of file diff --git a/cervisia/Makefile.am b/cervisia/Makefile.am new file mode 100644 index 00000000..eb985ea3 --- /dev/null +++ b/cervisia/Makefile.am @@ -0,0 +1,73 @@ +CERVISIA_VERSION = 2.4.10 +INCLUDES = -I./cvsservice -D_BSD_SOURCE $(all_includes) + +SUBDIRS = cvsservice . pics + +bin_PROGRAMS = +lib_LTLIBRARIES = +kdeinit_LTLIBRARIES = cervisia.la +kde_module_LTLIBRARIES = libcervisiapart.la +noinst_LTLIBRARIES = libcervisia.la + +libcervisia_la_SOURCES = annotatedlg.cpp diffdlg.cpp patchoptiondlg.cpp logdlg.cpp \ + progressdlg.cpp progressdlg.skel resolvedlg.cpp resolvedlg_p.cpp annotateview.cpp \ + diffview.cpp loglist.cpp logplainview.cpp logtree.cpp annotatectl.cpp \ + loginfo.cpp misc.cpp qttableview.cpp tooltip.cpp cervisiasettings.kcfgc \ + settingsdlg.cpp settingsdlg_advanced.ui +libcervisia_la_COMPILE_FIRST = cvsservice/cvsservice_stub.h cervisiasettings.h + +libcervisiapart_la_SOURCES = updateview.cpp protocolview.cpp protocolview.skel \ + watchdlg.cpp changelogdlg.cpp historydlg.cpp \ + repositorydlg.cpp commitdlg.cpp checkoutdlg.cpp updatedlg.cpp \ + tagdlg.cpp mergedlg.cpp cvsdir.cpp repositories.cpp cervisiapart.cpp \ + addrepositorydlg.cpp addremovedlg.cpp watchersdlg.cpp \ + updateview_items.cpp updateview_visitors.cpp entry.cpp \ + entry_status.cpp stringmatcher.cpp cvsinitdlg.cpp ignorelistbase.cpp dirignorelist.cpp \ + globalignorelist.cpp editwithmenu.cpp logmessageedit.cpp +libcervisiapart_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +libcervisiapart_la_LIBADD = $(LIB_KFILE) $(LIB_KPARTS) $(LIB_KUTILS) \ + cvsservice/libcvsservice.la libcervisia.la +libcervisiapart_la_COMPILE_FIRST = cvsservice/cvsservice_stub.h cervisiasettings.h + +cervisia_la_SOURCES = main.cpp cervisiashell.cpp +cervisia_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -module $(KDE_PLUGIN) +cervisia_la_LIBADD = $(LIB_KPARTS) $(LIB_KUTILS) cvsservice/libcvsservice.la libcervisia.la +cervisia_la_COMPILE_FIRST = cvsservice/cvsservice_stub.h cervisiasettings.h + +man_MANS = cervisia.1 + +METASOURCES = AUTO +KDE_ICON = AUTO + +EXTRA_DIST = cervisia.desktop cervisia.png cervisia-small.png +CLEANFILES = cervisia.1 + +xdg_apps_DATA = cervisia.desktop + +kde_kcfg_DATA = cervisiapart.kcfg + +partrcdir = $(kde_datadir)/cervisiapart +partrc_DATA = cervisiaui.rc + +shellrcdir = $(kde_datadir)/cervisia +shellrc_DATA = cervisiashellui.rc eventsrc + +update_DATA = cervisia.upd +update_SCRIPTS = move_repositories.pl change_colors.pl cervisia-normalize_cvsroot.pl cervisia-change_repos_list.pl +updatedir = $(kde_datadir)/kconf_update + +messages: rc.cpp + $(EXTRACTRC) *.rc >> rc.cpp + $(XGETTEXT) -C *.cpp *.h -o $(podir)/cervisia.pot + +srcdoc: + $(kde_bindir)/kdoc -a -p -d classdocs -n 'Cervisia' *.h -lqt -lkdecore -lkdeui -lkparts + +cervisia.1: $(srcdir)/cervisia.1.in + sed -e 's%_KDEHTMLDIR_%'${kde_htmldir}'%g;' \ + -e 's%_KDECONFDIR_%'${kde_confdir}'%g;' \ + < $(srcdir)/cervisia.1.in > cervisia.1 + +cervisia.1.in: cervisia.pod + pod2man --center "Cervisia" --release "${CERVISIA_VERSION}" \ + cervisia.pod > cervisia.1.in diff --git a/cervisia/README b/cervisia/README new file mode 100644 index 00000000..e310034e --- /dev/null +++ b/cervisia/README @@ -0,0 +1,19 @@ +Cervisia requires cvs 1.10 on the cvs server. With +cvs 1.9, diffs don't work! As this old version has a +y2k bug, switching is advisable anyway. + +From version 1.1 on, Cervisia implements the menu item +File->Revert. This hands over the command 'cvs update -C' +to the cvs client. This works only for cvs >= 1.11. + +Bug reports are welcome. My time is limited, so don't +expect wonders. +If you want to contribute bugfixes or improvements, +check out the source code from anonymous kde cvs. +In order to bootstrap the package, you have to +'make -f Makefile.cvs' there. Then you can make patches +either by using 'cvs diff -u' or by using the 'Create +patch' menu item in Cervisia. + + +Christian Loose diff --git a/cervisia/TODO b/cervisia/TODO new file mode 100644 index 00000000..f31cb683 --- /dev/null +++ b/cervisia/TODO @@ -0,0 +1,26 @@ +Ideas in no special order + +* Per-repository settings like enabling of + watch/edit/lock features + +* Log dialog option: Show only tagged revisions + +* Log dialog option: Show only your revisions + +* Log dialog: Allow to select latest on branch + +* Multi Log view + +* Implement "Add to .cvsignore" + +* cvs init + +* Implement a nice workaround for $CVSROOT/CVSROOT/cvsignore + +* Maybe use ui files for dialogs + +* Watch open working copy directory for changes (KDirWatcher) + +* Automatic 'cvs -n update' in specified time intervals (QTimer) + +* Add commit message to ChangeLog file (checkbox in commit dialog) diff --git a/cervisia/addremovedlg.cpp b/cervisia/addremovedlg.cpp new file mode 100644 index 00000000..108ac80e --- /dev/null +++ b/cervisia/addremovedlg.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 1999-2002 Bernd Gehrmann + * bernd@mail.berlios.de + * Copyright (c) 2003 Christian Loose + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "addremovedlg.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +AddRemoveDialog::AddRemoveDialog(ActionType action, QWidget* parent, const char* name) + : KDialogBase(parent, name, true, QString::null, + Ok | Cancel | Help, Ok, true) +{ + setCaption( (action==Add)? i18n("CVS Add") : + (action==AddBinary)? i18n("CVS Add Binary") : + i18n("CVS Remove") ); + + QFrame* mainWidget = makeMainWidget(); + + QBoxLayout *layout = new QVBoxLayout(mainWidget, 0, spacingHint()); + + QLabel *textlabel = new QLabel + ( (action==Add)? i18n("Add the following files to the repository:") : + (action==AddBinary)? i18n("Add the following binary files to the repository:") : + i18n("Remove the following files from the repository:") , + mainWidget ); + layout->addWidget(textlabel); + + m_listBox = new QListBox(mainWidget); + m_listBox->setSelectionMode(QListBox::NoSelection); + layout->addWidget(m_listBox, 5); + + // Add warning message to dialog when user wants to remove a file + if (action==Remove) + { + QBoxLayout *warningLayout = new QHBoxLayout; + + QLabel *warningIcon = new QLabel(mainWidget); + KIconLoader *loader = kapp->iconLoader(); + warningIcon->setPixmap(loader->loadIcon("messagebox_warning", KIcon::NoGroup, + KIcon::SizeMedium, KIcon::DefaultState, + 0, true)); + warningLayout->addWidget(warningIcon); + + QLabel *warningText = new QLabel(i18n("This will also remove the files from " + "your local working copy."), mainWidget); + warningLayout->addWidget(warningText); + + layout->addSpacing(5); + layout->addLayout(warningLayout); + layout->addSpacing(5); + } + + if( action == Remove ) + setHelp("removingfiles"); + else + setHelp("addingfiles"); +} + + +void AddRemoveDialog::setFileList(const QStringList& files) +{ + // the dot for the root directory is hard to see, so + // we convert it to the absolut path + if( files.find(".") != files.end() ) + { + QStringList copy(files); + int idx = copy.findIndex("."); + copy[idx] = QFileInfo(".").absFilePath(); + + m_listBox->insertStringList(copy); + } + else + m_listBox->insertStringList(files); +} + + +// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/cervisia/addremovedlg.h b/cervisia/addremovedlg.h new file mode 100644 index 00000000..06b98260 --- /dev/null +++ b/cervisia/addremovedlg.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 1999-2002 Bernd Gehrmann + * bernd@mail.berlios.de + * Copyright (c) 2003 Christian Loose + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef ADDREMOVEDLG_H +#define ADDREMOVEDLG_H + + +#include + + +class QListBox; +class QStringList; + + +class AddRemoveDialog : public KDialogBase +{ +public: + enum ActionType { Add, AddBinary, Remove }; + + explicit AddRemoveDialog(ActionType action, QWidget* parent=0, const char* name=0); + + void setFileList(const QStringList& files); + +private: + QListBox* m_listBox; +}; + +#endif + + +// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/cervisia/addrepositorydlg.cpp b/cervisia/addrepositorydlg.cpp new file mode 100644 index 00000000..d1d24cb0 --- /dev/null +++ b/cervisia/addrepositorydlg.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (C) 1999-2002 Bernd Gehrmann + * bernd@mail.berlios.de + * Copyright (c) 2002-2004 Christian Loose + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include "addrepositorydlg.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + + +AddRepositoryDialog::AddRepositoryDialog(KConfig& cfg, const QString& repo, + QWidget* parent, const char* name) + : KDialogBase(parent, name, true, i18n("Add Repository"), + Ok | Cancel, Ok, true) + , partConfig(cfg) +{ + QFrame* mainWidget = makeMainWidget(); + + QBoxLayout* layout = new QVBoxLayout(mainWidget, 0, spacingHint()); + + QLabel* repo_label = new QLabel(i18n("&Repository:"), mainWidget); + layout->addWidget(repo_label); + + repo_edit = new KLineEdit(mainWidget); + repo_edit->setFocus(); + repo_label->setBuddy(repo_edit); + if( !repo.isNull() ) + { + repo_edit->setText(repo); + repo_edit->setEnabled(false); + } + layout->addWidget(repo_edit); + + QLabel* rsh_label = new QLabel(i18n("Use remote &shell (only for :ext: repositories):"), mainWidget); + layout->addWidget(rsh_label); + + rsh_edit = new KLineEdit(mainWidget); + rsh_label->setBuddy(rsh_edit); + layout->addWidget(rsh_edit); + + QLabel* server_label = new QLabel(i18n("Invoke this program on the server side:"), + mainWidget); + layout->addWidget(server_label); + + server_edit = new KLineEdit(mainWidget); + server_label->setBuddy(server_edit); + layout->addWidget(server_edit); + + QHBox* compressionBox = new QHBox(mainWidget); + m_useDifferentCompression = new QCheckBox(i18n("Use different &compression level:"), compressionBox); + + m_compressionLevel = new KIntNumInput(compressionBox); + m_compressionLevel->setRange(0, 9, 1, false); + layout->addWidget(compressionBox); + + m_retrieveCvsignoreFile = new QCheckBox(i18n("Download cvsignore file from " + "server"), mainWidget); + layout->addWidget(m_retrieveCvsignoreFile); + + connect( repo_edit, SIGNAL(textChanged(const QString&)), + this, SLOT(repoChanged()) ); + connect( m_useDifferentCompression, SIGNAL(toggled(bool)), + this, SLOT(compressionToggled(bool)) ); + repoChanged(); + + QSize size = configDialogSize(partConfig, "AddRepositoryDialog"); + resize(size); +} + + +AddRepositoryDialog::~AddRepositoryDialog() +{ + saveDialogSize(partConfig, "AddRepositoryDialog"); +} + + +void AddRepositoryDialog::setRsh(const QString& rsh) +{ + rsh_edit->setText(rsh); +} + + +void AddRepositoryDialog::setServer(const QString& server) +{ + server_edit->setText(server); +} + + +void AddRepositoryDialog::setCompression(int compression) +{ + if( compression < 0 ) + { + // TODO: use KConfigXT to retrieve default compression level + m_compressionLevel->setValue(0); + m_useDifferentCompression->setChecked(false); + } + else + { + m_useDifferentCompression->setChecked(true); + m_compressionLevel->setValue(compression); + } + + compressionToggled(m_useDifferentCompression->isChecked()); +} + + +void AddRepositoryDialog::setRetrieveCvsignoreFile(bool enabled) +{ + m_retrieveCvsignoreFile->setChecked(enabled); +} + + +QString AddRepositoryDialog::repository() const +{ + return repo_edit->text(); +} + + +QString AddRepositoryDialog::rsh() const +{ + return rsh_edit->text(); +} + + +QString AddRepositoryDialog::server() const +{ + return server_edit->text(); +} + + +int AddRepositoryDialog::compression() const +{ + if( m_useDifferentCompression->isChecked() ) + return m_compressionLevel->value(); + else + return -1; +} + + +bool AddRepositoryDialog::retrieveCvsignoreFile() const +{ + return m_retrieveCvsignoreFile->isChecked(); +} + + +void AddRepositoryDialog::setRepository(const QString& repo) +{ + setCaption(i18n("Repository Settings")); + + repo_edit->setText(repo); + repo_edit->setEnabled(false); +} + + +void AddRepositoryDialog::repoChanged() +{ + QString repo = repository(); + rsh_edit->setEnabled((!repo.startsWith(":pserver:")) + && repo.contains(":")); + m_useDifferentCompression->setEnabled(repo.contains(":")); + if( !repo.contains(":") ) + m_compressionLevel->setEnabled(false); + else + compressionToggled(m_useDifferentCompression->isChecked()); +} + + +void AddRepositoryDialog::compressionToggled(bool checked) +{ + m_compressionLevel->setEnabled(checked); +} + +#include "addrepositorydlg.moc" + + +// Local Variables: +// c-basic-offset: 4 +// End: diff --git a/cervisia/addrepositorydlg.h b/cervisia/addrepositorydlg.h new file mode 100644 index 00000000..8fbf66fd --- /dev/null +++ b/cervisia/addrepositorydlg.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 1999-2002 Bernd Gehrmann + * bernd@mail.berlios.de + * Copyright (c) 2002-2004 Christian Loose + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef ADDREPOSITORYDLG_H +#define ADDREPOSITORYDLG_H + +#include + +class QCheckBox; +class KConfig; +class KIntNumInput; +class KLineEdit; + + +class AddRepositoryDialog : public KDialogBase +{ + Q_OBJECT + +public: + AddRepositoryDialog(KConfig& cfg, const QString& repo, QWidget* parent = 0, + const char* name = 0); + virtual ~AddRepositoryDialog(); + + void setRepository(const QString& repo); + void setRsh(const QString& rsh); + void setServer(const QString& server); + void setCompression(int compression); + void setRetrieveCvsignoreFile(bool enabled); + + QString repository() const; + QString rsh() const; + QString server() const; + int compression() const; + bool retrieveCvsignoreFile() const; + +private slots: + void repoChanged(); + void compressionToggled(bool checked); + +private: + KLineEdit* repo_edit; + KLineEdit* rsh_edit; + KLineEdit* server_edit; + QCheckBox* m_useDifferentCompression; + QCheckBox* m_retrieveCvsignoreFile; + KIntNumInput* m_compressionLevel; + KConfig& partConfig; +}; + +#endif + + +// Local Variables: +// c-basic-offset: 4 +// End: diff --git a/cervisia/annotatectl.cpp b/cervisia/annotatectl.cpp new file mode 100644 index 00000000..31f95f84 --- /dev/null +++ b/cervisia/annotatectl.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2002-2003 Christian Loose + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include "annotatectl.h" + +#include +#include + +#include +#include +#include + +#include "annotatedlg.h" +#include "loginfo.h" +#include "progressdlg.h" +#include "cvsservice_stub.h" +#include "cvsjob_stub.h" + +using namespace Cervisia; + +struct AnnotateController::Private +{ + typedef QMap RevisionCommentMap; + RevisionCommentMap comments; // maps comment to a revision + + CvsService_stub* cvsService; + AnnotateDialog* dialog; + ProgressDialog* progress; + + bool execute(const QString& fileName, const QString& revision); + void parseCvsLogOutput(); + void parseCvsAnnotateOutput(); +}; + + +AnnotateController::AnnotateController(AnnotateDialog* dialog, CvsService_stub* cvsService) + : d(new Private) +{ + // initialize private data + d->cvsService = cvsService; + d->dialog = dialog; + d->progress = 0; +} + + +AnnotateController::~AnnotateController() +{ + delete d; +} + + +void AnnotateController::showDialog(const QString& fileName, const QString& revision) +{ + if( !d->execute(fileName, revision) ) + { + delete d->dialog; + return; + } + + d->parseCvsLogOutput(); + d->parseCvsAnnotateOutput(); + + // hide progress dialog + delete d->progress; d->progress = 0; + + d->dialog->setCaption(i18n("CVS Annotate: %1").arg(fileName)); + d->dialog->show(); +} + + +bool AnnotateController::Private::execute(const QString& fileName, const QString& revision) +{ + DCOPRef job = cvsService->annotate(fileName, revision); + if( !cvsService->ok() ) + return false; + + progress = new ProgressDialog(dialog, "Annotate", job, "annotate", i18n("CVS Annotate")); + + return progress->execute(); +} + + +void AnnotateController::Private::parseCvsLogOutput() +{ + QString line, comment, rev; + + enum { Begin, Tags, Admin, Revision, + Author, Branches, Comment, Finished } state; + + state = Begin; + while( progress->getLine(line) ) + { + switch( state ) + { + case Begin: + if( line == "symbolic names:" ) + state = Tags; + break; + case Tags: + if( line[0] != '\t' ) + state = Admin; + break; + case Admin: + if( line == "----------------------------" ) + state = Revision; + break; + case Revision: + rev = line.section(' ', 1, 1); + state = Author; + break; + case Author: + state = Branches; + break; + case Branches: + if( !line.startsWith("branches:") ) + { + state = Comment; + comment = line; + } + break; + case Comment: + if( line == "----------------------------" ) + state = Revision; + else if( line == "=============================================================================" ) + state = Finished; + if( state == Comment ) + comment += QString("\n") + line; + else + comments[rev] = comment; + break; + case Finished: + ; + } + + if (state == Finished) + break; + } + + // skip header part of cvs annotate output + bool notEof = true; + while( notEof && !line.startsWith("*****") ) + notEof = progress->getLine(line); +} + + +void AnnotateController::Private::parseCvsAnnotateOutput() +{ + LogInfo logInfo; + QString rev, content, line; + QString oldRevision = ""; + bool odd = false; + + while( progress->getLine(line) ) + { + QString dateString = line.mid(23, 9); + if( !dateString.isEmpty() ) + logInfo.m_dateTime.setTime_t(KRFCDate::parseDate(dateString), Qt::UTC); + + rev = line.left(13).stripWhiteSpace(); + logInfo.m_author = line.mid(14, 8).stripWhiteSpace(); + content = line.mid(35, line.length()-35); + + logInfo.m_comment = comments[rev]; + if( logInfo.m_comment.isNull() ) + logInfo.m_comment = ""; + + if( rev == oldRevision ) + { + logInfo.m_author = QString::null; + rev = QString::null; + } + else + { + oldRevision = rev; + odd = !odd; + } + + logInfo.m_revision = rev; + + dialog->addLine(logInfo, content, odd); + } +} diff --git a/cervisia/annotatectl.h b/cervisia/annotatectl.h new file mode 100644 index 00000000..d6b76437 --- /dev/null +++ b/cervisia/annotatectl.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2002 Christian Loose + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef ANNOTATECTL_H +#define ANNOTATECTL_H + +#include + +class AnnotateDialog; +class CvsService_stub; +class QWidget; + + +class AnnotateController +{ +public: + AnnotateController(AnnotateDialog* dialog, CvsService_stub* cvsService); + ~AnnotateController(); + + void showDialog(const QString& fileName, const QString& revision = QString::null); + +private: + struct Private; + Private* d; +}; + + +#endif diff --git a/cervisia/annotatedlg.cpp b/cervisia/annotatedlg.cpp new file mode 100644 index 00000000..40b0d555 --- /dev/null +++ b/cervisia/annotatedlg.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 1999-2002 Bernd Gehrmann + * bernd@mail.berlios.de + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include "annotatedlg.h" + +#include "annotateview.h" + + +AnnotateDialog::AnnotateDialog(KConfig& cfg, QWidget *parent, const char *name) + : KDialogBase(parent, name, false, QString::null, + Close | Help, Close, true) + , partConfig(cfg) +{ + annotate = new AnnotateView(partConfig, this); + setMainWidget(annotate); + + setHelp("annotate"); + + setWFlags(Qt::WDestructiveClose | getWFlags()); + + QSize size = configDialogSize(partConfig, "AnnotateDialog"); + resize(size); +} + + +AnnotateDialog::~AnnotateDialog() +{ + saveDialogSize(partConfig, "AnnotateDialog"); +} + + +void AnnotateDialog::addLine(const Cervisia::LogInfo& logInfo, + const QString& content, bool odd) +{ + annotate->addLine(logInfo, content, odd); +} + + +// Local Variables: +// c-basic-offset: 4 +// End: diff --git a/cervisia/annotatedlg.h b/cervisia/annotatedlg.h new file mode 100644 index 00000000..48299cf3 --- /dev/null +++ b/cervisia/annotatedlg.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 1999-2002 Bernd Gehrmann + * bernd@mail.berlios.de + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef ANNOTATEDLG_H +#define ANNOTATEDLG_H + + +#include + + +class AnnotateView; +class QDate; +class KConfig; + +namespace Cervisia +{ +struct LogInfo; +} + + +class AnnotateDialog : public KDialogBase +{ +public: + + explicit AnnotateDialog( KConfig& cfg, QWidget *parent=0, const char *name=0 ); + + virtual ~AnnotateDialog(); + + void addLine(const Cervisia::LogInfo& logInfo, const QString& content, + bool odd); + +private: + AnnotateView *annotate; + KConfig& partConfig; +}; + +#endif + + +// Local Variables: +// c-basic-offset: 4 +// End: diff --git a/cervisia/annotateview.cpp b/cervisia/annotateview.cpp new file mode 100644 index 00000000..594ca936 --- /dev/null +++ b/cervisia/annotateview.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (C) 1999-2002 Bernd Gehrmann + * Copyright (c) 2003-2007 André Wöbbeking + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "annotateview.h" + +#include +#include +#include +#include + +#include "loginfo.h" +#include "tooltip.h" + + +using namespace Cervisia; + + +class AnnotateViewItem : public QListViewItem +{ +public: + enum { LineNumberColumn, AuthorColumn, ContentColumn }; + + AnnotateViewItem(AnnotateView *parent, const LogInfo& logInfo, + const QString &content, bool odd, int linenumber); + + virtual int compare(QListViewItem *item, int col, bool ascending) const; + virtual int width(const QFontMetrics &, const QListView *, int col) const; + virtual QString text(int col) const; + virtual void paintCell(QPainter *, const QColorGroup &, int, int, int); + +private: + LogInfo m_logInfo; + QString m_content; + bool m_odd; + int m_lineNumber; + friend class AnnotateView; + + static const int BORDER; +}; + + +const int AnnotateViewItem::BORDER = 4; + + +AnnotateViewItem::AnnotateViewItem(AnnotateView *parent, const LogInfo& logInfo, + const QString &content, bool odd, int linenumber) + : QListViewItem(parent) + , m_logInfo(logInfo) + , m_content(content) + , m_odd(odd) + , m_lineNumber(linenumber) +{} + + +int AnnotateViewItem::compare(QListViewItem *item, int, bool) const +{ + int linenum1 = m_lineNumber; + int linenum2 = static_cast(item)->m_lineNumber; + + return (linenum2 > linenum1)? -1 : (linenum2 < linenum1)? 1 : 0; +} + + +QString AnnotateViewItem::text(int col) const +{ + switch (col) + { + case LineNumberColumn: + return QString::number(m_lineNumber); + case AuthorColumn: + if( m_logInfo.m_author.isNull() ) + return QString::null; + else + return (m_logInfo.m_author + QChar(' ') + m_logInfo.m_revision); + case ContentColumn: + return m_content; + default: + ; + }; + + return QString::null; +} + + +void AnnotateViewItem::paintCell(QPainter *p, const QColorGroup &, int col, int width, int align) +{ + QColor backgroundColor; + + switch (col) + { + case LineNumberColumn: + backgroundColor = KGlobalSettings::highlightColor(); + p->setPen(KGlobalSettings::highlightedTextColor()); + break; + default: + backgroundColor = m_odd ? KGlobalSettings::baseColor() + : KGlobalSettings::alternateBackgroundColor(); + p->setPen(KGlobalSettings::textColor()); + break; + }; + + p->fillRect(0, 0, width, height(), backgroundColor); + + QString str = text(col); + if (str.isEmpty()) + return; + + if (align & (AlignTop || AlignBottom) == 0) + align |= AlignVCenter; + + p->drawText(BORDER, 0, width - 2*BORDER, height(), align, str); +} + + + +int AnnotateViewItem::width(const QFontMetrics &fm, const QListView *, int col) const +{ + return fm.width(text(col)) + 2*BORDER; +} + + +/*! + @todo The dummy column (remaining space eater) doesn't work + caused by a bug in QHeader::adjustHeaderSize() in Qt <= 3.0.4. +*/ + +AnnotateView::AnnotateView(KConfig &cfg, QWidget *parent, const char *name) + : QListView(parent, name, WRepaintNoErase | WResizeNoErase) +{ + setFrameStyle(QFrame::WinPanel | QFrame::Sunken); + setAllColumnsShowFocus(true); + setShowToolTips(false); + setSelectionMode(NoSelection); + header()->hide(); + // setResizeMode(LastColumn); + + addColumn(QString::null); + addColumn(QString::null); + addColumn(QString::null); + + setSorting(AnnotateViewItem::LineNumberColumn); + setColumnAlignment(AnnotateViewItem::LineNumberColumn, Qt::AlignRight); + + ToolTip* toolTip = new ToolTip(viewport()); + + connect(toolTip, SIGNAL(queryToolTip(const QPoint&, QRect&, QString&)), + this, SLOT(slotQueryToolTip(const QPoint&, QRect&, QString&))); + + KConfigGroupSaver cs(&cfg, "LookAndFeel"); + setFont(cfg.readFontEntry("AnnotateFont")); +} + + + +void AnnotateView::addLine(const LogInfo& logInfo, const QString& content, + bool odd) +{ + new AnnotateViewItem(this, logInfo, content, odd, childCount()+1); +} + + +QSize AnnotateView::sizeHint() const +{ + QFontMetrics fm(fontMetrics()); + return QSize(100 * fm.width("0"), 10 * fm.lineSpacing()); +} + + +void AnnotateView::slotQueryToolTip(const QPoint& viewportPos, + QRect& viewportRect, + QString& text) +{ + if (const AnnotateViewItem* item = static_cast(itemAt(viewportPos))) + { + const int column(header()->sectionAt(viewportPos.x())); + if ((column == AnnotateViewItem::AuthorColumn) && !item->m_logInfo.m_author.isNull()) + { + viewportRect = itemRect(item); + text = item->m_logInfo.createToolTipText(false); + } + } +} + + +#include "annotateview.moc" + + +// Local Variables: +// c-basic-offset: 4 +// End: diff --git a/cervisia/annotateview.h b/cervisia/annotateview.h new file mode 100644 index 00000000..be74d430 --- /dev/null +++ b/cervisia/annotateview.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 1999-2002 Bernd Gehrmann + * Copyright (c) 2003-2007 André Wöbbeking + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef ANNOTATEVIEW_H +#define ANNOTATEVIEW_H + + +#include + + +class KConfig; + + +namespace Cervisia +{ +struct LogInfo; +} + + +class AnnotateView : public QListView +{ + Q_OBJECT + +public: + + explicit AnnotateView( KConfig &cfg, QWidget *parent=0, const char *name=0 ); + + void addLine(const Cervisia::LogInfo& logInfo, const QString& content, + bool odd); + + virtual QSize sizeHint() const; + +private slots: + + void slotQueryToolTip(const QPoint&, QRect&, QString&); +}; + + +#endif diff --git a/cervisia/cervisia-change_repos_list.pl b/cervisia/cervisia-change_repos_list.pl new file mode 100644 index 00000000..925c49ee --- /dev/null +++ b/cervisia/cervisia-change_repos_list.pl @@ -0,0 +1,59 @@ +#!/usr/bin/perl + +# (copied from kdesdk/cervisia/misc.cpp) +# These regular expression parts aren't useful to check the validity of the +# CVSROOT specification. They are just used to extract the different parts of it. +$usernamerx = "([a-z0-9_][a-z0-9_-]*)?"; +$passwordrx = "(:[^@]+)?"; +$hostrx = "([^:/]+)"; +$portrx = "(:(\\d*))?"; +$pathrx = "(/.*)"; + +# concat above regexps into a single expression +$regexp = join('', ":pserver:(", $usernamerx, $passwordrx, "@)?", $hostrx, $portrx, $pathrx); + +$loginuser = getlogin || getpwuid($<); + +while(<>) +{ + ($key) = ($_ =~ /([^=]*)=(.*)$/); + ($value) = ($_ =~ /^[^=]*=(.*)$/); + + if( $key eq "Repos" ) + { + @repos = split(',', $value); + + foreach $repo ( @repos ) + { + # pserver CVSROOT specification? + if( $repo =~ m/($regexp)/ ) + { + # extract username, hostname, port and path from CVSROOT + $username = $3; + $hostname = $5; + $port = $7; + $path = $8; + + # replace empty port number + $port =~ s/^$/2401/; + + # replace empty username + $username =~ s/^$/$loginuser/; + + # create normalized CVSROOT specification + $repo = join('', ":pserver:", $username, "@", $hostname, ":", $port, $path); + } + } + + # remove duplicates from array + %seen = (); + @repos = grep { ! $seen{$_} ++ } @repos; + + $value = join(',', @repos); + print "# DELETE " . $key . "\n"; + print $key . "=" . $value . "\n"; + next; + } + + print $_; +} diff --git a/cervisia/cervisia-normalize_cvsroot.pl b/cervisia/cervisia-normalize_cvsroot.pl new file mode 100644 index 00000000..8a96dcb8 --- /dev/null +++ b/cervisia/cervisia-normalize_cvsroot.pl @@ -0,0 +1,53 @@ +#!/usr/bin/perl + +# (copied from kdesdk/cervisia/misc.cpp) +# These regular expression parts aren't useful to check the validity of the +# CVSROOT specification. They are just used to extract the different parts of it. +$usernamerx = "([a-z0-9_][a-z0-9_-]*)?"; +$passwordrx = "(:[^@]+)?"; +$hostrx = "([^:/]+)"; +$portrx = "(:(\\d*))?"; +$pathrx = "(/.*)"; + +# concat above regexps into a single expression +$regexp = join('', ":pserver:(", $usernamerx, $passwordrx, "@)?", $hostrx, $portrx, $pathrx); + +$loginuser = getlogin || getpwuid($<); + +while(<>) +{ + # skip empty lines + next if /^$/; + + # config group for a repository? + if( /^\[Repository-(.+)\]$/ ) + { + $oldcvsroot = $1; + + # pserver CVSROOT specification? + if( $oldcvsroot =~ m/($regexp)/ ) + { + # extract username, hostname, port and path from CVSROOT + $username = $3; + $hostname = $5; + $port = $7; + $path = $8; + + # replace empty port number + $port =~ s/^$/2401/; + + # replace empty username + $username =~ s/^$/$loginuser/; + + # create normalized CVSROOT specification + $newcvsroot = join('', ":pserver:", $username, "@", $hostname, ":", $port, $path); + + print "# DELETEGROUP [Repository-$oldcvsroot]\n"; + print "[Repository-$newcvsroot]\n"; + } + + next; + } + + print $_; +} diff --git a/cervisia/cervisia.1.in b/cervisia/cervisia.1.in new file mode 100644 index 00000000..b810f98c --- /dev/null +++ b/cervisia/cervisia.1.in @@ -0,0 +1,217 @@ +.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.3 +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sh \" Subsection heading +.br +.if t .Sp +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. | will give a +.\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to +.\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C' +.\" expand to `' in nroff, nothing in troff, for use with C<>. +.tr \(*W-|\(bv\*(Tr +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +'br\} +.\" +.\" If the F register is turned on, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. nr % 0 +. rr F +.\} +.\" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.hy 0 +.if n .na +.\" +.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). +.\" Fear. Run. Save yourself. No user-serviceable parts. +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds / +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +.\} +.rm #[ #] #H #V #F C +.\" ======================================================================== +.\" +.IX Title "CERVISIA 1" +.TH CERVISIA 1 "2006-01-05" "2.4.0" "Cervisia" +.SH "NAME" +Cervisia \- Graphical CVS frontend +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +\&\fBcervisia\fR + [\ \fB\-\-display\fR\ \fIdisplay\fR\ ] + [\ \fB\-\-caption\fR\ \fIcaption\fR\ ] + [\ \fB\-\-icon\fR\ \fIicon\fR\ ] + [\ \fB\-\-miniicon\fR\ \fIminiicon\fR\ ] + [\ \fB\-\-config\fR\ \fIfilename\fR\ ] + [\ \fB\-\-dcopserver\fR\ \fIserver\fR\ ] + [\ \fB\-\-nocrashhandler\fR\ ] + [\ \fB\-\-waitforwm\fR\ ] + [\ \fB\-\-style\fR\ \fIstyle\fR\ ] + [\ \fB\-\-geometry\fR\ \fIgeometry\fR\ ] + [\ \fB\-\-resolve\fR\ \fIfilename\fR\ ] + [\ \fB\-\-log\fR\ \fIfilename\fR\ ] + [\ \fB\-\-annotate\fR\ \fIfilename\fR\ ] + [\ \fIdirectory\fR\ ] +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +Cervisia is a graphical user interface for the Concurrent Versions +System. It is based on the \s-1KDE\s0 libraries and therefore shares +their Look'n'Feel, configuration and help system. +.SH "OPTIONS" +.IX Header "OPTIONS" +Cervisia accepts the following options: +.IP "\fIdirectory\fR" 4 +.IX Item "directory" +Tells Cervisia to open the sandbox in \fIdirectory\fR at startup +.IP "\fB\-\-resolve\fR \fIfilename\fR" 4 +.IX Item "--resolve filename" +Shows a resolve dialog for the given file +.IP "\fB\-\-log\fR \fIfilename\fR" 4 +.IX Item "--log filename" +Shows a log dialog for the given file +.IP "\fB\-\-annotate\fR \fIfilename\fR" 4 +.IX Item "--annotate filename" +Shows a annotation dialog for the given file +.IP "\fB\-\-caption\fR \fIcaption\fR" 4 +.IX Item "--caption caption" +Sets the caption, i. e. what is shown in the title bar +.IP "\fB\-\-icon\fR \fIicon\fR" 4 +.IX Item "--icon icon" +Sets the program's icon (used by window managers and panels) +.IP "\fB\-\-miniicon\fR \fIminiicon\fR" 4 +.IX Item "--miniicon miniicon" +Sets the program's mini icon (used by window managers and panels) +.IP "\fB\-\-config\fR \fIfilename\fR" 4 +.IX Item "--config filename" +Uses the given file for the configuration +.IP "\fB\-\-dcopserver\fR \fIserver\fR" 4 +.IX Item "--dcopserver server" +Sets the dcopserver the program should use +.IP "\fB\-\-nocrashhandler\fR" 4 +.IX Item "--nocrashhandler" +Disables the crash handler. Use this to get core dumps +.IP "\fB\-\-waitforwm\fR" 4 +.IX Item "--waitforwm" +Waits for a \s-1WM_NET\s0 compatible windowmanager +.IP "\fB\-\-style\fR \fIstyle\fR" 4 +.IX Item "--style style" +Sets the application \s-1GUI\s0 style +.IP "\fB\-\-geometry\fR \fIgeometry\fR" 4 +.IX Item "--geometry geometry" +Sets the geometry of the main window +.SH "FILES" +.IX Header "FILES" +\&\fI_KDECONFDIR_/cervisiarc\fR \- global configuration file +.Sp +\&\fI$HOME/.kde/share/config/cervisiarc\fR \- user-specific configuration file +.SH "SEE ALSO" +.IX Header "SEE ALSO" +\&\fI_KDEHTMLDIR_/en/cervisia/index.html\fR +.Sp +\&\fIcvs\fR\|(1) +.Sp +\&\fIhttp://cervisia.kde.org/\fR +.SH "AUTHOR" +.IX Header "AUTHOR" +Cervisia was originally developed by Bernd Gehrmann and is now maintained by +Christian Loose . +.SH "REPORTING BUGS" +.IX Header "REPORTING BUGS" +Report bugs at http://bugs.kde.org. diff --git a/cervisia/cervisia.desktop b/cervisia/cervisia.desktop new file mode 100644 index 00000000..b8745e0f --- /dev/null +++ b/cervisia/cervisia.desktop @@ -0,0 +1,74 @@ +[Desktop Entry] +GenericName=CVS Frontend +GenericName[af]=Cvs Voorprogram +GenericName[az]=CVS Ara Üzü +GenericName[bg]=Програма за CVS +GenericName[ca]=Programa de CVS +GenericName[cs]=Rozhraní pro CVS +GenericName[cy]=Ochr Blaen CVS +GenericName[da]=CVS-grænseflade +GenericName[de]=Graphische Oberfläche für CVS +GenericName[eo]=Fasado por la versioadministrilo "CVS" +GenericName[es]=Interfaz CVS +GenericName[et]=CVSi kasutajaliides +GenericName[eu]=CVS interfazea +GenericName[fa]=پایانۀ CVS +GenericName[fi]=Käyttöliittymä CVS:lle +GenericName[fr]=Interface graphique pour CVS +GenericName[ga]=Comhéadan ar CVS +GenericName[gl]=Interface para CVS +GenericName[he]=ממשק CVS +GenericName[hi]=सीवीएस फ्रन्टएण्ड +GenericName[hr]=CVS sučelje +GenericName[hu]=CVS-kliens +GenericName[is]=Myndrænt viðmót á CVS +GenericName[it]=Interfaccia CVS +GenericName[ja]=CVS フロントエンド +GenericName[kk]=CVS интерфейсі +GenericName[lt]=CVS naudotojo sąsaja +GenericName[lv]=CVS Frontends +GenericName[ms]=Bahagian Depan CVS +GenericName[nb]=CVS-grensesnitt +GenericName[nds]=CVS-Böversiet +GenericName[ne]=CVS फ्रन्टइन्ड +GenericName[nl]=CVS-hulpprogramma +GenericName[nn]=CVS-grensesnitt +GenericName[pa]=CVS ਮੁੱਖ ਝਲਕ +GenericName[pl]=Interfejs do CVS +GenericName[pt]=Interface de CVS +GenericName[pt_BR]=Interface para o CVS +GenericName[ro]=Interfaţă grafică pentru CVS +GenericName[ru]=Работа с репозиториями CVS +GenericName[sk]=Rozhranie pre CVS +GenericName[sl]=Vmesnik CVS +GenericName[sr]=Графички интерфејс за CVS +GenericName[sr@Latn]=Grafički interfejs za CVS +GenericName[sv]=CVS-gränssnitt +GenericName[ta]=CVS முன்பகுதி +GenericName[tg]=Интерфейс ба CVS +GenericName[th]=ฟร้อนต์เอนด์ CVS +GenericName[tr]=CVS Önyüzü +GenericName[uk]=Інтерфейс до CVS +GenericName[ven]=CVS yo iswa phanda +GenericName[xh]=CVS Isiqalo sesiphelo +GenericName[zh_CN]=CVS 前端 +GenericName[zh_TW]=CVS 前端 +GenericName[zu]=CVSIsiqalo sokugcina +Exec=cervisia -caption "%c" %i %m %u +Path= +Icon=cervisia +DocPath=cervisia/index.html +Type=Application +Terminal=false +MimeType=inode/directory; +Name=Cervisia +Name[hi]=सर्विसिया +Name[pa]=ਸਰਵੀਸੀਆ +Name[ta]= செர்வியா +Name[th]=เซอร์วิเซีย +ServiceTypes=KParts/ReadOnlyPart,Browser/View +X-KDE-Library=libcervisiapart +X-KDE-BrowserView-Args=DetailedList + +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Development; diff --git a/cervisia/cervisia.pod b/cervisia/cervisia.pod new file mode 100644 index 00000000..940ba8e0 --- /dev/null +++ b/cervisia/cervisia.pod @@ -0,0 +1,109 @@ +=head1 NAME + +Cervisia - Graphical CVS frontend + +=head1 SYNOPSIS + +B + S<[ B<--display> I ]> + S<[ B<--caption> I ]> + S<[ B<--icon> I ]> + S<[ B<--miniicon> I ]> + S<[ B<--config> I ]> + S<[ B<--dcopserver> I ]> + S<[ B<--nocrashhandler> ]> + S<[ B<--waitforwm> ]> + S<[ B<--style> I\n" ) + .arg( cg.highlight().name() ) + .arg( cg.highlightedText().name() ) ); + + text.append( "\n" ); + + QString highlightStyle = QString( "background: %1; color: %2; " ) + .arg( cg.highlight().name() ) + .arg( cg.highlightedText().name() ); + QString borderBottomStyle = QString( "border-bottom: solid %1 1px; " ) + .arg( cg.foreground().name() ); + QString borderTopStyle = QString( "border-top: solid %1 1px; " ) + .arg( cg.foreground().name() ); + + QString submitter = bug.submitter().fullName( true ); + int age = details.age(); + text.append( "
" ); + text.append( "server()->bugLink( bug ).url() + + "\">" + i18n("Bug Report from %1 " ) + .arg( submitter ) ); + int replies = details.parts().count() - 1; + if ( replies >= 1 ) text += i18n( "(1 reply)", "(%n replies)", replies ); + text += "
"; + text += "
" + + i18n( "1 day old", "%n days old", age ) + "
\n"; + + text.append( + QString( "
" + "" ) + .arg( cg.background().name() ) + .arg( cg.foreground().name() ) ); + text.append( textBugDetailsAttribute( details.version(), i18n("Version") ) ); + text.append( textBugDetailsAttribute( details.source(), i18n("Source") ) ); + text.append( textBugDetailsAttribute( details.compiler(), i18n("Compiler") ) ); + text.append( textBugDetailsAttribute( details.os(), i18n("OS") ) ); + text.append( "
\n" ); + + BugDetailsPart::List bdp = details.parts(); + BugDetailsPart::List::ConstIterator it; + bool firstHeader = true; + + for ( it = bdp.begin(); it != bdp.end(); ++it ) { + if ( bdp.count() > 1 ) { + text.append( "
" ); + QString sender = (*it).sender.fullName( true ); + QString date = KGlobal::locale()->formatDateTime( (*it).date, false ); + BugDetailsPart::List::ConstIterator it2 = it; + if ( ++it2 == bdp.end() ) + text.append( "server()->bugLink( bug ).url() + + "\">" + i18n("Bug Report from %1") + .arg( sender ) ); + else { + text.append( "server()->bugLink( bug ).url() + QString("#c%1").arg( replies ) + + "\">" + i18n("Reply #%1 from %2") + .arg( replies ).arg( sender ) ); + replies--; + } + text.append( "
\n" ); + text += "
" + date + "
\n"; + + firstHeader = false; + } + + // Adding of
 tags is now done by BugDetailsJob::processNode. This
+        // was breaking the display of attachments because they have 
\n + // after each line -> two newlines with
+        text.append( "
" ); + text.append( (*it).text ); + text.append( "
\n" ); + } + + QValueList atts = details.attachmentDetails(); + if ( atts.count() > 0 ) { + text.append( "" ); + text.append( QString( "") + .arg( i18n( "Attachment List") ) ); + text.append( QString("") + .arg( i18n("Description") ) + .arg( i18n("Date") ) + .arg( i18n("View") ) + .arg( i18n("Edit") ) ); + QValueList::iterator it; + for ( it = atts.begin() ; it != atts.end() ; ++it ) { + text.append( QString("").arg( (*it).description ) ) ; + text.append( QString("").arg( (*it).date ) ); + text.append( "" ); + text.append( "" ); + text.append( "" ); + } + } + + text.append( "" ); + + //kdDebug() << "BEGIN OUTPUT" << text << "END OUTPUT" << endl; + + m_bugDesc->view()->setContentsPos(0,0); + m_bugDesc->begin(); + m_bugDesc->write( text ); + m_bugDesc->end(); + + if ( KBBPrefs::instance()->mDebugMode ) mSource = text; +} + +void CWBugDetails::handleOpenURLRequest( const KURL &url, const KParts::URLArgs & ) +{ + new KRun( url ); +} + +QString CWBugDetails::textBugDetailsAttribute( const QString &value, + const QString &name ) +{ + QString text = ""; + if ( !value.isEmpty() ) { + text.append( "" + "" ); + } + return text; +} + +QString CWBugDetails::source() const +{ + return mSource; +} + +QString CWBugDetails::selectedText() const +{ + return m_bugDesc->selectedText(); +} + +#include "cwbugdetails.moc" + +/* vim: set et ts=4 sw=4 softtabstop=4: */ + diff --git a/kbugbuster/gui/cwbugdetails.h b/kbugbuster/gui/cwbugdetails.h new file mode 100644 index 00000000..31a5339f --- /dev/null +++ b/kbugbuster/gui/cwbugdetails.h @@ -0,0 +1,65 @@ +/* + cwbugdetails.h - Details of a bug report + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.org + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef KBBMAINWINDOW_CWBUGDETAILS_H +#define KBBMAINWINDOW_CWBUGDETAILS_H + +#include "bug.h" +#include "bugdetails.h" + +#include + +#include + +class KHTMLPart; + +namespace KBugBusterMainWindow +{ + +/** + * @author Martijn Klingens + */ +class CWBugDetails : public QWidget +{ + Q_OBJECT + + public: + CWBugDetails( QWidget* parent = 0, const char* name = 0 ); + ~CWBugDetails(); + + void setBug( const Bug &, const BugDetails & ); + + QString source() const; + QString selectedText() const; + + private slots: + void handleOpenURLRequest( const KURL &url, const KParts::URLArgs & ); + + private: + QString textBugDetailsAttribute( const QString &value, + const QString &name ); + + KHTMLPart *m_bugDesc; + + QString mSource; +}; + +} // namespace + +#endif + +/* vim: set et ts=4 softtabstop=4 sw=4: */ + diff --git a/kbugbuster/gui/cwbugdetailscontainer.cpp b/kbugbuster/gui/cwbugdetailscontainer.cpp new file mode 100644 index 00000000..b33ec3b1 --- /dev/null +++ b/kbugbuster/gui/cwbugdetailscontainer.cpp @@ -0,0 +1,269 @@ +/* + cwbugdetailscontainer.cpp - Container for bug details + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.org + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "bugsystem.h" +#include "bugcommand.h" +#include "bugserver.h" + +#include "cwbugdetails.h" +#include "cwloadingwidget.h" + +#include "cwbugdetailscontainer.h" +#include + +using namespace KBugBusterMainWindow; + +CWBugDetailsContainer::CWBugDetailsContainer( QWidget *parent , const char * name ) +: CWBugDetailsContainer_Base( parent, name ) +{ + // Do some stuff Designer can't do: + m_bugCloseBtn->setIconSet( BarIconSet( "edittrash" ) ); + m_bugCloseSilentlyBtn->setIconSet( BarIconSet( "edittrash" ) ); + m_bugReopenBtn->setIconSet( SmallIconSet( "idea" ) ); + m_bugReassignBtn->setIconSet( BarIconSet( "folder_new" ) ); + m_bugTitleBtn->setIconSet( SmallIconSet( "text_under" ) ); + m_bugSeverityBtn->setIconSet( SmallIconSet( "edit" ) ); + m_bugReplyBtn->setIconSet( SmallIconSet( "mail_replyall" ) ); + m_bugReplyPrivBtn->setIconSet( SmallIconSet( "mail_reply" ) ); + + // The Bugzilla mail interface doesn't support all commands yet. + m_bugCloseSilentlyBtn->hide(); + m_bugReassignBtn->hide(); + m_bugTitleBtn->hide(); + m_bugSeverityBtn->hide(); + + // Create Bug Details pane + m_bugDetails = new CWBugDetails( m_bugStack ); + + // Fill WidgetStack in Bug Details pane + m_bugLoading = new CWLoadingWidget( CWLoadingWidget::BottomFrame, + m_bugStack ); + connect( m_bugLoading, SIGNAL( clicked() ), SIGNAL( searchBugNumber() ) ); + + m_bugStack->addWidget( m_bugDetails, 0 ); + m_bugStack->addWidget( m_bugLoading, 1 ); + + setNoBug(); + + QFont f = m_bugLabel->font(); + f.setBold( true ); + m_bugLabel->setFont( f ); + + // Set fonts and margins + CWBugDetailsContainer_BaseLayout->setSpacing( KDialog::spacingHint() ); + CWBugDetailsContainer_BaseLayout->setMargin( KDialog::marginHint() ); + + connect( m_bugCloseBtn, SIGNAL( clicked() ), SIGNAL( signalCloseBug() ) ); + connect( m_bugCloseSilentlyBtn, SIGNAL( clicked() ), SIGNAL( signalCloseBugSilently() ) ); + connect( m_bugReopenBtn, SIGNAL( clicked() ), SIGNAL( signalReopenBug() ) ); + connect( m_bugReassignBtn, SIGNAL( clicked() ), SIGNAL( signalReassignBug() ) ); + connect( m_bugTitleBtn, SIGNAL( clicked() ), SIGNAL( signalTitleBug() ) ); + connect( m_bugSeverityBtn, SIGNAL( clicked() ), SIGNAL( signalSeverityBug() ) ); + connect( m_bugReplyBtn, SIGNAL( clicked() ), SIGNAL( signalReplyBug() ) ); + connect( m_bugReplyPrivBtn, SIGNAL( clicked() ), SIGNAL( signalReplyPrivateBug() ) ); + + connect( m_cmdClearBtn, SIGNAL( clicked() ), SIGNAL( signalClearCommand() ) ); + + connect( BugSystem::self(), SIGNAL( bugDetailsLoading( const Bug & ) ), + SLOT( setLoading( const Bug & ) ) ); + connect( BugSystem::self(), SIGNAL( bugDetailsCacheMiss( const Bug & ) ), + SLOT( setCacheMiss( const Bug & ) ) ); + connect( BugSystem::self(), SIGNAL( commandQueued( BugCommand * ) ), + SLOT( commandQueued( BugCommand * ) ) ); + connect( BugSystem::self(), SIGNAL( commandCanceled( const QString & ) ), + SLOT( clearCommand( const QString & ) ) ); +} + +CWBugDetailsContainer::~CWBugDetailsContainer() +{ +} + +void CWBugDetailsContainer::setBug( const Bug &bug, const BugDetails &details ) +{ + m_bug = bug; + m_bugDetails->setBug( bug, details ); + + QString labelText; + + if ( bug.mergedWith().size() ) + { + //FIXME: What should the separator be for lists? Don't see anything in KLocale for that + QString list; + Bug::BugMergeList mergedWith = bug.mergedWith(); + for (Bug::BugMergeList::ConstIterator i = mergedWith.begin(); i != mergedWith.end(); ++i) + { + list += QString::number(*i)+", "; + } + + list.truncate( list.length()-2 ); //Strip off the last ", " + + labelText = i18n("bug #number [Merged with: a list of bugs] (severity): title","Bug #%1 [Merged with: %2] (%3): %4") + .arg( bug.number() ) + .arg( list ) + .arg( bug.severityAsString() ) + .arg( bug.title() ); + + } + else + { + labelText = i18n("bug #number (severity): title","Bug #%1 (%2): %3") + .arg( bug.number() ).arg( bug.severityAsString() ) + .arg( bug.title() ); + } + + m_bugLabel->setText( KStringHandler::tagURLs( labelText ) ); + + showCommands( bug ); + + enableButtons( bug ); + + m_bugStack->raiseWidget( 0 ); + emit resetProgressBar(); +} + +void CWBugDetailsContainer::showCommands( const Bug& bug ) +{ + QPtrList commands = BugSystem::self()->server()->queryCommands( bug ); + if ( !commands.isEmpty() ) { + QString cmdDetails; + QString cmdText = i18n("Pending commands:")+" "; + bool first = true; + QPtrListIterator cmdIt( commands ); + for( ; cmdIt.current(); ++cmdIt ) + { + BugCommand *cmd = cmdIt.current(); + if (!first) + cmdText += " | "; // separator in case of multiple commands + first = false; + cmdText += QString("%1").arg( cmd->name() ); + if (!cmdDetails.isEmpty()) + cmdDetails += " | "; // separator in case of multiple commands + cmdDetails += cmd->details(); + } + // Set summary as text label, details into tooltip + m_cmdLabel->setText( cmdText ); + if ( !cmdDetails.isEmpty() ) { + QToolTip::add( m_cmdLabel, cmdDetails ); + } else { + QToolTip::remove( m_cmdLabel ); + } + m_cmdLabel->show(); + } else { + hideCommands(); + } +} + +void CWBugDetailsContainer::hideCommands() +{ + m_cmdLabel->hide(); +} + +void CWBugDetailsContainer::clearCommand( const QString &bug ) +{ + if ( bug == m_bug.number() ) + showCommands( m_bug ); +} + +void CWBugDetailsContainer::commandQueued( BugCommand *cmd ) +{ + // ### use == operator instead? + // (might not work because impl is different) + if ( cmd && cmd->bug().number() == m_bug.number() ) + showCommands( m_bug ); +} + +void CWBugDetailsContainer::setNoBug() +{ + m_bugLabel->setText( i18n("Bug Title") ); + + m_bug = Bug(); + showCommands( m_bug ); + + m_bugLoading->setText( i18n( "Click here to select a bug by number" ) ); + m_bugStack->raiseWidget( 1 ); +} + +void CWBugDetailsContainer::setLoading( const Bug &bug ) +{ + m_bug = bug; + showCommands( bug ); + + m_bugLoading->setText(i18n( "Retrieving Details for Bug %1\n\n(%2)" ) + .arg( bug.number() ).arg( bug.title() ) ); + m_bugStack->raiseWidget( 1 ); +} + +void CWBugDetailsContainer::setCacheMiss( const Bug &bug ) +{ + m_bug = bug; + showCommands( bug ); + + QString msg; + if( BugSystem::self()->disconnected() ) + msg = i18n( "Bug #%1 (%2) is not available offline." ). + arg( bug.number() ).arg( bug.title() ); + else + msg = i18n( "Retrieving details for bug #%1\n" + "(%2)" ). + arg( bug.number() ).arg( bug.title() ); + m_bugLoading->setText( msg ); + m_bugStack->raiseWidget( 1 ); +} + + +void CWBugDetailsContainer::enableButtons( const Bug &bug ) +{ + if( bug.isNull() ) { + m_bugCloseBtn->setEnabled( false ); + m_bugCloseSilentlyBtn->setEnabled( false ); + m_bugReopenBtn->setEnabled( false ); + m_bugReassignBtn->setEnabled( false ); + m_bugTitleBtn->setEnabled( false ); + m_bugSeverityBtn->setEnabled( false ); + m_bugReplyBtn->setEnabled( false ); + m_bugReplyPrivBtn->setEnabled( false ); + } else { + if( bug.status() != Bug::Closed ) { + m_bugCloseBtn->setEnabled( true ); + m_bugCloseSilentlyBtn->setEnabled( true ); + m_bugReopenBtn->setEnabled( false ); + } else { + m_bugCloseBtn->setEnabled( false ); + m_bugCloseSilentlyBtn->setEnabled( false ); + m_bugReopenBtn->setEnabled( true ); + } + m_bugReassignBtn->setEnabled( true ); + m_bugTitleBtn->setEnabled( true ); + m_bugSeverityBtn->setEnabled( true ); + m_bugReplyBtn->setEnabled( true ); + m_bugReplyPrivBtn->setEnabled( true ); + } +} + +#include "cwbugdetailscontainer.moc" + +/* vim: set et ts=4 sw=4 softtabstop=4: */ + diff --git a/kbugbuster/gui/cwbugdetailscontainer.h b/kbugbuster/gui/cwbugdetailscontainer.h new file mode 100644 index 00000000..c183a8ea --- /dev/null +++ b/kbugbuster/gui/cwbugdetailscontainer.h @@ -0,0 +1,88 @@ +/* + cwbugdetailscontainer.h - Container for bug details + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.org + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef KBBMAINWINDOW_CWBUGDETAILSCONTAINER_H +#define KBBMAINWINDOW_CWBUGDETAILSCONTAINER_H + +#include "bug.h" +#include "bugdetails.h" + +#include "cwbugdetailscontainer_base.h" + +class BugCommand; + +namespace KBugBusterMainWindow +{ + +class CWBugDetails; +class CWLoadingWidget; + +/** + * @author Martijn Klingens + */ +class CWBugDetailsContainer : public CWBugDetailsContainer_Base +{ + Q_OBJECT + +public: + CWBugDetailsContainer( QWidget* parent = 0, const char* name = 0 ); + ~CWBugDetailsContainer(); + + void setBug( const Bug &, const BugDetails & ); + + CWBugDetails *bugDetailsWidget() { return m_bugDetails; } + +public slots: + void setNoBug(); + void setLoading( const Bug &bug ); + void setCacheMiss( const Bug &bug ); + + void enableButtons( const Bug & ); + +signals: + void resetProgressBar(); + void searchBugNumber(); + + void signalCloseBug(); + void signalCloseBugSilently(); + void signalReopenBug(); + void signalReassignBug(); + void signalTitleBug(); + void signalSeverityBug(); + void signalReplyBug(); + void signalReplyPrivateBug(); + + void signalClearCommand(); + +private slots: + void showCommands( const Bug & ); + void clearCommand( const QString & ); + void commandQueued( BugCommand * ); + +private: + void hideCommands(); + + CWLoadingWidget *m_bugLoading; + CWBugDetails *m_bugDetails; + Bug m_bug; +}; + +} // namespace + +#endif + +/* vim: set et ts=4 softtabstop=4 sw=4: */ + diff --git a/kbugbuster/gui/cwbugdetailscontainer_base.ui b/kbugbuster/gui/cwbugdetailscontainer_base.ui new file mode 100644 index 00000000..c5ef87b7 --- /dev/null +++ b/kbugbuster/gui/cwbugdetailscontainer_base.ui @@ -0,0 +1,244 @@ + +CWBugDetailsContainer_Base + + + CWBugDetailsContainer_Base + + + + 0 + 0 + 507 + 559 + + + + + unnamed + + + 11 + + + 6 + + + + m_bugStack + + + + 7 + 7 + 0 + 0 + + + + + + Layout5 + + + + unnamed + + + 0 + + + 0 + + + + m_bugLabel + + + + 7 + 0 + 0 + 0 + + + + + 100 + 20 + + + + Bug Title + + + + + m_cmdLabel + + + Bug Commands + + + + + + + Layout3 + + + + unnamed + + + 0 + + + 6 + + + + m_cmdClearBtn + + + Clear Co&mmands + + + + + Spacer1 + + + Vertical + + + Expanding + + + + + m_bugCloseBtn + + + false + + + C&lose... + + + + + m_bugCloseSilentlyBtn + + + false + + + Close Silentl&y + + + + + m_bugReopenBtn + + + false + + + Re&open + + + + + m_bugReassignBtn + + + false + + + Re&assign... + + + + + m_bugTitleBtn + + + false + + + Change &Title... + + + + + m_bugSeverityBtn + + + false + + + Chan&ge Severity... + + + + + Spacer1_2 + + + Vertical + + + Expanding + + + + + m_bugReplyBtn + + + false + + + &Reply... + + + + + m_bugReplyPrivBtn + + + false + + + Reply &Privately... + + + + + + + + + QWidgetStack +
qwidgetstack.h
+ + -1 + -1 + + 1 + + 5 + 5 + 0 + 0 + + image0 +
+
+ + + 789c6dd2c10ac2300c00d07bbf2234b7229d1ddec44f503c0ae2a154410f53d0ed20e2bf6bdb656dd6861dd23d9a66591b0587fd1654235ebded6f0edcd53e419d87ae7b1f4f9b8f906d0bfe012317426a70b07bdc2f3ec77f8ed6b89559061a0343d06a124cc105596482585094bc0ae599b04646c9018926491b2205e140c485cace25755c175d0a967b622ff900b8cc9c7d29af594ea722d589167f813aa852ba07d94b9dce296e883fe7bb163f23896753 + + + +
diff --git a/kbugbuster/gui/cwbuglistcontainer.cpp b/kbugbuster/gui/cwbuglistcontainer.cpp new file mode 100644 index 00000000..0188a996 --- /dev/null +++ b/kbugbuster/gui/cwbuglistcontainer.cpp @@ -0,0 +1,328 @@ +/* + cwbuglistcontainer.cpp - Container for the bug list + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.org + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#if KDE_IS_VERSION( 3, 2, 90 ) +#include +#endif +#include + +#include "bugsystem.h" + +#include "cwloadingwidget.h" +#include "buglvi.h" +#include "kbbprefs.h" +#include "kfind.h" + +#include "cwbuglistcontainer.h" +#include + +using namespace KBugBusterMainWindow; + +CWBugListContainer::CWBugListContainer( QWidget *parent , const char * name ) + : QWidget( parent, name ), m_find(0), m_findItem(0) +{ + QBoxLayout *topLayout = new QVBoxLayout( this ); + topLayout->setSpacing( KDialog::spacingHint() ); + topLayout->setMargin( KDialog::marginHint() ); + + m_listLabel = new QLabel( this ); + topLayout->addWidget( m_listLabel ); + topLayout->setStretchFactor( m_listLabel, 0 ); + + QFont f = m_listLabel->font(); + f.setBold( true ); + m_listLabel->setFont( f ); + + m_listStack = new QWidgetStack( this ); + + // Create Outstanding Bugs listview + m_listBugs = new KListView( m_listStack ); + + topLayout->addWidget( new KListViewSearchLineWidget( m_listBugs, this ) ); + topLayout->addWidget( m_listStack ); + topLayout->setStretchFactor( m_listStack, 1 ); + + m_listBugs->addColumn( i18n( "Number" ) ); + m_listBugs->addColumn( i18n( "Age" ) ); + m_listBugs->addColumn( i18n( "Title" ), 500 ); // so that the widthmode isn't "Maximum" + m_listBugs->addColumn( i18n( "Status" ) ); + m_listBugs->addColumn( i18n( "Severity" ) ); + m_listBugs->addColumn( i18n( "Sender" ), 150 ); // idem. hardcoded widths suck a bit, but... + m_listBugs->setAllColumnsShowFocus( true ); + m_listBugs->setColumnAlignment( 0, AlignRight ); + m_listBugs->setSorting( 0, false ); + m_listBugs->setShowSortIndicator( true ); + m_listBugs->setSelectionMode( QListView::Extended ); // needed for merging bugs + + m_listBugs->restoreLayout( KBBPrefs::instance()->config(), "BugListLayout" ); + + connect( m_listBugs, SIGNAL( executed( QListViewItem * ) ), + SLOT( execute( QListViewItem * ) ) ); + connect( m_listBugs, SIGNAL( returnPressed( QListViewItem * ) ), + SLOT( execute( QListViewItem * ) ) ); + connect( m_listBugs, SIGNAL( currentChanged( QListViewItem * ) ), + SLOT( changeCurrent( QListViewItem * ) ) ); + + // Fill WidgetStack in Outstanding Bugs pane + m_listLoading = new CWLoadingWidget( CWLoadingWidget::TopFrame, + m_listStack ); + connect( m_listLoading, SIGNAL( clicked() ), SIGNAL( searchPackage() ) ); + + m_listStack->addWidget( m_listBugs, 0 ); + m_listStack->addWidget( m_listLoading, 1 ); + + setNoList(); + + connect( BugSystem::self(), SIGNAL( bugListLoading( const Package &, const QString & ) ), + SLOT( setLoading( const Package &, const QString & ) ) ); + connect( BugSystem::self(), SIGNAL( bugListLoading( const QString & ) ), + SLOT( setLoading( const QString & ) ) ); + connect( BugSystem::self(), SIGNAL( bugListCacheMiss( const Package & ) ), + SLOT( setCacheMiss( const Package & ) ) ); + connect( BugSystem::self(), SIGNAL( bugListCacheMiss( const QString & ) ), + SLOT( setCacheMiss( const QString & ) ) ); + connect( BugSystem::self(), SIGNAL( commandQueued( BugCommand * ) ), + SLOT( markBugCommand( BugCommand * ) ) ); + connect( BugSystem::self(), SIGNAL( commandCanceled( const QString & ) ), + SLOT( clearCommand( const QString & ) ) ); +} + +CWBugListContainer::~CWBugListContainer() +{ + m_listBugs->saveLayout( KBBPrefs::instance()->config(), "BugListLayout" ); + KBBPrefs::instance()->writeConfig(); + delete m_find; +} + +void CWBugListContainer::setBugList( const QString &label, const Bug::List &bugs ) +{ + // List pane is invisible by default, make visible + show(); + + m_listBugs->clear(); + emit resetProgressBar(); + bool showClosed = KBBPrefs::instance()->mShowClosedBugs; + bool showWishes = KBBPrefs::instance()->mShowWishes; + uint noBugs = 0; + uint noWishes = 0; + + for ( Bug::List::ConstIterator it = bugs.begin(); it != bugs.end(); ++it ) + { + if ( ( *it ).status() != Bug::Closed || showClosed ) + { + if ( ( *it ).severity() != Bug::Wishlist || showWishes ) + new BugLVI( m_listBugs, *it ); + + if ( ( *it ).severity() != Bug::Wishlist ) + noBugs++; + else + noWishes++; + } + } + + m_listLabel->setText( i18n( "%1 (%2 bugs, %3 wishes)" ).arg( label ).arg( noBugs ).arg( noWishes ) ); + m_listStack->raiseWidget( 0 ); +} + +void CWBugListContainer::setBugList( const Package &package, const QString &component, const Bug::List &bugs ) +{ + QString listLabel; + if ( component.isEmpty() ) + { + if ( package.components().count() > 1 ) + listLabel = i18n( "Product '%1', all components" ).arg( package.name() ); + else + listLabel = i18n( "Product '%1'" ).arg( package.name() ); + } + else + { + listLabel = i18n( "Product '%1', component '%2'" ).arg( package.name(), component ); + } + + setBugList( listLabel, bugs ); +} + +void CWBugListContainer::execute( QListViewItem *lvi ) +{ + BugLVI *item = dynamic_cast( lvi ); + if( !item ) + { + kdWarning() << "CWBugListContainer::execute() Selected bug " + << lvi->text( 0 ) + << " is not a BugLVI! Ignoring event." << endl; + return; + } + + emit executed( item->bug() ); +} + +void CWBugListContainer::changeCurrent( QListViewItem *lvi ) +{ + if( !lvi ) { + emit currentChanged( Bug() ); + return; + } + + BugLVI *item = dynamic_cast( lvi ); + if( !item ) + { + kdWarning() << "CWBugListContainer::changeCurrent() Selected bug " + << lvi->text( 0 ) + << " is not a BugLVI! Ignoring event." << endl; + return; + } + + emit currentChanged( item->bug() ); +} + +void CWBugListContainer::setNoList() +{ + m_listLabel->setText( i18n("Outstanding Bugs") ); + m_listLoading->setText( i18n( "Click here to select a product" ) ); + m_listStack->raiseWidget( 1 ); +} + +void CWBugListContainer::setLoading( const Package &package, const QString &component ) +{ + if ( component.isEmpty() ) + setLoading( i18n( "Retrieving List of Outstanding Bugs for Product '%1'..." ).arg( package.name() ) ); + else + setLoading( i18n( "Retrieving List of Outstanding Bugs for Product '%1' (Component %2)..." ).arg( package.name(), component ) ); +} + +void CWBugListContainer::setLoading( const QString &label ) +{ + m_listLoading->setText( label ); + m_listStack->raiseWidget( 1 ); +} + +void CWBugListContainer::setCacheMiss( const Package &package ) +{ + setCacheMiss( i18n( "Package '%1'" ).arg( package.name() ) ); +} + +void CWBugListContainer::setCacheMiss( const QString &label ) +{ + m_listLoading->setText( i18n( "%1 is not available offline." ).arg( label ) ); + m_listStack->raiseWidget( 1 ); +} + +void CWBugListContainer::markBugCommand( BugCommand *cmd ) +{ + BugLVI *item = (BugLVI *)m_listBugs->firstChild(); + while( item ) { + if ( item->bug().number() == cmd->bug().number() ) { + item->setCommandState( BugCommand::Queued ); + break; + } + item = (BugLVI *)item->nextSibling(); + } + m_listBugs->triggerUpdate(); +} + +void CWBugListContainer::clearCommand( const QString &bug ) +{ + BugLVI *item = (BugLVI *)m_listBugs->firstChild(); + while( item ) { + if ( item->bug().number() == bug ) { + item->setCommandState( BugCommand::None ); + break; + } + item = (BugLVI *)item->nextSibling(); + } + m_listBugs->triggerUpdate(); +} + +void CWBugListContainer::searchBugByTitle( int options, const QString& pattern ) +{ + m_find = new KFind( pattern, options, this ); + // Connect signals to code which handles highlighting + // of found text. + connect(m_find, SIGNAL( highlight( const QString &, int, int ) ), + this, SLOT( searchHighlight( const QString &, int, int ) ) ); + connect(m_find, SIGNAL( findNext() ), this, SLOT( slotFindNext() ) ); + + m_findItem = (BugLVI *)m_listBugs->firstChild(); + if ( options & KFindDialog::FromCursor && m_listBugs->currentItem() ) + m_findItem = (BugLVI *)m_listBugs->currentItem(); + + slotFindNext(); +} + +// Note: if a 'find next' action is added, then one should also ensure +// that m_findItem never becomes dangling (i.e. clear it when clearing the listview). +void CWBugListContainer::slotFindNext() +{ + KFind::Result res = KFind::NoMatch; + while( res == KFind::NoMatch && m_findItem ) { + + if ( m_find->needData() ) + m_find->setData( m_findItem->text(2) ); + + // Let KFind inspect the text fragment, and display a dialog if a match is found + res = m_find->find(); + + if ( res == KFind::NoMatch ) { + if ( m_find->options() & KFindDialog::FindBackwards ) + m_findItem = (BugLVI *)m_findItem->itemAbove(); + else + m_findItem = (BugLVI *)m_findItem->itemBelow(); + } + } + if ( res == KFind::NoMatch ) // i.e. at end + if ( m_find->shouldRestart() ) { + m_findItem = (BugLVI *)m_listBugs->firstChild(); + slotFindNext(); + } else { + delete m_find; + m_find = 0L; + } +} + +void CWBugListContainer::searchHighlight( const QString &, int, int ) +{ + if ( m_findItem ) { + m_listBugs->clearSelection(); + m_listBugs->setSelected( m_findItem, true ); + m_listBugs->ensureItemVisible( m_findItem ); + } +} + +QStringList CWBugListContainer::selectedBugs() const +{ + QStringList lst; + BugLVI *item = (BugLVI *)m_listBugs->firstChild(); + while( item ) { + if ( item->isSelected() ) + lst.append( item->bug().number() ); + item = (BugLVI *)item->nextSibling(); + } + return lst; +} + +#include "cwbuglistcontainer.moc" + +/* vim: set et ts=4 sw=4 softtabstop=4: */ diff --git a/kbugbuster/gui/cwbuglistcontainer.h b/kbugbuster/gui/cwbuglistcontainer.h new file mode 100644 index 00000000..bcda4c15 --- /dev/null +++ b/kbugbuster/gui/cwbuglistcontainer.h @@ -0,0 +1,99 @@ +/* + cwbuglistcontainer.h - Container for the bug list + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.org + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef KBBMAINWINDOW_CWBUGLISTCONTAINER_H +#define KBBMAINWINDOW_CWBUGLISTCONTAINER_H + +#include "package.h" +#include "bug.h" + +#include + +class KListView; +class KFind; +class BugCommand; +class BugLVI; + +namespace KBugBusterMainWindow +{ + +class CWLoadingWidget; + +/** + * @author Martijn Klingens + */ +class CWBugListContainer : public QWidget +{ + Q_OBJECT + +public: + CWBugListContainer( QWidget* parent = 0, const char* name = 0 ); + ~CWBugListContainer(); + + void setBugList( const Package &package, const QString &component, const Bug::List &bugs ); + + /** + * Overloaded method that takes a QString for the label. To be used when the + * bug list doesn't belong to a package, liek search results + */ + void setBugList( const QString &label, const Bug::List &bugs ); + + void searchBugByTitle( int options, const QString& pattern ); + + /** Return list of selected bugs in the listview. Used for merging. */ + QStringList selectedBugs() const; + +public slots: + void setNoList(); + void setLoading( const Package &package, const QString &component ); + void setLoading( const QString &label ); + void setCacheMiss( const Package &package ); + void setCacheMiss( const QString &label ); + void slotFindNext(); + +signals: + void resetProgressBar(); + void searchPackage(); + + void executed( const Bug & ); + void currentChanged( const Bug & ); + +private slots: + void execute( QListViewItem * ); + void changeCurrent( QListViewItem * ); + + void markBugCommand( BugCommand * ); + void clearCommand( const QString & ); + + void searchHighlight( const QString &, int, int ); + +private: + QLabel *m_listLabel; + QWidgetStack *m_listStack; + + KListView *m_listBugs; + KFind *m_find; + BugLVI *m_findItem; + + CWLoadingWidget *m_listLoading; +}; + +} // namespace + +#endif + +/* vim: set et ts=4 softtabstop=4 sw=4: */ + diff --git a/kbugbuster/gui/cwloadingwidget.cpp b/kbugbuster/gui/cwloadingwidget.cpp new file mode 100644 index 00000000..ddd218a5 --- /dev/null +++ b/kbugbuster/gui/cwloadingwidget.cpp @@ -0,0 +1,256 @@ +/* + cwloadingwidget.cpp - Widget to be shown while loading data + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.org + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "cwloadingwidget.h" + +#include +#include +#include +#include + +#include +#include +#include + +using namespace KBugBusterMainWindow; + +CWLoadingWidget::CWLoadingWidget( WidgetMode mode, QWidget *parent, + const char * name ) +: QFrame( parent, name ) +{ + init( mode ); +} + +CWLoadingWidget::CWLoadingWidget( const QString &text, WidgetMode mode, + QWidget *parent, const char * name ) +: QFrame( parent, name ) +{ + init( mode ); + setText( text ); +} + +void CWLoadingWidget::init( WidgetMode mode ) +{ + m_mode = mode; + + QPalette pal = palette(); + pal.setColor( QPalette::Active, QColorGroup::Background, + QColor( 49, 121, 173 ) ); + pal.setColor( QPalette::Inactive, QColorGroup::Background, + QColor( 49, 121, 173 ) ); + pal.setColor( QPalette::Disabled, QColorGroup::Background, + QColor( 49, 121, 173 ) ); + setPalette( pal ); + + setFrameShape( StyledPanel ); + setFrameShadow( Sunken ); + setLineWidth( 2 ); + + setBackgroundMode( NoBackground ); // no flicker + + // Load images and prepare pixmap effect + if( m_mode == TopFrame ) + { + m_logoPixmap = + new QPixmap( locate( "data", "kbugbuster/pics/logo.png" ) ); + m_topRightPixmap = + new QPixmap( locate( "data", "kbugbuster/pics/top-right.png" ) ); + m_barsPixmap = + new QPixmap( locate( "data", "kbugbuster/pics/bars.png" ) ); + m_toolsPixmap = 0L; + m_toolsPixmapEffect = 0L; + } + else + { + m_toolsPixmap = + new QPixmap( locate( "data", "kbugbuster/pics/tools.png" ) ); + + m_toolsPixmapEffect = new KPixmap( m_toolsPixmap->size() ); + + QPainter pb; + pb.begin( m_toolsPixmapEffect ); + pb.fillRect( 0, 0, m_toolsPixmap->width(), m_toolsPixmap->height(), + QBrush( QColor( 49, 121, 172 ) ) ); + pb.drawPixmap( 0, 0, *m_toolsPixmap ); + pb.end(); + + KPixmapEffect::fade( *m_toolsPixmapEffect, 0.75, white ); + + m_logoPixmap = 0L; + m_topRightPixmap = 0L; + m_barsPixmap = 0L; + } + + // Create and fill the buffer + m_buffer = new QPixmap; +} + +void CWLoadingWidget::resizeEvent( QResizeEvent * ) +{ + updatePixmap(); +} + +void CWLoadingWidget::setText( const QString &text ) +{ + m_text = text; + updatePixmap(); + repaint(); +} + +void CWLoadingWidget::updatePixmap() +{ + QRect cr = contentsRect(); + cr.setWidth( cr.width() + 2 ); + cr.setHeight( cr.height() + 2 ); + m_buffer->resize( cr.width(), cr.height() ); + + QPainter p( m_buffer ); + + // fill background + p.fillRect( 0, 0, cr.width(), cr.height(), + QBrush( QColor( 49, 121, 173 ) ) ); + + if( m_mode == TopFrame ) + { + QFont bigFont = QFont( KGlobalSettings::generalFont().family(), + 28, QFont::Bold, true ); + + int xoffset = m_logoPixmap->width(); + + // Draw bars tiled + int xpos = xoffset; + if( width() > xpos ) + p.drawTiledPixmap( xpos, 0, cr.width() - xpos, + m_barsPixmap->height(), *m_barsPixmap ); + + // Draw logo + p.drawPixmap(width() - m_topRightPixmap->width(), 0, *m_topRightPixmap); + p.drawPixmap( 0, 0, *m_logoPixmap ); + + // Draw title text + p.setPen( black ); + p.drawText( 150, 84, cr.width() - 150, 108 - 84, + AlignAuto | AlignVCenter, m_text ); + + // Draw intro text + QString desc = i18n( "Welcome to KBugBuster, a tool to manage the " + "KDE Bug Report System. With KBugBuster you can " + "manage outstanding bug reports for KDE from a " + "convenient front end." ); + p.setPen( black ); + p.drawText( 28, 128, cr.width() - 28, 184 - 128, + AlignAuto | AlignVCenter | WordBreak, desc ); + + // Draw the caption text + QString caption = i18n( "KBugBuster" ); + p.setFont( bigFont ); + p.setPen( QColor(139, 183, 222) ); + p.drawText( 220, 60, caption ); + p.setPen( black ); + p.drawText( 217, 57, caption ); + } + else + { + // draw tools image + if( cr.height() <= 24 ) + return; + + int toolsEffectY = cr.height() - m_toolsPixmap->height(); + int toolsEffectX = cr.width() - m_toolsPixmap->width(); + if ( toolsEffectX < 0) + toolsEffectX = 0; + if ( height() < 24 + m_toolsPixmap->height() ) + toolsEffectY = 24; + + p.drawPixmap( toolsEffectX, toolsEffectY, *m_toolsPixmap ); + + // draw textbox + if( cr.height() <= 24 + 50 ) + return; + + int fheight = fontMetrics().height(); + + int boxX = 25; + int boxY = 24 + 50; + int boxW = cr.width() - 2 * boxX - 2 * 10; + if( boxW > 500 ) + boxW = 500; + + QRect br = fontMetrics().boundingRect( boxX, boxY, + boxW, cr.height() - boxY - 10 - 2 * fheight, + AlignAuto | AlignTop | WordBreak, m_text ); + + QRect box = br; + box.setHeight( box.height() + 2 * fheight ); + box.setWidth( box.width() + 2 * 10 ); + if( box.width() < cr.width() - 2 * boxX ) + box.setWidth( QMIN( cr.width() - 2 * boxX, 500 + 2 * 10 ) ); + if( box.height() < 100 ) + box.setHeight( QMIN( cr.height() - boxY - 2 * fheight - 10, 100 ) ); + + p.setClipRect( box ); + p.fillRect( box, QBrush( QColor( 204, 222, 234 ) ) ); + p.drawPixmap( toolsEffectX, toolsEffectY, *m_toolsPixmapEffect ); + + p.setViewport( box ); + p.setWindow( 0, 0, box.width(), box.height() ); + + p.drawText( 10, fheight, br.width(), + QMAX( br.height(), box.height() - 2 * fheight ), + AlignAuto | AlignVCenter | WordBreak, m_text ); + } +} + +CWLoadingWidget::~CWLoadingWidget() +{ + if( m_toolsPixmap ) + delete m_toolsPixmap; + if( m_logoPixmap ) + delete m_logoPixmap; + if( m_topRightPixmap ) + delete m_topRightPixmap; + if( m_barsPixmap ) + delete m_barsPixmap; + if( m_toolsPixmapEffect ) + delete m_toolsPixmapEffect; + + delete m_buffer; + + m_toolsPixmap = 0L; + m_logoPixmap = 0L; + m_topRightPixmap = 0L; + m_barsPixmap = 0L; + m_toolsPixmapEffect = 0L; + m_buffer = 0L; +} + +void CWLoadingWidget::mouseReleaseEvent( QMouseEvent * ) +{ + emit clicked(); +} + +void CWLoadingWidget::drawContents( QPainter *p ) +{ + if( !m_buffer || m_buffer->isNull() ) + p->fillRect( contentsRect(), QBrush( QColor( 255, 121, 172 ) ) ); + else + p->drawPixmap( QPoint( contentsRect().x(), contentsRect().y()), + *m_buffer, contentsRect() ); +} + +#include "cwloadingwidget.moc" + +/* vim: set et ts=4 sw=4 softtabstop=4: */ diff --git a/kbugbuster/gui/cwloadingwidget.h b/kbugbuster/gui/cwloadingwidget.h new file mode 100644 index 00000000..17c91f05 --- /dev/null +++ b/kbugbuster/gui/cwloadingwidget.h @@ -0,0 +1,87 @@ +/* + cwloadingwidget.h - Widget to be shown while loading data + + begin : sun march 18 17:12:24 CET 2001 + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.org + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef KBBMAINWINDOW_CWLOADINGWIDGET_H +#define KBBMAINWINDOW_CWLOADINGWIDGET_H + +#include +#include + +class QPixmap; +class KPixmap; + +namespace KBugBusterMainWindow +{ + +/** + * @author Martijn Klingens + */ +class CWLoadingWidget : public QFrame +{ + Q_OBJECT + +public: + /** + * Use WidgetMode to specify the layout for the background images + * TopFrame loads and uses the logo and horizontal bars, + * BottomFrame loads the tools and the translucent block. + */ + enum WidgetMode { TopFrame = 0, BottomFrame }; + + CWLoadingWidget( WidgetMode mode = TopFrame, QWidget* parent = 0, + const char* name = 0 ); + CWLoadingWidget( const QString &text, WidgetMode mode = TopFrame, + QWidget* parent = 0, const char* name = 0 ); + ~CWLoadingWidget(); + + QString text() const { return m_text; } + void setText( const QString &text ); + +protected: + virtual void mouseReleaseEvent( QMouseEvent * ); + virtual void drawContents( QPainter *p ); + virtual void resizeEvent( QResizeEvent * ); + +signals: + void clicked(); + +private: + void init( WidgetMode mode ); + void updatePixmap(); + + QString m_text; + WidgetMode m_mode; + + // Pixmaps used + QPixmap *m_toolsPixmap; + QPixmap *m_logoPixmap; + QPixmap *m_topRightPixmap; + QPixmap *m_barsPixmap; + + // For performance reasons we apply the KPixmapEffect only once + KPixmap *m_toolsPixmapEffect; + + QPixmap *m_buffer; + +}; + +} // namespace + +#endif // KBBMAINWINDOW_CWLOADINGWIDGET_H + +/* vim: set et ts=4 softtabstop=4 sw=4: */ + diff --git a/kbugbuster/gui/cwsearchwidget.cpp b/kbugbuster/gui/cwsearchwidget.cpp new file mode 100644 index 00000000..8f7fcb26 --- /dev/null +++ b/kbugbuster/gui/cwsearchwidget.cpp @@ -0,0 +1,69 @@ +/* + cwsearchwidget.cpp - Search Pane + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.org + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "cwsearchwidget.h" + +using namespace KBugBusterMainWindow; + +CWSearchWidget::CWSearchWidget( QWidget *parent , const char * name ) +: CWSearchWidget_Base( parent, name ) +{ + // Set fonts and margins + CWSearchWidget_BaseLayout->setSpacing( KDialog::spacingHint() ); + CWSearchWidget_BaseLayout->setMargin( KDialog::marginHint() ); + + QFont f = m_searchLabel->font(); + f.setBold( true ); + m_searchLabel->setFont( f ); + + connect( m_searchDesc, SIGNAL( textChanged ( const QString & ) ), + this, SLOT( textDescriptionChanged ( const QString & ) ) ); + + connect( m_searchBugNumber, SIGNAL( textChanged ( const QString & ) ), + this, SLOT( textNumberChanged ( const QString & ) ) ); + + m_searchDescBtn->setEnabled( !m_searchDesc->text().isEmpty() ); + m_searchBugNumberBtn->setEnabled( !m_searchBugNumber->text().isEmpty() ); + +// m_searchPackages->setCompletionMode( KGlobalSettings::CompletionAuto ); +} + +CWSearchWidget::~CWSearchWidget() +{ +} + +void CWSearchWidget::textDescriptionChanged ( const QString &_text ) +{ + m_searchDescBtn->setEnabled( !_text.isEmpty() ); +} + +void CWSearchWidget::textNumberChanged ( const QString &_text ) +{ + m_searchBugNumberBtn->setEnabled( !_text.isEmpty() ); +} + +#include "cwsearchwidget.moc" + +/* vim: set et ts=4 sw=4 softtabstop=4: */ + diff --git a/kbugbuster/gui/cwsearchwidget.h b/kbugbuster/gui/cwsearchwidget.h new file mode 100644 index 00000000..4cf65720 --- /dev/null +++ b/kbugbuster/gui/cwsearchwidget.h @@ -0,0 +1,46 @@ +/* + cwsearchwidget.h - Search Pane + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.org + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef KBBMAINWINDOW_CWSEARCHWIDGET_H +#define KBBMAINWINDOW_CWSEARCHWIDGET_H + +#include "cwsearchwidget_base.h" + +namespace KBugBusterMainWindow +{ + +/** + * @author Martijn Klingens + */ +class CWSearchWidget : public CWSearchWidget_Base +{ + Q_OBJECT + +public: + CWSearchWidget( QWidget* parent = 0, const char* name = 0 ); + ~CWSearchWidget(); + +public slots: + void textNumberChanged ( const QString & ); + void textDescriptionChanged ( const QString & ); +}; + +} // namespace + +#endif + +/* vim: set et ts=4 softtabstop=4 sw=4: */ + diff --git a/kbugbuster/gui/cwsearchwidget_base.ui b/kbugbuster/gui/cwsearchwidget_base.ui new file mode 100644 index 00000000..c5e9b860 --- /dev/null +++ b/kbugbuster/gui/cwsearchwidget_base.ui @@ -0,0 +1,198 @@ + +CWSearchWidget_Base + + + CWSearchWidget_Base + + + + 0 + 0 + 165 + 266 + + + + + unnamed + + + 0 + + + 6 + + + + m_searchLabel + + + Search + + + + + TextLabel2 + + + &Package: + + + m_searchPackages + + + + + m_searchPackages + + + StrongFocus + + + true + + + NoInsertion + + + + + TextLabel9 + + + Bug &number: + + + m_searchBugNumber + + + + + Layout17 + + + + unnamed + + + 0 + + + 6 + + + + m_searchBugNumber + + + + 1 + 0 + 0 + 0 + + + + + + m_searchBugNumberBtn + + + + 0 + 0 + 0 + 0 + + + + + + + image0 + + + + + + + TextLabel7 + + + &Description: + + + m_searchDesc + + + + + Layout15 + + + + unnamed + + + 0 + + + 6 + + + + m_searchDesc + + + + 1 + 0 + 0 + 0 + + + + + + m_searchDescBtn + + + + 0 + 0 + 0 + 0 + + + + + + + image0 + + + + + + + Spacer1 + + + Vertical + + + Expanding + + + + + + + 789c75d4594fdb401405e0777e4584df5075b0e325b6aa3eb015ca1e76a8fa309ea518ca9eb055fdef9d39774815447311e4c3c73377c6cbfc5cef6477ab37373ff33052a34ef7f4b9baefcd99f1d5d5cbf71f5f7ecfcc6655cfff544daf3ffb69667638eae9def6cdb50d40e791a4fc04274aac7df1f840ecfc87bea3b350b4a5fba1787e420f42f1f8906e42d16b741b8a6e691b8a7e09e6f0323fc7eb0f42d179706e956a739afde62e146dde5cc8f93ab8487d49bf2774def65b4d1f0797b96e4c416f0757a9ceb4a1fb74ad2b23e73fd146175acecf68e7cdfe13ae87cb97fc0a9de94a8e23a52bdd9a92de129b8195f51cd1b5a9a3bfd28d56b1bf92567e3ed9bf535aeb26f67b41bbba5fcbfcb7c1755ae735af0f76e8526b23aee846d746f6eb9056a6b5d2df01ddfafee4f832ed8cb3355d0437a9298df4f318dd18b91e9b746933abe8bd60559b2caeff3adaca7c0968632a23f9fd6863a5df1bb12de2fc67b4f579997f8976b671d2ef46709b5a65a59f57baf2fdc97ed57463fc90f46a741bd7fb4c2babe3f963da5ae3647fcf8375660a53b17fde6f3a0fc5e3eb623fbfdc2f5774691b2bf335b4f2c7a59f5dda581dfb77b435b591e76731d8a42e73b27f0b74e9fa4eee8f6ff4c09696fde0e7c4b25fbf68edf3d2df88b6ae74f27c5d065b3e40f47d7411f30fb472ad53f23291420285f64df17f7a387acbc0c0c2e127ced1c14ce724e31317b8c42f5ce11a37fe7b4ce116b793cc1deef18011c678c4139ed1c5840e25ef0fbc62c12716b18465ace02b56b136c924f24ec137acfb3136b0892d6c6307bb18c655243217d6b0877d1ce0104738c6094e7126b34df5dcf9515264e8fb44eebf77ff59d718054a54feef853f67f03ec354871a4d82b03f18f8dfddfb0c73774982bbe12851e89236d16fa9c9387aea0a74d0ff5253e3e88f53b167eef9bbab3949c97b45f6fce319a732c9fb91980a73fdf93cf317e4bbac85 + + + + kcombobox.h + + + diff --git a/kbugbuster/gui/kbbbookmarkmanager.h b/kbugbuster/gui/kbbbookmarkmanager.h new file mode 100644 index 00000000..64edfc24 --- /dev/null +++ b/kbugbuster/gui/kbbbookmarkmanager.h @@ -0,0 +1,23 @@ +#ifndef KBBBOOKMARKMANAGER_H +#define KBBBOOKMARKMANAGER_H + +#include +#include + +class KBBBookmarkManager +{ +public: + static KBookmarkManager * self() { + if ( !s_bookmarkManager ) + { + QString bookmarksFile = locateLocal("data", QString::fromLatin1("kbugbuster/bookmarks.xml")); + s_bookmarkManager = KBookmarkManager::managerForFile( bookmarksFile ); + } + return s_bookmarkManager; + } + + + static KBookmarkManager *s_bookmarkManager; +}; + +#endif diff --git a/kbugbuster/gui/kbbmainwindow.cpp b/kbugbuster/gui/kbbmainwindow.cpp new file mode 100644 index 00000000..66a9b588 --- /dev/null +++ b/kbugbuster/gui/kbbmainwindow.cpp @@ -0,0 +1,504 @@ +/*************************************************************************** + kbbmainwindow.cpp - description + ------------------- + copyright : (C) 2001 by Martijn Klingens + email : klingens@kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kbbmainwindow.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "bugcommand.h" +#include "bugserver.h" +#include "bugserverconfig.h" +#include "bugsystem.h" +#include "centralwidget.h" +#include "cwbugdetails.h" +#include "kbbbookmarkmanager.h" +#include "kbbprefs.h" +#include "kfinddialog.h" +#include "packageselectdialog.h" +#include "preferencesdialog.h" + +#define ID_STATUS_MSG 1 + +using namespace KBugBusterMainWindow; + +class TextViewer : public KDialogBase +{ + public: + TextViewer( const QString &title, QWidget *parent = 0 ) + : KDialogBase( Plain, title, Ok, Ok, parent, 0, + false ) + { + QFrame *topFrame = plainPage(); + + QBoxLayout *topLayout = new QVBoxLayout( topFrame ); + + mTextView = new QTextEdit( topFrame ); + mTextView->setReadOnly( true ); + mTextView->setTextFormat( PlainText ); + topLayout->addWidget( mTextView ); + + resize( 600, 400 ); + } + + void setText( const QString &text ) + { + mTextView->setText( text ); + } + + private: + QTextEdit *mTextView; +}; + +KBookmarkManager* KBBBookmarkManager::s_bookmarkManager; + +KBBMainWindow::KBBMainWindow( const QCString &initialPackage, + const QCString &initialComponent, + const QCString &initialBug, + QWidget * , const char * name ) + : KMainWindow( 0, name ), mPreferencesDialog( 0 ), mResponseViewer( 0 ), + mBugSourceViewer( 0 ), mPackageSelectDialog( 0 ) +{ + BugSystem::self()->setCurrentServer( KBBPrefs::instance()->mCurrentServer ); + + m_statusLabel = new QLabel( i18n( "Welcome to KBugBuster." ), statusBar() ); + m_statusLabel->setMaximumHeight( statusBar()->fontMetrics().height() + 6 ); + m_statusLabel->setIndent( KDialog::marginHint() / 2 ); + statusBar()->addWidget( m_statusLabel, 1 ); + + m_mainWidget = new CentralWidget( initialPackage, initialComponent, + initialBug, this ); + setCentralWidget( m_mainWidget ); + + initActions(); + + m_progressBar = new QProgressBar( 100, statusBar() ); + m_progressBar->setCenterIndicator( true ); + m_progressBar->setMinimumWidth( 150 ); + m_progressBar->setMaximumHeight( statusBar()->fontMetrics().height() + 6 ); + statusBar()->addWidget( m_progressBar ); + connect( m_mainWidget, SIGNAL( resetProgressBar() ), + m_progressBar, SLOT( reset() ) ); + connect( m_mainWidget, SIGNAL( searchPackage() ), + this, SLOT( searchPackage() ) ); + connect( m_mainWidget, SIGNAL( searchBugNumber() ), + this, SLOT( searchBugNumber() ) ); + + connect( BugSystem::self(), SIGNAL( infoMessage( const QString & ) ), + SLOT( slotStatusMsg( const QString & ) ) ); + + connect( BugSystem::self(), SIGNAL( infoMessage( const QString & ) ), + SLOT( slotStatusMsg( const QString & ) ) ); + connect( BugSystem::self(), SIGNAL( infoPercent( unsigned long ) ), + SLOT( slotSetPercent( unsigned long ) ) ); + + m_mainWidget->readConfig(); +} + +KBBMainWindow::~KBBMainWindow() +{ +// kdDebug() << "KBBMainWindow::~KBBMainWindow()" << endl; + delete m_pBookmarkMenu; + + m_mainWidget->writeConfig(); + + KBBPrefs::instance()->writeConfig(); +} + +void KBBMainWindow::initActions() +{ + // Prepare and create XML GUI + fileQuit = KStdAction::quit( this, + SLOT( close() ), actionCollection() ); + fileQuit->setToolTip( i18n( "Quit KBugBuster" ) ); + + new KAction( i18n("See &Pending Changes"), "contents", 0, this, SLOT( slotListChanges() ), + actionCollection(), "file_seechanges" ); + new KAction( i18n("&Submit Changes"), "mail_send", 0, this, SLOT( slotSubmit() ), + actionCollection(), "file_submit" ); + + reloadpacklist = new KAction( i18n("Reload &Product List"), "reload", CTRL+Qt::Key_F5, m_mainWidget, SLOT( slotReloadPackageList() ), + actionCollection(), "reload_packagelist" ); + reloadpack= new KAction( i18n("Reload Bug &List (for current product)"), "reload", Qt::Key_F5, m_mainWidget, SLOT( slotReloadPackage() ), + actionCollection(), "reload_package" ); + reloadbug = new KAction( i18n("Reload Bug &Details (for current bug)"), "reload", SHIFT+Qt::Key_F5, m_mainWidget, SLOT( slotReloadBug() ), + actionCollection(), "reload_bug" ); + loadMyBugs = new KAction( i18n( "Load &My Bugs List" ), 0, m_mainWidget, SLOT( slotLoadMyBugs() ), + actionCollection(), "load_my_bugs" ); + reloadall = new KAction( i18n("Load All Bug Details (for current product)"), Qt::Key_F6, m_mainWidget, SLOT( slotRetrieveAllBugDetails() ), actionCollection(), "load_allbugs" ); + new KAction( i18n("Extract &Attachments"), "filesave", Qt::Key_F4, m_mainWidget, SLOT( slotExtractAttachments() ), + actionCollection(), "extract_attachments" ); + + new KAction( i18n("Clear Cache"), 0, this, SLOT( clearCache() ), + actionCollection(), "clear_cache" ); + + new KAction( i18n("&Search by Product..."), "goto", CTRL+Qt::Key_P, this, + SLOT( searchPackage() ), actionCollection(), "search_package" ); + new KAction( i18n("Search by Bug &Number..."), "filefind", CTRL+Qt::Key_N, this, + SLOT( searchBugNumber() ), actionCollection(), "search_bugnumber" ); + // For now "Description" searches by title. Maybe later we can have a + // full-text search interfacing bugs.kde.org and rename the current one to "By Title". + new KAction( i18n("Search by &Description...") ,"find", CTRL+Qt::Key_D, this, + SLOT( searchDescription() ), actionCollection(), "search_description" ); + +// new KAction( i18n("&Merge"), "view_remove", CTRL+Qt::Key_M, m_mainWidget, +// SLOT( mergeBugs() ), actionCollection(), "cmd_merge" ); +// new KAction( i18n("&Unmerge"), "view_top_bottom", CTRL+SHIFT+Qt::Key_M, m_mainWidget, +// SLOT( unmergeBugs() ), actionCollection(), "cmd_unmerge" ); + new KAction( i18n("C&lose..."), "edittrash", CTRL+Qt::Key_L, m_mainWidget, + SLOT( closeBug() ), actionCollection(), "cmd_close" ); +// new KAction( i18n("Clos&e Silently"), "edittrash", CTRL+Qt::Key_E, m_mainWidget, +// SLOT( closeBugSilently() ), actionCollection(), "cmd_close_silently" ); + new KAction( i18n("Re&open"), "idea", CTRL+Qt::Key_O, m_mainWidget, + SLOT( reopenBug() ), actionCollection(), "cmd_reopen" ); +// new KAction( i18n("Re&assign..."), "folder_new", CTRL+Qt::Key_A, m_mainWidget, +// SLOT( reassignBug() ), actionCollection(), "cmd_reassign" ); +// new KAction( i18n("Change &Title..."), "text_under", CTRL+Qt::Key_T, m_mainWidget, +// SLOT( titleBug() ), actionCollection(), "cmd_title" ); +// new KAction( i18n("Change &Severity..."), "edit", CTRL+Qt::Key_S, m_mainWidget, +// SLOT( severityBug() ), actionCollection(), "cmd_severity" ); + new KAction( i18n("&Reply..."), "mail_replyall",CTRL+Qt::Key_R , m_mainWidget, + SLOT( replyBug() ), actionCollection(), "cmd_reply" ); + new KAction( i18n("Reply &Privately..."), "mail_reply", CTRL+Qt::Key_I, m_mainWidget, + SLOT( replyPrivateBug() ), actionCollection(), "cmd_replyprivate" ); + + KStdAction::showMenubar(this, SLOT( slotToggleMenubar() ), actionCollection() ); +#if KDE_IS_VERSION( 3, 1, 90 ) + createStandardStatusBarAction(); + setStandardToolBarMenuEnabled(true); +#endif + + m_disconnectedAction = new KToggleAction( i18n("&Disconnected Mode"), 0, + this, + SLOT( slotDisconnectedAction() ), + actionCollection(), + "settings_disconnected" ); + m_disconnectedAction->setChecked( BugSystem::self()->disconnected() ); + + // Bookmarks menu + m_pamBookmarks = new KActionMenu( i18n( "&Bookmarks" ), "bookmark", actionCollection(), "bookmarks" ); + m_pBookmarkMenu = new KBookmarkMenu( KBBBookmarkManager::self(), this, m_pamBookmarks->popupMenu(), actionCollection(), true ); + + KStdAction::preferences( this, SLOT(preferences()), actionCollection() ); + + KToggleAction *toggleTmp = new KToggleAction( i18n("Show Closed Bugs"), "recycled", 0, this, SLOT( slotToggleDone() ), + actionCollection(), "cmd_toggle_done" ); +#if KDE_IS_VERSION( 3, 2, 90 ) + toggleTmp->setCheckedState(i18n("Hide Closed Bugs")); +#endif + toggleTmp->setChecked( KBBPrefs::instance()->mShowClosedBugs ); + + toggleTmp =new KToggleAction( i18n("Show Wishes"), "bookmark", 0, this, SLOT( slotToggleWishes() ), + actionCollection(), "cmd_toggle_wishes" ); +#if KDE_IS_VERSION( 3, 2, 90 ) + toggleTmp->setCheckedState(i18n("Hide Wishes")); +#endif + toggleTmp->setChecked(KBBPrefs::instance()->mShowWishes); + + mSelectServerAction = new KSelectAction( i18n( "Select Server" ), 0, 0, + this, + SLOT( slotSelectServer() ), + actionCollection(), + "select_server" ); + + setupSelectServerAction(); + + if ( KBBPrefs::instance()->mDebugMode ) { + new KAction( i18n("Show Last Server Response..."), 0 , this, + SLOT( showLastResponse() ), actionCollection(), + "debug_lastresponse" ); + new KAction( i18n("Show Bug HTML Source..."), 0 , this, + SLOT( showBugSource() ), actionCollection(), + "debug_showbugsource" ); + } + +#if KDE_IS_VERSION( 3, 2, 90 ) + setupGUI( (ToolBar | Keys | StatusBar | Save | Create ), "kbugbusterui.rc" ); +#else + createGUI(); +#endif +} + +void KBBMainWindow::slotToggleMenubar() +{ + if ( !hasMenuBar() ) + return; + + if ( menuBar()->isVisible() ) + menuBar()->hide(); + else + menuBar()->show(); +} + +void KBBMainWindow::setupSelectServerAction() +{ + QStringList servers; + int current = -1; + QValueList serverList = BugSystem::self()->serverList(); + QValueList::ConstIterator it; + for ( it = serverList.begin(); it != serverList.end(); ++it ) { + QString name = (*it)->serverConfig().name(); + servers.append( name ); + if ( name == KBBPrefs::instance()->mCurrentServer ) { + current = servers.count() - 1; + } + } + mSelectServerAction->setItems( servers ); + if ( current >= 0 ) { + mSelectServerAction->setCurrentItem( current ); + } +} + +QString KBBMainWindow::currentURL() const +{ + QString number=m_mainWidget->currentNumber(); + + if (number.isEmpty()) + return ""; + else + return "bug:"+number; +} + +QString KBBMainWindow::currentTitle() const +{ + return "#"+m_mainWidget->currentNumber()+": "+m_mainWidget->currentTitle(); +} + +void KBBMainWindow::openBookmarkURL( const QString & url ) +{ + if ( url.left(4)=="bug:" ) { + QString bugnumber = url.mid(4); + m_mainWidget->slotRetrieveBugDetails( Bug::fromNumber( bugnumber ) ); + } +} + +// --- SLOT IMPLEMENTATIONS ------------------------------------------------- + +void KBBMainWindow::slotDisconnectedAction() +{ + BugSystem::self()->setDisconnected( m_disconnectedAction->isChecked() ); + + bool enable = !m_disconnectedAction->isChecked(); + + reloadpacklist->setEnabled( enable ); + reloadpacklist->setEnabled( enable ); + reloadpack->setEnabled( enable ); + reloadbug->setEnabled( enable ); + reloadall->setEnabled( enable ); + loadMyBugs->setEnabled( enable ); +} + +void KBBMainWindow::slotStatusMsg( const QString &text ) +{ + // Change status message permanently + m_statusLabel->setText( text ); +} + +void KBBMainWindow::slotSubmit() +{ + BugSystem::self()->sendCommands(); +} + +void KBBMainWindow::slotListChanges() +{ + QStringList list = BugSystem::self()->server()->listCommands(); + + if (list.count() > 0) + { + int ret = KMessageBox::questionYesNoList( this, i18n("List of pending commands:"), + list, QString::null, KStdGuiItem::clear(), KStdGuiItem::close() ); + if ( ret == KMessageBox::Yes ) + { + // Ask for confirmation, it's too easy to click the wrong button in the above dlg box + if ( KMessageBox::warningContinueCancel( this, i18n("Do you really want to delete all commands?"), + i18n("Confirmation Required"), KGuiItem( i18n("&Delete"), "editdelete"), "clearcommands", true) + == KMessageBox::Continue ) + BugSystem::self()->clearCommands(); + } + } + else + { + KMessageBox::information( this, i18n("There are no pending commands.") ); + } +} + +void KBBMainWindow::slotSetPercent( unsigned long percent ) +{ + // KIO::Job::percent() shouldn't return an unsigned long. - Frerich + m_progressBar->setProgress( percent ); +} + +void KBBMainWindow::searchPackage() +{ + if ( !mPackageSelectDialog ) { + mPackageSelectDialog = new PackageSelectDialog( this ); + } + mPackageSelectDialog->setPackages( BugSystem::self()->packageList() ); + BugServerConfig cfg = BugSystem::self()->server()->serverConfig(); + QStringList recent = cfg.recentPackages(); + kdDebug() << "MainWindow RECENT: " << recent.join(",") << endl; + mPackageSelectDialog->setRecentPackages( recent ); + + mPackageSelectDialog->exec(); + Package package = mPackageSelectDialog->selectedPackage(); + + if ( package.isNull() ) { + return; + } + + QString component = mPackageSelectDialog->selectedComponent(); + m_mainWidget->slotRetrieveBugList( package.name(), component ); +} + +void KBBMainWindow::searchBugNumber() +{ + bool ok = false; + QString result = KInputDialog::getText( i18n("Search for Bug Number"), + i18n("Please enter a bug number:"), + QString::null, &ok, this ); + if ( ok ) { + //Strip whitespace and # if needed + result = result.stripWhiteSpace(); + if (result[0]=='#') + result = result.mid(1); + } + + if ( ok && !result.isEmpty()) { + // ### bad way to instantiate a bug! doesn't get us the details! + // Right - but do we need the details in this case ? There's no listview entry here... (DF) + m_mainWidget->slotRetrieveBugDetails( Bug::fromNumber( result ) ); + } +} + +void KBBMainWindow::searchDescription() +{ + kdDebug() << "KBBMainWindow::searchDescription()." << endl; + //KMessageBox::sorry(this,i18n("searchDescription(): to be implemented."),i18n("Not implemented")); + KFindDialog dlg( this ); + if ( dlg.exec() == KDialogBase::Accepted ) + m_mainWidget->searchBugByTitle( dlg.options(), dlg.pattern() ); +} + +bool KBBMainWindow::queryClose() +{ + if ( ! BugSystem::self()->server()->commandsPending() ) return true; + + int result = KMessageBox::warningYesNoCancel(this,i18n("There are unsent bug commands." + " Do you want to send them now?"), QString::null, i18n("Send"), i18n("Do Not Send")); + if ( result == KMessageBox::Cancel ) return false; + if ( result == KMessageBox::Yes ) { + BugSystem::self()->sendCommands(); + } + return true; +} + +void KBBMainWindow::preferences() +{ + if (!mPreferencesDialog) { + mPreferencesDialog = new PreferencesDialog(this); + connect( mPreferencesDialog, SIGNAL( configChanged() ), + SLOT( setupSelectServerAction() ) ); + connect( mPreferencesDialog, SIGNAL( configChanged() ), + m_mainWidget, SLOT( slotReloadPackage() ) ); + } + mPreferencesDialog->show(); + mPreferencesDialog->raise(); +} + +void KBBMainWindow::updatePackage() +{ + m_mainWidget->updatePackage(); +} + +void KBBMainWindow::slotToggleDone() +{ + KBBPrefs::instance()->mShowClosedBugs=!(KBBPrefs::instance()->mShowClosedBugs); + updatePackage(); +} + +void KBBMainWindow::slotToggleWishes() +{ + KBBPrefs::instance()->mShowWishes=!(KBBPrefs::instance()->mShowWishes); + updatePackage(); +} + +void KBBMainWindow::slotSelectServer() +{ + m_mainWidget->writeConfig(); + + QString currentServer = mSelectServerAction->currentText(); + + BugSystem::self()->setCurrentServer( currentServer ); + + m_mainWidget->initialize(); +} + +void KBBMainWindow::showLastResponse() +{ + if ( !mResponseViewer ) { + mResponseViewer = new TextViewer( i18n("Last Server Response"), this ); + } + + mResponseViewer->setText( BugSystem::lastResponse() ); + + mResponseViewer->show(); + mResponseViewer->raise(); +} + +void KBBMainWindow::showBugSource() +{ + if ( !mBugSourceViewer ) { + mBugSourceViewer = new TextViewer( i18n("Bug HTML Source"), this ); + } + + mBugSourceViewer->setText( m_mainWidget->bugDetailsWidget()->source() ); + + mBugSourceViewer->show(); + mBugSourceViewer->raise(); +} + +void KBBMainWindow::clearCache() +{ + BugSystem::self()->server()->cache()->clear(); +} + +#include "kbbmainwindow.moc" + +/* vim: set et ts=4 sw=4 softtabstop=4: */ + diff --git a/kbugbuster/gui/kbbmainwindow.h b/kbugbuster/gui/kbbmainwindow.h new file mode 100644 index 00000000..f139c733 --- /dev/null +++ b/kbugbuster/gui/kbbmainwindow.h @@ -0,0 +1,144 @@ +/* + kbbmainwindow.h - KBugBuster's main window + + Copyright (c) 2001-2004 by Martijn Klingens + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef KBBMAINWINDOW_H +#define KBBMAINWINDOW_H + +#include +#include +#include +#include + +#include "package.h" +#include "bug.h" +#include "bugdetails.h" + +class KAction; +class KActionMenu; +class KBookmarkMenu; +class KToggleAction; +class KSelectAction; +class QLabel; +class QListViewItem; +class QProgressBar; +class PreferencesDialog; +class TextViewer; +class PackageSelectDialog; + +namespace KBugBusterMainWindow +{ + class CentralWidget; +} + +/** + * @author Martijn Klingens + */ +class KBBMainWindow : public KMainWindow, virtual public KBookmarkOwner +{ + Q_OBJECT + public: + /** + * construtor of KBugBusterApp, calls all init functions to create the application. + */ + KBBMainWindow( const QCString &initialPackage = "", + const QCString &initialCpomponent = "", + const QCString &initialBug = "", + QWidget* parent = 0, const char* name = 0 ); + ~KBBMainWindow(); + + /// Overloaded functions of KBookmarkOwner + virtual void openBookmarkURL( const QString & _url ); + virtual QString currentTitle() const; + virtual QString currentURL() const; + + public slots: + /** + * Event handlers for our KActions + */ + void slotStatusMsg( const QString &text ); + void slotDisconnectedAction(); + void slotSubmit(); + void slotListChanges(); + void slotSetPercent( unsigned long percent ); + void slotSelectServer(); + + void showLastResponse(); + void showBugSource(); + + void clearCache(); + + /** + * Other event handlers + */ + + void searchPackage(); + void searchBugNumber(); + void searchDescription(); + + void preferences(); + void updatePackage(); + void slotToggleDone(); + void slotToggleWishes(); + + protected: + virtual bool queryClose(); + + protected slots: + void setupSelectServerAction(); + void slotToggleMenubar(); + + private: + void initActions(); + + /** + * Our main widget + */ + KBugBusterMainWindow::CentralWidget *m_mainWidget; + + /** + * Used KActions + */ + KAction *fileQuit; + KAction *reloadpacklist; + KAction *reloadpack; + KAction *reloadbug; + KAction *reloadall; + KAction *loadMyBugs; + KToggleAction *m_disconnectedAction; + + /** + * Status bar label. We need this, because the default Qt version doesn't + * support rich text in the messages + */ + QLabel *m_statusLabel; + QProgressBar *m_progressBar; + + PreferencesDialog *mPreferencesDialog; + + KActionMenu *m_pamBookmarks; + KBookmarkMenu* m_pBookmarkMenu; + + KSelectAction *mSelectServerAction; + + TextViewer *mResponseViewer; + TextViewer *mBugSourceViewer; + + PackageSelectDialog *mPackageSelectDialog; +}; + +#endif + +/* vim: set et ts=4 softtabstop=4 sw=4: */ + diff --git a/kbugbuster/gui/kbugbusterui.rc b/kbugbuster/gui/kbugbusterui.rc new file mode 100644 index 00000000..f347855c --- /dev/null +++ b/kbugbuster/gui/kbugbusterui.rc @@ -0,0 +1,78 @@ + + + + + &File + + + + + + + &View + + + + + + + + + + + + + + + S&earch + + + + + + &Commands + + + + + + + + + + + + &Settings + + + + + +Search Toolbar + + + + + +Command Toolbar + + + + + + + + + + + + + +Settings Toolbar + + + + + diff --git a/kbugbuster/gui/loadallbugsdlg.cpp b/kbugbuster/gui/loadallbugsdlg.cpp new file mode 100644 index 00000000..40ecd6d8 --- /dev/null +++ b/kbugbuster/gui/loadallbugsdlg.cpp @@ -0,0 +1,92 @@ +/*************************************************************************** + loadallbugsdlg.cpp - progress dialog while loading all bugs for a package + ------------------- + copyright : (C) 2002 by David Faure + email : david@mandrakesoft.com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation version 2. * + * * + ***************************************************************************/ + +#include "loadallbugsdlg.h" +#include "bugsystem.h" +#include "bugcache.h" +#include +#include +#include + +LoadAllBugsDlg::LoadAllBugsDlg( const Package& pkg, const QString &component ) + : QDialog( 0L, "progressdlg", TRUE ) +{ + m_bugLoadingProgress = new KIO::DefaultProgress( this ); + connect( m_bugLoadingProgress, SIGNAL( stopped() ), + this, SLOT( slotStopped() ) ); + setCaption( i18n( "Loading All Bugs for Product %1" ).arg( pkg.name() ) ); + connect( BugSystem::self(), + SIGNAL( bugDetailsAvailable( const Bug &, const BugDetails & ) ), + SLOT( slotBugDetailsAvailable( const Bug &, const BugDetails & ) ) ); + connect( BugSystem::self(), + SIGNAL( bugDetailsLoadingError() ), + SLOT( slotBugDetailsLoadingError() ) ); + // The package (and its buglist) has to be in the cache already... + m_bugs = BugSystem::self()->cache()->loadBugList( pkg, component, true ); + m_count = m_bugs.count(); + m_bugLoadingProgress->slotTotalSize( 0, m_count ); + kdDebug() << "LoadAllBugsDlg: " << m_count << " bugs to load" << endl; + m_processed = 0; + QTimer::singleShot( 0, this, SLOT( loadNextBug() ) ); +} + +void LoadAllBugsDlg::slotBugDetailsAvailable( const Bug &bug, const BugDetails & ) +{ + kdDebug() << "LoadAllBugsDlg::slotBugDetailsAvailable " << bug.number() << endl; + m_bugLoadingProgress->slotInfoMessage( 0L, i18n( "Bug %1 loaded" ).arg(bug.number()) ); + loadNextBug(); +} + +void LoadAllBugsDlg::slotBugDetailsLoadingError() +{ + // Abort at the first error. Otherwise we get spammed with "no host bugs.kde.org" msg boxes.... + reject(); +} + +void LoadAllBugsDlg::loadNextBug() +{ + kdDebug() << "LoadAllBugsDlg::loadNextBug" << endl; + if ( m_bugs.isEmpty() ) + { + kdDebug() << "LoadAllBugsDlg::loadNextBug DONE!" << endl; + accept(); + } else { + BugCache* cache = BugSystem::self()->cache(); + Bug bug; + do { + bug = m_bugs.front(); + m_bugs.pop_front(); + m_processed++; + m_bugLoadingProgress->slotPercent( 0L, (100 * m_processed) / m_count ); + kdDebug() << "looking at bug " << bug.number() << " in cache:" << cache->hasBugDetails( bug ) << endl; + } while ( cache->hasBugDetails( bug ) && !m_bugs.isEmpty() ); + if ( !cache->hasBugDetails( bug ) ) { + kdDebug() << "LoadAllBugsDlg::loadNextBug loading bug " << bug.number() << endl; + BugSystem::self()->retrieveBugDetails( bug ); + } else { + kdDebug() << "LoadAllBugsDlg::loadNextBug DONE!" << endl; + accept(); + } + } +} + +void LoadAllBugsDlg::slotStopped() +{ + kdDebug() << "LoadAllBugsDlg::slotStopped" << endl; + // TODO abort job? + reject(); +} + +#include "loadallbugsdlg.moc" diff --git a/kbugbuster/gui/loadallbugsdlg.h b/kbugbuster/gui/loadallbugsdlg.h new file mode 100644 index 00000000..685abcb0 --- /dev/null +++ b/kbugbuster/gui/loadallbugsdlg.h @@ -0,0 +1,43 @@ +/*************************************************************************** + loadallbugsdlg.h - progress dialog while loading all bugs for a package + ------------------- + copyright : (C) 2002 by David Faure + email : david@mandrakesoft.com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation version 2. * + * * + ***************************************************************************/ +#ifndef loadallbugsdlg_h +#define loadallbugsdlg_h + +#include +#include "bug.h" +class Package; +class BugDetails; + +namespace KIO { class DefaultProgress; } + +class LoadAllBugsDlg : public QDialog +{ + Q_OBJECT +public: + LoadAllBugsDlg( const Package& pkg, const QString &component ); + +protected slots: + void slotBugDetailsAvailable( const Bug &bug, const BugDetails &bd ); + void slotBugDetailsLoadingError(); + void slotStopped(); + void loadNextBug(); +private: + Bug::List m_bugs; + unsigned int m_processed; + unsigned int m_count; + KIO::DefaultProgress* m_bugLoadingProgress; +}; + +#endif diff --git a/kbugbuster/gui/messageeditor.cpp b/kbugbuster/gui/messageeditor.cpp new file mode 100644 index 00000000..517ef80b --- /dev/null +++ b/kbugbuster/gui/messageeditor.cpp @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "kbbprefs.h" + +#include "messageeditor.h" +#include +#include "messageeditor.moc" + +MessageEditor::MessageEditor( QWidget *parent ) + : KDialogBase(Plain,i18n("Edit Message Buttons"),Ok|Cancel,Ok,parent,0, + true,true) +{ + QFrame *topFrame = plainPage(); + QBoxLayout *topLayout = new QVBoxLayout(topFrame,0,spacingHint()); + + QBoxLayout *selectionLayout = new QHBoxLayout; + topLayout->addLayout(selectionLayout); + + QLabel *selectionLabel = new QLabel(i18n("Button:"),topFrame); + selectionLayout->addWidget(selectionLabel); + + mSelectionCombo = new QComboBox(topFrame); + selectionLayout->addWidget(mSelectionCombo); + connect(mSelectionCombo,SIGNAL(activated(int)),SLOT(changeMessage())); + + QPushButton *addButton = new QPushButton(i18n("Add Button..."),topFrame); + selectionLayout->addWidget(addButton); + connect(addButton,SIGNAL(clicked()),SLOT(addButton())); + + QPushButton *removeButton = new QPushButton(i18n("Remove Button"),topFrame); + selectionLayout->addWidget(removeButton); + connect(removeButton,SIGNAL(clicked()),SLOT(removeButton())); + + mMessageEdit = new KTextEdit(topFrame); + topLayout->addWidget(mMessageEdit,1); + + updateConfig(); +} + +void MessageEditor::updateConfig() +{ + mMessageButtons = KBBPrefs::instance()->mMessageButtons; + + mSelectionCombo->clear(); + + QMap::ConstIterator it; + for(it = mMessageButtons.begin();it != mMessageButtons.end();++it) { + mSelectionCombo->insertItem(it.key()); + } + + updateMessage(); +} + +void MessageEditor::addButton() +{ + QString txt; + txt = KInputDialog::getText(i18n("Add Message Button"), + i18n("Enter button name:"), QString::null, + NULL, this ); + + if ( !txt.isNull() ) { + saveMessage(); + mSelectionCombo->insertItem(txt); + mMessageButtons.insert(txt,""); + mSelectionCombo->setCurrentItem(mSelectionCombo->count()-1); + updateMessage(); + } + +} + +void MessageEditor::removeButton() +{ + int result = KMessageBox::warningContinueCancel(this, + i18n("Remove the button %1?").arg(mSelectionCombo->currentText()), + i18n("Remove"), KGuiItem( i18n("Delete"), "editdelete") ); + + if (result == KMessageBox::Continue) { + mMessageButtons.remove(mSelectionCombo->currentText()); + mSelectionCombo->removeItem(mSelectionCombo->currentItem()); + mSelectionCombo->setCurrentItem(0); + updateMessage(); + } +} + +void MessageEditor::changeMessage() +{ + saveMessage(); + updateMessage(); +} + +void MessageEditor::updateMessage() +{ + mCurrentButton = mSelectionCombo->currentText(); + + mMessageEdit->setText(mMessageButtons[mCurrentButton]); +} + +void MessageEditor::saveMessage() +{ + mMessageButtons.replace(mCurrentButton,mMessageEdit->text()); +} + +void MessageEditor::slotOk() +{ + saveMessage(); + + KBBPrefs::instance()->mMessageButtons = mMessageButtons; + accept(); +} diff --git a/kbugbuster/gui/messageeditor.h b/kbugbuster/gui/messageeditor.h new file mode 100644 index 00000000..e32e8cec --- /dev/null +++ b/kbugbuster/gui/messageeditor.h @@ -0,0 +1,33 @@ +#ifndef MESSAGEEDITOR_H +#define MESSAGEEDITOR_H + +#include + +class QComboBox; +class KTextEdit; + +class MessageEditor : public KDialogBase { + Q_OBJECT + public: + MessageEditor( QWidget *parent ); + + protected slots: + void slotOk(); + + private slots: + void addButton(); + void removeButton(); + void changeMessage(); + void saveMessage(); + void updateMessage(); + void updateConfig(); + + private: + QComboBox *mSelectionCombo; + KTextEdit *mMessageEdit; + + QString mCurrentButton; + QMap mMessageButtons; +}; + +#endif diff --git a/kbugbuster/gui/msginputdialog.cpp b/kbugbuster/gui/msginputdialog.cpp new file mode 100644 index 00000000..a3fc39c7 --- /dev/null +++ b/kbugbuster/gui/msginputdialog.cpp @@ -0,0 +1,226 @@ +// $Id$ +// (c) 2001, Cornelius Schumacher + +#include +#include + +#include +#include +#include +#include +#include + +#include "messageeditor.h" +#include "kbbprefs.h" +#include "bugsystem.h" +#include "bugcommand.h" + +#include "msginputdialog.h" +#include "msginputdialog.moc" + +MsgInputDialog::MsgInputDialog(MsgInputDialog::MessageType type, const Bug &bug, + const Package &package, const QString "edMsg, + QWidget *parent) + : KDialogBase(Plain,QString::null,User1|User2|Ok|Cancel,Ok,parent,0,false, + true,KStdGuiItem::clear(),i18n( "&Edit Presets..." )), + mBug( bug ), + mPackage( package ), + mType( type ) +{ + switch ( mType ) { + case Close: + setCaption( i18n("Close Bug %1").arg( mBug.number() ) ); + break; + case Reply: + setCaption( i18n("Reply to Bug") ); + break; + case ReplyPrivate: + setCaption( i18n("Reply Privately to Bug") ); + break; + default: + break; + } + + QFrame *topFrame = plainPage(); + ( new QHBoxLayout( topFrame ) )->setAutoAdd( true ); + + mSplitter = new QSplitter( QSplitter::Horizontal, topFrame ); + + QWidget *w = new QWidget( mSplitter ); + ( new QVBoxLayout( w, spacingHint(), -1 ) )->setAutoAdd( true ); + + if ( mType == Reply ) { + QWidget *r = new QWidget( w ); + QHBoxLayout* rlayout = new QHBoxLayout( r ); + + QLabel *rlabel = new QLabel( i18n("&Recipient:"),r ); + QFont f = r->font(); + f.setBold( true ); + r->setFont( f ); + rlayout->add( rlabel ); + + mRecipient = new QComboBox( r ); + mRecipient->insertItem( i18n("Normal (bugs.kde.org & Maintainer & kde-bugs-dist)"), BugCommand::Normal ); + mRecipient->insertItem( i18n("Maintonly (bugs.kde.org & Maintainer)"), BugCommand::Maintonly ); + mRecipient->insertItem( i18n("Quiet (bugs.kde.org only)"), BugCommand::Quiet ); + rlabel->setBuddy( mRecipient ); + rlayout->add( mRecipient ); + + QSpacerItem *rspacer= new QSpacerItem( 1,1,QSizePolicy::Expanding ); + rlayout->addItem( rspacer ); + + // Reply currently only replies to the bug tracking system + r->hide(); + } + + + QLabel *l = new QLabel( i18n( "&Message" ), w ); + QFont f = l->font(); + f.setBold( true ); + l->setFont( f ); + + mMessageEdit = new KTextEdit( w ); + mMessageEdit->setMinimumWidth( mMessageEdit->fontMetrics().width('x') * 72 ); + mMessageEdit->setWordWrap( QTextEdit::FixedColumnWidth ); + mMessageEdit->setWrapColumnOrWidth( 72 ); + l->setBuddy( mMessageEdit ); + + w = new QWidget( mSplitter ); + ( new QVBoxLayout( w, spacingHint(), -1 ) )->setAutoAdd( true ); + l = new QLabel( i18n( "&Preset Messages" ), w ); + l->setFont( f ); + + mPresets = new KListBox( w ); + updatePresets(); + l->setBuddy( mPresets ); + + connect( mPresets, SIGNAL( executed( QListBoxItem* ) ), + SLOT( slotPresetSelected( QListBoxItem * ) ) ); + connect( this, SIGNAL( user2Clicked() ), SLOT( editPresets() ) ); + connect( this, SIGNAL( user1Clicked() ), SLOT( clearMessage() ) ); + mMessageEdit->setFocus(); + + if ( !quotedMsg.isEmpty() ) + insertQuotedMessage( quotedMsg ); + + readConfig(); +} + +MsgInputDialog::~MsgInputDialog() +{ + kdDebug() << "MsgInputDialog::~MsgInputDialog()" << endl; + writeConfig(); +} + +void MsgInputDialog::readConfig() +{ + resize( KBBPrefs::instance()->mMsgDlgWidth, + KBBPrefs::instance()->mMsgDlgHeight ); + QValueList sizes = KBBPrefs::instance()->mMsgDlgSplitter; + mSplitter->setSizes( sizes ); +} + +void MsgInputDialog::writeConfig() +{ + KBBPrefs::instance()->mMsgDlgWidth = width(); + KBBPrefs::instance()->mMsgDlgHeight = height(); + KBBPrefs::instance()->mMsgDlgSplitter = mSplitter->sizes(); +} + +void MsgInputDialog::updatePresets() +{ + mPresets->clear(); + + QMap messageButtons = KBBPrefs::instance()->mMessageButtons; + + int id = 0; + QMap::ConstIterator it; + for( it = messageButtons.begin(); it != messageButtons.end(); ++it ) + mPresets->insertItem( it.key(), id ); +} + +QString MsgInputDialog::message() const +{ + return mMessageEdit->text(); +} + +void MsgInputDialog::editPresets() +{ + MessageEditor *dlg = new MessageEditor(this); + dlg->exec(); + delete dlg; + + updatePresets(); +} + +void MsgInputDialog::slotPresetSelected( QListBoxItem *lbi ) +{ + mMessageEdit->setText( KBBPrefs::instance()->mMessageButtons[ lbi->text() ] ); +} + +void MsgInputDialog::clearMessage() +{ + mMessageEdit->setText(""); +} + +void MsgInputDialog::queueCommand() +{ + switch ( mType ) { + case Close: + BugSystem::self()->queueCommand( + new BugCommandClose( mBug, message(), mPackage ) ); + break; + case Reply: + BugSystem::self()->queueCommand( + new BugCommandReply( mBug, message(), mRecipient->currentItem() ) ); + break; + case ReplyPrivate: + BugSystem::self()->queueCommand( + new BugCommandReplyPrivate( mBug, mBug.submitter().email, + message() ) ); + break; + default: + break; + } +} + +void MsgInputDialog::slotOk() +{ + queueCommand(); + delete this; +} + +void MsgInputDialog::slotCancel() +{ + delete this; +} + +void MsgInputDialog::insertQuotedMessage( const QString &msg ) +{ + Q_ASSERT( mMessageEdit->wordWrap() == QTextEdit::FixedColumnWidth ); + + const QString quotationMarker = "> "; + const unsigned int wrapColumn = mMessageEdit->wrapColumnOrWidth(); + + // ### Needs something more sophisticated than simplifyWhiteSpace to + // handle quoting multiple paragraphs properly. + QString line = msg.simplifyWhiteSpace(); + + QString quotedMsg; + while ( line.length() + quotationMarker.length() + 1 > wrapColumn ) { + int pos = wrapColumn - quotationMarker.length() - 1; + while ( pos > 0 && !line[ pos ].isSpace() ) + --pos; + if ( pos == 0 ) + pos = wrapColumn; + quotedMsg += quotationMarker + line.left( pos ) + "\n"; + line = line.mid( pos + 1 ); + } + quotedMsg += quotationMarker + line + "\n\n"; + + mMessageEdit->setText( quotedMsg ); + + const int lastPara = mMessageEdit->paragraphs() - 1; + const int lastParaLen = mMessageEdit->paragraphLength( lastPara ) - 1; + mMessageEdit->setCursorPosition( lastPara, lastParaLen ); +} diff --git a/kbugbuster/gui/msginputdialog.h b/kbugbuster/gui/msginputdialog.h new file mode 100644 index 00000000..9de767e3 --- /dev/null +++ b/kbugbuster/gui/msginputdialog.h @@ -0,0 +1,55 @@ +#ifndef MSGINPUTDIALOG_H +#define MSGINPUTDIALOG_H + +#include + +#include "bug.h" +#include "package.h" + +class KTextEdit; +class QSplitter; +class KListBox; + +class MsgInputDialog : public KDialogBase +{ + Q_OBJECT + public: + enum MessageType{ Close, Reply, ReplyPrivate }; + + MsgInputDialog( MessageType, const Bug &, const Package &, + const QString &, QWidget *parent=0); + virtual ~MsgInputDialog(); + + QString message() const; + + protected slots: + void slotOk(); + void slotCancel(); + + private slots: + void editPresets(); + void updatePresets(); + void slotPresetSelected( QListBoxItem * ); + void clearMessage(); + void queueCommand(); + + private: + void createButtons(); + void createLayout(); + + void readConfig(); + void writeConfig(); + + void insertQuotedMessage( const QString "edMsg ); + + QComboBox *mRecipient; + KTextEdit *mMessageEdit; + QSplitter *mSplitter; + KListBox *mPresets; + + Bug mBug; + Package mPackage; + MessageType mType; +}; + +#endif diff --git a/kbugbuster/gui/packagelvi.cpp b/kbugbuster/gui/packagelvi.cpp new file mode 100644 index 00000000..7fe7cfe6 --- /dev/null +++ b/kbugbuster/gui/packagelvi.cpp @@ -0,0 +1,38 @@ +/* + packagelvi.cpp - Custom QListViewItem that holds a Package object + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.org + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "packagelvi.h" + +PackageLVI::PackageLVI( QListView *parent , const Package &pkg, const QString &component ) +: QListViewItem( parent, pkg.name(), pkg.description() ) +{ + m_package = pkg; + m_component = component; +} + +PackageLVI::PackageLVI( QListViewItem *parent , const Package &pkg, const QString &component ) +: QListViewItem( parent, component ) +{ + m_package = pkg; + m_component = component; +} + +PackageLVI::~PackageLVI() +{ +} + +/* vim: set et ts=4 sw=4 softtabstop=4: */ + diff --git a/kbugbuster/gui/packagelvi.h b/kbugbuster/gui/packagelvi.h new file mode 100644 index 00000000..32f48642 --- /dev/null +++ b/kbugbuster/gui/packagelvi.h @@ -0,0 +1,51 @@ +/* + packagelvi.h - Custom QListViewItem that holds a Package object + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.org + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef PACKAGELVI_H +#define PACKAGELVI_H + +#include + +#include "package.h" + +/** + * @author Martijn Klingens + */ +class PackageLVI : public QListViewItem +{ +public: + // Top-level package + PackageLVI( QListView *parent , const Package &pkg, const QString &component ); + // Child component + PackageLVI( QListViewItem *parent , const Package &pkg, const QString &component ); + + ~PackageLVI(); + + Package& package() { return m_package; } + void setPackage( const Package &pkg ) { m_package = pkg; } + + QString component() { return m_component; } + void setComponent( const QString &component ) { m_component = component; } + +private: + Package m_package; + QString m_component; +}; + +#endif // PACKAGELVI_H + +/* vim: set et ts=4 softtabstop=4 sw=4: */ + diff --git a/kbugbuster/gui/packageselectdialog.cpp b/kbugbuster/gui/packageselectdialog.cpp new file mode 100644 index 00000000..7b791e9d --- /dev/null +++ b/kbugbuster/gui/packageselectdialog.cpp @@ -0,0 +1,214 @@ +#include +#include +#include + +#include +#include +#include + +#include "bugsystem.h" +#include "kbbprefs.h" +#include "bugserver.h" + +#include "packagelvi.h" +#include "packageselectdialog.h" +#include "packageselectdialog.moc" + +PackageListView::PackageListView( QWidget *parent ) : + QListView( parent ) +{ + setFocusPolicy( QWidget::StrongFocus ); +} + +void PackageListView::resetTyped() +{ + mTyped = ""; +} + +void PackageListView::keyPressEvent( QKeyEvent *e ) +{ + // Disable listview text completion for now + QListView::keyPressEvent( e ); + return; + + int k = e->key(); + if ( k == Key_Return || k == Key_Escape ) e->ignore(); + + QString key = e->text(); + mTyped.append(key); + emit typed( mTyped ); +} + +PackageSelectDialog::PackageSelectDialog(QWidget *parent,const char *name) : + KDialogBase( parent, name, true, i18n("Select Product"), Ok|Cancel ) +{ + QWidget *topWidget = new QWidget( this ); + setMainWidget( topWidget ); + + QBoxLayout *topLayout = new QVBoxLayout( topWidget ); + QSplitter *topSplitter = new QSplitter( QSplitter::Vertical, topWidget ); + topSplitter->setOpaqueResize( true ); + + topLayout->addWidget( topSplitter ); + + mRecentList = new QListView( topSplitter ); + mRecentList->addColumn( i18n("Recent") ); + mRecentList->resize( mRecentList->width(), mRecentList->fontMetrics().height() * + KBBPrefs::instance()->mRecentPackagesCount ); + + connect( mRecentList, SIGNAL( mouseButtonPressed( int, QListViewItem *, const QPoint &, int) ), + SLOT( recentSelected( int, QListViewItem * ) ) ); + connect( mRecentList, SIGNAL( doubleClicked( QListViewItem * ) ), + SLOT( slotOk() ) ); + + mCompletion = new KCompletion; + mCompletion->setCompletionMode( KGlobalSettings::CompletionAuto ); + + mCompleteList = new PackageListView( topSplitter ); + mCompleteList->addColumn( i18n("Name") ); + mCompleteList->addColumn( i18n("Description") ); + mCompleteList->setRootIsDecorated(true); + mCompleteList->setAllColumnsShowFocus( true ); + connect( mCompleteList, SIGNAL( typed( const QString & ) ), + SLOT( completeTyped( const QString & ) ) ); + + + connect( mCompleteList, SIGNAL( mouseButtonPressed( int, QListViewItem *, const QPoint &, int) ), + SLOT( completeSelected( int, QListViewItem * ) ) ); + connect( mCompleteList, SIGNAL( doubleClicked( QListViewItem * ) ), + SLOT( slotOk() ) ); + + mPackageEdit = new KLineEdit( topWidget ); + mPackageEdit->setFocus(); + + topLayout->addWidget( mPackageEdit ); + connect( mPackageEdit, SIGNAL( textChanged( const QString & ) ), + SLOT( completeTyped( const QString & ) ) ); + enableButtonOK( !mPackageEdit->text().isEmpty() ); +} + +PackageSelectDialog::~PackageSelectDialog() +{ + delete mCompletion; +} + +void PackageSelectDialog::setRecentPackages( const QStringList &recent ) +{ + mRecentList->clear(); + QStringList::ConstIterator it; + for( it = recent.begin(); it != recent.end(); ++it ) { + new QListViewItem( mRecentList, *it ); + } +} + +void PackageSelectDialog::setPackages( const Package::List &pkgs ) +{ + mCompleteList->clear(); + mCompletion->clear(); + mCompletionDict.clear(); + Package::List::ConstIterator it; + for( it = pkgs.begin(); it != pkgs.end(); ++it ) { + PackageLVI *item = new PackageLVI( mCompleteList, (*it), QString::null ); + QStringList components = (*it).components(); + + if (components.count() > 1) { + for( QStringList::ConstIterator cit = components.begin(); cit != components.end(); ++cit ) { + PackageLVI *component = new PackageLVI( item, (*it), (*cit) ); + QString completionName = (*it).name() + "/" + (*cit); + + mCompletion->addItem( completionName ); + mCompletionDict.insert( completionName, component ); + } + } + + mCompletion->addItem( (*it).name() ); + mCompletionDict.insert((*it).name(),item); + } +} + +void PackageSelectDialog::recentSelected( int, QListViewItem *item ) +{ + kdDebug() << "PackageSelectDialog::recentSelected()" << endl; + if ( item ) { + mCompleteList->clearSelection(); + // Why does a QLineEdit->setText() call emit the textChanged() signal? + mPackageEdit->blockSignals( true ); + mPackageEdit->setText( item->text( 0 ) ); + enableButtonOK(true); + mPackageEdit->blockSignals( false ); + } +} + +void PackageSelectDialog::completeSelected( int, QListViewItem *item ) +{ + PackageLVI *lvi = dynamic_cast(item); + + if ( lvi ) { + mRecentList->clearSelection(); + if ( lvi->component().isEmpty() ) { + mPackageEdit->setText( lvi->package().name() ); + } + else { + mPackageEdit->setText( lvi->package().name() + "/" + lvi->component() ); + } + } +} + +void PackageSelectDialog::slotOk() +{ + PackageLVI *item = (PackageLVI *)mCompleteList->selectedItem(); + if ( item ) { + mSelectedPackage = item->package(); + mSelectedComponent = item->component(); + + QString recent_key; + if ( item->component().isEmpty() ) + recent_key = item->package().name(); + else + recent_key = item->package().name() + "/" + item->component(); + + BugServer *server = BugSystem::self()->server(); + QStringList recent = server->serverConfig().recentPackages(); + if( !recent.contains( recent_key ) ) { + recent.prepend( recent_key ); + if ( int( recent.count() ) > KBBPrefs::instance()->mRecentPackagesCount ) { + recent.remove( recent.last() ); + } + kdDebug() << "RECENT: " << recent.join(",") << endl; + server->serverConfig().setRecentPackages( recent ); + } + } else { + QListViewItem *recentItem = mRecentList->selectedItem(); + if ( recentItem ) { + QStringList tokens = QStringList::split( '/', recentItem->text( 0 ) ); + mSelectedPackage = BugSystem::self()->package( tokens[0] ); + mSelectedComponent = tokens[1]; + } + } + mCompleteList->resetTyped(); + accept(); +} + +Package PackageSelectDialog::selectedPackage() +{ + return mSelectedPackage; +} + +QString PackageSelectDialog::selectedComponent() +{ + return mSelectedComponent; +} + +void PackageSelectDialog::completeTyped( const QString &typed ) +{ + kdDebug() << "completeTyped: " << typed << endl; + QString completed = mCompletion->makeCompletion( typed ); + kdDebug() << "completed: " << completed << endl; + if ( !completed.isEmpty() ) { + mCompleteList->setSelected( mCompletionDict[ completed ], true ); + mCompleteList->ensureItemVisible( mCompletionDict[ completed ] ); + } else { + mCompleteList->resetTyped(); + } + enableButtonOK( !typed.isEmpty() ); +} diff --git a/kbugbuster/gui/packageselectdialog.h b/kbugbuster/gui/packageselectdialog.h new file mode 100644 index 00000000..1fe596aa --- /dev/null +++ b/kbugbuster/gui/packageselectdialog.h @@ -0,0 +1,64 @@ +#ifndef PACKAGESELECTDIALOG_H +#define PACKAGESELECTDIALOG_H + +#include + +#include + +#include "package.h" + +class KCompletion; +class KLineEdit; + +class PackageListView : public QListView +{ + Q_OBJECT + public: + PackageListView( QWidget *parent ); + + void resetTyped(); + + signals: + void typed( const QString & ); + + protected: + void keyPressEvent( QKeyEvent *e ); + + private: + QString mTyped; +}; + +class PackageSelectDialog : public KDialogBase +{ + Q_OBJECT + public: + PackageSelectDialog(QWidget *parent=0,const char *name=0); + ~PackageSelectDialog(); + + void setRecentPackages( const QStringList & ); + void setPackages( const Package::List &pkgs ); + + Package selectedPackage(); + QString selectedComponent(); + + protected slots: + void slotOk(); + + private slots: + void recentSelected( int, QListViewItem * ); + void completeSelected( int, QListViewItem * ); + void completeTyped( const QString & ); + + private: + Package::List mPackages; + Package mSelectedPackage; + QString mSelectedComponent; + + QListView *mRecentList; + PackageListView *mCompleteList; + KLineEdit *mPackageEdit; + KCompletion *mCompletion; + QDict mCompletionDict; +}; + +#endif diff --git a/kbugbuster/gui/preferencesdialog.cpp b/kbugbuster/gui/preferencesdialog.cpp new file mode 100644 index 00000000..9cafff28 --- /dev/null +++ b/kbugbuster/gui/preferencesdialog.cpp @@ -0,0 +1,306 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "mailsender.h" +#include "kbbprefs.h" +#include "kbbmainwindow.h" +#include "serverconfigdialog.h" +#include "bugsystem.h" +#include "bugserver.h" +#include "bugserverconfig.h" + +#include "preferencesdialog.h" + +class ServerItem : public QListViewItem +{ + public: + ServerItem( QListView *listView, const BugServerConfig &cfg ) + : QListViewItem( listView ) + { + setServerConfig( cfg ); + } + + void setServerConfig( const BugServerConfig &cfg ) + { + mServerConfig = cfg; + setText( 0, cfg.name() ); + setText( 1, cfg.baseUrl().prettyURL() ); + setText( 2, cfg.user() ); + setText( 3, cfg.bugzillaVersion() ); + } + + const BugServerConfig &serverConfig() const { return mServerConfig; } + + private: + BugServerConfig mServerConfig; +}; + +class ServerListView : public QListView +{ + public: + ServerListView( QWidget *parent ) : QListView( parent ) + { + addColumn( i18n("Name") ); + addColumn( i18n("Base URL") ); + addColumn( i18n("User") ); + addColumn( i18n("Version") ); + } +}; + +PreferencesDialog::PreferencesDialog( QWidget* parent, const char* name ) + : KDialogBase ( IconList, i18n("Preferences"), Ok|Apply|Cancel, Ok, + parent, name, false, true ) +{ + setupServerPage(); + setupAdvancedPage(); + + readConfig(); +} + +PreferencesDialog::~PreferencesDialog() +{ +} + +void PreferencesDialog::setupServerPage() +{ + QFrame *topFrame = addPage( i18n("Servers"), 0, + DesktopIcon( "gohome", KIcon::SizeMedium ) ); + + QBoxLayout *layout = new QVBoxLayout( topFrame ); + layout->setSpacing( spacingHint() ); + + mServerList = new ServerListView( topFrame ); + layout->addWidget( mServerList ); + + QHBox *buttonBox = new QHBox( topFrame ); + buttonBox->setSpacing( spacingHint() ); + layout->addWidget( buttonBox ); + + QPushButton *addButton = new QPushButton( i18n("Add Server..."), buttonBox ); + connect( addButton, SIGNAL( clicked() ), SLOT( addServer() ) ); + + QPushButton *editButton = new QPushButton( i18n("Edit Server..."), buttonBox ); + connect( editButton, SIGNAL( clicked() ), SLOT( editServer() ) ); + + QPushButton *removeButton = new QPushButton( i18n("Delete Server"), buttonBox ); + connect( removeButton, SIGNAL( clicked() ), SLOT( removeServer() ) ); + + QPushButton *button = new QPushButton( i18n("Select Server From List..."), + topFrame ); + layout->addWidget( button ); + connect( button, SIGNAL( clicked() ), SLOT( selectServer() ) ); + connect( mServerList, SIGNAL( doubleClicked ( QListViewItem *)), this, SLOT( editServer())); +} + +void PreferencesDialog::setupAdvancedPage() +{ + QFrame *topFrame = addPage( i18n("Advanced"), 0, + DesktopIcon( "misc", KIcon::SizeMedium ) ); + + QBoxLayout *layout = new QVBoxLayout( topFrame ); + layout->setSpacing( spacingHint() ); + + QButtonGroup *mailGroup = new QButtonGroup( 1, Horizontal, + i18n( "Mail Client" ), topFrame ); + layout->addWidget( mailGroup ); + + mKMailButton = new QRadioButton( i18n( "&KMail" ), mailGroup ); + mDirectButton = new QRadioButton( i18n( "D&irect" ), mailGroup ); + mSendmailButton = new QRadioButton( i18n( "&Sendmail" ), mailGroup ); + + mShowClosedCheckBox = new QCheckBox( i18n( "Show closed bugs" ), topFrame ); + layout->addWidget( mShowClosedCheckBox ); + + mShowWishesCheckBox = new QCheckBox( i18n( "Show wishes" ), topFrame ); + layout->addWidget( mShowWishesCheckBox ); + + mShowVotedCheckBox = new QCheckBox( i18n( "Show bugs with number of votes greater than:" ), topFrame ); + layout->addWidget( mShowVotedCheckBox ); + + mMinVotesInput = new KIntNumInput( topFrame ); + mMinVotesInput->setMinValue( 0 ); + connect( mShowVotedCheckBox, SIGNAL(toggled(bool)), + mMinVotesInput, SLOT(setEnabled(bool)) ); + layout->addWidget( mMinVotesInput ); + + mSendBccCheckBox = new QCheckBox( i18n( "Send BCC to myself" ), topFrame ); + layout->addWidget( mSendBccCheckBox ); +} + +void PreferencesDialog::setDefaults() +{ + KBBPrefs::instance()->setDefaults(); + readConfig(); +} + +void PreferencesDialog::slotApply() +{ + writeConfig(); +} + +void PreferencesDialog::slotOk() +{ + writeConfig(); + accept(); +} + +void PreferencesDialog::slotCancel() +{ + hide(); +} + +void PreferencesDialog::addServer() +{ + ServerConfigDialog *dlg = new ServerConfigDialog( this ); + int result = dlg->exec(); + if ( result == QDialog::Accepted ) { + new ServerItem( mServerList, dlg->serverConfig() ); + } +} + +void PreferencesDialog::editServer() +{ + ServerItem *item = static_cast( mServerList->currentItem() ); + if ( !item ) return; + + ServerConfigDialog *dlg = new ServerConfigDialog( this ); + dlg->setServerConfig( item->serverConfig() ); + + int result = dlg->exec(); + if ( result == QDialog::Accepted ) { + item->setServerConfig( dlg->serverConfig() ); + } +} + +void PreferencesDialog::removeServer() +{ + QListViewItem *item = mServerList->currentItem(); + if ( !item ) return; + + delete item; +} + +void PreferencesDialog::selectServer() +{ + SelectServerDlg *dlg =new SelectServerDlg( this, "Select Server" ); + + int result = dlg->exec(); + if ( result == QDialog::Accepted ) { + ServerItem *item = dlg->serverSelected(); + if ( item ) { + new ServerItem( mServerList, item->serverConfig() ); + } + } + delete dlg; +} + +void PreferencesDialog::createServerItem( ServerListView *listView, + const QString &name, + const QString &url, + const QString &version ) +{ + BugServerConfig cfg( name, KURL( url ) ); + cfg.setBugzillaVersion( version ); + new ServerItem( listView, cfg ); +} + +void PreferencesDialog::readConfig() +{ + int client = KBBPrefs::instance()->mMailClient; + switch(client) { + default: + case MailSender::KMail: + mKMailButton->setChecked(true); + break; + case MailSender::Sendmail: + mSendmailButton->setChecked(true); + break; + case MailSender::Direct: + mDirectButton->setChecked(true); + break; + } + mShowClosedCheckBox->setChecked( KBBPrefs::instance()->mShowClosedBugs ); + mShowWishesCheckBox->setChecked( KBBPrefs::instance()->mShowWishes ); + mShowVotedCheckBox->setChecked( KBBPrefs::instance()->mShowVoted ); + mMinVotesInput->setValue( KBBPrefs::instance()->mMinVotes ); + mSendBccCheckBox->setChecked( KBBPrefs::instance()->mSendBCC ); + + mServerList->clear(); + QValueList servers = BugSystem::self()->serverList(); + QValueList::ConstIterator it; + for( it = servers.begin(); it != servers.end(); ++it ) { + new ServerItem( mServerList, (*it)->serverConfig() ); + } +} + +void PreferencesDialog::writeConfig() +{ + MailSender::MailClient client = MailSender::KMail; + + if (mKMailButton->isChecked()) client = MailSender::KMail; + if (mSendmailButton->isChecked()) client = MailSender::Sendmail; + if (mDirectButton->isChecked()) client = MailSender::Direct; + + KBBPrefs::instance()->mMailClient = client; + KBBPrefs::instance()->mShowClosedBugs = mShowClosedCheckBox->isChecked(); + KBBPrefs::instance()->mShowWishes = mShowWishesCheckBox->isChecked(); + KBBPrefs::instance()->mShowVoted = mShowVotedCheckBox->isChecked(); + KBBPrefs::instance()->mMinVotes = mMinVotesInput->value(); + KBBPrefs::instance()->mSendBCC = mSendBccCheckBox->isChecked(); + KBBPrefs::instance()->writeConfig(); + + QValueList servers; + QListViewItem *item; + for ( item = mServerList->firstChild(); item; + item = item->nextSibling() ) { + servers.append( static_cast( item )->serverConfig() ); + } + + BugSystem::self()->setServerList( servers ); + + emit configChanged(); +} + +SelectServerDlg::SelectServerDlg(PreferencesDialog *parent, const char */*name*/ ) + :KDialogBase(parent, 0, true, i18n("Select Server"), + KDialogBase::Ok | KDialogBase::Cancel) +{ + list = new ServerListView(this ); + setMainWidget( list ); + + parent->createServerItem( list, "KDE", "http://bugs.kde.org", "KDE" ); + parent->createServerItem( list, "GNOME", "http://bugzilla.gnome.org", "2.10" ); + parent->createServerItem( list, "Mozilla", "http://bugzilla.mozilla.org", "2.17.1" ); + parent->createServerItem( list, "Apache", "http://nagoya.apache.org/bugzilla/", "2.14.2" ); + parent->createServerItem( list, "XFree86", "http://bugs.xfree86.org/cgi-bin/bugzilla/", "2.14.2" ); + parent->createServerItem( list, "Ximian", "http://bugzilla.ximian.com", "2.10" ); + parent->createServerItem( list, "RedHat", "http://bugzilla.redhat.com/bugzilla/", "2.17.1" ); + parent->createServerItem( list, "Mandriva", "http://qa.mandriva.com/", "2.17.4" ); + connect( list, SIGNAL( doubleClicked ( QListViewItem *)), this, SLOT( slotDoubleClicked( QListViewItem *))); +} + + +ServerItem *SelectServerDlg::serverSelected() +{ + return static_cast( list->currentItem() ); +} + +void SelectServerDlg::slotDoubleClicked( QListViewItem *) +{ + accept(); +} + +#include "preferencesdialog.moc" diff --git a/kbugbuster/gui/preferencesdialog.h b/kbugbuster/gui/preferencesdialog.h new file mode 100644 index 00000000..29c72eaf --- /dev/null +++ b/kbugbuster/gui/preferencesdialog.h @@ -0,0 +1,76 @@ +#ifndef PREFERENCESDIALOG_H +#define PREFERENCESDIALOG_H + +#include + +class QCheckBox; +class QRadioButton; +class QLineEdit; +class QListView; +class KIntNumInput; +class ServerListView; + +class PreferencesDialog : public KDialogBase +{ + Q_OBJECT + public: + PreferencesDialog( QWidget* parent = 0, const char* name = 0 ); + ~PreferencesDialog(); + + void createServerItem( ServerListView *listView, const QString &name, + const QString &url, const QString &version ); + + public: + void readConfig(); + void writeConfig(); + + signals: + void configChanged(); + + protected slots: + void setDefaults(); + void slotApply(); + void slotOk(); + void slotCancel(); + + void addServer(); + void editServer(); + void removeServer(); + + void selectServer(); + + protected: + void setupServerPage(); + void setupAdvancedPage(); + + + private: + QCheckBox *mShowClosedCheckBox; + QCheckBox *mShowWishesCheckBox; + QCheckBox *mShowVotedCheckBox; + QCheckBox *mSendBccCheckBox; + KIntNumInput *mMinVotesInput; + QRadioButton *mKMailButton; + QRadioButton *mDirectButton; + QRadioButton *mSendmailButton; + QListView *mServerList; +}; + +class ServerListView; +class ServerItem; + +class SelectServerDlg : public KDialogBase +{ + Q_OBJECT +public: + SelectServerDlg(PreferencesDialog *parent, const char */*name*/ ); + ServerItem *serverSelected(); +protected slots: + void slotDoubleClicked( QListViewItem *); + +protected: + ServerListView *list; +}; + + +#endif diff --git a/kbugbuster/gui/serverconfigdialog.cpp b/kbugbuster/gui/serverconfigdialog.cpp new file mode 100644 index 00000000..32c5e241 --- /dev/null +++ b/kbugbuster/gui/serverconfigdialog.cpp @@ -0,0 +1,82 @@ +#include "serverconfigdialog.h" + +#include "bugserverconfig.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +ServerConfigDialog::ServerConfigDialog( QWidget *parent, const char *name ) : + KDialogBase( parent, name, true, i18n("Edit Bugzilla Server"), Ok|Cancel ) +{ + QWidget *topFrame = makeMainWidget(); + + QGridLayout *topLayout = new QGridLayout( topFrame ); + topLayout->setSpacing( spacingHint() ); + + QLabel *label; + + mServerName = new QLineEdit( topFrame ); + label = new QLabel( mServerName, i18n("Name:"), topFrame ); + topLayout->addWidget( label, 0, 0 ); + topLayout->addWidget( mServerName, 0, 1 ); + mServerName->setFocus(); + + mServerUrl = new QLineEdit( topFrame ); + label = new QLabel( mServerUrl, i18n("URL:"), topFrame ); + topLayout->addWidget( label, 1, 0 ); + topLayout->addWidget( mServerUrl, 1, 1 ); + + mUser = new QLineEdit( topFrame ); + label = new QLabel( mUser, i18n("User:"), topFrame ); + topLayout->addWidget( label, 2, 0 ); + topLayout->addWidget( mUser, 2, 1 ); + + mPassword = new KPasswordEdit( topFrame ); + label = new QLabel( mPassword, i18n("Password:"), topFrame ); + topLayout->addWidget( label, 3, 0 ); + topLayout->addWidget( mPassword, 3, 1 ); + + mVersion = new QComboBox( topFrame ); + label = new QLabel( mVersion, i18n("Bugzilla version:"), topFrame ); + topLayout->addWidget( label, 4, 0 ); + topLayout->addWidget( mVersion, 4, 1 ); + mVersion->insertStringList( BugServerConfig::bugzillaVersions() ); +} + +void ServerConfigDialog::setServerConfig( const BugServerConfig &cfg ) +{ + mServerName->setText( cfg.name() ); + mServerUrl->setText( cfg.baseUrl().url() ); + mUser->setText( cfg.user() ); + mPassword->setText( cfg.password() ); + + int i; + for( i = 0; i < mVersion->count(); ++i ) { + if ( mVersion->text( i ) == cfg.bugzillaVersion() ) { + mVersion->setCurrentItem( i ); + break; + } + } +} + +BugServerConfig ServerConfigDialog::serverConfig() +{ + BugServerConfig cfg; + + cfg.setName( mServerName->text() ); + cfg.setBaseUrl( KURL( mServerUrl->text() ) ); + cfg.setUser( mUser->text() ); + cfg.setPassword( mPassword->text() ); + cfg.setBugzillaVersion( mVersion->currentText() ); + + return cfg; +} + +#include "serverconfigdialog.moc" diff --git a/kbugbuster/gui/serverconfigdialog.h b/kbugbuster/gui/serverconfigdialog.h new file mode 100644 index 00000000..5764bfdf --- /dev/null +++ b/kbugbuster/gui/serverconfigdialog.h @@ -0,0 +1,28 @@ +#ifndef SERVERCONFIGDIALOG_H +#define SERVERCONFIGDIALOG_H + +#include + +class BugServerConfig; +class QLineEdit; +class KPasswordEdit; +class QComboBox; + +class ServerConfigDialog : public KDialogBase +{ + Q_OBJECT + public: + ServerConfigDialog( QWidget *parent = 0 , const char *name = 0 ); + + void setServerConfig( const BugServerConfig & ); + BugServerConfig serverConfig(); + + private: + QLineEdit *mServerName; + QLineEdit *mServerUrl; + QLineEdit *mUser; + KPasswordEdit *mPassword; + QComboBox *mVersion; +}; + +#endif diff --git a/kbugbuster/gui/severityselectdialog.cpp b/kbugbuster/gui/severityselectdialog.cpp new file mode 100644 index 00000000..714e6f3a --- /dev/null +++ b/kbugbuster/gui/severityselectdialog.cpp @@ -0,0 +1,40 @@ +#include +#include +#include + +#include + +#include "bugsystem.h" +#include "kbbprefs.h" + +#include "severityselectdialog.h" +#include "severityselectdialog.moc" + +SeveritySelectDialog::SeveritySelectDialog(QWidget *parent,const char *name) : + KDialogBase( parent, name, true, i18n("Select Severity"), Ok|Cancel ) +{ + mButtonGroup = new QButtonGroup( 1, Horizontal, i18n("Severity"), this ); + setMainWidget( mButtonGroup ); + + QValueList severities = Bug::severities(); + QValueList::ConstIterator it; + for( it = severities.begin(); it != severities.end(); ++it ) { + mButtonGroup->insert( + new QRadioButton( Bug::severityToString( *it ), mButtonGroup ), int(*it) ); + } +} + +void SeveritySelectDialog::setSeverity( Bug::Severity s ) +{ + mButtonGroup->setButton( s ); +} + +Bug::Severity SeveritySelectDialog::selectedSeverity() +{ + return (Bug::Severity)mButtonGroup->id( mButtonGroup->selected() ); +} + +QString SeveritySelectDialog::selectedSeverityAsString() +{ + return Bug::severityToString( selectedSeverity() ); +} diff --git a/kbugbuster/gui/severityselectdialog.h b/kbugbuster/gui/severityselectdialog.h new file mode 100644 index 00000000..12f36fe5 --- /dev/null +++ b/kbugbuster/gui/severityselectdialog.h @@ -0,0 +1,23 @@ +#ifndef SEVERITYSELECTDIALOG_H +#define SEVERITYSELECTDIALOG_H + +#include + +#include "bug.h" + +class SeveritySelectDialog : public KDialogBase +{ + Q_OBJECT + public: + SeveritySelectDialog(QWidget *parent=0,const char *name=0); + + void setSeverity( Bug::Severity ); + + Bug::Severity selectedSeverity(); + QString selectedSeverityAsString(); + + private: + QButtonGroup *mButtonGroup; +}; + +#endif diff --git a/kbugbuster/hi128-app-kbugbuster.png b/kbugbuster/hi128-app-kbugbuster.png new file mode 100644 index 00000000..a39081d9 Binary files /dev/null and b/kbugbuster/hi128-app-kbugbuster.png differ diff --git a/kbugbuster/hi16-app-kbugbuster.png b/kbugbuster/hi16-app-kbugbuster.png new file mode 100644 index 00000000..827d5f8e Binary files /dev/null and b/kbugbuster/hi16-app-kbugbuster.png differ diff --git a/kbugbuster/hi22-app-kbugbuster.png b/kbugbuster/hi22-app-kbugbuster.png new file mode 100644 index 00000000..3a3d1ea2 Binary files /dev/null and b/kbugbuster/hi22-app-kbugbuster.png differ diff --git a/kbugbuster/hi32-app-kbugbuster.png b/kbugbuster/hi32-app-kbugbuster.png new file mode 100644 index 00000000..59773925 Binary files /dev/null and b/kbugbuster/hi32-app-kbugbuster.png differ diff --git a/kbugbuster/hi48-app-kbugbuster.png b/kbugbuster/hi48-app-kbugbuster.png new file mode 100644 index 00000000..2565d767 Binary files /dev/null and b/kbugbuster/hi48-app-kbugbuster.png differ diff --git a/kbugbuster/hi64-app-kbugbuster.png b/kbugbuster/hi64-app-kbugbuster.png new file mode 100644 index 00000000..f2f45269 Binary files /dev/null and b/kbugbuster/hi64-app-kbugbuster.png differ diff --git a/kbugbuster/kbugbuster.desktop b/kbugbuster/kbugbuster.desktop new file mode 100644 index 00000000..1fa713a9 --- /dev/null +++ b/kbugbuster/kbugbuster.desktop @@ -0,0 +1,78 @@ +# KDE Config File +[Desktop Entry] +Type=Application +Exec=kbugbuster -caption "%c" %i %m +Icon=kbugbuster +DocPath=kbugbuster/index.html +GenericName=KDE Bug Management +GenericName[af]=Kde Fout Bestuuring +GenericName[bg]=Управление на грешки - KDE +GenericName[bs]=KDE upravljanje bugovima +GenericName[ca]=Gestió d'errors a KDE +GenericName[cs]=Správa chyb KDE +GenericName[cy]=Rheolaeth Namau KDE +GenericName[da]=KDE Fejlretningshåndtering +GenericName[de]=KDE-Programmfehler-Verwaltung +GenericName[el]=Διαχείριση σφαλμάτων του KDE +GenericName[eo]=KDE-Eraroadministrado +GenericName[es]=Administración de errores de KDE +GenericName[et]=KDE veahaldusprogramm +GenericName[eu]=KDE programa-errore kudeaketa +GenericName[fa]=مدیریت اشکال KDE +GenericName[fi]=KDE:n vianhallinta +GenericName[fo]=KDE-villuhandfaring +GenericName[fr]=Outil de gestion de bogues pour KDE +GenericName[ga]=Bainisteoireacht Fabhtanna KDE +GenericName[gl]=Xestión de erros para KDE +GenericName[he]=ניהול של באגים ב-KDE +GenericName[hi]=केडीई बग प्रबंधक +GenericName[hr]=KDE upravljanje bugovima +GenericName[hu]=KDE hibakezelő +GenericName[is]=KDE villustjórnun +GenericName[it]=Gestione bug di KDE +GenericName[ja]=KDE バグマネージメント +GenericName[ka]=KDE ბზიკთა მართვა +GenericName[kk]=KDE қателерді басқару +GenericName[lt]=KDE ydų tvarkymas +GenericName[lv]=KDE Kļūdu Pārvalde +GenericName[ms]=Pengurusan Pepijat KDE +GenericName[nb]=KDE-feilhåndtering +GenericName[nds]=KDE-Programmfehler-Pleeg +GenericName[ne]=केडीई बग प्रबन्धक +GenericName[nl]=KDE-bugs beheren +GenericName[nn]=KDE-feilhandtering +GenericName[pa]=KDE ਬੱਗ ਪਰਬੰਧਨ +GenericName[pl]=Zarządzanie błędami w KDE +GenericName[pt]=Gestão de Erros do KDE +GenericName[pt_BR]=Gerenciamento de Erros do KDE +GenericName[ro]=Utilitar de administrarea a erorilor din KDE +GenericName[ru]=Отслеживание ошибок +GenericName[sk]=Správa chýb KDE +GenericName[sl]=Upravljanje s hrošči v KDE +GenericName[sr]=KDE-ово управљање грешкама +GenericName[sr@Latn]=KDE-ovo upravljanje greškama +GenericName[sv]=Verktyg för KDE-felhantering +GenericName[ta]= KDE பக் மேனேஜ்மென்ட் +GenericName[tg]=Утилитаи идоракунии хатогиҳо +GenericName[th]=เครื่องมือจัดการบักสำหรับ KDE +GenericName[tr]=KDE Hata Ayıklayıcı +GenericName[uk]=Керування вадами KDE +GenericName[ven]=Malangulele a Bug a KDE +GenericName[vi]=Trình quản lí bug KDE +GenericName[xh]=Umxholo Wokuxoxwa We KDE +GenericName[zh_CN]=KDE 除错管理 +GenericName[zh_TW]=KDE 臭蟲管理 +GenericName[zu]=KDE Ukuphathwa Kwegciwane +Terminal=false +Name=KBugBuster +Name[af]=K-fout-buster +Name[cy]=KNamWasgydd +Name[eo]=Eraroĉasilo +Name[hi]=के-बग-बस्टर +Name[lv]=KKļūduMednieks +Name[pl]=Przeglądarka bazy błędów +Name[sv]=Kbugbuster +Name[ta]= Kபக்பஸ்டர் +Name[th]=บักบัสเตอร์ +Name[ven]=Mupandeli wa Baga wa K +Categories=Qt;KDE;Development; diff --git a/kbugbuster/kresources/Makefile.am b/kbugbuster/kresources/Makefile.am new file mode 100644 index 00000000..ddfde0fa --- /dev/null +++ b/kbugbuster/kresources/Makefile.am @@ -0,0 +1,16 @@ +INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/kbugbuster/backend $(all_includes) + +kde_module_LTLIBRARIES = kcal_bugzilla.la + +kcal_bugzilla_la_SOURCES = kcalresource.cpp kcalresourceconfig.cpp \ + kcalresource_plugin.cpp resourceprefs.kcfgc +kcal_bugzilla_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) +kcal_bugzilla_la_LIBADD = ../backend/libkbbbackend.la -lkcal + +servicedir = $(kde_servicesdir)/kresources/kcal +service_DATA = bugzilla.desktop + +METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kres_bugzilla.pot diff --git a/kbugbuster/kresources/bugzilla.desktop b/kbugbuster/kresources/bugzilla.desktop new file mode 100644 index 00000000..75cbb5a5 --- /dev/null +++ b/kbugbuster/kresources/bugzilla.desktop @@ -0,0 +1,48 @@ +[Desktop Entry] +Name=Bugzilla To-do List +Name[bg]=Задачи (Bugzilla) +Name[ca]=Llista de pendents de Bugzilla +Name[cs]=Seznam úkolů (Bugzilla) +Name[da]=Bugzilla gøremålsliste +Name[de]=Bugzilla Todo-Liste +Name[el]=Προς υλοποίηση λίστα του Bugzilla +Name[es]=Listado de tareas pendientes de BugZilla +Name[et]=Bugzilla ülesannete nimekiri +Name[eu]=Bugzilla-ren egiteke zerrenda +Name[fa]=فهرست کار انجامی Bugzilla +Name[fi]=Bugzilla-tehtäväluettelo +Name[fr]=Liste de tâches de Bugzilla +Name[ga]=Tascliosta Bugzilla +Name[gl]=Lista de itens por facer de Bugzilla +Name[he]=רשימת מטלות של Bugzilla +Name[hu]=Bugzilla feladatlista +Name[is]=Bugzilla verklisti +Name[it]=Lista delle cosa da fare di Bugzilla +Name[ja]=BugzillaToDo リスト +Name[ka]=Bugzilla-ს დავალებათა სია +Name[kk]=Bugzilla To-do тізімі +Name[lt]=Bugzilla darbų sąrašas +Name[nb]=Bugzilla-huskeliste +Name[nds]=Bugzilla-Opgavenlist +Name[ne]=बगजिला गर्नुपर्ने कार्य सूची +Name[nl]=Bugzilla Todo-lijst +Name[nn]=Bugzilla-hugseliste +Name[pa]=ਬੱਗਜੀਲਾ ਕਰਨ ਸੂਚੀ +Name[pl]=Lista rzeczy do zrobienia w Bugzilli +Name[pt]=Lista de Itens Por-Fazer do Bugzilla +Name[pt_BR]=Lista de Itens Por-Fazer do Bugzilla +Name[ru]=Список TODO Bugzilla +Name[sk]=Zoznam úloh v Bugzille +Name[sl]=Seznam »za-narediti« v Bugzilli +Name[sr]=Листа послова Bugzilla-е +Name[sr@Latn]=Lista poslova Bugzilla-e +Name[sv]=Bugzilla uppgiftslista +Name[tr]=Bugzilla To-do Listesi +Name[uk]=Список завдань Bugzilla +Name[zh_CN]=Bugzilla 待办列表 +Name[zh_TW]=Bugzilla 待辦清單 +X-KDE-Library=kcal_bugzilla +Type=Service +ServiceTypes=KResources/Plugin +X-KDE-ResourceFamily=calendar +X-KDE-ResourceType=bugzilla diff --git a/kbugbuster/kresources/kcalresource.cpp b/kbugbuster/kresources/kcalresource.cpp new file mode 100644 index 00000000..fbd91bc1 --- /dev/null +++ b/kbugbuster/kresources/kcalresource.cpp @@ -0,0 +1,316 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2003 Cornelius Schumacher + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "bugsystem.h" +#include "bugserver.h" + +#include "kcalresourceconfig.h" +#include "resourceprefs.h" + +#include "kcalresource.h" + +KCalResource::KCalResource( const KConfig* config ) + : ResourceCached( config ), mLock( 0 ) +{ + mPrefs = new KBB::ResourcePrefs; + + KConfigSkeletonItem::List items = mPrefs->items(); + KConfigSkeletonItem::List::Iterator it; + for( it = items.begin(); it != items.end(); ++it ) { + (*it)->setGroup( identifier() ); + } + + if ( config ) { + readConfig( config ); + } + + init(); +} + +KCalResource::~KCalResource() +{ + close(); + + if ( mDownloadJob ) mDownloadJob->kill(); + if ( mUploadJob ) mUploadJob->kill(); + + delete mLock; +} + +void KCalResource::init() +{ + mDownloadJob = 0; + mUploadJob = 0; + + setType( "remote" ); + + mOpen = false; + + mLock = new KABC::LockNull( true ); + + KConfig config( "kbugbusterrc" ); + + BugSystem::self()->readConfig( &config ); +} + +KBB::ResourcePrefs *KCalResource::prefs() +{ + return mPrefs; +} + +void KCalResource::readConfig( const KConfig * ) +{ + mPrefs->readConfig(); +} + +void KCalResource::writeConfig( KConfig *config ) +{ + kdDebug() << "KCalResource::writeConfig()" << endl; + + ResourceCalendar::writeConfig( config ); + + mPrefs->writeConfig(); +} + +QString KCalResource::cacheFile() +{ + QString file = locateLocal( "cache", "kcal/kresources/" + identifier() ); + kdDebug() << "KCalResource::cacheFile(): " << file << endl; + return file; +} + +bool KCalResource::doOpen() +{ + kdDebug(5800) << "KCalResource::doOpen()" << endl; + + mOpen = true; + + return true; +} + +bool KCalResource::doLoad() +{ + kdDebug() << "KCalResource::doLoad()" << endl; + + if ( !mOpen ) return true; + + if ( mDownloadJob ) { + kdWarning() << "KCalResource::doLoad(): download still in progress." + << endl; + return false; + } + if ( mUploadJob ) { + kdWarning() << "KCalResource::doLoad(): upload still in progress." + << endl; + return false; + } + + mCalendar.close(); + + mCalendar.load( cacheFile() ); + + BugSystem *kbb = BugSystem::self(); + + kdDebug() << "KNOWN SERVERS:" << endl; + QValueList servers = kbb->serverList(); + QValueList::ConstIterator it; + for( it = servers.begin(); it != servers.end(); ++it ) { + kdDebug() << " " << (*it)->identifier() << endl; + } + + kbb->setCurrentServer( mPrefs->server() ); + if ( !kbb->server() ) { + kdError() << "Server not found." << endl; + return false; + } else { + kdDebug() << "CURRENT SERVER: " << kbb->server()->identifier() << endl; + } + + kbb->retrievePackageList(); + + Package package = kbb->package( mPrefs->product() ); + + connect( kbb, SIGNAL( bugListAvailable( const Package &, const QString &, + const Bug::List & ) ), + SLOT( slotBugListAvailable( const Package &, const QString &, + const Bug::List & ) ) ); + + kbb->retrieveBugList( package, mPrefs->component() ); + + return true; +} + +void KCalResource::slotBugListAvailable( const Package &, const QString &, + const Bug::List &bugs ) +{ + kdDebug() << "KCalResource::slotBugListAvailable()" << endl; + + if ( bugs.isEmpty() ) return; + + QString masterUid = "kbb_" + BugSystem::self()->server()->identifier(); + KCal::Todo *masterTodo = mCalendar.todo( masterUid ); + if ( !masterTodo ) { + masterTodo = new KCal::Todo; + masterTodo->setUid( masterUid ); + masterTodo->setSummary( resourceName() ); + mCalendar.addTodo( masterTodo ); + } + + Bug::List::ConstIterator it; + for( it = bugs.begin(); it != bugs.end(); ++it ) { + Bug bug = *it; + kdDebug() << " Bug " << bug.number() << ": " << bug.title() << endl; + QString uid = "KBugBuster_" + bug.number(); + KCal::Todo *newTodo = 0; + KCal::Todo *todo = mCalendar.todo( uid ); + if ( !todo ) { + newTodo = new KCal::Todo; + newTodo->setUid( uid ); + QString uri = "http://bugs.kde.org/show_bug.cgi?id=%1"; + newTodo->addAttachment( new KCal::Attachment( uri.arg( bug.number() ) ) ); + todo = newTodo; + } + + todo->setSummary( bug.number() + ": " + bug.title() ); + todo->setRelatedTo( masterTodo ); + + if ( newTodo ) mCalendar.addTodo( newTodo ); + } + + emit resourceChanged( this ); +} + +void KCalResource::slotLoadJobResult( KIO::Job *job ) +{ + if ( job->error() ) { + job->showErrorDialog( 0 ); + } else { + kdDebug() << "KCalResource::slotLoadJobResult() success" << endl; + + mCalendar.close(); + mCalendar.load( cacheFile() ); + + emit resourceChanged( this ); + } + + mDownloadJob = 0; + + emit resourceLoaded( this ); +} + +bool KCalResource::doSave() +{ + kdDebug() << "KCalResource::doSave()" << endl; + + if ( !mOpen ) return true; + + if ( readOnly() ) { + emit resourceSaved( this ); + return true; + } + + if ( mDownloadJob ) { + kdWarning() << "KCalResource::save(): download still in progress." + << endl; + return false; + } + if ( mUploadJob ) { + kdWarning() << "KCalResource::save(): upload still in progress." + << endl; + return false; + } + + mCalendar.save( cacheFile() ); + + mUploadJob = KIO::file_copy( KURL( cacheFile() ), mUploadUrl, -1, true ); + connect( mUploadJob, SIGNAL( result( KIO::Job * ) ), + SLOT( slotSaveJobResult( KIO::Job * ) ) ); + + return true; +} + +bool KCalResource::isSaving() +{ + return mUploadJob; +} + +void KCalResource::slotSaveJobResult( KIO::Job *job ) +{ + if ( job->error() ) { + job->showErrorDialog( 0 ); + } else { + kdDebug() << "KCalResource::slotSaveJobResult() success" << endl; + } + + mUploadJob = 0; + + emit resourceSaved( this ); +} + +void KCalResource::doClose() +{ + if ( !mOpen ) return; + + mCalendar.close(); + mOpen = false; +} + +KABC::Lock *KCalResource::lock() +{ + return mLock; +} + +void KCalResource::dump() const +{ + ResourceCalendar::dump(); + kdDebug(5800) << " DownloadUrl: " << mDownloadUrl.url() << endl; + kdDebug(5800) << " UploadUrl: " << mUploadUrl.url() << endl; + kdDebug(5800) << " ReloadPolicy: " << mReloadPolicy << endl; +} + +#include "kcalresource.moc" diff --git a/kbugbuster/kresources/kcalresource.h b/kbugbuster/kresources/kcalresource.h new file mode 100644 index 00000000..9970f5e4 --- /dev/null +++ b/kbugbuster/kresources/kcalresource.h @@ -0,0 +1,123 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2003 Cornelius Schumacher + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KCALRESOURCE_H +#define KCALRESOURCE_H + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace KIO { +class FileCopyJob; +class Job; +} + +namespace KBB { +class ResourcePrefs; +} + +/** + This class provides a calendar stored as a remote file. +*/ +class KCalResource : public KCal::ResourceCached +{ + Q_OBJECT + + friend class KCalResourceConfig; + + public: + /** + Reload policy. + + @see setReloadPolicy(), reloadPolicy() + */ + enum { ReloadNever, ReloadOnStartup, ReloadOnceADay, ReloadAlways }; + + /** + Create resource from configuration information stored in KConfig object. + */ + KCalResource( const KConfig * ); + ~KCalResource(); + + void readConfig( const KConfig *config ); + void writeConfig( KConfig *config ); + + KBB::ResourcePrefs *prefs(); + + /** + Return name of file used as cache for remote file. + */ + QString cacheFile(); + + KABC::Lock *lock(); + + bool isSaving(); + + void dump() const; + + protected slots: + void slotBugListAvailable( const Package &, const QString &, + const Bug::List &bugs ); + + void slotLoadJobResult( KIO::Job * ); + void slotSaveJobResult( KIO::Job * ); + + protected: + bool doOpen(); + void doClose(); + bool doLoad(); + bool doSave(); + + private: + void init(); + + KBB::ResourcePrefs *mPrefs; + + KURL mDownloadUrl; + KURL mUploadUrl; + + int mReloadPolicy; + + KCal::ICalFormat mFormat; + + bool mOpen; + + KIO::FileCopyJob *mDownloadJob; + KIO::FileCopyJob *mUploadJob; + + KABC::Lock *mLock; + + class Private; + Private *d; +}; + +#endif diff --git a/kbugbuster/kresources/kcalresource_plugin.cpp b/kbugbuster/kresources/kcalresource_plugin.cpp new file mode 100644 index 00000000..23b5f8ac --- /dev/null +++ b/kbugbuster/kresources/kcalresource_plugin.cpp @@ -0,0 +1,37 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2003 Cornelius Schumacher + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "kcalresourceconfig.h" +#include "kcalresource.h" + +#include +#include + +using namespace KCal; + +extern "C" +{ + KDE_EXPORT void *init_kcal_bugzilla() + { + KGlobal::locale()->insertCatalogue( "kres_bugzilla" ); + return new KRES::PluginFactory(); + } +} diff --git a/kbugbuster/kresources/kcalresourceconfig.cpp b/kbugbuster/kresources/kcalresourceconfig.cpp new file mode 100644 index 00000000..bb404445 --- /dev/null +++ b/kbugbuster/kresources/kcalresourceconfig.cpp @@ -0,0 +1,92 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2003 Cornelius Schumacher + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "kcalresource.h" +#include "resourceprefs.h" +#include "kcalresourceconfig.h" + +KCalResourceConfig::KCalResourceConfig( QWidget* parent, const char* name ) + : KRES::ConfigWidget( parent, name ) +{ + resize( 245, 115 ); + + QGridLayout *mainLayout = new QGridLayout( this, 2, 2 ); + mainLayout->setSpacing( KDialog::spacingHint() ); + + QLabel *label = new QLabel( i18n("Server:"), this ); + mainLayout->addWidget( label, 0, 0 ); + + mServerEdit = new KLineEdit( this ); + mainLayout->addWidget( mServerEdit, 0, 1 ); + + + label = new QLabel( i18n("Product:"), this ); + mainLayout->addWidget( label, 1, 0 ); + + mProductEdit = new KLineEdit( this ); + mainLayout->addWidget( mProductEdit, 1, 1 ); + + + label = new QLabel( i18n("Component:"), this ); + mainLayout->addWidget( label, 2, 0 ); + + mComponentEdit = new KLineEdit( this ); + mainLayout->addWidget( mComponentEdit, 2, 1 ); +} + +void KCalResourceConfig::loadSettings( KRES::Resource *resource ) +{ + KCalResource *res = static_cast( resource ); + if ( res ) { + KBB::ResourcePrefs *p = res->prefs(); + mServerEdit->setText( p->server() ); + mProductEdit->setText( p->product() ); + mComponentEdit->setText( p->component() ); + } else { + kdError(5700) << "KCalResourceConfig::loadSettings(): no KCalResource, cast failed" << endl; + } +} + +void KCalResourceConfig::saveSettings( KRES::Resource *resource ) +{ + KCalResource *res = static_cast( resource ); + if ( res ) { + KBB::ResourcePrefs *p = res->prefs(); + p->setServer( mServerEdit->text() ); + p->setProduct( mProductEdit->text() ); + p->setComponent( mComponentEdit->text() ); + } else { + kdError(5700) << "KCalResourceConfig::saveSettings(): no KCalResource, cast failed" << endl; + } +} + +#include "kcalresourceconfig.moc" diff --git a/kbugbuster/kresources/kcalresourceconfig.h b/kbugbuster/kresources/kcalresourceconfig.h new file mode 100644 index 00000000..43ab60a1 --- /dev/null +++ b/kbugbuster/kresources/kcalresourceconfig.h @@ -0,0 +1,53 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2003 Cornelius Schumacher + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KCALRESOURCECONFIG_H +#define KCALRESOURCECONFIG_H + +#include +#include + +class KLineEdit; + +/** + Configuration widget for remote resource. + + @see KCalResource +*/ +class KCalResourceConfig : public KRES::ConfigWidget +{ + Q_OBJECT + public: + KCalResourceConfig( QWidget *parent = 0, const char *name = 0 ); + + public slots: + virtual void loadSettings( KRES::Resource *resource ); + virtual void saveSettings( KRES::Resource *resource ); + + private: + KLineEdit *mServerEdit; + KLineEdit *mComponentEdit; + KLineEdit *mProductEdit; + + class Private; + Private *d; +}; + +#endif diff --git a/kbugbuster/kresources/kresources_kcal_bugzilla.kcfg b/kbugbuster/kresources/kresources_kcal_bugzilla.kcfg new file mode 100644 index 00000000..ce23c969 --- /dev/null +++ b/kbugbuster/kresources/kresources_kcal_bugzilla.kcfg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/kbugbuster/kresources/resourceprefs.kcfgc b/kbugbuster/kresources/resourceprefs.kcfgc new file mode 100644 index 00000000..0f41bdb1 --- /dev/null +++ b/kbugbuster/kresources/resourceprefs.kcfgc @@ -0,0 +1,9 @@ +# Code generation options for kconfig_compiler +File=kresources_kcal_bugzilla.kcfg +ClassName=ResourcePrefs +NameSpace=KBB +Singleton=false +Mutators=true +GlobalEnums=true +#ItemAccessors=true +#SetUserTexts=true diff --git a/kbugbuster/lo16-app-kbugbuster.png b/kbugbuster/lo16-app-kbugbuster.png new file mode 100644 index 00000000..eb693eb6 Binary files /dev/null and b/kbugbuster/lo16-app-kbugbuster.png differ diff --git a/kbugbuster/lo32-app-kbugbuster.png b/kbugbuster/lo32-app-kbugbuster.png new file mode 100644 index 00000000..3ffbba21 Binary files /dev/null and b/kbugbuster/lo32-app-kbugbuster.png differ diff --git a/kbugbuster/main.cpp b/kbugbuster/main.cpp new file mode 100644 index 00000000..456d9dc6 --- /dev/null +++ b/kbugbuster/main.cpp @@ -0,0 +1,83 @@ +/*************************************************************************** + main.cpp - description + ------------------- + begin : zo mrt 18 17:12:24 CET 2001 + copyright : (C) 2001 by Martijn Klingens + email : klingens@kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "gui/kbbmainwindow.h" +#include "bugsystem.h" +#include "kbbprefs.h" + +static const char description[] = + I18N_NOOP("KBugBuster"); +// INSERT A DESCRIPTION FOR YOUR APPLICATION HERE + +static const KCmdLineOptions options[] = +{ + {"d", 0, 0}, + {"disconnected", I18N_NOOP("Start in disconnected mode"), 0}, + {"pkg", 0, 0}, + {"package ", I18N_NOOP("Start with the buglist for "), 0}, + {"bug
", I18N_NOOP("Start with bug report
"), 0}, + KCmdLineLastOption +}; + +int main(int argc, char *argv[]) +{ + KAboutData aboutData( "kbugbuster", I18N_NOOP( "KBugBuster" ), + VERSION, description, KAboutData::License_GPL, + I18N_NOOP("(c) 2001,2002,2003 the KBugBuster authors") ); + aboutData.addAuthor( "Martijn Klingens", 0, "klingens@kde.org" ); + aboutData.addAuthor( "Cornelius Schumacher", 0, "schumacher@kde.org" ); + aboutData.addAuthor( "Simon Hausmann", 0, "hausmann@kde.org" ); + aboutData.addAuthor( "David Faure", 0, "faure@kde.org" ); + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions( options ); + + KApplication app; + + app.dcopClient()->attach(); + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if (args->isSet("disconnected")) { + BugSystem::self()->setDisconnected( true ); + } + + if ( app.isRestored() ) + { + RESTORE( KBBMainWindow ); + } + else + { + KBBMainWindow *mainWin = new KBBMainWindow(args->getOption("package"), args->getOption("bug")); + + // Since all background jobs remaing running after closing the + // main window we force a quit here + QObject::connect( &app, SIGNAL( lastWindowClosed() ), + &app, SLOT( quit() ) ); + mainWin->show(); + return app.exec(); + } +} + +/* vim: set et ts=4 sw=4 softtabstop=4: */ diff --git a/kbugbuster/pics/Makefile.am b/kbugbuster/pics/Makefile.am new file mode 100644 index 00000000..6ce39e37 --- /dev/null +++ b/kbugbuster/pics/Makefile.am @@ -0,0 +1,4 @@ +logo_DATA = tools.png logo.png bars.png top-right.png +logodir = $(kde_datadir)/kbugbuster/pics + +EXTRA_DIST = $(logo_DATA) diff --git a/kbugbuster/pics/bars.png b/kbugbuster/pics/bars.png new file mode 100644 index 00000000..7d03c72c Binary files /dev/null and b/kbugbuster/pics/bars.png differ diff --git a/kbugbuster/pics/logo.png b/kbugbuster/pics/logo.png new file mode 100644 index 00000000..360d6435 Binary files /dev/null and b/kbugbuster/pics/logo.png differ diff --git a/kbugbuster/pics/tools.png b/kbugbuster/pics/tools.png new file mode 100644 index 00000000..7f9584e3 Binary files /dev/null and b/kbugbuster/pics/tools.png differ diff --git a/kbugbuster/pics/top-right.png b/kbugbuster/pics/top-right.png new file mode 100644 index 00000000..bad44f08 Binary files /dev/null and b/kbugbuster/pics/top-right.png differ diff --git a/kcachegrind/AUTHORS b/kcachegrind/AUTHORS new file mode 100644 index 00000000..ded6005d --- /dev/null +++ b/kcachegrind/AUTHORS @@ -0,0 +1 @@ +Josef Weidendorfer diff --git a/kcachegrind/COPYING b/kcachegrind/COPYING new file mode 100644 index 00000000..5b6e7c66 --- /dev/null +++ b/kcachegrind/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/kcachegrind/ChangeLog b/kcachegrind/ChangeLog new file mode 100644 index 00000000..b19b0fb8 --- /dev/null +++ b/kcachegrind/ChangeLog @@ -0,0 +1,89 @@ +2004/06/30 + * Leak fixes + * Crash fixes on reload (make setData() synchroneous) + * Some update fixes in the data model (tracedata.cpp) + * Fix update problems in Function Profile + * Reselect active function on refresh in function profile + with grouping on + +2004/04/28 + * toplevel.h/cpp, kcachegrindui.rc + - Switching Layouts + * multiview.cpp: Removed some qDebug's + * Same term fixes + +2004/04/26 + * cachegrindloader.cpp, fixcost.cpp: + - Allow Ranges in Subposition Spec, currently not used + - Correctly parse "Desc: Trigger:" + - Allow Event Spec (Long Name, Formula) with "event:" + * listutils.cpp: + - make level meters for costs only 1 bar + (2 with upper from 0..50%, lower 50%..100% is really confusing) + - Besides from Call graph and Tree maps, truncate bars to + only use needed size (removes lots of empty rectangles) + * CallGraphView: + - some fixes when no data is loaded + * functionselection.cpp (Function Profile) + - activation on mouse release to allow for context menu + * tracedata.cpp + - more robust parsing of events lists + - Introduction of Ranges (not currently used) + * utils.cpp: + - more robust parsing functions + +2004/04/05 + * CallGraphView: + - Add Context menu item "Export as Image" + - Hide Birdseye-View if call-graph fits into widget + - Error messages in Canvas when something goes wrong + * Some Fixes, qDebug->kdDebug + +2004/04/02 + * In most views columns for 2nd Event Type added + * Context menus modified to allow quick change of 2nd Event Type + * Toolbar simplified (only most used actions) + * Terminology fixes ("cost type"->"event type", + "trace data"->"profile data", long names of Ir,Dr,...) + * Sorting costs in lists is always descending now + * New File menu item: "Add..." other profile data to current window + * Detect Cachegrind format by "events:" content, not file name + Allows for arbitrary names of profile data files. + +2004/03/25 + * New Class Addr as wrapper for memory addresses. Use 64bit + to allow loading of data produced on 64bit architectures + +2004/03/17 + + * costtypeview.cpp, tracedata.h/cpp: + Fixed deletion of custom types + * cachegrindloader.cpp, tracedata.h/cpp: + Moved String compression handling in Cachegrind files + to CachegrindLoader + * Do not show inclusive cost column in FunctionSelection + side bar if not available + * Remove "isPartOfTrace" from Loader interface + (we allow parts from multiple experiments for comp.) + * partview.cpp, partlistitem.h/cpp: + Remove Column Callees, add Trigger + +2003/05/10 + + * Status progress on loading and cycle calculation + * Corrected order of trace parts (PID/PartNo/ThreadID) + * Allow adding traces (BUGGY...) + +2003/02/06 + + * Version 0.3a + * Bugfixes: + - Compiles with KDE 3.0.x + - Always select a first cost type + - Loading from another directory + + +2002/11/28 + + * Version 0.3 + diff --git a/kcachegrind/INSTALL b/kcachegrind/INSTALL new file mode 100644 index 00000000..02a4a074 --- /dev/null +++ b/kcachegrind/INSTALL @@ -0,0 +1,167 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes a while. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Type `make install' to install the programs and any data files and + documentation. + + 4. You can remove the program binaries and object files from the + source code directory by typing `make clean'. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/kcachegrind/Makefile.am b/kcachegrind/Makefile.am new file mode 100644 index 00000000..595471f5 --- /dev/null +++ b/kcachegrind/Makefile.am @@ -0,0 +1,6 @@ +SUBDIRS = kcachegrind pics converters + +EXTRA_DIST = \ + AUTHORS COPYING NEWS ChangeLog INSTALL README TODO \ + kcachegrind.lsm kcachegrind.spec version.h + diff --git a/kcachegrind/NEWS b/kcachegrind/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/kcachegrind/README b/kcachegrind/README new file mode 100644 index 00000000..30fee51e --- /dev/null +++ b/kcachegrind/README @@ -0,0 +1,62 @@ +KCachegrind +=========== + + +What is all this about ? +------------------------- + +Profiling, i.e. determinating most time consuming execution parts, +is an important last step when developing applications. +KCachegrind visualizes traces, generated by profiling, in various ways; +most notable is the TreeMap visualization of the calls happening +and a condensed version of it, the Coverage analysis. +KCachegrind is designed to allow fast browsing and to provide a quick +overview of very large programs, such as KDE applications (but not +limited to!). + +At the moment, it uses Cachegrind as profiling backend, which is using +the excellent CPU simulator in Valgrind. Thus, profiling does not +need any preparation, can cope with shared libraries and plugin +architectures, and allows for profile runs to not influence the measuring +by the profile itself (all in contrast to e.g. GProf). Disadvantage is +slower profile runs, unfortunately. + +For Cachegrind to provide call tree information, a patch is provided. +This enables the most interesting visualization features of KCachegrind. + + +Requirements +------------ + +A call-tree version of Cachegrind: + - X86 Linux + - Valgrind 1.0.x with call-tree patch from KCachegrind Website + - Valgrind 2.0.x with call-tree skin installed + +Cachegrind runs on x86 platforms, KCachegrind on all KDE enabled +platforms (KDE 3.0.x). + + +Compilation and Installation +---------------------------- + +Simple do the command sequence + + ./configure --prefix= + make + make install + + + +KCachegrind features +-------------------- + +Most important: TreeMap calltree visualisation. +For the rest, see the detailed "What's this?" help for +each part of KCachegrind and the quick starter on the +WWW page ( http://kcachegrind.sourceforge.net/cgi-bin/show.cgi ) + + + +Happy Profiling, + Josef Weidendorfer diff --git a/kcachegrind/TODO b/kcachegrind/TODO new file mode 100644 index 00000000..1eca67ed --- /dev/null +++ b/kcachegrind/TODO @@ -0,0 +1,100 @@ +TODO/Wishlist Items +=================== + + +KCachegrind +----------- + +All cost Lists: +* Show up to a number of items, not down to a threadshold. + If more, add a "..." with number of items not shown, and context option + to show more +* "Copy from Top" converts lists into ASCII, puts into clipboard + + +Configuration: + Source dirs per ELF object + +Layout: +* 1/2/3/4 vertical/horizontal FunctionInfos + with Shift/Wraparound selection mode +* Inside each FunctionInfo different Layouts + - tabbed layout + - top: info, bottom left: calls/coverage, bottom right: graph/source +* Long/short info tab + +General: +* Selected Item can be a object/file/class/function/line +* Configuration Dlg + - Local config (?) + - Cost Types + - function colors + - Try to reload source after config. +* Session Management + + + +Annotation Views: + + BUGS: + * Draw problem with multiple srcs to one target + * REP case... + + TODO: + * Selectable Jumps (Arrows) + * Tooltip for Jumps (Kind, from/to, jump count) + * Show direction (arrows) on jump lines + + Source view TODO: + * Implicit jumps (green) [needs support from the tool?] + + + +Callgraph: +* Fix Arrows for back-arcs +* Less "Jumps" for minimap +* Correct Keyboard navigation (how?) + +Types: +* Ratios +* Automatic subtypes + +WISHS: +* Support for Data tracing + Which variables are touched how often from which function? + - Some graphical visualisation... + +* GCC -pg (gmon.out) as Profiling Backend +* Demangler (use c++filt) +* Calculation of call weights (if not given) +* OProfile, DynaProf + +Support for KCachegrind in Calltree +----------------------------------- + +WISHS: +- store more details of calltree + - for every function call: executed from shared lib + (Not needed, if function names are unique in whole app) + - adaptive call chain context (Really needed ? MUCH Data!) +- dump at + - breakpoints + - watchpoints (with data tracing!) + - every xxx BBs (DONE) +- dump around + - function invocation + - KAction event + - DCOP event + +- data accesses from (instr address/count) + stack: -> (function, stackframe-offset) + dynamic: -> (mem region start, [type], offset) + type can be get when a constructor is called for region + static: -> (mem region start, type, offset) + +* Generate full instr/data access trace for offline analysis. + +* Appending mode + + + diff --git a/kcachegrind/configure.in.in b/kcachegrind/configure.in.in new file mode 100644 index 00000000..22701fc8 --- /dev/null +++ b/kcachegrind/configure.in.in @@ -0,0 +1,8 @@ +KCACHEGRIND_VERSION=0.4.6kde +AC_SUBST(KCACHEGRIND_VERSION) + +AC_FUNC_MMAP + +dnl AC_OUTPUT( kcachegrind/version.h ) +dnl AC_OUTPUT( kcachegrind/kcachegrind.spec ) +dnl AC_OUTPUT( kcachegrind/kcachegrind.lsm ) diff --git a/kcachegrind/converters/Makefile.am b/kcachegrind/converters/Makefile.am new file mode 100644 index 00000000..08b3696b --- /dev/null +++ b/kcachegrind/converters/Makefile.am @@ -0,0 +1,2 @@ +bin_SCRIPTS = hotshot2calltree op2calltree pprof2calltree dprof2calltree \ + memprof2calltree diff --git a/kcachegrind/converters/README b/kcachegrind/converters/README new file mode 100644 index 00000000..6a1c46c5 --- /dev/null +++ b/kcachegrind/converters/README @@ -0,0 +1,24 @@ +This directory contains some scripts to convert output of different +profiling tools into the format which can be loaded by KCachegrind. +See the comment at start of every script for details. + +In the long run, these should be replaced by import filters in +KCachegrind directly, but I can't promise anything. Partly, this +is because some scripts are provided as contribution from others. + +hotshot2calltree Converter from Python Hotshot Profiler. +op2calltree Converter from OProfile sampling data. +dprof2calltree Converter from PERL::DProf Profiler. +pprof2calltree Converter from APD PHP Profiler. + +Thanks go to +* George Schlossnagle for + dprof2calltree and pprof2calltree, +* Jrg Beyer for + hotshot2calltree + +If you want to write a converter, have a look at the calltree format +description on the web site (kcachegrind.sf.net). + +Josef + diff --git a/kcachegrind/converters/dprof2calltree b/kcachegrind/converters/dprof2calltree new file mode 100644 index 00000000..940457c8 --- /dev/null +++ b/kcachegrind/converters/dprof2calltree @@ -0,0 +1,199 @@ +#!/usr/bin/perl +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# - Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - All advertising materials mentioning features or use of this software +# must display the following acknowledgement: This product includes software +# developed by OmniTI Computer Consulting. +# +# - Neither name of the company nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS `AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Copyright (c) 2004 OmniTI Computer Consulting +# All rights reserved +# The following code was written by George Schlossnagle +# and is provided completely free and without any warranty. +# + +# +# This script is designed to convert the tmon.out output emitted +# from Perl's Devel::DProf profiling package. To use this: +# +# 1) Run your perl script as +# > perl -d:DProf yoursript.pl +# This will create a file called tmon.out. If you want to +# inspect it on the command line, look at the man page +# for dprofp for details. +# +# 2) Run +# > dprof2calltree -f tmon.out +# or +# > dprof2calltree -f tmon.out -o cachegrind.out.foo +# +# This creates a cachegrind-style file called cachgrind.out.tmon.out or +# cachegrind.out.foo, respecitvely. +# +# 3) Run kcachegrind cachegrind.out.foo +# +# 4) Enjoy! + +use strict; +use Config; +use Getopt::Std; +use IO::File; + +my @callstack; +my %function_info; +my $tree = {}; +my $total_cost = 0; +my %opts; + +getopt('f:o:', \%opts); + +my $infd; +usage() unless ($opts{'f'} && ($infd = IO::File->new($opts{'f'}, "r"))); + +my $outfd; +my $outfile = $opts{'o'}; +unless($outfile) { + $opts{'f'} =~ m!([^/]+)$!; + $outfile = "cachegrind.out.$1"; +} +$outfd = new IO::File $outfile, "w"; +usage() unless defined $outfd; + +while(<$infd>) { + last if /^PART2/; +} +while(<$infd>) { + chomp; + my @args = split; + if($args[0] eq '@') { + # record timing event + my $call_element = pop @callstack; + if($call_element) { + $call_element->{'cost'} += $args[3]; + $call_element->{'cumm_cost'} += $args[3]; + $total_cost += $args[3]; + push @callstack, $call_element; + } + } + elsif($args[0] eq '&') { + # declare function + $function_info{$args[1]}->{'package'} = $args[2]; + if($args[2] ne 'main') { + $function_info{$args[1]}->{'name'} = $args[2]."::".$args[3]; + } else { + $function_info{$args[1]}->{'name'} = $args[3]; + } + } + elsif($args[0] eq '+') { + # push myself onto the stack + my $call_element = { 'specifier' => $args[1], 'cost' => 0 }; + push @callstack, $call_element; + } + elsif($args[0] eq '-') { + my $called = pop @callstack; + my $called_id = $called->{'specifier'}; + my $caller = pop @callstack; + if (exists $tree->{$called_id}) { + $tree->{$called_id}->{'cost'} += $called->{'cost'}; + } + else { + $tree->{$called_id} = $called; + } + if($caller) { + $caller->{'child_calls'}++; + my $caller_id = $caller->{'specifier'}; + if(! exists $tree->{$caller_id} ) { + $tree->{$caller_id} = { 'specifier' => $caller_id, 'cost' => 0 }; +# $tree->{$caller_id} = $caller; + } + $caller->{'cumm_cost'} += $called->{'cumm_cost'}; + $tree->{$caller_id}->{'called_funcs'}->[$tree->{$caller_id}->{'call_counter'}++]->{$called_id} += $called->{'cumm_cost'}; + push @callstack, $caller; + } + } + elsif($args[0] eq '*') { + # goto &func + # replace last caller with self + my $call_element = pop @callstack; + $call_element->{'specifier'} = $args[1]; + push @callstack, $call_element; + } + else {print STDERR "Unexpected line: $_\n";} +} + +# +# Generate output +# +my $output = ''; +$output .= "events: Tick\n"; +$output .= "summary: $total_cost\n"; +$output .= "cmd: your script\n\n"; +foreach my $specifier ( keys %$tree ) { + my $caller_package = $function_info{$specifier}->{'package'} || '???'; + my $caller_name = $function_info{$specifier}->{'name'} || '???'; + my $include = find_include($caller_package); + $output .= "ob=\n"; + $output .= sprintf "fl=%s\n", find_include($caller_package); + $output .= sprintf "fn=%s\n", $caller_name; + $output .= sprintf "1 %d\n", $tree->{$specifier}->{'cost'}; + if(exists $tree->{$specifier}->{'called_funcs'}) { + foreach my $items (@{$tree->{$specifier}->{'called_funcs'}}) { + while(my ($child_specifier, $costs) = each %$items) { + $output .= sprintf "cfn=%s\n", $function_info{$child_specifier}->{'name'}; + $output .= sprintf "cfi=%s\n", find_include($function_info{$child_specifier}->{'package'}); + $output .= "calls=1\n"; + $output .= sprintf "1 %d\n", $costs; + } + } + } + $output .= "\n"; +} +print STDERR "Writing kcachegrind output to $outfile\n"; +$outfd->print($output); + + + +sub find_include { + my $module = shift; + $module =~ s!::!/!g; + for (@INC) { + if ( -f "$_/$module.pm" ) { + return "$_/$module.pm"; + } + if ( -f "$_/$module.so" ) { + return "$_/$module.so"; + } + } + return "???"; +} + +sub usage() { + print STDERR "dprof2calltree -f [-o outfile]\n"; + exit -1; +} + + +# vim: set sts=2 ts=2 bs ai expandtab : diff --git a/kcachegrind/converters/hotshot2calltree b/kcachegrind/converters/hotshot2calltree new file mode 100644 index 00000000..176f82f3 --- /dev/null +++ b/kcachegrind/converters/hotshot2calltree @@ -0,0 +1,394 @@ +#!/usr/bin/env python +# _*_ coding: latin1 _*_ + +# +# Copyright (c) 2003 by WEB.DE, Karlsruhe +# Autor: Jrg Beyer +# +# hotshot2cachegrind is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation, version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# +# This script transforms the pstat output of the hotshot +# python profiler into the input of kcachegrind. +# +# example usage: +# modify you python script to run this code: +# +# import hotshot +# filename = "pythongrind.prof" +# prof = hotshot.Profile(filename, lineevents=1) +# prof.runcall(run) # assuming that "run" should be called. +# prof.close() +# +# it will run the "run"-method under profiling and write +# the results in a file, called "pythongrind.prof". +# +# then call this script: +# hotshot2cachegrind -o +# or here: +# hotshot2cachegrind cachegrind.out.0 pythongrind.prof +# +# then call kcachegrind: +# kcachegrind cachegrind.out.0 +# +# TODO: +# * es gibt Probleme mit rekursiven (direkt und indirekt) Aufrufen - dann +# stimmen die Kosten nicht. +# +# * einige Funktionen werden mit "?" als Name angezeigt. Evtl sind +# das nur die C/C++ extensions. +# +# * es fehlt noch ein Funktionsnamen Mangling, dass die Filenamen bercksichtigt, +# zZ sind alle __init__'s und alle run's schwer unterscheidbar :-( +# +version = "$Revision$" +progname = "hotshot2cachegrind" + +import os, sys +from hotshot import stats,log +import os.path + +file_limit=0 + +what2text = { + log.WHAT_ADD_INFO : "ADD_INFO", + log.WHAT_DEFINE_FUNC : "DEFINE_FUNC", + log.WHAT_DEFINE_FILE : "DEFINE_FILE", + log.WHAT_LINENO : "LINENO", + log.WHAT_EXIT : "EXIT", + log.WHAT_ENTER : "ENTER"} + +# a pseudo caller on the caller stack. This represents +# the Python interpreter that executes the given python +# code. +root_caller = ("PythonInterpreter",0,"execute") + +class CallStack: + """A tiny Stack implementation, based on python lists""" + def __init__(self): + self.stack = [] + self.recursion_counter = {} + def push(self, elem): + """put something on the stack""" + self.stack.append(elem) + rc = self.recursion_counter.get(elem, 0) + self.recursion_counter[elem] = rc + 1 + + def pop(self): + """get the head element of the stack and remove it from teh stack""" + elem = self.stack[-1:][0] + rc = self.recursion_counter.get(elem) - 1 + if rc>0: + self.recursion_counter[elem] = rc + else: + del self.recursion_counter[elem] + return self.stack.pop() + + def top(self): + """get the head element of the stack, stack is unchanged.""" + return self.stack[-1:][0] + def handleLineCost(self, tdelta): + p, c = self.stack.pop() + self.stack.append( (p,c + tdelta) ) + def size(self): + """ return how many elements the stack has""" + return len(self.stack) + + def __str__(self): + return "[stack: %s]" % self.stack + + def recursion(self, pos): + return self.recursion_counter.get(pos, 0) + #return self.recursion_dict.has_key((entry[0][0], entry[0][2])) + +def return_from_call(caller_stack, call_dict, cost_now): + """return from a function call + remove the function from the caller stack, + add the costs to the calling function. + """ + called, cost_at_enter = caller_stack.pop() + caller, caller_cost = caller_stack.top() + + #print "return_from_call: %s ruft %s" % (caller, called,) + + per_file_dict = call_dict.get(called[0], {}) + per_caller_dict = per_file_dict.get(called[2], {}) + cost_so_far, call_counter = per_caller_dict.get(caller, (0, 0)) + + if caller_stack.recursion(called): + per_caller_dict[caller] = (cost_so_far, call_counter + 1) + else: + per_caller_dict[caller] = (cost_so_far + cost_now - cost_at_enter, call_counter + 1) + + per_file_dict[called[2]] = per_caller_dict + call_dict[called[0]] = per_file_dict + + +def updateStatus(filecount): + sys.stdout.write("reading File #%d \r" % filecount) + sys.stdout.flush() +def convertProfFiles(output, inputfilenames): + """convert all the given input files into one kcachegrind + input file. + """ + call_dict = {} + cost_per_pos = {} + cost_per_function = {} + caller_stack = CallStack() + caller_stack.push((root_caller, 0)) + + total_cost = 0 + filecount = 1 + number_of_files = len(inputfilenames) + for inputfilename in inputfilenames: + updateStatus(filecount) + cost, filecount = convertHandleFilename(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount) + total_cost += cost + if (file_limit > 0) and (filecount > file_limit): + break + + print + print "total_cost: % d Ticks",total_cost + dumpResults(output, call_dict, total_cost, cost_per_pos, cost_per_function) + +def convertHandleFilename(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount): + updateStatus(filecount) + if not ((file_limit > 0) and (filecount > file_limit)): + if os.path.isdir(inputfilename): + cost, filecount = convertProfDir(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount) + elif os.path.isfile(inputfilename): + cost = convertProfFile(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function) + filecount += 1 + else: + sys.stderr.write("warn: ignoring '%s', is no file and no directory\n" % inputfilename) + cost = 0 + return (cost, filecount) + +def convertProfDir(start, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount): + cost = 0 + filenames = os.listdir(start) + for f in filenames: + if (file_limit > 0) and (filecount > file_limit): + break + full = os.path.join(start, f) + c, filecount = convertHandleFilename(full, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount) + cost += c; + return (cost, filecount) + +def handleCostPerPos(cost_per_pos, pos, current_cost): + """ + the cost per source position are managed in a dict in a dict. + + the cost are handled per file and there per function. + so, the per-file-dict contains some per-function-dicts + which sum up the cost per line (in this function and in + this file). + """ + filename = pos[0] + lineno = pos[1] + funcname = pos[2] + file_dict = cost_per_pos.get(filename, {}) + func_dict = file_dict.get(funcname, {}) + func_dict.setdefault(lineno, 0) + func_dict[lineno] += current_cost + file_dict[funcname] = func_dict + cost_per_pos[filename] = file_dict + +def convertProfFile(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function): + """convert a single input file into one kcachegrind + data. + + this is the most expensive function in this python source :-) + """ + + total_cost = 0 + try: + logreader = log.LogReader(inputfilename) + current_cost = 0 + hc = handleCostPerPos # shortcut + for item in logreader: + what, pos ,tdelta = item + (file, lineno, func) = pos + #line = "%s %s %d %s %d" % (what2text[what], file, lineno, func, tdelta) + #print line + # most common cases first + if what == log.WHAT_LINENO: + # add the current cost to the current function + hc(cost_per_pos, pos, tdelta) + total_cost += tdelta + elif what == log.WHAT_ENTER: + caller_stack.push((pos, total_cost)) + hc(cost_per_pos, pos, tdelta) + total_cost += tdelta + elif what == log.WHAT_EXIT: + hc(cost_per_pos, pos, tdelta) + total_cost += tdelta + return_from_call(caller_stack, call_dict, total_cost) + else: + assert 0, "duh: %d" % what + + + # I have no idea, why sometimes the stack is not empty - we + # have to rewind the stack to get 100% for the root_caller + while caller_stack.size() > 1: + return_from_call(caller_stack, call_dict, total_cost) + + except IOError: + print "could not open inputfile '%s', ignore this." % inputfilename + except EOFError, m: + print "EOF: %s" % (m,) + return total_cost + +def pretty_name(file, function): + #pfile = os.path.splitext(os.path.basename(file)) [0] + #return "%s_[%s]" % (function, file) + return "%s" % function + #return "%s::%s" % (file, function) + #return "%s_%s" % (pfile, function) + +class TagWriter: + def __init__(self, output): + self.output = output + self.last_values = {} + + def clearTag(self, tag): + if self.last_values.has_key(tag): + del self.last_values[ tag ] + def clear(self): + self.last_values = {} + + def write(self, tag, value): + self.output.write("%s=%s\n" % (tag, value)) + #if (not self.last_values.has_key(tag)) or self.last_values[tag] != value: + # self.last_values[ tag ] = value + # self.output.write("%s=%s\n" % (tag, value)) + +def dumpResults(output, call_dict, total_cost, cost_per_pos, cost_per_function): + """write the collected results in the format kcachegrind + could read. + """ + # the intro + output.write("events: Tick\n") + output.write("summary: %d\n" % total_cost) + output.write("cmd: your python script\n") + output.write("\n") + tagwriter = TagWriter(output) + + # now the costs per line + for file in cost_per_pos.keys(): + func_dict = cost_per_pos[file] + for func in func_dict.keys(): + line_dict = func_dict[func] + tagwriter.write("ob", file) + tagwriter.write("fn", func)# pretty_name(file, func)) ; output.write("# ^--- 2\n") + tagwriter.write("fl", file) + for line in line_dict: + output.write("%d %d\n" %( line, line_dict[line] )) + + output.write("\n\n") + # now the function calls. For each caller all the called + # functions and their costs are written. + for file in call_dict.keys(): + per_file_dict = call_dict[file] + #print "file %s -> %s" % (file, per_file_dict) + for called_x in per_file_dict.keys(): + #print "called_x:",called_x + per_caller_dict = per_file_dict[called_x] + #print "called_x %s wird gerufen von: %s" % (called_x, per_caller_dict) + for caller_x in per_caller_dict.keys(): + tagwriter.write("ob", caller_x[0]) + tagwriter.write("fn", caller_x[2])# pretty_name(caller_x[2], caller_x[0])) ; output.write("# ^--- 1\n") + tagwriter.write("fl", caller_x[0]) + tagwriter.write("cob", file) + tagwriter.write("cfn", called_x) #pretty_name(file, called_x)) + tagwriter.write("cfl", file) + cost, count = per_caller_dict[caller_x] + #print "called_x:",called_x + output.write("calls=%d\n%d %d\n" % (count, caller_x[1], cost)) + tagwriter.clear() + #tagwriter.clearTag("cob") + # is it a bug in kcachegrind, that the "cob=xxx" line has + # to be rewritten after a calls entry with costline ? + #assert cost <= total_cost, "caller_x: %s, per_caller_dict: %s " % (caller_x, per_caller_dict, ) + #output.write("calls=%d\n%d %d\n" % (count, caller_x[1], cost)) + output.write("\n") + +def run_without_optparse(): + """parse the options without optparse, use sys.argv""" + if len(sys.argv) < 4 or sys.argv[1] != "-o" : + print "usage: hotshot2cachegrind -o outputfile in1 [in2 [in3 [...]]]" + return + outputfilename = sys.argv[2] + try: + output = file(outputfilename, "w") + args = sys.argv[3:] + convertProfFiles(output, args) + output.close() + except IOError: + print "could not open '%s' for writing." % outputfilename + +def run_with_optparse(): + """parse the options with optparse""" + + global file_limit + + versiontext = "%s version: %s" % ( progname, version.split()[1], ) + parser = OptionParser(version=versiontext) + parser.add_option("-o", "--output", + action="store", type="string", dest="outputfilename", + help="write output into FILE") + parser.add_option("--file-limit", + action="store", dest="file_limit", default=0, + help="stop after given number of input files") + output = sys.stdout + close_output = 0 + (options, args) = parser.parse_args() + file_limit = int(options.file_limit) + try: + if options.outputfilename and options.outputfilename != "-": + output = file(options.outputfilename, "w") + close_output = 1 + except IOError: + print "could not open '%s' for writing." % options.outputfilename + if output: + convertProfFiles(output, args) + if close_output: + output.close() + + +def profile_myself(): + import hotshot + filename = "self.prof" + if not os.path.exists(filename): + prof = hotshot.Profile(filename, lineevents=1) + prof.runcall(run) + prof.close() + else: + print "not profiling myself, since '%s' exists, running normal" % filename + run() + +# check if optparse is available. +try: + from optparse import OptionParser + run = run_with_optparse +except ImportError: + run = run_without_optparse + +if __name__ == "__main__": + try: + run() + #profile_myself() + except KeyboardInterrupt: + sys.exit(1) diff --git a/kcachegrind/converters/memprof2calltree b/kcachegrind/converters/memprof2calltree new file mode 100755 index 00000000..e82d6e85 --- /dev/null +++ b/kcachegrind/converters/memprof2calltree @@ -0,0 +1,38 @@ +#!/usr/bin/perl +# +# Convert the memory profiles of memprof to calltree format, +# loadable with KCachegrind +# +# (C) 2004, Josef Weidendorfer + +print "events: Allocated\n"; + +while(<>) { + if (/^(\S.*)$/) { + $next = 0; + print "\nfn=$1\n"; + next; + } + if (/^ children:/) { + $next = 1; #children + next; + } + if (/^ inherited:/) { + $next = 2; #inherited + next; + } + if (/^ total:/) { + # ignore, is calculated + next; + } + if (/^ self:\s*(\d+)/) { + if ($1 ne "0") { + print "0 $1\n"; + } + next; + } + if (/^\s+(\S.*?):\s*(\d+)$/) { + if ($next < 2) { next; } + print "cfn=$1\ncalls=0 0\n0 $2\n"; + } +} diff --git a/kcachegrind/converters/op2calltree b/kcachegrind/converters/op2calltree new file mode 100755 index 00000000..ff755390 --- /dev/null +++ b/kcachegrind/converters/op2calltree @@ -0,0 +1,238 @@ +#!/usr/bin/perl +# +# Copyright (c) 2004 +# Author: Josef Weidendorfer +# +# op2calltree is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation, version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# +# Converter from OProfile's output of "opreport -gdf" (v 0.8) +# into callgrind format. +# +# Generate a OProfile report with opreport and flags -gdf +# and pipe this as standard input into this script. +# This will generate separate cachegrind files for every application. +# + + +# parse symbol line. example (with 1 event type, $has_image==0): +# 308 0.1491 /path/source.c:6 /path/app main +sub parseSymSpec { + $e = 0; + while($e < $eventCount) { + ($line) = ($line =~ /\d+\s+\S+\s+(.*)/); + $e++; + } + if ($line =~ s/^\(no location information\)\s+//) { + $file = "???"; + $linenr = 0; + } + else { + ($file,$linenr) = ($line =~ s/(\S+?):(\d+)\s+//); + } + if ($has_image) { + if ($line =~ s/^(\S+)\s+//) { $img = $1; } + } + if ($has_app) { + if ($line =~ s/^(\S+)\s+//) { $app = $1; } + if (!$has_image) { $img = $app; } + } + $sym = $line; + + $app =~ s/^.*\///; + if ($sym eq "(no symbols)") { $sym = "???"; } + $file{$sym} = $file; + $linenr{$sym} = $linenr; + $app{$sym} = $app; + $img{$app,$sym} = $img; + $syms{$app}++; + + if ($app ne $oldApp) { + $oldApp = $app; + print "\n\nApp $app\n"; + } + print " Symbol $sym (Image $img)\n"; +} + + + +$eventCount = 0; +$descCount = 0; +$lnr = 0; +$has_image = 0; +$has_app = 0; +$app = "unnamed"; +$img = "???"; + +# first loop till first symbol specification +while(<>) { + $lnr++; + chomp; + if (/^CPU:/) { + $desc[$descCount++] = $_; + next; + } + if (/^Counted\s*(\S+)/) { + $desc[$descCount++] = $_; + $eventCount++; + $events[$eventCount] = $1; + next; + } + if (/^(Profiling through timer.*)/) { + $desc[$descCount++] = $_; + $eventCount++; + $events[$eventCount] = "Timer"; + next; + } + if (/^vma/) { + # title row: adapt to separation options of OProfile + if (/image/) { $has_image = 1; } + if (/app/) { $has_app = 1; } + next; + } + if (/^([0-9a-fA-F]+)\s*(.*)$/) { + $vmaSym = $1; + $line = $2; + last; + } +} + +if ($eventCount == 0) { + die "No Events found"; +} + +print "Description:\n"; +foreach $d (@desc) { print " $d\n"; } +print "\n"; + +print "Events:"; +foreach $e (@events) { print " $e"; } +print "\n"; + +parseSymSpec; + +while(<>) { + $lnr++; + if (/^([0-9a-fA-F]+)\s*(.*)$/) { + $vmaSym = $1; + $line = $2; + + parseSymSpec; + next; + } + if (/^\s+([0-9a-fA-F]+)\s*(.*)$/) { + + $sampleCount{$app,$sym}++; + $sc = $sampleCount{$app,$sym}; + + $vma{$app,$sym,$sc} = $1; + $line = $2; + + $e = 1; + while($e <= $eventCount) { + ($cost, $line) = ($line =~ /(\d+)\s+\S+\s+(.*)/); + $summary{$app,$e} += $cost; + $cost{"$app,$sym,$sc,$e"} = $cost; + $e++; + } + if ($line =~ /\(no location information\)/) { + $file = "???"; + $linenr = 0; + } + else { + ($file,$linenr) = ($line =~ /(\S+?):(\d+)/); + } + $sFile{$app,$sym,$sc} = $file; + $linenr{$app,$sym,$sc} = $linenr; + + $file =~ s/^.*\///; + print " Sample $sc: $vma{$app,$sym,$sc} ($file:$linenr):"; + foreach $e (1 .. $eventCount) { $c = $cost{"$app,$sym,$sc,$e"} ; print " $c"; } + print "\n"; + next; + } + die "ERROR: Reading line $lnr '$_'\n"; +} + +foreach $app (keys %syms) { + if ($app eq "") { next; } + print "Generating dump for App '$app'...\n"; + + $out = "# Generated by op2cg, using OProfile with opreport -gdf\n"; + $out .= "positions: instr line\n"; + + $out .= "events:"; + foreach $e (@events) { $out .= " $e"; } + $out .= "\n"; + + $out .= "summary:"; + foreach $e (1 .. $eventCount) { $out .= " $summary{$app,$e}"; } + $out .= "\n\n"; + + %fileNum = (); + $fileNum = 1; + $sf = ""; + + $img = ""; + + foreach $sym (keys %file) { + if ($sampleCount{$app,$sym} eq "") { next; } + + if ($img{$app,$sym} ne $img) { + $img = $img{$app,$sym}; + $out .= "ob=$img\n"; + } + + $file = $file{$sym}; + if ($sf ne $file) { + if ($fileNum{$file} eq "") { + $fileNum{$file} = $fileNum; + $out .= "fl=($fileNum) $file\n"; + $fileNum++; + } + else { + $out .= "fl=($fileNum{$file})\n"; + } + $sf = $file; + } + + $out .= "fn=$sym\n"; + foreach $sc (1 .. $sampleCount{$app,$sym}) { + if ($sf ne $sFile{$app,$sym,$sc}) { + $sf = $sFile{$app,$sym,$sc}; + if ($sf eq $file) { + $out .= "fe=($fileNum{$file})\n"; + } + else { + if ($fileNum{$sf} eq "") { + $fileNum{$sf} = $fileNum; + $out .= "fi=($fileNum) $sf\n"; + $fileNum++; + } + else { + $out .= "fi=($fileNum{$sf})\n"; + } + } + } + $out .= "0x$vma{$app,$sym,$sc} $linenr{$app,$sym,$sc}"; + foreach $e (1 .. $eventCount) { $c = $cost{"$app,$sym,$sc,$e"} ; $out .= " $c"; } + $out .= "\n"; + } + } + + open OUT, ">oprof.out.$app"; + print OUT $out; + close OUT; +} diff --git a/kcachegrind/converters/pprof2calltree b/kcachegrind/converters/pprof2calltree new file mode 100644 index 00000000..59f8770d --- /dev/null +++ b/kcachegrind/converters/pprof2calltree @@ -0,0 +1,218 @@ +#!/usr/bin/env php +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# - Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - All advertising materials mentioning features or use of this software +# must display the following acknowledgement: This product includes software +# developed by OmniTI Computer Consulting. +# +# - Neither name of the company nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS `AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Copyright (c) 2004 OmniTI Computer Consulting +# All rights reserved +# The following code was written by George Schlossnagle +# and is provided completely free and without any warranty. +# +# This script is designed to convert the pprof output from +# APD (http://pecl.php.net/apd/) to one readable by kcachegrind. To use +# this script: +# +# 1) Install APD. +# 2) Profile your script with APD accordingto the directions in it's +# README file. +# 3) Take the pprof trace file for your script (pprof.XXXXX.Y) and run it +# through this script as follows: +# > pprof2calltree -f pprof.12345.1 +# This creates a new file cachegrind.out.12345.1 +# 4) View your trace with pprof2calltree cachegrind.out.12345.1 + +readPHPArgv(); +array_shift($args); +$shortoptions = 'f:'; +$retval = $con->getopt( $args, $shortoptions); +if(is_object($retval)) { + usage(); +} +foreach ($retval[0] as $kv_array) { + $opt[$kv_array[0]] = $kv_array[1]; +} +if(!$opt['f']) { + usage(); +} +if(!file_exists($opt['f'])) { + print "Trace file ${opt['f']} does not exist\n"; + exit; +} +$IN = fopen($opt['f'], "r"); +if(!$IN) { + print "Trace file ${opt['f']} could not be opened\n"; + exit; +} + +$path_parts = pathinfo($opt['f']); +$outfile = "cachegrind.out.".$path_parts['basename']; +$OUT = fopen($outfile, "w"); +if(!$OUT) { + print "Destination file $outfile could not be opened.\n"; + exit; +} + +while(($line = fgets($IN)) !== false) { + $line = rtrim($line); + if($line == "END_HEADER") { + break; + } +} +$tree = array(); +$callstack = array(); +while(($line = fgets($IN)) !== false) { + $line = rtrim($line); + $args = explode(" ", $line); + if($args[0] == '!') { + $file_lookup[$args[1]] = $args[2]; + } + else if($args[0] == '&') { + $function_lookup[$args[1]] = $args[2]; + $function_type[$args[1]] = ($args[3] == 2)?"USER":"INTERNAL"; + } + else if($args[0] == '+') { + $val = array(function_id => $args[1], + file_id => $args[2], + line => $args[3], + cost => 0); + array_push($callstack, $val); + } + else if($args[0] == '-') { + // retrieve $called to discard + $called = array_pop($callstack); + // retrieve $caller for reference + $caller = array_pop($callstack); + $called_id = $called['function_id']; + + // Set meta data if not already set' + if(!array_key_exists($called_id, $tree)) { + $tree[$called_id] = $called; + // initialize these to 0 + $tree[$called_id]['cost_per_line'] = array(); + } + if($caller !== null) { + $caller['child_calls']++; + $caller_id = $caller['function_id']; + if(!array_key_exists($caller_id, $tree)) { + $tree[$caller_id] = $caller; + } + $caller['cost'] += $called['cost']; + $tree[$caller_id]['called_funcs'][$tree[$caller_id]['call_counter']++][$called_id][$called['file_id']][$called['line']] += $called['cost']; + array_push($callstack, $caller); + } + if(is_array($called['cost_per_line'])) { + foreach($called[cost_per_line] as $file => $lines) { + foreach($lines as $line => $cost) { + $tree[$called_id]['cost_per_line'][$file][$line] += $cost; + } + } + } + } + else if($args[0] == '@') { + $called = array_pop($callstack); + switch(count($args)) { + // support new and old-style pprof data + case 6: + $file = $args[1]; + $line = $args[2]; + $real_tm = $args[5]; + break; + case 4: + $file = $called['file_id']; + $line = $called['line']; + $real_tm = $args[3]; + break; + + } + $called['cost_per_line'][$file][$line] += $real_tm; + $called['cost'] += $real_tm; + $total_cost += $real_tm; + array_push($callstack, $called); + } +} + +ob_start(); +print "events: Tick\n"; +print "summary: $total_cost\n"; +printf("cmd: %s\n", $file_lookup[1]); +print "\n"; + +foreach($tree as $caller => $data) { + $filename = $file_lookup[$data['file_id']]?$file_lookup[$data['file_id']]:"???"; + printf("ob=%s\n", $function_type[$caller]); + printf("fl=%s\n", $filename); + printf("fn=%s\n", $function_lookup[$caller]); + if(is_array($data['cost_per_line'])) { + foreach($data['cost_per_line'] as $file => $lines) { + foreach($lines as $line => $cost) { + print "$line $cost\n"; + } + } + } + else if ($data['cost']) { + printf("COST %s %s\n", $items['line'], $items['cost']); + } + else { + print_r($items); + } + if(is_array($data['called_funcs'])) { + foreach($data['called_funcs'] as $counter => $items) { + foreach($items as $called_id => $costs) { + if(is_array($costs)) { + printf("cfn=%s\n", $function_lookup[$called_id]); + foreach($costs as $file => $lines) { + printf("cfi=%s\ncalls=1\n", $file_lookup[$file]); + foreach($lines as $line => $cost) { + print "$line $cost\n"; + } + } + } + } + } + } + print "\n"; +} +print "\ntotals=$total_cost\n"; +$buffer = ob_get_clean(); +print "Writing kcachegrind compatible output to $outfile\n"; +fwrite($OUT, $buffer); + +function usage() +{ + print << + +EOD; + exit(1); +} +?> diff --git a/kcachegrind/kcachegrind.lsm.in b/kcachegrind/kcachegrind.lsm.in new file mode 100644 index 00000000..fba32141 --- /dev/null +++ b/kcachegrind/kcachegrind.lsm.in @@ -0,0 +1,11 @@ +Begin3 +Title: kcachegrind +Version: @KCACHEGRIND_VERSION@ +Description: KDE Profiling Visualisation Tool +Keywords: Profiling, Performance Analysis, Visualisation, Development +Author: Josef Weidendorfer +Maintained-by: Josef Weidendorfer +Home-page: http://kcachegrind.sourceforge.net +Platforms: Linux and other Unices +Copying-policy: GNU Public License +End diff --git a/kcachegrind/kcachegrind.spec.in b/kcachegrind/kcachegrind.spec.in new file mode 100644 index 00000000..060dd28e --- /dev/null +++ b/kcachegrind/kcachegrind.spec.in @@ -0,0 +1,55 @@ +Summary: KDE Profiling Visualisation Tool +Name: kcachegrind +Version: @KCACHEGRIND_VERSION@ +Release: 1 +Copyright: GPL +Group: Development/Tools +Vendor: (none) +URL: http://kcachegrind.sourceforge.net +Packager: Josef Weidendorfer +Source: kcachegrind-@KCACHEGRIND_VERSION@.tar.gz +BuildRoot: /var/tmp/build + +%description +KCachegrind is a GPL'd tool for quick browsing in and visualisation +of performance data of an application run. This data is produced by +profiling tools and typically includes distribution of cost events +to source code ranges (instructions, source lines, functions, C++ classes) +and call relationship of functions. +KCachegrind has a list of functions sorted according to different cost +types, and can provide various performance views for a function like +direct/indirect callers/callees, TreeMap visualisation of cost distribution +among callees, call graph sectors centered around the function and +annotated source/assembler. +Currently, KCachegrind depends on data delivered by the profiling tool +calltree, powered by the Valgrind runtime instrumentation framework. + +%prep +%setup +CFLAGS="$RPM_OPT_FLAGS" CXXFLAGS="$RPM_OPT_FLAGS" ./configure \ + \ + $LOCALFLAGS +%build +# Setup for parallel builds +numprocs=`egrep -c ^cpu[0-9]+ /proc/stat || :` +if [ "$numprocs" = "0" ]; then + numprocs=1 +fi + +make -j$numprocs + +%install +make install-strip DESTDIR=$RPM_BUILD_ROOT + +cd $RPM_BUILD_ROOT +find . -type d | sed '1,2d;s,^\.,\%attr(-\,root\,root) \%dir ,' > $RPM_BUILD_DIR/file.list.kcachegrind +find . -type f | sed 's,^\.,\%attr(-\,root\,root) ,' >> $RPM_BUILD_DIR/file.list.kcachegrind +find . -type l | sed 's,^\.,\%attr(-\,root\,root) ,' >> $RPM_BUILD_DIR/file.list.kcachegrind + +%clean +rm -rf $RPM_BUILD_ROOT/* +rm -rf $RPM_BUILD_DIR/kcachegrind +rm -rf ../file.list.kcachegrind + + +%files -f ../file.list.kcachegrind diff --git a/kcachegrind/kcachegrind/Doxyfile b/kcachegrind/kcachegrind/Doxyfile new file mode 100644 index 00000000..72db41bc --- /dev/null +++ b/kcachegrind/kcachegrind/Doxyfile @@ -0,0 +1,157 @@ +# Doxygen configuration generated by Doxywizard version 0.1 +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = kcachegrind +PROJECT_NUMBER = +OUTPUT_DIRECTORY = +OUTPUT_LANGUAGE = English +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = YES +HIDE_UNDOC_MEMBERS = +HIDE_UNDOC_CLASSES = +BRIEF_MEMBER_DESC = +REPEAT_BRIEF = +ALWAYS_DETAILED_SEC = +FULL_PATH_NAMES = +STRIP_FROM_PATH = +INTERNAL_DOCS = +CLASS_DIAGRAMS = +SOURCE_BROWSER = +INLINE_SOURCES = +STRIP_CODE_COMMENTS = +CASE_SENSE_NAMES = +SHORT_NAMES = +HIDE_SCOPE_NAMES = +VERBATIM_HEADERS = +SHOW_INCLUDE_FILES = +JAVADOC_AUTOBRIEF = +INHERIT_DOCS = +INLINE_INFO = +SORT_MEMBER_DOCS = +DISTRIBUTE_GROUP_DOC = +TAB_SIZE = +ENABLED_SECTIONS = +GENERATE_TODOLIST = +GENERATE_TESTLIST = +GENERATE_BUGLIST = +ALIASES = +MAX_INITIALIZER_LINES = +OPTIMIZE_OUTPUT_FOR_C = +SHOW_USED_FILES = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = +WARNINGS = +WARN_IF_UNDOCUMENTED = +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = . +FILE_PATTERNS = *.cpp \ + *.h +RECURSIVE = no +EXCLUDE = +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +IMAGE_PATH = +INPUT_FILTER = +FILTER_SOURCE_FILES = +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = +COLS_IN_ALPHA_INDEX = +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = +HTML_OUTPUT = html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = +GENERATE_HTMLHELP = +GENERATE_CHI = +BINARY_TOC = +TOC_EXPAND = +DISABLE_INDEX = +ENUM_VALUES_PER_LINE = +GENERATE_TREEVIEW = +TREEVIEW_WIDTH = +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +COMPACT_LATEX = +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = +USE_PDFLATEX = +LATEX_BATCHMODE = +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = +RTF_HYPERLINKS = +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = +MACRO_EXPANSION = +EXPAND_ONLY_PREDEF = +SEARCH_INCLUDES = +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +HAVE_DOT = +CLASS_GRAPH = +COLLABORATION_GRAPH = +INCLUDE_GRAPH = +INCLUDED_BY_GRAPH = +GRAPHICAL_HIERARCHY = +DOT_PATH = +MAX_DOT_GRAPH_WIDTH = +MAX_DOT_GRAPH_HEIGHT = +GENERATE_LEGEND = +DOT_CLEANUP = +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = +CGI_NAME = search.cgi +CGI_URL = +DOC_URL = +DOC_ABSPATH = +BIN_ABSPATH = /usr/local/bin/ +EXT_DOC_PATHS = diff --git a/kcachegrind/kcachegrind/Makefile.am b/kcachegrind/kcachegrind/Makefile.am new file mode 100644 index 00000000..3b4871aa --- /dev/null +++ b/kcachegrind/kcachegrind/Makefile.am @@ -0,0 +1,62 @@ +bin_PROGRAMS = kcachegrind + +kcachegrind_SOURCES = \ + functionselectionbase.ui \ + stackselectionbase.ui \ + partselectionbase.ui \ + configdlgbase.ui \ + loader.cpp cachegrindloader.cpp treemap.cpp pool.cpp \ + main.cpp configuration.cpp \ + functionselection.cpp coverage.cpp partgraph.cpp \ + toplevel.cpp stackselection.cpp stackbrowser.cpp \ + subcost.cpp tracedata.cpp partselection.cpp configdlg.cpp \ + utils.cpp fixcost.cpp \ + traceitemview.cpp instrview.cpp tabview.cpp \ + sourceview.cpp callmapview.cpp callview.cpp \ + coverageview.cpp costtypeview.cpp partview.cpp \ + listutils.cpp costtypeitem.cpp multiview.cpp \ + callitem.cpp coverageitem.cpp sourceitem.cpp \ + costlistitem.cpp partlistitem.cpp functionitem.cpp \ + instritem.cpp stackitem.cpp callgraphview.cpp + +kcachegrind_COMPILE_FIRST = ../version.h + +kcachegrind_LDADD = $(LIB_KIO) + +KDE_ICON = AUTO + +xdg_apps_DATA = kcachegrind.desktop + +mimeapplicationdir = $(kde_mimedir)/application +mimeapplication_DATA = x-kcachegrind.desktop + +EXTRA_DIST = \ + kcachegrind.desktop \ + x-kcachegrind.desktop \ + hi32-app-kcachegrind.png \ + hi48-app-kcachegrind.png \ + Doxyfile \ + kcachegrindui.rc + +# set the include path for X, qt and KDE +INCLUDES= $(all_includes) + +METASOURCES = AUTO + +# the library search path. +kcachegrind_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +rcdir = $(kde_datadir)/kcachegrind +rc_DATA = kcachegrindui.rc + +tipdir = $(kde_datadir)/kcachegrind +tip_DATA = tips + +messages: rc.cpp + $(PREPARETIPS) > tips.cpp + LIST=`find . -name \*.h -o -name \*.cpp`; \ + if test -n "$$LIST"; then \ + $(XGETTEXT) $$LIST -o $(podir)/kcachegrind.pot; \ + fi + rm -f tips.cpp + diff --git a/kcachegrind/kcachegrind/cachegrindloader.cpp b/kcachegrind/kcachegrind/cachegrindloader.cpp new file mode 100644 index 00000000..912b3bf6 --- /dev/null +++ b/kcachegrind/kcachegrind/cachegrindloader.cpp @@ -0,0 +1,1323 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002, 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include + +#include +#include + +#include +#include + +#include "loader.h" +#include "tracedata.h" +#include "utils.h" +#include "fixcost.h" + + +#define TRACE_LOADER 0 + +/* + * Loader for Callgrind Profile data (format based on Cachegrind format). + * See Callgrind documentation for the file format. + */ + +class CachegrindLoader: public Loader +{ +public: + CachegrindLoader(); + + bool canLoadTrace(QFile* file); + bool loadTrace(TracePart*); + bool isPartOfTrace(QString file, TraceData*); + +private: + bool loadTraceInternal(TracePart*); + + enum lineType { SelfCost, CallCost, BoringJump, CondJump }; + + bool parsePosition(FixString& s, PositionSpec& newPos); + + // position setters + void clearPosition(); + void ensureObject(); + void ensureFile(); + void ensureFunction(); + void setObject(const QString&); + void setCalledObject(const QString&); + void setFile(const QString&); + void setCalledFile(const QString&); + void setFunction(const QString&); + void setCalledFunction(const QString&); + + QString _emptyString; + + // current line in file to read in + QString _filename; + int _lineNo; + + TraceSubMapping* subMapping; + TraceData* _data; + TracePart* _part; + + // current position + lineType nextLineType; + bool hasLineInfo, hasAddrInfo; + PositionSpec currentPos; + + // current function/line + TraceObject* currentObject; + TracePartObject* currentPartObject; + TraceFile* currentFile; + TracePartFile* currentPartFile; + TraceFunction* currentFunction; + TracePartFunction* currentPartFunction; + TraceFunctionSource* currentFunctionSource; + TraceInstr* currentInstr; + TracePartInstr* currentPartInstr; + TraceLine* currentLine; + TracePartLine* currentPartLine; + + // current call + TraceObject* currentCalledObject; + TracePartObject* currentCalledPartObject; + TraceFile* currentCalledFile; + TracePartFile* currentCalledPartFile; + TraceFunction* currentCalledFunction; + TracePartFunction* currentCalledPartFunction; + SubCost currentCallCount; + + // current jump + TraceFile* currentJumpToFile; + TraceFunction* currentJumpToFunction; + PositionSpec targetPos; + SubCost jumpsFollowed, jumpsExecuted; + + /** Support for compressed string format + * This uses the following string compression model + * for objects, files, functions: + * If the name matches + * "() Name": this is a compression specification, + * mapping the integer number to Name and using Name. + * "()" : this is a compression reference. + * Assumes previous compression specification of the + * integer number to a name, uses this name. + * "Name" : Regular name + */ + void clearCompression(); + const QString& checkUnknown(const QString& n); + TraceObject* compressedObject(const QString& name); + TraceFile* compressedFile(const QString& name); + TraceFunction* compressedFunction(const QString& name, + TraceFile*, TraceObject*); + + QPtrVector _objectVector, _fileVector, _functionVector; +}; + + + +/********************************************************** + * Loader + */ + + +CachegrindLoader::CachegrindLoader() + : Loader("Callgrind", + i18n( "Import filter for Cachegrind/Callgrind generated profile data files") ) +{ + _emptyString = QString(""); +} + +bool CachegrindLoader::canLoadTrace(QFile* file) +{ + if (!file) return false; + + if (!file->isOpen()) { + if (!file->open( IO_ReadOnly ) ) { + kdDebug() << QFile::encodeName(_filename) << ": " + << strerror( errno ) << endl; + return false; + } + } + + /* + * We recognize this as cachegrind/callgrind format if in the first + * 2047 bytes we see the string "\nevents:" + */ + char buf[2048]; + int read = file->readBlock(buf,2047); + if (read < 0) + return false; + buf[read] = 0; + + QCString s; + s.setRawData(buf, read+1); + int pos = s.find("events:"); + if (pos>0 && buf[pos-1] != '\n') pos = -1; + s.resetRawData(buf, read+1); + return (pos>=0); +} + +bool CachegrindLoader::loadTrace(TracePart* p) +{ + /* do the loading in a new object so parallel load + * operations do not interfere each other. + */ + CachegrindLoader l; + + /* emit progress signals via the singleton loader */ + connect(&l, SIGNAL(updateStatus(QString, int)), + this, SIGNAL(updateStatus(QString, int))); + + return l.loadTraceInternal(p); +} + +Loader* createCachegrindLoader() +{ + return new CachegrindLoader(); +} + + + +/** + * Return false if this is no position specification + */ +bool CachegrindLoader::parsePosition(FixString& line, + PositionSpec& newPos) +{ + char c; + uint diff; + + if (hasAddrInfo) { + + if (!line.first(c)) return false; + + if (c == '*') { + // nothing changed + line.stripFirst(c); + newPos.fromAddr = currentPos.fromAddr; + newPos.toAddr = currentPos.toAddr; + } + else if (c == '+') { + line.stripFirst(c); + line.stripUInt(diff, false); + newPos.fromAddr = currentPos.fromAddr + diff; + newPos.toAddr = newPos.fromAddr; + } + else if (c == '-') { + line.stripFirst(c); + line.stripUInt(diff, false); + newPos.fromAddr = currentPos.fromAddr - diff; + newPos.toAddr = newPos.fromAddr; + } + else if (c >= '0') { + uint64 v; + line.stripUInt64(v, false); + newPos.fromAddr = Addr(v); + newPos.toAddr = newPos.fromAddr; + } + else return false; + + // Range specification + if (line.first(c)) { + if (c == '+') { + line.stripFirst(c); + line.stripUInt(diff); + newPos.toAddr = newPos.fromAddr + diff; + } + else if ((c == '-') || (c == ':')) { + line.stripFirst(c); + uint64 v; + line.stripUInt64(v); + newPos.toAddr = Addr(v); + } + } + line.stripSpaces(); + +#if TRACE_LOADER + if (newPos.fromAddr == newPos.toAddr) + kdDebug() << " Got Addr " << newPos.fromAddr.toString() << endl; + else + kdDebug() << " Got AddrRange " << newPos.fromAddr.toString() + << ":" << newPos.toAddr.toString() << endl; +#endif + + } + + if (hasLineInfo) { + + if (!line.first(c)) return false; + + if (c > '9') return false; + else if (c == '*') { + // nothing changed + line.stripFirst(c); + newPos.fromLine = currentPos.fromLine; + newPos.toLine = currentPos.toLine; + } + else if (c == '+') { + line.stripFirst(c); + line.stripUInt(diff, false); + newPos.fromLine = currentPos.fromLine + diff; + newPos.toLine = newPos.fromLine; + } + else if (c == '-') { + line.stripFirst(c); + line.stripUInt(diff, false); + if (currentPos.fromLine < diff) { + kdError() << _filename << ":" << _lineNo + << " - Negative line number " + << (int)currentPos.fromLine - (int)diff << endl; + diff = currentPos.fromLine; + } + newPos.fromLine = currentPos.fromLine - diff; + newPos.toLine = newPos.fromLine; + } + else if (c >= '0') { + line.stripUInt(newPos.fromLine, false); + newPos.toLine = newPos.fromLine; + } + else return false; + + // Range specification + if (line.first(c)) { + if (c == '+') { + line.stripFirst(c); + line.stripUInt(diff); + newPos.toLine = newPos.fromLine + diff; + } + else if ((c == '-') || (c == ':')) { + line.stripFirst(c); + line.stripUInt(newPos.toLine); + } + } + line.stripSpaces(); + +#if TRACE_LOADER + if (newPos.fromLine == newPos.toLine) + kdDebug() << " Got Line " << newPos.fromLine << endl; + else + kdDebug() << " Got LineRange " << newPos.fromLine + << ":" << newPos.toLine << endl; +#endif + + } + + return true; +} + +// Support for compressed strings +void CachegrindLoader::clearCompression() +{ + // this doesn't delete previous contained objects + _objectVector.clear(); + _fileVector.clear(); + _functionVector.clear(); + + // reset to reasonable init size. We double lengths if needed. + _objectVector.resize(100); + _fileVector.resize(1000); + _functionVector.resize(10000); +} + +const QString& CachegrindLoader::checkUnknown(const QString& n) +{ + if (n == "???") return _emptyString; + return n; +} + +TraceObject* CachegrindLoader::compressedObject(const QString& name) +{ + if ((name[0] != '(') || !name[1].isDigit()) return _data->object(checkUnknown(name)); + + // compressed format using _objectVector + int p = name.find(')'); + if (p<2) { + kdError() << _filename << ":" << _lineNo + << " - Invalid compressed ELF object ('" + << name << "')" << endl; + return 0; + } + unsigned index = name.mid(1, p-1).toInt(); + TraceObject* o = 0; + p++; + if ((int)name.length()>p) { + while(name.at(p).isSpace()) p++; + + if (_objectVector.size() <= index) { + int newSize = index * 2; +#if TRACE_LOADER + kdDebug() << " CachegrindLoader: objectVector enlarged to " + << newSize << endl; +#endif + _objectVector.resize(newSize); + } + + QString realName = checkUnknown(name.mid(p)); + o = (TraceObject*) _objectVector.at(index); + if (o && (o->name() != realName)) { + kdError() << _filename << ":" << _lineNo + << " - Redefinition of compressed ELF object index " << index + << " (was '" << o->name() + << "') to '" << realName << "'" << endl; + } + + o = _data->object(realName); + _objectVector.insert(index, o); + } + else { + if ((_objectVector.size() <= index) || + ( (o=(TraceObject*)_objectVector.at(index)) == 0)) { + kdError() << _filename << ":" << _lineNo + << " - Undefined compressed ELF object index " << index << endl; + return 0; + } + } + + return o; +} + + +// Note: Callgrind sometimes gives different IDs for same file +// (when references to same source file come from different ELF objects) +TraceFile* CachegrindLoader::compressedFile(const QString& name) +{ + if ((name[0] != '(') || !name[1].isDigit()) return _data->file(checkUnknown(name)); + + // compressed format using _fileVector + int p = name.find(')'); + if (p<2) { + kdError() << _filename << ":" << _lineNo + << " - Invalid compressed file ('" + << name << "')" << endl; + return 0; + } + unsigned int index = name.mid(1, p-1).toUInt(); + TraceFile* f = 0; + p++; + if ((int)name.length()>p) { + while(name.at(p).isSpace()) p++; + + if (_fileVector.size() <= index) { + int newSize = index * 2; +#if TRACE_LOADER + kdDebug() << " CachegrindLoader::fileVector enlarged to " + << newSize << endl; +#endif + _fileVector.resize(newSize); + } + + QString realName = checkUnknown(name.mid(p)); + f = (TraceFile*) _fileVector.at(index); + if (f && (f->name() != realName)) { + kdError() << _filename << ":" << _lineNo + << " - Redefinition of compressed file index " << index + << " (was '" << f->name() + << "') to '" << realName << "'" << endl; + } + + f = _data->file(realName); + _fileVector.insert(index, f); + } + else { + if ((_fileVector.size() <= index) || + ( (f=(TraceFile*)_fileVector.at(index)) == 0)) { + kdError() << _filename << ":" << _lineNo + << " - Undefined compressed file index " << index << endl; + return 0; + } + } + + return f; +} + +// Note: Callgrind gives different IDs even for same function +// when parts of the function are from different source files. +// Thus, it is no error when multiple indexes map to same function. +TraceFunction* CachegrindLoader::compressedFunction(const QString& name, + TraceFile* file, + TraceObject* object) +{ + if ((name[0] != '(') || !name[1].isDigit()) + return _data->function(checkUnknown(name), file, object); + + // compressed format using _functionVector + int p = name.find(')'); + if (p<2) { + kdError() << _filename << ":" << _lineNo + << " - Invalid compressed function ('" + << name << "')" << endl; + return 0; + } + + + unsigned int index = name.mid(1, p-1).toUInt(); + TraceFunction* f = 0; + p++; + if ((int)name.length()>p) { + while(name.at(p).isSpace()) p++; + + if (_functionVector.size() <= index) { + int newSize = index * 2; +#if TRACE_LOADER + kdDebug() << " CachegrindLoader::functionVector enlarged to " + << newSize << endl; +#endif + _functionVector.resize(newSize); + } + + QString realName = checkUnknown(name.mid(p)); + f = (TraceFunction*) _functionVector.at(index); + if (f && (f->name() != realName)) { + kdError() << _filename << ":" << _lineNo + << " - Redefinition of compressed function index " << index + << " (was '" << f->name() + << "') to '" << realName << "'" << endl; + } + + f = _data->function(realName, file, object); + _functionVector.insert(index, f); + +#if TRACE_LOADER + kdDebug() << "compressedFunction: Inserted at Index " << index + << "\n " << f->fullName() + << "\n in " << f->cls()->fullName() + << "\n in " << f->file()->fullName() + << "\n in " << f->object()->fullName() << endl; +#endif + } + else { + if ((_functionVector.size() <= index) || + ( (f=(TraceFunction*)_functionVector.at(index)) == 0)) { + kdError() << _filename << ":" << _lineNo + << " - Undefined compressed function index " + << index << endl; + return 0; + } + + // there was a check if the used function (returned from KCachegrinds + // model) has the same object and file as here given to us, but that was wrong: + // that holds only if we make this assumption on the model... + } + + return f; +} + + +// make sure that a valid object is set, at least dummy with empty name +void CachegrindLoader::ensureObject() +{ + if (currentObject) return; + + currentObject = _data->object(_emptyString); + currentPartObject = currentObject->partObject(_part); +} + +void CachegrindLoader::setObject(const QString& name) +{ + currentObject = compressedObject(name); + if (!currentObject) { + kdError() << _filename << ":" << _lineNo + << " - Invalid object specification, setting to unknown" << endl; + + currentObject = _data->object(_emptyString); + } + + currentPartObject = currentObject->partObject(_part); + currentFunction = 0; + currentPartFunction = 0; +} + +void CachegrindLoader::setCalledObject(const QString& name) +{ + currentCalledObject = compressedObject(name); + + if (!currentCalledObject) { + kdError() << _filename << ":" << _lineNo + << " - Invalid called specification, setting to unknown" << endl; + + currentCalledObject = _data->object(_emptyString); + } + + currentCalledPartObject = currentCalledObject->partObject(_part); +} + + +// make sure that a valid file is set, at least dummy with empty name +void CachegrindLoader::ensureFile() +{ + if (currentFile) return; + + currentFile = _data->file(_emptyString); + currentPartFile = currentFile->partFile(_part); +} + +void CachegrindLoader::setFile(const QString& name) +{ + currentFile = compressedFile(name); + + if (!currentFile) { + kdWarning() << _filename << ":" << _lineNo + << " - Invalid file specification, setting to unknown" << endl; + + currentFile = _data->file(_emptyString); + } + + currentPartFile = currentFile->partFile(_part); + currentLine = 0; + currentPartLine = 0; +} + +void CachegrindLoader::setCalledFile(const QString& name) +{ + currentCalledFile = compressedFile(name); + + if (!currentCalledFile) { + kdError() << _filename << ":" << _lineNo + << " - Invalid called file specification, setting to unknown" << endl; + + currentCalledFile = _data->file(_emptyString); + } + + currentCalledPartFile = currentCalledFile->partFile(_part); +} + +// make sure that a valid function is set, at least dummy with empty name +void CachegrindLoader::ensureFunction() +{ + if (currentFunction) return; + + kdWarning() << _filename << ":" << _lineNo + << " - Function name not set" << endl; + + ensureFile(); + ensureObject(); + + currentFunction = _data->function(_emptyString, + currentFile, + currentObject); + currentPartFunction = currentFunction->partFunction(_part, + currentPartFile, + currentPartObject); +} + +void CachegrindLoader::setFunction(const QString& name) +{ + ensureFile(); + ensureObject(); + + currentFunction = compressedFunction( name, + currentFile, + currentObject); + + if (!currentFunction) { + kdWarning() << _filename << ":" << _lineNo + << " - Invalid function, setting to unknown" << endl; + + currentFunction = _data->function(_emptyString, + currentFile, + currentObject); + } + + currentPartFunction = currentFunction->partFunction(_part, + currentPartFile, + currentPartObject); + + currentFunctionSource = 0; + currentLine = 0; + currentPartLine = 0; +} + +void CachegrindLoader::setCalledFunction(const QString& name) +{ + // if called object/file not set, use current object/file + if (!currentCalledObject) { + currentCalledObject = currentObject; + currentCalledPartObject = currentPartObject; + } + + if (!currentCalledFile) { + // !=0 as functions needs file + currentCalledFile = currentFile; + currentCalledPartFile = currentPartFile; + } + + currentCalledFunction = compressedFunction(name, + currentCalledFile, + currentCalledObject); + if (!currentCalledFunction) { + kdWarning() << _filename << ":" << _lineNo + << " - Invalid called function, setting to unknown" << endl; + + currentCalledFunction = _data->function(_emptyString, + currentCalledFile, + currentCalledObject); + } + + currentCalledPartFunction = + currentCalledFunction->partFunction(_part, + currentCalledPartFile, + currentCalledPartObject); +} + + +void CachegrindLoader::clearPosition() +{ + currentPos = PositionSpec(); + + // current function/line + currentFunction = 0; + currentPartFunction = 0; + currentFunctionSource = 0; + currentFile = 0; + currentPartFile = 0; + currentObject = 0; + currentPartObject = 0; + currentLine = 0; + currentPartLine = 0; + currentInstr = 0; + currentPartInstr = 0; + + // current call + currentCalledObject = 0; + currentCalledPartObject = 0; + currentCalledFile = 0; + currentCalledPartFile = 0; + currentCalledFunction = 0; + currentCalledPartFunction = 0; + currentCallCount = 0; + + // current jump + currentJumpToFile = 0; + currentJumpToFunction = 0; + targetPos = PositionSpec(); + jumpsFollowed = 0; + jumpsExecuted = 0; + + subMapping = 0; +} + + +/** + * The main import function... + */ +bool CachegrindLoader::loadTraceInternal(TracePart* part) +{ + clearCompression(); + clearPosition(); + + _part = part; + _data = part->data(); + QFile* pFile = part->file(); + + if (!pFile) return false; + + _filename = pFile->name(); + + FixFile file(pFile); + if (!file.exists()) { + kdError() << "File doesn't exist\n" << endl; + return false; + } + kdDebug() << "Loading " << _filename << " ..." << endl; + QString statusMsg = i18n("Loading %1").arg(_filename); + int statusProgress = 0; + emit updateStatus(statusMsg,statusProgress); + + +#if USE_FIXCOST + // FixCost Memory Pool + FixPool* pool = _data->fixPool(); +#endif + + _lineNo = 0; + FixString line; + char c; + bool totalsSet = false; + + // current position + nextLineType = SelfCost; + // default if there's no "positions:" line + hasLineInfo = true; + hasAddrInfo = false; + + while (file.nextLine(line)) { + + _lineNo++; + +#if TRACE_LOADER + kdDebug() << "[CachegrindLoader] " << _filename << ":" << _lineNo + << " - '" << QString(line) << "'" << endl; +#endif + + // if we cannot strip a character, this was an empty line + if (!line.first(c)) continue; + + if (c <= '9') { + + if (c == '#') continue; + + // parse position(s) + if (!parsePosition(line, currentPos)) { + kdError() << _filename << ":" << _lineNo + << " - Invalid position specification ('" + << QString(line) << "')" << endl; + continue; + } + + // go through after big switch + } + else { // if (c > '9') + + line.stripFirst(c); + + /* in order of probability */ + switch(c) { + + case 'f': + + // fl=, fi=, fe= + if (line.stripPrefix("l=") || + line.stripPrefix("i=") || + line.stripPrefix("e=")) { + + setFile(line); + continue; + } + + // fn= + if (line.stripPrefix("n=")) { + + setFunction(line); + + // on a new function, update status + int progress = (int)(100.0 * file.current() / file.len() +.5); + if (progress != statusProgress) { + statusProgress = progress; + + /* When this signal is connected, it most probably + * should lead to GUI update. Thus, when multiple + * "long operations" (like file loading) are in progress, + * this can temporarly switch to another operation. + */ + emit updateStatus(statusMsg,statusProgress); + } + + continue; + } + + break; + + case 'c': + // cob= + if (line.stripPrefix("ob=")) { + setCalledObject(line); + continue; + } + + // cfi= / cfl= + if (line.stripPrefix("fl=") || + line.stripPrefix("fi=")) { + setCalledFile(line); + continue; + } + + // cfn= + if (line.stripPrefix("fn=")) { + + setCalledFunction(line); + continue; + } + + // calls= + if (line.stripPrefix("alls=")) { + // ignore long lines... + line.stripUInt64(currentCallCount); + nextLineType = CallCost; + continue; + } + + // cmd: + if (line.stripPrefix("md:")) { + QString command = QString(line).stripWhiteSpace(); + if (!_data->command().isEmpty() && + _data->command() != command) { + + kdWarning() << _filename << ":" << _lineNo + << " - Redefined command, was '" + << _data->command() + << "'" << endl; + } + _data->setCommand(command); + continue; + } + + // creator: + if (line.stripPrefix("reator:")) { + // ignore ... + continue; + } + + break; + + case 'j': + + // jcnd= + if (line.stripPrefix("cnd=")) { + bool valid; + + valid = line.stripUInt64(jumpsFollowed) && + line.stripPrefix("/") && + line.stripUInt64(jumpsExecuted) && + parsePosition(line, targetPos); + + if (!valid) { + kdError() << _filename << ":" << _lineNo + << " - Invalid jcnd line" << endl; + } + else + nextLineType = CondJump; + continue; + } + + if (line.stripPrefix("ump=")) { + bool valid; + + valid = line.stripUInt64(jumpsExecuted) && + parsePosition(line, targetPos); + + if (!valid) { + kdError() << _filename << ":" << _lineNo + << " - Invalid jump line" << endl; + } + else + nextLineType = BoringJump; + continue; + } + + // jfi= + if (line.stripPrefix("fi=")) { + currentJumpToFile = compressedFile(line); + continue; + } + + // jfn= + if (line.stripPrefix("fn=")) { + + if (!currentJumpToFile) { + // !=0 as functions needs file + currentJumpToFile = currentFile; + } + + currentJumpToFunction = + compressedFunction(line, + currentJumpToFile, + currentObject); + continue; + } + + break; + + case 'o': + + // ob= + if (line.stripPrefix("b=")) { + setObject(line); + continue; + } + + break; + + case '#': + continue; + + case 't': + + // totals: + if (line.stripPrefix("otals:")) continue; + + // thread: + if (line.stripPrefix("hread:")) { + part->setThreadID(QString(line).toInt()); + continue; + } + + // timeframe (BB): + if (line.stripPrefix("imeframe (BB):")) { + part->setTimeframe(line); + continue; + } + + break; + + case 'd': + + // desc: + if (line.stripPrefix("esc:")) { + + line.stripSurroundingSpaces(); + + // desc: Trigger: + if (line.stripPrefix("Trigger:")) { + part->setTrigger(line); + } + + continue; + } + break; + + case 'e': + + // events: + if (line.stripPrefix("vents:")) { + subMapping = _data->mapping()->subMapping(line); + part->setFixSubMapping(subMapping); + continue; + } + + // event:[=][:] + if (line.stripPrefix("vent:")) { + line.stripSurroundingSpaces(); + + FixString e, f, l; + if (!line.stripName(e)) { + kdError() << _filename << ":" << _lineNo + << " - Invalid event" << endl; + continue; + } + line.stripSpaces(); + if (!line.stripFirst(c)) continue; + + if (c=='=') f = line.stripUntil(':'); + line.stripSpaces(); + + // add to known cost types + if (line.isEmpty()) line = e; + TraceCostType::add(new TraceCostType(e,line,f)); + continue; + } + break; + + case 'p': + + // part: + if (line.stripPrefix("art:")) { + part->setPartNumber(QString(line).toInt()); + continue; + } + + // pid: + if (line.stripPrefix("id:")) { + part->setProcessID(QString(line).toInt()); + continue; + } + + // positions: + if (line.stripPrefix("ositions:")) { + QString positions(line); + hasLineInfo = (positions.find("line")>=0); + hasAddrInfo = (positions.find("instr")>=0); + continue; + } + break; + + case 'v': + + // version: + if (line.stripPrefix("ersion:")) { + part->setVersion(line); + continue; + } + break; + + case 's': + + // summary: + if (line.stripPrefix("ummary:")) { + if (!subMapping) { + kdError() << "No event line found. Skipping '" << _filename << endl; + return false; + } + + part->totals()->set(subMapping, line); + continue; + } + + case 'r': + + // rcalls= (deprecated) + if (line.stripPrefix("calls=")) { + // handle like normal calls: we need the sum of call count + // recursive cost is discarded in cycle detection + line.stripUInt64(currentCallCount); + nextLineType = CallCost; + + kdDebug() << "WARNING: This trace dump was generated by an old " + "version\n of the call-tree skin. Use a new one!" << endl; + + continue; + } + break; + + default: + break; + } + + kdError() << _filename << ":" << _lineNo + << " - Invalid line '" << c << QString(line) << "'" << endl; + continue; + } + + if (!subMapping) { + kdError() << "No event line found. Skipping '" << _filename << "'" << endl; + return false; + } + + // for a cost line, we always need a current function + ensureFunction(); + + +#if USE_FIXCOST + if (!currentFunctionSource || + (currentFunctionSource->file() != currentFile)) + currentFunctionSource = currentFunction->sourceFile(currentFile, + true); +#else + if (hasAddrInfo) { + if (!currentInstr || + (currentInstr->addr() != currentPos.fromAddr)) { + currentInstr = currentFunction->instr(currentPos.fromAddr, + true); + + if (!currentInstr) { + kdError() << _filename << ":" << _lineNo + << " - Invalid address " + << currentPos.fromAddr.toString() << endl; + + continue; + } + + currentPartInstr = currentInstr->partInstr(part, + currentPartFunction); + } + } + + if (hasLineInfo) { + if (!currentLine || + (currentLine->lineno() != currentPos.fromLine)) { + + currentLine = currentFunction->line(currentFile, + currentPos.fromLine, + true); + currentPartLine = currentLine->partLine(part, + currentPartFunction); + } + if (hasAddrInfo && currentInstr) + currentInstr->setLine(currentLine); + } +#endif + +#if TRACE_LOADER + kdDebug() << _filename << ":" << _lineNo + << endl << " currentInstr " + << (currentInstr ? currentInstr->toString().ascii() : ".") + << endl << " currentLine " + << (currentLine ? currentLine->toString().ascii() : ".") + << "( file " << currentFile->name() << ")" + << endl << " currentFunction " + << currentFunction->prettyName().ascii() + << endl << " currentCalled " + << (currentCalledFunction ? currentCalledFunction->prettyName().ascii() : ".") + << endl; +#endif + + // create cost item + + if (nextLineType == SelfCost) { + +#if USE_FIXCOST + new (pool) FixCost(part, pool, + currentFunctionSource, + currentPos, + currentPartFunction, + line); +#else + if (hasAddrInfo) { + TracePartInstr* partInstr; + partInstr = currentInstr->partInstr(part, currentPartFunction); + + if (hasLineInfo) { + // we need to set back after reading for the line + int l = line.len(); + const char* s = line.ascii(); + + partInstr->addCost(subMapping, line); + line.set(s,l); + } + else + partInstr->addCost(subMapping, line); + } + + if (hasLineInfo) { + TracePartLine* partLine; + partLine = currentLine->partLine(part, currentPartFunction); + partLine->addCost(subMapping, line); + } +#endif + + if (!line.isEmpty()) { + kdError() << _filename << ":" << _lineNo + << " - Garbage at end of cost line ('" + << QString(line) << "')" << endl; + } + } + else if (nextLineType == CallCost) { + nextLineType = SelfCost; + + TraceCall* calling = currentFunction->calling(currentCalledFunction); + TracePartCall* partCalling = + calling->partCall(part, currentPartFunction, + currentCalledPartFunction); + +#if USE_FIXCOST + FixCallCost* fcc; + fcc = new (pool) FixCallCost(part, pool, + currentFunctionSource, + hasLineInfo ? currentPos.fromLine : 0, + hasAddrInfo ? currentPos.fromAddr : Addr(0), + partCalling, + currentCallCount, line); + fcc->setMax(_data->callMax()); +#else + if (hasAddrInfo) { + TraceInstrCall* instrCall; + TracePartInstrCall* partInstrCall; + + instrCall = calling->instrCall(currentInstr); + partInstrCall = instrCall->partInstrCall(part, partCalling); + partInstrCall->addCallCount(currentCallCount); + + if (hasLineInfo) { + // we need to set back after reading for the line + int l = line.len(); + const char* s = line.ascii(); + + partInstrCall->addCost(subMapping, line); + line.set(s,l); + } + else + partInstrCall->addCost(subMapping, line); + + // update maximum of call cost + _data->callMax()->maxCost(partInstrCall); + } + + if (hasLineInfo) { + TraceLineCall* lineCall; + TracePartLineCall* partLineCall; + + lineCall = calling->lineCall(currentLine); + partLineCall = lineCall->partLineCall(part, partCalling); + + partLineCall->addCallCount(currentCallCount); + partLineCall->addCost(subMapping, line); + + // update maximum of call cost + _data->callMax()->maxCost(partLineCall); + } +#endif + currentCalledFile = 0; + currentCalledPartFile = 0; + currentCalledObject = 0; + currentCalledPartObject = 0; + currentCallCount = 0; + + if (!line.isEmpty()) { + kdError() << _filename << ":" << _lineNo + << " - Garbage at end of call cost line ('" + << QString(line) << "')" << endl; + } + } + else { // (nextLineType == BoringJump || nextLineType == CondJump) + + TraceFunctionSource* targetSource; + + if (!currentJumpToFunction) + currentJumpToFunction = currentFunction; + + targetSource = (currentJumpToFile) ? + currentJumpToFunction->sourceFile(currentJumpToFile, true) : + currentFunctionSource; + +#if USE_FIXCOST + new (pool) FixJump(part, pool, + /* source */ + hasLineInfo ? currentPos.fromLine : 0, + hasAddrInfo ? currentPos.fromAddr : 0, + currentPartFunction, + currentFunctionSource, + /* target */ + hasLineInfo ? targetPos.fromLine : 0, + hasAddrInfo ? targetPos.fromAddr : Addr(0), + currentJumpToFunction, + targetSource, + (nextLineType == CondJump), + jumpsExecuted, jumpsFollowed); +#endif + + if (0) { + kdDebug() << _filename << ":" << _lineNo + << " - jump from 0x" << currentPos.fromAddr.toString() + << " (line " << currentPos.fromLine + << ") to 0x" << targetPos.fromAddr.toString() + << " (line " << targetPos.fromLine << ")" << endl; + + if (nextLineType == BoringJump) + kdDebug() << " Boring Jump, count " << jumpsExecuted.pretty() << endl; + else + kdDebug() << " Cond. Jump, followed " << jumpsFollowed.pretty() + << ", executed " << jumpsExecuted.pretty() << endl; + } + + nextLineType = SelfCost; + currentJumpToFunction = 0; + currentJumpToFile = 0; + + if (!line.isEmpty()) { + kdError() << _filename << ":" << _lineNo + << " - Garbage at end of jump cost line ('" + << QString(line) << "')" << endl; + } + + } + } + + + emit updateStatus(statusMsg,100); + + _part->invalidate(); + if (!totalsSet) { + _part->totals()->clear(); + _part->totals()->addCost(_part); + } + + pFile->close(); + + return true; +} + diff --git a/kcachegrind/kcachegrind/callgraphview.cpp b/kcachegrind/kcachegrind/callgraphview.cpp new file mode 100644 index 00000000..f00efd94 --- /dev/null +++ b/kcachegrind/kcachegrind/callgraphview.cpp @@ -0,0 +1,2734 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Callgraph View + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "configuration.h" +#include "callgraphview.h" +#include "toplevel.h" +#include "listutils.h" + + +/* + * TODO: + * - Zooming option for work canvas? (e.g. 1:1 - 1:3) + */ + +#define DEBUG_GRAPH 0 + +// CallGraphView defaults + +#define DEFAULT_FUNCLIMIT .05 +#define DEFAULT_CALLLIMIT .05 +#define DEFAULT_MAXCALLER 2 +#define DEFAULT_MAXCALLING -1 +#define DEFAULT_SHOWSKIPPED false +#define DEFAULT_EXPANDCYCLES false +#define DEFAULT_CLUSTERGROUPS false +#define DEFAULT_DETAILLEVEL 1 +#define DEFAULT_LAYOUT GraphOptions::TopDown +#define DEFAULT_ZOOMPOS Auto + + +// +// GraphEdgeList +// + +GraphEdgeList::GraphEdgeList() + : _sortCallerPos(true) +{} + +int GraphEdgeList::compareItems(Item item1, Item item2) +{ + CanvasEdge* e1 = ((GraphEdge*)item1)->canvasEdge(); + CanvasEdge* e2 = ((GraphEdge*)item2)->canvasEdge(); + + // edges without arrow visualisations are sorted as low + if (!e1) return -1; + if (!e2) return 1; + + int dx1, dy1, dx2, dy2; + int x, y; + if (_sortCallerPos) { + e1->controlPoints().point(0,&x,&y); + e2->controlPoints().point(0,&dx1,&dy1); + dx1 -= x; dy1 -= y; + } + else { + QPointArray a1 = e1->controlPoints(); + QPointArray a2 = e2->controlPoints(); + a1.point(a1.count()-2,&x,&y); + a2.point(a2.count()-1,&dx2,&dy2); + dx2 -= x; dy2 -= y; + } + double at1 = atan2(double(dx1), double(dy1)); + double at2 = atan2(double(dx2), double(dy2)); + + return (at1 < at2) ? 1:-1; +} + + + + +// +// GraphNode +// + +GraphNode::GraphNode() +{ + _f=0; + self = incl = 0; + _cn = 0; + + _visible = false; + _lastCallerIndex = _lastCallingIndex = -1; + + callers.setSortCallerPos(false); + callings.setSortCallerPos(true); + _lastFromCaller = true; +} + +TraceCall* GraphNode::visibleCaller() +{ + if (0) qDebug("GraphNode::visibleCaller %s: last %d, count %d", + _f->prettyName().ascii(), _lastCallerIndex, callers.count()); + + GraphEdge* e = callers.at(_lastCallerIndex); + if (e && !e->isVisible()) e = 0; + if (!e) { + double maxCost = 0.0; + GraphEdge* maxEdge = 0; + int idx = 0; + for(e = callers.first();e; e=callers.next(),idx++) + if (e->isVisible() && (e->cost > maxCost)) { + maxCost = e->cost; + maxEdge = e; + _lastCallerIndex = idx; + } + e = maxEdge; + } + return e ? e->call() : 0; +} + +TraceCall* GraphNode::visibleCalling() +{ + if (0) qDebug("GraphNode::visibleCalling %s: last %d, count %d", + _f->prettyName().ascii(), _lastCallingIndex, callings.count()); + + GraphEdge* e = callings.at(_lastCallingIndex); + if (e && !e->isVisible()) e = 0; + if (!e) { + double maxCost = 0.0; + GraphEdge* maxEdge = 0; + int idx = 0; + for(e = callings.first();e; e=callings.next(),idx++) + if (e->isVisible() && (e->cost > maxCost)) { + maxCost = e->cost; + maxEdge = e; + _lastCallingIndex = idx; + } + e = maxEdge; + } + return e ? e->call() : 0; +} + +void GraphNode::setCalling(GraphEdge* e) +{ + _lastCallingIndex = callings.findRef(e); + _lastFromCaller = false; +} + +void GraphNode::setCaller(GraphEdge* e) +{ + _lastCallerIndex = callers.findRef(e); + _lastFromCaller = true; +} + +TraceFunction* GraphNode::nextVisible() +{ + TraceCall* c; + if (_lastFromCaller) { + c = nextVisibleCaller(callers.at(_lastCallerIndex)); + if (c) return c->called(true); + c = nextVisibleCalling(callings.at(_lastCallingIndex)); + if (c) return c->caller(true); + } + else { + c = nextVisibleCalling(callings.at(_lastCallingIndex)); + if (c) return c->caller(true); + c = nextVisibleCaller(callers.at(_lastCallerIndex)); + if (c) return c->called(true); + } + return 0; +} + +TraceFunction* GraphNode::priorVisible() +{ + TraceCall* c; + if (_lastFromCaller) { + c = priorVisibleCaller(callers.at(_lastCallerIndex)); + if (c) return c->called(true); + c = priorVisibleCalling(callings.at(_lastCallingIndex)); + if (c) return c->caller(true); + } + else { + c = priorVisibleCalling(callings.at(_lastCallingIndex)); + if (c) return c->caller(true); + c = priorVisibleCaller(callers.at(_lastCallerIndex)); + if (c) return c->called(true); + } + return 0; +} + +TraceCall* GraphNode::nextVisibleCaller(GraphEdge* last) +{ + GraphEdge* e; + bool found = false; + int idx = 0; + for(e = callers.first();e; e=callers.next(),idx++) { + if (found && e->isVisible()) { + _lastCallerIndex = idx; + return e->call(); + } + if (e == last) found = true; + } + return 0; +} + +TraceCall* GraphNode::nextVisibleCalling(GraphEdge* last) +{ + GraphEdge* e; + bool found = false; + int idx = 0; + for(e = callings.first();e; e=callings.next(),idx++) { + if (found && e->isVisible()) { + _lastCallingIndex = idx; + return e->call(); + } + if (e == last) found = true; + } + return 0; +} + +TraceCall* GraphNode::priorVisibleCaller(GraphEdge* last) +{ + GraphEdge *e, *prev = 0; + int prevIdx = -1, idx = 0; + for(e = callers.first(); e; e=callers.next(),idx++) { + if (e == last) { + _lastCallerIndex = prevIdx; + return prev ? prev->call() : 0; + } + if (e->isVisible()) { + prev = e; + prevIdx = idx; + } + } + return 0; +} + +TraceCall* GraphNode::priorVisibleCalling(GraphEdge* last) +{ + GraphEdge *e, *prev = 0; + int prevIdx = -1, idx = 0; + for(e = callings.first(); e; e=callings.next(),idx++) { + if (e == last) { + _lastCallingIndex = prevIdx; + return prev ? prev->call() : 0; + } + if (e->isVisible()) { + prev = e; + prevIdx = idx; + } + } + return 0; +} + +// +// GraphEdge +// + +GraphEdge::GraphEdge() +{ + _c=0; + _from = _to = 0; + _fromNode = _toNode = 0; + cost = count = 0; + _ce = 0; + + _visible = false; + _lastFromCaller = true; +} + +QString GraphEdge::prettyName() +{ + if (_c) return _c->prettyName(); + if (_from) return i18n("Call(s) from %1").arg(_from->prettyName()); + if (_to) return i18n("Call(s) to %1").arg(_to->prettyName()); + return i18n("(unknown call)"); +} + + +TraceFunction* GraphEdge::visibleCaller() +{ + if (_from) { + _lastFromCaller = true; + if (_fromNode) _fromNode->setCalling(this); + return _from; + } + return 0; +} + +TraceFunction* GraphEdge::visibleCalling() +{ + if (_to) { + _lastFromCaller = false; + if (_toNode) _toNode->setCaller(this); + return _to; + } + return 0; +} + +TraceCall* GraphEdge::nextVisible() +{ + TraceCall* res = 0; + + if (_lastFromCaller && _fromNode) { + res = _fromNode->nextVisibleCalling(this); + if (!res && _toNode) + res = _toNode->nextVisibleCaller(this); + } + else if (_toNode) { + res = _toNode->nextVisibleCaller(this); + if (!res && _fromNode) + res = _fromNode->nextVisibleCalling(this); + } + return res; +} + +TraceCall* GraphEdge::priorVisible() +{ + TraceCall* res = 0; + + if (_lastFromCaller && _fromNode) { + res = _fromNode->priorVisibleCalling(this); + if (!res && _toNode) + res = _toNode->priorVisibleCaller(this); + } + else if (_toNode) { + res = _toNode->priorVisibleCaller(this); + if (!res && _fromNode) + res = _fromNode->priorVisibleCalling(this); + } + return res; +} + + + +// +// GraphOptions +// + +QString GraphOptions::layoutString(Layout l) +{ + if (l == Circular) return QString("Circular"); + if (l == LeftRight) return QString("LeftRight"); + return QString("TopDown"); +} + +GraphOptions::Layout GraphOptions::layout(QString s) +{ + if (s == QString("Circular")) return Circular; + if (s == QString("LeftRight")) return LeftRight; + return TopDown; +} + + +// +// StorableGraphOptions +// + +StorableGraphOptions::StorableGraphOptions() +{ + // default options + _funcLimit = DEFAULT_FUNCLIMIT; + _callLimit = DEFAULT_CALLLIMIT; + _maxCallerDepth = DEFAULT_MAXCALLER; + _maxCallingDepth = DEFAULT_MAXCALLING; + _showSkipped = DEFAULT_SHOWSKIPPED; + _expandCycles = DEFAULT_EXPANDCYCLES; + _detailLevel = DEFAULT_DETAILLEVEL; + _layout = DEFAULT_LAYOUT; +} + + + + +// +// GraphExporter +// + +GraphExporter::GraphExporter() +{ + _go = this; + _tmpFile = 0; + _item = 0; + reset(0, 0, 0, TraceItem::NoCostType, QString::null); +} + + +GraphExporter::GraphExporter(TraceData* d, TraceFunction* f, TraceCostType* ct, + TraceItem::CostType gt, QString filename) +{ + _go = this; + _tmpFile = 0; + _item = 0; + reset(d, f, ct, gt, filename); +} + + +GraphExporter::~GraphExporter() +{ + if (_item && _tmpFile) { +#if DEBUG_GRAPH + _tmpFile->unlink(); +#endif + delete _tmpFile; + } +} + + +void GraphExporter::reset(TraceData*, TraceItem* i, TraceCostType* ct, + TraceItem::CostType gt, QString filename) +{ + _graphCreated = false; + _nodeMap.clear(); + _edgeMap.clear(); + + if (_item && _tmpFile) { + _tmpFile->unlink(); + delete _tmpFile; + } + + if (i) { + switch(i->type()) { + case TraceItem::Function: + case TraceItem::FunctionCycle: + case TraceItem::Call: + break; + default: + i = 0; + } + } + + _item = i; + _costType = ct; + _groupType = gt; + if (!i) return; + + if (filename.isEmpty()) { + _tmpFile = new KTempFile(QString::null, ".dot"); + _dotName = _tmpFile->name(); + _useBox = true; + } + else { + _tmpFile = 0; + _dotName = filename; + _useBox = false; + } +} + + + +void GraphExporter::setGraphOptions(GraphOptions* go) +{ + if (go == 0) go = this; + _go = go; +} + +void GraphExporter::createGraph() +{ + if (!_item) return; + if (_graphCreated) return; + _graphCreated = true; + + if ((_item->type() == TraceItem::Function) || + (_item->type() == TraceItem::FunctionCycle)) { + TraceFunction* f = (TraceFunction*) _item; + + double incl = f->inclusive()->subCost(_costType); + _realFuncLimit = incl * _go->funcLimit(); + _realCallLimit = incl * _go->callLimit(); + + buildGraph(f, 0, true, 1.0); // down to callings + + // set costs of function back to 0, as it will be added again + GraphNode& n = _nodeMap[f]; + n.self = n.incl = 0.0; + + buildGraph(f, 0, false, 1.0); // up to callers + } + else { + TraceCall* c = (TraceCall*) _item; + + double incl = c->subCost(_costType); + _realFuncLimit = incl * _go->funcLimit(); + _realCallLimit = incl * _go->callLimit(); + + // create edge + TraceFunction *caller, *called; + caller = c->caller(false); + called = c->called(false); + QPair p(caller, called); + GraphEdge& e = _edgeMap[p]; + e.setCall(c); + e.setCaller(p.first); + e.setCalling(p.second); + e.cost = c->subCost(_costType); + e.count = c->callCount(); + + SubCost s = called->inclusive()->subCost(_costType); + buildGraph(called, 0, true, e.cost / s); // down to callings + s = caller->inclusive()->subCost(_costType); + buildGraph(caller, 0, false, e.cost / s); // up to callers + } +} + +void GraphExporter::writeDot() +{ + if (!_item) return; + + QFile* file = 0; + QTextStream* stream = 0; + + if (_tmpFile) + stream = _tmpFile->textStream(); + else { + file = new QFile(_dotName); + if ( !file->open( IO_WriteOnly ) ) { + kdError() << "Can't write dot file '" << _dotName << "'" << endl; + return; + } + stream = new QTextStream(file); + } + + if (!_graphCreated) createGraph(); + + /* Generate dot format... + * When used for the CallGraphView (in contrast to "Export Callgraph..."), + * the labels are only dummy placeholders to reserve space for our own + * drawings. + */ + + *stream << "digraph \"callgraph\" {\n"; + + if (_go->layout() == LeftRight) { + *stream << QString(" rankdir=LR;\n"); + } + else if (_go->layout() == Circular) { + TraceFunction *f = 0; + switch(_item->type()) { + case TraceItem::Function: + case TraceItem::FunctionCycle: + f = (TraceFunction*) _item; + break; + case TraceItem::Call: + f = ((TraceCall*)_item)->caller(true); + break; + default: + break; + } + if (f) + *stream << QString(" center=F%1;\n").arg((long)f, 0, 16); + *stream << QString(" overlap=false;\n splines=true;\n"); + } + + // for clustering + QMap > nLists; + + GraphNodeMap::Iterator nit; + for ( nit = _nodeMap.begin(); + nit != _nodeMap.end(); ++nit ) { + GraphNode& n = *nit; + + if (n.incl <= _realFuncLimit) continue; + + // for clustering: get cost item group of function + TraceCostItem* g; + TraceFunction* f = n.function(); + switch(_groupType) { + case TraceItem::Object: g = f->object(); break; + case TraceItem::Class: g = f->cls(); break; + case TraceItem::File: g = f->file(); break; + case TraceItem::FunctionCycle: g = f->cycle(); break; + default: g = 0; break; + } + nLists[g].append(&n); + } + + QMap >::Iterator lit; + int cluster = 0; + for ( lit = nLists.begin(); + lit != nLists.end(); ++lit, cluster++ ) { + QPtrList& l = lit.data(); + TraceCostItem* i = lit.key(); + + if (_go->clusterGroups() && i) { + QString iabr = i->prettyName(); + if ((int)iabr.length() > Configuration::maxSymbolLength()) + iabr = iabr.left(Configuration::maxSymbolLength()) + "..."; + + *stream << QString("subgraph \"cluster%1\" { label=\"%2\";\n") + .arg(cluster).arg(iabr); + } + + GraphNode* np; + for(np = l.first(); np; np = l.next() ) { + TraceFunction* f = np->function(); + + QString abr = f->prettyName(); + if ((int)abr.length() > Configuration::maxSymbolLength()) + abr = abr.left(Configuration::maxSymbolLength()) + "..."; + + *stream << QString(" F%1 [").arg((long)f, 0, 16); + if (_useBox) { + // make label 3 lines for CallGraphView + *stream << QString("shape=box,label=\"** %1 **\\n**\\n%2\"];\n") + .arg(abr) + .arg(SubCost(np->incl).pretty()); + } + else + *stream << QString("label=\"%1\\n%2\"];\n") + .arg(abr) + .arg(SubCost(np->incl).pretty()); + } + + if (_go->clusterGroups() && i) + *stream << QString("}\n"); + } + + GraphEdgeMap::Iterator eit; + for ( eit = _edgeMap.begin(); + eit != _edgeMap.end(); ++eit ) { + GraphEdge& e = *eit; + + if (e.cost < _realCallLimit) continue; + if (!_go->expandCycles()) { + // don't show inner cycle calls + if (e.call()->inCycle()>0) continue; + } + + + GraphNode& from = _nodeMap[e.from()]; + GraphNode& to = _nodeMap[e.to()]; + + e.setCallerNode(&from); + e.setCallingNode(&to); + + if ((from.incl <= _realFuncLimit) || + (to.incl <= _realFuncLimit)) continue; + + // remove dumped edges from n.callers/n.callings + from.callings.removeRef(&e); + to.callers.removeRef(&e); + from.callingSet.remove(&e); + to.callerSet.remove(&e); + + *stream << QString(" F%1 -> F%2 [weight=%3") + .arg((long)e.from(), 0, 16) + .arg((long)e.to(), 0, 16) + .arg((long)log(log(e.cost))); + + if (_go->detailLevel() ==1) + *stream << QString(",label=\"%1\"") + .arg(SubCost(e.cost).pretty()); + else if (_go->detailLevel() ==2) + *stream << QString(",label=\"%3\\n%4 x\"") + .arg(SubCost(e.cost).pretty()) + .arg(SubCost(e.count).pretty()); + + *stream << QString("];\n"); + } + + if (_go->showSkipped()) { + + // Create sum-edges for skipped edges + GraphEdge* e; + double costSum, countSum; + for ( nit = _nodeMap.begin(); + nit != _nodeMap.end(); ++nit ) { + GraphNode& n = *nit; + if (n.incl <= _realFuncLimit) continue; + + costSum = countSum = 0.0; + for (e=n.callers.first();e;e=n.callers.next()) { + costSum += e->cost; + countSum += e->count; + } + if (costSum > _realCallLimit) { + + QPair p(0, n.function()); + e = &(_edgeMap[p]); + e->setCalling(p.second); + e->cost = costSum; + e->count = countSum; + + *stream << QString(" R%1 [shape=point,label=\"\"];\n") + .arg((long)n.function(), 0, 16); + *stream << QString(" R%1 -> F%2 [label=\"%3\\n%4 x\",weight=%5];\n") + .arg((long)n.function(), 0, 16) + .arg((long)n.function(), 0, 16) + .arg(SubCost(costSum).pretty()) + .arg(SubCost(countSum).pretty()) + .arg((int)log(costSum)); + } + + costSum = countSum = 0.0; + for (e=n.callings.first();e;e=n.callings.next()) { + costSum += e->cost; + countSum += e->count; + } + if (costSum > _realCallLimit) { + + QPair p(n.function(), 0); + e = &(_edgeMap[p]); + e->setCaller(p.first); + e->cost = costSum; + e->count = countSum; + + *stream << QString(" S%1 [shape=point,label=\"\"];\n") + .arg((long)n.function(), 0, 16); + *stream << QString(" F%1 -> S%2 [label=\"%3\\n%4 x\",weight=%5];\n") + .arg((long)n.function(), 0, 16) + .arg((long)n.function(), 0, 16) + .arg(SubCost(costSum).pretty()) + .arg(SubCost(countSum).pretty()) + .arg((int)log(costSum)); + } + } + } + + // clear edges here completely. + // Visible edges are inserted again on parsing in CallGraphView::refresh + for ( nit = _nodeMap.begin(); + nit != _nodeMap.end(); ++nit ) { + GraphNode& n = *nit; + n.callers.clear(); + n.callings.clear(); + n.callerSet.clear(); + n.callingSet.clear(); + } + + *stream << "}\n"; + + if (_tmpFile) { + _tmpFile->close(); + } + else { + file->close(); + delete file; + delete stream; + } +} + +void GraphExporter::sortEdges() +{ + GraphNodeMap::Iterator nit; + for ( nit = _nodeMap.begin(); + nit != _nodeMap.end(); ++nit ) { + GraphNode& n = *nit; + + n.callers.sort(); + n.callings.sort(); + } +} + +TraceFunction* GraphExporter::toFunc(QString s) +{ + if (s[0] != 'F') return 0; + bool ok; + TraceFunction* f = (TraceFunction*) s.mid(1).toULong(&ok, 16); + if (!ok) return 0; + + return f; +} + +GraphNode* GraphExporter::node(TraceFunction* f) +{ + if (!f) return 0; + + GraphNodeMap::Iterator it = _nodeMap.find(f); + if (it == _nodeMap.end()) return 0; + + return &(*it); +} + +GraphEdge* GraphExporter::edge(TraceFunction* f1, TraceFunction* f2) +{ + GraphEdgeMap::Iterator it = _edgeMap.find(qMakePair(f1, f2)); + if (it == _edgeMap.end()) return 0; + + return &(*it); +} + + +/** + * We do a DFS and don't stop on already visited nodes/edges, + * but add up costs. We only stop if limits/max depth is reached. + * + * For a node/edge, it can happen that the first time visited the + * cost will below the limit, so the search is stopped. + * If on a further visit of the node/edge the limit is reached, + * we use the whole node/edge cost and continue search. + */ +void GraphExporter::buildGraph(TraceFunction* f, int d, + bool toCallings, double factor) +{ +#if DEBUG_GRAPH + kdDebug() << "buildGraph(" << f->prettyName() << "," << d << "," << factor + << ") [to " << (toCallings ? "Callings":"Callers") << "]" << endl; +#endif + + double oldIncl = 0.0; + GraphNode& n = _nodeMap[f]; + if (n.function() == 0) { + n.setFunction(f); + } + else + oldIncl = n.incl; + + double incl = f->inclusive()->subCost(_costType) * factor; + n.incl += incl; + n.self += f->subCost(_costType) * factor; + if (0) qDebug(" Added Incl. %f, now %f", incl, n.incl); + + // A negative depth limit means "unlimited" + int maxDepth = toCallings ? _go->maxCallingDepth() : _go->maxCallerDepth(); + if ((maxDepth>=0) && (d >= maxDepth)) { + if (0) qDebug(" Cutoff, max depth reached"); + return; + } + + // if we just reached the limit by summing, do a DFS + // from here with full incl. cost because of previous cutoffs + if ((n.incl >= _realFuncLimit) && (oldIncl < _realFuncLimit)) incl = n.incl; + + if (f->cycle()) { + // for cycles members, we never stop on first visit, but always on 2nd + // note: a 2nd visit never should happen, as we don't follow inner-cycle + // calls + if (oldIncl > 0.0) { + if (0) qDebug(" Cutoff, 2nd visit to Cycle Member"); + // and takeback cost addition, as it's added twice + n.incl = oldIncl; + n.self -= f->subCost(_costType) * factor; + return; + } + } + else if (incl <= _realFuncLimit) { + if (0) qDebug(" Cutoff, below limit"); + return; + } + + TraceCall* call; + TraceFunction* f2; + + + // on entering a cycle, only go the FunctionCycle + TraceCallList l = toCallings ? + f->callings(false) : f->callers(false); + + for (call=l.first();call;call=l.next()) { + + f2 = toCallings ? call->called(false) : call->caller(false); + + double count = call->callCount() * factor; + double cost = call->subCost(_costType) * factor; + + // ignore function calls with absolute cost < 3 per call + // No: This would skip a lot of functions e.g. with L2 cache misses + // if (count>0.0 && (cost/count < 3)) continue; + + double oldCost = 0.0; + QPair p(toCallings ? f:f2, + toCallings ? f2:f); + GraphEdge& e = _edgeMap[p]; + if (e.call() == 0) { + e.setCall(call); + e.setCaller(p.first); + e.setCalling(p.second); + } + else + oldCost = e.cost; + + e.cost += cost; + e.count += count; + if (0) qDebug(" Edge to %s, added cost %f, now %f", + f2->prettyName().ascii(), cost, e.cost); + + // if this call goes into a FunctionCycle, we also show the real call + if (f2->cycle() == f2) { + TraceFunction* realF; + realF = toCallings ? call->called(true) : call->caller(true); + QPair realP(toCallings ? f:realF, + toCallings ? realF:f); + GraphEdge& e = _edgeMap[realP]; + if (e.call() == 0) { + e.setCall(call); + e.setCaller(realP.first); + e.setCalling(realP.second); + } + e.cost += cost; + e.count += count; + } + + // - don't do a DFS on calls in recursion/cycle + if (call->inCycle()>0) continue; + if (call->isRecursion()) continue; + + if (toCallings) { + GraphEdgeSet::Iterator it = n.callingSet.find(&e); + if (it == n.callingSet.end()) { + n.callings.append(&e); + n.callingSet.insert(&e, 1 ); + } + } + else { + GraphEdgeSet::Iterator it = n.callerSet.find(&e); + if (it == n.callerSet.end()) { + n.callers.append(&e); + n.callerSet.insert(&e, 1 ); + } + } + + // if we just reached the call limit (=func limit by summing, do a DFS + // from here with full incl. cost because of previous cutoffs + if ((e.cost >= _realCallLimit) && (oldCost < _realCallLimit)) cost = e.cost; + if (cost < _realCallLimit) { + if (0) qDebug(" Edge Cutoff, limit not reached"); + continue; + } + + SubCost s; + if (call->inCycle()) + s = f2->cycle()->inclusive()->subCost(_costType); + else + s = f2->inclusive()->subCost(_costType); + SubCost v = call->subCost(_costType); + buildGraph(f2, d+1, toCallings, factor * v / s); + } +} + + +// +// PannerView +// +PannerView::PannerView(QWidget * parent, const char * name) + : QCanvasView(parent, name, WNoAutoErase | WStaticContents) +{ + _movingZoomRect = false; + + // why doesn't this avoid flicker ? + viewport()->setBackgroundMode(Qt::NoBackground); + setBackgroundMode(Qt::NoBackground); +} + +void PannerView::setZoomRect(QRect r) +{ + QRect oldRect = _zoomRect; + _zoomRect = r; + updateContents(oldRect); + updateContents(_zoomRect); +} + +void PannerView::drawContents(QPainter * p, int clipx, int clipy, int clipw, int cliph) +{ + // save/restore around QCanvasView::drawContents seems to be needed + // for QT 3.0 to get the red rectangle drawn correct + p->save(); + QCanvasView::drawContents(p,clipx,clipy,clipw,cliph); + p->restore(); + if (_zoomRect.isValid()) { + p->setPen(red.dark()); + p->drawRect(_zoomRect); + p->setPen(red); + p->drawRect(QRect(_zoomRect.x()+1, _zoomRect.y()+1, + _zoomRect.width()-2, _zoomRect.height()-2)); + } +} + +void PannerView::contentsMousePressEvent(QMouseEvent* e) +{ + if (_zoomRect.isValid()) { + if (!_zoomRect.contains(e->pos())) + emit zoomRectMoved(e->pos().x() - _zoomRect.center().x(), + e->pos().y() - _zoomRect.center().y()); + + _movingZoomRect = true; + _lastPos = e->pos(); + } +} + +void PannerView::contentsMouseMoveEvent(QMouseEvent* e) +{ + if (_movingZoomRect) { + emit zoomRectMoved(e->pos().x() - _lastPos.x(), e->pos().y() - _lastPos.y()); + _lastPos = e->pos(); + } +} + +void PannerView::contentsMouseReleaseEvent(QMouseEvent*) +{ + _movingZoomRect = false; + emit zoomRectMoveFinished(); +} + + + + + +// +// CanvasNode +// + +CanvasNode::CanvasNode(CallGraphView* v, GraphNode* n, + int x, int y, int w, int h, QCanvas* c) + : QCanvasRectangle(x, y, w, h, c), _node(n), _view(v) +{ + setPosition(0, DrawParams::TopCenter); + setPosition(1, DrawParams::BottomCenter); + + updateGroup(); + + if (!_node || !_view) return; + + if (_node->function()) + setText(0, _node->function()->prettyName()); + + TraceCost* totalCost; + if (_view->topLevel()->showExpanded()) { + if (_view->activeFunction()) { + if (_view->activeFunction()->cycle()) + totalCost = _view->activeFunction()->cycle()->inclusive(); + else + totalCost = _view->activeFunction()->inclusive(); + } + else + totalCost = (TraceCost*) _view->activeItem(); + } + else + totalCost = _view->data(); + double total = totalCost->subCost(_view->costType()); + double inclP = 100.0 * n->incl / total; + if (_view->topLevel()->showPercentage()) + setText(1, QString("%1 %") + .arg(inclP, 0, 'f', Configuration::percentPrecision())); + else + setText(1, SubCost(n->incl).pretty()); + setPixmap(1, percentagePixmap(25,10,(int)(inclP+.5), Qt::blue, true)); +} + +void CanvasNode::setSelected(bool s) +{ + StoredDrawParams::setSelected(s); + update(); +} + +void CanvasNode::updateGroup() +{ + if (!_view || !_node) return; + + QColor c = Configuration::functionColor(_view->groupType(), + _node->function()); + setBackColor(c); + update(); +} + +void CanvasNode::drawShape(QPainter& p) +{ + QRect r = rect(), origRect = r; + + r.setRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2); + + RectDrawing d(r); + d.drawBack(&p, this); + r.setRect(r.x()+2, r.y()+2, r.width()-4, r.height()-4); + + if (StoredDrawParams::selected() && _view->hasFocus()) { + _view->style().drawPrimitive( QStyle::PE_FocusRect, &p, r, + _view->colorGroup()); + } + + // draw afterwards to always get a frame even when zoomed + p.setPen(StoredDrawParams::selected() ? red : black); + p.drawRect(origRect); + + d.setRect(r); + d.drawField(&p, 0, this); + d.drawField(&p, 1, this); +} + + +// +// CanvasEdgeLabel +// + +CanvasEdgeLabel::CanvasEdgeLabel(CallGraphView* v, CanvasEdge* ce, + int x, int y, int w, int h, QCanvas* c) + : QCanvasRectangle(x, y, w, h, c), _ce(ce), _view(v) +{ + GraphEdge* e = ce->edge(); + if (!e) return; + + setPosition(1, DrawParams::TopCenter); + setText(1, QString("%1 x").arg(SubCost(e->count).pretty())); + + setPosition(0, DrawParams::BottomCenter); + + TraceCost* totalCost; + if (_view->topLevel()->showExpanded()) { + if (_view->activeFunction()) { + if (_view->activeFunction()->cycle()) + totalCost = _view->activeFunction()->cycle()->inclusive(); + else + totalCost = _view->activeFunction()->inclusive(); + } + else + totalCost = (TraceCost*) _view->activeItem(); + } + else + totalCost = _view->data(); + double total = totalCost->subCost(_view->costType()); + double inclP = 100.0 * e->cost / total; + if (_view->topLevel()->showPercentage()) + setText(0, QString("%1 %") + .arg(inclP, 0, 'f', Configuration::percentPrecision())); + else + setText(0, SubCost(e->cost).pretty()); + setPixmap(0, percentagePixmap(25,10,(int)(inclP+.5), Qt::blue, true)); + + if (e->call() && (e->call()->isRecursion() || e->call()->inCycle())) { + QString icon = "undo"; + KIconLoader* loader = KApplication::kApplication()->iconLoader(); + QPixmap p= loader->loadIcon(icon, KIcon::Small, 0, + KIcon::DefaultState, 0, true); + setPixmap(0, p); + } +} + +void CanvasEdgeLabel::drawShape(QPainter& p) +{ + QRect r = rect(); + //p.setPen(blue); + //p.drawRect(r); + RectDrawing d(r); + d.drawField(&p, 0, this); + d.drawField(&p, 1, this); +} + +// +// CanvasEdgeArrow + +CanvasEdgeArrow::CanvasEdgeArrow(CanvasEdge* ce, QCanvas* c) + : QCanvasPolygon(c), _ce(ce) +{} + +void CanvasEdgeArrow::drawShape(QPainter& p) +{ + if (_ce->isSelected()) p.setBrush(Qt::red); + + QCanvasPolygon::drawShape(p); +} + +// +// CanvasEdge +// + +CanvasEdge::CanvasEdge(GraphEdge* e, QCanvas* c) + : QCanvasSpline(c), _edge(e) +{ + _label = 0; + _arrow = 0; +} + +void CanvasEdge::setSelected(bool s) +{ + QCanvasItem::setSelected(s); + update(); + if (_arrow) _arrow->setSelected(s); +} + +QPointArray CanvasEdge::areaPoints() const +{ + int minX = poly[0].x(), minY = poly[0].y(); + int maxX = minX, maxY = minY; + int i; + + if (0) qDebug("CanvasEdge::areaPoints\n P 0: %d/%d", minX, minY); + int len = poly.count(); + for (i=1;i maxX) maxX = poly[i].x(); + if (poly[i].y() > maxY) maxY = poly[i].y(); + if (0) qDebug(" P %d: %d/%d", i, poly[i].x(), poly[i].y()); + } + QPointArray a = poly.copy(), b = poly.copy(); + if (minX == maxX) { + a.translate(-2, 0); + b.translate(2, 0); + } + else { + a.translate(0, -2); + b.translate(0, 2); + } + a.resize(2*len); + for (i=0;iv2) { + r.setRect(r.x()-d, r.y()-d, r.width()+2*d, r.height()+2*d); + v /= f; + } + + _p = new QPixmap(r.size()); + _p->fill(Qt::white); + QPainter p(_p); + p.setPen(Qt::NoPen); + + r.moveBy(-r.x(), -r.y()); + + while (vwidth(), _p->height()); + move(n->rect().center().x()-_p->width()/2, + n->rect().center().y()-_p->height()/2); +} + + +void CanvasFrame::drawShape(QPainter& p) +{ + p.drawPixmap( int(x()), int(y()), *_p ); +} + + + + +// +// Tooltips for CallGraphView +// + +class CallGraphTip: public QToolTip +{ +public: + CallGraphTip( QWidget* p ):QToolTip(p) {} + +protected: + void maybeTip( const QPoint & ); +}; + +void CallGraphTip::maybeTip( const QPoint& pos ) +{ + if (!parentWidget()->inherits( "CallGraphView" )) return; + CallGraphView* cgv = (CallGraphView*)parentWidget(); + + QPoint cPos = cgv->viewportToContents(pos); + + if (0) qDebug("CallGraphTip for (%d/%d) -> (%d/%d) ?", + pos.x(), pos.y(), cPos.x(), cPos.y()); + + QCanvasItemList l = cgv->canvas()->collisions(cPos); + if (l.count() == 0) return; + QCanvasItem* i = l.first(); + + if (i->rtti() == CANVAS_NODE) { + CanvasNode* cn = (CanvasNode*)i; + GraphNode* n = cn->node(); + if (0) qDebug("CallGraphTip: Mouse on Node '%s'", + n->function()->prettyName().ascii()); + + QString tipStr = QString("%1 (%2)").arg(cn->text(0)).arg(cn->text(1)); + QPoint vPosTL = cgv->contentsToViewport(i->boundingRect().topLeft()); + QPoint vPosBR = cgv->contentsToViewport(i->boundingRect().bottomRight()); + tip(QRect(vPosTL, vPosBR), tipStr); + + return; + } + + // redirect from label / arrow to edge + if (i->rtti() == CANVAS_EDGELABEL) + i = ((CanvasEdgeLabel*)i)->canvasEdge(); + if (i->rtti() == CANVAS_EDGEARROW) + i = ((CanvasEdgeArrow*)i)->canvasEdge(); + + if (i->rtti() == CANVAS_EDGE) { + CanvasEdge* ce = (CanvasEdge*)i; + GraphEdge* e = ce->edge(); + if (0) qDebug("CallGraphTip: Mouse on Edge '%s'", + e->prettyName().ascii()); + + QString tipStr; + if (!ce->label()) + tipStr = e->prettyName(); + else + tipStr = QString("%1 (%2)") + .arg(ce->label()->text(0)).arg(ce->label()->text(1)); + tip(QRect(pos.x()-5,pos.y()-5,pos.x()+5,pos.y()+5), tipStr); + } +} + + + + +// +// CallGraphView +// +CallGraphView::CallGraphView(TraceItemView* parentView, + QWidget* parent, const char* name) + : QCanvasView(parent, name), TraceItemView(parentView) +{ + _zoomPosition = DEFAULT_ZOOMPOS; + _lastAutoPosition = TopLeft; + + _canvas = 0; + _xMargin = _yMargin = 0; + _completeView = new PannerView(this); + _cvZoom = 1; + _selectedNode = 0; + _selectedEdge = 0; + + _exporter.setGraphOptions(this); + + _completeView->setVScrollBarMode(QScrollView::AlwaysOff); + _completeView->setHScrollBarMode(QScrollView::AlwaysOff); + _completeView->raise(); + _completeView->hide(); + + setFocusPolicy(QWidget::StrongFocus); + setBackgroundMode(Qt::NoBackground); + + connect(this, SIGNAL(contentsMoving(int,int)), + this, SLOT(contentsMovingSlot(int,int))); + connect(_completeView, SIGNAL(zoomRectMoved(int,int)), + this, SLOT(zoomRectMoved(int,int))); + connect(_completeView, SIGNAL(zoomRectMoveFinished()), + this, SLOT(zoomRectMoveFinished())); + + QWhatsThis::add( this, whatsThis() ); + + // tooltips... + _tip = new CallGraphTip(this); + + _renderProcess = 0; + _prevSelectedNode = 0; + connect(&_renderTimer, SIGNAL(timeout()), + this, SLOT(showRenderWarning())); +} + +CallGraphView::~CallGraphView() +{ + delete _completeView; + delete _tip; + + if (_canvas) { + setCanvas(0); + delete _canvas; + } +} + +QString CallGraphView::whatsThis() const +{ + return i18n( "Call Graph around active Function" + "

Depending on configuration, this view shows " + "the call graph environment of the active function. " + "Note: the shown cost is only the cost which is " + "spent while the active function was actually running; " + "i.e. the cost shown for main() - if it's visible - should " + "be the same as the cost of the active function, as that's " + "the part of inclusive cost of main() spent while the active " + "function was running.

" + "

For cycles, blue call arrows indicate that this is an " + "artificial call added for correct drawing which " + "actually never happened.

" + "

If the graph is larger than the widget area, an overview " + "panner is shown in one edge. " + "There are similar visualization options to the " + "Call Treemap; the selected function is highlighted.

"); +} + +void CallGraphView::updateSizes(QSize s) +{ + if (!_canvas) return; + + if (s == QSize(0,0)) s = size(); + + // the part of the canvas that should be visible + int cWidth = _canvas->width() - 2*_xMargin + 100; + int cHeight = _canvas->height() - 2*_yMargin + 100; + + // hide birds eye view if no overview needed + if (!_data || !_activeItem || + ((cWidth < s.width()) && cHeight < s.height())) { + _completeView->hide(); + return; + } + _completeView->show(); + + // first, assume use of 1/3 of width/height (possible larger) + double zoom = .33 * s.width() / cWidth; + if (zoom * cHeight < .33 * s.height()) zoom = .33 * s.height() / cHeight; + + // fit to widget size + if (cWidth * zoom > s.width()) zoom = s.width() / (double)cWidth; + if (cHeight * zoom > s.height()) zoom = s.height() / (double)cHeight; + + // scale to never use full height/width + zoom = zoom * 3/4; + + // at most a zoom of 1/3 + if (zoom > .33) zoom = .33; + + if (zoom != _cvZoom) { + _cvZoom = zoom; + if (0) qDebug("Canvas Size: %dx%d, Visible: %dx%d, Zoom: %f", + _canvas->width(), _canvas->height(), + cWidth, cHeight, zoom); + + QWMatrix wm; + wm.scale( zoom, zoom ); + _completeView->setWorldMatrix(wm); + + // make it a little bigger to compensate for widget frame + _completeView->resize(int(cWidth * zoom) + 4, + int(cHeight * zoom) + 4); + + // update ZoomRect in completeView + contentsMovingSlot(contentsX(), contentsY()); + } + + _completeView->setContentsPos(int(zoom*(_xMargin-50)), + int(zoom*(_yMargin-50))); + + int cvW = _completeView->width(); + int cvH = _completeView->height(); + int x = width()- cvW - verticalScrollBar()->width() -2; + int y = height()-cvH - horizontalScrollBar()->height() -2; + QPoint oldZoomPos = _completeView->pos(); + QPoint newZoomPos = QPoint(0,0); + ZoomPosition zp = _zoomPosition; + if (zp == Auto) { + QPoint tl1Pos = viewportToContents(QPoint(0,0)); + QPoint tl2Pos = viewportToContents(QPoint(cvW,cvH)); + QPoint tr1Pos = viewportToContents(QPoint(x,0)); + QPoint tr2Pos = viewportToContents(QPoint(x+cvW,cvH)); + QPoint bl1Pos = viewportToContents(QPoint(0,y)); + QPoint bl2Pos = viewportToContents(QPoint(cvW,y+cvH)); + QPoint br1Pos = viewportToContents(QPoint(x,y)); + QPoint br2Pos = viewportToContents(QPoint(x+cvW,y+cvH)); + int tlCols = _canvas->collisions(QRect(tl1Pos,tl2Pos)).count(); + int trCols = _canvas->collisions(QRect(tr1Pos,tr2Pos)).count(); + int blCols = _canvas->collisions(QRect(bl1Pos,bl2Pos)).count(); + int brCols = _canvas->collisions(QRect(br1Pos,br2Pos)).count(); + int minCols = tlCols; + zp = _lastAutoPosition; + switch(zp) { + case TopRight: minCols = trCols; break; + case BottomLeft: minCols = blCols; break; + case BottomRight: minCols = brCols; break; + default: + case TopLeft: minCols = tlCols; break; + } + if (minCols > tlCols) { minCols = tlCols; zp = TopLeft; } + if (minCols > trCols) { minCols = trCols; zp = TopRight; } + if (minCols > blCols) { minCols = blCols; zp = BottomLeft; } + if (minCols > brCols) { minCols = brCols; zp = BottomRight; } + + _lastAutoPosition = zp; + } + + switch(zp) { + case TopRight: + newZoomPos = QPoint(x,0); + break; + case BottomLeft: + newZoomPos = QPoint(0,y); + break; + case BottomRight: + newZoomPos = QPoint(x,y); + break; + default: + break; + } + if (newZoomPos != oldZoomPos) _completeView->move(newZoomPos); +} + +void CallGraphView::focusInEvent(QFocusEvent*) +{ + if (!_canvas) return; + + if (_selectedNode && _selectedNode->canvasNode()) { + _selectedNode->canvasNode()->setSelected(true); // requests item update + _canvas->update(); + } +} + +void CallGraphView::focusOutEvent(QFocusEvent* e) +{ + // trigger updates as in focusInEvent + focusInEvent(e); +} + +void CallGraphView::keyPressEvent(QKeyEvent* e) +{ + if (!_canvas) { + e->ignore(); + return; + } + + if ((e->key() == Key_Return) || + (e->key() == Key_Space)) { + if (_selectedNode) + activated(_selectedNode->function()); + else if (_selectedEdge && _selectedEdge->call()) + activated(_selectedEdge->call()); + return; + } + + // move selected node/edge + if (!(e->state() & (ShiftButton | ControlButton)) && + (_selectedNode || _selectedEdge) && + ((e->key() == Key_Up) || + (e->key() == Key_Down) || + (e->key() == Key_Left) || + (e->key() == Key_Right))) { + + TraceFunction* f = 0; + TraceCall* c = 0; + + // rotate arrow key meaning for LeftRight layout + int key = e->key(); + if (_layout == LeftRight) { + switch(key) { + case Key_Up: key = Key_Left; break; + case Key_Down: key = Key_Right; break; + case Key_Left: key = Key_Up; break; + case Key_Right: key = Key_Down; break; + default: break; + } + } + + if (_selectedNode) { + if (key == Key_Up) c = _selectedNode->visibleCaller(); + if (key == Key_Down) c = _selectedNode->visibleCalling(); + if (key == Key_Right) f = _selectedNode->nextVisible(); + if (key == Key_Left) f = _selectedNode->priorVisible(); + } + else if (_selectedEdge) { + if (key == Key_Up) f = _selectedEdge->visibleCaller(); + if (key == Key_Down) f = _selectedEdge->visibleCalling(); + if (key == Key_Right) c = _selectedEdge->nextVisible(); + if (key == Key_Left) c = _selectedEdge->priorVisible(); + } + + if (c) selected(c); + if (f) selected(f); + return; + } + + // move canvas... + if (e->key() == Key_Home) + scrollBy(-_canvas->width(),0); + else if (e->key() == Key_End) + scrollBy(_canvas->width(),0); + else if (e->key() == Key_Prior) + scrollBy(0,-visibleHeight()/2); + else if (e->key() == Key_Next) + scrollBy(0,visibleHeight()/2); + else if (e->key() == Key_Left) + scrollBy(-visibleWidth()/10,0); + else if (e->key() == Key_Right) + scrollBy(visibleWidth()/10,0); + else if (e->key() == Key_Down) + scrollBy(0,visibleHeight()/10); + else if (e->key() == Key_Up) + scrollBy(0,-visibleHeight()/10); + else e->ignore(); +} + +void CallGraphView::resizeEvent(QResizeEvent* e) +{ + QCanvasView::resizeEvent(e); + if (_canvas) updateSizes(e->size()); +} + +TraceItem* CallGraphView::canShow(TraceItem* i) +{ + if (i) { + switch(i->type()) { + case TraceItem::Function: + case TraceItem::FunctionCycle: + case TraceItem::Call: + return i; + default: + break; + } + } + return 0; +} + +void CallGraphView::doUpdate(int changeType) +{ + // Special case ? + if (changeType == costType2Changed) return; + + if (changeType == selectedItemChanged) { + if (!_canvas) return; + + if (!_selectedItem) return; + + GraphNode* n = 0; + GraphEdge* e = 0; + if ((_selectedItem->type() == TraceItem::Function) || + (_selectedItem->type() == TraceItem::FunctionCycle)) { + n = _exporter.node((TraceFunction*)_selectedItem); + if (n == _selectedNode) return; + } + else if (_selectedItem->type() == TraceItem::Call) { + TraceCall* c = (TraceCall*)_selectedItem; + e = _exporter.edge(c->caller(false), c->called(false)); + if (e == _selectedEdge) return; + } + + // unselected any selected item + if (_selectedNode && _selectedNode->canvasNode()) { + _selectedNode->canvasNode()->setSelected(false); + } + _selectedNode = 0; + if (_selectedEdge && _selectedEdge->canvasEdge()) { + _selectedEdge->canvasEdge()->setSelected(false); + } + _selectedEdge = 0; + + // select + CanvasNode* sNode = 0; + if (n && n->canvasNode()) { + _selectedNode = n; + _selectedNode->canvasNode()->setSelected(true); + + if (!_isMoving) sNode = _selectedNode->canvasNode(); + } + if (e && e->canvasEdge()) { + _selectedEdge = e; + _selectedEdge->canvasEdge()->setSelected(true); + +#if 0 // don't change position when selecting edge + if (!_isMoving) { + if (_selectedEdge->fromNode()) + sNode = _selectedEdge->fromNode()->canvasNode(); + if (!sNode && _selectedEdge->toNode()) + sNode = _selectedEdge->toNode()->canvasNode(); + } +#endif + } + if (sNode) { + double x = sNode->x() + sNode->width()/2; + double y = sNode->y() + sNode->height()/2; + + ensureVisible(int(x),int(y), + sNode->width()/2+50, sNode->height()/2+50); + } + + _canvas->update(); + return; + } + + if (changeType == groupTypeChanged) { + if (!_canvas) return; + + if (_clusterGroups) { + refresh(); + return; + } + + QCanvasItemList l = _canvas->allItems(); + QCanvasItemList::iterator it; + for (it = l.begin();it != l.end(); ++it) + if ((*it)->rtti() == CANVAS_NODE) + ((CanvasNode*) (*it))->updateGroup(); + + _canvas->update(); + return; + } + + if (changeType & dataChanged) { + // invalidate old selection and graph part + _exporter.reset(_data, _activeItem, _costType, _groupType); + _selectedNode = 0; + _selectedEdge = 0; + } + + refresh(); +} + +void CallGraphView::clear() +{ + if (!_canvas) return; + + delete _canvas; + _canvas = 0; + _completeView->setCanvas(0); + setCanvas(0); +} + +void CallGraphView::showText(QString s) +{ + clear(); + _renderTimer.stop(); + + _canvas = new QCanvas(QApplication::desktop()->width(), + QApplication::desktop()->height()); + + QCanvasText* t = new QCanvasText(s, _canvas); + t->move(5, 5); + t->show(); + center(0,0); + setCanvas(_canvas); + _canvas->update(); + _completeView->hide(); +} + +void CallGraphView::showRenderWarning() +{ + QString s; + + if (_renderProcess) + s =i18n("Warning: a long lasting graph layouting is in progress.\n" + "Reduce node/edge limits for speedup.\n"); + else + s = i18n("Layouting stopped.\n"); + + s.append(i18n("The call graph has %1 nodes and %2 edges.\n") + .arg(_exporter.nodeCount()) + .arg(_exporter.edgeCount())); + + showText(s); +} + +void CallGraphView::stopRendering() +{ + if (!_renderProcess) return; + + _renderProcess->kill(); + delete _renderProcess; + _renderProcess = 0; + _unparsedOutput = QString::null; + + _renderTimer.start(200, true); +} + +void CallGraphView::refresh() +{ + // trigger start of background rendering + if (_renderProcess) stopRendering(); + + // we want to keep a selected node item at the same global position + _prevSelectedNode = _selectedNode; + _prevSelectedPos = QPoint(-1,-1); + if (_selectedNode) { + QPoint center = _selectedNode->canvasNode()->boundingRect().center(); + _prevSelectedPos = contentsToViewport(center); + } + + if (!_data || !_activeItem) { + showText(i18n("No item activated for which to draw the call graph.")); + return; + } + + TraceItem::CostType t = _activeItem->type(); + switch(t) { + case TraceItem::Function: + case TraceItem::FunctionCycle: + case TraceItem::Call: + break; + default: + showText(i18n("No call graph can be drawn for the active item.")); + return; + } + + if (1) kdDebug() << "CallGraphView::refresh" << endl; + + _selectedNode = 0; + _selectedEdge = 0; + _exporter.reset(_data, _activeItem, _costType, _groupType); + _exporter.writeDot(); + + _renderProcess = new QProcess(this); + if (_layout == GraphOptions::Circular) + _renderProcess->addArgument( "twopi" ); + else + _renderProcess->addArgument( "dot" ); + _renderProcess->addArgument(_exporter.filename()); + _renderProcess->addArgument( "-Tplain" ); + + connect( _renderProcess, SIGNAL(readyReadStdout()), + this, SLOT(readDotOutput()) ); + connect( _renderProcess, SIGNAL(processExited()), + this, SLOT(dotExited()) ); + + if (1) kdDebug() << "Running '" + << _renderProcess->arguments().join(" ") + << "'..." << endl; + + if ( !_renderProcess->start() ) { + QString e = i18n("No call graph is available because the following\n" + "command cannot be run:\n'%1'\n") + .arg(_renderProcess->arguments().join(" ")); + e += i18n("Please check that 'dot' is installed (package GraphViz)."); + showText(e); + + delete _renderProcess; + _renderProcess = 0; + + return; + } + + _unparsedOutput = QString::null; + + // layouting of more than seconds is dubious + _renderTimer.start(1000, true); +} + +void CallGraphView::readDotOutput() +{ + _unparsedOutput.append( _renderProcess->readStdout() ); +} + +void CallGraphView::dotExited() +{ + QString line, cmd; + CanvasNode *rItem; + QCanvasEllipse* eItem; + CanvasEdge* sItem; + CanvasEdgeLabel* lItem; + QTextStream* dotStream; + double scale = 1.0, scaleX = 1.0, scaleY = 1.0; + double dotWidth, dotHeight; + GraphNode* activeNode = 0; + GraphEdge* activeEdge = 0; + + _renderTimer.stop(); + viewport()->setUpdatesEnabled(false); + clear(); + dotStream = new QTextStream(_unparsedOutput, IO_ReadOnly); + + int lineno = 0; + while (1) { + line = dotStream->readLine(); + if (line.isNull()) break; + lineno++; + if (line.isEmpty()) continue; + + QTextStream lineStream(line, IO_ReadOnly); + lineStream >> cmd; + + if (0) qDebug("%s:%d - line '%s', cmd '%s'", + _exporter.filename().ascii(), lineno, + line.ascii(), cmd.ascii()); + + if (cmd == "stop") break; + + if (cmd == "graph") { + QString dotWidthString, dotHeightString; + lineStream >> scale >> dotWidthString >> dotHeightString; + dotWidth = dotWidthString.toDouble(); + dotHeight = dotHeightString.toDouble(); + + if (_detailLevel == 0) { scaleX = scale * 70; scaleY = scale * 40; } + else if (_detailLevel == 1) { scaleX = scale * 80; scaleY = scale * 70; } + else { scaleX = scale * 60; scaleY = scale * 100; } + + if (!_canvas) { + int w = (int)(scaleX * dotWidth); + int h = (int)(scaleY * dotHeight); + + // We use as minimum canvas size the desktop size. + // Otherwise, the canvas would have to be resized on widget resize. + _xMargin = 50; + if (w < QApplication::desktop()->width()) + _xMargin += (QApplication::desktop()->width()-w)/2; + + _yMargin = 50; + if (h < QApplication::desktop()->height()) + _yMargin += (QApplication::desktop()->height()-h)/2; + + _canvas = new QCanvas(int(w+2*_xMargin), int(h+2*_yMargin)); + +#if DEBUG_GRAPH + kdDebug() << _exporter.filename().ascii() << ":" << lineno + << " - graph (" << dotWidth << " x " << dotHeight + << ") => (" << w << " x " << h << ")" << endl; +#endif + } + else + kdWarning() << "Ignoring 2nd 'graph' from dot (" + << _exporter.filename() << ":" << lineno << ")" << endl; + continue; + } + + if ((cmd != "node") && (cmd != "edge")) { + kdWarning() << "Ignoring unknown command '" << cmd << "' from dot (" + << _exporter.filename() << ":" << lineno << ")" << endl; + continue; + } + + if (_canvas == 0) { + kdWarning() << "Ignoring '" << cmd << "' without 'graph' from dot (" + << _exporter.filename() << ":" << lineno << ")" << endl; + continue; + } + + if (cmd == "node") { + // x, y are centered in node + QString nodeName, label, nodeX, nodeY, nodeWidth, nodeHeight; + double x, y, width, height; + lineStream >> nodeName >> nodeX >> nodeY >> nodeWidth >> nodeHeight; + x = nodeX.toDouble(); + y = nodeY.toDouble(); + width = nodeWidth.toDouble(); + height = nodeHeight.toDouble(); + + GraphNode* n = _exporter.node(_exporter.toFunc(nodeName)); + + int xx = (int)(scaleX * x + _xMargin); + int yy = (int)(scaleY * (dotHeight - y) + _yMargin); + int w = (int)(scaleX * width); + int h = (int)(scaleY * height); + +#if DEBUG_GRAPH + kdDebug() << _exporter.filename() << ":" << lineno + << " - node '" << nodeName << "' ( " + << x << "/" << y << " - " + << width << "x" << height << " ) => (" + << xx-w/2 << "/" << yy-h/2 << " - " + << w << "x" << h << ")" << endl; +#endif + + + // Unnamed nodes with collapsed edges (with 'R' and 'S') + if (nodeName[0] == 'R' || nodeName[0] == 'S') { + w = 10, h = 10; + eItem = new QCanvasEllipse(w, h, _canvas); + eItem->move(xx, yy); + eItem->setBrush(Qt::gray); + eItem->setZ(1.0); + eItem->show(); + continue; + } + + if (!n) { + qDebug("Warning: Unknown function '%s' ?!", nodeName.ascii()); + continue; + } + n->setVisible(true); + + rItem = new CanvasNode(this, n, xx-w/2, yy-h/2, w, h, _canvas); + n->setCanvasNode(rItem); + + if (n) { + if (n->function() == activeItem()) activeNode = n; + if (n->function() == selectedItem()) _selectedNode = n; + rItem->setSelected(n == _selectedNode); + } + + rItem->setZ(1.0); + rItem->show(); + + continue; + } + + // edge + + QString node1Name, node2Name, label, edgeX, edgeY; + double x, y; + QPointArray pa; + int points, i; + lineStream >> node1Name >> node2Name >> points; + + GraphEdge* e = _exporter.edge(_exporter.toFunc(node1Name), + _exporter.toFunc(node2Name)); + if (!e) { + kdWarning() << "Unknown edge '" << node1Name << "'-'" + << node2Name << "' from dot (" + << _exporter.filename() << ":" << lineno << ")" << endl; + continue; + } + e->setVisible(true); + if (e->fromNode()) e->fromNode()->callings.append(e); + if (e->toNode()) e->toNode()->callers.append(e); + + if (0) qDebug(" Edge with %d points:", points); + + pa.resize(points); + for (i=0;i> edgeX >> edgeY; + x = edgeX.toDouble(); + y = edgeY.toDouble(); + + int xx = (int)(scaleX * x + _xMargin); + int yy = (int)(scaleY * (dotHeight - y) + _yMargin); + + if (0) qDebug(" P %d: ( %f / %f ) => ( %d / %d)", + i, x, y, xx, yy); + + pa.setPoint(i, xx, yy); + } + if (i < points) { + qDebug("CallGraphView: Can't read %d spline points (%s:%d)", + points, _exporter.filename().ascii(), lineno); + continue; + } + + // calls into/out of cycles are special: make them blue + QColor arrowColor = Qt::black; + TraceFunction* caller = e->fromNode() ? e->fromNode()->function() : 0; + TraceFunction* called = e->toNode() ? e->toNode()->function() : 0; + if ( (caller && (caller->cycle() == caller)) || + (called && (called->cycle() == called)) ) arrowColor = Qt::blue; + + sItem = new CanvasEdge(e, _canvas); + e->setCanvasEdge(sItem); + sItem->setControlPoints(pa, false); + sItem->setPen(QPen(arrowColor, 1 /*(int)log(log(e->cost))*/ )); + sItem->setZ(0.5); + sItem->show(); + + if (e->call() == selectedItem()) _selectedEdge = e; + if (e->call() == activeItem()) activeEdge = e; + sItem->setSelected(e == _selectedEdge); + + // Arrow head + QPoint arrowDir; + int indexHead = -1; + + // check if head is at start of spline... + // this is needed because dot always gives points from top to bottom + CanvasNode* fromNode = e->fromNode() ? e->fromNode()->canvasNode() : 0; + if (fromNode) { + QPoint toCenter = fromNode->rect().center(); + int dx0 = pa.point(0).x() - toCenter.x(); + int dy0 = pa.point(0).y() - toCenter.y(); + int dx1 = pa.point(points-1).x() - toCenter.x(); + int dy1 = pa.point(points-1).y() - toCenter.y(); + if (dx0*dx0+dy0*dy0 > dx1*dx1+dy1*dy1) { + // start of spline is nearer to call target node + indexHead=-1; + while(arrowDir.isNull() && (indexHead1)) { + indexHead--; + arrowDir = pa.point(indexHead) - pa.point(indexHead-1); + } + } + + if (!arrowDir.isNull()) { + // arrow around pa.point(indexHead) with direction arrowDir + arrowDir *= 10.0/sqrt(double(arrowDir.x()*arrowDir.x() + + arrowDir.y()*arrowDir.y())); + QPointArray a(3); + a.setPoint(0, pa.point(indexHead) + arrowDir); + a.setPoint(1, pa.point(indexHead) + QPoint(arrowDir.y()/2, + -arrowDir.x()/2)); + a.setPoint(2, pa.point(indexHead) + QPoint(-arrowDir.y()/2, + arrowDir.x()/2)); + + if (0) qDebug(" Arrow: ( %d/%d, %d/%d, %d/%d)", + a.point(0).x(), a.point(0).y(), + a.point(1).x(), a.point(1).y(), + a.point(2).x(), a.point(2).y()); + + CanvasEdgeArrow* aItem = new CanvasEdgeArrow(sItem,_canvas); + aItem->setPoints(a); + aItem->setBrush(arrowColor); + aItem->setZ(1.5); + aItem->show(); + + sItem->setArrow(aItem); + } + + if (lineStream.atEnd()) continue; + + // parse quoted label + QChar c; + lineStream >> c; + while (c.isSpace()) lineStream >> c; + if (c != '\"') { + lineStream >> label; + label = c + label; + } + else { + lineStream >> c; + while(!c.isNull() && (c != '\"')) { + //if (c == '\\') lineStream >> c; + + label += c; + lineStream >> c; + } + } + lineStream >> edgeX >> edgeY; + x = edgeX.toDouble(); + y = edgeY.toDouble(); + + int xx = (int)(scaleX * x + _xMargin); + int yy = (int)(scaleY * (dotHeight - y) + _yMargin); + + if (0) qDebug(" Label '%s': ( %f / %f ) => ( %d / %d)", + label.ascii(), x, y, xx, yy); + + // Fixed Dimensions for Label: 100 x 40 + int w = 100; + int h = _detailLevel * 20; + lItem = new CanvasEdgeLabel(this, sItem, xx-w/2, yy-h/2, w, h, _canvas); + // edge labels above nodes + lItem->setZ(1.5); + sItem->setLabel(lItem); + if (h>0) lItem->show(); + + } + delete dotStream; + + // for keyboard navigation + // TODO: Edge sorting. Better keep left-to-right edge order from dot now + // _exporter.sortEdges(); + + if (!_canvas) { + _canvas = new QCanvas(size().width(),size().height()); + QString s = i18n("Error running the graph layouting tool.\n"); + s += i18n("Please check that 'dot' is installed (package GraphViz)."); + QCanvasText* t = new QCanvasText(s, _canvas); + t->move(5, 5); + t->show(); + center(0,0); + } + else if (!activeNode && !activeEdge) { + QString s = i18n("There is no call graph available for function\n" + "\t'%1'\n" + "because it has no cost of the selected event type."); + QCanvasText* t = new QCanvasText(s.arg(_activeItem->name()), _canvas); + // t->setTextFlags(Qt::AlignHCenter | Qt::AlignVCenter); + t->move(5,5); + t->show(); + center(0,0); + } + + _completeView->setCanvas(_canvas); + setCanvas(_canvas); + + // if we don't have a selection, or the old selection is not + // in visible graph, make active function selected for this view + if ((!_selectedNode || !_selectedNode->canvasNode()) && + (!_selectedEdge || !_selectedEdge->canvasEdge())) { + if (activeNode) { + _selectedNode = activeNode; + _selectedNode->canvasNode()->setSelected(true); + } + else if (activeEdge) { + _selectedEdge = activeEdge; + _selectedEdge->canvasEdge()->setSelected(true); + } + } + + CanvasNode* sNode = 0; + if (_selectedNode) + sNode = _selectedNode->canvasNode(); + else if (_selectedEdge) { + if (_selectedEdge->fromNode()) + sNode = _selectedEdge->fromNode()->canvasNode(); + if (!sNode && _selectedEdge->toNode()) + sNode = _selectedEdge->toNode()->canvasNode(); + } + if (sNode) { + int x = int(sNode->x() + sNode->width()/2); + int y = int(sNode->y() + sNode->height()/2); + + if (_prevSelectedNode) { + if (rect().contains(_prevSelectedPos)) + setContentsPos(x-_prevSelectedPos.x(), + y-_prevSelectedPos.y()); + else + ensureVisible(x,y, + sNode->width()/2+50, sNode->height()/2+50); + } + else center(x,y); + } + + if (activeNode) { + CanvasNode* cn = activeNode->canvasNode(); + CanvasFrame* f = new CanvasFrame(cn, _canvas); + f->setZ(-1); + f->show(); + } + + _cvZoom = 0; + updateSizes(); + + _canvas->update(); + viewport()->setUpdatesEnabled(true); + + delete _renderProcess; + _renderProcess = 0; +} + +void CallGraphView::contentsMovingSlot(int x, int y) +{ + QRect z(int(x * _cvZoom), int(y * _cvZoom), + int(visibleWidth() * _cvZoom)-1, int(visibleHeight() * _cvZoom)-1); + if (0) qDebug("moving: (%d,%d) => (%d/%d - %dx%d)", + x, y, z.x(), z.y(), z.width(), z.height()); + _completeView->setZoomRect(z); +} + +void CallGraphView::zoomRectMoved(int dx, int dy) +{ + if (leftMargin()>0) dx = 0; + if (topMargin()>0) dy = 0; + scrollBy(int(dx/_cvZoom),int(dy/_cvZoom)); +} + +void CallGraphView::zoomRectMoveFinished() +{ + if (_zoomPosition == Auto) updateSizes(); +} + +void CallGraphView::contentsMousePressEvent(QMouseEvent* e) +{ + // clicking on the viewport sets focus + setFocus(); + + _isMoving = true; + + QCanvasItemList l = canvas()->collisions(e->pos()); + if (l.count()>0) { + QCanvasItem* i = l.first(); + + if (i->rtti() == CANVAS_NODE) { + GraphNode* n = ((CanvasNode*)i)->node(); + if (0) qDebug("CallGraphView: Got Node '%s'", + n->function()->prettyName().ascii()); + + selected(n->function()); + } + + // redirect from label / arrow to edge + if (i->rtti() == CANVAS_EDGELABEL) + i = ((CanvasEdgeLabel*)i)->canvasEdge(); + if (i->rtti() == CANVAS_EDGEARROW) + i = ((CanvasEdgeArrow*)i)->canvasEdge(); + + if (i->rtti() == CANVAS_EDGE) { + GraphEdge* e = ((CanvasEdge*)i)->edge(); + if (0) qDebug("CallGraphView: Got Edge '%s'", + e->prettyName().ascii()); + + if (e->call()) selected(e->call()); + } + } + _lastPos = e->globalPos(); +} + +void CallGraphView::contentsMouseMoveEvent(QMouseEvent* e) +{ + if (_isMoving) { + int dx = e->globalPos().x() - _lastPos.x(); + int dy = e->globalPos().y() - _lastPos.y(); + scrollBy(-dx, -dy); + _lastPos = e->globalPos(); + } +} + +void CallGraphView::contentsMouseReleaseEvent(QMouseEvent*) +{ + _isMoving = false; + if (_zoomPosition == Auto) updateSizes(); +} + +void CallGraphView::contentsMouseDoubleClickEvent(QMouseEvent* e) +{ + QCanvasItemList l = canvas()->collisions(e->pos()); + if (l.count() == 0) return; + QCanvasItem* i = l.first(); + + if (i->rtti() == CANVAS_NODE) { + GraphNode* n = ((CanvasNode*)i)->node(); + if (0) qDebug("CallGraphView: Double Clicked on Node '%s'", + n->function()->prettyName().ascii()); + + activated(n->function()); + } + + // redirect from label / arrow to edge + if (i->rtti() == CANVAS_EDGELABEL) + i = ((CanvasEdgeLabel*)i)->canvasEdge(); + if (i->rtti() == CANVAS_EDGEARROW) + i = ((CanvasEdgeArrow*)i)->canvasEdge(); + + if (i->rtti() == CANVAS_EDGE) { + GraphEdge* e = ((CanvasEdge*)i)->edge(); + if (e->call()) { + if (0) qDebug("CallGraphView: Double Clicked On Edge '%s'", + e->call()->prettyName().ascii()); + + activated(e->call()); + } + } +} + +void CallGraphView::contentsContextMenuEvent(QContextMenuEvent* e) +{ + QCanvasItemList l = canvas()->collisions(e->pos()); + QCanvasItem* i = (l.count() == 0) ? 0 : l.first(); + + QPopupMenu popup; + TraceFunction *f = 0, *cycle = 0; + TraceCall* c = 0; + + if (i) { + if (i->rtti() == CANVAS_NODE) { + GraphNode* n = ((CanvasNode*)i)->node(); + if (0) qDebug("CallGraphView: Menu on Node '%s'", + n->function()->prettyName().ascii()); + f = n->function(); + cycle = f->cycle(); + + QString name = f->prettyName(); + popup.insertItem(i18n("Go to '%1'") + .arg(Configuration::shortenSymbol(name)), 93); + if (cycle && (cycle != f)) { + name = Configuration::shortenSymbol(cycle->prettyName()); + popup.insertItem(i18n("Go to '%1'").arg(name), 94); + } + popup.insertSeparator(); + } + + // redirect from label / arrow to edge + if (i->rtti() == CANVAS_EDGELABEL) + i = ((CanvasEdgeLabel*)i)->canvasEdge(); + if (i->rtti() == CANVAS_EDGEARROW) + i = ((CanvasEdgeArrow*)i)->canvasEdge(); + + if (i->rtti() == CANVAS_EDGE) { + GraphEdge* e = ((CanvasEdge*)i)->edge(); + if (0) qDebug("CallGraphView: Menu on Edge '%s'", + e->prettyName().ascii()); + c = e->call(); + if (c) { + QString name = c->prettyName(); + popup.insertItem(i18n("Go to '%1'") + .arg(Configuration::shortenSymbol(name)), 95); + + popup.insertSeparator(); + } + } + } + + if (_renderProcess) { + popup.insertItem(i18n("Stop Layouting"), 999); + popup.insertSeparator(); + } + + addGoMenu(&popup); + popup.insertSeparator(); + + QPopupMenu epopup; + epopup.insertItem(i18n("As PostScript"), 201); + epopup.insertItem(i18n("As Image ..."), 202); + + popup.insertItem(i18n("Export Graph"), &epopup, 200); + popup.insertSeparator(); + + QPopupMenu gpopup1; + gpopup1.setCheckable(true); + gpopup1.insertItem(i18n("Unlimited"), 100); + gpopup1.setItemEnabled(100, (_funcLimit>0.005)); + gpopup1.insertSeparator(); + gpopup1.insertItem(i18n("None"), 101); + gpopup1.insertItem(i18n("max. 2"), 102); + gpopup1.insertItem(i18n("max. 5"), 103); + gpopup1.insertItem(i18n("max. 10"), 104); + gpopup1.insertItem(i18n("max. 15"), 105); + if (_maxCallerDepth<-1) _maxCallerDepth=-1; + switch(_maxCallerDepth) { + case -1: gpopup1.setItemChecked(100,true); break; + case 0: gpopup1.setItemChecked(101,true); break; + case 2: gpopup1.setItemChecked(102,true); break; + case 5: gpopup1.setItemChecked(103,true); break; + case 10: gpopup1.setItemChecked(104,true); break; + case 15: gpopup1.setItemChecked(105,true); break; + default: + gpopup1.insertItem(i18n("< %1").arg(_maxCallerDepth), 106); + gpopup1.setItemChecked(106,true); break; + } + + QPopupMenu gpopup2; + gpopup2.setCheckable(true); + gpopup2.insertItem(i18n("Unlimited"), 110); + gpopup2.setItemEnabled(110, (_funcLimit>0.005)); + gpopup2.insertSeparator(); + gpopup2.insertItem(i18n("None"), 111); + gpopup2.insertItem(i18n("max. 2"), 112); + gpopup2.insertItem(i18n("max. 5"), 113); + gpopup2.insertItem(i18n("max. 10"), 114); + gpopup2.insertItem(i18n("max. 15"), 115); + if (_maxCallingDepth<-1) _maxCallingDepth=-1; + switch(_maxCallingDepth) { + case -1: gpopup2.setItemChecked(110,true); break; + case 0: gpopup2.setItemChecked(111,true); break; + case 2: gpopup2.setItemChecked(112,true); break; + case 5: gpopup2.setItemChecked(113,true); break; + case 10: gpopup2.setItemChecked(114,true); break; + case 15: gpopup2.setItemChecked(115,true); break; + default: + gpopup2.insertItem(i18n("< %1").arg(_maxCallingDepth), 116); + gpopup2.setItemChecked(116,true); break; + } + + QPopupMenu gpopup3; + gpopup3.setCheckable(true); + gpopup3.insertItem(i18n("No Minimum"), 120); + gpopup3.setItemEnabled(120, + (_maxCallerDepth>=0) && (_maxCallingDepth>=0)); + gpopup3.insertSeparator(); + gpopup3.insertItem(i18n("50 %"), 121); + gpopup3.insertItem(i18n("20 %"), 122); + gpopup3.insertItem(i18n("10 %"), 123); + gpopup3.insertItem(i18n("5 %"), 124); + gpopup3.insertItem(i18n("3 %"), 125); + gpopup3.insertItem(i18n("2 %"), 126); + gpopup3.insertItem(i18n("1.5 %"), 127); + gpopup3.insertItem(i18n("1 %"), 128); + if (_funcLimit<0) _funcLimit = DEFAULT_FUNCLIMIT; + if (_funcLimit>.5) _funcLimit = .5; + if (_funcLimit == 0.0) gpopup3.setItemChecked(120,true); + else if (_funcLimit >= 0.5) gpopup3.setItemChecked(121,true); + else if (_funcLimit >= 0.2) gpopup3.setItemChecked(122,true); + else if (_funcLimit >= 0.1) gpopup3.setItemChecked(123,true); + else if (_funcLimit >= 0.05) gpopup3.setItemChecked(124,true); + else if (_funcLimit >= 0.03) gpopup3.setItemChecked(125,true); + else if (_funcLimit >= 0.02) gpopup3.setItemChecked(126,true); + else if (_funcLimit >= 0.015) gpopup3.setItemChecked(127,true); + else gpopup3.setItemChecked(128,true); + double oldFuncLimit = _funcLimit; + + QPopupMenu gpopup4; + gpopup4.setCheckable(true); + gpopup4.insertItem(i18n("Same as Node"), 160); + gpopup4.insertItem(i18n("50 % of Node"), 161); + gpopup4.insertItem(i18n("20 % of Node"), 162); + gpopup4.insertItem(i18n("10 % of Node"), 163); + if (_callLimit<0) _callLimit = DEFAULT_CALLLIMIT; + if (_callLimit >= _funcLimit) _callLimit = _funcLimit; + if (_callLimit == _funcLimit) gpopup4.setItemChecked(160,true); + else if (_callLimit >= 0.5 * _funcLimit) gpopup4.setItemChecked(161,true); + else if (_callLimit >= 0.2 * _funcLimit) gpopup4.setItemChecked(162,true); + else gpopup4.setItemChecked(163,true); + + QPopupMenu gpopup; + gpopup.setCheckable(true); + gpopup.insertItem(i18n("Caller Depth"), &gpopup1, 80); + gpopup.insertItem(i18n("Callee Depth"), &gpopup2, 81); + gpopup.insertItem(i18n("Min. Node Cost"), &gpopup3, 82); + gpopup.insertItem(i18n("Min. Call Cost"), &gpopup4, 83); + gpopup.insertSeparator(); + gpopup.insertItem(i18n("Arrows for Skipped Calls"), 130); + gpopup.setItemChecked(130,_showSkipped); + gpopup.insertItem(i18n("Inner-cycle Calls"), 131); + gpopup.setItemChecked(131,_expandCycles); + gpopup.insertItem(i18n("Cluster Groups"), 132); + gpopup.setItemChecked(132,_clusterGroups); + + QPopupMenu vpopup; + vpopup.setCheckable(true); + vpopup.insertItem(i18n("Compact"), 140); + vpopup.insertItem(i18n("Normal"), 141); + vpopup.insertItem(i18n("Tall"), 142); + vpopup.setItemChecked(140,_detailLevel == 0); + vpopup.setItemChecked(141,_detailLevel == 1); + vpopup.setItemChecked(142,_detailLevel == 2); + vpopup.insertSeparator(); + vpopup.insertItem(i18n("Top to Down"), 150); + vpopup.insertItem(i18n("Left to Right"), 151); + vpopup.insertItem(i18n("Circular"), 152); + vpopup.setItemChecked(150,_layout == TopDown); + vpopup.setItemChecked(151,_layout == LeftRight); + vpopup.setItemChecked(152,_layout == Circular); + + QPopupMenu opopup; + opopup.insertItem(i18n("TopLeft"), 170); + opopup.insertItem(i18n("TopRight"), 171); + opopup.insertItem(i18n("BottomLeft"), 172); + opopup.insertItem(i18n("BottomRight"), 173); + opopup.insertItem(i18n("Automatic"), 174); + opopup.setItemChecked(170,_zoomPosition == TopLeft); + opopup.setItemChecked(171,_zoomPosition == TopRight); + opopup.setItemChecked(172,_zoomPosition == BottomLeft); + opopup.setItemChecked(173,_zoomPosition == BottomRight); + opopup.setItemChecked(174,_zoomPosition == Auto); + + popup.insertItem(i18n("Graph"), &gpopup, 70); + popup.insertItem(i18n("Visualization"), &vpopup, 71); + popup.insertItem(i18n("Birds-eye View"), &opopup, 72); + + int r = popup.exec(e->globalPos()); + + switch(r) { + case 93: activated(f); break; + case 94: activated(cycle); break; + case 95: activated(c); break; + + case 999: stopRendering(); break; + + case 201: + { + TraceFunction* f = activeFunction(); + if (!f) break; + + GraphExporter ge(data(), f, costType(), groupType(), + QString("callgraph.dot")); + ge.setGraphOptions(this); + ge.writeDot(); + + system("(dot callgraph.dot -Tps > callgraph.ps; kghostview callgraph.ps)&"); + } + break; + + case 202: + // write current content of canvas as image to file + { + if (!_canvas) return; + + QString fn = KFileDialog::getSaveFileName(":","*.png"); + + if (!fn.isEmpty()) { + QPixmap pix(_canvas->size()); + QPainter p(&pix); + _canvas->drawArea( _canvas->rect(), &p ); + pix.save(fn,"PNG"); + } + } + break; + + case 100: _maxCallerDepth = -1; break; + case 101: _maxCallerDepth = 0; break; + case 102: _maxCallerDepth = 2; break; + case 103: _maxCallerDepth = 5; break; + case 104: _maxCallerDepth = 10; break; + case 105: _maxCallerDepth = 15; break; + + case 110: _maxCallingDepth = -1; break; + case 111: _maxCallingDepth = 0; break; + case 112: _maxCallingDepth = 2; break; + case 113: _maxCallingDepth = 5; break; + case 114: _maxCallingDepth = 10; break; + case 115: _maxCallingDepth = 15; break; + + case 120: _funcLimit = 0; break; + case 121: _funcLimit = 0.5; break; + case 122: _funcLimit = 0.2; break; + case 123: _funcLimit = 0.1; break; + case 124: _funcLimit = 0.05; break; + case 125: _funcLimit = 0.03; break; + case 126: _funcLimit = 0.02; break; + case 127: _funcLimit = 0.015; break; + case 128: _funcLimit = 0.01; break; + + case 130: _showSkipped = !_showSkipped; break; + case 131: _expandCycles = !_expandCycles; break; + case 132: _clusterGroups = !_clusterGroups; break; + + case 140: _detailLevel = 0; break; + case 141: _detailLevel = 1; break; + case 142: _detailLevel = 2; break; + + case 150: _layout = TopDown; break; + case 151: _layout = LeftRight; break; + case 152: _layout = Circular; break; + + case 160: _callLimit = _funcLimit; break; + case 161: _callLimit = .5 * _funcLimit; break; + case 162: _callLimit = .2 * _funcLimit; break; + case 163: _callLimit = .1 * _funcLimit; break; + + case 170: _zoomPosition = TopLeft; break; + case 171: _zoomPosition = TopRight; break; + case 172: _zoomPosition = BottomLeft; break; + case 173: _zoomPosition = BottomRight; break; + case 174: _zoomPosition = Auto; break; + + default: break; + } + if (r>=120 && r<130) _callLimit *= _funcLimit / oldFuncLimit; + + if (r>99 && r<170) refresh(); + if (r>169 && r<180) updateSizes(); +} + +CallGraphView::ZoomPosition CallGraphView::zoomPos(QString s) +{ + if (s == QString("TopLeft")) return TopLeft; + if (s == QString("TopRight")) return TopRight; + if (s == QString("BottomLeft")) return BottomLeft; + if (s == QString("BottomRight")) return BottomRight; + if (s == QString("Automatic")) return Auto; + + return DEFAULT_ZOOMPOS; +} + +QString CallGraphView::zoomPosString(ZoomPosition p) +{ + if (p == TopRight) return QString("TopRight"); + if (p == BottomLeft) return QString("BottomLeft"); + if (p == BottomRight) return QString("BottomRight"); + if (p == Auto) return QString("Automatic"); + + return QString("TopLeft"); +} + +void CallGraphView::readViewConfig(KConfig* c, + QString prefix, QString postfix, bool) +{ + KConfigGroup* g = configGroup(c, prefix, postfix); + + if (0) qDebug("CallGraphView::readViewConfig"); + + _maxCallerDepth = g->readNumEntry("MaxCaller", DEFAULT_MAXCALLER); + _maxCallingDepth = g->readNumEntry("MaxCalling", DEFAULT_MAXCALLING); + _funcLimit = g->readDoubleNumEntry("FuncLimit", DEFAULT_FUNCLIMIT); + _callLimit = g->readDoubleNumEntry("CallLimit", DEFAULT_CALLLIMIT); + _showSkipped = g->readBoolEntry("ShowSkipped", DEFAULT_SHOWSKIPPED); + _expandCycles = g->readBoolEntry("ExpandCycles", DEFAULT_EXPANDCYCLES); + _clusterGroups = g->readBoolEntry("ClusterGroups", + DEFAULT_CLUSTERGROUPS); + _detailLevel = g->readNumEntry("DetailLevel", DEFAULT_DETAILLEVEL); + _layout = GraphOptions::layout(g->readEntry("Layout", + layoutString(DEFAULT_LAYOUT))); + _zoomPosition = zoomPos(g->readEntry("ZoomPosition", + zoomPosString(DEFAULT_ZOOMPOS))); + + delete g; +} + +void CallGraphView::saveViewConfig(KConfig* c, + QString prefix, QString postfix, bool) +{ + KConfigGroup g(c, (prefix+postfix).ascii()); + + writeConfigEntry(&g, "MaxCaller", _maxCallerDepth, DEFAULT_MAXCALLER); + writeConfigEntry(&g, "MaxCalling", _maxCallingDepth, DEFAULT_MAXCALLING); + writeConfigEntry(&g, "FuncLimit", _funcLimit, DEFAULT_FUNCLIMIT); + writeConfigEntry(&g, "CallLimit", _callLimit, DEFAULT_CALLLIMIT); + writeConfigEntry(&g, "ShowSkipped", _showSkipped, DEFAULT_SHOWSKIPPED); + writeConfigEntry(&g, "ExpandCycles", _expandCycles, DEFAULT_EXPANDCYCLES); + writeConfigEntry(&g, "ClusterGroups", _clusterGroups, + DEFAULT_CLUSTERGROUPS); + writeConfigEntry(&g, "DetailLevel", _detailLevel, DEFAULT_DETAILLEVEL); + writeConfigEntry(&g, "Layout", + layoutString(_layout), layoutString(DEFAULT_LAYOUT).utf8().data()); + writeConfigEntry(&g, "ZoomPosition", + zoomPosString(_zoomPosition), + zoomPosString(DEFAULT_ZOOMPOS).utf8().data()); +} + +#include "callgraphview.moc" + diff --git a/kcachegrind/kcachegrind/callgraphview.h b/kcachegrind/kcachegrind/callgraphview.h new file mode 100644 index 00000000..af861d41 --- /dev/null +++ b/kcachegrind/kcachegrind/callgraphview.h @@ -0,0 +1,499 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Callgraph View + */ + +#ifndef CALLGRAPHVIEW_H +#define CALLGRAPHVIEW_H + +#include +#include +#include +#include + +#include "treemap.h" // for DrawParams +#include "tracedata.h" +#include "traceitemview.h" + +class QProcess; + +class KTempFile; +class CanvasNode; +class CanvasEdge; +class GraphEdge; +class CallGraphView; + +// sorts according start/end position of a call arc +// this depends on attached CanvasEdge's ! +class GraphEdgeList: public QPtrList +{ + public: + GraphEdgeList(); + void setSortCallerPos(bool b) { _sortCallerPos = b; } + + protected: + int compareItems ( Item item1, Item item2 ); + + private: + bool _sortCallerPos; +}; + + +typedef QMap GraphEdgeSet; + +// temporary parts of call graph to be shown +class GraphNode +{ +public: + GraphNode(); + + TraceFunction* function() { return _f; } + void setFunction(TraceFunction* f) { _f = f; } + + CanvasNode* canvasNode() { return _cn; } + void setCanvasNode(CanvasNode* cn) { _cn = cn; } + + bool isVisible() { return _visible; } + void setVisible(bool v) { _visible = v; } + + // keyboard navigation + TraceCall* visibleCaller(); + TraceCall* visibleCalling(); + void setCalling(GraphEdge*); + void setCaller(GraphEdge*); + TraceFunction* nextVisible(); + TraceFunction* priorVisible(); + TraceCall* nextVisibleCaller(GraphEdge*); + TraceCall* nextVisibleCalling(GraphEdge*); + TraceCall* priorVisibleCaller(GraphEdge*); + TraceCall* priorVisibleCalling(GraphEdge*); + + double self, incl; + GraphEdgeList callers, callings; + // for fast unique insertion of GraphEdges in above lists + GraphEdgeSet callerSet, callingSet; + + private: + TraceFunction* _f; + CanvasNode* _cn; + bool _visible; + + // for keyboard navigation + int _lastCallerIndex, _lastCallingIndex; + bool _lastFromCaller; +}; + +class GraphEdge +{ +public: + GraphEdge(); + + CanvasEdge* canvasEdge() { return _ce; } + void setCanvasEdge(CanvasEdge* ce) { _ce = ce; } + + TraceCall* call() { return _c; } + void setCall(TraceCall* c) { _c = c; } + + bool isVisible() { return _visible; } + void setVisible(bool v) { _visible = v; } + + GraphNode* fromNode() { return _fromNode; } + GraphNode* toNode() { return _toNode; } + TraceFunction* from() { return _from; } + TraceFunction* to() { return _to; } + + // has special cases for collapsed edges + QString prettyName(); + + void setCaller(TraceFunction* f) { _from = f; } + void setCalling(TraceFunction* f) { _to = f; } + void setCallerNode(GraphNode* n) { _fromNode = n; } + void setCallingNode(GraphNode* n) { _toNode = n; } + + // keyboard navigation + TraceFunction* visibleCaller(); + TraceFunction* visibleCalling(); + TraceCall* nextVisible(); + TraceCall* priorVisible(); + + double cost, count; + + private: + // we have a _c *and* _from/_to because for collapsed edges, + // only _to or _from will be unequal NULL + TraceCall* _c; + TraceFunction * _from, * _to; + GraphNode *_fromNode, *_toNode; + CanvasEdge* _ce; + bool _visible; + // for keyboard navigation: have we last reached this edge via a caller? + bool _lastFromCaller; + +}; + + +typedef QMap GraphNodeMap; +typedef QMap, GraphEdge> GraphEdgeMap; + + +/* Abstract Interface for graph options */ +class GraphOptions +{ + public: + enum Layout { TopDown, LeftRight, Circular}; + + virtual double funcLimit() = 0; + virtual double callLimit() = 0; + virtual int maxCallerDepth() = 0; + virtual int maxCallingDepth() = 0; + virtual bool showSkipped() = 0; + virtual bool expandCycles() = 0; + virtual bool clusterGroups() = 0; + virtual int detailLevel() = 0; + virtual Layout layout() = 0; + + static QString layoutString(Layout); + static Layout layout(QString); +}; + +/* Graph Options Storage */ +class StorableGraphOptions: public GraphOptions +{ + public: + StorableGraphOptions(); + + // implementation of getters + virtual double funcLimit() { return _funcLimit; } + virtual double callLimit() { return _callLimit; } + virtual int maxCallerDepth() { return _maxCallerDepth; } + virtual int maxCallingDepth() { return _maxCallingDepth; } + virtual bool showSkipped() { return _showSkipped; } + virtual bool expandCycles() { return _expandCycles; } + virtual bool clusterGroups() { return _clusterGroups; } + virtual int detailLevel() { return _detailLevel; } + virtual Layout layout() { return _layout; } + + // setters + void setMaxCallerDepth(int d) { _maxCallerDepth = d; } + void setMaxCallingDepth(int d) { _maxCallingDepth = d; } + void setFuncLimit(double l) { _funcLimit = l; } + void setCallLimit(double l) { _callLimit = l; } + void setShowSkipped(bool b) { _showSkipped = b; } + void setExpandCycles(bool b) { _expandCycles = b; } + void setClusterGroups(bool b) { _clusterGroups = b; } + void setDetailLevel(int l) { _detailLevel = l; } + void setLayout(Layout l) { _layout = l; } + + protected: + double _funcLimit, _callLimit; + int _maxCallerDepth, _maxCallingDepth; + bool _showSkipped, _expandCycles, _clusterGroups; + int _detailLevel; + Layout _layout; +}; + +/** + * GraphExporter + * + * Generates a graph file for "dot" + * Create an instance and + */ +class GraphExporter: public StorableGraphOptions +{ +public: + GraphExporter(); + GraphExporter(TraceData*, TraceFunction*, TraceCostType*, + TraceItem::CostType, QString filename = QString::null); + virtual ~GraphExporter(); + + void reset(TraceData*, TraceItem*, TraceCostType*, + TraceItem::CostType, QString filename = QString::null); + + QString filename() { return _dotName; } + int edgeCount() { return _edgeMap.count(); } + int nodeCount() { return _nodeMap.count(); } + + // Set the object from which to get graph options for creation. + // Default is this object itself (supply 0 for default) + void setGraphOptions(GraphOptions* go = 0); + + // Create a subgraph with given limits/maxDepths + void createGraph(); + + // calls createGraph before dumping of not already created + void writeDot(); + + // to map back to structures when parsing a layouted graph + + /* is a helper for node() and edge(). + * Don't use the returned pointer directly, but only with + * node() or edge(), because it could be a dangling pointer. + */ + TraceFunction* toFunc(QString); + GraphNode* node(TraceFunction*); + GraphEdge* edge(TraceFunction*, TraceFunction*); + + /* After CanvasEdges are attached to GraphEdges, we can + * sort the incoming and outgoing edges of all nodes + * regarding start/end points for keyboard navigation + */ + void sortEdges(); + +private: + void buildGraph(TraceFunction*, int, bool, double); + + QString _dotName; + TraceItem* _item; + TraceCostType* _costType; + TraceItem::CostType _groupType; + KTempFile* _tmpFile; + double _realFuncLimit, _realCallLimit; + int _maxDepth; + bool _graphCreated; + + GraphOptions* _go; + + // optional graph attributes + bool _useBox; + + // graph parts written to file + GraphNodeMap _nodeMap; + GraphEdgeMap _edgeMap; +}; + +/** + * A panner layed over a QCanvas + */ +class PannerView: public QCanvasView +{ + Q_OBJECT + +public: + PannerView(QWidget * parent = 0, const char * name = 0); + + void setZoomRect(QRect r); + +signals: + void zoomRectMoved(int dx, int dy); + void zoomRectMoveFinished(); + +protected: + void contentsMousePressEvent(QMouseEvent*); + void contentsMouseMoveEvent(QMouseEvent*); + void contentsMouseReleaseEvent(QMouseEvent*); + void drawContents(QPainter * p, int clipx, int clipy, int clipw, int cliph); + + QRect _zoomRect; + bool _movingZoomRect; + QPoint _lastPos; +}; + + +/* + * Canvas Items: + * - CanvasNode (Rectangular Area) + * - CanvasEdge (Spline curve) + * - CanvasEdgeLabel (Label for edges) + * - CanvasEdgeArrow (Arrows at the end of the edge spline) + * - CanvasFrame (Grey background blending to show active node) + */ + +enum { + CANVAS_NODE = 1122, + CANVAS_EDGE, CANVAS_EDGELABEL, CANVAS_EDGEARROW, + CANVAS_FRAME +}; + +class CanvasNode: public QCanvasRectangle, public StoredDrawParams +{ +public: + CanvasNode(CallGraphView*,GraphNode*, int, int, int, int, QCanvas*); + + void updateGroup(); + void setSelected(bool); + void drawShape(QPainter&); + + GraphNode* node() { return _node; } + int rtti() const { return CANVAS_NODE; } + +private: + GraphNode* _node; + CallGraphView* _view; +}; + +class CanvasEdgeLabel: public QCanvasRectangle, public StoredDrawParams +{ +public: + CanvasEdgeLabel(CallGraphView*, CanvasEdge*, int, int, int, int, QCanvas*); + + void drawShape(QPainter&); + + CanvasEdge* canvasEdge() { return _ce; } + int rtti() const { return CANVAS_EDGELABEL; } + +private: + CanvasEdge* _ce; + CallGraphView* _view; +}; + +class CanvasEdgeArrow: public QCanvasPolygon +{ +public: + CanvasEdgeArrow(CanvasEdge*, QCanvas*); + + void drawShape(QPainter&); + + CanvasEdge* canvasEdge() { return _ce; } + int rtti() const { return CANVAS_EDGEARROW; } + +private: + CanvasEdge* _ce; +}; + + +class CanvasEdge: public QCanvasSpline +{ +public: + CanvasEdge(GraphEdge*, QCanvas*); + + void setSelected(bool); + void drawShape(QPainter&); + QPointArray areaPoints() const; + + CanvasEdgeLabel* label() { return _label; } + void setLabel(CanvasEdgeLabel* l) { _label = l; } + CanvasEdgeArrow* arrow() { return _arrow; } + void setArrow(CanvasEdgeArrow* a) { _arrow = a; } + + GraphEdge* edge() { return _edge; } + int rtti() const { return CANVAS_EDGE; } + +private: + GraphEdge* _edge; + CanvasEdgeLabel* _label; + CanvasEdgeArrow* _arrow; +}; + + +class CanvasFrame: public QCanvasRectangle +{ +public: + CanvasFrame( CanvasNode*, QCanvas *canvas ); + int rtti () const { return CANVAS_FRAME; } + bool hit( const QPoint&) const { return false; } +protected: + void drawShape( QPainter & ); +private: + static QPixmap* _p; +}; + + +class CallGraphTip; + +/** + * A CanvasView showing a part of the call graph + * and another zoomed out CanvasView in a border acting as + * a panner to select to visible part (only if needed) + */ +class CallGraphView: public QCanvasView, public TraceItemView, + public StorableGraphOptions +{ + Q_OBJECT + +public: + enum ZoomPosition { TopLeft, TopRight, BottomLeft, BottomRight, Auto }; + + CallGraphView(TraceItemView* parentView, + QWidget* parent=0, const char* name=0); + ~CallGraphView(); + + void readViewConfig(KConfig*, QString prefix, QString postfix, bool); + void saveViewConfig(KConfig*, QString prefix, QString postfix, bool); + + QWidget* widget() { return this; } + QString whatsThis() const; + + ZoomPosition zoomPos() const { return _zoomPosition; } + static ZoomPosition zoomPos(QString); + static QString zoomPosString(ZoomPosition); + +public slots: + void contentsMovingSlot(int,int); + void zoomRectMoved(int,int); + void zoomRectMoveFinished(); + + void showRenderWarning(); + void stopRendering(); + void readDotOutput(); + void dotExited(); + +protected: + void resizeEvent(QResizeEvent*); + void contentsMousePressEvent(QMouseEvent*); + void contentsMouseMoveEvent(QMouseEvent*); + void contentsMouseReleaseEvent(QMouseEvent*); + void contentsMouseDoubleClickEvent(QMouseEvent*); + void contentsContextMenuEvent(QContextMenuEvent*); + void keyPressEvent(QKeyEvent*); + void focusInEvent(QFocusEvent*); + void focusOutEvent(QFocusEvent*); + +private: + void updateSizes(QSize s = QSize(0,0)); + TraceItem* canShow(TraceItem*); + void doUpdate(int); + void refresh(); + void makeFrame(CanvasNode*, bool active); + void clear(); + void showText(QString); + + QCanvas *_canvas; + int _xMargin, _yMargin; + PannerView *_completeView; + double _cvZoom; + + CallGraphTip* _tip; + + bool _isMoving; + QPoint _lastPos; + + GraphExporter _exporter; + + GraphNode* _selectedNode; + GraphEdge* _selectedEdge; + + // widget options + ZoomPosition _zoomPosition, _lastAutoPosition; + + // background rendering + QProcess* _renderProcess; + QTimer _renderTimer; + GraphNode* _prevSelectedNode; + QPoint _prevSelectedPos; + QString _unparsedOutput; +}; + + + + +#endif + + + diff --git a/kcachegrind/kcachegrind/callitem.cpp b/kcachegrind/kcachegrind/callitem.cpp new file mode 100644 index 00000000..51bc19d2 --- /dev/null +++ b/kcachegrind/kcachegrind/callitem.cpp @@ -0,0 +1,185 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002, 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Items for caller/callee view. + */ + +#include +#include +#include +#include + +#include "configuration.h" +#include "listutils.h" +#include "callitem.h" +#include "callview.h" +#include "toplevel.h" + +// CallItem + + +CallItem::CallItem(CallView* view, QListView* parent, TraceCall* c) + : QListViewItem(parent) +{ + _call = c; + _view = view; + + _active = _view->activeFunction(); + bool baseIsCycle = (_active && (_active == _active->cycle())); + + QString fName; + if (_view->showCallers()) { + _shown = _call->caller(true); + fName = c->callerName(!baseIsCycle); + } + else { + _shown = _call->called(true); + fName = c->calledName(!baseIsCycle); + } + + _shown->addPrettyLocation(fName); + + setText(3, fName); + updateGroup(); + updateCost(); +} + +void CallItem::updateGroup() +{ + QColor c = Configuration::functionColor(_view->groupType(), _shown); + setPixmap(3, colorPixmap(10, 10, c)); +} + +void CallItem::updateCost() +{ + bool sameCycle = _shown->cycle() && (_active->cycle() == _shown->cycle()); + bool shownIsCycle = (_shown == _shown->cycle()); + bool selectedIsCycle = (_active == _active->cycle()); + if (_call->isRecursion()) sameCycle=true; + + QString cStr; + if ((selectedIsCycle || shownIsCycle) && sameCycle) + cStr = "-"; + else { + _cc = _call->callCount(); + if (_cc == 0) + cStr = i18n("(active)"); + else + cStr = _call->prettyCallCount(); + } + setText(2, cStr); + + TraceCost* totalCost; + if (_view->topLevel()->showExpanded()) { + if (_active->cycle()) + totalCost = _active->cycle()->inclusive(); + else + totalCost = _active->inclusive(); + } + else + totalCost = _active->data(); + + TraceCostType* ct = _view->costType(); + _sum = _call->subCost(ct); + double total = totalCost->subCost(ct); + + if (total == 0.0) { + QString str = "-"; + + setText(0, str); + setPixmap(0, QPixmap()); + } + else { + double sum = 100.0 * _sum / total; + + if (_view->topLevel()->showPercentage()) + setText(0, QString("%1") + .arg(sum, 0, 'f', Configuration::percentPrecision())); + else + setText(0, _call->prettySubCost(ct)); + + setPixmap(0, costPixmap(ct, _call, total, false)); + } + + // Cost Type 2 + TraceCostType* ct2 = _view->costType2(); + if (ct2) { + _sum2 = _call->subCost(ct2); + double total = totalCost->subCost(ct2); + + if (total == 0.0) { + QString str = "-"; + + setText(1, str); + setPixmap(1, QPixmap()); + } + else { + double sum = 100.0 * _sum2 / total; + + if (_view->topLevel()->showPercentage()) + setText(1, QString("%1") + .arg(sum, 0, 'f', Configuration::percentPrecision())); + else + setText(1, _call->prettySubCost(ct2)); + + setPixmap(1, costPixmap(ct2, _call, total, false)); + } + } + + QPixmap p; + if (sameCycle && !selectedIsCycle && !shownIsCycle) { + + QString icon = "undo"; + KIconLoader* loader = KApplication::kApplication()->iconLoader(); + p= loader->loadIcon(icon, KIcon::Small, 0, + KIcon::DefaultState, 0, true); + } + setPixmap(2, p); +} + + +int CallItem::compare(QListViewItem * i, int col, bool ascending ) const +{ + const CallItem* ci1 = this; + const CallItem* ci2 = (CallItem*) i; + + // we always want descending order + if (ascending) { + ci1 = ci2; + ci2 = this; + } + + if (col==0) { + if (ci1->_sum < ci2->_sum) return -1; + if (ci1->_sum > ci2->_sum) return 1; + return 0; + } + if (col==1) { + if (ci1->_sum2 < ci2->_sum2) return -1; + if (ci1->_sum2 > ci2->_sum2) return 1; + return 0; + } + if (col==2) { + if (ci1->_cc < ci2->_cc) return -1; + if (ci1->_cc > ci2->_cc) return 1; + return 0; + } + return QListViewItem::compare(i, col, ascending); +} + diff --git a/kcachegrind/kcachegrind/callitem.h b/kcachegrind/kcachegrind/callitem.h new file mode 100644 index 00000000..b3013b0c --- /dev/null +++ b/kcachegrind/kcachegrind/callitem.h @@ -0,0 +1,50 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Items of call view. + */ + +#ifndef CALLITEM_H +#define CALLITEM_H + +#include +#include "tracedata.h" + +class CallView; + +class CallItem: public QListViewItem +{ +public: + CallItem(CallView*, QListView*, TraceCall* c); + + int compare(QListViewItem * i, int col, bool ascending ) const; + TraceCall* call() { return _call; } + CallView* view() { return _view; } + void updateCost(); + void updateGroup(); + +private: + SubCost _sum, _sum2; + SubCost _cc; + TraceCall* _call; + CallView* _view; + TraceFunction *_active, *_shown; +}; + +#endif diff --git a/kcachegrind/kcachegrind/callmapview.cpp b/kcachegrind/kcachegrind/callmapview.cpp new file mode 100644 index 00000000..8098c6b1 --- /dev/null +++ b/kcachegrind/kcachegrind/callmapview.cpp @@ -0,0 +1,999 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Call Map View + */ + +#include +#include + +#include +#include +#include +#include + +#include "callmapview.h" +#include "configuration.h" +#include "listutils.h" +#include "toplevel.h" + +// +// CallMapView +// + + +// defaults +#define DEFAULT_SPLITMODE "Rows" +#define DEFAULT_DRAWNAME true +#define DEFAULT_DRAWCOST true +#define DEFAULT_DRAWLOCATION false +#define DEFAULT_DRAWCALLS false +#define DEFAULT_FORCESTRINGS false +#define DEFAULT_ROTATION true +#define DEFAULT_SHADING true +#define DEFAULT_MAXAREA 100 + + +CallMapView::CallMapView(bool showCallers, TraceItemView* parentView, + QWidget* parent, const char* name) + : TreeMapWidget(new CallMapBaseItem(), parent, name), TraceItemView(parentView) +{ + _showCallers = showCallers; + + setFieldType(0, i18n( "Name" )); + setFieldType(1, i18n( "Cost" )); + setFieldType(2, i18n( "Location" )); + setFieldPosition(2, TreeMapItem::TopLeft); + setFieldType(3, i18n( "Calls" )); + setFieldPosition(3, TreeMapItem::TopRight); + + setSplitMode(DEFAULT_SPLITMODE); + setFieldVisible(0, DEFAULT_DRAWNAME); + setFieldVisible(1, DEFAULT_DRAWCOST); + setFieldVisible(2, DEFAULT_DRAWLOCATION); + setFieldVisible(3, DEFAULT_DRAWCALLS); + setFieldForced(0, DEFAULT_FORCESTRINGS); + setFieldForced(1, DEFAULT_FORCESTRINGS); + setFieldForced(2, DEFAULT_FORCESTRINGS); + setFieldForced(3, DEFAULT_FORCESTRINGS); + setAllowRotation(DEFAULT_ROTATION); + setShadingEnabled(DEFAULT_SHADING); + setMinimalArea(DEFAULT_MAXAREA); + + connect(this, + SIGNAL(doubleClicked(TreeMapItem*)), + SLOT(activatedSlot(TreeMapItem*))); + connect(this, + SIGNAL(returnPressed(TreeMapItem*)), + SLOT(activatedSlot(TreeMapItem*))); + connect(this, + SIGNAL(currentChanged(TreeMapItem*, bool)), + SLOT(selectedSlot(TreeMapItem*, bool))); + connect(this, + SIGNAL(contextMenuRequested(TreeMapItem*,const QPoint &)), + SLOT(context(TreeMapItem*,const QPoint &))); + + QWhatsThis::add( this, whatsThis()); +} + +QString CallMapView::whatsThis() const +{ + QString s = _showCallers ? + i18n( "Caller Map" + "

This graph shows the nested hierarchy of " + "all callers of the current activated function. " + "Each colored rectangle represents a function; " + "its size tries to be proportional to the cost spent " + "therein while the active function is running " + "(however, there are drawing constrains).

") : + i18n("Call Map" + "

This graph shows the nested hierarchy of " + "all callees of the current activated function. " + "Each colored rectangle represents a function; " + "its size tries to be proportional to the cost spent " + "therein while the active function is running " + "(however, there are drawing constrains).

"); + + s += i18n( "

Appearance options can be found in the " + "in the context menu. To get exact size proportions, " + "choose 'Hide incorrect borders'. As this mode can be " + "very time consuming, you may want to limit " + "the maximum drawn nesting level before. " + "'Best' determinates the split direction for children " + "from the aspect ratio of the parent. " + "'Always Best' decides on remaining space for each " + "sibling. " + "'Ignore Proportions' takes space for function name " + "drawing before drawing children. Note that " + "size proportions can get heavily wrong.

" + + "

This is a TreeMap widget. " + "Keyboard navigation is available with the left/right arrow " + "keys for traversing siblings, and up/down arrow keys " + "to go a nesting level up/down. " + "Return activates the current item.

"); + + return s; +} + +void CallMapView::setData(TraceData* d) +{ + TraceItemView::setData(d); + + ((CallMapBaseItem*)base())->setFunction(0); +} + +void CallMapView::context(TreeMapItem* i,const QPoint & p) +{ + if (!i) return; + + QPopupMenu popup; + QPopupMenu fpopup; // select function subpopup + QPopupMenu vpopup; // visualisation subpopup + QPopupMenu dpopup; // split direction + QPopupMenu bpopup; // border subpopup + QPopupMenu l1popup; // depth limit subpopup + QPopupMenu l2popup; // function limit subpopup + QPopupMenu l3popup; // area limit subpopup + + TreeMapItem* item = i; + int count; + + QString shortCurrentName; + if (i) { + shortCurrentName = i->text(0); + if ((int)shortCurrentName.length() > Configuration::maxSymbolLength()) + shortCurrentName = + shortCurrentName.left(Configuration::maxSymbolLength()) + "..."; + } + + if (item) { + popup.insertItem(i18n("Go To"), &fpopup, 100); + count = 0; + while (counttext(0); + if ((int)name.length()>Configuration::maxSymbolLength()) + name = name.left(Configuration::maxSymbolLength()) + "..."; + fpopup.insertItem(name, 101+count); + item = item->parent(); + count++; + } + popup.insertSeparator(); + } + + addGoMenu(&popup); + popup.insertSeparator(); + + l1popup.setCheckable(true); + popup.insertItem(i18n("Stop at Depth"), &l1popup, 12); + + int maxDepth = maxDrawingDepth(); + l1popup.insertItem(i18n("No Depth Limit"), 50); + l1popup.setItemChecked(50, maxDepth==-1); + l1popup.insertSeparator(); + l1popup.insertItem(i18n("Depth 10"), 51); + l1popup.setItemChecked(51, maxDepth==10); + l1popup.insertItem(i18n("Depth 15"), 52); + l1popup.setItemChecked(52, maxDepth==15); + l1popup.insertItem(i18n("Depth 20"), 53); + l1popup.setItemChecked(53, maxDepth==20); + if (i) { + l1popup.insertSeparator(); + l1popup.insertItem(i18n("Depth of '%1' (%2)") + .arg(shortCurrentName).arg(i->depth()), 55); + l1popup.setItemChecked(55, maxDepth == i->depth()); + } + if (maxDepth>0) { + l1popup.insertSeparator(); + l1popup.insertItem(i18n("Decrement Depth (to %1)").arg(maxDepth-1), 56); + l1popup.insertItem(i18n("Increment Depth (to %1)").arg(maxDepth+1), 57); + } + + l2popup.setCheckable(true); + popup.insertItem(i18n("Stop at Function"), &l2popup, 13); + l2popup.insertItem(i18n("No Function Limit"), 200); + l2popup.setItemChecked(200, fieldStop(0).isEmpty()); + bool foundStopName = false; + item = i; + if (i) { + l2popup.insertSeparator(); + count = 0; + while (counttext(0); + if ((int)name.length()>Configuration::maxSymbolLength()) + name = name.left(Configuration::maxSymbolLength()) + "..."; + l2popup.insertItem(name, 201+count); + if (item->text(0) == fieldStop(0)) { + l2popup.setItemChecked(201+count, true); + foundStopName = true; + } + item = item->parent(); + count++; + } + } + if (!foundStopName && !fieldStop(0).isEmpty()) { + l2popup.insertSeparator(); + QString name = fieldStop(0); + if ((int)name.length()>Configuration::maxSymbolLength()) + name = name.left(Configuration::maxSymbolLength()) + "..."; + l2popup.insertItem(name, 199); + l2popup.setItemChecked(199, true); + } + + l3popup.setCheckable(true); + popup.insertItem(i18n("Stop at Area"), &l3popup, 14); + + int mArea = minimalArea(); + l3popup.insertItem(i18n("No Area Limit"), 60); + l3popup.setItemChecked(60, mArea ==-1); + l3popup.insertSeparator(); + l3popup.insertItem(i18n("50 Pixels"), 63); + l3popup.setItemChecked(63, mArea==50); + l3popup.insertItem(i18n("100 Pixels"), 64); + l3popup.setItemChecked(64, mArea==100); + l3popup.insertItem(i18n("200 Pixels"), 65); + l3popup.setItemChecked(65, mArea==200); + l3popup.insertItem(i18n("500 Pixels"), 66); + l3popup.setItemChecked(66, mArea==500); + int currentArea = 0; + if (i) { + currentArea = i->width() * i->height(); + l3popup.insertSeparator(); + l3popup.insertItem(i18n("Area of '%1' (%2)") + .arg(shortCurrentName).arg(currentArea), 67); + l3popup.setItemChecked(67, mArea == currentArea); + } + if (mArea>0) { + l3popup.insertSeparator(); + l3popup.insertItem(i18n("Double Area Limit (to %1)") + .arg(mArea*2), 68); + l3popup.insertItem(i18n("Half Area Limit (to %1)") + .arg(mArea/2), 69); + } + + popup.insertSeparator(); + + vpopup.setCheckable(true); + popup.insertItem(i18n("Visualisation"), &vpopup, 10); + + QPopupMenu splitpopup; + addSplitDirectionItems(&splitpopup, 1001); + vpopup.insertItem(i18n("Split Direction"), &splitpopup, 1000); + + vpopup.insertItem(i18n("Skip Incorrect Borders"), 40); + vpopup.setItemEnabled(40, !_showCallers); + vpopup.setItemChecked(40, skipIncorrectBorder()); + + bpopup.setCheckable(true); + vpopup.insertItem(i18n("Border Width"), &bpopup, 41); + bpopup.insertItem(i18n("Border 0"), 42); + bpopup.setItemEnabled(42, !_showCallers); + bpopup.setItemChecked(42, borderWidth()==0); + bpopup.insertItem(i18n("Border 1"), 43); + bpopup.setItemChecked(43, borderWidth()==1); + bpopup.insertItem(i18n("Border 2"), 44); + bpopup.setItemChecked(44, borderWidth()==2); + bpopup.insertItem(i18n("Border 3"), 45); + bpopup.setItemChecked(45, borderWidth()==3); + + vpopup.insertSeparator(); + + vpopup.insertItem(i18n("Draw Symbol Names"), 20); + vpopup.insertItem(i18n("Draw Cost"), 21); + vpopup.insertItem(i18n("Draw Location"), 22); + vpopup.insertItem(i18n("Draw Calls"), 23); + vpopup.insertSeparator(); + + vpopup.insertItem(i18n("Ignore Proportions"), 24); + vpopup.insertItem(i18n("Allow Rotation"), 25); + if (!fieldVisible(0) && + !fieldVisible(1) && + !fieldVisible(2) && + !fieldVisible(3)) { + vpopup.setItemEnabled(24, false); + vpopup.setItemEnabled(25, false); + } + else { + vpopup.setItemChecked(20,fieldVisible(0)); + vpopup.setItemChecked(21,fieldVisible(1)); + vpopup.setItemChecked(22,fieldVisible(2)); + vpopup.setItemChecked(23,fieldVisible(3)); + vpopup.setItemChecked(24,fieldForced(0)); + vpopup.setItemChecked(25,allowRotation()); + } + + vpopup.insertItem(i18n("Shading"), 26); + vpopup.setItemChecked(26,isShadingEnabled()); + + int r = popup.exec(mapToGlobal(p)); + + if (r>100 && r<150) { + r -= 100; + while (i && (r>1)) { + i=i->parent(); + r--; + } + activatedSlot(i); + return; + } + + if (r>200 && r<250) { + r -= 200; + while (i && (r>1)) { + i=i->parent(); + r--; + } + if (i) + setFieldStop(0, i->text(0)); + + return; + } + + switch(r) { + case 20: + setFieldVisible(0, !vpopup.isItemChecked(20)); + break; + + case 21: + setFieldVisible(1, !vpopup.isItemChecked(21)); + break; + + case 22: + setFieldVisible(2, !vpopup.isItemChecked(22)); + break; + + case 23: + setFieldVisible(3, !vpopup.isItemChecked(23)); + break; + + case 24: + setFieldForced(0, !vpopup.isItemChecked(24)); + setFieldForced(1, !vpopup.isItemChecked(24)); + setFieldForced(2, !vpopup.isItemChecked(24)); + setFieldForced(3, !vpopup.isItemChecked(24)); + break; + + case 25: setAllowRotation(!vpopup.isItemChecked(25)); break; + case 26: setShadingEnabled(!vpopup.isItemChecked(26)); break; + + case 40: + setSkipIncorrectBorder(!vpopup.isItemChecked(40)); + break; + + case 42: setBorderWidth(0); break; + case 43: setBorderWidth(1); break; + case 44: setBorderWidth(2); break; + case 45: setBorderWidth(3); break; + + case 50: setMaxDrawingDepth(-1); break; + case 51: setMaxDrawingDepth(10); break; + case 52: setMaxDrawingDepth(15); break; + case 53: setMaxDrawingDepth(20); break; + case 55: setMaxDrawingDepth(i->depth()); break; + case 56: setMaxDrawingDepth(maxDepth-1); break; + case 57: setMaxDrawingDepth(maxDepth+1); break; + + case 200: setFieldStop(0, QString::null); break; + + case 60: setMinimalArea(-1); break; + case 61: setMinimalArea(10); break; + case 62: setMinimalArea(20); break; + case 63: setMinimalArea(50); break; + case 64: setMinimalArea(100); break; + case 65: setMinimalArea(200); break; + case 66: setMinimalArea(500); break; + case 67: setMinimalArea(currentArea); break; + case 68: setMinimalArea(mArea*2); break; + case 69: setMinimalArea(mArea/2); break; + } +} + +void CallMapView::activatedSlot(TreeMapItem* item) +{ + if (!item) return; + + if (item->rtti() == 1) { + CallMapBaseItem* bi = (CallMapBaseItem*)item; + activated(bi->function()); + } + else if (item->rtti() == 2) { + CallMapCallingItem* ci = (CallMapCallingItem*)item; + activated(ci->function()); + } + else if (item->rtti() == 3) { + CallMapCallerItem* ci = (CallMapCallerItem*)item; + activated(ci->function()); + } +} + +void CallMapView::selectedSlot(TreeMapItem* item, bool kbd) +{ + if (!item) return; + if (item->text(0).isEmpty()) return; + + if (kbd) { + QString msg = i18n("Call Map: Current is '%1'").arg(item->text(0)); + if (_topLevel) + _topLevel->showMessage(msg, 5000); + } + + TraceFunction* f = 0; + + if (item->rtti() == 1) { + CallMapBaseItem* bi = (CallMapBaseItem*)item; + f = bi->function(); + } + else if (item->rtti() == 2) { + CallMapCallingItem* ci = (CallMapCallingItem*)item; + f = ci->function(); + } + else if (item->rtti() == 3) { + CallMapCallerItem* ci = (CallMapCallerItem*)item; + f = ci->function(); + } + if (f) { + // this avoids marking + _selectedItem = f; + selected(f); + } +} + +TraceItem* CallMapView::canShow(TraceItem* i) +{ + TraceItem::CostType t = i ? i->type() : TraceItem::NoCostType; + + switch(t) { + case TraceItem::Function: + case TraceItem::FunctionCycle: + return i; + default: + break; + } + return 0; +} + +void CallMapView::doUpdate(int changeType) +{ + if (changeType == costType2Changed) return; + + // if there is a selected item, always draw marking... + if (changeType & selectedItemChanged) { + TraceFunction* f = 0; + + if (_selectedItem) { + switch(_selectedItem->type()) { + case TraceItem::Function: + case TraceItem::FunctionCycle: + f = (TraceFunction*)_selectedItem; + break; + default: + break; + } + } + // if this is the only change... + if (changeType == selectedItemChanged) { + setMarked(f ? 1:0, true); + return; + } + setMarked(f ? 1:0, false); + } + + + if (changeType & activeItemChanged) { + TraceFunction* f = 0; + + if (_activeItem) { + switch(_activeItem->type()) { + case TraceItem::Function: + case TraceItem::FunctionCycle: + f = (TraceFunction*)_activeItem; + break; + default: + break; + } + } + ((CallMapBaseItem*)base())->setFunction(f); + } + else if ( ((changeType & partsChanged) && Configuration::showCycles()) || + (changeType & dataChanged) || + (changeType & configChanged)) { + /* regenerates the treemap because traceitems were added/removed */ + base()->refresh(); + } + else if ((changeType & partsChanged) || + (changeType & costTypeChanged)) { + /* we need to do the draw order sorting again as the values change */ + resort(); + redraw(); + } + else + redraw(); +} + + + +QColor CallMapView::groupColor(TraceFunction* f) const +{ + if (!f) + return colorGroup().button(); + + return Configuration::functionColor(_groupType, f); +} + + +QString CallMapView::tipString(TreeMapItem* i) const +{ + QString tip, itemTip; + int count = 0; + + //qDebug("CallMapView::tipString for '%s'", i->text(0).ascii()); + + // first, SubPartItem's + while (i && counttext(0); + if ((int)itemTip.length()>Configuration::maxSymbolLength()) + itemTip = itemTip.left(Configuration::maxSymbolLength()) + "..."; + + if (!i->text(1).isEmpty()) + itemTip += " (" + i->text(1) + ")"; + + if (!tip.isEmpty()) tip += "\n"; + + tip += itemTip; + i = i->parent(); + count++; + } + if (count == Configuration::maxSymbolCount()) tip += "\n..."; + + return tip; +} + + +TraceCost* CallMapView::totalCost() +{ + TraceFunction* f = ((CallMapBaseItem*)base())->function(); + if (!f) return 0; + + return Configuration::showExpanded() ? f->inclusive() : f->data(); +} + + + + +// CallMapBaseItem + +CallMapBaseItem::CallMapBaseItem() +{ + _f = 0; +} + +void CallMapBaseItem::setFunction(TraceFunction* f) +{ + if (f == _f) return; + + _f = f; + refresh(); +} + + +QString CallMapBaseItem::text(int textNo) const +{ + if (textNo == 0) { + if (!_f) + return i18n("(no function)"); + + return _f->prettyName(); + } + + if (!_f) return QString::null; + + if (textNo == 2) return _f->prettyLocation(); + if (textNo == 3) return _f->calledCount().pretty(); + if (textNo != 1) return QString::null; + + TraceCostType* ct = ((CallMapView*)widget())->costType(); + TraceCost* t = ((CallMapView*)widget())->totalCost(); + + if (Configuration::showPercentage()) { + double sum, total = t->subCost(ct); + if (total == 0.0) + sum = 100.0; + else + sum = 100.0 * _f->inclusive()->subCost(ct) / total; + + return QString("%1 %") + .arg(sum, 0, 'f', Configuration::percentPrecision()); + } + return _f->inclusive()->prettySubCost(ct); +} + +QPixmap CallMapBaseItem::pixmap(int i) const +{ + if ((i != 1) || !_f) return QPixmap(); + + TraceCostType* ct = ((CallMapView*)widget())->costType(); + TraceCost* t = ((CallMapView*)widget())->totalCost(); + + // colored level meter with frame + return costPixmap( ct, _f->inclusive(), (double) (t->subCost(ct)), true); +} + + +double CallMapBaseItem::value() const +{ + if (!_f) return 0.0; + + TraceCostType* ct; + ct = ((CallMapView*)widget())->costType(); + return (double) _f->inclusive()->subCost(ct); +} + + +double CallMapBaseItem::sum() const +{ + if (!_f) return 0.0; + + CallMapView* w = (CallMapView*)widget(); + + if (w->showCallers()) + return 0.0; + else + return (double) _f->inclusive()->subCost(w->costType()); +} + + +bool CallMapBaseItem::isMarked(int) const +{ + return ((CallMapView*)widget())->selectedItem() == _f; +} + +TreeMapItemList* CallMapBaseItem::children() +{ + if (_f && !initialized()) { + CallMapView* w = (CallMapView*)widget(); + + if (0) qDebug("Create Function %s (%s)", + w->showCallers() ? "Callers":"Callees", + text(0).ascii()); + + TraceCall* call; + + setSorting(-1); + if (w->showCallers()) { + TraceCallList l = _f->callers(); + for (call=l.first();call;call=l.next()) { + + // don't show calls inside of a cycle + if (call->inCycle()>0) continue; + if (call->isRecursion()) continue; + + addItem(new CallMapCallerItem(1.0, call)); + } + + setSum(0); + } + else { + TraceCallList l = _f->callings(); + for (call=l.first();call;call=l.next()) { + + // don't show calls inside of a cycle + if (call->inCycle()>0) continue; + if (call->isRecursion()) continue; + + CallMapCallingItem* i = new CallMapCallingItem(1.0, call); + i->init(); + addItem(i); + } + + setSum(_f->inclusive()->subCost(w->costType())); + } + setSorting(-2, false); + } + + return _children; +} + +QColor CallMapBaseItem::backColor() const +{ + return ((CallMapView*)widget())->groupColor(_f); +} + + + +// CallMapCallingItems + +CallMapCallingItem::CallMapCallingItem(double factor, TraceCall* c) +{ + _factor = factor; + _c = c; +} + +void CallMapCallingItem::init() +{ +#if 0 + // create assoziation: if not possible, i.e. an ass. already exists + // for the function, we need to draw the recursive version + _recursive = !setFunction(_c->called()); + _valid = true; +#endif +} + +QString CallMapCallingItem::text(int textNo) const +{ + if (textNo == 0) { + if (!_c) + return i18n("(no call)"); + + return _c->calledName(); + } + + if (textNo == 2) return _c->called()->prettyLocation(); + if (textNo == 3) return SubCost(_factor * _c->callCount()).pretty(); + if (textNo != 1) return QString::null; + + TraceCostType* ct; + ct = ((CallMapView*)widget())->costType(); + + SubCost val = SubCost(_factor * _c->subCost(ct)); + if (Configuration::showPercentage()) { + // percentage relative to function cost + TraceCost* t = ((CallMapView*)widget())->totalCost(); + double p = 100.0 * _factor * _c->subCost(ct) / t->subCost(ct); + return QString("%1 %") + .arg(p, 0, 'f', Configuration::percentPrecision()); + } + return val.pretty(); +} + +QPixmap CallMapCallingItem::pixmap(int i) const +{ + if (i != 1) return QPixmap(); + + // Cost pixmap + TraceCostType* ct = ((CallMapView*)widget())->costType(); + TraceCost* t = ((CallMapView*)widget())->totalCost(); + + // colored level meter with frame + return costPixmap( ct, _c, t->subCost(ct) / _factor, true); +} + + +double CallMapCallingItem::value() const +{ + TraceCostType* ct; + ct = ((CallMapView*)widget())->costType(); + return _factor * _c->subCost(ct); +} + +double CallMapCallingItem::sum() const +{ + return value(); +} + +bool CallMapCallingItem::isMarked(int) const +{ + return ((CallMapView*)widget())->selectedItem() == _c->called(); +} + + +TreeMapItemList* CallMapCallingItem::children() +{ + if (!initialized()) { + if (0) qDebug("Create Calling subitems (%s)", path(0).join("/").ascii()); + + TraceCostType* ct; + ct = ((CallMapView*)widget())->costType(); + + // same as sum() + SubCost s = _c->called()->inclusive()->subCost(ct); + SubCost v = _c->subCost(ct); + if (v>s) { + qDebug("Warning: CallingItem subVal %u > Sum %u (%s)", + (unsigned)v, (unsigned)s, _c->called()->prettyName().ascii()); + v = s; + } + double newFactor = _factor * v / s; + +#if 0 + qDebug("CallingItem: Subitems of %s => %s, factor %f * %d/%d => %f", + _c->caller()->prettyName().ascii(), + _c->called()->prettyName().ascii(), + _factor, v, s, newFactor); +#endif + setSorting(-1); + TraceCall* call; + TraceCallList l = _c->called()->callings(); + for (call=l.first();call;call=l.next()) { + + // don't show calls inside of a cycle + if (call->inCycle()>0) continue; + if (call->isRecursion()) continue; + + CallMapCallingItem* i = new CallMapCallingItem(newFactor, call); + i->init(); + addItem(i); + } + setSorting(-2, false); + } + + return _children; +} + + +QColor CallMapCallingItem::backColor() const +{ + CallMapView* w = (CallMapView*)widget(); + return w->groupColor(_c->called()); +} + + +// CallMapCallerItem + +CallMapCallerItem::CallMapCallerItem(double factor, TraceCall* c) +{ + _factor = factor; + _c = c; +} + +QString CallMapCallerItem::text(int textNo) const +{ + if (textNo == 0) { + if (!_c) + return i18n("(no call)"); + + return _c->callerName(); + } + + if (textNo == 2) return _c->caller()->prettyLocation(); + if (textNo == 3) return SubCost(_factor * _c->callCount()).pretty(); + if (textNo != 1) return QString::null; + + TraceCostType* ct; + ct = ((CallMapView*)widget())->costType(); + + SubCost val = SubCost(_factor * _c->subCost(ct)); + if (Configuration::showPercentage()) { + TraceCost* t = ((CallMapView*)widget())->totalCost(); + double p = 100.0 * _factor * _c->subCost(ct) / t->subCost(ct); + return QString("%1 %") + .arg(p, 0, 'f', Configuration::percentPrecision()); + } + return val.pretty(); +} + + +QPixmap CallMapCallerItem::pixmap(int i) const +{ + if (i != 1) return QPixmap(); + + // Cost pixmap + TraceCostType* ct = ((CallMapView*)widget())->costType(); + TraceCost* t = ((CallMapView*)widget())->totalCost(); + + // colored level meter with frame + return costPixmap( ct, _c, t->subCost(ct) / _factor, true ); +} + + +double CallMapCallerItem::value() const +{ + TraceCostType* ct; + ct = ((CallMapView*)widget())->costType(); + return (double) _c->subCost(ct); +} + +bool CallMapCallerItem::isMarked(int) const +{ + return ((CallMapView*)widget())->selectedItem() == _c->caller(); +} + + +TreeMapItemList* CallMapCallerItem::children() +{ + if (!initialized()) { + //qDebug("Create Caller subitems (%s)", name().ascii()); + + TraceCostType* ct; + ct = ((CallMapView*)widget())->costType(); + + SubCost s = _c->caller()->inclusive()->subCost(ct); + SubCost v = _c->subCost(ct); + double newFactor = _factor * v / s; + + +#if 0 + qDebug("CallerItem: Subitems of %s => %s, factor %f * %d/%d => %f", + _c->caller()->prettyName().ascii(), + _c->called()->prettyName().ascii(), + _factor, v, s, newFactor); +#endif + setSorting(-1); + + TraceCall* call; + TraceCallList l = _c->caller()->callers(); + for (call=l.first();call;call=l.next()) { + + // don't show calls inside of a cycle + if (call->inCycle()>0) continue; + if (call->isRecursion()) continue; + + TreeMapItem* i = new CallMapCallerItem(newFactor, call); + addItem(i); + } + setSorting(-2, false); + } + + return _children; +} + +QColor CallMapCallerItem::backColor() const +{ + CallMapView* w = (CallMapView*)widget(); + return w->groupColor(_c->caller()); +} + +void CallMapView::readViewConfig(KConfig* c, + QString prefix, QString postfix, bool) +{ + KConfigGroup* g = configGroup(c, prefix, postfix); + + setSplitMode(g->readEntry("SplitMode", DEFAULT_SPLITMODE)); + + setFieldVisible(0, g->readBoolEntry("DrawName", DEFAULT_DRAWNAME)); + setFieldVisible(1, g->readBoolEntry("DrawCost", DEFAULT_DRAWCOST)); + setFieldVisible(2, g->readBoolEntry("DrawLocation", DEFAULT_DRAWLOCATION)); + setFieldVisible(3, g->readBoolEntry("DrawCalls", DEFAULT_DRAWCALLS)); + + bool enable = g->readBoolEntry("ForceStrings", DEFAULT_FORCESTRINGS); + setFieldForced(0, enable); + setFieldForced(1, enable); + setFieldForced(2, enable); + setFieldForced(3, enable); + + setAllowRotation(g->readBoolEntry("AllowRotation", DEFAULT_ROTATION)); + setShadingEnabled(g->readBoolEntry("Shading", DEFAULT_SHADING)); + setFieldStop(0, g->readEntry("StopName")); + setMaxDrawingDepth(g->readNumEntry("MaxDepth", -1)); + setMinimalArea(g->readNumEntry("MaxArea", DEFAULT_MAXAREA)); + + delete g; +} + +void CallMapView::saveViewConfig(KConfig* c, + QString prefix, QString postfix, bool) +{ + KConfigGroup g(c, (prefix+postfix).ascii()); + + writeConfigEntry(&g, "SplitMode", splitModeString(), DEFAULT_SPLITMODE); + writeConfigEntry(&g, "DrawName", fieldVisible(0), DEFAULT_DRAWNAME); + writeConfigEntry(&g, "DrawCost", fieldVisible(1), DEFAULT_DRAWCOST); + writeConfigEntry(&g, "DrawLocation", fieldVisible(2), DEFAULT_DRAWLOCATION); + writeConfigEntry(&g, "DrawCalls", fieldVisible(3), DEFAULT_DRAWCALLS); + // when option for all text (0-3) + writeConfigEntry(&g, "ForceStrings", fieldForced(0), DEFAULT_FORCESTRINGS); + + writeConfigEntry(&g, "AllowRotation", allowRotation(), DEFAULT_ROTATION); + writeConfigEntry(&g, "Shading", isShadingEnabled(), DEFAULT_SHADING); + + writeConfigEntry(&g, "StopName", fieldStop(0), ""); + writeConfigEntry(&g, "MaxDepth", maxDrawingDepth(), -1); + writeConfigEntry(&g, "MaxArea", minimalArea(), DEFAULT_MAXAREA); +} + +#include "callmapview.moc" diff --git a/kcachegrind/kcachegrind/callmapview.h b/kcachegrind/kcachegrind/callmapview.h new file mode 100644 index 00000000..8561a0ac --- /dev/null +++ b/kcachegrind/kcachegrind/callmapview.h @@ -0,0 +1,129 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Call Map View + */ + +#ifndef CALLMAPVIEW_H +#define CALLMAPVIEW_H + +#include "treemap.h" +#include "tracedata.h" +#include "traceitemview.h" + +class CallMapView: public TreeMapWidget, public TraceItemView +{ + Q_OBJECT + +public: + + CallMapView(bool showCallers, TraceItemView* parentView, + QWidget* parent=0, const char* name=0); + + QWidget* widget() { return this; } + QString whatsThis() const; + void setData(TraceData*); + + void readViewConfig(KConfig*, QString prefix, QString postfix, bool); + void saveViewConfig(KConfig*, QString prefix, QString postfix, bool); + + bool showCallers() const { return _showCallers; } + TraceCost* totalCost(); + QString tipString(TreeMapItem*) const; + QColor groupColor(TraceFunction*) const; + +private slots: + void context(TreeMapItem*,const QPoint &); + void selectedSlot(TreeMapItem*, bool); + void activatedSlot(TreeMapItem*); + +private: + TraceItem* canShow(TraceItem*); + void doUpdate(int); + + bool _showCallers; +}; + + + +// Subitems of CallMap + +class CallMapBaseItem: public TreeMapItem +{ +public: + CallMapBaseItem(); + + void setFunction(TraceFunction* f); + TraceFunction* function() { return _f; } + int rtti() const { return 1; } + double sum() const; + double value() const ; + bool isMarked(int) const; + QString text(int) const; + QPixmap pixmap(int) const; + TreeMapItemList* children(); + QColor backColor() const; + +private: + TraceFunction* _f; +}; + + +class CallMapCallingItem: public TreeMapItem +{ +public: + CallMapCallingItem(double factor, TraceCall* c); + void init(); + int rtti() const { return 2; } + int borderWidth() const { return widget()->borderWidth(); } + TraceFunction* function() { return _c->called(); } + double value() const; + double sum() const; + bool isMarked(int) const; + QString text(int) const; + QPixmap pixmap(int) const; + TreeMapItemList* children(); + QColor backColor() const; + +private: + TraceCall* _c; + double _factor; +}; + +class CallMapCallerItem: public TreeMapItem +{ +public: + CallMapCallerItem(double factor, TraceCall* c); + int rtti() const { return 3; } + int borderWidth() const { return widget()->borderWidth(); } + TraceFunction* function() { return _c->caller(); } + double value() const; + bool isMarked(int) const; + QString text(int) const; + QPixmap pixmap(int) const; + TreeMapItemList* children(); + QColor backColor() const; + +private: + TraceCall* _c; + double _factor; +}; + + +#endif diff --git a/kcachegrind/kcachegrind/callview.cpp b/kcachegrind/kcachegrind/callview.cpp new file mode 100644 index 00000000..edd7ee6a --- /dev/null +++ b/kcachegrind/kcachegrind/callview.cpp @@ -0,0 +1,256 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Call Views + */ + +#include +#include +#include + +#include "configuration.h" +#include "callitem.h" +#include "callview.h" + + + +// +// CallView +// + + +CallView::CallView(bool showCallers, TraceItemView* parentView, + QWidget* parent, const char* name) + : QListView(parent, name), TraceItemView(parentView) +{ + _showCallers = showCallers; + + addColumn( i18n( "Cost" ) ); + addColumn( i18n( "Cost 2" ) ); + if (_showCallers) { + addColumn( i18n( "Count" ) ); + addColumn( i18n( "Caller" ) ); + } + else { + addColumn( i18n( "Count" ) ); + addColumn( i18n( "Callee" ) ); + } + + setSorting(0,false); + setColumnAlignment(0, Qt::AlignRight); + setColumnAlignment(1, Qt::AlignRight); + setColumnAlignment(2, Qt::AlignRight); + setAllColumnsShowFocus(true); + setResizeMode(QListView::LastColumn); + setMinimumHeight(50); + + connect( this, + SIGNAL( selectionChanged(QListViewItem*) ), + SLOT( selectedSlot(QListViewItem*) ) ); + + connect( this, + SIGNAL(contextMenuRequested(QListViewItem*, const QPoint &, int)), + SLOT(context(QListViewItem*, const QPoint &, int))); + + connect(this, + SIGNAL(doubleClicked(QListViewItem*)), + SLOT(activatedSlot(QListViewItem*))); + + connect(this, + SIGNAL(returnPressed(QListViewItem*)), + SLOT(activatedSlot(QListViewItem*))); + + QWhatsThis::add( this, whatsThis() ); +} + +QString CallView::whatsThis() const +{ + return _showCallers ? + i18n( "List of direct Callers" + "

This list shows all functions calling the " + "current selected one directly, together with " + "a call count and the cost spent in the current " + "selected function while being called from the " + "function from the list.

" + "

An icon instead of an inclusive cost specifies " + "that this is a call inside of a recursive cycle. " + "An inclusive cost makes no sense here.

" + "

Selecting a function makes it the current selected " + "one of this information panel. " + "If there are two panels (Split mode), the " + "function of the other panel is changed instead.

") : + i18n( "List of direct Callees" + "

This list shows all functions called by the " + "current selected one directly, together with " + "a call count and the cost spent in this function " + "while being called from the selected function.

" + "

Selecting a function makes it the current selected " + "one of this information panel. " + "If there are two panels (Split mode), the " + "function of the other panel is changed instead.

"); +} + + +void CallView::context(QListViewItem* i, const QPoint & p, int col) +{ + QPopupMenu popup; + + // Menu entry: + TraceCall* c = i ? ((CallItem*) i)->call() : 0; + TraceFunction *f = 0, *cycle = 0; + + if (c) { + QString name = _showCallers ? c->callerName(true) : c->calledName(true); + f = _showCallers ? c->caller(true) : c->called(true); + cycle = f->cycle(); + + popup.insertItem(i18n("Go to '%1'") + .arg(Configuration::shortenSymbol(name)), 93); + + if (cycle) { + name = Configuration::shortenSymbol(cycle->prettyName()); + popup.insertItem(i18n("Go to '%1'").arg(name), 94); + } + + popup.insertSeparator(); + } + + if ((col == 0) || (col == 1)) { + addCostMenu(&popup); + popup.insertSeparator(); + } + addGoMenu(&popup); + + int r = popup.exec(p); + if (r == 93) activated(f); + else if (r == 94) activated(cycle); +} + +void CallView::selectedSlot(QListViewItem * i) +{ + if (!i) return; + TraceCall* c = ((CallItem*) i)->call(); + // Should we skip cycles here? + TraceItem* f = _showCallers ? c->caller(false) : c->called(false); + + _selectedItem = f; + selected(f); +} + +void CallView::activatedSlot(QListViewItem * i) +{ + if (!i) return; + TraceCall* c = ((CallItem*) i)->call(); + // skip cycles: use the context menu to get to the cycle... + TraceItem* f = _showCallers ? c->caller(true) : c->called(true); + + activated(f); +} + +TraceItem* CallView::canShow(TraceItem* i) +{ + TraceItem::CostType t = i ? i->type() : TraceItem::NoCostType; + + switch(t) { + case TraceItem::Function: + case TraceItem::FunctionCycle: + return i; + default: + break; + } + return 0; +} + +void CallView::doUpdate(int changeType) +{ + // Special case ? + if (changeType == selectedItemChanged) { + + if (!_selectedItem) { + clearSelection(); + return; + } + + CallItem* ci = (CallItem*) QListView::selectedItem(); + TraceCall* c; + TraceItem* ti; + if (ci) { + c = ci->call(); + ti = _showCallers ? c->caller() : c->called(); + if (ti == _selectedItem) return; + } + + QListViewItem *item; + for (item = firstChild();item;item = item->nextSibling()) { + c = ((CallItem*) item)->call(); + ti = _showCallers ? c->caller() : c->called(); + if (ti == _selectedItem) { + ensureItemVisible(item); + setSelected(item, true); + break; + } + } + if (!item && ci) clearSelection(); + return; + } + + if (changeType == groupTypeChanged) { + QListViewItem *item; + for (item = firstChild();item;item = item->nextSibling()) + ((CallItem*)item)->updateGroup(); + return; + } + + refresh(); +} + +void CallView::refresh() +{ + clear(); + setColumnWidth(0, 50); + setColumnWidth(1, _costType2 ? 50:0); + setColumnWidth(2, 50); + if (_costType) + setColumnText(0, _costType->name()); + if (_costType2) + setColumnText(1, _costType2->name()); + + if (!_data || !_activeItem) return; + + TraceFunction* f = activeFunction(); + if (!f) return; + + TraceCall* call; + // In the call lists, we skip cycles to show the real call relations + TraceCallList l = _showCallers ? f->callers(true) : f->callings(true); + + // Allow resizing of column 1 + setColumnWidthMode(1, QListView::Maximum); + + for (call=l.first();call;call=l.next()) + if (call->subCost(_costType)>0) + new CallItem(this, this, call); + + if (!_costType2) { + setColumnWidthMode(1, QListView::Manual); + setColumnWidth(1, 0); + } +} + +#include "callview.moc" diff --git a/kcachegrind/kcachegrind/callview.h b/kcachegrind/kcachegrind/callview.h new file mode 100644 index 00000000..7b5adc79 --- /dev/null +++ b/kcachegrind/kcachegrind/callview.h @@ -0,0 +1,55 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Call Views + */ + +#ifndef CALLVIEW_H +#define CALLVIEW_H + +#include +#include "tracedata.h" +#include "traceitemview.h" + +class CallView: public QListView, public TraceItemView +{ + Q_OBJECT + +public: + CallView(bool showCallers, TraceItemView* parentView, + QWidget* parent=0, const char* name=0); + + virtual QWidget* widget() { return this; } + QString whatsThis() const; + bool showCallers() const { return _showCallers; } + +private slots: + void context(QListViewItem*,const QPoint &, int); + void selectedSlot(QListViewItem*); + void activatedSlot(QListViewItem*); + +private: + TraceItem* canShow(TraceItem*); + void doUpdate(int); + void refresh(); + + bool _showCallers; +}; + +#endif diff --git a/kcachegrind/kcachegrind/configdlg.cpp b/kcachegrind/kcachegrind/configdlg.cpp new file mode 100644 index 00000000..61e85dff --- /dev/null +++ b/kcachegrind/kcachegrind/configdlg.cpp @@ -0,0 +1,398 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002, 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Configuration Dialog for KCachegrind + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "configdlg.h" +#include "tracedata.h" +#include "configuration.h" + + +ConfigDlg::ConfigDlg(Configuration* c, TraceData* data, + QWidget* parent, const char* name) + :ConfigDlgBase(parent, name) +{ + _config = c; + _data = data; + _objectCS = 0; + _classCS = 0; + _fileCS = 0; + KIntValidator * numValidator = new KIntValidator( this ); + maxListEdit->setValidator(numValidator ); + symbolCount->setValidator(numValidator ); + symbolLength->setValidator(numValidator ); + precisionEdit->setValidator(numValidator ); + contextEdit->setValidator(numValidator ); + +#if 0 + QListViewItem *oItem, *fItem, *cItem, *fnItem; + oItem = new(colorList, i18n("ELF Objects")); + + fItem = new(colorList, i18n("Source Files")); + cItem = new(colorList, i18n("C++ Classes")); + fnItem = new(colorList, i18n("Function (no Grouping)")); +#endif + + connect(objectCombo, SIGNAL(activated(const QString &)), + this, SLOT(objectActivated(const QString &))); + connect(objectCombo, SIGNAL(textChanged(const QString &)), + this, SLOT(objectActivated(const QString &))); + connect(objectCheck, SIGNAL(toggled(bool)), + this, SLOT(objectCheckChanged(bool))); + connect(objectColor, SIGNAL(changed(const QColor &)), + this, SLOT(objectColorChanged(const QColor &))); + + connect(classCombo, SIGNAL(activated(const QString &)), + this, SLOT(classActivated(const QString &))); + connect(classCombo, SIGNAL(textChanged(const QString &)), + this, SLOT(classActivated(const QString &))); + connect(classCheck, SIGNAL(toggled(bool)), + this, SLOT(classCheckChanged(bool))); + connect(classColor, SIGNAL(changed(const QColor &)), + this, SLOT(classColorChanged(const QColor &))); + + connect(fileCombo, SIGNAL(activated(const QString &)), + this, SLOT(fileActivated(const QString &))); + connect(fileCombo, SIGNAL(textChanged(const QString &)), + this, SLOT(fileActivated(const QString &))); + connect(fileCheck, SIGNAL(toggled(bool)), + this, SLOT(fileCheckChanged(bool))); + connect(fileColor, SIGNAL(changed(const QColor &)), + this, SLOT(fileColorChanged(const QColor &))); + + QString objectPrefix = TraceCost::typeName(TraceCost::Object); + QString classPrefix = TraceCost::typeName(TraceCost::Class); + QString filePrefix = TraceCost::typeName(TraceCost::File); + + objectCombo->setDuplicatesEnabled(false); + classCombo->setDuplicatesEnabled(false); + fileCombo->setDuplicatesEnabled(false); + objectCombo->setAutoCompletion(true); + classCombo->setAutoCompletion(true); + fileCombo->setAutoCompletion(true); + + // first unspecified cost items from data + TraceObjectMap::Iterator oit; + QStringList oList; + for ( oit = data->objectMap().begin(); + oit != data->objectMap().end(); ++oit ) + oList.append((*oit).prettyName()); + + TraceClassMap::Iterator cit; + QStringList cList; + for ( cit = data->classMap().begin(); + cit != data->classMap().end(); ++cit ) + cList.append((*cit).prettyName()); + + TraceFileMap::Iterator fit; + QStringList fList; + for ( fit = data->fileMap().begin(); + fit != data->fileMap().end(); ++fit ) + fList.append((*fit).prettyName()); + + // then already defined colors (have to check for duplicates!) + QDictIterator it( c->_colors ); + for( ; it.current(); ++it ) { + if ((*it)->automatic) continue; + + QString n = it.currentKey(); + if (n.startsWith(objectPrefix)) { + n = n.remove(0, objectPrefix.length()+1); + if (oList.findIndex(n) == -1) oList.append(n); + } + else if (n.startsWith(classPrefix)) { + n = n.remove(0, classPrefix.length()+1); + if (cList.findIndex(n) == -1) cList.append(n); + } + else if (n.startsWith(filePrefix)) { + n = n.remove(0, filePrefix.length()+1); + if (fList.findIndex(n) == -1) fList.append(n); + } + } + + oList.sort(); + cList.sort(); + fList.sort(); + objectCombo->insertStringList(oList); + classCombo->insertStringList(cList); + fileCombo->insertStringList(fList); + + objectActivated(objectCombo->currentText()); + classActivated(classCombo->currentText()); + fileActivated(fileCombo->currentText()); + + maxListEdit->setText(QString::number(c->_maxListCount)); + + _dirItem = 0; + + QListViewItem* i = new QListViewItem(dirList, i18n("(always)")); + i->setOpen(true); + QStringList::Iterator sit = c->_generalSourceDirs.begin(); + for(; sit != c->_generalSourceDirs.end(); ++sit ) { + QString d = (*sit); + if (d.isEmpty()) d = "/"; + new QListViewItem(i, d); + } + for ( oit = data->objectMap().begin(); + oit != data->objectMap().end(); ++oit ) { + QString n = (*oit).name(); + i = new QListViewItem(dirList, n); + i->setOpen(true); + QStringList* dirs = c->_objectSourceDirs[n]; + if (!dirs) continue; + + sit = dirs->begin(); + for(; sit != dirs->end(); ++sit ) { + QString d = (*sit); + if (d.isEmpty()) d = "/"; + new QListViewItem(i, d); + } + } + + connect(dirList, SIGNAL(selectionChanged(QListViewItem*)), + this, SLOT(dirsItemChanged(QListViewItem*))); + connect(addDirButton, SIGNAL(clicked()), + this, SLOT(dirsAddPressed())); + connect(deleteDirButton, SIGNAL(clicked()), + this, SLOT(dirsDeletePressed())); + dirList->setSelected(dirList->firstChild(), true); + + symbolCount->setText(QString::number(c->_maxSymbolCount)); + symbolLength->setText(QString::number(c->_maxSymbolLength)); + precisionEdit->setText(QString::number(c->_percentPrecision)); + contextEdit->setText(QString::number(c->_context)); +} + +ConfigDlg::~ConfigDlg() +{ +} + +bool ConfigDlg::configure(Configuration* c, TraceData* d, QWidget* p) +{ + ConfigDlg dlg(c, d, p); + + if (dlg.exec()) { + + bool ok; + int newValue = dlg.maxListEdit->text().toUInt(&ok); + if (ok && newValue < 500) + c->_maxListCount = newValue; + else + QMessageBox::warning(p, i18n("KCachegrind Configuration"), + i18n("The Maximum Number of List Items should be below 500." + "The previous set value (%1) will still be used.") + .arg(QString::number(c->_maxListCount)), + QMessageBox::Ok, 0); + + c->_maxSymbolCount = dlg.symbolCount->text().toInt(); + c->_maxSymbolLength = dlg.symbolLength->text().toInt(); + c->_percentPrecision = dlg.precisionEdit->text().toInt(); + c->_context = dlg.contextEdit->text().toInt(); + return true; + } + return false; +} + +void ConfigDlg::objectActivated(const QString & s) +{ +// qDebug("objectActivated: %s", s.ascii()); + + if (s.isEmpty()) { _objectCS=0; return; } + + QString n = TraceCost::typeName(TraceCost::Object) + "-" + s; + + Configuration* c = Configuration::config(); + Configuration::ColorSetting* cs = c->_colors[n]; + if (!cs) + cs = Configuration::color(n); +// else +// qDebug("found color %s", n.ascii()); + + _objectCS = cs; + + objectCheck->setChecked(cs->automatic); + objectColor->setColor(cs->color); + + /* + qDebug("Found Color %s, automatic to %s", + _objectCS->name.ascii(), + _objectCS->automatic ? "true":"false"); + */ +} + + +void ConfigDlg::objectCheckChanged(bool b) +{ + if (_objectCS) { + _objectCS->automatic = b; + /* + qDebug("Set Color %s automatic to %s", + _objectCS->name.ascii(), + _objectCS->automatic ? "true":"false"); + */ + } +} + +void ConfigDlg::objectColorChanged(const QColor & c) +{ + if (_objectCS) _objectCS->color = c; +} + +void ConfigDlg::classActivated(const QString & s) +{ +// qDebug("classActivated: %s", s.ascii()); + + if (s.isEmpty()) { _classCS=0; return; } + + QString n = TraceCost::typeName(TraceCost::Class) + "-" + s; + + Configuration* c = Configuration::config(); + Configuration::ColorSetting* cs = c->_colors[n]; + if (!cs) + cs = Configuration::color(n); + + _classCS = cs; + + classCheck->setChecked(cs->automatic); + classColor->setColor(cs->color); + +} + + +void ConfigDlg::classCheckChanged(bool b) +{ + if (_classCS) _classCS->automatic = b; +} + +void ConfigDlg::classColorChanged(const QColor & c) +{ + if (_classCS) _classCS->color = c; +} + + +void ConfigDlg::fileActivated(const QString & s) +{ +// qDebug("fileActivated: %s", s.ascii()); + + if (s.isEmpty()) { _fileCS=0; return; } + + QString n = TraceCost::typeName(TraceCost::File) + "-" + s; + + Configuration* c = Configuration::config(); + Configuration::ColorSetting* cs = c->_colors[n]; + if (!cs) + cs = Configuration::color(n); + + _fileCS = cs; + + fileCheck->setChecked(cs->automatic); + fileColor->setColor(cs->color); +} + + +void ConfigDlg::fileCheckChanged(bool b) +{ + if (_fileCS) _fileCS->automatic = b; +} + +void ConfigDlg::fileColorChanged(const QColor & c) +{ + if (_fileCS) _fileCS->color = c; +} + + +void ConfigDlg::dirsItemChanged(QListViewItem* i) +{ + _dirItem = i; + deleteDirButton->setEnabled(i->depth() == 1); + addDirButton->setEnabled(i->depth() == 0); +} + +void ConfigDlg::dirsDeletePressed() +{ + if (!_dirItem || (_dirItem->depth() == 0)) return; + QListViewItem* p = _dirItem->parent(); + if (!p) return; + + Configuration* c = Configuration::config(); + QString objName = p->text(0); + + QStringList* dirs; + if (objName == i18n("(always)")) + dirs = &(c->_generalSourceDirs); + else + dirs = c->_objectSourceDirs[objName]; + if (!dirs) return; + + dirs->remove(_dirItem->text(0)); + delete _dirItem; + _dirItem = 0; + + deleteDirButton->setEnabled(false); +} + +void ConfigDlg::dirsAddPressed() +{ + if (!_dirItem || (_dirItem->depth() >0)) return; + + Configuration* c = Configuration::config(); + QString objName = _dirItem->text(0); + + QStringList* dirs; + if (objName == i18n("(always)")) + dirs = &(c->_generalSourceDirs); + else { + dirs = c->_objectSourceDirs[objName]; + if (!dirs) { + dirs = new QStringList; + c->_objectSourceDirs.insert(objName, dirs); + } + } + + QString newDir; + newDir = KFileDialog::getExistingDirectory(QString::null, + this, + i18n("Choose Source Folder")); + if (newDir.isEmpty()) return; + + // even for "/", we strip the tailing slash + if (newDir.endsWith("/")) + newDir = newDir.left(newDir.length()-1); + + if (dirs->findIndex(newDir)>=0) return; + + dirs->append(newDir); + if (newDir.isEmpty()) newDir = QString("/"); + new QListViewItem(_dirItem, newDir); +} + +#include "configdlg.moc" diff --git a/kcachegrind/kcachegrind/configdlg.h b/kcachegrind/kcachegrind/configdlg.h new file mode 100644 index 00000000..d8bd2f89 --- /dev/null +++ b/kcachegrind/kcachegrind/configdlg.h @@ -0,0 +1,64 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002, 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Configuration Dialog for KCachegrind + */ + +#ifndef CONFIGDLG_H +#define CONFIGDLG_H + +#include "configdlgbase.h" +#include "configuration.h" + +class TraceData; + +class ConfigDlg : public ConfigDlgBase +{ + Q_OBJECT + +public: + ConfigDlg(Configuration*, TraceData*, + QWidget* parent = 0, const char* name = 0); + ~ConfigDlg(); + + static bool configure(Configuration*, TraceData*, QWidget*); + +protected slots: + void objectActivated(const QString &); + void objectCheckChanged(bool); + void objectColorChanged(const QColor &); + void classActivated(const QString &); + void classCheckChanged(bool); + void classColorChanged(const QColor &); + void fileActivated(const QString &); + void fileCheckChanged(bool); + void fileColorChanged(const QColor &); + void dirsItemChanged(QListViewItem*); + void dirsDeletePressed(); + void dirsAddPressed(); + +private: + Configuration* _config; + TraceData* _data; + + Configuration::ColorSetting *_objectCS, *_classCS, *_fileCS; + QListViewItem* _dirItem; +}; + +#endif diff --git a/kcachegrind/kcachegrind/configdlgbase.ui b/kcachegrind/kcachegrind/configdlgbase.ui new file mode 100644 index 00000000..bf2c1bd8 --- /dev/null +++ b/kcachegrind/kcachegrind/configdlgbase.ui @@ -0,0 +1,653 @@ + +ConfigDlgBase + + + configDlgBase + + + + 0 + 0 + 447 + 378 + + + + Configuration + + + + unnamed + + + 11 + + + 6 + + + + tabWidget2 + + + + tab + + + General + + + + unnamed + + + + layout10 + + + + unnamed + + + + precisionEdit + + + + 7 + 0 + 2 + 0 + + + + + + TextLabel2 + + + Truncated when more/longer than: + + + + + TextLabel4_3 + + + Precision of percentage values: + + + + + TextLabel3 + + + Symbols in tooltips and context menus + + + + + symbolLength + + + + 4 + 0 + 0 + 0 + + + + + + Spacer6_2_2_2 + + + Horizontal + + + Fixed + + + + 16 + 20 + + + + + + maxListEdit + + + + + symbolCount + + + + 4 + 0 + 0 + 0 + + + + + + TextLabel5 + + + Maximum number of items in lists: + + + + + + + TextLabel1 + + + + 1 + + + + NoFrame + + + Plain + + + Cost Item Colors + + + + + Layout9 + + + + unnamed + + + 0 + + + 6 + + + + Spacer9 + + + Vertical + + + Fixed + + + + 20 + 16 + + + + + + Spacer6 + + + Horizontal + + + Fixed + + + + 16 + 20 + + + + + + Layout9 + + + + unnamed + + + 0 + + + 6 + + + + classCombo + + + + 300 + 32767 + + + + true + + + + + fileCheck + + + Automatic + + + + + TextLabel4 + + + Object: + + + + + TextLabel4_2_2 + + + Class: + + + + + fileColor + + + + 0 + 0 + 0 + 0 + + + + + + + + + classCheck + + + Automatic + + + + + objectColor + + + + + + + + objectCheck + + + Automatic + + + + + TextLabel4_2 + + + File: + + + + + classColor + + + + 0 + 0 + 0 + 0 + + + + + + + + + fileCombo + + + + 300 + 32767 + + + + true + + + + + objectCombo + + + + 3 + 0 + 0 + 0 + + + + + 300 + 32767 + + + + true + + + + + + + + + + + tab + + + Annotations + + + + unnamed + + + + layout8 + + + + unnamed + + + + TextLabel4_3_2 + + + Context lines in annotations: + + + + + contextEdit + + + + 7 + 0 + 2 + 0 + + + + + + + + TextLabel1_2 + + + + 1 + + + + Source Folders + + + + + layout11 + + + + unnamed + + + + Spacer6_2 + + + Horizontal + + + Fixed + + + + 16 + 20 + + + + + + + Object / Related Source Base + + + true + + + true + + + + dirList + + + true + + + + + layout10 + + + + unnamed + + + + addDirButton + + + Add... + + + + + Spacer5 + + + Vertical + + + Expanding + + + + 16 + 49 + + + + + + deleteDirButton + + + Delete + + + + + + + Spacer9_2 + + + Vertical + + + Fixed + + + + 20 + 16 + + + + + + + + + + + Line1 + + + HLine + + + Sunken + + + Horizontal + + + + + Layout4 + + + + unnamed + + + 0 + + + 6 + + + + Spacer2 + + + Horizontal + + + Expanding + + + + 210 + 0 + + + + + + PushButton2 + + + &OK + + + true + + + + + PushButton1 + + + &Cancel + + + + + + + + + PushButton2 + clicked() + configDlgBase + accept() + + + PushButton1 + clicked() + configDlgBase + reject() + + + classCheck + toggled(bool) + classColor + setDisabled(bool) + + + fileCheck + toggled(bool) + fileColor + setDisabled(bool) + + + objectCheck + toggled(bool) + objectColor + setDisabled(bool) + + + + objectCombo + objectCheck + classCombo + classCheck + classColor + fileCombo + fileCheck + fileColor + maxListEdit + PushButton1 + PushButton2 + + + kcolorbutton.h + + + + diff --git a/kcachegrind/kcachegrind/configuration.cpp b/kcachegrind/kcachegrind/configuration.cpp new file mode 100644 index 00000000..f04f434b --- /dev/null +++ b/kcachegrind/kcachegrind/configuration.cpp @@ -0,0 +1,490 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002, 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Configuration for KCachegrind + */ + +#include +#include +#include + +#include "configuration.h" +#include "tracedata.h" +#include "configdlgbase.h" + +#include "traceitemview.h" + +// +// Some predefined cost types... +// + +static QStringList knownTypes() +{ + QStringList l; + + l << "Ir" << "Dr" << "Dw" + << "I1mr" << "D1mr" << "D1mw" + << "I2mr" << "D2mr" << "D2mw" + + << "Smp" << "Sys" << "User" + << "L1m" << "L2m" << "CEst"; + + return l; +} + + +static QString knownFormula(QString name) +{ + if (name =="L1m") return QString("I1mr + D1mr + D1mw"); + if (name =="L2m") return QString("I2mr + D2mr + D2mw"); + if (name =="CEst") return QString("Ir + 10 L1m + 100 L2m"); + + return QString::null; +} + +static QString knownLongName(QString name) +{ + if (name == "Ir") return i18n("Instruction Fetch"); + if (name =="Dr") return i18n("Data Read Access"); + if (name =="Dw") return i18n("Data Write Access"); + if (name =="I1mr") return i18n("L1 Instr. Fetch Miss"); + if (name =="D1mr") return i18n("L1 Data Read Miss"); + if (name =="D1mw") return i18n("L1 Data Write Miss"); + if (name =="I2mr") return i18n("L2 Instr. Fetch Miss"); + if (name =="D2mr") return i18n("L2 Data Read Miss"); + if (name =="D2mw") return i18n("L2 Data Write Miss"); + if (name =="Smp") return i18n("Samples"); + if (name =="Sys") return i18n("System Time"); + if (name =="User") return i18n("User Time"); + if (name =="L1m") return i18n("L1 Miss Sum"); + if (name =="L2m") return i18n("L2 Miss Sum"); + if (name =="CEst") return i18n("Cycle Estimation"); + + return QString::null; +} + + + + +// +// Configuration +// + +Configuration* Configuration::_config = 0; + +Configuration::Configuration() + :_colors(517) +{ + _config = 0; + + _colors.setAutoDelete(true); + _objectSourceDirs.setAutoDelete(true); + + // defaults + _showPercentage = true; + _showExpanded = false; + _showCycles = true; + _cycleCut = 0.0; + _percentPrecision = 2; + + // max symbol count/length in tooltip/popup + _maxSymbolLength = 30; + _maxSymbolCount = 10; + _maxListCount = 100; + + // annotation behaviour + _context = 3; + _noCostInside = 20; +} + +Configuration* Configuration::config() +{ + if (!_config) + _config = new Configuration(); + + return _config; +} + + +void Configuration::saveOptions(KConfig* kconfig) +{ + Configuration* c = config(); + + // color options + KConfigGroup colorConfig(kconfig, QCString("CostColors")); + QDictIterator it( c->_colors ); + int count = 1; + for( ; it.current(); ++it ) { + if ( !(*it)->automatic ) { + colorConfig.writeEntry( QString("Name%1").arg(count), + it.currentKey()); + colorConfig.writeEntry( QString("Color%1").arg(count), + (*it)->color); + //qDebug("Written Color %s (%d)", it.currentKey().ascii(), count); + + count++; + } + } + colorConfig.writeEntry( "Count", count-1); + + // source options + KConfigGroup sourceConfig(kconfig, QCString("Source")); + sourceConfig.writeEntry("Dirs", c->_generalSourceDirs, ':'); + QDictIterator it2( c->_objectSourceDirs ); + count = 1; + for( ; it2.current(); ++it2 ) { + sourceConfig.writeEntry( QString("Object%1").arg(count), + it2.currentKey()); + sourceConfig.writeEntry( QString("Dirs%1").arg(count), + *(*it2), ':'); + count++; + } + sourceConfig.writeEntry( "Count", count-1); + + // general options + KConfigGroup generalConfig(kconfig, QCString("General")); + generalConfig.writeEntry("ShowPercentage", c->_showPercentage); + generalConfig.writeEntry("ShowExpanded", c->_showExpanded); + generalConfig.writeEntry("ShowCycles", c->_showCycles); + generalConfig.writeEntry("CycleCut", c->_cycleCut); + generalConfig.writeEntry("MaxSymbolCount", c->_maxSymbolCount); + generalConfig.writeEntry("MaxListCount", c->_maxListCount); + generalConfig.writeEntry("MaxSymbolLength", c->_maxSymbolLength); + generalConfig.writeEntry("PercentPrecision", c->_percentPrecision); + + generalConfig.writeEntry("Context", c->_context); + generalConfig.writeEntry("NoCostInside", c->_noCostInside); + + KConfigGroup ctConfig(kconfig, QCString("CostTypes")); + int ctCount = TraceCostType::knownTypeCount(); + ctConfig.writeEntry( "Count", ctCount); + for (int i=0; iname()); + + // Use localized key + TraceItemView::writeConfigEntry(&ctConfig, + QString("Longname%1").arg(i+1).ascii(), + t->longName(), + knownLongName(t->name()).utf8().data() /*, true */ ); + TraceItemView::writeConfigEntry(&ctConfig, + QString("Formula%1").arg(i+1).ascii(), + t->formula(), knownFormula(t->name()).utf8().data()); + } +} + + + + +void Configuration::readOptions(KConfig* kconfig) +{ + int i, count; + Configuration* c = config(); + + // color options + c->_colors.clear(); + + // colors for default cost types: + // red for L2 misses, green for L1 misses, blue for normal accesses + c->color("CostType-I2mr")->color = QColor(240, 0, 0); + c->color("CostType-D2mr")->color = QColor(180,40,40); + c->color("CostType-D2mw")->color = QColor(120,80,80); + + c->color("CostType-I1mr")->color = QColor(0, 240, 0); + c->color("CostType-D1mr")->color = QColor(40,180,40); + c->color("CostType-D1mw")->color = QColor(80,120,80); + + c->color("CostType-Ir")->color = QColor(0, 0, 240); + c->color("CostType-Dr")->color = QColor(40,40,180); + c->color("CostType-Dw")->color = QColor(80,80,120); + + KConfigGroup colorConfig(kconfig, QCString("CostColors")); + count = colorConfig.readNumEntry("Count", 0); + for (i=1;i<=count;i++) { + QString n = colorConfig.readEntry(QString("Name%1").arg(i)); + QColor color = colorConfig.readColorEntry(QString("Color%1").arg(i)); + + if (n.isEmpty()) continue; + + ColorSetting* cs = new ColorSetting; + cs->name = n; + cs->automatic = false; + cs->color = color; + + c->_colors.insert(n, cs); + + //qDebug("Read Color %s", n.ascii()); + } + + // source options + KConfigGroup sourceConfig(kconfig, QCString("Source")); + QStringList dirs; + dirs = sourceConfig.readListEntry("Dirs", ':'); + if (dirs.count()>0) c->_generalSourceDirs = dirs; + count = sourceConfig.readNumEntry("Count", 0); + c->_objectSourceDirs.clear(); + if (count>17) c->_objectSourceDirs.resize(count); + for (i=1;i<=count;i++) { + QString n = sourceConfig.readEntry(QString("Object%1").arg(i)); + dirs = sourceConfig.readListEntry(QString("Dirs%1").arg(i), ':'); + + if (n.isEmpty() || (dirs.count()==0)) continue; + + c->_objectSourceDirs.insert(n, new QStringList(dirs)); + } + + + // general options + KConfigGroup generalConfig(kconfig, QCString("General")); + c->_showPercentage = generalConfig.readBoolEntry("ShowPercentage", true); + c->_showExpanded = generalConfig.readBoolEntry("ShowExpanded", false); + c->_showCycles = generalConfig.readBoolEntry("ShowCycles", true); + c->_cycleCut = generalConfig.readDoubleNumEntry("CycleCut", 0.0); + c->_maxSymbolCount = generalConfig.readNumEntry("MaxSymbolCount", 10); + c->_maxListCount = generalConfig.readNumEntry("MaxListCount", 100); + c->_maxSymbolLength = generalConfig.readNumEntry("MaxSymbolLength", 30); + c->_percentPrecision = generalConfig.readNumEntry("PercentPrecision", 2); + + c->_context = generalConfig.readNumEntry("Context", 3); + c->_noCostInside = generalConfig.readNumEntry("NoCostInside", 20); + + // known cost types + if (TraceCostType::knownTypeCount()==0) { + + KConfigGroup ctConfig(kconfig, QCString("CostTypes")); + int ctCount = ctConfig.readNumEntry("Count", 0); + if (ctCount>0) { + for (int i=1;i<=ctCount;i++) { + QString n = ctConfig.readEntry(QString("Name%1").arg(i)); + QString l = ctConfig.readEntry(QString("Longname%1").arg(i)); + if (l.isEmpty()) l = knownLongName(n); + QString f = ctConfig.readEntry(QString("Formula%1").arg(i)); + if (f.isEmpty()) f = knownFormula(n); + + TraceCostType::add(new TraceCostType(n, l, f)); + } + } + else { + // add default types + + QString longName, formula; + TraceCostType* ct; + QStringList l = knownTypes(); + for ( QStringList::Iterator it = l.begin(); + it != l.end(); ++it ) { + longName = knownLongName(*it); + formula = knownFormula(*it); + ct = new TraceCostType(*it, longName, formula); + TraceCostType::add(ct); + } + } + } +} + +QColor Configuration::groupColor(TraceItem* cost) +{ + QString n; + + if (!cost) + n = QString("default"); + else + n = TraceCost::typeName(cost->type()) + "-" + cost->prettyName(); + + return color(n)->color; +} + +QColor Configuration::costTypeColor(TraceCostType* t) +{ + QString n; + + if (!t) + n = QString("CostType-default"); + else + n = QString("CostType-%1").arg(t->name()); + + return color(n)->color; +} + +QColor Configuration::functionColor(TraceCost::CostType gt, + TraceFunction* f) +{ + TraceCost* group = f; + QString n; + + switch(gt) { + case TraceCost::Object: group = f->object(); break; + case TraceCost::Class: group = f->cls(); break; + case TraceCost::File: group = f->file(); break; + default: + break; + } + + if (group != f) { + // first look for manual color of a function in a group + n = TraceCost::typeName(group->type()) + + "-" + group->prettyName() + + "-" + f->prettyName(); + + ColorSetting* cs = color(n, false); + if (cs) return cs->color; + } + return groupColor(group); +} + +Configuration::ColorSetting* Configuration::color(QString n, bool createNew) +{ +// qDebug("Color for %s", n.latin1()); + + // predefined ? + Configuration* c = config(); + ColorSetting* cs = c->_colors[n]; + if (cs || !createNew) return cs; + + // automatic colors... + int h = 0, s = 100; + const char* str = n.ascii(); + while (*str) { + h = (h * 37 + s* (unsigned)*str) % 256; + s = (s * 17 + h* (unsigned)*str) % 192; + str++; + } + + //qDebug("New color for %s: H %d, S %d", n.ascii(), h, 64+s); + QColor color = QColor(h, 64+s, 192, QColor::Hsv); + + cs = new ColorSetting; + cs->name = n; + cs->automatic = true; + cs->color = color; + c->_colors.insert(n, cs); + + //qDebug("new Color %s", n.ascii()); + + return cs; +} + +/* Gives back a list of all Source Base Directories of Objects in + * current trace. If a special object is given in 2nd argument, + * put its Source Base in front. + */ +QStringList Configuration::sourceDirs(TraceData* data, TraceObject* o) +{ + QStringList l = config()->_generalSourceDirs, *ol, *ol2 = 0; + TraceObjectMap::Iterator oit; + for ( oit = data->objectMap().begin(); + oit != data->objectMap().end(); ++oit ) { + ol = config()->_objectSourceDirs[(*oit).name()]; + if (&(*oit) == o) { + ol2 = ol; + continue; + } + if (!ol) continue; + + for(unsigned int i=0;icount();i++) + l.prepend( (*ol)[i] ); + } + if (ol2) { + for(unsigned int i=0;icount();i++) + l.prepend( (*ol2)[i] ); + } + if (0) kdDebug() << "Configuration::sourceDirs: " << l.join(":") << endl; + + return l; +} + +bool Configuration::showPercentage() +{ + return config()->_showPercentage; +} + +bool Configuration::showExpanded() +{ + return config()->_showExpanded; +} + +bool Configuration::showCycles() +{ + return config()->_showCycles; +} + +void Configuration::setShowPercentage(bool s) +{ + Configuration* c = config(); + if (c->_showPercentage == s) return; + + c->_showPercentage = s; +} + +void Configuration::setShowExpanded(bool s) +{ + Configuration* c = config(); + if (c->_showExpanded == s) return; + + c->_showExpanded = s; +} + +void Configuration::setShowCycles(bool s) +{ + Configuration* c = config(); + if (c->_showCycles == s) return; + + c->_showCycles = s; +} + +double Configuration::cycleCut() +{ + return config()->_cycleCut; +} + +int Configuration::percentPrecision() +{ + return config()->_percentPrecision; +} + +int Configuration::maxSymbolLength() +{ + return config()->_maxSymbolLength; +} + +QString Configuration::shortenSymbol(QString s) +{ + if ((int)s.length() > maxSymbolLength()) + s = s.left(maxSymbolLength()) + "..."; + return s; +} + +int Configuration::maxListCount() +{ + return config()->_maxListCount; +} + +int Configuration::maxSymbolCount() +{ + return config()->_maxSymbolCount; +} + +int Configuration::context() +{ + return config()->_context; +} + +int Configuration::noCostInside() +{ + return config()->_noCostInside; +} diff --git a/kcachegrind/kcachegrind/configuration.h b/kcachegrind/kcachegrind/configuration.h new file mode 100644 index 00000000..6c808c0c --- /dev/null +++ b/kcachegrind/kcachegrind/configuration.h @@ -0,0 +1,101 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002, 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Configuration for KCachegrind + */ + +#ifndef CONFIGURATION_H +#define CONFIGURATION_H + +#include +#include +#include + +#include "tracedata.h" + +class KConfig; + +class Configuration +{ + friend class ConfigDlg; + +public: + Configuration(); + + static Configuration* config(); + + static void saveOptions(KConfig*); + static void readOptions(KConfig*); + + // color for visualisation of an object + static QColor functionColor(TraceItem::CostType gt, TraceFunction*); + static QColor groupColor(TraceItem*); + static QColor costTypeColor(TraceCostType*); + static QStringList sourceDirs(TraceData*, TraceObject* o = 0); + static bool showPercentage(); + static bool showExpanded(); + static bool showCycles(); + + // lower percentage limit of cost items filled into lists + static int percentPrecision(); + // max symbol lengths/count in tooltip/popup + static int maxSymbolLength(); + // strip a symbol name according to + static QString shortenSymbol(QString); + static int maxSymbolCount(); + // max. number of items in lists + static int maxListCount(); + + // how many lines of context to show before/after annotated source/assembler + static int context(); + // how many lines without cost are still regarded as inside a function + static int noCostInside(); + + static void setShowPercentage(bool); + static void setShowExpanded(bool); + + static void setShowCycles(bool); + // upper limit for cutting of a call in cycle detection + static double cycleCut(); + +private: + struct ColorSetting { + QString name; + QColor color; + bool automatic; + }; + + static ColorSetting* color(QString, bool createNew = true); + + QDict _colors; + + QStringList _generalSourceDirs; + QDict _objectSourceDirs; + + bool _showPercentage, _showExpanded, _showCycles; + double _cycleCut; + int _percentPrecision; + int _maxSymbolLength, _maxSymbolCount, _maxListCount; + int _context, _noCostInside; + + static Configuration* _config; +}; + + +#endif diff --git a/kcachegrind/kcachegrind/costlistitem.cpp b/kcachegrind/kcachegrind/costlistitem.cpp new file mode 100644 index 00000000..bfe22730 --- /dev/null +++ b/kcachegrind/kcachegrind/costlistitem.cpp @@ -0,0 +1,136 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002, 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include + +#include +#include + +#include +#include +#include + +#include "listutils.h" +#include "costlistitem.h" +#include "coverage.h" +#include "configuration.h" + +// CostListItem + + +CostListItem::CostListItem(QListView* parent, TraceCostItem* costItem, + TraceCostType* ct, int size) + :QListViewItem(parent) +{ + _groupSize = size; + _skipped = 0; + _costItem = costItem; + setCostType(ct); + + if (costItem) { + updateName(); + setPixmap(1, colorPixmap(10, 10, + Configuration::groupColor(_costItem))); + } +} + +CostListItem::CostListItem(QListView* parent, int skipped, + TraceCostItem* costItem, TraceCostType* ct) + :QListViewItem(parent) +{ + _skipped = skipped; + _costItem = costItem; + setCostType(ct); + + setText(1, i18n("(%n item skipped)", "(%n items skipped)", _skipped)); +} + +void CostListItem::setCostType(TraceCostType* ct) +{ + _costType = ct; + update(); +} + +void CostListItem::updateName() +{ + if (!_costItem) return; + + QString n = _costItem->prettyName(); + if (_groupSize>=0) n += QString(" (%1)").arg(_groupSize); + + setText(1, n); +} + +void CostListItem::setSize(int s) +{ + _groupSize = s; + updateName(); +} + +void CostListItem::update() +{ + if (!_costItem) return; + TraceData* d = _costItem->data(); + + double total = d->subCost(_costType); + if (total == 0.0) { + setText(0, QString("---")); + setPixmap(0, QPixmap()); + return; + } + + _pure = _costItem->subCost(_costType); + double pure = 100.0 * _pure / total; + QString str; + if (Configuration::showPercentage()) + str = QString("%1").arg(pure, 0, 'f', Configuration::percentPrecision()); + else + str = _costItem->prettySubCost(_costType); + + if (_skipped) { + // special handling for skip entries... + setText(0, QString("< %1").arg(str)); + return; + } + + setText(0, str); + setPixmap(0, costPixmap(_costType, _costItem, total, false)); +} + +int CostListItem::compare(QListViewItem * i, int col, bool ascending ) const +{ + const CostListItem* fi1 = this; + const CostListItem* fi2 = (CostListItem*) i; + + // we always want descending order + if (ascending) { + fi1 = fi2; + fi2 = this; + } + + // a skip entry is always sorted last + if (fi1->_skipped) return -1; + if (fi2->_skipped) return 1; + + if (col==0) { + if (fi1->_pure < fi2->_pure) return -1; + if (fi1->_pure > fi2->_pure) return 1; + return 0; + } + return QListViewItem::compare(i, col, ascending); +} diff --git a/kcachegrind/kcachegrind/costlistitem.h b/kcachegrind/kcachegrind/costlistitem.h new file mode 100644 index 00000000..28b90248 --- /dev/null +++ b/kcachegrind/kcachegrind/costlistitem.h @@ -0,0 +1,52 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef COSTLISTITEM_H +#define COSTLISTITEM_H + +#include +#include "tracedata.h" + +class CostListItem: public QListViewItem +{ +public: + CostListItem(QListView* parent, TraceCostItem* cost, + TraceCostType* ct, int size = -1); + // entry with multiple skipped items + CostListItem(QListView* parent, int skipped, TraceCostItem* cost, + TraceCostType* ct); + + int compare(QListViewItem * i, int col, bool ascending ) const; + TraceCostItem* costItem() { return (_skipped) ? 0 : _costItem; } + void setCostType(TraceCostType* ct); + void update(); + void setSize(int s); + +private: + void updateName(); + + SubCost _pure; + TraceCostType* _costType; + TraceCostItem* _costItem; + // >0 only for last item in list, if items are skipped + int _skipped; + // number of items in group, is put in parenthesis after name + int _groupSize; +}; + +#endif diff --git a/kcachegrind/kcachegrind/costtypeitem.cpp b/kcachegrind/kcachegrind/costtypeitem.cpp new file mode 100644 index 00000000..99d123d8 --- /dev/null +++ b/kcachegrind/kcachegrind/costtypeitem.cpp @@ -0,0 +1,149 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Items of cost type view. + */ + +#include +#include + +#include "configuration.h" +#include "listutils.h" +#include "costtypeitem.h" + + +// CostTypeItem + + +CostTypeItem::CostTypeItem(QListView* parent, TraceCostItem* costItem, + TraceCostType* ct, TraceCost::CostType gt) + :QListViewItem(parent) +{ + _costItem = costItem; + _costType = ct; + _groupType = gt; + + if (ct) { + setText(0, ct->longName()); + setText(3, ct->name()); + QString formula = ct->formula(); + setText(5, formula); + if (!formula.isEmpty()) { + setText(4, "="); + // we have a virtual type: allow editing + setRenameEnabled(0, true); + setRenameEnabled(3, true); + setRenameEnabled(5, true); + } + } + else { + setText(0, i18n("Unknown Type")); + } + update(); +} + +void CostTypeItem::setGroupType(TraceCost::CostType gt) +{ + if (_groupType == gt) return; + + _groupType = gt; + update(); +} + +void CostTypeItem::update() +{ + TraceData* d = _costItem ? _costItem->data() : 0; + double total = d ? ((double)d->subCost(_costType)) : 0.0; + + if (total == 0.0) { + setText(1, "-"); + setPixmap(1, QPixmap()); + setText(2, "-"); + setPixmap(2, QPixmap()); + return; + } + + TraceFunction* f = (_costItem->type()==TraceCost::Function) ? + (TraceFunction*)_costItem : 0; + + TraceCost* selfTotalCost = f ? f->data() : d; + if (f && Configuration::showExpanded()) { + switch(_groupType) { + case TraceCost::Object: selfTotalCost = f->object(); break; + case TraceCost::Class: selfTotalCost = f->cls(); break; + case TraceCost::File: selfTotalCost = f->file(); break; + case TraceCost::FunctionCycle: selfTotalCost = f->cycle(); break; + default: break; + } + } + if (_costItem->type()==TraceCost::FunctionCycle) { + f = (TraceFunction*)_costItem; + selfTotalCost = f->data(); + } + + double selfTotal = selfTotalCost->subCost(_costType); + + // for all cost items there's a self cost + _pure = _costItem ? _costItem->subCost(_costType) : SubCost(0); + double pure = 100.0 * _pure / selfTotal; + if (Configuration::showPercentage()) { + setText(2, QString("%1") + .arg(pure, 0, 'f', Configuration::percentPrecision())); + } + else + setText(2, _costItem->prettySubCost(_costType)); + + setPixmap(2, costPixmap(_costType, _costItem, selfTotal, false)); + + if (!f) { + setText(1, "-"); + setPixmap(1, QPixmap()); + return; + } + + _sum = f->inclusive()->subCost(_costType); + double sum = 100.0 * _sum / total; + if (Configuration::showPercentage()) { + setText(1, QString("%1") + .arg(sum, 0, 'f', Configuration::percentPrecision())); + } + else + setText(1, _sum.pretty()); + + setPixmap(1, costPixmap(_costType, f->inclusive(), total, false)); +} + + +int CostTypeItem::compare(QListViewItem * i, int col, bool ascending ) const +{ + CostTypeItem* fi = (CostTypeItem*) i; + if (col==0) { + if (_sum < fi->_sum) return -1; + if (_sum > fi->_sum) return 1; + return 0; + } + if (col==1) { + if (_pure < fi->_pure) return -1; + if (_pure > fi->_pure) return 1; + return 0; + } + return QListViewItem::compare(i, col, ascending); +} + + diff --git a/kcachegrind/kcachegrind/costtypeitem.h b/kcachegrind/kcachegrind/costtypeitem.h new file mode 100644 index 00000000..a9df7d91 --- /dev/null +++ b/kcachegrind/kcachegrind/costtypeitem.h @@ -0,0 +1,50 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Items of cost type view. + */ + +#ifndef COSTTYEPITEM_H +#define COSTTYEPITEM_H + +#include +#include "tracedata.h" + + +class CostTypeItem: public QListViewItem +{ +public: + CostTypeItem(QListView* parent, TraceCostItem* costItem, + TraceCostType* ct, TraceCost::CostType gt); + + int compare(QListViewItem * i, int col, bool ascending ) const; + void setGroupType(TraceCost::CostType); + TraceCostItem* costItem() { return _costItem; } + TraceCostType* costType() { return _costType; } + void update(); + +private: + SubCost _sum, _pure; + TraceCostType* _costType; + TraceCostItem* _costItem; + TraceCost::CostType _groupType; +}; + + +#endif diff --git a/kcachegrind/kcachegrind/costtypeview.cpp b/kcachegrind/kcachegrind/costtypeview.cpp new file mode 100644 index 00000000..6c08a8c4 --- /dev/null +++ b/kcachegrind/kcachegrind/costtypeview.cpp @@ -0,0 +1,310 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Cost Type View + */ + +#include +#include +#include + +#include "configuration.h" +#include "costtypeitem.h" +#include "costtypeview.h" +#include "toplevel.h" + + +// +// CostTypeView +// + + +CostTypeView::CostTypeView(TraceItemView* parentView, + QWidget* parent, const char* name) + : QListView(parent, name), TraceItemView(parentView) +{ + addColumn( i18n( "Event Type" ) ); + addColumn( i18n( "Incl." ) ); + addColumn( i18n( "Self" ) ); + addColumn( i18n( "Short" ) ); + addColumn( QString::null ); + addColumn( i18n( "Formula" ) ); + + setSorting(-1); + setAllColumnsShowFocus(true); + setColumnAlignment(1, Qt::AlignRight); + setColumnAlignment(2, Qt::AlignRight); + setColumnAlignment(3, Qt::AlignRight); + setMinimumHeight(50); + + connect( this, + SIGNAL( selectionChanged(QListViewItem*) ), + SLOT( selectedSlot(QListViewItem*) ) ); + + connect( this, + SIGNAL(contextMenuRequested(QListViewItem*, const QPoint &, int)), + SLOT(context(QListViewItem*, const QPoint &, int))); + + connect(this, + SIGNAL(doubleClicked(QListViewItem*)), + SLOT(activatedSlot(QListViewItem*))); + + connect(this, + SIGNAL(returnPressed(QListViewItem*)), + SLOT(activatedSlot(QListViewItem*))); + + connect(this, + SIGNAL(itemRenamed(QListViewItem*,int,const QString&)), + SLOT(renamedSlot(QListViewItem*,int,const QString&))); + + QWhatsThis::add( this, whatsThis() ); +} + +QString CostTypeView::whatsThis() const +{ + return i18n( "Cost Types List" + "

This list shows all cost types available " + "and what the self/inclusive cost of the " + "current selected function is for that cost type.

" + "

By choosing a cost type from the list, " + "you change the cost type of costs shown " + "all over KCachegrind to be the selected one.

"); +} + + +void CostTypeView::context(QListViewItem* i, const QPoint & p, int) +{ + QPopupMenu popup; + + TraceCostType* ct = i ? ((CostTypeItem*) i)->costType() : 0; + + if (ct) + popup.insertItem(i18n("Set Secondary Event Type"), 99); + if (_costType2) + popup.insertItem(i18n("Remove Secondary Event Type"), 98); + if (popup.count()>0) + popup.insertSeparator(); + + if (ct && !ct->isReal()) { + popup.insertItem(i18n("Edit Long Name"), 93); + popup.insertItem(i18n("Edit Short Name"), 94); + popup.insertItem(i18n("Edit Formula"), 95); + popup.insertItem(i18n("Remove"), 96); + popup.insertSeparator(); + } + + addGoMenu(&popup); + + popup.insertSeparator(); + popup.insertItem(i18n("New Cost Type ..."), 97); + + int r = popup.exec(p); + if (r == 98) selectedCostType2(0); + else if (r == 99) selectedCostType2(ct); + else if (r == 93) i->startRename(0); + else if (r == 94) i->startRename(3); + else if (r == 95) i->startRename(5); + else if (r == 96) { + + // search for a previous type + TraceCostType* prev = 0, *ct = 0; + TraceCostMapping* m = _data->mapping(); + for (int i=0;irealCount();i++) { + ct = m->realType(i); + if (ct) prev = ct; + } + for (int i=0;ivirtualCount();i++) { + ct = m->virtualType(i); + if (ct == _costType) break; + if (ct) prev = ct; + } + + if (_data->mapping()->remove(ct)) { + // select previous cost type + selectedCostType(prev); + if (_costType2 == ct) + selectedCostType2(prev); + refresh(); + } + } + else if (r == 97) { + int i = 1; + while(1) { + if (!TraceCostType::knownVirtualType(i18n("New%1").arg(i))) + break; + i++; + } + // add same new cost type to this mapping and to known types + QString shortName = i18n("New%1").arg(i); + QString longName = i18n("New Cost Type %1").arg(i); + TraceCostType::add(new TraceCostType(shortName, longName, "0")); + _data->mapping()->add(new TraceCostType(shortName, longName, "0")); + refresh(); + } +} + +void CostTypeView::selectedSlot(QListViewItem * i) +{ + TraceCostType* ct = i ? ((CostTypeItem*) i)->costType() : 0; + if (ct) + selectedCostType(ct); +} + +void CostTypeView::activatedSlot(QListViewItem * i) +{ + TraceCostType* ct = i ? ((CostTypeItem*) i)->costType() : 0; + if (ct) + selectedCostType2(ct); +} + +TraceItem* CostTypeView::canShow(TraceItem* i) +{ + if (!i) return 0; + + switch(i->type()) { + case TraceCost::Object: + case TraceCost::Class: + case TraceCost::File: + case TraceCost::Call: + case TraceCost::FunctionCycle: + case TraceCost::Function: + break; + default: + return 0; + } + return i; +} + +void CostTypeView::doUpdate(int changeType) +{ + // Special case ? + if (changeType == selectedItemChanged) return; + + if (changeType == costType2Changed) return; + + if (changeType == groupTypeChanged) { + QListViewItem *item; + for (item = firstChild();item;item = item->nextSibling()) + ((CostTypeItem*)item)->setGroupType(_groupType); + + return; + } + + if (changeType == costTypeChanged) { + QListViewItem *item; + for (item = firstChild();item;item = item->nextSibling()) + if ( ((CostTypeItem*)item)->costType() == _costType) { + setSelected(item, true); + ensureItemVisible(item); + break; + } + + return; + } + + if (changeType == partsChanged) { + QListViewItem *item; + for (item = firstChild();item;item = item->nextSibling()) + ((CostTypeItem*)item)->update(); + + return; + } + + + refresh(); +} + +void CostTypeView::refresh() +{ + clear(); + setColumnWidth(1, 50); + setColumnWidth(2, 50); + + if (!_data || !_activeItem) return; + switch(_activeItem->type()) { + case TraceCost::Object: + case TraceCost::Class: + case TraceCost::File: + case TraceCost::FunctionCycle: + case TraceCost::Function: + break; + default: + return; + } + TraceCostItem* c = (TraceCostItem*) _activeItem; + + TraceCostType* ct =0 ; + QListViewItem* item = 0; + QString sumStr, pureStr; + QListViewItem* costItem=0; + + TraceCostMapping* m = _data->mapping(); + for (int i=m->virtualCount()-1;i>=0;i--) { + ct = m->virtualType(i); + if (!ct) continue; + item = new CostTypeItem(this, c, ct, _groupType); + if (ct == _costType) costItem = item; + } + for (int i=m->realCount()-1;i>=0;i--) { + ct = m->realType(i); + item = new CostTypeItem(this, c, ct, _groupType); + if (ct == _costType) costItem = item; + } + + if (costItem) { + setSelected(costItem, true); + ensureItemVisible(costItem); + } + + if (item) setMinimumHeight(3*item->height()); +} + + +void CostTypeView::renamedSlot(QListViewItem* item,int c,const QString& t) +{ + TraceCostType* ct = item ? ((CostTypeItem*) item)->costType() : 0; + if (!ct || ct->isReal()) return; + + // search for matching known Type + int knownCount = TraceCostType::knownTypeCount(); + TraceCostType* known = 0; + for (int i=0; iname() == ct->name()) break; + } + + if (c == 0) { + ct->setLongName(t); + if (known) known->setLongName(t); + } + else if (c == 3) { + ct->setName(t); + if (known) known->setName(t); + } + else if (c == 5) { + ct->setFormula(t); + if (known) known->setFormula(t); + } + else return; + + if (_topLevel) _topLevel->configChanged(); + refresh(); +} + +#include "costtypeview.moc" diff --git a/kcachegrind/kcachegrind/costtypeview.h b/kcachegrind/kcachegrind/costtypeview.h new file mode 100644 index 00000000..c5f51494 --- /dev/null +++ b/kcachegrind/kcachegrind/costtypeview.h @@ -0,0 +1,53 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Cost Type View + */ + +#ifndef COSTTYPEVIEW_H +#define COSTTYPEVIEW_H + +#include +#include "tracedata.h" +#include "traceitemview.h" + +class CostTypeView: public QListView, public TraceItemView +{ + Q_OBJECT + +public: + CostTypeView(TraceItemView* parentView, + QWidget* parent=0, const char* name=0); + + virtual QWidget* widget() { return this; } + QString whatsThis() const; + +private slots: + void context(QListViewItem*,const QPoint &, int); + void selectedSlot(QListViewItem*); + void activatedSlot(QListViewItem*); + void renamedSlot(QListViewItem*,int,const QString&); + +private: + TraceItem* canShow(TraceItem*); + void doUpdate(int); + void refresh(); +}; + +#endif diff --git a/kcachegrind/kcachegrind/coverage.cpp b/kcachegrind/kcachegrind/coverage.cpp new file mode 100644 index 00000000..b928fc42 --- /dev/null +++ b/kcachegrind/kcachegrind/coverage.cpp @@ -0,0 +1,329 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Function Coverage Analysis + */ + +#include "coverage.h" + +//#define DEBUG_COVERAGE 1 + +TraceCostType* Coverage::_costType; + +const int Coverage::maxHistogramDepth = maxHistogramDepthValue; +const int Coverage::Rtti = 1; + +Coverage::Coverage() +{ +} + +void Coverage::init() +{ + _self = 0.0; + _incl = 0.0; + _callCount = 0.0; + // should always be overwritten before usage + _firstPercentage = 1.0; + _minDistance = 9999; + _maxDistance = 0; + _active = false; + _inRecursion = false; + for (int i = 0;imaxP) { + maxP = _inclHisto[i]; + medD = i; + } + + return medD; +} + +int Coverage::selfMedian() +{ + double maxP = _selfHisto[0]; + int medD = 0; + for (int i = 1;imaxP) { + maxP = _selfHisto[i]; + medD = i; + } + + return medD; +} + +TraceFunctionList Coverage::coverage(TraceFunction* f, CoverageMode m, + TraceCostType* ct) +{ + invalidate(f->data(), Coverage::Rtti); + + _costType = ct; + + // function f takes ownership over c! + Coverage* c = new Coverage(); + c->setFunction(f); + c->init(); + + TraceFunctionList l; + + if (m == Caller) + c->addCallerCoverage(l, 1.0, 0); + else + c->addCallingCoverage(l, 1.0, 1.0, 0); + + return l; +} + +void Coverage::addCallerCoverage(TraceFunctionList& fList, + double pBack, int d) +{ + TraceCallList cList; + TraceCall* call; + Coverage* c; + + if (_inRecursion) return; + + double incl; + incl = (double) (_function->inclusive()->subCost(_costType)); + + if (_active) { +#ifdef DEBUG_COVERAGE + qDebug("CallerCov: D %d, %s (was active, incl %f, self %f): newP %f", d, + _function->prettyName().ascii(), _incl, _self, pBack); +#endif + _inRecursion = true; + } + else { + _active = true; + + // only add cost if this is no recursion + + _incl += pBack; + _firstPercentage = pBack; + + if (_minDistance > d) _minDistance = d; + if (_maxDistance < d) _maxDistance = d; + if (dprettyName().ascii(), _incl, pBack); +#endif + } + + double callVal, pBackNew; + + cList = _function->callers(); + for (call=cList.first();call;call=cList.next()) { + if (call->inCycle()>0) continue; + if (call->isRecursion()) continue; + + if (call->subCost(_costType)>0) { + TraceFunction* caller = call->caller(); + + c = (Coverage*) caller->assoziation(rtti()); + if (!c) { + c = new Coverage(); + c->setFunction(caller); + } + if (!c->isValid()) { + c->init(); + fList.append(caller); + } + + if (c->isActive()) continue; + if (c->inRecursion()) continue; + + callVal = (double) call->subCost(_costType); + pBackNew = pBack * (callVal / incl); + + // FIXME ?!? + + if (!c->isActive()) { + if (d>=0) + c->callCount() += (double)call->callCount(); + else + c->callCount() += _callCount; + } + else { + // adjust pNew by sum of geometric series of recursion factor. + // Thus we can avoid endless recursion here + pBackNew *= 1.0 / (1.0 - pBackNew / c->firstPercentage()); + } + + // Limit depth + if (pBackNew > 0.0001) + c->addCallerCoverage(fList, pBackNew, d+1); + } + } + + if (_inRecursion) + _inRecursion = false; + else if (_active) + _active = false; +} + +/** + * pForward is time on percent used, + * pBack is given to allow for calculation of call counts + */ +void Coverage::addCallingCoverage(TraceFunctionList& fList, + double pForward, double pBack, int d) +{ + TraceCallList cList; + TraceCall* call; + Coverage* c; + + if (_inRecursion) return; + +#ifdef DEBUG_COVERAGE + static const char* spaces = " "; +#endif + + double self, incl; + incl = (double) (_function->inclusive()->subCost(_costType)); + +#ifdef DEBUG_COVERAGE + qDebug("CngCov:%s - %s (incl %f, self %f): forward %f, back %f", + spaces+strlen(spaces)-d, + _function->prettyName().ascii(), _incl, _self, pForward, pBack); +#endif + + + if (_active) { + _inRecursion = true; + +#ifdef DEBUG_COVERAGE + qDebug("CngCov:%s < %s: STOP (is active)", + spaces+strlen(spaces)-d, + _function->prettyName().ascii()); +#endif + + } + else { + _active = true; + + // only add cost if this is no recursion + self = pForward * (_function->subCost(_costType)) / incl; + _incl += pForward; + _self += self; + _firstPercentage = pForward; + + if (_minDistance > d) _minDistance = d; + if (_maxDistance < d) _maxDistance = d; + if (dprettyName().ascii(), _incl, _self); +#endif + } + + double callVal, pForwardNew, pBackNew; + + cList = _function->callings(); + for (call=cList.first();call;call=cList.next()) { + if (call->inCycle()>0) continue; + if (call->isRecursion()) continue; + + if (call->subCost(_costType)>0) { + TraceFunction* calling = call->called(); + + c = (Coverage*) calling->assoziation(rtti()); + if (!c) { + c = new Coverage(); + c->setFunction(calling); + } + if (!c->isValid()) { + c->init(); + fList.append(calling); + } + + if (c->isActive()) continue; + if (c->inRecursion()) continue; + + callVal = (double) call->subCost(_costType); + pForwardNew = pForward * (callVal / incl); + pBackNew = pBack * (callVal / + calling->inclusive()->subCost(_costType)); + + if (!c->isActive()) { + c->callCount() += pBack * call->callCount(); + +#ifdef DEBUG_COVERAGE + qDebug("CngCov:%s > %s: forward %f, back %f, calls %f -> %f, now %f", + spaces+strlen(spaces)-d, + calling->prettyName().ascii(), + pForwardNew, pBackNew, + (double)call->callCount(), + pBack * call->callCount(), + c->callCount()); +#endif + } + else { + // adjust pNew by sum of geometric series of recursion factor. + // Thus we can avoid endless recursion here + double fFactor = 1.0 / (1.0 - pForwardNew / c->firstPercentage()); + double bFactor = 1.0 / (1.0 - pBackNew); +#ifdef DEBUG_COVERAGE + qDebug("CngCov:%s Recursion - origP %f, actP %f => factor %f, newP %f", + spaces+strlen(spaces)-d, + c->firstPercentage(), pForwardNew, + fFactor, pForwardNew * fFactor); +#endif + pForwardNew *= fFactor; + pBackNew *= bFactor; + + } + + // Limit depth + if (pForwardNew > 0.0001) + c->addCallingCoverage(fList, pForwardNew, pBackNew, d+1); + } + } + + if (_inRecursion) + _inRecursion = false; + else if (_active) + _active = false; +} + diff --git a/kcachegrind/kcachegrind/coverage.h b/kcachegrind/kcachegrind/coverage.h new file mode 100644 index 00000000..b6a5107e --- /dev/null +++ b/kcachegrind/kcachegrind/coverage.h @@ -0,0 +1,102 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Function Coverage Analysis + */ + +#ifndef COVERAGE_H +#define COVERAGE_H + +#include "tracedata.h" + +/** + * Coverage of a function. + * When analysis is done, every function involved will have a + * pointer to an object of this class. + * + * This function also holds the main routine for coverage analysis, + * Coverage::coverage(), as static method. + */ +class Coverage : public TraceAssoziation +{ +public: + /* Direction of coverage analysis */ + enum CoverageMode { Caller, Called }; + + // max depth for distance histogram +#define maxHistogramDepthValue 40 + static const int maxHistogramDepth; + + static const int Rtti; + + Coverage(); + + virtual int rtti() { return Rtti; } + void init(); + + TraceFunction* function() { return _function; } + double self() { return _self; } + double inclusive() { return _incl; } + double firstPercentage() { return _firstPercentage; } + double& callCount() { return _callCount; } + int minDistance() { return _minDistance; } + int maxDistance() { return _maxDistance; } + int inclusiveMedian(); + int selfMedian(); + double* selfHistogram() { return _selfHisto; } + double* inclusiveHistogram() { return _inclHisto; } + bool isActive() { return _active; } + bool inRecursion() { return _inRecursion; } + + void setSelf(float p) { _self = p; } + void setInclusive(float p) { _incl = p; } + void setCallCount(float cc) { _callCount = cc; } + void setActive(bool a) { _active = a; } + void setInRecursion(bool r) { _inRecursion = r; } + + /** + * Calculate coverage of all functions based on function f. + * If mode is Called, the coverage of functions called by + * f is calculated, otherwise that of functions calling f. + * SubCost type ct is used for the analysis. + * Self values are undefined for Caller mode. + * + * Returns list of functions covered. + * Coverage degree of returned functions can be get + * with function->coverage()->percentage() + */ + static TraceFunctionList coverage(TraceFunction* f, CoverageMode m, + TraceCostType* ct); + +private: + void addCallerCoverage(TraceFunctionList& l, double, int d); + void addCallingCoverage(TraceFunctionList& l, double, double, int d); + + double _self, _incl, _firstPercentage, _callCount; + int _minDistance, _maxDistance; + bool _active, _inRecursion; + double _selfHisto[maxHistogramDepthValue]; + double _inclHisto[maxHistogramDepthValue]; + + // temporary set for one coverage analysis + static TraceCostType* _costType; +}; + +#endif + diff --git a/kcachegrind/kcachegrind/coverageitem.cpp b/kcachegrind/kcachegrind/coverageitem.cpp new file mode 100644 index 00000000..d3542a95 --- /dev/null +++ b/kcachegrind/kcachegrind/coverageitem.cpp @@ -0,0 +1,343 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Items of coverage view. + */ + +#include +#include + +#include "configuration.h" +#include "listutils.h" +#include "coverage.h" +#include "coverageitem.h" + + +// CallerCoverageItem + + +CallerCoverageItem::CallerCoverageItem(QListView* parent, Coverage* c, + TraceFunction* base, + TraceCostType* ct, + TraceCost::CostType gt) + : QListViewItem(parent) +{ + _skipped = 0; + _coverage = c; + _function = c ? c->function() : 0; + _base = base; + _groupType = TraceCost::NoCostType; + + setText(3, _function->prettyNameWithLocation()); + + setCostType(ct); + setGroupType(gt); +} + +CallerCoverageItem::CallerCoverageItem(QListView* parent, int skipped, Coverage* c, + TraceFunction* base, + TraceCostType* ct, + TraceCost::CostType gt) + : QListViewItem(parent) +{ + _skipped = skipped; + _coverage = c; + _function = c ? c->function() : 0; + _base = base; + _groupType = TraceCost::NoCostType; + + setText(3, i18n("(%n function skipped)", "(%n functions skipped)", _skipped)); + + setCostType(ct); + setGroupType(gt); +} + +void CallerCoverageItem::setGroupType(TraceCost::CostType gt) +{ + if (_skipped) return; + if (_groupType == gt) return; + _groupType = gt; + + QColor c = Configuration::functionColor(_groupType, _function); + setPixmap(3, colorPixmap(10, 10, c)); +} + +void CallerCoverageItem::setCostType(TraceCostType* ct) +{ + _costType = ct; + update(); +} + +void CallerCoverageItem::update() +{ + if (!_coverage) { + setText(0, QString::null); + setText(1, QString::null); + return; + } + + _pSum = 100.0 * _coverage->inclusive(); + SubCost realSum = _base->inclusive()->subCost(_costType); + _sum = SubCost(realSum * _coverage->inclusive()); + QString str; + if (Configuration::showPercentage()) + str = QString("%1").arg(_pSum, 0, 'f', Configuration::percentPrecision()); + else + str = _sum.pretty(); + + if (_skipped) { + setText(0, QString("< %1").arg(str)); + return; + } + + setText(0, str); + setPixmap(0, partitionPixmap(25, 10, _coverage->inclusiveHistogram(), 0, + Coverage::maxHistogramDepth, false)); + + // call count + _cc = SubCost(_coverage->callCount()); + setText(2, _cc ? _cc.pretty() : QString("(0)")); + + // distance (min/max/median) + _distance = _coverage->inclusiveMedian(); + QString distString; + if (_coverage->minDistance() == _coverage->maxDistance()) + distString = QString::number(_distance); + else + distString = QString("%1-%2 (%3)") + .arg(_coverage->minDistance()) + .arg(_coverage->maxDistance()) + .arg(_distance); + setText(1, distString); +} + + +int CallerCoverageItem::compare(QListViewItem * i, + int col, bool ascending ) const +{ + const CallerCoverageItem* ci1 = this; + const CallerCoverageItem* ci2 = (CallerCoverageItem*) i; + + // we always want descending order + if (ascending) { + ci1 = ci2; + ci2 = this; + } + + // a skip entry is always sorted last + if (ci1->_skipped) return -1; + if (ci2->_skipped) return 1; + + if (col==0) { + if (ci1->_pSum < ci2->_pSum) return -1; + if (ci1->_pSum > ci2->_pSum) return 1; + + // for same percentage (e.g. all 100%), use distance info + if (ci1->_distance < ci2->_distance) return -1; + if (ci1->_distance > ci2->_distance) return 1; + return 0; + } + + if (col==1) { + if (ci1->_distance < ci2->_distance) return -1; + if (ci1->_distance > ci2->_distance) return 1; + return 0; + } + + if (col==2) { + if (ci1->_cc < ci2->_cc) return -1; + if (ci1->_cc > ci2->_cc) return 1; + return 0; + } + return QListViewItem::compare(i, col, ascending); +} + + +// CalleeCoverageItem + + +CalleeCoverageItem::CalleeCoverageItem(QListView* parent, Coverage* c, + TraceFunction* base, + TraceCostType* ct, + TraceCost::CostType gt) + : QListViewItem(parent) +{ + _skipped = 0; + _coverage = c; + _function = c ? c->function() : 0; + _base = base; + _groupType = TraceCost::NoCostType; + + setText(4, _function->prettyNameWithLocation()); + + setCostType(ct); + setGroupType(gt); +} + +CalleeCoverageItem::CalleeCoverageItem(QListView* parent, int skipped, Coverage* c, + TraceFunction* base, + TraceCostType* ct, + TraceCost::CostType gt) + : QListViewItem(parent) +{ + _skipped = skipped; + _coverage = c; + _function = c ? c->function() : 0; + _base = base; + _groupType = TraceCost::NoCostType; + + setText(4, i18n("(%n function skipped)", "(%n functions skipped)", _skipped)); + + setCostType(ct); + setGroupType(gt); +} + +void CalleeCoverageItem::setGroupType(TraceCost::CostType gt) +{ + if (_skipped) return; + if (_groupType == gt) return; + _groupType = gt; + + QColor c = Configuration::functionColor(_groupType, _function); + setPixmap(4, colorPixmap(10, 10, c)); +} + +void CalleeCoverageItem::setCostType(TraceCostType* ct) +{ + _costType = ct; + update(); +} + +void CalleeCoverageItem::update() +{ + if (!_coverage) { + setText(0, QString::null); + setText(1, QString::null); + setText(2, QString::null); + return; + } + + _pSum = 100.0 * _coverage->inclusive(); + + // pSum/pSelf are percentages of inclusive cost of base + SubCost realSum = _base->inclusive()->subCost(_costType); + _sum = SubCost(realSum * _coverage->inclusive()); + + + QString str; + if (Configuration::showPercentage()) + str = QString("%1").arg(_pSum, 0, 'f', Configuration::percentPrecision()); + else + str = _sum.pretty(); + + if (_skipped) { + str = QString("< %1").arg(str); + setText(0, str); + setText(1, str); + return; + } + setText(0, str); + + _pSelf = 100.0 * _coverage->self(); + _self = SubCost(realSum * _coverage->self()); + + if (Configuration::showPercentage()) { + setText(1, QString("%1") + .arg(_pSelf, 0, 'f', Configuration::percentPrecision())); + } + else { + setText(1, _self.pretty()); + } + + setPixmap(0, partitionPixmap(25, 10, _coverage->inclusiveHistogram(), 0, + Coverage::maxHistogramDepth, false)); + setPixmap(1, partitionPixmap(25, 10, _coverage->selfHistogram(), 0, + Coverage::maxHistogramDepth, false)); + + + _cc = SubCost(_coverage->callCount()); + setText(3, _cc ? _cc.pretty() : QString("(0)")); + + // for comparations + _distance = _coverage->inclusiveMedian(); + QString distString; + if (_coverage->minDistance() == _coverage->maxDistance()) + distString = QString::number(_distance); + else { + int sMed = _coverage->selfMedian(); + QString med; + if (_distance == sMed) + med = QString::number(_distance); + else + med = QString("%1/%2").arg(_distance).arg(sMed); + + distString = QString("%1-%2 (%3)") + .arg(_coverage->minDistance()) + .arg(_coverage->maxDistance()) + .arg(med); + } + setText(2, distString); +} + + +int CalleeCoverageItem::compare(QListViewItem * i, + int col, bool ascending ) const +{ + CalleeCoverageItem* ci = (CalleeCoverageItem*) i; + + // a skip entry is always sorted last + if (_skipped) return -1; + if (ci->_skipped) return 1; + + if (col==0) { + if (_pSum < ci->_pSum) return -1; + if (_pSum > ci->_pSum) return 1; + + // for same percentage (e.g. all 100%), use distance info + if (_distance < ci->_distance) return -1; + if (_distance > ci->_distance) return 1; + return 0; + } + + if (col==1) { + if (_pSelf < ci->_pSelf) return -1; + if (_pSelf > ci->_pSelf) return 1; + + // for same percentage (e.g. all 100%), use distance info + if (_distance < ci->_distance) return -1; + if (_distance > ci->_distance) return 1; + return 0; + } + + if (col==2) { + // we want to sort the distance in contra direction to costs + if (_distance < ci->_distance) return 1; + if (_distance > ci->_distance) return -1; + return 0; + } + + if (col==3) { + if (_cc < ci->_cc) return -1; + if (_cc > ci->_cc) return 1; + return 0; + } + return QListViewItem::compare(i, col, ascending); +} + + diff --git a/kcachegrind/kcachegrind/coverageitem.h b/kcachegrind/kcachegrind/coverageitem.h new file mode 100644 index 00000000..99d77625 --- /dev/null +++ b/kcachegrind/kcachegrind/coverageitem.h @@ -0,0 +1,82 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Items of coverage view. + */ + +#ifndef COVERAGEITEM_H +#define COVERAGEITEM_H + +#include +#include "tracedata.h" + +class Coverage; + +class CallerCoverageItem: public QListViewItem +{ +public: + CallerCoverageItem(QListView* parent, Coverage* c, TraceFunction* base, + TraceCostType* ct, TraceCost::CostType gt); + CallerCoverageItem(QListView* parent, int skipped, Coverage* c, TraceFunction* base, + TraceCostType* ct, TraceCost::CostType gt); + + int compare(QListViewItem * i, int col, bool ascending ) const; + TraceFunction* function() { return (_skipped) ? 0 : _function; } + void setCostType(TraceCostType* ct); + void setGroupType(TraceCost::CostType); + void update(); + +private: + float _pSum; + SubCost _sum; + TraceCostType* _costType; + TraceCost::CostType _groupType; + SubCost _cc; + int _distance, _skipped; + TraceFunction *_function, *_base; + Coverage* _coverage; +}; + + +class CalleeCoverageItem: public QListViewItem +{ +public: + CalleeCoverageItem(QListView* parent, Coverage* c, TraceFunction* base, + TraceCostType* ct, TraceCost::CostType gt); + CalleeCoverageItem(QListView* parent, int skipped, Coverage* c, TraceFunction* base, + TraceCostType* ct, TraceCost::CostType gt); + + int compare(QListViewItem * i, int col, bool ascending ) const; + TraceFunction* function() { return (_skipped) ? 0 : _function; } + void setCostType(TraceCostType* ct); + void setGroupType(TraceCost::CostType); + void update(); + +private: + float _pSum, _pSelf; + SubCost _sum, _self; + TraceCostType* _costType; + TraceCost::CostType _groupType; + SubCost _cc; + int _distance, _skipped; + TraceFunction *_function, *_base; + Coverage* _coverage; +}; + +#endif diff --git a/kcachegrind/kcachegrind/coverageview.cpp b/kcachegrind/kcachegrind/coverageview.cpp new file mode 100644 index 00000000..e6f0a14f --- /dev/null +++ b/kcachegrind/kcachegrind/coverageview.cpp @@ -0,0 +1,321 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Coverage Views + */ + +#include +#include +#include + +#include "configuration.h" +#include "coverageitem.h" +#include "coverage.h" +#include "coverageview.h" + + + +// +// CoverageView +// + + +CoverageView::CoverageView(bool showCallers, TraceItemView* parentView, + QWidget* parent, const char* name) + : QListView(parent, name), TraceItemView(parentView) +{ + _showCallers = showCallers; + + + addColumn( i18n( "Incl." ) ); + if (_showCallers) { + addColumn( i18n( "Distance" ) ); + addColumn( i18n( "Called" ) ); + addColumn( i18n( "Caller" ) ); + } + else { + addColumn( i18n( "Self" ) ); + addColumn( i18n( "Distance" ) ); + addColumn( i18n( "Calling" ) ); + addColumn( i18n( "Callee" ) ); + setColumnAlignment(3, Qt::AlignRight); + } + + setSorting(0,false); + setColumnAlignment(0, Qt::AlignRight); + setColumnAlignment(1, Qt::AlignRight); + setColumnAlignment(2, Qt::AlignRight); + setAllColumnsShowFocus(true); + setResizeMode(QListView::LastColumn); + setMinimumHeight(50); + + connect( this, + SIGNAL( selectionChanged(QListViewItem*) ), + SLOT( selectedSlot(QListViewItem*) ) ); + + connect( this, + SIGNAL(contextMenuRequested(QListViewItem*, const QPoint &, int)), + SLOT(context(QListViewItem*, const QPoint &, int))); + + connect(this, + SIGNAL(doubleClicked(QListViewItem*)), + SLOT(activatedSlot(QListViewItem*))); + + connect(this, + SIGNAL(returnPressed(QListViewItem*)), + SLOT(activatedSlot(QListViewItem*))); + + QWhatsThis::add( this, whatsThis() ); +} + +QString CoverageView::whatsThis() const +{ + return _showCallers ? + i18n( "List of all Callers" + "

This list shows all functions calling the " + "current selected one, either directly or with " + "several functions in-between on the stack; the " + "number of functions in-between plus one " + "is called the Distance (e.g. " + "for function A,B,C there exists a call from " + "A to C when A calls B and B calls C, i.e. " + "A => B => C. The distance here is 2).

" + + "

Absolute cost shown is the cost spent in the " + "selected function while a listed function is active; " + "relative cost is the percentage of all cost spent in " + "the selected function while the listed one is " + "active. The cost graphic shows logarithmic " + "percentage with a different color for each " + "distance.

" + + "

As there can be many calls from the same function, " + "the distance column sometimes shows " + "the range of distances for all " + "calls happening; then, in parentheses, there is the " + "medium distance, i.e. the distance where most of the " + "call costs happened.

" + + "

Selecting a function makes it the current selected " + "one of this information panel. " + "If there are two panels (Split mode), the " + "function of the other panel is changed instead.

") : + + i18n( "List of all Callees" + "

This list shows all functions called by the " + "current selected one, either directly or with " + "several function in-between on the stack; the " + "number of function in-between plus one " + "is called the Distance (e.g. " + "for function A,B,C there exists a call from " + "A to C when A calls B and B calls C, i.e. " + "A => B => C. The distance here is 2).

" + + "

Absolute cost shown is the cost spent in the " + "listed function while the selected is active; " + "relative cost is the percentage of all cost spent in " + "the listed function while the selected one is active. " + "The cost graphic always shows logarithmic " + "percentage with a different color for each " + "distance.

" + + "

As there can be many calls to the same function, " + "the distance column sometimes shows " + "the range of distances for all " + "calls happening; then, in parentheses, there is the " + "medium distance, i.e. the distance where most of the " + "call costs happened.

" + + "

Selecting a function makes it the current selected " + "one of this information panel. " + "If there are two panels (Split mode), the " + "function of the other panel is changed instead.

"); +} + +void CoverageView::context(QListViewItem* i, const QPoint & p, int c) +{ + QPopupMenu popup; + + TraceFunction* f = 0; + if (i) { + f = _showCallers ? + ((CallerCoverageItem*)i)->function() : + ((CalleeCoverageItem*)i)->function(); + } + + if (f) { + QString name = f->name(); + if ((int)name.length()>Configuration::maxSymbolLength()) + name = name.left(Configuration::maxSymbolLength()) + "..."; + popup.insertItem(i18n("Go to '%1'").arg(name), 93); + popup.insertSeparator(); + } + + if ((c == 0) || (!_showCallers && c == 1)) { + addCostMenu(&popup, false); + popup.insertSeparator(); + } + addGoMenu(&popup); + + int r = popup.exec(p); + if (r == 93) activated(f); +} + +void CoverageView::selectedSlot(QListViewItem * i) +{ + TraceFunction* f = 0; + if (i) { + f = _showCallers ? + ((CallerCoverageItem*)i)->function() : + ((CalleeCoverageItem*)i)->function(); + } + + if (f) { + _selectedItem = f; + selected(f); + } +} + +void CoverageView::activatedSlot(QListViewItem * i) +{ + TraceFunction* f = 0; + if (i) { + f = _showCallers ? + ((CallerCoverageItem*)i)->function() : + ((CalleeCoverageItem*)i)->function(); + } + + if (f) activated(f); +} + +TraceItem* CoverageView::canShow(TraceItem* i) +{ + TraceItem::CostType t = i ? i->type() : TraceItem::NoCostType; + + switch(t) { + case TraceItem::Function: + case TraceItem::FunctionCycle: + return i; + default: + break; + } + return 0; +} + +void CoverageView::doUpdate(int changeType) +{ + // Special case ? + if (changeType == selectedItemChanged) { + + if (!_selectedItem) { + clearSelection(); + return; + } + + TraceFunction* f = 0; + QListViewItem* i = QListView::selectedItem(); + if (i) { + f = _showCallers ? + ((CallerCoverageItem*)i)->function() : + ((CalleeCoverageItem*)i)->function(); + } + if (f == _selectedItem) return; + + QListViewItem *item; + for (item = firstChild();item;item = item->nextSibling()) { + f = _showCallers ? + ((CallerCoverageItem*)item)->function() : + ((CalleeCoverageItem*)item)->function(); + if (f == _selectedItem) { + ensureItemVisible(item); + setCurrentItem(item); + break; + } + } + return; + } + + if (changeType == groupTypeChanged) { + QListViewItem *item; + for (item = firstChild();item;item = item->nextSibling()) { + if (_showCallers) + ((CallerCoverageItem*)item)->setGroupType(_groupType); + else + ((CalleeCoverageItem*)item)->setGroupType(_groupType); + } + return; + } + + refresh(); +} + +void CoverageView::refresh() +{ + clear(); + setColumnWidth(0, 50); + if (!_showCallers) + setColumnWidth(1, 50); + + if (!_data || !_activeItem) return; + + TraceItem::CostType t = _activeItem->type(); + TraceFunction* f = 0; + if (t == TraceItem::Function) f = (TraceFunction*) _activeItem; + if (t == TraceItem::FunctionCycle) f = (TraceFunction*) _activeItem; + if (!f) return; + + TraceFunction* ff; + TraceFunctionList l; + + _hc.clear(Configuration::maxListCount()); + SubCost realSum = f->inclusive()->subCost(_costType); + + if (_showCallers) + l = Coverage::coverage(f, Coverage::Caller, _costType); + else + l = Coverage::coverage(f, Coverage::Called, _costType); + + for (ff=l.first();ff;ff=l.next()) { + Coverage* c = (Coverage*) ff->assoziation(Coverage::Rtti); + if (c && (c->inclusive()>0.0)) + _hc.addCost(ff, SubCost(realSum * c->inclusive())); + } + + for(int i=0;i<_hc.realCount();i++) { + ff = (TraceFunction*) _hc[i]; + Coverage* c = (Coverage*) ff->assoziation(Coverage::Rtti); + if (_showCallers) + new CallerCoverageItem(this, c, f, _costType, _groupType); + else + new CalleeCoverageItem(this, c, f, _costType, _groupType); + } + if (_hc.hasMore()) { + // a placeholder for all the functions skipped ... + ff = (TraceFunction*) _hc[_hc.maxSize()-1]; + Coverage* c = (Coverage*) ff->assoziation(Coverage::Rtti); + if (_showCallers) + new CallerCoverageItem(this, _hc.count() - _hc.maxSize(), + c, f, _costType, _groupType); + else + new CalleeCoverageItem(this, _hc.count() - _hc.maxSize(), + c, f, _costType, _groupType); + } +} + +#include "coverageview.moc" diff --git a/kcachegrind/kcachegrind/coverageview.h b/kcachegrind/kcachegrind/coverageview.h new file mode 100644 index 00000000..ac70ec0e --- /dev/null +++ b/kcachegrind/kcachegrind/coverageview.h @@ -0,0 +1,56 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Coverage Views + */ + +#ifndef COVERAGEVIEW_H +#define COVERAGEVIEW_H + +#include +#include "tracedata.h" +#include "traceitemview.h" +#include "listutils.h" + +class CoverageView: public QListView, public TraceItemView +{ + Q_OBJECT + +public: + CoverageView(bool showCallers, TraceItemView* parentView, + QWidget* parent=0, const char* name=0); + + virtual QWidget* widget() { return this; } + QString whatsThis() const; + +private slots: + void context(QListViewItem*,const QPoint &, int); + void selectedSlot(QListViewItem*); + void activatedSlot(QListViewItem*); + +private: + TraceItem* canShow(TraceItem*); + void doUpdate(int); + void refresh(); + + HighestCostList _hc; + bool _showCallers; +}; + +#endif diff --git a/kcachegrind/kcachegrind/dumpmanager.cpp b/kcachegrind/kcachegrind/dumpmanager.cpp new file mode 100644 index 00000000..6dda67e6 --- /dev/null +++ b/kcachegrind/kcachegrind/dumpmanager.cpp @@ -0,0 +1,50 @@ +/** + * DumpManager + * Part of KCachegrind + * 2003, Josef Weidendorfer (GPL V2) + */ + +#include "dumpmanager.h" + + +// +// Dump +// + +Dump::Dump(QString file) +{ + _filename = file; +} + + +// +// DumpManager +// + +DumpManager* DumpManager::_self = 0; + + +DumpManager::DumpManager() +{ +} + +DumpManager* DumpManager::self() +{ + if (!_self) + _self = new DumpManager(); + + return _self; +} + + +DumpList DumpManager::loadableDumps() +{ + DumpList res; + + return res; +} + +TraceData* DumpManager::load(Dump*) +{ + return 0; +} diff --git a/kcachegrind/kcachegrind/dumpmanager.h b/kcachegrind/kcachegrind/dumpmanager.h new file mode 100644 index 00000000..3e2ff9e1 --- /dev/null +++ b/kcachegrind/kcachegrind/dumpmanager.h @@ -0,0 +1,59 @@ +/** + * DumpManager + * Part of KCachegrind + * 2003, Josef Weidendorfer (GPL V2) + * + * DumpManager is a Singleton. + * - Has List of current loaded dumps / loadable dumps + * - Does "communication" with current running profiles + * for dump selection dockable + */ + +#ifndef DUMPMANAGER_H +#define DUMPMANAGER_H + +#include +#include + +class Dump; +class TraceData; + +typedef QPtrList DumpList; + + +/** + * A loadable profile Dump + */ +class Dump +{ +public: + Dump(QString); + + QString filename() { return _filename; } + +private: + QString _filename; +}; + + +/* + * TODO: + * - Everything + * + */ + +class DumpManager +{ +public: + DumpManager(); + + DumpManager* self(); + + DumpList loadableDumps(); + TraceData* load(Dump*); + +private: + static DumpManager* _self; +}; + +#endif diff --git a/kcachegrind/kcachegrind/dumpselection.cpp b/kcachegrind/kcachegrind/dumpselection.cpp new file mode 100644 index 00000000..c16470a7 --- /dev/null +++ b/kcachegrind/kcachegrind/dumpselection.cpp @@ -0,0 +1,33 @@ +/** + * DumpSelection Dockable + * Part of KCachegrind + * 2003, Josef Weidendorfer (GPL V2) + * + * - Fast Selection of dumps to load/activate/use for comparing + * - Start a profile run from GUI (current supported: Callgrind) + * - View state of running profile runs. + * + */ + +#include "dumpselection.h" + +/* + * TODO: + * - Everything !! + * - Request State info on current function.. + * + */ + + +DumpSelection::DumpSelection( TopLevel* top, + QWidget* parent, const char* name) + : DumpSelectionBase(parent, name), TraceItemView(0, top) +{ +} + +DumpSelection::~DumpSelection() +{} + + +#include "dumpselection.moc" + diff --git a/kcachegrind/kcachegrind/dumpselection.h b/kcachegrind/kcachegrind/dumpselection.h new file mode 100644 index 00000000..8c59d3e4 --- /dev/null +++ b/kcachegrind/kcachegrind/dumpselection.h @@ -0,0 +1,29 @@ +/** + * DumpSelection Dockable + * Part of KCachegrind + * 2003, Josef Weidendorfer (GPL V2) + * + * - Fast Selection of dumps to load/activate/use for comparing + * - Start a profile run from GUI (current supported: Callgrind) + * - View state of running profile runs. + * + */ + +#ifndef DUMPSELECTION_H +#define DUMPSELECTION_H + +#include "dumpselectionbase.h" +#include "traceitemview.h" + +class DumpSelection : public DumpSelectionBase, public TraceItemView +{ + Q_OBJECT + +public: + DumpSelection( TopLevel*, QWidget* parent = 0, const char* name = 0); + virtual ~DumpSelection(); + + QWidget* widget() { return this; } +}; + +#endif diff --git a/kcachegrind/kcachegrind/dumpselectionbase.ui b/kcachegrind/kcachegrind/dumpselectionbase.ui new file mode 100644 index 00000000..37bc1c46 --- /dev/null +++ b/kcachegrind/kcachegrind/dumpselectionbase.ui @@ -0,0 +1,1082 @@ + +DumpSelectionBase + + + DumpSelectionBase + + + + 0 + 0 + 349 + 832 + + + + Profile Dumps + + + + unnamed + + + + splitter1 + + + Vertical + + + + + Target + + + true + + + true + + + + + + + + true + + + true + + + + + Time + + + true + + + true + + + + + Path + + + true + + + true + + + + listView1 + + + + + tabWidget2 + + + + tab + + + Options + + + + unnamed + + + + textLabel1 + + + + 5 + 5 + 1 + 0 + + + + Target command: + + + + + lineEdit1 + + + + + textLabel2 + + + Profiler options: + + + + + + Option + + + true + + + true + + + + + Value + + + true + + + true + + + + + Trace + + + + + + + + + + + + + Jumps + + + + + + + + + + + + + + Instructions + + + + + + + + + + + + + + + Events + + + + + + + + + + + + + Full Cache + + + + + + + + + + + + + + Custom + + + + + + + + + + + + + + + Collect + + + + + + + + + + + + + At Startup + + + + + + + + + + + + + + While In + + + + + + + + + + + + + + + Skip + + + + + + + + + + + + + PLT + + + + + + + + + + + + + + Function + + + + + + + + + + + + + + + Dump Profile + + + + + + + + + + + + + Every BBs + + + + + + + + + + + + + + On Entering + + + + + + + + + + + + + + On Leaving + + + + + + + + + + + + + + + Zero Events + + + + + + + + + + + + + On Entering + + + + + + + + + + + + + + + Separate + + + + + + + + + + + + + Threads + + + + + + + + + + + + + + Recursions + + + + + + + + + + + + + + Call Chain + + + + + + + + + + + + + + listView3 + + + + + textLabel1_2 + + + + 5 + 5 + 1 + 0 + + + + Custom profiler options: + + + + + lineEdit1_2 + + + + + layout3 + + + + unnamed + + + + spacer1 + + + Horizontal + + + Expanding + + + + 21 + 20 + + + + + + pushButton2 + + + Run New Profile + + + + + + + + + tab + + + Info + + + + unnamed + + + + textLabel8 + + + Dump reason: + + + + + lineEdit3 + + + + + textLabel6 + + + Event summary: + + + + + + Name + + + true + + + true + + + + + Sum + + + true + + + true + + + + listView4 + + + + + textLabel7 + + + Miscellaneous: + + + + + textEdit2 + + + + + layout7 + + + + unnamed + + + + spacer3 + + + Horizontal + + + Expanding + + + + 50 + 20 + + + + + + pushButton6 + + + Show + + + + + pushButton5 + + + Compare + + + + + + + + + tab + + + State + + + + unnamed + + + + layout2 + + + + unnamed + + + + pushButton1 + + + Update + + + + + checkBox1 + + + Every [s]: + + + + + lineEdit3_2 + + + + + + + + Counter + + + true + + + true + + + + + Value + + + true + + + true + + + + + Dumps Done + + + + + + + + + + + + + + Is Collecting + + + + + + + + + + + + + + Executed + + + + + + + + + + + + + Basic Blocks + + + + + + + + + + + + + + Calls + + + + + + + + + + + + + + Jumps + + + + + + + + + + + + + + + Events + + + + + + + + + + + + + Ir + + + + + + + + + + + + + + + Distinct + + + + + + + + + + + + + ELF Objects + + + + + + + + + + + + + + Functions + + + + + + + + + + + + + + Contexts + + + + + + + + + + + + + + listView4_3 + + + + + layout4 + + + + unnamed + + + + textLabel4 + + + + 5 + 5 + 1 + 0 + + + + Stack trace: + + + + + checkBox2 + + + Sync. + + + + + + + + # + + + true + + + true + + + + + Incl. + + + true + + + true + + + + + Called + + + true + + + true + + + + + Function + + + true + + + true + + + + + Location + + + true + + + true + + + + listView7 + + + + + layout6 + + + + unnamed + + + + pushButton7 + + + Start + + + + + spacer2 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + pushButton6_2 + + + Zero + + + + + pushButton4 + + + Dump + + + + + + + + + tab + + + Messages + + + + unnamed + + + + textEdit2_2 + + + + + layout6 + + + + unnamed + + + + pushButton9 + + + Kill Run + + + + + spacer4 + + + Horizontal + + + Expanding + + + + 21 + 20 + + + + + + pushButton8 + + + Clear + + + + + + + + + + + + diff --git a/kcachegrind/kcachegrind/fixcost.cpp b/kcachegrind/kcachegrind/fixcost.cpp new file mode 100644 index 00000000..41029265 --- /dev/null +++ b/kcachegrind/kcachegrind/fixcost.cpp @@ -0,0 +1,174 @@ +/* + * Part of KCacheGrind + * + * 2003, Josef Weidendorfer + */ + +#include "fixcost.h" +#include "utils.h" + + +// FixCost + +FixCost::FixCost(TracePart* part, FixPool* pool, + TraceFunctionSource* functionSource, + PositionSpec& pos, + TracePartFunction* partFunction, + FixString& s) +{ + int maxCount = part->fixSubMapping()->count(); + + _part = part; + _functionSource = functionSource; + _pos = pos; + + _cost = (SubCost*) pool->reserve(sizeof(SubCost) * maxCount); + s.stripSpaces(); + int i = 0; + while(iallocateReserved(sizeof(SubCost) * _count)) + _count = 0; + + _nextCostOfPartFunction = partFunction ? + partFunction->setFirstFixCost(this) : 0; +} + +void* FixCost::operator new(size_t size, FixPool* pool) +{ + return pool->allocate(size); +} + +void FixCost::addTo(TraceCost* c) +{ + TraceSubMapping* sm = _part->fixSubMapping(); + + int i, realIndex; + + for(i=0; i<_count; i++) { + realIndex = sm->realIndex(i); + c->addCost(realIndex, _cost[i]); + } +} + + + +// FixCallCost + +FixCallCost::FixCallCost(TracePart* part, FixPool* pool, + TraceFunctionSource* functionSource, + unsigned int line, Addr addr, + TracePartCall* partCall, + SubCost callCount, FixString& s) +{ + if (0) qDebug("Got FixCallCost (addr 0x%s, line %d): calls %s", + addr.toString().ascii(), line, + callCount.pretty().ascii()); + + int maxCount = part->fixSubMapping()->count(); + + _part = part; + _functionSource = functionSource; + _line = line; + _addr = addr; + + _cost = (SubCost*) pool->reserve(sizeof(SubCost) * (maxCount+1)); + s.stripSpaces(); + int i = 0; + while(iallocateReserved(sizeof(SubCost) * (_count+1) )) + _count = 0; + else + _cost[_count] = callCount; + + _nextCostOfPartCall = partCall ? partCall->setFirstFixCallCost(this) : 0; +} + +void* FixCallCost::operator new(size_t size, FixPool* pool) +{ + return pool->allocate(size); +} + +void FixCallCost::addTo(TraceCallCost* c) +{ + TraceSubMapping* sm = _part->fixSubMapping(); + + int i, realIndex; + + for(i=0; i<_count; i++) { + realIndex = sm->realIndex(i); + c->addCost(realIndex, _cost[i]); + } + c->addCallCount(_cost[_count]); + + if (0) qDebug("Adding from (addr 0x%s, ln %d): calls %s", + _addr.toString().ascii(), _line, + _cost[_count].pretty().ascii()); +} + +void FixCallCost::setMax(TraceCost* c) +{ + TraceSubMapping* sm = _part->fixSubMapping(); + + int i, realIndex; + + for(i=0; i<_count; i++) { + realIndex = sm->realIndex(i); + c->maxCost(realIndex, _cost[i]); + } +} + + +// FixJump + +FixJump::FixJump(TracePart* part, FixPool* pool, + unsigned int line, Addr addr, + TracePartFunction* partFunction, + TraceFunctionSource* source, + unsigned int targetLine, Addr targetAddr, + TraceFunction* targetFunction, + TraceFunctionSource* targetSource, + bool isCondJump, + SubCost executed, SubCost followed) +{ + _part = part; + _source = source; + _line = line; + _addr = addr; + + _targetFunction = targetFunction; + _targetSource = targetSource; + _targetLine = targetLine; + _targetAddr = targetAddr; + + _isCondJump = isCondJump; + + int size = (isCondJump ? 2 : 1) * sizeof(SubCost); + _cost = (SubCost*) pool->allocate(size); + _cost[0] = executed; + if (isCondJump) _cost[1] = followed; + + _nextJumpOfPartFunction = partFunction ? + partFunction->setFirstFixJump(this) : 0; +} + +void* FixJump::operator new(size_t size, FixPool* pool) +{ + return pool->allocate(size); +} + +void FixJump::addTo(TraceJumpCost* jc) +{ + jc->addExecutedCount(_cost[0]); + if (_isCondJump) + jc->addFollowedCount(_cost[1]); +} diff --git a/kcachegrind/kcachegrind/fixcost.h b/kcachegrind/kcachegrind/fixcost.h new file mode 100644 index 00000000..7e90fb40 --- /dev/null +++ b/kcachegrind/kcachegrind/fixcost.h @@ -0,0 +1,171 @@ +/* + * Part of KCacheGrind + * + * 2003, Josef Weidendorfer + */ + +#ifndef FIXCOST_H +#define FIXCOST_H + +/** + * Setting USE_FIXCOST to 1 enables a memory space hack: + * For some data, build up internal data model lazy by using + * the Fix*Cost classes, which are simple copies from input data. + */ +#define USE_FIXCOST 1 + +#include "tracedata.h" +#include "pool.h" + +class PositionSpec +{ + public: + PositionSpec() + { fromLine = 0, toLine = 0, fromAddr = 0, toAddr = 0; } + PositionSpec(uint l1, uint l2, Addr a1, Addr a2) + { fromLine = l1, toLine = l2, fromAddr = a1, toAddr = a2; } + + bool isLineRegion() const { return (fromLine != toLine); } + bool isAddrRegion() const { return (fromAddr != toAddr); } + + uint fromLine, toLine; + Addr fromAddr, toAddr; +}; + +/** + * A class holding an unchangable cost item of an input file. + * + * As there can be a lot of such cost items, we use our own + * allocator which uses FixPool + */ +class FixCost +{ + + public: + FixCost(TracePart*, FixPool*, + TraceFunctionSource*, + PositionSpec&, + TracePartFunction*, + FixString&); + + void *operator new(size_t size, FixPool*); + + void addTo(TraceCost*); + + TracePart* part() const { return _part; } + bool isLineRegion() const { return _pos.isLineRegion(); } + bool isAddrRegion() const { return _pos.isAddrRegion(); } + uint fromLine() const { return _pos.fromLine; } + uint line() const { return _pos.fromLine; } + uint toLine() const { return _pos.toLine; } + Addr fromAddr() const { return _pos.fromAddr; } + Addr addr() const { return _pos.fromAddr; } + Addr toAddr() const { return _pos.toAddr; } + TraceFunctionSource* functionSource() const { return _functionSource; } + + FixCost* nextCostOfPartFunction() const + { return _nextCostOfPartFunction; } + + private: + int _count; + SubCost* _cost; + PositionSpec _pos; + + TracePart* _part; + TraceFunctionSource* _functionSource; + FixCost *_nextCostOfPartFunction; +}; + +/** + * A FixCallCost will be inserted into a + * - TracePartCall to keep source/target function info + * - TraceFunctionSourceFile to keep file info of call source + */ +class FixCallCost +{ + + public: + FixCallCost(TracePart*, FixPool*, + TraceFunctionSource*, + unsigned int line, + Addr addr, + TracePartCall*, + SubCost, FixString&); + + void *operator new(size_t size, FixPool*); + + void addTo(TraceCallCost*); + void setMax(TraceCost*); + + TracePart* part() const { return _part; } + unsigned int line() const { return _line; } + Addr addr() const { return _addr; } + SubCost callCount() const { return _cost[_count]; } + TraceFunctionSource* functionSource() const { return _functionSource; } + FixCallCost* nextCostOfPartCall() const + { return _nextCostOfPartCall; } + + private: + // we use 1 SubCost more than _count: _cost[_count] is the call count + int _count; + SubCost* _cost; + unsigned int _line; + Addr _addr; + + TracePart* _part; + TraceFunctionSource* _functionSource; + FixCallCost* _nextCostOfPartCall; +}; + +/** + * A class holding a jump (mostly) inside of a function + */ +class FixJump +{ + + public: + FixJump(TracePart*, FixPool*, + /* source position */ + unsigned int line, Addr addr, + TracePartFunction*, TraceFunctionSource*, + /* target position */ + unsigned int targetLine, Addr targetAddr, + TraceFunction*, TraceFunctionSource*, + bool isCondJump, + SubCost, SubCost); + + void *operator new(size_t size, FixPool*); + + void addTo(TraceJumpCost*); + + TracePart* part() const { return _part; } + unsigned int line() const { return _line; } + Addr addr() const { return _addr; } + TraceFunctionSource* source() const { return _source; } + TraceFunction* targetFunction() const { return _targetFunction; } + unsigned int targetLine() const { return _targetLine; } + Addr targetAddr() const { return _targetAddr; } + TraceFunctionSource* targetSource() const { return _targetSource; } + bool isCondJump() { return _isCondJump; } + SubCost executedCount() const { return _cost[0]; } + SubCost followedCount() const + { return _isCondJump ? _cost[1] : SubCost(0); } + + FixJump* nextJumpOfPartFunction() const + { return _nextJumpOfPartFunction; } + + private: + bool _isCondJump; + SubCost* _cost; + unsigned int _line, _targetLine; + Addr _addr, _targetAddr; + + TracePart* _part; + TraceFunctionSource *_source, *_targetSource; + TraceFunction* _targetFunction; + FixJump *_nextJumpOfPartFunction; +}; + +#endif + + diff --git a/kcachegrind/kcachegrind/functionitem.cpp b/kcachegrind/kcachegrind/functionitem.cpp new file mode 100644 index 00000000..9d62b2eb --- /dev/null +++ b/kcachegrind/kcachegrind/functionitem.cpp @@ -0,0 +1,236 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * List Item for the FunctionSelection list + */ + + +//#include + +//#include +//#include + +#include +#include +#include + +#include "listutils.h" +#include "functionitem.h" +#include "configuration.h" + + +// FunctionItem + +FunctionItem::FunctionItem(QListView* parent, TraceFunction* f, + TraceCostType* ct, TraceCost::CostType gt) + :QListViewItem(parent) +{ +#if 0 + _costPixValid = false; + _groupPixValid = false; +#endif + + _function = f; + _skipped = 0; + _groupType = TraceCost::NoCostType; + setGroupType(gt); + setCostType(ct); + + setText(3, f->prettyName()); + setText(4, f->prettyLocation()); +} + +FunctionItem::FunctionItem(QListView* parent, int skipped, + TraceFunction* f, TraceCostType* ct) + :QListViewItem(parent) +{ +#if 0 + _costPixValid = false; + _groupPixValid = false; +#endif + _skipped = skipped; + _function = f; + _groupType = TraceCost::NoCostType; + setCostType(ct); + + setText(3, i18n("(%n function skipped)", "(%n functions skipped)", skipped)); +} + +#if 0 +const QPixmap* FunctionItem::pixmap(int column) const +{ + if (column == 3) { + if (!_groupPixValid) { + QColor c = Configuration::functionColor(_groupType, _function); + _groupPix = colorPixmap(10, 10, c); + _groupPixValid = true; + } + return &_groupPix; + } + if (column == 1) { + if (!_costPixValid) { + _costPix = colorPixmap(10, 10, c); + _costPixValid = true; + } + return &_costPix; + } + return 0; +} +#endif + +void FunctionItem::setGroupType(TraceCost::CostType gt) +{ + if (_skipped) return; + if (_groupType == gt) return; + _groupType = gt; + + +#if 0 + _groupPixValid = false; + viewList()->repaint(); +#else + QColor c = Configuration::functionColor(_groupType, _function); + setPixmap(3, colorPixmap(10, 10, c)); +#endif +} + +void FunctionItem::setCostType(TraceCostType* c) +{ + _costType = c; + update(); +} + +void FunctionItem::update() +{ + double inclTotal = _function->data()->subCost(_costType); + QString str; + + TraceCost* selfCost = _function->data(); + if (Configuration::showExpanded()) { + switch(_groupType) { + case TraceCost::Object: selfCost = _function->object(); break; + case TraceCost::Class: selfCost = _function->cls(); break; + case TraceCost::File: selfCost = _function->file(); break; + default: break; + } + } + double selfTotal = selfCost->subCost(_costType); + + if (_skipped) { + // special handling for skip entries... + + // only text updates of incl./self + + // for all skipped functions, cost is below the given function + _sum = _function->inclusive()->subCost(_costType); + double incl = 100.0 * _sum / inclTotal; + if (Configuration::showPercentage()) + str = QString("%1").arg(incl, 0, 'f', Configuration::percentPrecision()); + else + str = _function->inclusive()->prettySubCost(_costType); + str = "< " + str; + setText(0, str); + setText(1, str); + return; + } + + // Call count... + if (_function->calledCount() >0) + str = _function->prettyCalledCount(); + else { + if (_function == _function->cycle()) + str = QString("-"); + else + str = QString("(0)"); + } + setText(2, str); + + // Incl. cost + _sum = _function->inclusive()->subCost(_costType); + if (inclTotal == 0.0) { + setPixmap(0, QPixmap()); + setText(0, "-"); + } + else { + double incl = 100.0 * _sum / inclTotal; + if (Configuration::showPercentage()) + setText(0, QString("%1") + .arg(incl, 0, 'f', Configuration::percentPrecision())); + else + setText(0, _function->inclusive()->prettySubCost(_costType)); + + setPixmap(0, costPixmap(_costType, _function->inclusive(), inclTotal, false)); + } + + // self + _pure = _function->subCost(_costType); + if (selfTotal == 0.0) { + setPixmap(1, QPixmap()); + setText(1, "-"); + } + else { + double self = 100.0 * _pure / selfTotal; + + if (Configuration::showPercentage()) + setText(1, QString("%1") + .arg(self, 0, 'f', Configuration::percentPrecision())); + else + setText(1, _function->prettySubCost(_costType)); + + setPixmap(1, costPixmap(_costType, _function, selfTotal, false)); + } +} + + +int FunctionItem::compare(QListViewItem * i, int col, bool ascending ) const +{ + const FunctionItem* fi1 = this; + const FunctionItem* fi2 = (FunctionItem*) i; + + // we always want descending order + if (ascending) { + fi1 = fi2; + fi2 = this; + } + + // a skip entry is always sorted last + if (fi1->_skipped) return -1; + if (fi2->_skipped) return 1; + + if (col==0) { + if (fi1->_sum < fi2->_sum) return -1; + if (fi1->_sum > fi2->_sum) return 1; + return 0; + } + if (col==1) { + if (fi1->_pure < fi2->_pure) return -1; + if (fi1->_pure > fi2->_pure) return 1; + return 0; + } + if (col==2) { + if (fi1->_function->calledCount() < + fi2->_function->calledCount()) return -1; + if (fi1->_function->calledCount() > + fi2->_function->calledCount()) return 1; + return 0; + } + + return QListViewItem::compare(i, col, ascending); +} + diff --git a/kcachegrind/kcachegrind/functionitem.h b/kcachegrind/kcachegrind/functionitem.h new file mode 100644 index 00000000..94545dcb --- /dev/null +++ b/kcachegrind/kcachegrind/functionitem.h @@ -0,0 +1,58 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * List Item for the FunctionSelection list + */ + +#ifndef FUNCTIONITEM_H +#define FUNCTIONITEM_H + +#include +#include "tracedata.h" + +class FunctionItem: public QListViewItem +{ +public: + FunctionItem(QListView* parent, TraceFunction* function, + TraceCostType* ct, TraceCost::CostType gt); + // constructor for a "Skipped ... " entry + FunctionItem(QListView* parent, int skipped, + TraceFunction* function, TraceCostType* ct); + + int compare(QListViewItem * i, int col, bool ascending ) const; + TraceFunction* function() { return (_skipped) ? 0 : _function; } + void setCostType(TraceCostType* ct); + void setGroupType(TraceCost::CostType); + void update(); + +#if 0 + const QPixmap* pixmap (int column) const; + bool _costPixValid, _groupPixValid; + QPixMap _costPix, _groupPix; +#endif + +private: + SubCost _sum, _pure; + TraceCostType* _costType; + TraceCost::CostType _groupType; + TraceFunction* _function; + int _skipped; +}; + +#endif diff --git a/kcachegrind/kcachegrind/functionselection.cpp b/kcachegrind/kcachegrind/functionselection.cpp new file mode 100644 index 00000000..8c4e8a5a --- /dev/null +++ b/kcachegrind/kcachegrind/functionselection.cpp @@ -0,0 +1,871 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002, 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * For function selection, to be put into a QDockWindow + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "traceitemview.h" +#include "stackbrowser.h" +#include "functionselection.h" +#include "partgraph.h" +#include "functionitem.h" +#include "costlistitem.h" +#include "configuration.h" +#include "toplevel.h" + +FunctionSelection::FunctionSelection( TopLevel* top, + QWidget* parent, const char* name) + : FunctionSelectionBase(parent, name), TraceItemView(0, top) +{ + _group = 0; + _inSetGroup = false; + _inSetFunction = false; + + QStringList args; + args << i18n("(No Grouping)") + << TraceCost::i18nTypeName(TraceItem::Object) + << TraceCost::i18nTypeName(TraceItem::File) + << TraceCost::i18nTypeName(TraceItem::Class) + << TraceCost::i18nTypeName(TraceItem::FunctionCycle); + + groupBox->insertStringList(args); + // this needs same order of grouptype actionlist! + connect(groupBox, SIGNAL(activated(int)), + top, SLOT(groupTypeSelected(int))); + + // search while typing... + connect(searchEdit, SIGNAL(textChanged(const QString&)), + this, SLOT(searchChanged(const QString&))); + connect(&_searchTimer, SIGNAL(timeout()), + this, SLOT(queryDelayed())); + // select first matching group/function on return + connect(searchEdit, SIGNAL(returnPressed()), + this, SLOT(searchReturnPressed())); + searchEdit->setMinimumWidth(50); + + // we start with desending cost sorting + functionList->setSorting(0,false); + functionList->setColumnAlignment(0, Qt::AlignRight); + functionList->setColumnAlignment(1, Qt::AlignRight); + functionList->setColumnAlignment(2, Qt::AlignRight); + functionList->setAllColumnsShowFocus(true); + // functionList->setShowSortIndicator(true); + // we can have very long function and location names + functionList->setColumnWidthMode(3, QListView::Manual); + functionList->setColumnWidth(3, 200); + functionList->setColumnWidthMode(4, QListView::Manual); + functionList->setColumnWidth(4, 200); + + groupList->setSorting(0,false); + groupList->setColumnAlignment(0, Qt::AlignRight); + groupList->setAllColumnsShowFocus(true); + // groupList->setShowSortIndicator(true); + groupList->setResizeMode(QListView::LastColumn); + +#if 0 + // single click press activation + connect(functionList, SIGNAL(selectionChanged(QListViewItem*)), + this, SLOT(functionActivated(QListViewItem*))); + connect(functionList, + SIGNAL(contextMenuRequested(QListViewItem*, const QPoint &, int)), + this, SLOT(functionContext(QListViewItem*, const QPoint &, int))); +#else + // single click release activation + connect(functionList, SIGNAL(selectionChanged(QListViewItem*)), + this, SLOT(functionSelected(QListViewItem*))); + connect(functionList, SIGNAL(clicked(QListViewItem*)), + this, SLOT(functionActivated(QListViewItem*))); + connect(functionList, SIGNAL(returnPressed(QListViewItem*)), + this, SLOT(functionActivated(QListViewItem*))); + connect(functionList, + SIGNAL(contextMenuRequested(QListViewItem*, const QPoint &, int)), + this, SLOT(functionContext(QListViewItem*, const QPoint &, int))); +#endif + + connect(groupList, SIGNAL(selectionChanged(QListViewItem*)), + this, SLOT(groupSelected(QListViewItem*))); + connect(groupList, SIGNAL(doubleClicked(QListViewItem*)), + this, SLOT(groupDoubleClicked(QListViewItem*))); + connect(groupList, SIGNAL(returnPressed(QListViewItem*)), + this, SLOT(groupDoubleClicked(QListViewItem*))); + connect(groupList, + SIGNAL(contextMenuRequested(QListViewItem*, const QPoint &, int)), + this, SLOT(groupContext(QListViewItem*, const QPoint &, int))); + + // start hidden + groupList->hide(); +} + +FunctionSelection::~FunctionSelection() +{ +} + +void FunctionSelection::searchReturnPressed() +{ + query(searchEdit->text()); + + QListViewItem* item; + if (_groupType != TraceItem::Function) { + // if current group not matching, select first matching group + item = groupList->currentItem(); + if (!item || !item->isVisible()) { + item = groupList->firstChild(); + for (;item;item = item->nextSibling()) + if (item->isVisible()) break; + if (!item) return; + + setGroup(((CostListItem*)item)->costItem()); + return; + } + } + + functionActivated(functionList->firstChild()); +} + +// trigger the query after some delay, dependent on length +void FunctionSelection::searchChanged(const QString& q) +{ + _searchDelayed = q; + int ms = 100; + if (q.length()<5) ms = 200; + if (q.length()<2) ms = 300; + _searchTimer.start(ms,true); +} + +void FunctionSelection::queryDelayed() +{ + query(_searchDelayed); +} + +void FunctionSelection::functionContext(QListViewItem* i, + const QPoint & p, int c) +{ + QPopupMenu popup; + TraceFunction* f = 0; + + if (i) { + f = ((FunctionItem*) i)->function(); + if (f) { + popup.insertItem(i18n("Go to %1").arg(f->prettyName()), 93); + popup.insertSeparator(); + } + } + + if ((c == 0) || (c == 1)) { + addCostMenu(&popup,false); + popup.insertSeparator(); + } + addGroupMenu(&popup); + popup.insertSeparator(); + addGoMenu(&popup); + + int r = popup.exec(p); + if (r == 93) activated(f); +} + +void FunctionSelection::groupContext(QListViewItem* /*i*/, + const QPoint & p, int c) +{ + QPopupMenu popup; + +#if 0 + TraceCostItem* g = 0; + if (i) { + g = ((CostListItem*) i)->costItem(); + if (!g) { + popup.insertItem(i18n("Show All Items"), 93); + popup.insertSeparator(); + } + } +#endif + if (c == 0) { + addCostMenu(&popup,false); + popup.insertSeparator(); + } + addGroupMenu(&popup); + popup.insertSeparator(); + addGoMenu(&popup); + + popup.exec(p); +} + + +void FunctionSelection::addGroupMenu(QPopupMenu* popup) +{ + QPopupMenu *popup1 = new QPopupMenu(popup); + popup1->setCheckable(true); + + if (_groupType != TraceItem::Function) { + popup1->insertItem(i18n("No Grouping"),0); + popup1->insertSeparator(); + } + popup1->insertItem(TraceCost::i18nTypeName(TraceItem::Object),1); + popup1->insertItem(TraceCost::i18nTypeName(TraceItem::File),2); + popup1->insertItem(TraceCost::i18nTypeName(TraceItem::Class),3); + popup1->insertItem(TraceCost::i18nTypeName(TraceItem::FunctionCycle),4); + switch(_groupType) { + case TraceItem::Object: popup1->setItemChecked(1, true); break; + case TraceItem::File: popup1->setItemChecked(2, true); break; + case TraceItem::Class: popup1->setItemChecked(3, true); break; + case TraceItem::FunctionCycle: popup1->setItemChecked(4, true); break; + default: break; + } + connect(popup1,SIGNAL(activated(int)), + _topLevel,SLOT(groupTypeSelected(int))); + + popup->insertItem(i18n("Grouping"), popup1); +} + + +TraceItem* FunctionSelection::canShow(TraceItem* i) +{ + TraceItem::CostType t = i ? i->type() : TraceItem::NoCostType; + + switch(t) { + case TraceItem::Function: + case TraceItem::FunctionCycle: + case TraceItem::Object: + case TraceItem::File: + case TraceItem::Class: + break; + + case TraceItem::Instr: + i = ((TraceInstr*)i)->function(); + break; + + case TraceItem::Line: + i = ((TraceLine*)i)->functionSource()->function(); + break; + + default: + i = 0; + break; + } + return i; +} + + +void FunctionSelection::doUpdate(int changeType) +{ + // Special case ? + if (changeType == selectedItemChanged) return; + + // we don't show cost 2 at all... + if (changeType == costType2Changed) return; + + if (changeType == activeItemChanged) { + if (_activeItem ==0) { + functionList->clearSelection(); + return; + } + switch(_activeItem->type()) { + case TraceItem::Object: + case TraceItem::File: + case TraceItem::Class: + setGroup((TraceCostItem*)_activeItem); + return; + default: break; + } + + // active item is a function + TraceFunction* f = (TraceFunction*) _activeItem; + + // if already current, nothing to do + QListViewItem* i = functionList->currentItem(); + if (i && (((FunctionItem*)i)->function() == f)) { + functionList->setSelected(i,true); + return; + } + + // reset searchEdit (as not activated from this view) + _searchString = QString::null; + query(QString::null); + + // select cost item group of function + switch(_groupType) { + case TraceItem::Object: setGroup(f->object()); break; + case TraceItem::Class: setGroup(f->cls()); break; + case TraceItem::File: setGroup(f->file()); break; + case TraceItem::FunctionCycle: setGroup(f->cycle()); break; + default: + break; + } + + QListViewItem* item = functionList->firstChild(); + for (;item;item = item->nextSibling()) + if (((FunctionItem*)item)->function() == f) + break; + + if (!item) + item = new FunctionItem(functionList, f, _costType, _groupType); + + functionList->ensureItemVisible(item); + // prohibit signalling of a function selection + _inSetFunction = true; + functionList->setSelected(item, true); + _inSetFunction = false; + + return; + } + + if (changeType & groupTypeChanged) { + if (_activeItem && (_activeItem->type() == TraceItem::Function)) { + TraceFunction* f = (TraceFunction*) _activeItem; + + // select cost item group of function + switch(_groupType) { + case TraceItem::Object: _group = f->object(); break; + case TraceItem::Class: _group = f->cls(); break; + case TraceItem::File: _group = f->file(); break; + case TraceItem::FunctionCycle: _group = f->cycle(); break; + default: + _group = 0; + break; + } + } + + int id; + switch(_groupType) { + case TraceItem::Object: id = 1; break; + case TraceItem::File: id = 2; break; + case TraceItem::Class: id = 3; break; + case TraceItem::FunctionCycle: id = 4; break; + default: id = 0; break; + } + groupBox->setCurrentItem(id); + + if (_groupType == TraceItem::Function) + groupList->hide(); + else + groupList->show(); + } + + // reset searchEdit + _searchString = QString::null; + query(QString::null); + + refresh(); +} + + +/* + * This set/selects a group of the set available within the + * current group type + */ +void FunctionSelection::setGroup(TraceCostItem* g) +{ + if (!g) return; + if (g->type() != _groupType) return; + if (g == _group) return; + _group = g; + + QListViewItem* item = groupList->firstChild(); + for (;item;item = item->nextSibling()) + if (((CostListItem*)item)->costItem() == g) + break; + + if (item) { + groupList->ensureItemVisible(item); + // prohibit signalling of a group selection + _inSetGroup = true; + groupList->setSelected(item, true); + _inSetGroup = false; + } + else + groupList->clearSelection(); +} + + +void FunctionSelection::refresh() +{ + groupList->setUpdatesEnabled(false); + groupList->clear(); + + // make cost columns as small as possible: + // the new functions make them as wide as needed + groupList->setColumnWidth(0, 50); + + groupList->setColumnText(1, TraceItem::i18nTypeName(_groupType)); + + if (!_data || _data->parts().count()==0) { + functionList->clear(); + groupList->setUpdatesEnabled(true); + groupList->repaint(); + + // this clears all other lists + functionList->setSelected(functionList->firstChild(), true); + return; + } + + /* + qDebug("FunctionSelection::fillLists (%s)", + _data->command().ascii()); + */ + + TraceObjectMap::Iterator oit; + TraceClassMap::Iterator cit; + TraceFileMap::Iterator fit; + QListViewItem *i = 0, *item = 0, *fitem = 0; + + // Fill up group list. + // Always show group of current function, even if cost below low limit. + // + + _hc.clear(Configuration::maxListCount()); + + TraceCostItem *group; + + // update group from _activeItem if possible + if (_activeItem && (_activeItem->type() == _groupType)) + _group = (TraceCostItem*) _activeItem; + + switch(_groupType) { + case TraceItem::Object: + + for ( oit = _data->objectMap().begin(); + oit != _data->objectMap().end(); ++oit ) + _hc.addCost(&(*oit), (*oit).subCost(_costType)); + break; + + case TraceItem::Class: + + for ( cit = _data->classMap().begin(); + cit != _data->classMap().end(); ++cit ) + _hc.addCost(&(*cit), (*cit).subCost(_costType)); + break; + + case TraceItem::File: + + for ( fit = _data->fileMap().begin(); + fit != _data->fileMap().end(); ++fit ) + _hc.addCost(&(*fit), (*fit).subCost(_costType)); + break; + + case TraceItem::FunctionCycle: + { + // add all cycles + TraceFunctionCycleList l = _data->functionCycles(); + for (group=l.first();group;group=l.next()) + _hc.addCost(group, group->subCost(_costType)); + } + + break; + + default: + { + QListViewItem* oldItem = functionList->selectedItem(); + TraceFunction* oldFunction = 0; + int oldPos = 0; + if (oldItem) { + oldFunction = ((FunctionItem*)oldItem)->function(); + oldPos = oldItem->itemPos(); + oldPos -= functionList->contentsY(); + if (oldPos < 0 || oldPos > functionList->height()) + oldFunction = 0; + } + + // switching off QListView updates is buggy with some QT versions... + //functionList->setUpdatesEnabled(false); + functionList->clear(); + setCostColumnWidths(); + + if (0) qDebug("Function %s at %d, Item %p", + oldFunction ? oldFunction->name().ascii() : "-", + oldPos, (void*)oldItem); + + TraceFunctionMap::Iterator it; + TraceFunction *f; + i = 0; + fitem = 0; + for ( it = _data->functionMap().begin(); + it != _data->functionMap().end(); ++it ) + _hc.addCost(&(*it), (*it).inclusive()->subCost(_costType)); + + TraceFunctionCycleList l = _data->functionCycles(); + for (f=l.first();f;f=l.next()) + _hc.addCost(f, f->inclusive()->subCost(_costType)); + + if (_activeItem && + ((_activeItem->type() == TraceItem::Function) || + (_activeItem->type() == TraceItem::FunctionCycle))) + fitem = new FunctionItem(functionList, (TraceFunction*)_activeItem, + _costType, _groupType); + + for(int i=0;i<_hc.realCount();i++) { + f = (TraceFunction*)_hc[i]; + if (f == _activeItem) continue; + new FunctionItem(functionList, f, _costType, _groupType); + } + if (_hc.hasMore()) { + // a placeholder for all the cost items skipped ... + new FunctionItem(functionList, _hc.count() - _hc.maxSize(), + (TraceFunction*)_hc[_hc.maxSize()-1], _costType); + } + functionList->sort(); + + if (fitem && oldFunction) { + _inSetFunction = true; + functionList->setSelected(fitem, true); + _inSetFunction = false; + int newPos = functionList->itemPos(fitem) - functionList->contentsY(); + functionList->scrollBy(0, newPos-oldPos); + } + else if (fitem) { + functionList->ensureItemVisible(fitem); + _inSetFunction = true; + functionList->setSelected(fitem, true); + _inSetFunction = false; + } + else + functionList->clearSelection(); + + //functionList->setUpdatesEnabled(true); + //functionList->repaint(); + groupList->setUpdatesEnabled(true); + groupList->repaint(); + return; + } + } + + // we always put group of active item in list, even if + // it would be skipped because of small costs + if (_group) + item = new CostListItem(groupList, _group, _costType); + + for(int i=0;i<_hc.realCount();i++) { + group = (TraceCostItem*)_hc[i]; + // don't put group of active item twice into list + if (group == _group) continue; + new CostListItem(groupList, group, _costType); + } + if (_hc.hasMore()) { + // a placeholder for all the cost items skipped ... + new CostListItem(groupList, _hc.count() - _hc.maxSize(), + (TraceCostItem*)_hc[_hc.maxSize()-1], _costType); + } + groupList->sort(); + if (item) { + groupList->ensureItemVisible(item); + _inSetGroup = true; + groupList->setSelected(item, true); + _inSetGroup = false; + } + else + groupList->clearSelection(); + + groupList->setUpdatesEnabled(true); + groupList->repaint(); +} + + +void FunctionSelection::groupSelected(QListViewItem* i) +{ + if (!i) return; + if (!_data) return; + + TraceCostItem* g = ((CostListItem*) i)->costItem(); + if (!g) return; + + _group = g; + + TraceFunctionList list; + + switch(g->type()) { + case TraceItem::Object: + list = ((TraceObject*)g)->functions(); + break; + case TraceItem::Class: + list = ((TraceClass*)g)->functions(); + break; + case TraceItem::File: + list = ((TraceFile*)g)->functions(); + break; + case TraceItem::FunctionCycle: + list = ((TraceFunctionCycle*)g)->members(); + break; + default: + return; + } + + // switching off QListView updates is buggy with some QT versions... + //functionList->setUpdatesEnabled(false); + + functionList->clear(); + setCostColumnWidths(); + + double total; + if (Configuration::showExpanded()) + total = (double) g->subCost(_costType); + else + total = (double) _data->subCost(_costType); +#if 0 + if (total == 0.0) { + functionList->setUpdatesEnabled(true); + functionList->repaint(); + return; + } +#endif + + QRegExp re(_searchString, false, true); + + FunctionItem* fitem = 0; + TraceFunction *f; + _hc.clear(Configuration::maxListCount()); + for (f=list.first();f;f=list.next()) { + if (re.search(f->prettyName())<0) continue; + + _hc.addCost(f, f->inclusive()->subCost(_costType)); + if (_activeItem == f) + fitem = new FunctionItem(functionList, (TraceFunction*)_activeItem, + _costType, _groupType); + } + + for(int i=0;i<_hc.realCount();i++) { + if (_activeItem == (TraceFunction*)_hc[i]) continue; + new FunctionItem(functionList, (TraceFunction*)_hc[i], + _costType, _groupType); + } + + if (_hc.hasMore()) { + // a placeholder for all the functions skipped ... + new FunctionItem(functionList, _hc.count() - _hc.maxSize(), + (TraceFunction*)_hc[_hc.maxSize()-1], _costType); + } + functionList->sort(); + + if (fitem) { + functionList->ensureItemVisible(fitem); + _inSetFunction = true; + functionList->setSelected(fitem, true); + _inSetFunction = false; + } + + //functionList->setUpdatesEnabled(true); + //functionList->repaint(); + + // Don't emit signal if cost item was changed programatically + if (!_inSetGroup) { + _selectedItem = g; + selected(g); + } +} + +void FunctionSelection::groupDoubleClicked(QListViewItem* i) +{ + if (!i) return; + if (!_data) return; + TraceCostItem* g = ((CostListItem*) i)->costItem(); + + if (!g) return; + // group must be selected first + if (g != _group) return; + + activated(g); +} + + +TraceCostItem* FunctionSelection::group(QString s) +{ + QListViewItem *item; + item = groupList->firstChild(); + for(;item;item = item->nextSibling()) + if (((CostListItem*)item)->costItem()->name() == s) + return ((CostListItem*)item)->costItem(); + + return 0; +} + + + +void FunctionSelection::functionSelected(QListViewItem* i) +{ + if (!i) return; + if (!_data) return; + + TraceFunction* f = ((FunctionItem*) i)->function(); + if (!f) return; + + //qDebug("FunctionSelection::functionSelected %s", f->name().ascii()); + + // Don't emit signal if function was changed programatically + if (!_inSetFunction) { + _selectedItem = f; + selected(f); + } +} + +void FunctionSelection::functionActivated(QListViewItem* i) +{ + if (!i) return; + if (!_data) return; + TraceFunction* f = ((FunctionItem*) i)->function(); + + if (!f) return; + + if (!_inSetFunction) + activated(f); +} + +void FunctionSelection::updateGroupSizes(bool hideEmpty) +{ + QListViewItem* item = groupList->firstChild(); + for (;item;item = item->nextSibling()) { + CostListItem* i = (CostListItem*)item; + int size = (_groupSize.contains(i->costItem())) ? + _groupSize[i->costItem()] : -1; + i->setSize(size); + i->setVisible(!hideEmpty || (size>0)); + } +} + +void FunctionSelection::query(QString query) +{ + if (searchEdit->text() != query) + searchEdit->setText(query); + if (_searchString == query) { + // when resetting query, get rid of group sizes + if (query.isEmpty()) { + _groupSize.clear(); + updateGroupSizes(false); + } + return; + } + _searchString = query; + + QRegExp re(query, false, true); + _groupSize.clear(); + + TraceFunction* f = 0; + TraceFunctionList list2; + + _hc.clear(Configuration::maxListCount()); + + TraceFunctionMap::Iterator it; + for ( it = _data->functionMap().begin(); + it != _data->functionMap().end(); ++it ) { + f = &(*it); + if (re.search(f->prettyName())>=0) { + if (_group) { + if (_groupType==TraceItem::Object) { + if (_groupSize.contains(f->object())) + _groupSize[f->object()]++; + else + _groupSize[f->object()] = 1; + if (f->object() != _group) continue; + } + else if (_groupType==TraceItem::Class) { + if (_groupSize.contains(f->cls())) + _groupSize[f->cls()]++; + else + _groupSize[f->cls()] = 1; + if (f->cls() != _group) continue; + } + else if (_groupType==TraceItem::File) { + if (_groupSize.contains(f->file())) + _groupSize[f->file()]++; + else + _groupSize[f->file()] = 1; + if (f->file() != _group) continue; + } + else if (_groupType==TraceItem::FunctionCycle) { + if (_groupSize.contains(f->cycle())) + _groupSize[f->cycle()]++; + else + _groupSize[f->cycle()] = 1; + if (f->cycle() != _group) continue; + } + } + _hc.addCost(f, f->inclusive()->subCost(_costType)); + } + } + + updateGroupSizes(true); + + FunctionItem *fi, *item = 0; + + functionList->clear(); + setCostColumnWidths(); + + for(int i=0;i<_hc.realCount();i++) { + fi = new FunctionItem(functionList, (TraceFunction*)_hc[i], + _costType, _groupType); + if (_activeItem == f) item = fi; + } + if (_hc.hasMore()) { + // a placeholder for all the functions skipped ... + new FunctionItem(functionList, _hc.count() - _hc.maxSize(), + (TraceFunction*)_hc[_hc.maxSize()-1], _costType); + } + + functionList->sort(); + + + if (item) { + functionList->ensureItemVisible(item); + _inSetFunction = true; + functionList->setSelected(item, true); + _inSetFunction = false; + } + else { + // this emits a function selection + functionList->setSelected(functionList->firstChild(), true); + } +} + +bool FunctionSelection::setTopFunction() +{ + QListViewItem* i = functionList->firstChild(); + // this emits a function selection + functionList->setSelected(i, true); + functionActivated(i); + return i!=0; +} + +void FunctionSelection::setCostColumnWidths() +{ + if (_costType && (_costType->subCost(_data->callMax())>0) ) { + functionList->setColumnWidthMode(0, QListView::Maximum); + functionList->setColumnWidth(0,50); + functionList->setColumnWidthMode(2, QListView::Maximum); + functionList->setColumnWidth(2,50); + } + else { + functionList->setColumnWidthMode(0, QListView::Manual); + functionList->setColumnWidth(0,0); + functionList->setColumnWidthMode(2, QListView::Manual); + functionList->setColumnWidth(2,0); + } + + functionList->setColumnWidth(1, 50); +} + + + +#include "functionselection.moc" diff --git a/kcachegrind/kcachegrind/functionselection.h b/kcachegrind/kcachegrind/functionselection.h new file mode 100644 index 00000000..84e637aa --- /dev/null +++ b/kcachegrind/kcachegrind/functionselection.h @@ -0,0 +1,85 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002, 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * For function selection, to be put into a QDockWindow + */ + +#ifndef FUNCTIONSELECTION_H +#define FUNCTIONSELECTION_H + +#include "functionselectionbase.h" +#include "traceitemview.h" +#include "tracedata.h" +#include "listutils.h" + +class QPopupMenu; + +class TraceFunction; +class TraceData; +class StackBrowser; +class NestedAreaItem; + +class FunctionSelection : public FunctionSelectionBase, public TraceItemView +{ + Q_OBJECT + +public: + FunctionSelection( TopLevel*, QWidget* parent = 0, const char* name = 0); + ~FunctionSelection(); + + TraceCostItem* group(QString); + void setGroup(TraceCostItem*); + void query(QString); + bool setTopFunction(); + + QWidget* widget() { return this; } + + void addGroupMenu(QPopupMenu*); + +public slots: + void searchReturnPressed(); + void searchChanged(const QString&); + void queryDelayed(); + void groupDoubleClicked( QListViewItem* ); + void functionActivated( QListViewItem* ); + void groupSelected( QListViewItem* ); + void functionSelected( QListViewItem* ); + void functionContext(QListViewItem*, const QPoint &, int); + void groupContext(QListViewItem*, const QPoint &, int); + +private: + TraceItem* canShow(TraceItem* i); + void doUpdate(int); + void selectFunction(); + void refresh(); + void setCostColumnWidths(); + void updateGroupSizes(bool hideEmpty); + + TraceCostItem* _group; + + QString _searchString, _searchDelayed; + QTimer _searchTimer; + QMap _groupSize; + + HighestCostList _hc; + // when setting a + bool _inSetGroup, _inSetFunction; +}; + +#endif diff --git a/kcachegrind/kcachegrind/functionselectionbase.ui b/kcachegrind/kcachegrind/functionselectionbase.ui new file mode 100644 index 00000000..e59dc26c --- /dev/null +++ b/kcachegrind/kcachegrind/functionselectionbase.ui @@ -0,0 +1,163 @@ + +FunctionSelectionBase + + + FunctionSelectionBase + + + + 0 + 0 + 223 + 485 + + + + Function Profile + + + + unnamed + + + 3 + + + 6 + + + + layout1 + + + + unnamed + + + + searchLabel + + + &Search: + + + searchEdit + + + + + searchEdit + + + + + groupBox + + + + + + + + Self + + + true + + + true + + + + + Group + + + true + + + true + + + + groupList + + + + 7 + 5 + 0 + 0 + + + + + 32767 + 150 + + + + + + + Incl. + + + true + + + true + + + + + Self + + + true + + + true + + + + + Called + + + true + + + true + + + + + Function + + + true + + + true + + + + + Location + + + true + + + true + + + + functionList + + + + + + diff --git a/kcachegrind/kcachegrind/hi32-app-kcachegrind.png b/kcachegrind/kcachegrind/hi32-app-kcachegrind.png new file mode 100644 index 00000000..bd41daec Binary files /dev/null and b/kcachegrind/kcachegrind/hi32-app-kcachegrind.png differ diff --git a/kcachegrind/kcachegrind/hi48-app-kcachegrind.png b/kcachegrind/kcachegrind/hi48-app-kcachegrind.png new file mode 100644 index 00000000..58c2efdb Binary files /dev/null and b/kcachegrind/kcachegrind/hi48-app-kcachegrind.png differ diff --git a/kcachegrind/kcachegrind/instritem.cpp b/kcachegrind/kcachegrind/instritem.cpp new file mode 100644 index 00000000..85705f64 --- /dev/null +++ b/kcachegrind/kcachegrind/instritem.cpp @@ -0,0 +1,469 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Items of instruction view. + */ + +#include +#include + +#include +#include +#include +#include + +#include "configuration.h" +#include "listutils.h" +#include "instritem.h" +#include "instrview.h" + + +// InstrItem + +// for messages +InstrItem::InstrItem(InstrView* iv, QListView* parent, + Addr addr, const QString& msg) + : QListViewItem(parent) +{ + _view = iv; + _addr = addr; + _instr = 0; + _instrCall = 0; + _instrJump = 0; + _inside = false; + + setText(0, addr.pretty()); + setText(6, msg); + + updateGroup(); + updateCost(); +} + +// for code lines +InstrItem::InstrItem(InstrView* iv, QListView* parent, + Addr addr, bool inside, + const QString& code, const QString& cmd, + const QString& args, TraceInstr* instr) + : QListViewItem(parent) +{ + _view = iv; + _addr = addr; + _instr = instr; + _instrCall = 0; + _instrJump = 0; + _inside = inside; + + if (args == "...") + setText(0, args); + else + setText(0, addr.pretty()); + setText(4, code); + setText(5, cmd); + setText(6, args); + + TraceLine* l; + if (instr && (l = instr->line())) + setText(7, l->name()); + + updateGroup(); + updateCost(); +} + +// for call lines +InstrItem::InstrItem(InstrView* iv, QListViewItem* parent, Addr addr, + TraceInstr* instr, TraceInstrCall* instrCall) + : QListViewItem(parent) +{ + _view = iv; + _addr = addr; + _instr = instr; + _instrCall = instrCall; + _instrJump = 0; + _inside = true; + + //qDebug("InstrItem: (file %d, line %d) Linecall to %s", + // fileno, lineno, _lineCall->call()->called()->prettyName().ascii()); + + SubCost cc = _instrCall->callCount(); + QString templ = " "; + if (cc==0) + templ += i18n("Active call to '%1'"); + else + templ += i18n("%n call to '%1'", "%n calls to '%1'", cc); + + QString callStr = templ.arg(_instrCall->call()->calledName()); + TraceFunction* calledF = _instrCall->call()->called(); + calledF->addPrettyLocation(callStr); + + setText(6, callStr); + + updateGroup(); + updateCost(); +} + +// for jump lines +InstrItem::InstrItem(InstrView* iv, QListViewItem* parent, Addr addr, + TraceInstr* instr, TraceInstrJump* instrJump) + : QListViewItem(parent) +{ + _view = iv; + _addr = addr; + _inside = true; + _instr = instr; + _instrCall = 0; + _instrJump = instrJump; + + //qDebug("SourceItem: (file %d, line %d) Linecall to %s", + // fileno, lineno, _lineCall->call()->called()->prettyName().ascii()); + + QString jStr; + if (_instrJump->isCondJump()) + jStr = i18n("Jump %1 of %2 times to 0x%3") + .arg(_instrJump->followedCount().pretty()) + .arg(_instrJump->executedCount().pretty()) + .arg(_instrJump->instrTo()->addr().toString()); + else + jStr = i18n("Jump %1 times to 0x%2") + .arg(_instrJump->executedCount().pretty()) + .arg(_instrJump->instrTo()->addr().toString()); + + setText(6, jStr); + + updateGroup(); + updateCost(); +} + + +void InstrItem::updateGroup() +{ + if (!_instrCall) return; + + TraceFunction* f = _instrCall->call()->called(); + QColor c = Configuration::functionColor(_view->groupType(), f); + setPixmap(6, colorPixmap(10, 10, c)); +} + +void InstrItem::updateCost() +{ + _pure = SubCost(0); + _pure2 = SubCost(0); + + if (!_instr) return; + if (_instrJump) return; + + TraceCost* instrCost = _instrCall ? + (TraceCost*)_instrCall : (TraceCost*)_instr; + + // don't show any cost inside of cycles + if (_instrCall && + ((_instrCall->call()->inCycle()>0) || + (_instrCall->call()->isRecursion()>0))) { + QString str; + QPixmap p; + + QString icon = "undo"; + KIconLoader* loader = KApplication::kApplication()->iconLoader(); + p= loader->loadIcon(icon, KIcon::Small, 0, + KIcon::DefaultState, 0, true); + if (p.isNull()) + str = i18n("(cycle)"); + + setText(1, str); + setPixmap(1, p); + setText(2, str); + setPixmap(2, p); + return; + } + + TraceCost* totalCost; + if (Configuration::showExpanded()) + totalCost = _instr->function()->inclusive(); + else + totalCost = _instr->function()->data(); + + TraceCostType *ct = _view->costType(); + _pure = ct ? instrCost->subCost(ct) : SubCost(0); + if (_pure == 0) { + setText(1, QString::null); + setPixmap(1, QPixmap()); + } + else { + double total = totalCost->subCost(ct); + double pure = 100.0 * _pure / total; + + if (Configuration::showPercentage()) + setText(1, QString("%1") + .arg(pure, 0, 'f', Configuration::percentPrecision())); + else + setText(1, _pure.pretty()); + + setPixmap(1, costPixmap(ct, instrCost, total, false)); + } + + TraceCostType *ct2 = _view->costType2(); + _pure2 = ct2 ? instrCost->subCost(ct2) : SubCost(0); + if (_pure2 == 0) { + setText(2, QString::null); + setPixmap(2, QPixmap()); + } + else { + double total = totalCost->subCost(ct2); + double pure = 100.0 * _pure2 / total; + + if (Configuration::showPercentage()) + setText(2, QString("%1") + .arg(pure, 0, 'f', Configuration::percentPrecision())); + else + setText(2, _pure2.pretty()); + + setPixmap(2, costPixmap(ct2, instrCost, total, false)); + } +} + + +int InstrItem::compare(QListViewItem * i, int col, bool ascending ) const +{ + const InstrItem* ii1 = this; + const InstrItem* ii2 = (InstrItem*) i; + + // we always want descending order + if (((col>0) && ascending) || + ((col==0) && !ascending) ) { + ii1 = ii2; + ii2 = this; + } + + if (col==1) { + if (ii1->_pure < ii2->_pure) return -1; + if (ii1->_pure > ii2->_pure) return 1; + return 0; + } + if (col==2) { + if (ii1->_pure2 < ii2->_pure2) return -1; + if (ii1->_pure2 > ii2->_pure2) return 1; + return 0; + } + if (col==0) { + if (ii1->_addr < ii2->_addr) return -1; + if (ii1->_addr > ii2->_addr) return 1; + + // Same address: code gets above calls/jumps + if (!ii1->_instrCall && !ii1->_instrJump) return -1; + if (!ii2->_instrCall && !ii2->_instrJump) return 1; + + // calls above jumps + if (ii1->_instrCall && !ii2->_instrCall) return -1; + if (ii2->_instrCall && !ii1->_instrCall) return 1; + + if (ii1->_instrCall && ii2->_instrCall) { + // Two calls: desending sort according costs + if (ii1->_pure < ii2->_pure) return 1; + if (ii1->_pure > ii2->_pure) return -1; + + // Two calls: sort according function names + TraceFunction* f1 = ii1->_instrCall->call()->called(); + TraceFunction* f2 = ii2->_instrCall->call()->called(); + if (f1->prettyName() > f2->prettyName()) return 1; + return -1; + } + + // Two jumps: descending sort according target address + if (ii1->_instrJump->instrTo()->addr() < + ii2->_instrJump->instrTo()->addr()) + return -1; + if (ii1->_instrJump->instrTo()->addr() > + ii2->_instrJump->instrTo()->addr()) + return 1; + return 0; + + } + return QListViewItem::compare(i, col, ascending); +} + +void InstrItem::paintCell( QPainter *p, const QColorGroup &cg, + int column, int width, int alignment ) +{ + QColorGroup _cg( cg ); + + if ( !_inside || ((column==1) || column==2)) + _cg.setColor( QColorGroup::Base, cg.button() ); + else if ((_instrCall || _instrJump) && column>2) + _cg.setColor( QColorGroup::Base, cg.midlight() ); + + if (column == 3) + paintArrows(p, _cg, width); + else + QListViewItem::paintCell( p, _cg, column, width, alignment ); +} + +void InstrItem::setJumpArray(const QMemArray& a) +{ + _jump.duplicate(a); +} + +void InstrItem::paintArrows(QPainter *p, const QColorGroup &cg, int width) +{ + QListView *lv = listView(); + if ( !lv ) return; + InstrView* iv = (InstrView*) lv; + + const BackgroundMode bgmode = lv->viewport()->backgroundMode(); + const QColorGroup::ColorRole crole + = QPalette::backgroundRoleFromMode( bgmode ); + if ( cg.brush( crole ) != lv->colorGroup().brush( crole ) ) + p->fillRect( 0, 0, width, height(), cg.brush( crole ) ); + else + iv->paintEmptyArea( p, QRect( 0, 0, width, height() ) ); + + if ( isSelected() && lv->allColumnsShowFocus() ) + p->fillRect( 0, 0, width, height(), cg.brush( QColorGroup::Highlight ) ); + + int marg = lv->itemMargin(); + int yy = height()/2, y1, y2; + QColor c; + + int start = -1, end = -1; + + // draw line borders, detect start/stop of a line + for(int i=0;i< (int)_jump.size();i++) { + if (_jump[i] == 0) continue; + + y1 = 0; + y2 = height(); + if ((_instrJump == _jump[i]) && + (_jump[i]->instrFrom()->addr() == _addr)) { + + //kdDebug() << "InstrItem " << _addr.toString() << ": start " << i << endl; + if (start<0) start = i; + if (_jump[i]->instrTo()->addr() <= _addr) + y2 = yy; + else + y1 = yy; + } + else if (!_instrJump && !_instrCall && + (_jump[i]->instrTo()->addr() == _addr)) { + + //kdDebug() << "InstrItem " << _addr.toString() << ": end " << i << endl; + if (end<0) end = i; + if (_jump[i]->instrFrom()->addr() < _addr) + y2 = yy; + else + y1 = yy; + } + + c = _jump[i]->isCondJump() ? red : blue; +#if 0 + if (_jump[i] == ((TraceItemView*)_view)->selectedItem()) { + p->fillRect( marg + 6*i-2, (y1==0) ? y1: y1-2, + 8, (y2-y1==height())? y2:y2+2, + cg.brush( QColorGroup::Highlight ) ); + c = lv->colorGroup().highlightedText(); + } +#endif + p->fillRect( marg + 6*i, y1, 4, y2, c); + p->setPen(c.light()); + p->drawLine( marg + 6*i, y1, marg + 6*i, y2); + p->setPen(c.dark()); + p->drawLine( marg + 6*i +3, y1, marg + 6*i +3, y2); + } + + // draw start/stop horizontal line + int x, y = yy-2, w, h = 4; + if (start >= 0) { +#if 0 + if (_jump[start] == ((TraceItemView*)_view)->selectedItem()) { + c = lv->colorGroup().highlightedText(); + } +#endif + c = _jump[start]->isCondJump() ? red : blue; + x = marg + 6*start; + w = 6*(iv->arrowLevels() - start) + 10; + p->fillRect( x, y, w, h, c); + p->setPen(c.light()); + p->drawLine(x, y, x+w-1, y); + p->drawLine(x, y, x, y+h-1); + p->setPen(c.dark()); + p->drawLine(x+w-1, y, x+w-1, y+h-1); + p->drawLine(x+1, y+h-1, x+w-1, y+h-1); + } + if (end >= 0) { + c = _jump[end]->isCondJump() ? red : blue; + x = marg + 6*end; + w = 6*(iv->arrowLevels() - end) + 10; + + QPointArray a; + a.putPoints(0, 7, x, y+h, + x,y, x+w-8, y, x+w-8, y-2, + x+w, yy, + x+w-8, y+h+2, x+w-8, y+h); + p->setBrush(c); + p->drawConvexPolygon(a); + + p->setPen(c.light()); + p->drawPolyline(a, 0, 5); + p->setPen(c.dark()); + p->drawPolyline(a, 4, 2); + p->setPen(c.light()); + p->drawPolyline(a, 5, 2); + p->setPen(c.dark()); + p->drawPolyline(a, 6, 2); + } + + // draw inner vertical line for start/stop + // this overwrites borders of horizontal line + for(int i=0;i< (int)_jump.size();i++) { + if (_jump[i] == 0) continue; + + c = _jump[i]->isCondJump() ? red : blue; + + if (_jump[i]->instrFrom()->addr() == _addr) { + bool drawUp = true; + if (_jump[i]->instrTo()->addr() == _addr) + if (start<0) drawUp=false; + if (_jump[i]->instrTo()->addr() > _addr) drawUp=false; + if (drawUp) + p->fillRect( marg + 6*i +1, 0, 2, yy, c); + else + p->fillRect( marg + 6*i +1, yy, 2, height()-yy, c); + } + else if (_jump[i]->instrTo()->addr() == _addr) { + if (end<0) end = i; + if (_jump[i]->instrFrom()->addr() < _addr) + p->fillRect( marg + 6*i +1, 0, 2, yy, c); + else + p->fillRect( marg + 6*i +1, yy, 2, height()-yy, c); + } + } + +} + +int InstrItem::width( const QFontMetrics& fm, + const QListView* lv, int c ) const +{ + if (c != 3) return QListViewItem::width(fm, lv, c); + + InstrView* iv = (InstrView*) lv; + int levels = iv->arrowLevels(); + + if (levels == 0) return 0; + + // 10 pixels for the arrow + return 10 + 6*levels + lv->itemMargin() * 2; +} + diff --git a/kcachegrind/kcachegrind/instritem.h b/kcachegrind/kcachegrind/instritem.h new file mode 100644 index 00000000..50cc26e1 --- /dev/null +++ b/kcachegrind/kcachegrind/instritem.h @@ -0,0 +1,86 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Items of instruction view. + */ + +#ifndef INSTRITEM_H +#define INSTRITEM_H + +#include +#include "tracedata.h" + +class InstrView; + +class InstrItem: public QListViewItem +{ + +public: + // for messages + InstrItem(InstrView* iv, QListView* parent, + Addr addr, const QString&); + + // for instruction lines + InstrItem(InstrView* iv, QListView* parent, + Addr addr, bool inside, + const QString&, const QString&, const QString&, + TraceInstr* instr); + + // for call instr + InstrItem(InstrView* iv, QListViewItem* parent, Addr addr, + TraceInstr* instr, TraceInstrCall* instrCall); + + // for jump lines + InstrItem(InstrView* iv, QListViewItem* parent, Addr addr, + TraceInstr* instr, TraceInstrJump* instrJump); + + Addr addr() const { return _addr; } + TraceInstr* instr() const { return _instr; } + TraceInstrCall* instrCall() const { return _instrCall; } + TraceInstrJump* instrJump() const { return _instrJump; } + + int compare(QListViewItem * i, int col, bool ascending ) const; + + void paintCell(QPainter *p, const QColorGroup &cg, + int column, int width, int alignment ); + int width( const QFontMetrics& fm, + const QListView* lv, int c ) const; + + void updateGroup(); + void updateCost(); + + // arrow lines + void setJumpArray(const QMemArray& a); + +protected: + void paintArrows(QPainter *p, const QColorGroup &cg, int width); + QMemArray _jump; + +private: + InstrView* _view; + SubCost _pure, _pure2; + Addr _addr; + TraceInstr* _instr; + TraceInstrJump* _instrJump; + TraceInstrCall* _instrCall; + bool _inside; +}; + + +#endif diff --git a/kcachegrind/kcachegrind/instrview.cpp b/kcachegrind/kcachegrind/instrview.cpp new file mode 100644 index 00000000..7bb8c2c1 --- /dev/null +++ b/kcachegrind/kcachegrind/instrview.cpp @@ -0,0 +1,949 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Instruction View + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "configuration.h" +#include "instritem.h" +#include "instrview.h" + +// InstrView defaults + +#define DEFAULT_SHOWHEXCODE true + + +// Helpers for parsing output of 'objdump' + +static Addr parseAddr(char* buf) +{ + Addr addr; + uint pos = 0; + + // check for instruction line: * ":" * + while(buf[pos]==' ' || buf[pos]=='\t') pos++; + + int digits = addr.set(buf + pos); + if ((digits==0) || (buf[pos+digits] != ':')) return Addr(0); + + return addr; +} + + +static bool parseLine(char* buf, Addr& addr, + uint& pos1, uint& pos2, uint& pos3) +{ + // check for instruction line: * ":" * + + pos1 = 0; + while(buf[pos1]==' ' || buf[pos1]=='\t') pos1++; + + int digits = addr.set(buf + pos1); + pos1 += digits; + if ((digits==0) || (buf[pos1] != ':')) return false; + + // further parsing of objdump output... + pos1++; + while(buf[pos1]==' ' || buf[pos1]=='\t') pos1++; + + // skip code, pattern "xx "* + pos2 = pos1; + while(1) { + if (! ((buf[pos2]>='0' && buf[pos2]<='9') || + (buf[pos2]>='a' && buf[pos2]<='f')) ) break; + if (! ((buf[pos2+1]>='0' && buf[pos2+1]<='9') || + (buf[pos2+1]>='a' && buf[pos2+1]<='f')) ) break; + if (buf[pos2+2] != ' ') break; + pos2 += 3; + } + buf[pos2-1]=0; + while(buf[pos2]==' '|| buf[pos2]=='\t') pos2++; + + // skip mnemonic + pos3 = pos2; + while(buf[pos3] && buf[pos3]!=' ' && buf[pos3]!='\t') pos3++; + if (buf[pos3] != 0) { + buf[pos3] = 0; + pos3++; + while(buf[pos3]==' '|| buf[pos3]=='\t') pos3++; + } + + // maximal 50 chars + if (strlen(buf+pos2) > 50) + strcpy(buf+pos2+47, "..."); + + if (0) qDebug("For 0x%s: Code '%s', Mnc '%s', Args '%s'", + addr.toString().ascii(), buf+pos1, buf+pos2, buf+pos3); + + return true; +} + + + + +// +// InstrView +// + + +InstrView::InstrView(TraceItemView* parentView, + QWidget* parent, const char* name) + : QListView(parent, name), TraceItemView(parentView) +{ + _showHexCode = DEFAULT_SHOWHEXCODE; + _lastHexCodeWidth = 50; + + _inSelectionUpdate = false; + _arrowLevels = 0; + _lowList.setSortLow(true); + _highList.setSortLow(false); + + addColumn( i18n( "#" ) ); + addColumn( i18n( "Cost" ) ); + addColumn( i18n( "Cost 2" ) ); + addColumn( "" ); + addColumn( i18n( "Hex" ) ); + addColumn( "" ); // Instruction + addColumn( i18n( "Assembler" ) ); + addColumn( i18n( "Source Position" ) ); + + setAllColumnsShowFocus(true); + setColumnAlignment(1, Qt::AlignRight); + setColumnAlignment(2, Qt::AlignRight); + + connect(this, + SIGNAL(contextMenuRequested(QListViewItem*, const QPoint &, int)), + SLOT(context(QListViewItem*, const QPoint &, int))); + + connect(this, SIGNAL(selectionChanged(QListViewItem*)), + SLOT(selectedSlot(QListViewItem*))); + + connect(this, + SIGNAL(doubleClicked(QListViewItem*)), + SLOT(activatedSlot(QListViewItem*))); + + connect(this, + SIGNAL(returnPressed(QListViewItem*)), + SLOT(activatedSlot(QListViewItem*))); + + QWhatsThis::add( this, whatsThis()); +} + +void InstrView::paintEmptyArea( QPainter * p, const QRect & r) +{ + QListView::paintEmptyArea(p, r); +} + +QString InstrView::whatsThis() const +{ + return i18n( "Annotated Assembler" + "

The annotated assembler list shows the " + "machine code instructions of the current selected " + "function together with (self) cost spent while " + "executing an instruction. If this is a call " + "instruction, lines with details on the " + "call happening are inserted into the source: " + "the cost spent inside of the call, the " + "number of calls happening, and the call destination.

" + "

The disassembler output shown is generated with " + "the 'objdump' utility from the 'binutils' package.

" + "

Select a line with call information to " + "make the destination function of this call current.

"); +} + +void InstrView::context(QListViewItem* i, const QPoint & p, int c) +{ + QPopupMenu popup; + + TraceInstrCall* ic = i ? ((InstrItem*) i)->instrCall() : 0; + TraceInstrJump* ij = i ? ((InstrItem*) i)->instrJump() : 0; + TraceFunction* f = ic ? ic->call()->called() : 0; + TraceInstr* instr = ij ? ij->instrTo() : 0; + + if (f) { + QString name = f->name(); + if ((int)name.length()>Configuration::maxSymbolLength()) + name = name.left(Configuration::maxSymbolLength()) + "..."; + popup.insertItem(i18n("Go to '%1'").arg(name), 93); + popup.insertSeparator(); + } + else if (instr) { + popup.insertItem(i18n("Go to Address %1").arg(instr->name()), 93); + popup.insertSeparator(); + } + + if ((c == 1) || (c == 2)) { + addCostMenu(&popup); + popup.insertSeparator(); + } + addGoMenu(&popup); + + popup.insertSeparator(); + popup.setCheckable(true); + popup.insertItem(i18n("Hex Code"), 94); + if (_showHexCode) popup.setItemChecked(94,true); + + int r = popup.exec(p); + if (r == 93) { + if (f) activated(f); + if (instr) activated(instr); + } + else if (r == 94) { + _showHexCode = !_showHexCode; + // remember width when hiding + if (!_showHexCode) + _lastHexCodeWidth = columnWidth(4); + setColumnWidths(); + } +} + + +void InstrView::selectedSlot(QListViewItem * i) +{ + if (!i) return; + // programatically selected items are not signalled + if (_inSelectionUpdate) return; + + TraceInstrCall* ic = ((InstrItem*) i)->instrCall(); + TraceInstrJump* ij = ((InstrItem*) i)->instrJump(); + + if (!ic && !ij) { + TraceInstr* instr = ((InstrItem*) i)->instr(); + if (instr) { + _selectedItem = instr; + selected(instr); + } + return; + } + + if (ic) { + _selectedItem = ic; + selected(ic); + } + else if (ij) { + _selectedItem = ij; + selected(ij); + } +} + +void InstrView::activatedSlot(QListViewItem * i) +{ + if (!i) return; + TraceInstrCall* ic = ((InstrItem*) i)->instrCall(); + TraceInstrJump* ij = ((InstrItem*) i)->instrJump(); + + if (!ic && !ij) { + TraceInstr* instr = ((InstrItem*) i)->instr(); + if (instr) activated(instr); + return; + } + + if (ic) { + TraceFunction* f = ic->call()->called(); + if (f) activated(f); + } + else if (ij) { + TraceInstr* instr = ij->instrTo(); + if (instr) activated(instr); + } +} + + +TraceItem* InstrView::canShow(TraceItem* i) +{ + TraceItem::CostType t = i ? i->type() : TraceItem::NoCostType; + TraceFunction* f = 0; + + switch(t) { + case TraceItem::Function: + f = (TraceFunction*) i; + break; + + case TraceItem::Instr: + f = ((TraceInstr*)i)->function(); + select(i); + break; + + case TraceItem::Line: + f = ((TraceLine*)i)->functionSource()->function(); + select(i); + break; + + default: + break; + } + + return f; +} + + +void InstrView::doUpdate(int changeType) +{ + // Special case ? + if (changeType == selectedItemChanged) { + + if (!_selectedItem) { + clearSelection(); + return; + } + + InstrItem *ii = (InstrItem*)QListView::selectedItem(); + if (ii) { + if ((ii->instr() == _selectedItem) || + (ii->instr() && (ii->instr()->line() == _selectedItem))) return; + } + + QListViewItem *item, *item2; + for (item = firstChild();item;item = item->nextSibling()) { + ii = (InstrItem*)item; + if ((ii->instr() == _selectedItem) || + (ii->instr() && (ii->instr()->line() == _selectedItem))) { + ensureItemVisible(item); + _inSelectionUpdate = true; + setCurrentItem(item); + _inSelectionUpdate = false; + break; + } + item2 = item->firstChild(); + for (;item2;item2 = item2->nextSibling()) { + ii = (InstrItem*)item2; + if (!ii->instrCall()) continue; + if (ii->instrCall()->call()->called() == _selectedItem) { + ensureItemVisible(item2); + _inSelectionUpdate = true; + setCurrentItem(item2); + _inSelectionUpdate = false; + break; + } + } + if (item2) break; + } + return; + } + + if (changeType == groupTypeChanged) { + QListViewItem *item, *item2; + for (item = firstChild();item;item = item->nextSibling()) + for (item2 = item->firstChild();item2;item2 = item2->nextSibling()) + ((InstrItem*)item2)->updateGroup(); + return; + } + + refresh(); +} + +void InstrView::setColumnWidths() +{ + if (_showHexCode) { + setColumnWidthMode(4, QListView::Maximum); + setColumnWidth(4, _lastHexCodeWidth); + } + else { + setColumnWidthMode(4, QListView::Manual); + setColumnWidth(4, 0); + } +} + +void InstrView::refresh() +{ + _arrowLevels = 0; + + // reset to automatic sizing to get column width + setColumnWidthMode(4, QListView::Maximum); + + clear(); + setColumnWidth(0, 20); + setColumnWidth(1, 50); + setColumnWidth(2, _costType2 ? 50:0); + setColumnWidth(3, 0); // arrows, defaults to invisible + setColumnWidth(4, 0); // hex code column + setColumnWidth(5, 20); // command column + setColumnWidth(6, 200); // arg column + setSorting(0); // always reset to address number sort + if (_costType) + setColumnText(1, _costType->name()); + if (_costType2) + setColumnText(2, _costType2->name()); + + if (!_data || !_activeItem) return; + + TraceItem::CostType t = _activeItem->type(); + TraceFunction* f = 0; + if (t == TraceItem::Function) f = (TraceFunction*) _activeItem; + if (t == TraceItem::Instr) { + f = ((TraceInstr*)_activeItem)->function(); + if (!_selectedItem) _selectedItem = _activeItem; + } + if (t == TraceItem::Line) { + f = ((TraceLine*)_activeItem)->functionSource()->function(); + if (!_selectedItem) _selectedItem = _activeItem; + } + + if (!f) return; + + // Allow resizing of column 2 + setColumnWidthMode(2, QListView::Maximum); + + // check for instruction map + TraceInstrMap::Iterator itStart, it, tmpIt, itEnd; + TraceInstrMap* instrMap = f->instrMap(); + if (instrMap) { + it = instrMap->begin(); + itEnd = instrMap->end(); + // get first instruction with cost of selected type + while(it != itEnd) { + if ((*it).hasCost(_costType)) break; + if (_costType2 && (*it).hasCost(_costType2)) break; + ++it; + } + } + if (!instrMap || (it == itEnd)) { + new InstrItem(this, this, 1, + i18n("There is no instruction info in the profile data file.")); + new InstrItem(this, this, 2, + i18n("For the Valgrind Calltree Skin, rerun with option")); + new InstrItem(this, this, 3, i18n(" --dump-instr=yes")); + new InstrItem(this, this, 4, i18n("To see (conditional) jumps, additionally specify")); + new InstrItem(this, this, 5, i18n(" --trace-jump=yes")); + return; + } + + // initialisation for arrow drawing + // create sorted list of jumps (for jump arrows) + _lowList.clear(); + _highList.clear(); + itStart = it; + while(1) { + TraceInstrJumpList jlist = (*it).instrJumps(); + TraceInstrJump* ij; + for (ij=jlist.first();ij;ij=jlist.next()) { + if (ij->executedCount()==0) continue; + _lowList.append(ij); + _highList.append(ij); + } + ++it; + while(it != itEnd) { + if ((*it).hasCost(_costType)) break; + if (_costType2 && (*it).hasCost(_costType2)) break; + ++it; + } + if (it == itEnd) break; + } + _lowList.sort(); + _highList.sort(); + _lowList.first(); // iterators to list start + _highList.first(); + _arrowLevels = 0; + _jump.resize(0); + + + // do multiple calls to 'objdump' if there are large gaps in addresses + it = itStart; + while(1) { + itStart = it; + while(1) { + tmpIt = it; + ++it; + while(it != itEnd) { + if ((*it).hasCost(_costType)) break; + if (_costType2 && (*it).hasCost(_costType2)) break; + ++it; + } + if (it == itEnd) break; + if (!(*it).addr().isInRange( (*tmpIt).addr(),10000) ) break; + } + + // tmpIt is always last instruction with cost + if (!fillInstrRange(f, itStart, ++tmpIt)) break; + if (it == itEnd) break; + } + + _lastHexCodeWidth = columnWidth(4); + setColumnWidths(); + + if (!_costType2) { + setColumnWidthMode(2, QListView::Manual); + setColumnWidth(2, 0); + } +} + +/* This is called after adding instrItems, for each of them in + * address order. _jump is the global array of valid jumps + * for a line while we iterate downwards. + * The existing jumps, sorted in lowList according lower address, + * is iterated in the same way. + */ +void InstrView::updateJumpArray(Addr addr, InstrItem* ii, + bool ignoreFrom, bool ignoreTo) +{ + TraceInstrJump* ij; + Addr lowAddr, highAddr; + int iEnd = -1, iStart = -1; + + if (0) qDebug("updateJumpArray(addr 0x%s, jump to %s)", + addr.toString().ascii(), + ii->instrJump() + ? ii->instrJump()->instrTo()->name().ascii() : "?" ); + + // check for new arrows starting from here downwards + ij=_lowList.current(); + while(ij) { + lowAddr = ij->instrFrom()->addr(); + if (ij->instrTo()->addr() < lowAddr) + lowAddr = ij->instrTo()->addr(); + + if (lowAddr > addr) break; + + // if target is downwards but we draw no source, break + if (ignoreFrom && (lowAddr < ij->instrTo()->addr())) break; + // if source is downward but we draw no target, break + if (ignoreTo && (lowAddr < ij->instrFrom()->addr())) break; + // if this is another jump start, break + if (ii->instrJump() && (ij != ii->instrJump())) break; + +#if 0 + for(iStart=0;iStart<_arrowLevels;iStart++) + if (_jump[iStart] && + (_jump[iStart]->instrTo() == ij->instrTo())) break; +#else + iStart = _arrowLevels; +#endif + + if (iStart==_arrowLevels) { + for(iStart=0;iStart<_arrowLevels;iStart++) + if (_jump[iStart] == 0) break; + if (iStart==_arrowLevels) { + _arrowLevels++; + _jump.resize(_arrowLevels); + } + if (0) qDebug(" new start at %d for %s", iStart, ij->name().ascii()); + _jump[iStart] = ij; + } + ij=_lowList.next(); + } + + ii->setJumpArray(_jump); + + // check for active arrows ending here + ij=_highList.current(); + while(ij) { + highAddr = ij->instrFrom()->addr(); + if (ij->instrTo()->addr() > highAddr) { + highAddr = ij->instrTo()->addr(); + if (ignoreTo) break; + } + else if (ignoreFrom) break; + + if (highAddr > addr) break; + + for(iEnd=0;iEnd<_arrowLevels;iEnd++) + if (_jump[iEnd] == ij) break; + if (iEnd==_arrowLevels) { + kdDebug() << "InstrView: no jump start for end at 0x" + << highAddr.toString() << " ?" << endl; + iEnd = -1; + } + + if (0 && (iEnd>=0)) + qDebug(" end %d (%s to %s)", + iEnd, + _jump[iEnd]->instrFrom()->name().ascii(), + _jump[iEnd]->instrTo()->name().ascii()); + + if (0 && ij) qDebug("next end: %s to %s", + ij->instrFrom()->name().ascii(), + ij->instrTo()->name().ascii()); + + ij=_highList.next(); + if (highAddr > addr) + break; + else { + if (iEnd>=0) _jump[iEnd] = 0; + iEnd = -1; + } + } + if (iEnd>=0) _jump[iEnd] = 0; +} + + + +/** + * Fill up with instructions from cost range [it;itEnd[ + */ +bool InstrView::fillInstrRange(TraceFunction* function, + TraceInstrMap::Iterator it, + TraceInstrMap::Iterator itEnd) +{ + Addr costAddr, nextCostAddr, objAddr, addr; + Addr dumpStartAddr, dumpEndAddr; + TraceInstrMap::Iterator costIt; + + // shouldn't happen + if (it == itEnd) return false; + + // calculate address range for call to objdump + TraceInstrMap::Iterator tmpIt = itEnd; + --tmpIt; + nextCostAddr = (*it).addr(); + dumpStartAddr = (nextCostAddr<20) ? Addr(0) : nextCostAddr -20; + dumpEndAddr = (*tmpIt).addr() +20; + + // generate command + QString popencmd, objfile; + objfile = function->object()->name(); + objfile = objfile.replace(QRegExp("[\"']"), ""); // security... + popencmd = QString("objdump -C -d " + "--start-address=0x%1 --stop-address=0x%2 \"%3\"") + .arg(dumpStartAddr.toString()).arg(dumpEndAddr.toString()) + .arg(objfile); + if (1) qDebug("Running '%s'...", popencmd.ascii()); + + // and run... + FILE* iFILE = popen(QFile::encodeName( popencmd ), "r"); + if (iFILE == 0) { + new InstrItem(this, this, 1, + i18n("There is an error trying to execute the command")); + new InstrItem(this, this, 2, ""); + new InstrItem(this, this, 3, popencmd); + new InstrItem(this, this, 4, ""); + new InstrItem(this, this, 5, + i18n("Check that you have installed 'objdump'.")); + new InstrItem(this, this, 6, + i18n("This utility can be found in the 'binutils' package.")); + return false; + } + QFile file; + file.open(IO_ReadOnly, iFILE); + +#define BUF_SIZE 256 + + char buf[BUF_SIZE]; + bool inside = false, skipLineWritten = true; + int readBytes = -1; + int objdumpLineno = 0, dumpedLines = 0, noAssLines = 0; + SubCost most = 0; + TraceInstr* currInstr; + InstrItem *ii, *ii2, *item = 0, *first = 0, *selected = 0; + QString code, cmd, args; + bool needObjAddr = true, needCostAddr = true; + + costAddr = 0; + objAddr = 0; + + while (1) { + + if (needObjAddr) { + needObjAddr = false; + + // read next objdump line + while (1) { + readBytes=file.readLine(buf, BUF_SIZE); + if (readBytes<=0) { + objAddr = 0; + break; + } + + objdumpLineno++; + if (readBytes == BUF_SIZE) { + qDebug("ERROR: Line %d of '%s' too long\n", + objdumpLineno, popencmd.ascii()); + } + else if ((readBytes>0) && (buf[readBytes-1] == '\n')) + buf[readBytes-1] = 0; + + objAddr = parseAddr(buf); + if ((objAddrdumpEndAddr)) + objAddr = 0; + if (objAddr != 0) break; + } + + if (0) kdDebug() << "Got ObjAddr: 0x" << objAddr.toString() << endl; + } + + // try to keep objAddr in [costAddr;nextCostAddr] + if (needCostAddr && + (nextCostAddr > 0) && + ((objAddr == Addr(0)) || (objAddr >= nextCostAddr)) ) { + needCostAddr = false; + + costIt = it; + ++it; + while(it != itEnd) { + if ((*it).hasCost(_costType)) break; + if (_costType2 && (*it).hasCost(_costType2)) break; + ++it; + } + costAddr = nextCostAddr; + nextCostAddr = (it == itEnd) ? Addr(0) : (*it).addr(); + + if (0) kdDebug() << "Got nextCostAddr: 0x" << nextCostAddr.toString() + << ", costAddr 0x" << costAddr.toString() << endl; + } + + // if we have no more address from objdump, stop + if (objAddr == 0) break; + + if ((nextCostAddr==0) || (costAddr == 0) || + (objAddr < nextCostAddr)) { + // next line is objAddr + + uint pos1, pos2, pos3; + + // this sets addr + parseLine(buf, addr, pos1, pos2, pos3); + code = QString(buf + pos1); + cmd = QString(buf + pos2); + args = QString(buf + pos3); + + if (costAddr == objAddr) { + currInstr = &(*costIt); + needCostAddr = true; + } + else + currInstr = 0; + + needObjAddr = true; + + if (0) kdDebug() << "Dump Obj Addr: 0x" << addr.toString() + << " [" << cmd << " " << args << "], cost (0x" + << costAddr.toString() << ", next 0x" + << nextCostAddr.toString() << ")" << endl; + } + else { + addr = costAddr; + code = cmd = QString(); + args = i18n("(No Assembler)"); + + currInstr = &(*costIt); + needCostAddr = true; + + noAssLines++; + if (0) kdDebug() << "Dump Cost Addr: 0x" << addr.toString() + << " (no ass), objAddr 0x" << objAddr.toString() << endl; + } + + // update inside + if (!inside) { + if (currInstr) inside = true; + } + else { + if (0) kdDebug() << "Check if 0x" << addr.toString() << " is in ]0x" + << costAddr.toString() << ",0x" + << (nextCostAddr - 3*Configuration::noCostInside()).toString() + << "[" << endl; + + // Suppose a average instruction len of 3 bytes + if ( (addr > costAddr) && + ((nextCostAddr==0) || + (addr < nextCostAddr - 3*Configuration::noCostInside()) )) + inside = false; + } + + int context = Configuration::context(); + + if ( ((costAddr==0) || (addr > costAddr + 3*context)) && + ((nextCostAddr==0) || (addr < nextCostAddr - 3*context)) ) { + + // the very last skipLine can be ommitted + if ((it == itEnd) && + (itEnd == function->instrMap()->end())) skipLineWritten=true; + + if (!skipLineWritten) { + skipLineWritten = true; + // a "skipping" line: print "..." instead of a line number + code = cmd = QString(); + args = QString("..."); + } + else + continue; + } + else + skipLineWritten = false; + + + ii = new InstrItem(this, this, addr, inside, + code, cmd, args, currInstr); + dumpedLines++; + if (0) kdDebug() << "Dumped 0x" << addr.toString() << " " + << (inside ? "Inside " : "Outside") + << (currInstr ? "Cost" : "") << endl; + + // no calls/jumps if we have no cost for this line + if (!currInstr) continue; + + if (!selected && + (currInstr == _selectedItem) || + (currInstr->line() == _selectedItem)) selected = ii; + + if (!first) first = ii; + + if (currInstr->subCost(_costType) > most) { + item = ii; + most = currInstr->subCost(_costType); + } + + ii->setOpen(true); + TraceInstrCallList list = currInstr->instrCalls(); + TraceInstrCall* ic; + for (ic=list.first();ic;ic=list.next()) { + if ((ic->subCost(_costType)==0) && + (ic->subCost(_costType2)==0)) continue; + + if (ic->subCost(_costType) > most) { + item = ii; + most = ic->subCost(_costType); + } + + ii2 = new InstrItem(this, ii, addr, currInstr, ic); + + if (!selected && (ic->call()->called() == _selectedItem)) + selected = ii2; + } + + TraceInstrJumpList jlist = currInstr->instrJumps(); + TraceInstrJump* ij; + for (ij=jlist.first();ij;ij=jlist.next()) { + if (ij->executedCount()==0) continue; + + new InstrItem(this, ii, addr, currInstr, ij); + } + } + + if (selected) item = selected; + if (item) first = item; + if (first) { + ensureItemVisible(first); + _inSelectionUpdate = true; + setCurrentItem(first); + _inSelectionUpdate = false; + } + + file.close(); + pclose(iFILE); + + // for arrows: go down the list according to list sorting + sort(); + QListViewItem *item1, *item2; + for (item1=firstChild();item1;item1 = item1->nextSibling()) { + ii = (InstrItem*)item1; + updateJumpArray(ii->addr(), ii, true, false); + + for (item2=item1->firstChild();item2;item2 = item2->nextSibling()) { + ii2 = (InstrItem*)item2; + if (ii2->instrJump()) + updateJumpArray(ii->addr(), ii2, false, true); + else + ii2->setJumpArray(_jump); + } + } + + if (arrowLevels()) + setColumnWidth(3, 10 + 6*arrowLevels() + itemMargin() * 2); + else + setColumnWidth(3, 0); + + + if (noAssLines > 1) { + // trace cost not machting code + + new InstrItem(this, this, 1, + i18n("There is %n cost line without assembler code.", + "There are %n cost lines without assembler code.", noAssLines)); + new InstrItem(this, this, 2, + i18n("This happens because the code of")); + new InstrItem(this, this, 3, QString(" %1").arg(objfile)); + new InstrItem(this, this, 4, + i18n("does not seem to match the profile data file.")); + new InstrItem(this, this, 5, ""); + new InstrItem(this, this, 6, + i18n("Are you using an old profile data file or is the above mentioned")); + new InstrItem(this, this, 7, + i18n("ELF object from an updated installation/another machine?")); + new InstrItem(this, this, 8, ""); + return false; + } + + if (dumpedLines == 0) { + // no matching line read from popen + new InstrItem(this, this, 1, + i18n("There seems to be an error trying to execute the command")); + new InstrItem(this, this, 2, ""); + new InstrItem(this, this, 3, popencmd); + new InstrItem(this, this, 4, ""); + new InstrItem(this, this, 5, + i18n("Check that the ELF object used in the command exists.")); + new InstrItem(this, this, 6, + i18n("Check that you have installed 'objdump'.")); + new InstrItem(this, this, 7, + i18n("This utility can be found in the 'binutils' package.")); + return false; + } + + return true; +} + + +void InstrView::updateInstrItems() +{ + InstrItem* ii; + QListViewItem* item = firstChild(); + for (;item;item = item->nextSibling()) { + ii = (InstrItem*)item; + TraceInstr* instr = ii->instr(); + if (!instr) continue; + + ii->updateCost(); + + QListViewItem *next, *i = ii->firstChild(); + for (;i;i = next) { + next = i->nextSibling(); + ((InstrItem*)i)->updateCost(); + } + } +} + +void InstrView::readViewConfig(KConfig* c, + QString prefix, QString postfix, bool) +{ + KConfigGroup* g = configGroup(c, prefix, postfix); + + if (0) qDebug("InstrView::readViewConfig"); + + _showHexCode = g->readBoolEntry("ShowHexCode", DEFAULT_SHOWHEXCODE); + + delete g; +} + +void InstrView::saveViewConfig(KConfig* c, + QString prefix, QString postfix, bool) +{ + KConfigGroup g(c, (prefix+postfix).ascii()); + + writeConfigEntry(&g, "ShowHexCode", _showHexCode, DEFAULT_SHOWHEXCODE); +} + +#include "instrview.moc" diff --git a/kcachegrind/kcachegrind/instrview.h b/kcachegrind/kcachegrind/instrview.h new file mode 100644 index 00000000..9a2f81cf --- /dev/null +++ b/kcachegrind/kcachegrind/instrview.h @@ -0,0 +1,82 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Instruction View + */ + +#ifndef INSTRVIEW_H +#define INSTRVIEW_H + +#include +#include "traceitemview.h" + +class InstrItem; + +class InstrView : public QListView, public TraceItemView +{ + friend class InstrItem; + + Q_OBJECT + +public: + InstrView(TraceItemView* parentView, + QWidget* parent = 0, const char* name = 0); + + virtual QWidget* widget() { return this; } + QString whatsThis() const; + + void readViewConfig(KConfig*, QString prefix, QString postfix, bool); + void saveViewConfig(KConfig*, QString prefix, QString postfix, bool); + +protected: + int arrowLevels() { return _arrowLevels; } + void paintEmptyArea( QPainter *, const QRect & ); + +private slots: + void context(QListViewItem*, const QPoint &, int); + void selectedSlot(QListViewItem *); + void activatedSlot(QListViewItem *); + +private: + TraceItem* canShow(TraceItem*); + void doUpdate(int); + void refresh(); + void setColumnWidths(); + void fillInstr(); + void updateJumpArray(Addr,InstrItem*,bool,bool); + bool fillInstrRange(TraceFunction*, + TraceInstrMap::Iterator,TraceInstrMap::Iterator); + void updateInstrItems(); + + bool _inSelectionUpdate; + + // arrows + int _arrowLevels; + // temporary needed on creation... + QMemArray _jump; + TraceInstrJumpList _lowList, _highList; + + // remember width of hex code column if hidden + int _lastHexCodeWidth; + + // widget options + bool _showHexCode; +}; + +#endif diff --git a/kcachegrind/kcachegrind/kcachegrind.desktop b/kcachegrind/kcachegrind/kcachegrind.desktop new file mode 100644 index 00000000..84088653 --- /dev/null +++ b/kcachegrind/kcachegrind/kcachegrind.desktop @@ -0,0 +1,103 @@ +# KDE Config File +[Desktop Entry] +Type=Application +Exec=kcachegrind -caption "%c" %i %m %u +MimeType=application/x-kcachegrind; +Icon=kcachegrind +DocPath=kcachegrind/index.html +Terminal=false +Name=KCachegrind +Name[hi]=के-केश-ग्रिंड +Name[sv]=Kcachegrind +Name[ta]= இடைமாற்றகட்டம் +GenericName=Profiler Frontend +GenericName[bs]=Profiler frontend +GenericName[ca]=Interfície de Profiler +GenericName[cs]=Rozhraní pro profilaci +GenericName[cy]=Blaen-wyneb Proffilydd +GenericName[da]=Grænseflade til profilering +GenericName[de]=Profiler Oberfläche +GenericName[el]=Πρόγραμμα προφίλ +GenericName[eo]=Fasado de Profililo +GenericName[es]=Interfaz para Profiler +GenericName[et]=Profileerimisrakendus +GenericName[eu]=Profilatzailearen interfazea +GenericName[fa]=پایانۀ گزارش‌گیر +GenericName[fi]=Profiloijan käyttöliittymä +GenericName[fr]=Interface de profilage +GenericName[ga]=Comhéadan ar Phróifíleoir +GenericName[gl]=Interface para o profiler +GenericName[hi]=प्रोफ़ाइलर फ्रन्टएण्ड +GenericName[hu]=Profilozó +GenericName[is]=Myndrænt viðmót á afkastakönnuð +GenericName[it]=Interfaccia a profiler +GenericName[ja]=プロファイラフロントエンド +GenericName[ka]=პროფილერის Frontend +GenericName[kk]=Профильдеткіштің интерфейсі +GenericName[lt]=Profiliuoklio naudotojo sąsaja +GenericName[nb]=Grensesnitt for profilvisning +GenericName[nds]=Profiler-Böversiet +GenericName[ne]=प्रोफाइलर फ्रन्टइन्ड +GenericName[nl]=Profiler-hulpprogramma +GenericName[nn]=Grensesnitt for profilvising +GenericName[pa]=ਪਰੋਫਾਇਲਰ ਮੁੱਖ ਭੂਮੀ +GenericName[pl]=Interfejs do profilera +GenericName[pt]=Interface de Profiler +GenericName[pt_BR]=Interface para o Profiler +GenericName[ru]=Профилировщик +GenericName[sk]=Rozhranie pre profiler +GenericName[sl]=Vmesnik profilnika +GenericName[sr]=Графички интерфејс за профајлер +GenericName[sr@Latn]=Grafički interfejs za profajler +GenericName[sv]=Profileringsgränssnitt +GenericName[ta]= விவரக்குறிப்பு முன்பகுதி +GenericName[tg]=Интерфейс ба профилкунанда +GenericName[tr]=Profil Önyüzü +GenericName[uk]=Інтерфейс до Profiler +GenericName[zh_CN]=个性数据前端 +GenericName[zh_TW]=分析器前端 +Comment=Visualization of Performance Profiling Data +Comment[bg]=Визуализация на данните за производителност +Comment[bs]=Vizualizacija podataka za profiliranje performansi +Comment[ca]=Visualizació de dades de perfilat de rendiment +Comment[cs]=Vizualizace profilovacích dat výkonu +Comment[da]=Visualisering af profileringsdata +Comment[de]=Visualisierung von Daten des Laufzeitverhaltens eines Programmes +Comment[el]=Αναπαράσταση δεδομένων ταχύτητας προφίλ +Comment[en_GB]=Visualisation of Performance Profiling Data +Comment[es]=Visualización de datos de análisis de rendimiento +Comment[et]=Jõudluse profileerimise andmete visualiseerimise vahend +Comment[eu]=Errendimendu profil datuen bistaratzea +Comment[fa]=تجسم کارایی گزارش داده‌ها +Comment[fi]=Visualisointi tehokkuusprofiloinnin tiedoista +Comment[fr]=Visualisation des données de performance de profilage +Comment[gl]=Visualización dos datos da análise de rendimento +Comment[hi]=परफार्मेस प्रोफाइलिंग डाटा का विजुअलाइज़ेशन +Comment[hu]=Teljesítményprofil-adatok megjelenítése +Comment[is]=Sjónræn framsetning gagna úr afkastakönnun +Comment[it]=Visualizzazione dei dati di profiling delle prestazioni +Comment[ja]=パフォーマンスプロファイルデータを視覚化 +Comment[ka]=წარმადობის მაპროფფილებელი მონაცემების ვიზუალიზაცია +Comment[kk]=Деректерді профильдеудің визуализациясы +Comment[lt]=Veikimo profiliavimo duomenų vizualizacija +Comment[nb]=Vis informasjon om ytelse. +Comment[nds]=Visualiseren vun Programmleisten-Looptietdaten +Comment[ne]=सम्पादन प्रोफाइलिङ डाटाको दृष्टिकरण +Comment[nl]=Visualisatie van Performance Profiling Data +Comment[nn]=Vis informasjon om yting +Comment[pl]=Wizualizacja danych profilowania wydajności +Comment[pt]=Visualização dos Dados de Análise de Performance +Comment[pt_BR]=Visualização de Dados de Perfil de Desempenho +Comment[ru]=Утилита для визуального профилирования приложений +Comment[sk]=Vizualizácia dát o výkone +Comment[sl]=Vizualizacija podatkov profilnih zmogljivosti +Comment[sr]=Визуелизација података о профилисању перформанси +Comment[sr@Latn]=Vizuelizacija podataka o profilisanju performansi +Comment[sv]=Åskådliggörande av profileringsdata för prestanda +Comment[ta]= விவர தகவலை செயல்பாட்டு காட்சியாளிப்பு +Comment[tg]=Утилита барои гузориши профили визуалӣ +Comment[uk]=Візуалізація даних профілювання швидкодії +Comment[zh_CN]=性能个性数据的可视化表现 +Comment[zh_TW]=效能分析資料視覺化 +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Development; diff --git a/kcachegrind/kcachegrind/kcachegrindui.rc b/kcachegrind/kcachegrind/kcachegrindui.rc new file mode 100644 index 00000000..af9b8dcd --- /dev/null +++ b/kcachegrind/kcachegrind/kcachegrindui.rc @@ -0,0 +1,57 @@ + + + + &File + + + + + + &View + + + + + &Layout + + + + + + + + + + + + + + + + + Sidebars + + + + + + + + + Main Toolbar + + + + + + + + + + + + + State Toolbar + + + diff --git a/kcachegrind/kcachegrind/listutils.cpp b/kcachegrind/kcachegrind/listutils.cpp new file mode 100644 index 00000000..7a14e552 --- /dev/null +++ b/kcachegrind/kcachegrind/listutils.cpp @@ -0,0 +1,266 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Some helper functions for QListViewItem derivates + */ + +#include +#include "listutils.h" + +#define COSTPIX_WIDTH 25 + +QPixmap colorPixmap(int w, int h, QColor c) +{ + static QPixmap* pixs[37]; + static QColor cols[37]; + static bool inited = false; + + if (!inited) { + for (int i=0;i<37;i++) pixs[i]=0; + inited = true; + } + int hash = (w+h+c.red()+c.green()+c.blue()) % 37; + if (pixs[hash]) { + if ((pixs[hash]->width() == w) && + (pixs[hash]->height() == h) && + (cols[hash] == c)) + return *pixs[hash]; + + delete pixs[hash]; + } + + + QPixmap* pix = new QPixmap(w, h); + pix->fill(c); + QPainter p(pix); + p.setPen(c.light()); + p.drawLine(0, 0, w-1, 0); + p.drawLine(0, 0, 0, h-1); + p.setPen(c.dark()); + p.drawLine(w-1, 0, w-1, h-1); + p.drawLine(0, h-1, w-1, h-1); + + pixs[hash] = pix; + cols[hash] = c; + return *pix; +} + +/** + * Create a percentage pixmap with a filling rate of p percent (0-100). + * When withFrame==false, the pixmap is truncated to only the filled portion. + */ +QPixmap percentagePixmap(int w, int h, int percent, QColor c, bool framed) +{ + int iw, ix1, ix2, ih, iy1, iy2; + + // inner rectangle to fill with bar + if (framed) { + iw = w-2, ix1 = 1, ix2 = w-2; + ih = h-2, iy1 = 1, iy2 = h-2; + } + else { + iw = w; ix1 = 0; ix2 = w-1; + ih = h; iy1 = 0; iy2 = h-1; + } + + /* Limit bar to 100% */ + int filled = (percent>100) ? iw+1 : iw*percent/100+1; + if (!framed) w=filled-1; + if (w<3) return QPixmap(); + + QPixmap pix(w, h); + pix.fill(Qt::white); + QPainter p(&pix); + p.setPen(Qt::black); + if (framed) + p.drawRect(0, 0, w, h); + + // inside + p.setPen(Qt::NoPen); + p.setBrush(c); + p.drawRect(ix1, iy1, filled-1,ih); + + // frame + ix2 = ix1+filled-2; + p.setPen(c.light()); + p.drawLine(ix1, iy1, ix2, iy1); + p.drawLine(ix1, iy1, ix1, iy2); + p.setPen(c.dark()); + p.drawLine(ix1+1, iy2, ix2, iy2); + p.drawLine(ix2, iy1, ix2, iy2); + + return pix; +} + +inline QColor partitionColor(int d, int max) +{ + return QColor( (720*d/max) % 360, + 255-(128*d/max), 192, QColor::Hsv); +} + + +QPixmap partitionPixmap(int w, int h, + double* hist, QColor* cArray, int maxIndex, bool framed) +{ + int lastPos = 0, nextPos; + double val=0.0, sum=0.0; + int d, dmin=maxIndex, dmax=0; + for (d = 0;d0.0) { + sum += hist[d]; + if (dmin>d) dmin = d; + if (dmax=iw) x2=iw-1; + + // inside + p.setPen(Qt::NoPen); + p.setBrush(c); + p.drawRect(x1, iy1, x2-x1+1, ih); + + // lighter top border + p.setPen(c.light()); + p.drawLine(x1, iy1, x2-1, iy1); + + // when width for last and current distance >2, draw full 3D effect... + if (!leftDrawn) { + p.drawLine(x1, iy1+1, x1, iy2); + leftDrawn = true; + } + + // darker bottom border + p.setPen(c.dark()); + p.drawLine(x1, iy2, x2-1, iy2); + + lastPos = nextPos; + lastDiff = diff; + cLast = c; + d++; + } + + // right border (in last color) + if (x2>0) + p.drawLine(x2, iy1, x2, iy2); + + return pix; +} + + +QPixmap costPixmap(TraceCostType* ct, TraceCost* cost, double total, bool framed) +{ + if (ct->isReal()) { + QColor color = ct->color(); + double p = 100.0 * cost->subCost(ct) / total; + return percentagePixmap(COSTPIX_WIDTH, 10, (int)(p+.5), color, framed); + } + + int maxIndex; + double h[MaxRealIndexValue]; + QColor* cs = ct->mapping()->realColors(); + maxIndex = ct->histCost(cost, total, h); + + if (maxIndex ==0) return QPixmap(); + return partitionPixmap(COSTPIX_WIDTH, 10, h, cs, maxIndex, framed); +} + + + +// HighestCostList + +HighestCostList::HighestCostList() +{ + _maxSize = 0; + _count = 0; + _costType = 0; +} + +void HighestCostList::clear(int maxSize) +{ + _maxSize = maxSize; + _count = 0; + _item.resize(maxSize); + _cost.resize(maxSize); +} + +void HighestCostList::addCost(TraceCost* c, SubCost cost) +{ + int i; + + _count++; + if (_count > _maxSize) { + if (_cost[_maxSize-1] >= cost) return; + i = _maxSize-1; + } + else i = _count-1; + + for(; i>0; i--) { + if (_cost[i-1] >= cost) break; + else { + _cost[i] = _cost[i-1]; + _item[i] = _item[i-1]; + } + } + _cost[i] = cost; + _item[i] = c; +} + + diff --git a/kcachegrind/kcachegrind/listutils.h b/kcachegrind/kcachegrind/listutils.h new file mode 100644 index 00000000..abf7c723 --- /dev/null +++ b/kcachegrind/kcachegrind/listutils.h @@ -0,0 +1,65 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Some helper functions for QListViewItem derivates + */ + +#ifndef LISTUTILS_H +#define LISTUTILS_H + +#include +#include +#include +#include "tracedata.h" + +QString bigNum(SubCost); +QPixmap colorPixmap(int w, int h, QColor c); +QPixmap percentagePixmap(int w, int h, int percent, QColor c, bool framed); +QPixmap partitionPixmap(int w, int h, double* hist, QColor*, + int maxIndex, bool framed); +QPixmap costPixmap(TraceCostType* ct, TraceCost* cost, double total, bool framed); + +/** + * A class to calculate the TraceCost items + * with highest cost. + */ + +class HighestCostList +{ + public: + HighestCostList(); + + void clear(int maxSize); + void addCost(TraceCost*, SubCost); + int count() { return _count; } + int realCount() { return (_count > _maxSize) ? _maxSize:_count; } + int maxSize() { return _maxSize; } + bool hasMore() { return _count > _maxSize; } + TraceCost* operator[] (int i) + { return (i>=0 && i<_count && i<_maxSize) ? _item[i] : 0; } + + private: + TraceCostList _list; + int _maxSize, _count; + TraceCostType* _costType; + QMemArray _item; + QMemArray _cost; +}; + +#endif diff --git a/kcachegrind/kcachegrind/lo16-app-kcachegrind.png b/kcachegrind/kcachegrind/lo16-app-kcachegrind.png new file mode 100644 index 00000000..0985586b Binary files /dev/null and b/kcachegrind/kcachegrind/lo16-app-kcachegrind.png differ diff --git a/kcachegrind/kcachegrind/lo32-app-kcachegrind.png b/kcachegrind/kcachegrind/lo32-app-kcachegrind.png new file mode 100644 index 00000000..12542c8a Binary files /dev/null and b/kcachegrind/kcachegrind/lo32-app-kcachegrind.png differ diff --git a/kcachegrind/kcachegrind/loader.cpp b/kcachegrind/kcachegrind/loader.cpp new file mode 100644 index 00000000..2b373851 --- /dev/null +++ b/kcachegrind/kcachegrind/loader.cpp @@ -0,0 +1,85 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Base class for loaders of profiling data. + */ + +#include "loader.h" + + +/// Loader + +LoaderList Loader::_loaderList; + +Loader::Loader(QString name, QString desc) +{ + _name = name; + _description = desc; +} + +Loader::~Loader() +{} + +bool Loader::canLoadTrace(QFile*) +{ + return false; +} + +bool Loader::loadTrace(TracePart*) +{ + return false; +} + +Loader* Loader::matchingLoader(QFile* file) +{ + Loader* l; + for (l=_loaderList.first(); l; l = _loaderList.next()) + if (l->canLoadTrace(file)) + return l; + + return 0; +} + +Loader* Loader::loader(QString name) +{ + Loader* l; + for (l=_loaderList.first(); l; l = _loaderList.next()) + if (l->name() == name) + return l; + + return 0; +} + +// factories of available loaders +Loader* createCachegrindLoader(); + +void Loader::initLoaders() +{ + _loaderList.append(createCachegrindLoader()); + //_loaderList.append(GProfLoader::createLoader()); +} + +void Loader::deleteLoaders() +{ + _loaderList.setAutoDelete(true); + _loaderList.clear(); +} + + +#include "loader.moc" diff --git a/kcachegrind/kcachegrind/loader.h b/kcachegrind/kcachegrind/loader.h new file mode 100644 index 00000000..70b98c48 --- /dev/null +++ b/kcachegrind/kcachegrind/loader.h @@ -0,0 +1,79 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Base class for loaders of profiling data. + */ + +#ifndef LOADER_H +#define LOADER_H + +#include +#include +#include + +class QFile; +class TraceData; +class TracePart; +class Loader; + + +typedef QPtrList LoaderList; + +/** + * To implement a new loader, inherit from the Loader class + * and implement canLoadTrace(), loadTrace() and if a trace in + * this format can consist out of multiple parts, implement + * isPartOfTrace(), too. + * For registration, put into the static initLoaders() function + * of this base class a _loaderList.append(new MyLoader()). + * + * KCachegrind will use the first matching loader. + */ + +class Loader: public QObject +{ + Q_OBJECT + +public: + Loader(QString name, QString desc); + virtual ~Loader(); + + virtual bool canLoadTrace(QFile* file); + virtual bool loadTrace(TracePart*); + + static Loader* matchingLoader(QFile* file); + static Loader* loader(QString name); + static void initLoaders(); + static void deleteLoaders(); + + QString name() const { return _name; } + QString description() const { return _description; } + +signals: + void updateStatus(QString, int); + +private: + QString _name, _description; + + static LoaderList _loaderList; +}; + + + +#endif diff --git a/kcachegrind/kcachegrind/main.cpp b/kcachegrind/kcachegrind/main.cpp new file mode 100644 index 00000000..299d485a --- /dev/null +++ b/kcachegrind/kcachegrind/main.cpp @@ -0,0 +1,95 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * KCachegrind startup + */ + +// for KCACHEGRIND_VERSION +#include "../version.h" + +#include +#include +#include +#include +#include + +#include "toplevel.h" +#include "tracedata.h" +#include "loader.h" + +static KCmdLineOptions options[] = +{ + { "r ", I18N_NOOP("Run under cachegrind"), 0 }, + { "+[trace]", I18N_NOOP("Show information of this trace"), 0 }, + KCmdLineLastOption // End of options. +}; + +int main( int argc, char ** argv ) +{ + KAboutData aboutData("kcachegrind", + I18N_NOOP("KCachegrind"), + KCACHEGRIND_VERSION, + I18N_NOOP("KDE Frontend for Cachegrind"), + KAboutData::License_GPL, + I18N_NOOP("(C) 2002, 2003, 2004"), 0, + "http://kcachegrind.sf.net"); + aboutData.addAuthor("Josef Weidendorfer", + I18N_NOOP("Author/Maintainer"), + "Josef.Weidendorfer@gmx.de"); + + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineArgs::addCmdLineOptions( options ); + + KApplication a; + TopLevel* t; + Loader::initLoaders(); + + if (a.isRestored()){ + int n = 1; + while (KMainWindow::canBeRestored(n)){ + (new TopLevel())->restore(n); + n++; + } + } + else { + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if (args->count()>0) { + for(int i = 0; i < args->count(); i++) { + t = new TopLevel(); + t->show(); + t->loadDelayed(QFile::decodeName(args->arg(i))); + } + } + else { + // load trace in current dir + t = new TopLevel(); + t->show(); + t->loadDelayed("."); + } + } + + a.connect( &a, SIGNAL( lastWindowClosed() ), &a, SLOT( quit() ) ); + int res = a.exec(); + + // to make leak checking in valgrind happy... + Loader::deleteLoaders(); + TraceItem::cleanup(); + + return res; +} diff --git a/kcachegrind/kcachegrind/multiview.cpp b/kcachegrind/kcachegrind/multiview.cpp new file mode 100644 index 00000000..09dfcd85 --- /dev/null +++ b/kcachegrind/kcachegrind/multiview.cpp @@ -0,0 +1,224 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * MultiView, enclosing multiple TabView's with a user choosable + * active view (i.e. focus), separated by a splitter. + * Selection of the active view is shown in the next to the right view + * (with wrap around). + */ + +#include +#include +#include + +#include "multiview.h" +#include "tabview.h" + +// +// MultiView +// + +MultiView::MultiView(TopLevel* top, QWidget* parent, const char* name) + : QSplitter(parent, name), TraceItemView(0, top) +{ + // default + setOrientation(Qt::Horizontal); + + appendView(); + _active = _views.first(); + _active->setActive(true); +} + +void MultiView::setData(TraceData* d) +{ + TraceItemView::setData(d); + + TabView* tv; + for(tv=_views.first(); tv; tv=_views.next()) + tv->setData(d); +} + +void MultiView::setChildCount(int n) +{ + while(n< (int)_views.count()) removeView(); + while(n> (int)_views.count()) appendView(); +} + +void MultiView::appendView() +{ + int n = _views.count()+1; + + TabView* tv = new TabView(this, this, + QString("TabView-%1").arg(n).ascii()); + connect(tv, SIGNAL(activated(TabView*)), + this, SLOT(tabActivated(TabView*)) ); + _views.append(tv); + tv->show(); + + // set same attributes as in active view + tv->set(0, _data, _costType, _costType2, + _groupType, _partList, _activeItem, 0); + tv->updateView(); + + if (0) kdDebug() << "MultiView::appendView, now " + << _views.count() << endl; +} + +void MultiView::removeView() +{ + if (_views.count()<=1) return; + + TabView* last = _views.last(); + + // if last tab is active, make first active + if (last == _active) { + TabView* newActive = _views.first(); + newActive->setActive(true); + tabActivated(newActive); + } + + _views.removeRef(last); + delete last; + + if (0) kdDebug() << "MultiView::removeView, now " + << _views.count() << endl; +} + + +void MultiView::tabActivated(TabView* newActiveTab) +{ + if (_active == newActiveTab) return; + + if (0) kdDebug() << "MultiView::tabActivated " + << newActiveTab->name() << endl; + + TraceItem* oldActiveItem = 0; + if (_active) { + oldActiveItem = _active->activeItem(); + _active->setActive(false); + } + _active = newActiveTab; + + // make the active item of the new TabView active + if (_active && (oldActiveItem != _active->activeItem())) + TraceItemView::activated(_active->activeItem()); +} + +void MultiView::selected(TraceItemView* sender, TraceItem* i) +{ + if (0) kdDebug() << "MultiView::selected " << i->name() + << ", sender " << sender->widget()->name() << endl; + + // we react only on selection changes of the active TabView + if (sender != (TraceItemView*)_active) return; + + _views.findRef(_active); + TabView* next = _views.next(); + if (!next) next = _views.first(); + + // don't change item of active tab + if (next == _active) return; + + next->activate(i); + next->updateView(); +} + +void MultiView::activated(TraceItemView* sender, TraceItem* i) +{ + if (0) kdDebug() << "MultiView::activated " << i->name() + << ", sender " << sender->widget()->name() << endl; + + // we react only on selection changes of the active TabView + if (sender != (TraceItemView*)_active) return; + + TraceItemView::activated(sender,i); +} + +void MultiView::doUpdate(int changeType) +{ + TabView* tv; + for(tv=_views.first(); tv; tv=_views.next()) { + tv->set(changeType, _data, _costType, _costType2, + _groupType, _partList, + (tv == _active) ? _activeItem : tv->activeItem(), + tv->selectedItem()); + tv->notifyChange(changeType); + if (tv->isViewVisible()) + tv->updateView(); + } +} + + +void MultiView::readViewConfig(KConfig* c, + QString prefix, QString postfix, + bool withOptions) +{ + if (0) qDebug("%s::readConfig(%s%s)", name(), + prefix.ascii(), postfix.ascii()); + + QString active; + KConfigGroup* g = configGroup(c, prefix, postfix); + int n = g->readNumEntry("Panels", 1); + setChildCount(n); + setOrientation( (g->readEntry("Orientation") == QString("Horizontal")) ? + Qt::Horizontal : Qt::Vertical ); + + setSizes(g->readIntListEntry("PanelSizes")); + + active = g->readEntry("ActivePanel", ""); + delete g; + + TabView* tv, *activeTV = 0; + for(tv=_views.first();tv;tv=_views.next()) { + if (tv->name() == active) activeTV=tv; + tv->readViewConfig(c, QString("%1-%2").arg(prefix).arg(tv->name()), + postfix, withOptions); + } + + // activate panel after restoring + if (!activeTV) activeTV = _views.first(); + + if (_active == activeTV) + TraceItemView::activated(_active->activeItem()); + else + activeTV->setActive(true); +} + +void MultiView::saveViewConfig(KConfig* c, + QString prefix, QString postfix, + bool withOptions) +{ + KConfigGroup g(c, (prefix+postfix).ascii()); + + g.writeEntry("Panels", childCount()); + g.writeEntry("Orientation", + (orientation() == Qt::Horizontal) ? + "Horizontal" : "Vertical"); + + g.writeEntry("PanelSizes", sizes()); + g.writeEntry("ActivePanel", _active ? _active->name() : "none"); + + TabView* tv; + for(tv=_views.first();tv;tv=_views.next()) + tv->saveViewConfig(c, QString("%1-%2").arg(prefix).arg(tv->name()), + postfix, withOptions); +} + + +#include "multiview.moc" diff --git a/kcachegrind/kcachegrind/multiview.h b/kcachegrind/kcachegrind/multiview.h new file mode 100644 index 00000000..d4c56b19 --- /dev/null +++ b/kcachegrind/kcachegrind/multiview.h @@ -0,0 +1,66 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * MultiView, enclosing multiple (default: 2) TabView's with a user + * choosable active view (i.e. focus). This is a splitter itself. + * Selection of the active view is shown in the next to the right view + * (with wrap around). + */ + +#ifndef MULTIVIEW_H +#define MULTIVIEW_H + +#include +#include +#include "traceitemview.h" +#include "tabview.h" // because of QPtrList + +class MultiView : public QSplitter, public TraceItemView +{ + Q_OBJECT + +public: + MultiView(TopLevel* top, QWidget* parent = 0, const char* name = 0); + + QWidget* widget() { return this; } + TabView* activeTabView() const { return _active; } + void setData(TraceData*); + + void appendView(); + void removeView(); + void setChildCount(int); + int childCount() { return _views.count(); } + + void selected(TraceItemView*, TraceItem*); + void activated(TraceItemView*, TraceItem*); + + void readViewConfig(KConfig*, QString prefix, QString postfix, bool); + void saveViewConfig(KConfig*, QString prefix, QString postfix, bool); + +public slots: + void tabActivated(TabView*); + + private: + void doUpdate(int); + + TabView* _active; + QPtrList _views; +}; + +#endif diff --git a/kcachegrind/kcachegrind/partgraph.cpp b/kcachegrind/kcachegrind/partgraph.cpp new file mode 100644 index 00000000..f4fa72a3 --- /dev/null +++ b/kcachegrind/kcachegrind/partgraph.cpp @@ -0,0 +1,534 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * TracePart as Nested Area + */ + +#include + +#include "partgraph.h" +#include "configuration.h" +#include "listutils.h" + + +// PartAreaWidget + +PartAreaWidget::PartAreaWidget(QWidget* parent, const char* name) + : TreeMapWidget(new BasePartItem(), parent, name) +{ + _data = 0; + _function = 0; + + _costType = 0; + _groupType = TraceCost::NoCostType; + _visualisation = NoVisualisation; + _zoomFunction = false; + _callLevels = 1; +} + +void PartAreaWidget::setData(TraceData* data) +{ + if (data == _data) return; + + _data = data; + _function = 0; + _hiddenParts.clear(); + + ((BasePartItem*)base())->setData(data); +} + +void PartAreaWidget::changeHidden(const TracePartList& list) +{ + _hiddenParts = list; + base()->refresh(); +} + + +void PartAreaWidget::setCostType(TraceCostType* ct) +{ + _costType = ct; + + // this resizes items + base()->redraw(); +} + +void PartAreaWidget::setVisualisation(VisualisationMode m) +{ + _visualisation = m; + refreshParts(); +} + +void PartAreaWidget::setZoomFunction(bool zoomFunction) +{ + _zoomFunction = zoomFunction; + refreshParts(); +} + +void PartAreaWidget::setCallLevels(int callLevels) +{ + _callLevels = callLevels; + refreshParts(); +} + +void PartAreaWidget::refreshParts() +{ + // rebuild only subparts to keep part selection state + TreeMapItem* i; + TreeMapItemList* l = base()->children(); + if (l) + for (i=l->first();i;i=l->next()) + i->refresh(); + + // but resize part areas + base()->redraw(); +} + + +void PartAreaWidget::setFunction(TraceFunction* f) +{ + _function = f; + + if (_visualisation == PartAreaWidget::Inclusive) + refreshParts(); +} + +void PartAreaWidget::setGroupType(TraceCost::CostType gt) +{ + _groupType = gt; + + // rebuild hierarchy below parts. + // thus, selected parts stay selected + TreeMapItem* i; + TreeMapItemList* l = base()->children(); + if (l) + for (i=l->first();i;i=l->next()) + i->refresh(); + + base()->redraw(); +} + +bool PartAreaWidget::isHidden(TracePart* part) const +{ + return (_hiddenParts.containsRef(part)>0); +} + +QColor PartAreaWidget::groupColor(TraceFunction* f) const +{ + if (!f) + return colorGroup().button(); + + return Configuration::functionColor(_groupType, f); +} + +QString PartAreaWidget::tipString(TreeMapItem* i) const +{ + QString tip, itemTip; + int count = 0; + + //qDebug("PartAreaWidget::tipString for '%s'", i->name().ascii()); + + // first, SubPartItem's + while (i && countrtti() == 3) { + itemTip = i->text(0); + if ((int)itemTip.length()>Configuration::maxSymbolLength()) + itemTip = itemTip.left(Configuration::maxSymbolLength()) + "..."; + + if (!i->text(1).isEmpty()) + itemTip += " (" + i->text(1) + ")"; + + if (!tip.isEmpty()) + itemTip += "\n"; + + tip = itemTip + tip; + i = i->parent(); + count++; + } + + // skip to part + while (i && i->rtti()==3) i = i->parent(); + + if (i && i->rtti()==2) { + itemTip = i18n("Profile Part %1").arg(i->text(0)); + if (!i->text(1).isEmpty()) + itemTip += " (" + i->text(1) + ")"; + + if (!tip.isEmpty()) + itemTip += "\n"; + + tip = itemTip + tip; + } + +// qDebug("PartAreaWidget:: tip %s, itemTip %s", +// tip.ascii(), itemTip.ascii()); + + return tip; +} + + + + + +// BasePartItem + +BasePartItem::BasePartItem() + : TreeMapItem() +{ + _data = 0; + setSorting(-1); +} + +void BasePartItem::setData(TraceData* data) +{ + if (data == _data) return; + + _data = data; + refresh(); +} + +TreeMapItemList* BasePartItem::children() +{ + if (!_data) return _children; + + if (!initialized()) { +// qDebug("Create Parts (%s)", name().ascii()); + + PartAreaWidget* w = (PartAreaWidget*) widget(); + TracePart* part; + TracePartList l = _data->parts(); + for (part=l.first();part;part=l.next()) + if (!w->isHidden(part)) + addItem(new PartItem(part)); + } + + return _children; +} + +QString BasePartItem::text(int textNo) const +{ + if (textNo == 0) { + if (!_data) + return i18n("(no trace)"); + + if (_data->parts().count() == 0) + return i18n("(no part)"); + } + return QString::null; +} + + +QColor BasePartItem::backColor() const +{ + return widget()->colorGroup().base(); +} + +double BasePartItem::value() const +{ + if (!_data) return 0; + + PartAreaWidget* w = (PartAreaWidget*) widget(); + return (double)_data->subCost(w->costType()); +} + + + + + +// PartItem + +PartItem::PartItem(TracePart* p) +{ + _p = p; + _factor=1; +} + +QString PartItem::text(int textNo) const +{ + if (textNo == 0) + return _p->prettyName(); + + if (textNo != 1) + return QString::null; + + TraceCostType* ct; + PartAreaWidget* w = (PartAreaWidget*)widget(); + SubCost v; + + ct = w->costType(); + v = _p->subCost(ct); + + if (Configuration::showPercentage()) { + TraceCost* t = _p->data()->totals(); + double p = 100.0 * v / t->subCost(ct); + return QString("%1 %") + .arg(p, 0, 'f', Configuration::percentPrecision()); + } + return v.pretty(); +} + + +QPixmap PartItem::pixmap(int i) const +{ + if (i != 1) return QPixmap(); + + // Cost pixmap + + TraceCostType* ct = ((PartAreaWidget*)widget())->costType(); + return costPixmap( ct, _p, (double) (_p->data()->totals()->subCost(ct)), false ); +} + + +double PartItem::value() const +{ + PartAreaWidget* w = (PartAreaWidget*)widget(); + TraceCostType* ct = w->costType(); + if ((w->visualisation() == PartAreaWidget::Inclusive) && + w->zoomFunction()) { + + // use value of zoomed function + TraceFunction* f = w->function(); + if (f) { + TracePartFunction* pf = (TracePartFunction*) f->findDepFromPart(_p); + if (pf) + return (double) pf->inclusive()->subCost(ct); + // when function is not available in part, hide part + return 0.0; + } + } + return (double) _p->subCost(ct); +} + +double PartItem::sum() const +{ + PartAreaWidget* w = (PartAreaWidget*)widget(); + if (w->visualisation() == PartAreaWidget::Inclusive) { + double s = value(); + //qDebug("PartItem::sum [part %s]: %d", _p->name().ascii(), s); + return s; + } + return 0.0; +} + +TreeMapItemList* PartItem::children() +{ + if (initialized()) return _children; + + TraceCost* c; +// qDebug("Create Part subitems (%s)", name().ascii()); + + PartAreaWidget* w = (PartAreaWidget*)widget(); + if (w->visualisation() == PartAreaWidget::Inclusive) { + TraceFunction* f = w->function(); + if (f) { + c = f->findDepFromPart(_p); + if (c) addItem(new SubPartItem(c)); + } + + return _children; + } + + + switch( ((PartAreaWidget*)widget())->groupType() ) { + + case TraceCost::Object: + { + TraceObjectMap::Iterator it; + for ( it = _p->data()->objectMap().begin(); + it != _p->data()->objectMap().end(); ++it ) { + c = (*it).findDepFromPart(_p); + if (c) + addItem(new SubPartItem(c)); + } + } + break; + + case TraceCost::Class: + { + TraceClassMap::Iterator it; + for ( it = _p->data()->classMap().begin(); + it != _p->data()->classMap().end(); ++it ) { + c = (*it).findDepFromPart(_p); + if (c) + addItem(new SubPartItem(c)); + } + } + break; + + case TraceCost::File: + { + TraceFileMap::Iterator it; + for ( it = _p->data()->fileMap().begin(); + it != _p->data()->fileMap().end(); ++it ) { + c = (*it).findDepFromPart(_p); + if (c) + addItem(new SubPartItem(c)); + } + } + break; + + case TraceCost::Function: + { + TraceFunctionMap::Iterator it; + for ( it = _p->data()->functionMap().begin(); + it != _p->data()->functionMap().end(); ++it ) { + c = (*it).findDepFromPart(_p); + if (c) + addItem(new SubPartItem(c)); + } + } + break; + + default: + break; + } + + return _children; +} + + +QColor PartItem::backColor() const +{ + PartAreaWidget* w = (PartAreaWidget*)widget(); + return w->groupColor(0); +} + + +// SubPartItem + +SubPartItem::SubPartItem(TraceCost* c) +{ + _partCostItem = c; + _factor=1; +} + +QString SubPartItem::text(int textNo) const +{ + if (textNo == 0) { + if (!_partCostItem) + return i18n("(unknown)"); + + return _partCostItem->dependant()->prettyName(); + } + + if (textNo != 1) + return QString::null; + + TraceCostType* ct; + PartAreaWidget* w = (PartAreaWidget*)widget(); + SubCost v; + + ct = w->costType(); + if (w->visualisation() == PartAreaWidget::Inclusive) + v = ((TracePartFunction*)_partCostItem)->inclusive()->subCost(ct); + else + v = _partCostItem->subCost(ct); + + if (Configuration::showPercentage()) { + TraceCost* t = Configuration::showExpanded() ? + _partCostItem->part() : _partCostItem->part()->data()->totals(); + double p = 100.0 * v / t->subCost(ct); + return QString("%1 %") + .arg(p, 0, 'f', Configuration::percentPrecision()); + } + return v.pretty(); +} + +QPixmap SubPartItem::pixmap(int i) const +{ + if (i != 1) return QPixmap(); + + // Cost pixmap + + PartAreaWidget* w = (PartAreaWidget*)widget(); + TraceCostType* ct = w->costType(); + TraceCost* t = Configuration::showExpanded() ? + _partCostItem->part() : _partCostItem->part()->data()->totals(); + TraceCost* c; + if (w->visualisation() == PartAreaWidget::Inclusive) + c = ((TracePartFunction*)_partCostItem)->inclusive(); + else + c = _partCostItem; + + return costPixmap( ct, c, (double) (t->subCost(ct)), false ); +} + +double SubPartItem::value() const +{ + TraceCostType* ct; + PartAreaWidget* w = (PartAreaWidget*)widget(); + + ct = w->costType(); + if (w->visualisation() == PartAreaWidget::Inclusive) + return (double) + ((TracePartFunction*)_partCostItem)->inclusive()->subCost(ct); + + return (double) _partCostItem->subCost(ct); +} + +double SubPartItem::sum() const +{ + PartAreaWidget* w = (PartAreaWidget*)widget(); + if (w->visualisation() == PartAreaWidget::Inclusive) { + double s = value(); + //qDebug("SubPartItem::sum [Cost %s]: %d", _cost->name().ascii(), s); + return s; + } + return 0.0; +} + +TreeMapItemList* SubPartItem::children() +{ + if (!initialized()) { +// qDebug("Create Part sub-subitems (%s)", name().ascii()); + + PartAreaWidget* w = (PartAreaWidget*)widget(); + + if (depth()-2 > w->callLevels()) + return _children; + + if (w->visualisation() == PartAreaWidget::Inclusive) { + TracePartCall* call; + TracePartCallList l; + + setSum(value()); + + l = ((TracePartFunction*)_partCostItem)->partCallings(); + for (call=l.first();call;call=l.next()) { + TraceFunction* called = call->call()->called(); + TraceCost* partCalled = called->findDepFromPart(call->part()); + if (partCalled) + addItem(new SubPartItem(partCalled)); + } + } + } + + return _children; +} + + +QColor SubPartItem::backColor() const +{ + PartAreaWidget* w = (PartAreaWidget*)widget(); + if (w->visualisation() == PartAreaWidget::Inclusive) + return w->groupColor((TraceFunction*)(_partCostItem->dependant())); + + return Configuration::groupColor(_partCostItem->dependant()); +} + + +#include "partgraph.moc" diff --git a/kcachegrind/kcachegrind/partgraph.h b/kcachegrind/kcachegrind/partgraph.h new file mode 100644 index 00000000..d4d5b121 --- /dev/null +++ b/kcachegrind/kcachegrind/partgraph.h @@ -0,0 +1,131 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * TracePart Graph + */ + +#ifndef PARTGRAPH_H +#define PARTGRAPH_H + +#include "treemap.h" +#include "tracedata.h" + +class PartAreaWidget: public TreeMapWidget +{ + Q_OBJECT + +public: + // Visualisation inside of trace parts + enum VisualisationMode { NoVisualisation, Partitioning, Inclusive }; + + PartAreaWidget(QWidget* parent=0, const char* name=0); + + void setData(TraceData* d); + void setCostType(TraceCostType* ct); + void setGroupType(TraceCost::CostType gt); + void setVisualisation(VisualisationMode); + void setZoomFunction(bool zoomFunction); + void setCallLevels(int callLevels); + void setFunction(TraceFunction* f); + + TraceCostType* costType() const { return _costType; } + TraceCost::CostType groupType() const { return _groupType; } + TraceFunction* function() const { return _function; } + VisualisationMode visualisation() const { return _visualisation; } + bool zoomFunction() const { return _zoomFunction; } + int callLevels() const { return _callLevels; } + + QColor groupColor(TraceFunction*) const; + QString tipString(TreeMapItem*) const; + + void changeHidden(const TracePartList& list); + bool isHidden(TracePart*) const; + +private: + void refreshParts(); + + TraceData* _data; + TraceCostType* _costType; + TraceCost::CostType _groupType; + TraceFunction* _function; + VisualisationMode _visualisation; + bool _zoomFunction; + int _callLevels; + + TracePartList _hiddenParts; +}; + +class BasePartItem: public TreeMapItem +{ +public: + BasePartItem(); + + void setData(TraceData* d); + + int rtti() const { return 1; } + double value() const; + QString text(int) const; + int borderWidth() const { return 0; } + TreeMapItemList* children(); + QColor backColor() const; + +private: + TraceData* _data; +}; + +class PartItem: public TreeMapItem +{ +public: + PartItem(TracePart* p); + int rtti() const { return 2; } + TracePart* part() { return _p; } + double value() const; + double sum() const; + int borderWidth() const { return 0; } + QString text(int) const; + QPixmap pixmap(int) const; + TreeMapItemList* children(); + QColor backColor() const; + +private: + TracePart* _p; + unsigned int _factor; +}; + +class SubPartItem: public TreeMapItem +{ +public: + SubPartItem(TraceCost*); + int rtti() const { return 3; } + TraceCost* partCostItem() { return _partCostItem; } + double value() const; + double sum() const; + SplitMode splitMode() const { return Vertical; } + QString text(int) const; + QPixmap pixmap(int) const; + TreeMapItemList* children(); + QColor backColor() const; + +private: + TraceCost* _partCostItem; + unsigned int _factor; +}; + + +#endif diff --git a/kcachegrind/kcachegrind/partlistitem.cpp b/kcachegrind/kcachegrind/partlistitem.cpp new file mode 100644 index 00000000..4f60cf1e --- /dev/null +++ b/kcachegrind/kcachegrind/partlistitem.cpp @@ -0,0 +1,189 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include + +#include +#include + +#include +#include +#include + +#include "listutils.h" +#include "partlistitem.h" +#include "coverage.h" +#include "configuration.h" + + +// PartListItem + +PartListItem::PartListItem(QListView* parent, TraceCostItem* costItem, + TraceCostType* ct, TraceCost::CostType gt, + TracePart* part) + :QListViewItem(parent) +{ + _partCostItem = costItem->findDepFromPart(part); + _part = part; + _groupType = gt; + _costType = ct; + +#if 0 + QString partName = QString::number(part->partNumber()); + if (part->data()->maxThreadID() >1) + partName += i18n(" (Thread %1)").arg(part->threadID()); + setText(0, partName); +#else + setText(0, _part->prettyName()); +#endif + + if (_part->trigger().isEmpty()) + setText(4,i18n("(none)")); + else + setText(4, _part->trigger()); + + update(); +} + +void PartListItem::setCostType(TraceCostType* ct) +{ + if (_costType == ct) return; + + _costType = ct; + update(); +} + +void PartListItem::setGroupType(TraceCost::CostType gt) +{ + if (_groupType == gt) return; + + _groupType = gt; + update(); +} + +void PartListItem::update() +{ + TracePartFunction* pf; + pf = !_partCostItem ? 0 : + (_partCostItem->type()==TraceCost::PartFunction) ? + ((TracePartFunction*)_partCostItem) : 0; + + double total = _part->subCost(_costType); + + TraceCost* selfTotalCost = _part; + if (pf && Configuration::showExpanded()) { + switch(_groupType) { + case TraceCost::Object: selfTotalCost = pf->partObject(); break; + case TraceCost::Class: selfTotalCost = pf->partClass(); break; + case TraceCost::File: selfTotalCost = pf->partFile(); break; + default: break; + } + } + double selfTotal = selfTotalCost->subCost(_costType); + + _pure = _partCostItem ? _partCostItem->subCost(_costType) : SubCost(0); + _sum = pf ? pf->inclusive()->subCost(_costType) : SubCost(0); + + if (selfTotal == 0 || !_partCostItem) { + setText(2, QString("-")); + setPixmap(2, QPixmap()); + } + else { + double pure = 100.0 * _pure / selfTotal; + if (Configuration::showPercentage()) { + setText(2, QString("%1") + .arg(pure, 0, 'f', Configuration::percentPrecision())); + } + else + setText(2, _partCostItem->prettySubCost(_costType)); + + setPixmap(2, costPixmap(_costType, _partCostItem, selfTotal, false)); + } + + if (total == 0 || !pf) { + setText(1, QString("-")); + setPixmap(1, QPixmap()); + } + else { + double sum = 100.0 * _sum / total; + if (Configuration::showPercentage()) { + setText(1, QString("%1") + .arg(sum, 0, 'f', Configuration::percentPrecision())); + } + else + setText(1, _sum.pretty()); + + setPixmap(1, costPixmap(_costType, pf->inclusive(), total, false)); + } + + if (!pf) { + setText(3, QString("-")); + _callers = 0; + return; + } + + TracePartCall* pc; + TracePartCallList pl; + SubCost callers, callees; + QString str; + + callers = 0; + pl = pf->partCallers(); + for (pc=pl.first();pc;pc=pl.next()) { + callers += pc->callCount(); + } + + if ((callers == 0) && (pf->calledContexts()>0)) + str = i18n("(active)"); + else + str = callers.pretty(); + + _callers = callers; + setText(3, str); +} + + +int PartListItem::compare(QListViewItem * i, int col, bool ascending ) const +{ + PartListItem* fi = (PartListItem*) i; + if (col==0) { + int mTID = _part->data()->maxThreadID()+1; + int mNum = _part->data()->maxPartNumber()+1; + + return + (_part->processID() - fi->_part->processID()) * mTID * mNum + + (_part->partNumber() - fi->_part->partNumber()) * mTID + + (_part->threadID() - fi->_part->threadID()); + } + if (col==1) { + if (_sum < fi->_sum) return -1; + if (_sum > fi->_sum) return 1; + return 0; + } + if (col==2) { + if (_pure < fi->_pure) return -1; + if (_pure > fi->_pure) return 1; + return 0; + } + if (col==3) { + if (_callers < fi->_callers) return -1; + if (_callers > fi->_callers) return 1; + return 0; + } + return QListViewItem::compare(i, col, ascending); +} diff --git a/kcachegrind/kcachegrind/partlistitem.h b/kcachegrind/kcachegrind/partlistitem.h new file mode 100644 index 00000000..98a674fe --- /dev/null +++ b/kcachegrind/kcachegrind/partlistitem.h @@ -0,0 +1,54 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef PARTLISTITEM_H +#define PARTLISTITEM_H + +#include +#include "tracedata.h" + +/** + * For info tab, trace part list. + * Needs update on + * - cost type change + * + * Note: on a cost item / percentage change, the list is rebuild + */ +class PartListItem: public QListViewItem +{ +public: + PartListItem(QListView* parent, TraceCostItem* costItem, + TraceCostType* ct, TraceCost::CostType gt, TracePart* part); + + int compare(QListViewItem * i, int col, bool ascending ) const; + TraceCost* partCostItem() { return _partCostItem; } + void setCostType(TraceCostType* ct); + void setGroupType(TraceCost::CostType); + TracePart* part() { return _part; } + void update(); + +private: + SubCost _sum, _pure; + SubCost _callers; + TraceCostType* _costType; + TraceCost* _partCostItem; + TracePart* _part; + TraceCost::CostType _groupType; +}; + +#endif diff --git a/kcachegrind/kcachegrind/partselection.cpp b/kcachegrind/kcachegrind/partselection.cpp new file mode 100644 index 00000000..85a46c35 --- /dev/null +++ b/kcachegrind/kcachegrind/partselection.cpp @@ -0,0 +1,567 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * For part file selection, to be put into a QDockWindow + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "partselection.h" +#include "partgraph.h" + +PartSelection::PartSelection( QWidget* parent, const char* name) + : PartSelectionBase(parent, name) +{ + _data = 0; + _costType = 0; + _costType2 = 0; + _groupType = TraceItem::NoCostType; + _group = 0; + _function = 0; + _inSelectionUpdate = false; + + _diagramMode = false; + _drawFrames = true; + + partAreaWidget->setAllowRotation(false); + partAreaWidget->setMaxSelectDepth(2); + partAreaWidget->setSelectionMode(TreeMapWidget::Extended); + partAreaWidget->setSplitMode(TreeMapItem::HAlternate); + partAreaWidget->setVisibleWidth(2, true); + partAreaWidget->setFieldType(0, i18n( "Name" )); + partAreaWidget->setFieldType(1, i18n( "Cost" )); + + connect(partAreaWidget, SIGNAL(selectionChanged()), + this, SLOT(selectionChanged())); + connect(partAreaWidget, SIGNAL(currentChanged(TreeMapItem*, bool)), + this, SLOT(currentChangedSlot(TreeMapItem*, bool))); + connect(partAreaWidget, SIGNAL(doubleClicked(TreeMapItem*)), + this, SLOT(doubleClicked(TreeMapItem*))); + connect(partAreaWidget, + SIGNAL(contextMenuRequested(TreeMapItem*,const QPoint &)), + this, + SLOT(contextMenuRequested(TreeMapItem*,const QPoint &))); + + _showInfo = true; + showInfo(false); +} + +PartSelection::~PartSelection() +{ +} + +void PartSelection::setData(TraceData* data) +{ + if (_data == data) return; + + _data = data; + partAreaWidget->setData(data); + fillInfo(); +} + + +void PartSelection::refresh() +{ + partAreaWidget->redraw(); + fillInfo(); +} + +void PartSelection::setCostType(TraceCostType* ct) +{ + if (ct == _costType) return; + _costType = ct; + + partAreaWidget->setCostType(ct); +} + +void PartSelection::setCostType2(TraceCostType* ct) +{ + if (ct == _costType2) return; + _costType2 = ct; + if (!_diagramMode) return; + + //TODO: get max cost(type1)/cost(type2) of shown parts + //partAreaWidget->setCostType(ct); +} + +void PartSelection::setGroupType(TraceItem::CostType gt) +{ + if (gt == _groupType) return; + _groupType = gt; + + partAreaWidget->setGroupType(gt); +} + +void PartSelection::setGroup(TraceCostItem*) +{ +} + +void PartSelection::setFunction(TraceFunction* f) +{ + if (_function == f) return; + _function = f; + + //kdDebug() << "PartSelection::setFunction " << f->name() << endl; + + // FIXME: The TreeMap shouldn't produce spurious selectionChanged events + _inSelectionUpdate = true; + partAreaWidget->setFunction(_function); + _inSelectionUpdate = false; +} + +void PartSelection::setPart(TracePart*) +{} + +void PartSelection::currentChangedSlot(TreeMapItem* i, bool kbd) +{ + if (!i) return; + if (!kbd) return; + if (i->text(0).isEmpty()) return; + + QString str = i->text(0); + if (!i->text(1).isEmpty()) + str += " (" + i->text(1) + ")"; + QString msg = i18n("Profile Part Overview: Current is '%1'").arg(str); + emit showMessage(msg, 5000); + + if (_showInfo) fillInfo(); +} + + +void PartSelection::doubleClicked(TreeMapItem* i) +{ + if (!i || i->rtti() != 3) return; + + TraceCost* c = ((SubPartItem*) i)->partCostItem(); + TraceCostItem* ci = 0; + + switch(c->type()) { + case TraceItem::PartFunction: + { + TraceFunction* f = ((TracePartFunction*)c)->function(); + if (f) + emit functionChanged(f); + } + return; + + case TraceItem::PartObject: + ci = ((TracePartObject*)c)->object(); + break; + case TraceItem::PartClass: + ci = ((TracePartClass*)c)->cls(); + break; + case TraceItem::PartFile: + ci = ((TracePartFile*)c)->file(); + break; + default: + break; + } + + if (ci) + emit groupChanged(ci); +} + + +void PartSelection::selectionChanged() +{ + if (_inSelectionUpdate) return; + + kdDebug() << "PartSelection::selectionChanged" << endl; + + bool something_changed = false; + bool nothingSelected = true; + + TracePartList pList; + TreeMapItem* i; + TracePart* part; + + // if nothing is selected, activate all parts + TreeMapItemList* list = partAreaWidget->base()->children(); + if (!list) return; + + for (i=list->first();i;i=list->next()) + if (partAreaWidget->isSelected(i)) { + nothingSelected = false; + break; + } + + for (i=list->first();i;i=list->next()) { + part = ((PartItem*)i)->part(); + bool active = nothingSelected || partAreaWidget->isSelected(i); + if (active) { + pList.append(part); + something_changed = true; + } + } + + if (something_changed) { + //qDebug("PartSelection: Something changed."); + emit activePartsChanged(pList); + } +} + +/* this makes the graph selection the same to the parts in the list */ +void PartSelection::activePartsChangedSlot(const TracePartList& list) +{ + _inSelectionUpdate = true; + + kdDebug() << "Entering PartSelection::activePartsChangedSlot" << endl; + + TreeMapItem* i; + TreeMapItemList l = *partAreaWidget->base()->children(); + // first deselect inactive, then select active (makes current active) + for (i=l.first();i;i=l.next()) { + TracePart* part = ((PartItem*)i)->part(); + bool active = (list.containsRef(part)>0); + if (!active && partAreaWidget->isSelected(i)) { +#if 0 + qDebug("PartSelection::partsChangedSlot: Part %s changed to unselected.", + ((PartItem*)i)->part()->shortName().ascii()); +#endif + + partAreaWidget->setSelected(i, false); + } + } + for (i=l.first();i;i=l.next()) { + TracePart* part = ((PartItem*)i)->part(); + bool active = (list.containsRef(part)>0); + if (active && !partAreaWidget->isSelected(i)) { +#if 0 + qDebug("PartSelection::partsChangedSlot: Part %s changed to selected.", + ((PartItem*)i)->part()->shortName().ascii()); +#endif + partAreaWidget->setSelected(i, true); + } + } + + _inSelectionUpdate = false; + + kdDebug() << "Leaving PartSelection::activePartsChangedSlot" << endl; + + fillInfo(); +} + +void PartSelection::contextMenuRequested(TreeMapItem* i, + const QPoint & p) +{ + if (!i) return; + + QPopupMenu popup; + QPopupMenu ppopup; + QPopupMenu vpopup; + + QString str; + TreeMapItem* s = 0; + + if (_data && (_data->parts().count()>1)) { + s = partAreaWidget->possibleSelection(i); + if (!s->text(0).isEmpty()) { + str = (partAreaWidget->isSelected(s)) ? + i18n("Deselect") : i18n("Select"); + str += " '" + s->text(0) + "'"; + popup.insertItem(str, 1); + } + + popup.insertItem(i18n("Select All Parts"), 2); + + popup.insertItem(i18n("Visible Parts"), &ppopup, 10); + + ppopup.insertItem(i18n("Hide Selected Parts"), 3); + ppopup.insertItem(i18n("Unhide Hidden Parts"), 4); + + popup.insertSeparator(); + } + + popup.insertItem(i18n("Go Back"), 99); + if (i->rtti() == 3) { + TreeMapItem* ni = i; + int id = 100; + while (ni && ni->rtti() == 3) { + TraceCost* c = ((SubPartItem*)ni)->partCostItem(); + if (c->type() == TraceItem::PartFunction) + if ( ((TracePartFunction*)c)->function() == _function) break; + + str = i18n("Select") + " '" + ni->text(0) + "'"; + popup.insertItem(str, id); + ni = ni->parent(); + id++; + } + } + popup.insertSeparator(); + + vpopup.setCheckable(true); + popup.insertItem(i18n("Visualization"), &vpopup, 10); + + vpopup.insertItem(i18n("Partitioning Mode"), 30); + vpopup.insertItem(i18n("Diagram Mode"), 34); + vpopup.insertItem(i18n("Zoom Function"), 31); + vpopup.insertItem(i18n("Show Direct Calls"), 32); + vpopup.insertItem(i18n("Increment Shown Call Levels"), 33); + if (partAreaWidget->visualisation() == PartAreaWidget::Partitioning) { + vpopup.setItemChecked(30, true); + vpopup.setItemEnabled(31, false); + vpopup.setItemEnabled(32, false); + vpopup.setItemEnabled(33, false); + } + else { + vpopup.setItemChecked(31, partAreaWidget->zoomFunction()); + } + vpopup.setItemChecked(34, _diagramMode); + + vpopup.insertSeparator(); + + vpopup.insertItem(i18n("Draw Names"), 20); + vpopup.insertItem(i18n("Draw Costs"), 21); + vpopup.insertItem(i18n("Ignore Proportions"), 22); + vpopup.insertItem(i18n("Draw Frames"), 24); + vpopup.insertItem(i18n("Allow Rotation"), 23); + if (!partAreaWidget->fieldVisible(0) && + !partAreaWidget->fieldVisible(1)) { + vpopup.setItemEnabled(22, false); + vpopup.setItemEnabled(23, false); + } + else { + vpopup.setItemChecked(20,partAreaWidget->fieldVisible(0)); + vpopup.setItemChecked(21,partAreaWidget->fieldVisible(1)); + vpopup.setItemChecked(22,partAreaWidget->fieldForced(0)); + vpopup.setItemChecked(23,partAreaWidget->allowRotation()); + vpopup.setItemChecked(24,_drawFrames); + } + + if (_showInfo) + popup.insertItem(i18n("Hide Info"), 40); + else + popup.insertItem(i18n("Show Info"), 41); + + int r = popup.exec(partAreaWidget->mapToGlobal(p)); + + if (r>=100) { + TreeMapItem* ci = i; + while (ci && r>100) { + ci = ci->parent(); + r--; + } + doubleClicked(ci); + return; + } + + switch(r) { + case 1: + // select/deselect part under mouse + partAreaWidget->setSelected(s, !partAreaWidget->isSelected(s)); + break; + + case 2: + // select all parts + { + TreeMapItemList list = *partAreaWidget->base()->children(); + partAreaWidget->setRangeSelection(list.first(), list.last(), true); + } + break; + + case 3: + emit partsHideSelected(); + break; + + case 4: + emit partsUnhideAll(); + break; + + case 99: + // last selected function + emit goBack(); + break; + + case 20: + partAreaWidget->setFieldVisible(0, !vpopup.isItemChecked(20)); + break; + + case 21: + partAreaWidget->setFieldVisible(1, !vpopup.isItemChecked(21)); + break; + + case 22: + partAreaWidget->setFieldForced(0, !vpopup.isItemChecked(22)); + partAreaWidget->setFieldForced(1, !vpopup.isItemChecked(22)); + break; + + case 23: partAreaWidget->setAllowRotation(!vpopup.isItemChecked(23)); break; + + case 24: + _drawFrames = !_drawFrames; + partAreaWidget->drawFrame(2,_drawFrames); + partAreaWidget->drawFrame(3,_drawFrames); + break; + + case 30: + partAreaWidget->setVisualisation(!vpopup.isItemChecked(30) ? + PartAreaWidget::Partitioning : + PartAreaWidget::Inclusive); + break; + + case 31: + // zoom/unzoom function + partAreaWidget->setZoomFunction(!vpopup.isItemChecked(31)); + break; + + case 32: + case 33: + // change call Levels + { + int l = (r==32) ? 1 : partAreaWidget->callLevels()+1; + partAreaWidget->setCallLevels(l); + } + break; + + case 34: + _diagramMode = !_diagramMode; + partAreaWidget->setTransparent(2,_diagramMode); + break; + + + case 40: + case 41: + showInfo(r==41); + break; + + default: + break; + } +} + +void PartSelection::hiddenPartsChangedSlot(const TracePartList& list) +{ + partAreaWidget->changeHidden(list); +} + +void PartSelection::readVisualisationConfig(KConfigGroup* config) +{ + bool enable; + + QString mode = config->readEntry("PartitionMode", "Inclusive"); + if (mode == "Inclusive") + partAreaWidget->setVisualisation(PartAreaWidget::Inclusive); + else + partAreaWidget->setVisualisation(PartAreaWidget::Partitioning); + + _diagramMode = config->readBoolEntry("DiagramMode", false); + partAreaWidget->setTransparent(2,_diagramMode); + + _drawFrames = config->readBoolEntry("DrawFrames", true); + partAreaWidget->drawFrame(2,_drawFrames); + partAreaWidget->drawFrame(3,_drawFrames); + + enable = config->readBoolEntry("GraphZoom", false); + partAreaWidget->setZoomFunction(enable); + + int levels = config->readNumEntry("GraphLevels", 1); + partAreaWidget->setCallLevels(levels); + + enable = config->readBoolEntry("GraphDrawName", true); + partAreaWidget->setFieldVisible(0, enable); + + enable = config->readBoolEntry("GraphDrawCost", true); + partAreaWidget->setFieldVisible(1, enable); + + enable = config->readBoolEntry("GraphForceStrings", false); + partAreaWidget->setFieldForced(0, enable); + partAreaWidget->setFieldForced(1, enable); + + enable = config->readBoolEntry("GraphAllowRotation", true); + partAreaWidget->setAllowRotation(enable); + + showInfo(config->readBoolEntry("ShowInfo", false)); +} + +void PartSelection::saveVisualisationConfig(KConfigGroup* config) +{ + QString mode; + if (partAreaWidget->visualisation() == PartAreaWidget::Inclusive) + mode = "Inclusive"; + else + mode = "Partitioning"; + config->writeEntry("PartitionMode", mode); + + config->writeEntry("DiagramMode", _diagramMode); + config->writeEntry("DrawFrames", _drawFrames); + + config->writeEntry("GraphZoom", partAreaWidget->zoomFunction()); + config->writeEntry("GraphLevels", partAreaWidget->callLevels()); + config->writeEntry("GraphDrawName", partAreaWidget->fieldVisible(0)); + config->writeEntry("GraphDrawCosts", partAreaWidget->fieldVisible(1)); + config->writeEntry("GraphForceStrings", partAreaWidget->fieldForced(0)); + config->writeEntry("GraphAllowRotation", partAreaWidget->allowRotation()); + + config->writeEntry("ShowInfo", _showInfo); +} + +void PartSelection::showInfo(bool enable) +{ + if (_showInfo == enable) return; + + _showInfo = enable; + if (enable) { + rangeLabel->show(); + fillInfo(); + } + else + rangeLabel->hide(); +} + +void PartSelection::fillInfo() +{ + if (!_data) { + rangeLabel->setText(i18n("(no trace loaded)")); + return; + } + + QString info = _data->activePartRange(); + + TreeMapItem* i = partAreaWidget->current(); + while (i && i->rtti()!=2) i = i->parent(); + if (i) { + TracePart* part = ((PartItem*)i)->part(); + + //if (!part->trigger().isEmpty()) info += ", " + part->trigger(); + if (!part->timeframe().isEmpty()) + info += ", Time " + part->timeframe() + " BBs"; + } + else { + TracePart* part = _data->parts().first(); + + if (part && !part->version().isEmpty()) + info += ", Cachegrind " + part->version(); + } + + + rangeLabel->setText(info); +} + +#include "partselection.moc" diff --git a/kcachegrind/kcachegrind/partselection.h b/kcachegrind/kcachegrind/partselection.h new file mode 100644 index 00000000..168446f1 --- /dev/null +++ b/kcachegrind/kcachegrind/partselection.h @@ -0,0 +1,95 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * PartSelection for KCachegrind + * For part file selection, to be put into a QDockWindow + */ + +#ifndef PARTSELECTION_H +#define PARTSELECTION_H + +#include + +#include "partselectionbase.h" +#include "partgraph.h" +#include "tracedata.h" + +class KConfigGroup; +class TraceFunction; +class TraceData; +class TreeMapItem; + +class PartSelection: public PartSelectionBase +{ + Q_OBJECT + +public: + PartSelection( QWidget* parent = 0, const char* name = 0); + ~PartSelection(); + + TraceData* data() { return _data; } + void setData(TraceData*); + + PartAreaWidget* graph() { return partAreaWidget; } + + void readVisualisationConfig(KConfigGroup*); + void saveVisualisationConfig(KConfigGroup*); + +signals: + void activePartsChanged(const TracePartList& list); + void partsHideSelected(); + void partsUnhideAll(); + void groupChanged(TraceCostItem*); + void functionChanged(TraceItem*); + void showMessage(const QString&, int); + void goBack(); + +public slots: + void selectionChanged(); + void doubleClicked(TreeMapItem*); + void contextMenuRequested(TreeMapItem*, const QPoint &); + void currentChangedSlot(TreeMapItem*, bool); + + void setPart(TracePart*); + void setCostType(TraceCostType*); + void setCostType2(TraceCostType*); + void setGroupType(TraceItem::CostType); + void setGroup(TraceCostItem*); + void setFunction(TraceFunction*); + void activePartsChangedSlot(const TracePartList& list); + void hiddenPartsChangedSlot(const TracePartList& list); + void refresh(); + void showInfo(bool); + +private: + void fillInfo(); + + TraceData* _data; + TraceCostType *_costType, *_costType2; + TraceItem::CostType _groupType; + TraceCostItem* _group; + TraceFunction* _function; + bool _showInfo; + bool _diagramMode; + bool _drawFrames; + + bool _inSelectionUpdate; +}; + +#endif diff --git a/kcachegrind/kcachegrind/partselectionbase.ui b/kcachegrind/kcachegrind/partselectionbase.ui new file mode 100644 index 00000000..3267f49a --- /dev/null +++ b/kcachegrind/kcachegrind/partselectionbase.ui @@ -0,0 +1,89 @@ + +PartSelectionBase + + + PartSelectionBase + + + + 0 + 0 + 460 + 402 + + + + Parts Overview + + + + unnamed + + + 6 + + + 6 + + + + partAreaWidget + + + + 5 + 7 + 0 + 0 + + + + + 0 + 50 + + + + + + rangeLabel + + + + 7 + 5 + 0 + 0 + + + + (no trace parts) + + + + + + + PartAreaWidget +
partgraph.h
+ + -1 + -1 + + 0 + + 5 + 7 + 0 + 0 + + image0 +
+
+ + + 789c9597db4e1d4b0e86eff31428be8b46b5fb54dd551acd051020211c4320c0682eecaa5e9ccf90005bf3ee53cbbfe9d9c9c548a38ea27c2977b5cbfe6dd7fae3c3c2e1cee6c2873fde3d3cf2e3595a48a77cbff0213f5d5dbdfcf35ffff8f3ddfba65998fff161a179ffb777ef771f17d2c2d6cdf53807c705a889cdac6ee72c47736eebd637b5f2b672d70ecd30e744ca7d9b9a4ad7579587363433e527e5d072abefbb03636978ceb4a31c5bc13a91716ad41f89e0aec2fe74abcc6d6ad5f9e4945357375ef75f997357751dd61d19c71afb61bdef421b951f274ebaffb6716cb3f2b2f230f18d3183dd95b1b441d7d59f2e94757ccf1b27eceff69563618d0f5dcfd9d7beeb7a5dd7f3fac6fb4ee32b87c619f1168d976f7d6ff6df26d6ef4b301e1bac7f54f63e747a5e87f5c173276aff605c22a8fefc540e5e3af5972f956359577bf2c6d978cb78d6697edc9eb2f8b143fed7945359d778b0e6c3e7be81bf7465dce23cf4ac3c16567f19fecefaae53bd081b7baccbb131b7d05f679ca03f27c6b9d1f388c6bbaffab7f80dca4d1fed7bfafdbeedb9533d490f1e2ae8d5693ccacb83d77cd027e3d4416faa97beefa3d7f8f086f168f9d9020f43a3f5c5aa877e28eb88f74fe319becfaa873e0c15de775f95639f3dec37c143d361bf2be3cef4b76bdcb7d84ff5d2cbd0227facf1ecd360fa67d54b9f078ffcd10f636e11ff53e3847853ad3c1b668837e9fe43358c2df45a81430b3db2e6676842d368fd48abdc068b87e0fd2e788bf792b21f861e7afd0e0e5ce37dcdd7d0872218e5d789f57b3218a71ae7bb501eca3af67b510e21c05fd27a1b6218ac3ef17d0ed68fe8cb1be3fc6e5159863ca0bfed824340fe687d62c4838c23f677f0370de380fcdd2be7e03dfab1d6cf300bcd80fa80bfb358dbf9547fa18abe413d68fe421d63ade7737aded094cf63fdcc38f4d0bfee17dac2d08380a3a0dff24cb90bdc23ffaa87e0636c912fed87612e38d4df1e982bc4dbdd29872001f14aca319413eb793e83cbfbe8af27606e4c0f5f8d3bcb7763ec6b8da73b57e69083d61f9d82b9b5fada31f635fcaf8d7b7b5ffb41f16e34ff34fe214dfc0c8e1ef1a22330bfcdc345e31e4c33e3c1e6e181b1609e3ac43f8759803e8cd9f2e7e0ff18ab80ef5d8239e2fc0ef129f90ff0bf0773347fee8d13ce279bc6b34af5c61abf58157bf4b765b05435f4abfec726968c2b67e536fa80fe388279067f680d2c750dff75fe9766da07e44bc02521ba4e1d584c1fb261dcc23fd97f63f8c3af136bfc687362c4e76962ed8fac7a2ecdb7853f7c63ec6bf467bd3fc421c680780c60b6f9275fc045aea84fadaf18a2e0fc04ff0237bd9e8fefc0d236e8ef3f8d83c57fdd38daf94edeb886be74fec618b3ed7f3731e66b0fe67a403e76c0454ef8de9231dbfe9f8dc5fc47be38a6887ad2f950964d2fdc1a27d3c7b6710693f6ef9864b4fdb4ffc6cc5540fea0972c339c87afc1c9f4443a5fe2c84d40bd2f82cb3ae6c7b1716dbc37b1e68bf5be118b7e3dead78125d87d0bfa99bd7d8fb78c1bdb4fcfcfd51bcb9d716bebda3fb8966cf7cf57709103f2e327c6fe5a2fdc4ab4fb01deef5283fa65d50ffb54c13fd67ec37d61e845fb070f8571df7c341eec7e7a691c6ae453fb771937d1fa999ebfa43358bf573d3327c6fdd0a9de59b87414e59f60911ef3ff07380d16bf0b63b6fbb8ce0f4e523a80ee8ff8a5f21fe83f384f2e8cf9d419db7d4bb4bff32cb1dd871ae38c79417962dd9f8f2786bfaa3fa992ddbf68d5d8d6797d62dc27b4bf4a9bdeee5be7c619efbb169c2b9bb7aa67e972857a67d59bf8943cf4a6f5237dae3b9c3f4f8cef5713637e6a3c6548e546a7eb87e0b28e7cf7c68ddd2fb15f28acf166bd2f484c19f921f8cbc57de84ff52c397b8f7e178d7bbb7f7e9f18f743d593ccd210d12f9e8c05fd8417c1e5e704e2afefa7aa5cf850ff4fc6a1473debfc4d7561c4ebd3c48897f69fd4e4d8eb7e4efb736a0b23fe9adfd465bb3f388d6ff22933fa1df6f7597ad483c62371f6e6ff8671b6f89e198fb8cfb0ea3f49619c6f6562cc27d5474a7906e6b589d1df549f29e78479450fe0b1c2fc67d5731a0ba33ffc97a167fd7d92ebb1b1f9a87acdcdd8a23ff2e3c4a81fed0fb91bbb88f8e9f772f9b9c6b8bfac187bd41febbcc8c35831f4bb6edc227eeec8b847bc58ef0f398d91a1179d6f391746bdebfd60ac4716b0180bd8a97e473f8ea2dfdb7d9c3f8e1cff8f8760057b27e57f92cb6e743377e24efff29cb97377e12edd55b190c99eddb5bb71b7eeceddbb07f7e89edc0ff7d33dbb17f7ea16dd925b761fdd8a63d8174f52b15e756bee93fbecd6dd17b7e136dd96db763b6ed77d2dd67bee9bdb77078ed49e8b27b7c5fabb3b7447eed855ae768d6bcbd339ef7a37b8e022392a2734fbd1dd119350a24c23cde8844ee98ccee9c2f9c2977445d77443b793fd8ceee89e1ee8919ee807fdb4e7995ee8b5d82fd252b15ffe8bfd097da4155aa535fa34597fa6f5f2f717da28f69bb445dbb433d99fd22e7da53dfa3659efd3017da7c3f2af233aa68aeadfec1b6aa9236fd63d0d14ca15c0719913737b965fedcbf0c83cc29a677cc2a77cc6e77cc1977cc5d7c5fee637fbdbb2db9d5adff343b17ee427fec1737ee69762fffa9bfd222ff17259fdc82bbcca6bfc893ff33a7fe10ddee42ddee69ddfec77f92beff137dee703fece877cc4c7aee58a6b6ee8b8fc10ed7eb13f635f9a4bc5e54719477152ba671977a505c84845c57222a7bfd89fcb999ccb855cca955cbfc5546ee456ee68b14cf67b799047799aec2fdc92fc28999cef567224cff222afb2c85b32e36d599265f9282bb23ad95fba6559934f2593f3e798b74acc8fd5f6b3accb17d9904dd9926db3a7520d1fdd37d9915df95a72599e1291566df7e49beccb817c974339829eb55e56dc7e51ec0d2dcbb1545297a791563af1c5eff2d3beb4faf8562f568f0744745b9e9d5f1f7992d5e412a186ffef7afff7dfdffd077c99ae99 + + + +
diff --git a/kcachegrind/kcachegrind/partview.cpp b/kcachegrind/kcachegrind/partview.cpp new file mode 100644 index 00000000..ea1db4d7 --- /dev/null +++ b/kcachegrind/kcachegrind/partview.cpp @@ -0,0 +1,235 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Part View + */ + +#include +#include +#include +#include + +#include "configuration.h" +#include "partlistitem.h" +#include "toplevel.h" +#include "partview.h" + + + +// +// PartView +// + + +PartView::PartView(TraceItemView* parentView, + QWidget* parent, const char* name) + : QListView(parent, name), TraceItemView(parentView) +{ + _inSelectionUpdate = false; + + addColumn( i18n( "Profile Part" ) ); + addColumn( i18n( "Incl." ) ); + addColumn( i18n( "Self" ) ); + addColumn( i18n( "Called" ) ); + //addColumn( i18n( "Fixed" ) ); + addColumn( i18n( "Comment" ) ); + + setAllColumnsShowFocus(true); + setColumnAlignment(1, Qt::AlignRight); + setColumnAlignment(2, Qt::AlignRight); + setColumnAlignment(3, Qt::AlignRight); + setMinimumHeight(50); + setSelectionMode(Extended); + + connect( this, + SIGNAL( selectionChanged() ), + SLOT( selectionChangedSlot() ) ); + + connect( this, + SIGNAL(contextMenuRequested(QListViewItem*, const QPoint &, int)), + SLOT(context(QListViewItem*, const QPoint &, int))); + + QWhatsThis::add( this, whatsThis() ); +} + +QString PartView::whatsThis() const +{ + return i18n( "Trace Part List" + "

This list shows all trace parts of the loaded " + "trace. For each part, the " + "self/inclusive cost of the current selected " + "function, spent in the part, is shown; " + "percentage costs are always relative to the " + "total cost of the part (not to the whole " + "trace as in the Trace Part Overview). " + "Also shown are the calls happening to/from the " + "current function inside of the trace part.

" + "

By choosing one or more trace parts from the " + "list, the costs shown all over KCachegrind will " + "only be the ones spent in the selected part(s). " + "If no list selection is shown, in fact all trace " + "parts are selected implicitly.

" + "

This is a multi-selection list. You can select " + "ranges by dragging the mouse or use SHIFT/CTRL " + "modifiers. " + "Selection/Deselection of trace parts can also be " + "done by using the Trace Part Overview Dockable. " + "This one also supports multiple selection.

" + "

Note that the list is hidden if only one trace " + "part is loaded.

"); +} + + +void PartView::context(QListViewItem* i, const QPoint & pos, int) +{ + QPopupMenu popup; + + TracePart* p = i ? ((PartListItem*) i)->part() : 0; + + if (p) { + popup.insertItem(i18n("Select '%1'").arg(p->name()), 93); + popup.insertItem(i18n("Hide '%1'").arg(p->name()), 94); + popup.insertSeparator(); + } + + popup.insertItem(i18n("Hide Selected"), 95); + popup.insertItem(i18n("Show All"), 96); + popup.insertSeparator(); + + addGoMenu(&popup); + + int r = popup.exec(pos); + if (r == 95) { + ; + } + + // TODO: ... +} + +void PartView::selectionChangedSlot() +{ + if (_inSelectionUpdate) return; + + TracePartList l; + QListViewItem* item = firstChild(); + for(;item;item = item->nextSibling()) + if (item->isSelected()) + l.append( ((PartListItem*)item)->part() ); + + selected(l); +} + + +TraceItem* PartView::canShow(TraceItem* i) +{ + if (!data()) return 0; + if (data()->parts().count()>1) return i; + return 0; +} + +void PartView::doUpdate(int changeType) +{ + // Special case ? + if (changeType == costType2Changed) return; + if (changeType == selectedItemChanged) return; + + if (changeType == groupTypeChanged) { + QListViewItem *item; + for (item = firstChild();item;item = item->nextSibling()) + ((PartListItem*)item)->setGroupType(_groupType); + + return; + } + + if (changeType == costTypeChanged) { + QListViewItem *item; + for (item = firstChild();item;item = item->nextSibling()) + ((PartListItem*)item)->setCostType(_costType); + + return; + } + + if (changeType == partsChanged) { + + TracePart* part; + + QListViewItem* item; + _inSelectionUpdate = true; + item = firstChild(); + for(;item;item = item->nextSibling()) { + part = ((PartListItem*)item)->part(); + + if (_partList.containsRef(part)>0) { + setSelected(item, true); + ensureItemVisible(item); + } + else + setSelected(item, false); + } + _inSelectionUpdate = false; + + return; + } + + refresh(); +} + +void PartView::refresh() +{ + clear(); + setColumnWidth(1, 50); + setColumnWidth(2, 50); + + if (!_data || !_activeItem) return; + + TraceItem::CostType t = _activeItem->type(); + TraceFunction* f = 0; + if (t == TraceItem::Function) f = (TraceFunction*) _activeItem; + if (!f) return; + + TracePart* part; + TracePartList hidden; + if (_topLevel) + hidden = _topLevel->hiddenParts(); + + TracePartList allParts = _data->parts(); + + _inSelectionUpdate = true; + + QListViewItem* item = 0; + for (part = allParts.first(); part; part = allParts.next()) { + if (hidden.findRef(part)>=0) continue; + item = new PartListItem(this, f, _costType, _groupType, part); + + if (part->isActive()) { + setSelected(item, true); + ensureItemVisible(item); + } + } + + _inSelectionUpdate = false; + + if (item) { + int headerHeight = header()->height(); + int itemHeight = item->height(); + setMinimumHeight(headerHeight + 2*itemHeight + 2); + } +} + +#include "partview.moc" diff --git a/kcachegrind/kcachegrind/partview.h b/kcachegrind/kcachegrind/partview.h new file mode 100644 index 00000000..5c6c513b --- /dev/null +++ b/kcachegrind/kcachegrind/partview.h @@ -0,0 +1,54 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Part View + */ + +#ifndef PARTVIEW_H +#define PARTVIEW_H + +#include +#include "tracedata.h" +#include "traceitemview.h" + +class PartView: public QListView, public TraceItemView +{ + Q_OBJECT + +public: + PartView(TraceItemView* parentView, + QWidget* parent=0, const char* name=0); + + virtual QWidget* widget() { return this; } + QString whatsThis() const; + + void refresh(); + +private slots: + void context(QListViewItem*,const QPoint &, int); + void selectionChangedSlot(); + +private: + TraceItem* canShow(TraceItem*); + void doUpdate(int); + + bool _inSelectionUpdate; +}; + +#endif diff --git a/kcachegrind/kcachegrind/pool.cpp b/kcachegrind/kcachegrind/pool.cpp new file mode 100644 index 00000000..ec8d4074 --- /dev/null +++ b/kcachegrind/kcachegrind/pool.cpp @@ -0,0 +1,258 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002-2004 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include +#include "pool.h" + +// FixPool + +#define CHUNK_SIZE 100000 + +struct SpaceChunk +{ + struct SpaceChunk* next; + unsigned int used; + char space[1]; +}; + +FixPool::FixPool() +{ + _first = _last = 0; + _reservation = 0; + _count = 0; + _size = 0; +} + +FixPool::~FixPool() +{ + struct SpaceChunk* chunk = _first, *next; + + while(chunk) { + next = chunk->next; + free(chunk); + chunk = next; + } + + if (0) qDebug("~FixPool: Had %d objects with total size %d\n", + _count, _size); +} + +void* FixPool::allocate(unsigned int size) +{ + if (!ensureSpace(size)) return 0; + + _reservation = 0; + void* result = _last->space + _last->used; + _last->used += size; + + _count++; + _size += size; + + return result; +} + +void* FixPool::reserve(unsigned int size) +{ + if (!ensureSpace(size)) return 0; + _reservation = size; + + return _last->space + _last->used; +} + + +bool FixPool::allocateReserved(unsigned int size) +{ + if (_reservation < size) return false; + + _reservation = 0; + _last->used += size; + + _count++; + _size += size; + + return true; +} + +bool FixPool::ensureSpace(unsigned int size) +{ + if (_last && _last->used + size <= CHUNK_SIZE) return true; + + struct SpaceChunk* newChunk; + + // we don't allow allocation sizes > CHUNK_SIZE + if (size > CHUNK_SIZE) return false; + + newChunk = (struct SpaceChunk*) malloc(sizeof(struct SpaceChunk) + + CHUNK_SIZE); + newChunk->next = 0; + newChunk->used = 0; + + if (!_last) { + _last = _first = newChunk; + } + else { + _last->next = newChunk; + _last = newChunk; + } + return true; +} + + +// DynPool + +DynPool::DynPool() +{ + _data = (char*) malloc(CHUNK_SIZE); + _used = 0; + _size = CHUNK_SIZE; + + // end marker + *(int*)_data = 0; +} + +DynPool::~DynPool() +{ + // we could check for correctness by iteration over all objects + + ::free(_data); +} + +bool DynPool::allocate(char** ptr, unsigned int size) +{ + // round up to multiple of 4 + size = (size+3) & ~3; + + /* need 12 bytes more: + * - 4 bytes for forward chain + * - 4 bytes for pointer to ptr + * - 4 bytes as end marker (not used for new object) + */ + if (!ensureSpace(size + 12)) return false; + + char** obj = (char**) (_data+_used); + obj[0] = (char*)(_data + _used + size + 8); + obj[1] = (char*)ptr; + *(int*)(_data+_used+size+8) = 0; + *ptr = _data+_used+8; + + _used += size + 8; + + return true; +} + +void DynPool::free(char** ptr) +{ + if (!ptr || + !*ptr || + (*(char**)(*ptr - 4)) != (char*)ptr ) + qFatal("Chaining error in DynPool::free"); + + (*(char**)(*ptr - 4)) = 0; + *ptr = 0; +} + +bool DynPool::ensureSpace(unsigned int size) +{ + if (_used + size <= _size) return true; + + unsigned int newsize = _size *3/2 + CHUNK_SIZE; + char* newdata = (char*) malloc(newsize); + + unsigned int freed = 0, len; + char **p, **pnext, **pnew; + + qDebug("DynPool::ensureSpace size: %d => %d, used %d. %p => %p", + _size, newsize, _used, _data, newdata); + + pnew = (char**) newdata; + p = (char**) _data; + while(*p) { + pnext = (char**) *p; + len = (char*)pnext - (char*)p; + + if (0) qDebug(" [%8p] Len %d (ptr %p), freed %d (=> %p)", + p, len, p[1], freed, pnew); + + /* skip freed space ? */ + if (p[1] == 0) { + freed += len; + p = pnext; + continue; + } + + // new and old still at same address ? + if (pnew == p) { + pnew = p = pnext; + continue; + } + + // copy object + pnew[0] = (char*)pnew + len; + pnew[1] = p[1]; + memcpy((char*)pnew + 8, (char*)p + 8, len-8); + + // update pointer to object + char** ptr = (char**) p[1]; + if (*ptr != ((char*)p)+8) + qFatal("Chaining error in DynPool::ensureSpace"); + *ptr = ((char*)pnew)+8; + + pnew = (char**) pnew[0]; + p = pnext; + } + pnew[0] = 0; + + unsigned int newused = (char*)pnew - (char*)newdata; + qDebug("DynPool::ensureSpace size: %d => %d, used %d => %d (%d freed)", + _size, newsize, _used, newused, freed); + + ::free(_data); + _data = newdata; + _size = newsize; + _used = newused; + + return true; +} + +/* Testing the DynPool +int main() +{ + char* bufs[CHUNK_SIZE]; + int i; + + DynPool p; + + for(i=0;i20)) + p.free(bufs+i-20); + } + + for(i=0;i + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef POOL_H +#define POOL_H + +/** + * Pool objects: containers for many small objects. + */ + +struct SpaceChunk; + +/** + * FixPool + * + * For objects with fixed size and life time + * ending with that of the pool. + */ +class FixPool +{ + public: + FixPool(); + ~FixPool(); + + /** + * Take bytes from the pool + */ + void* allocate(unsigned int size); + + /** + * Reserve space. If you call allocateReservedSpace(realsize) + * with realSize < reserved size directly after, you + * will get the same memory area. + */ + void* reserve(unsigned int size); + + /** + * Before calling this, you have to reserve at least bytes + * with reserveSpace(). + */ + bool allocateReserved(unsigned int size); + + private: + /* Checks that there is enough space in the last chunk. + * Returns false if this is not possible. + */ + bool ensureSpace(unsigned int); + + struct SpaceChunk *_first, *_last; + unsigned int _reservation; + int _count, _size; +}; + +/** + * DynPool + * + * For objects which probably need to be resized + * in the future. Objects also can be deleted to free up space. + * As objects can also be moved in a defragmentation step, + * access has to be done via the given pointer object. + */ +class DynPool +{ + public: + DynPool(); + ~DynPool(); + + /** + * Take bytes from the pool, changing <*ptr> + * to point to this allocated space. + * <*ptr> will be changed if the object is moved. + * Returns false if no space available. + */ + bool allocate(char** ptr, unsigned int size); + + /** + * To resize, first allocate new space, and free old + * afterwards. + */ + void free(char** ptr); + + private: + /* Checks that there is enough space. If not, + * it compactifies, possibly moving objects. + */ + bool ensureSpace(unsigned int); + + char* _data; + unsigned int _used, _size; +}; + +#endif // POOL_H diff --git a/kcachegrind/kcachegrind/sourceitem.cpp b/kcachegrind/kcachegrind/sourceitem.cpp new file mode 100644 index 00000000..dabad043 --- /dev/null +++ b/kcachegrind/kcachegrind/sourceitem.cpp @@ -0,0 +1,444 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Items of source view. + */ + +#include +#include +#include + +#include +#include +#include + +#include "configuration.h" +#include "listutils.h" +#include "sourceview.h" +#include "sourceitem.h" + + +// SourceItem + +// for source lines +SourceItem::SourceItem(SourceView* sv, QListView* parent, + int fileno, unsigned int lineno, + bool inside, const QString& src, + TraceLine* line) + : QListViewItem(parent) +{ + _view = sv; + _lineno = lineno; + _fileno = fileno; + _inside = inside; + _line = line; + _lineCall = 0; + _lineJump = 0; + + if (src == "...") + setText(0, src); + else + setText(0, QString::number(lineno)); + + QString s = src; + setText(4, s.replace( QRegExp("\t"), " " )); + + updateGroup(); + updateCost(); +} + +// for call lines +SourceItem::SourceItem(SourceView* sv, QListViewItem* parent, + int fileno, unsigned int lineno, + TraceLine* line, TraceLineCall* lineCall) + : QListViewItem(parent) +{ + _view = sv; + _lineno = lineno; + _fileno = fileno; + _inside = true; + _line = line; + _lineCall = lineCall; + _lineJump = 0; + + //qDebug("SourceItem: (file %d, line %d) Linecall to %s", + // fileno, lineno, _lineCall->call()->called()->prettyName().ascii()); + + SubCost cc = _lineCall->callCount(); + QString templ = " "; + if (cc==0) + templ += i18n("Active call to '%1'"); + else + templ += i18n("%n call to '%1'", "%n calls to '%1'", cc); + + QString callStr = templ.arg(_lineCall->call()->calledName()); + TraceFunction* calledF = _lineCall->call()->called(); + calledF->addPrettyLocation(callStr); + + setText(4, callStr); + + updateGroup(); + updateCost(); +} + +// for jump lines +SourceItem::SourceItem(SourceView* sv, QListViewItem* parent, + int fileno, unsigned int lineno, + TraceLine* line, TraceLineJump* lineJump) + : QListViewItem(parent) +{ + _view = sv; + _lineno = lineno; + _fileno = fileno; + _inside = true; + _line = line; + _lineCall = 0; + _lineJump = lineJump; + + //qDebug("SourceItem: (file %d, line %d) Linecall to %s", + // fileno, lineno, _lineCall->call()->called()->prettyName().ascii()); + + QString to; + if (_lineJump->lineTo()->functionSource() == _line->functionSource()) + to = _lineJump->lineTo()->name(); + else + to = _lineJump->lineTo()->prettyName(); + + QString jStr; + if (_lineJump->isCondJump()) + jStr = i18n("Jump %1 of %2 times to %3") + .arg(_lineJump->followedCount().pretty()) + .arg(_lineJump->executedCount().pretty()) + .arg(to); + else + jStr = i18n("Jump %1 times to %2") + .arg(_lineJump->executedCount().pretty()) + .arg(to); + + setText(4, jStr); +} + + +void SourceItem::updateGroup() +{ + if (!_lineCall) return; + + TraceFunction* f = _lineCall->call()->called(); + QColor c = Configuration::functionColor(_view->groupType(), f); + setPixmap(4, colorPixmap(10, 10, c)); +} + +void SourceItem::updateCost() +{ + _pure = SubCost(0); + _pure2 = SubCost(0); + + if (!_line) return; + if (_lineJump) return; + + TraceCost* lineCost = _lineCall ? (TraceCost*)_lineCall : (TraceCost*)_line; + + // don't show any cost inside of cycles + if (_lineCall && + ((_lineCall->call()->inCycle()>0) || + (_lineCall->call()->isRecursion()>0))) { + QString str; + QPixmap p; + + QString icon = "undo"; + KIconLoader* loader = KApplication::kApplication()->iconLoader(); + p= loader->loadIcon(icon, KIcon::Small, 0, + KIcon::DefaultState, 0, true); + if (p.isNull()) + str = i18n("(cycle)"); + + setText(1, str); + setPixmap(1, p); + setText(2, str); + setPixmap(2, p); + return; + } + + TraceCost* totalCost; + if (Configuration::showExpanded()) + totalCost = _line->functionSource()->function()->inclusive(); + else + totalCost = _line->functionSource()->function()->data(); + + TraceCostType* ct = _view->costType(); + _pure = ct ? lineCost->subCost(ct) : SubCost(0); + if (_pure == 0) { + setText(1, QString::null); + setPixmap(1, QPixmap()); + } + else { + double total = totalCost->subCost(ct); + double pure = 100.0 * _pure / total; + + if (Configuration::showPercentage()) + setText(1, QString("%1") + .arg(pure, 0, 'f', Configuration::percentPrecision())); + else + setText(1, _pure.pretty()); + + setPixmap(1, costPixmap(ct, lineCost, total, false)); + } + + TraceCostType* ct2 = _view->costType2(); + _pure2 = ct2 ? lineCost->subCost(ct2) : SubCost(0); + if (_pure2 == 0) { + setText(2, QString::null); + setPixmap(2, QPixmap()); + } + else { + double total = totalCost->subCost(ct2); + double pure2 = 100.0 * _pure2 / total; + + if (Configuration::showPercentage()) + setText(2, QString("%1") + .arg(pure2, 0, 'f', Configuration::percentPrecision())); + else + setText(2, _pure2.pretty()); + + setPixmap(2, costPixmap(ct2, lineCost, total, false)); + } +} + + +int SourceItem::compare(QListViewItem * i, int col, bool ascending ) const +{ + const SourceItem* si1 = this; + const SourceItem* si2 = (SourceItem*) i; + + // we always want descending order + if (((col>0) && ascending) || + ((col==0) && !ascending) ) { + si1 = si2; + si2 = this; + } + + if (col==1) { + if (si1->_pure < si2->_pure) return -1; + if (si1->_pure > si2->_pure) return 1; + return 0; + } + if (col==2) { + if (si1->_pure2 < si2->_pure2) return -1; + if (si1->_pure2 > si2->_pure2) return 1; + return 0; + } + if (col==0) { + // Sort file numbers + if (si1->_fileno < si2->_fileno) return -1; + if (si1->_fileno > si2->_fileno) return 1; + + // Sort line numbers + if (si1->_lineno < si2->_lineno) return -1; + if (si1->_lineno > si2->_lineno) return 1; + + // Same line: code gets above calls/jumps + if (!si1->_lineCall && !si1->_lineJump) return -1; + if (!si2->_lineCall && !si2->_lineJump) return 1; + + // calls above jumps + if (si1->_lineCall && !si2->_lineCall) return -1; + if (si2->_lineCall && !si1->_lineCall) return 1; + + if (si1->_lineCall && si2->_lineCall) { + // Two calls: desending sort according costs + if (si1->_pure < si2->_pure) return 1; + if (si1->_pure > si2->_pure) return -1; + + // Two calls: sort according function names + TraceFunction* f1 = si1->_lineCall->call()->called(); + TraceFunction* f2 = si2->_lineCall->call()->called(); + if (f1->prettyName() > f2->prettyName()) return 1; + return -1; + } + + // Two jumps: descending sort according target line + if (si1->_lineJump->lineTo()->lineno() < + si2->_lineJump->lineTo()->lineno()) + return -1; + if (si1->_lineJump->lineTo()->lineno() > + si2->_lineJump->lineTo()->lineno()) + return 1; + return 0; + } + return QListViewItem::compare(i, col, ascending); +} + +void SourceItem::paintCell( QPainter *p, const QColorGroup &cg, + int column, int width, int alignment ) +{ + QColorGroup _cg( cg ); + + if ( !_inside || ((column==1) || (column==2))) + _cg.setColor( QColorGroup::Base, cg.button() ); + else if ((_lineCall || _lineJump) && column>2) + _cg.setColor( QColorGroup::Base, cg.midlight() ); + + if (column == 3) + paintArrows(p, _cg, width); + else + QListViewItem::paintCell( p, _cg, column, width, alignment ); +} + +void SourceItem::setJumpArray(const QMemArray& a) +{ + _jump.duplicate(a); +} + +void SourceItem::paintArrows(QPainter *p, const QColorGroup &cg, int width) +{ + QListView *lv = listView(); + if ( !lv ) return; + SourceView* sv = (SourceView*) lv; + + const BackgroundMode bgmode = lv->viewport()->backgroundMode(); + const QColorGroup::ColorRole crole + = QPalette::backgroundRoleFromMode( bgmode ); + if ( cg.brush( crole ) != lv->colorGroup().brush( crole ) ) + p->fillRect( 0, 0, width, height(), cg.brush( crole ) ); + else + sv->paintEmptyArea( p, QRect( 0, 0, width, height() ) ); + + if ( isSelected() && lv->allColumnsShowFocus() ) + p->fillRect( 0, 0, width, height(), cg.brush( QColorGroup::Highlight ) ); + + int marg = lv->itemMargin(); + int yy = height()/2, y1, y2; + QColor c; + + int start = -1, end = -1; + + // draw line borders, detect start/stop of a line + for(int i=0;i< (int)_jump.size();i++) { + if (_jump[i] == 0) continue; + + y1 = 0; + y2 = height(); + if (_lineJump && + (_lineJump->lineTo() == _jump[i]->lineTo()) && + (_jump[i]->lineFrom()->lineno() == _lineno)) { + + if (start<0) start = i; + if (_lineJump == _jump[i]) { + if (_jump[i]->lineTo()->lineno() <= _lineno) + y2 = yy; + else + y1 = yy; + } + } + else if (!_lineJump && !_lineCall && + (_jump[i]->lineTo()->lineno() == _lineno)) { + if (end<0) end = i; + if (_jump[i]->lineFrom()->lineno() < _lineno) + y2 = yy; + else + y1 = yy; + } + + c = _jump[i]->isCondJump() ? red : blue; + p->fillRect( marg + 6*i, y1, 4, y2, c); + p->setPen(c.light()); + p->drawLine( marg + 6*i, y1, marg + 6*i, y2); + p->setPen(c.dark()); + p->drawLine( marg + 6*i +3, y1, marg + 6*i +3, y2); + } + + // draw start/stop horizontal line + int x, y = yy-2, w, h = 4; + if (start >= 0) { + c = _jump[start]->isCondJump() ? red : blue; + x = marg + 6*start; + w = 6*(sv->arrowLevels() - start) + 10; + p->fillRect( x, y, w, h, c); + p->setPen(c.light()); + p->drawLine(x, y, x+w-1, y); + p->drawLine(x, y, x, y+h-1); + p->setPen(c.dark()); + p->drawLine(x+w-1, y, x+w-1, y+h-1); + p->drawLine(x+1, y+h-1, x+w-1, y+h-1); + } + if (end >= 0) { + c = _jump[end]->isCondJump() ? red : blue; + x = marg + 6*end; + w = 6*(sv->arrowLevels() - end) + 10; + + QPointArray a; + a.putPoints(0, 7, x, y+h, + x,y, x+w-8, y, x+w-8, y-2, + x+w, yy, + x+w-8, y+h+2, x+w-8, y+h); + p->setBrush(c); + p->drawConvexPolygon(a); + + p->setPen(c.light()); + p->drawPolyline(a, 0, 5); + p->setPen(c.dark()); + p->drawPolyline(a, 4, 2); + p->setPen(c.light()); + p->drawPolyline(a, 5, 2); + p->setPen(c.dark()); + p->drawPolyline(a, 6, 2); + } + + // draw inner vertical line for start/stop + // this overwrites borders of horizontal line + for(int i=0;i< (int)_jump.size();i++) { + if (_jump[i] == 0) continue; + + c = _jump[i]->isCondJump() ? red : blue; + + if (_jump[i]->lineFrom()->lineno() == _lineno) { + bool drawUp = true; + if (_jump[i]->lineTo()->lineno() == _lineno) + if (start<0) drawUp = false; + if (_jump[i]->lineTo()->lineno() > _lineno) drawUp = false; + if (drawUp) + p->fillRect( marg + 6*i +1, 0, 2, yy, c); + else + p->fillRect( marg + 6*i +1, yy, 2, height()-yy, c); + } + else if (_jump[i]->lineTo()->lineno() == _lineno) { + if (end<0) end = i; + if (_jump[i]->lineFrom()->lineno() < _lineno) + p->fillRect( marg + 6*i +1, 0, 2, yy, c); + else + p->fillRect( marg + 6*i +1, yy, 2, height()-yy, c); + } + } + +} + +int SourceItem::width( const QFontMetrics& fm, + const QListView* lv, int c ) const +{ + if (c != 3) return QListViewItem::width(fm, lv, c); + + SourceView* sv = (SourceView*) lv; + int levels = sv->arrowLevels(); + + if (levels == 0) return 0; + + // 10 pixels for the arrow + return 10 + 6*levels + lv->itemMargin() * 2; +} + diff --git a/kcachegrind/kcachegrind/sourceitem.h b/kcachegrind/kcachegrind/sourceitem.h new file mode 100644 index 00000000..b1a3aec4 --- /dev/null +++ b/kcachegrind/kcachegrind/sourceitem.h @@ -0,0 +1,84 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Items of source view. + */ + +#ifndef SOURCEITEM_H +#define SOURCEITEM_H + +#include +#include "tracedata.h" + +class SourceView; + +class SourceItem: public QListViewItem +{ +public: + // for source lines + SourceItem(SourceView* sv, QListView* parent, + int fileno, unsigned int lineno, + bool inside, const QString& src, + TraceLine* line = 0); + + // for call lines + SourceItem(SourceView* sv, QListViewItem* parent, + int fileno, unsigned int lineno, + TraceLine* line, TraceLineCall* lineCall); + + // for jump lines + SourceItem(SourceView* sv, QListViewItem* parent, + int fileno, unsigned int lineno, + TraceLine* line, TraceLineJump* lineJump); + + uint lineno() const { return _lineno; } + int fileNumber() const { return _fileno; } + bool inside() const { return _inside; } + TraceLine* line() const { return _line; } + TraceLineCall* lineCall() const { return _lineCall; } + TraceLineJump* lineJump() const { return _lineJump; } + + int compare(QListViewItem * i, int col, bool ascending ) const; + + void paintCell( QPainter *p, const QColorGroup &cg, + int column, int width, int alignment ); + int width( const QFontMetrics& fm, + const QListView* lv, int c ) const; + void updateGroup(); + void updateCost(); + + // arrow lines + void setJumpArray(const QMemArray& a); + +protected: + void paintArrows(QPainter *p, const QColorGroup &cg, int width); + QMemArray _jump; + +private: + SourceView* _view; + SubCost _pure, _pure2; + uint _lineno; + int _fileno; // for line sorting (even with multiple files) + bool _inside; + TraceLine* _line; + TraceLineJump* _lineJump; + TraceLineCall* _lineCall; +}; + +#endif diff --git a/kcachegrind/kcachegrind/sourceview.cpp b/kcachegrind/kcachegrind/sourceview.cpp new file mode 100644 index 00000000..e5102828 --- /dev/null +++ b/kcachegrind/kcachegrind/sourceview.cpp @@ -0,0 +1,813 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Source View + */ + +#include +#include +#include +#include +#include +#include + +#include "configuration.h" +#include "sourceitem.h" +#include "sourceview.h" + + +// +// SourceView +// + + +SourceView::SourceView(TraceItemView* parentView, + QWidget* parent, const char* name) + : QListView(parent, name), TraceItemView(parentView) +{ + _inSelectionUpdate = false; + + _arrowLevels = 0; + _lowList.setSortLow(true); + _highList.setSortLow(false); + + addColumn( i18n( "#" ) ); + addColumn( i18n( "Cost" ) ); + addColumn( i18n( "Cost 2" ) ); + addColumn( "" ); + addColumn( i18n( "Source (unknown)" ) ); + + setAllColumnsShowFocus(true); + setColumnAlignment(0, Qt::AlignRight); + setColumnAlignment(1, Qt::AlignRight); + setColumnAlignment(2, Qt::AlignRight); + setResizeMode(QListView::LastColumn); + + connect(this, + SIGNAL(contextMenuRequested(QListViewItem*, const QPoint &, int)), + SLOT(context(QListViewItem*, const QPoint &, int))); + + connect(this, + SIGNAL(selectionChanged(QListViewItem*)), + SLOT(selectedSlot(QListViewItem*))); + + connect(this, + SIGNAL(doubleClicked(QListViewItem*)), + SLOT(activatedSlot(QListViewItem*))); + + connect(this, + SIGNAL(returnPressed(QListViewItem*)), + SLOT(activatedSlot(QListViewItem*))); + + QWhatsThis::add( this, whatsThis()); +} + +void SourceView::paintEmptyArea( QPainter * p, const QRect & r) +{ + QListView::paintEmptyArea(p, r); +} + + +QString SourceView::whatsThis() const +{ + return i18n( "Annotated Source" + "

The annotated source list shows the " + "source lines of the current selected function " + "together with (self) cost spent while executing the " + "code of this source line. If there was a call " + "in a source line, lines with details on the " + "call happening are inserted into the source: " + "the cost spent inside of the call, the " + "number of calls happening, and the call destination.

" + "

Select a inserted call information line to " + "make the destination function current.

"); +} + +void SourceView::context(QListViewItem* i, const QPoint & p, int c) +{ + QPopupMenu popup; + + // Menu entry: + TraceLineCall* lc = i ? ((SourceItem*) i)->lineCall() : 0; + TraceLineJump* lj = i ? ((SourceItem*) i)->lineJump() : 0; + TraceFunction* f = lc ? lc->call()->called() : 0; + TraceLine* line = lj ? lj->lineTo() : 0; + + if (f) { + QString name = f->name(); + if ((int)name.length()>Configuration::maxSymbolLength()) + name = name.left(Configuration::maxSymbolLength()) + "..."; + popup.insertItem(i18n("Go to '%1'").arg(name), 93); + popup.insertSeparator(); + } + else if (line) { + popup.insertItem(i18n("Go to Line %1").arg(line->name()), 93); + popup.insertSeparator(); + } + + if ((c == 1) || (c == 2)) { + addCostMenu(&popup); + popup.insertSeparator(); + } + addGoMenu(&popup); + + int r = popup.exec(p); + if (r == 93) { + if (f) activated(f); + if (line) activated(line); + } +} + + +void SourceView::selectedSlot(QListViewItem * i) +{ + if (!i) return; + // programatically selected items are not signalled + if (_inSelectionUpdate) return; + + TraceLineCall* lc = ((SourceItem*) i)->lineCall(); + TraceLineJump* lj = ((SourceItem*) i)->lineJump(); + + if (!lc && !lj) { + TraceLine* l = ((SourceItem*) i)->line(); + if (l) { + _selectedItem = l; + selected(l); + } + return; + } + + TraceFunction* f = lc ? lc->call()->called() : 0; + if (f) { + _selectedItem = f; + selected(f); + } + else { + TraceLine* line = lj ? lj->lineTo() : 0; + if (line) { + _selectedItem = line; + selected(line); + } + } +} + +void SourceView::activatedSlot(QListViewItem * i) +{ + if (!i) return; + TraceLineCall* lc = ((SourceItem*) i)->lineCall(); + TraceLineJump* lj = ((SourceItem*) i)->lineJump(); + + if (!lc && !lj) { + TraceLine* l = ((SourceItem*) i)->line(); + if (l) activated(l); + return; + } + + TraceFunction* f = lc ? lc->call()->called() : 0; + if (f) activated(f); + else { + TraceLine* line = lj ? lj->lineTo() : 0; + if (line) activated(line); + } +} + +TraceItem* SourceView::canShow(TraceItem* i) +{ + TraceItem::CostType t = i ? i->type() : TraceItem::NoCostType; + TraceFunction* f = 0; + + switch(t) { + case TraceItem::Function: + f = (TraceFunction*) i; + break; + + case TraceItem::Instr: + f = ((TraceInstr*)i)->function(); + select(i); + break; + + case TraceItem::Line: + f = ((TraceLine*)i)->functionSource()->function(); + select(i); + break; + + default: + break; + } + + return f; +} + +void SourceView::doUpdate(int changeType) +{ + // Special case ? + if (changeType == selectedItemChanged) { + + if (!_selectedItem) { + clearSelection(); + return; + } + + TraceLine* sLine = 0; + if (_selectedItem->type() == TraceItem::Line) + sLine = (TraceLine*) _selectedItem; + if (_selectedItem->type() == TraceItem::Instr) + sLine = ((TraceInstr*)_selectedItem)->line(); + + SourceItem* si = (SourceItem*)QListView::selectedItem(); + if (si) { + if (si->line() == sLine) return; + if (si->lineCall() && + (si->lineCall()->call()->called() == _selectedItem)) return; + } + + QListViewItem *item, *item2; + for (item = firstChild();item;item = item->nextSibling()) { + si = (SourceItem*)item; + if (si->line() == sLine) { + ensureItemVisible(item); + _inSelectionUpdate = true; + setCurrentItem(item); + _inSelectionUpdate = false; + break; + } + item2 = item->firstChild(); + for (;item2;item2 = item2->nextSibling()) { + si = (SourceItem*)item2; + if (!si->lineCall()) continue; + if (si->lineCall()->call()->called() == _selectedItem) { + ensureItemVisible(item2); + _inSelectionUpdate = true; + setCurrentItem(item2); + _inSelectionUpdate = false; + break; + } + } + if (item2) break; + } + return; + } + + if (changeType == groupTypeChanged) { + QListViewItem *item, *item2; + for (item = firstChild();item;item = item->nextSibling()) + for (item2 = item->firstChild();item2;item2 = item2->nextSibling()) + ((SourceItem*)item2)->updateGroup(); + } + + refresh(); +} + +void SourceView::refresh() +{ + clear(); + setColumnWidth(0, 20); + setColumnWidth(1, 50); + setColumnWidth(2, _costType2 ? 50:0); + setColumnWidth(3, 0); // arrows, defaults to invisible + setSorting(0); // always reset to line number sort + if (_costType) + setColumnText(1, _costType->name()); + if (_costType2) + setColumnText(2, _costType2->name()); + + _arrowLevels = 0; + + if (!_data || !_activeItem) { + setColumnText(4, i18n("(No Source)")); + return; + } + + TraceItem::CostType t = _activeItem->type(); + TraceFunction* f = 0; + if (t == TraceItem::Function) f = (TraceFunction*) _activeItem; + if (t == TraceItem::Instr) { + f = ((TraceInstr*)_activeItem)->function(); + if (!_selectedItem) _selectedItem = _activeItem; + } + if (t == TraceItem::Line) { + f = ((TraceLine*)_activeItem)->functionSource()->function(); + if (!_selectedItem) _selectedItem = _activeItem; + } + + if (!f) return; + + // Allow resizing of column 2 + setColumnWidthMode(2, QListView::Maximum); + + TraceFunctionSource* mainSF = f->sourceFile(); + + // skip first source if there's no debug info and there are more sources + // (this is for a bug in GCC 2.95.x giving unknown source for prologs) + if (mainSF && + (mainSF->firstLineno() == 0) && + (mainSF->lastLineno() == 0) && + (f->sourceFiles().count()>1) ) { + // skip + } + else + fillSourceFile(mainSF, 0); + + TraceFunctionSource* sf; + int fileno = 1; + TraceFunctionSourceList l = f->sourceFiles(); + for (sf=l.first();sf;sf=l.next(), fileno++) + if (sf != mainSF) + fillSourceFile(sf, fileno); + + if (!_costType2) { + setColumnWidthMode(2, QListView::Manual); + setColumnWidth(2, 0); + } +} + + +// helper for fillSourceList: +// search recursive for a file, starting from a base dir +static bool checkFileExistance(QString& dir, const QString& name) +{ + // we leave this in... + qDebug("Checking %s/%s", dir.ascii(), name.ascii()); + + if (QFile::exists(dir + "/" + name)) return true; + + // check in subdirectories + QDir d(dir); + d.setFilter( QDir::Dirs | QDir::NoSymLinks ); + d.setSorting( QDir::Unsorted ); + QStringList subdirs = d.entryList(); + QStringList::Iterator it =subdirs.begin(); + for(; it != subdirs.end(); ++it ) { + if (*it == "." || *it == ".." || *it == "CVS") continue; + + dir = d.filePath(*it); + if (checkFileExistance(dir, name)) return true; + } + return false; +} + + +void SourceView::updateJumpArray(uint lineno, SourceItem* si, + bool ignoreFrom, bool ignoreTo) +{ + TraceLineJump* lj; + uint lowLineno, highLineno; + int iEnd = -1, iStart = -1; + + if (0) qDebug("updateJumpArray(line %d, jump to %s)", + lineno, + si->lineJump() + ? si->lineJump()->lineTo()->name().ascii() : "?" ); + + + lj=_lowList.current(); + while(lj) { + lowLineno = lj->lineFrom()->lineno(); + if (lj->lineTo()->lineno() < lowLineno) + lowLineno = lj->lineTo()->lineno(); + + if (lowLineno > lineno) break; + + if (ignoreFrom && (lowLineno < lj->lineTo()->lineno())) break; + if (ignoreTo && (lowLineno < lj->lineFrom()->lineno())) break; + + if (si->lineJump() && (lj != si->lineJump())) break; + + int asize = (int)_jump.size(); +#if 0 + for(iStart=0;iStartlineTo() == lj->lineTo())) break; +#else + iStart = asize; +#endif + + if (iStart == asize) { + for(iStart=0;iStart _arrowLevels) _arrowLevels = asize; + } + + if (0) qDebug(" start %d (%s to %s)", + iStart, + lj->lineFrom()->name().ascii(), + lj->lineTo()->name().ascii()); + + _jump[iStart] = lj; + } + lj=_lowList.next(); + } + + si->setJumpArray(_jump); + + lj=_highList.current(); + while(lj) { + highLineno = lj->lineFrom()->lineno(); + if (lj->lineTo()->lineno() > highLineno) { + highLineno = lj->lineTo()->lineno(); + if (ignoreTo) break; + } + else if (ignoreFrom) break; + + if (highLineno > lineno) break; + + for(iEnd=0;iEnd< (int)_jump.size();iEnd++) + if (_jump[iEnd] == lj) break; + if (iEnd == (int)_jump.size()) { + qDebug("LineView: no jump start for end at %x ?", highLineno); + iEnd = -1; + } + lj=_highList.next(); + + if (0 && (iEnd>=0)) + qDebug(" end %d (%s to %s)", + iEnd, + _jump[iEnd]->lineFrom()->name().ascii(), + _jump[iEnd]->lineTo()->name().ascii()); + + if (0 && lj) qDebug("next end: %s to %s", + lj->lineFrom()->name().ascii(), + lj->lineTo()->name().ascii()); + + if (highLineno > lineno) + break; + else { + if (iEnd>=0) _jump[iEnd] = 0; + iEnd = -1; + } + } + if (iEnd>=0) _jump[iEnd] = 0; +} + + +/* If sourceList is empty we set the source file name into the header, + * else this code is of a inlined function, and we add "inlined from..." + */ +void SourceView::fillSourceFile(TraceFunctionSource* sf, int fileno) +{ + if (!sf) return; + + if (0) qDebug("Selected Item %s", + _selectedItem ? _selectedItem->name().ascii() : "(none)"); + + TraceLineMap::Iterator lineIt, lineItEnd; + int nextCostLineno = 0, lastCostLineno = 0; + + bool validSourceFile = (!sf->file()->name().isEmpty()); + + TraceLine* sLine = 0; + if (_selectedItem) { + if (_selectedItem->type() == TraceItem::Line) + sLine = (TraceLine*) _selectedItem; + if (_selectedItem->type() == TraceItem::Instr) + sLine = ((TraceInstr*)_selectedItem)->line(); + } + + if (validSourceFile) { + TraceLineMap* lineMap = sf->lineMap(); + if (lineMap) { + lineIt = lineMap->begin(); + lineItEnd = lineMap->end(); + // get first line with cost of selected type + while(lineIt != lineItEnd) { + if (&(*lineIt) == sLine) break; + if ((*lineIt).hasCost(_costType)) break; + if (_costType2 && (*lineIt).hasCost(_costType2)) break; + ++lineIt; + } + + nextCostLineno = (lineIt == lineItEnd) ? 0 : (*lineIt).lineno(); + if (nextCostLineno<0) { + kdError() << "SourceView::fillSourceFile: Negative line number " + << nextCostLineno << endl + << " Function '" << sf->function()->name() << "'" << endl + << " File '" << sf->file()->name() << "'" << endl; + nextCostLineno = 0; + } + + } + + if (nextCostLineno == 0) { + new SourceItem(this, this, fileno, 0, false, + i18n("There is no cost of current selected type associated")); + new SourceItem(this, this, fileno, 1, false, + i18n("with any source line of this function in file")); + new SourceItem(this, this, fileno, 2, false, + QString(" '%1'").arg(sf->function()->prettyName())); + new SourceItem(this, this, fileno, 3, false, + i18n("Thus, no annotated source can be shown.")); + return; + } + } + + QString filename = sf->file()->shortName(); + QString dir = sf->file()->directory(); + if (!dir.isEmpty()) + filename = dir + "/" + filename; + + if (nextCostLineno>0) { + // we have debug info... search for source file + if (!QFile::exists(filename)) { + QStringList list = Configuration::sourceDirs(_data, + sf->function()->object()); + QStringList::Iterator it; + + for ( it = list.begin(); it != list.end(); ++it ) { + dir = *it; + if (checkFileExistance(dir, sf->file()->shortName())) break; + } + + if (it == list.end()) + nextCostLineno = 0; + else { + filename = dir + "/" + sf->file()->shortName(); + // no need to search again + sf->file()->setDirectory(dir); + } + } + } + + // do it here, because the source directory could have been set before + if (childCount()==0) { + setColumnText(4, validSourceFile ? + i18n("Source ('%1')").arg(filename) : + i18n("Source (unknown)")); + } + else { + new SourceItem(this, this, fileno, 0, true, + validSourceFile ? + i18n("--- Inlined from '%1' ---").arg(filename) : + i18n("--- Inlined from unknown source ---")); + } + + if (nextCostLineno == 0) { + new SourceItem(this, this, fileno, 0, false, + i18n("There is no source available for the following function:")); + new SourceItem(this, this, fileno, 1, false, + QString(" '%1'").arg(sf->function()->prettyName())); + if (sf->file()->name().isEmpty()) { + new SourceItem(this, this, fileno, 2, false, + i18n("This is because no debug information is present.")); + new SourceItem(this, this, fileno, 3, false, + i18n("Recompile source and redo the profile run.")); + if (sf->function()->object()) { + new SourceItem(this, this, fileno, 4, false, + i18n("The function is located in this ELF object:")); + new SourceItem(this, this, fileno, 5, false, + QString(" '%1'") + .arg(sf->function()->object()->prettyName())); + } + } + else { + new SourceItem(this, this, fileno, 2, false, + i18n("This is because its source file cannot be found:")); + new SourceItem(this, this, fileno, 3, false, + QString(" '%1'").arg(sf->file()->name())); + new SourceItem(this, this, fileno, 4, false, + i18n("Add the folder of this file to the source folder list.")); + new SourceItem(this, this, fileno, 5, false, + i18n("The list can be found in the configuration dialog.")); + } + return; + } + + + // initialisation for arrow drawing + // create sorted list of jumps (for jump arrows) + TraceLineMap::Iterator it = lineIt, nextIt; + _lowList.clear(); + _highList.clear(); + while(1) { + + nextIt = it; + ++nextIt; + while(nextIt != lineItEnd) { + if (&(*nextIt) == sLine) break; + if ((*nextIt).hasCost(_costType)) break; + if (_costType2 && (*nextIt).hasCost(_costType2)) break; + ++nextIt; + } + + TraceLineJumpList jlist = (*it).lineJumps(); + TraceLineJump* lj; + for (lj=jlist.first();lj;lj=jlist.next()) { + if (lj->executedCount()==0) continue; + // skip jumps to next source line with cost + //if (lj->lineTo() == &(*nextIt)) continue; + + _lowList.append(lj); + _highList.append(lj); + } + it = nextIt; + if (it == lineItEnd) break; + } + _lowList.sort(); + _highList.sort(); + _lowList.first(); // iterators to list start + _highList.first(); + _jump.resize(0); + + + char buf[256]; + bool inside = false, skipLineWritten = true; + int readBytes; + int fileLineno = 0; + SubCost most = 0; + + TraceLine* currLine; + SourceItem *si, *si2, *item = 0, *first = 0, *selected = 0; + QFile file(filename); + if (!file.open(IO_ReadOnly)) return; + while (1) { + readBytes=file.readLine(buf, sizeof( buf )); + if (readBytes<=0) { + // for nice empty 4 lines after function with EOF + buf[0] = 0; + } + + if (readBytes >= (int) sizeof( buf )) { + qDebug("%s:%d Line too long\n", + sf->file()->name().ascii(), fileLineno); + } + else if ((readBytes>0) && (buf[readBytes-1] == '\n')) + buf[readBytes-1] = 0; + + + // keep fileLineno inside [lastCostLineno;nextCostLineno] + fileLineno++; + if (fileLineno == nextCostLineno) { + currLine = &(*lineIt); + + // get next line with cost of selected type + ++lineIt; + while(lineIt != lineItEnd) { + if (&(*lineIt) == sLine) break; + if ((*lineIt).hasCost(_costType)) break; + if (_costType2 && (*lineIt).hasCost(_costType2)) break; + ++lineIt; + } + + lastCostLineno = nextCostLineno; + nextCostLineno = (lineIt == lineItEnd) ? 0 : (*lineIt).lineno(); + } + else + currLine = 0; + + // update inside + if (!inside) { + if (currLine) inside = true; + } + else { + if ( (fileLineno > lastCostLineno) && + ((nextCostLineno == 0) || + (fileLineno < nextCostLineno - Configuration::noCostInside()) )) + inside = false; + } + + int context = Configuration::context(); + + if ( ((lastCostLineno==0) || (fileLineno > lastCostLineno + context)) && + ((nextCostLineno==0) || (fileLineno < nextCostLineno - context))) { + if (lineIt == lineItEnd) break; + + if (!skipLineWritten) { + skipLineWritten = true; + // a "skipping" line: print "..." instead of a line number + strcpy(buf,"..."); + } + else + continue; + } + else + skipLineWritten = false; + + si = new SourceItem(this, this, + fileno, fileLineno, inside, QString(buf), + currLine); + + if (!currLine) continue; + + if (!selected && (currLine == sLine)) selected = si; + if (!first) first = si; + + if (currLine->subCost(_costType) > most) { + item = si; + most = currLine->subCost(_costType); + } + + si->setOpen(true); + TraceLineCallList list = currLine->lineCalls(); + TraceLineCall* lc; + for (lc=list.first();lc;lc=list.next()) { + if ((lc->subCost(_costType)==0) && + (lc->subCost(_costType2)==0)) continue; + + if (lc->subCost(_costType) > most) { + item = si; + most = lc->subCost(_costType); + } + + si2 = new SourceItem(this, si, fileno, fileLineno, currLine, lc); + + if (!selected && (lc->call()->called() == _selectedItem)) + selected = si2; + } + + TraceLineJumpList jlist = currLine->lineJumps(); + TraceLineJump* lj; + for (lj=jlist.first();lj;lj=jlist.next()) { + if (lj->executedCount()==0) continue; + + new SourceItem(this, si, fileno, fileLineno, currLine, lj); + } + } + + if (selected) item = selected; + if (item) first = item; + if (first) { + ensureItemVisible(first); + _inSelectionUpdate = true; + setCurrentItem(first); + _inSelectionUpdate = false; + } + + file.close(); + + // for arrows: go down the list according to list sorting + sort(); + QListViewItem *item1, *item2; + for (item1=firstChild();item1;item1 = item1->nextSibling()) { + si = (SourceItem*)item1; + updateJumpArray(si->lineno(), si, true, false); + + for (item2=item1->firstChild();item2;item2 = item2->nextSibling()) { + si2 = (SourceItem*)item2; + if (si2->lineJump()) + updateJumpArray(si->lineno(), si2, false, true); + else + si2->setJumpArray(_jump); + } + } + + if (arrowLevels()) + setColumnWidth(3, 10 + 6*arrowLevels() + itemMargin() * 2); + else + setColumnWidth(3, 0); +} + + +void SourceView::updateSourceItems() +{ + setColumnWidth(1, 50); + setColumnWidth(2, _costType2 ? 50:0); + // Allow resizing of column 2 + setColumnWidthMode(2, QListView::Maximum); + + if (_costType) + setColumnText(1, _costType->name()); + if (_costType2) + setColumnText(2, _costType2->name()); + + SourceItem* si; + QListViewItem* item = firstChild(); + for (;item;item = item->nextSibling()) { + si = (SourceItem*)item; + TraceLine* l = si->line(); + if (!l) continue; + + si->updateCost(); + + QListViewItem *next, *i = si->firstChild(); + for (;i;i = next) { + next = i->nextSibling(); + ((SourceItem*)i)->updateCost(); + } + } + + if (!_costType2) { + setColumnWidthMode(2, QListView::Manual); + setColumnWidth(2, 0); + } +} + +#include "sourceview.moc" diff --git a/kcachegrind/kcachegrind/sourceview.h b/kcachegrind/kcachegrind/sourceview.h new file mode 100644 index 00000000..4836032a --- /dev/null +++ b/kcachegrind/kcachegrind/sourceview.h @@ -0,0 +1,70 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Source View + */ + +#ifndef SOURCEVIEW_H +#define SOURCEVIEW_H + +#include +#include "traceitemview.h" + +class SourceItem; + +class SourceView : public QListView, public TraceItemView +{ + friend class SourceItem; + + Q_OBJECT + +public: + SourceView(TraceItemView* parentView, + QWidget* parent = 0, const char* name = 0); + + QWidget* widget() { return this; } + QString whatsThis() const; + +protected: + int arrowLevels() { return _arrowLevels; } + void paintEmptyArea( QPainter *, const QRect & ); + +private slots: + void context(QListViewItem*, const QPoint &, int); + void selectedSlot(QListViewItem *); + void activatedSlot(QListViewItem *); + +private: + TraceItem* canShow(TraceItem*); + void doUpdate(int); + void refresh(); + void updateJumpArray(uint,SourceItem*,bool,bool); + void fillSourceFile(TraceFunctionSource*, int); + void updateSourceItems(); + + bool _inSelectionUpdate; + + // arrows + int _arrowLevels; + // temporary needed on creation... + QMemArray _jump; + TraceLineJumpList _lowList, _highList; +}; + +#endif diff --git a/kcachegrind/kcachegrind/stackbrowser.cpp b/kcachegrind/kcachegrind/stackbrowser.cpp new file mode 100644 index 00000000..783edf99 --- /dev/null +++ b/kcachegrind/kcachegrind/stackbrowser.cpp @@ -0,0 +1,417 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include + +#include "stackbrowser.h" + +// Stack + +Stack::Stack(TraceFunction* top, TraceCallList calls) +{ + _refCount = 0; + _top = top; + _calls = calls; + + extendBottom(); +} + +Stack::Stack(TraceFunction* f) +{ + _refCount = 0; + _top = f; + + extendBottom(); + extendTop(); +} + +void Stack::extendBottom() +{ + TraceCallList l; + TraceCall *c, *call; + SubCost most; + TraceFunction* f; + + if (_calls.last()) + f = _calls.last()->called(); + else + f = _top; + + if (!f) return; + // don't follow calls from cycles + if (f->cycle() == f) return; + + + int max = 30; + + // try to extend to lower stack frames + while (f && (max-- >0)) { + l = f->callings(); + call = 0; + most = 0; + for (c=l.first();c;c=l.next()) { + // no cycle calls in stack: could be deleted without notice + if (c->called()->cycle() == c->called()) continue; + // no simple recursions + if (c->called() == _top) continue; + + if (c->called()->name().isEmpty()) continue; + SubCost sc = c->subCost(0); // FIXME + if (sc == 0) continue; + + if (sc > most) { + most = sc; + call = c; + } + } + if (!call) + break; + + _calls.append(call); + f = call->called(); + } +} + + +void Stack::extendTop() +{ + TraceCallList l; + TraceCall *c, *call; + SubCost most; + + int max = 10; + + // don't follow calls from cycles + if (_top->cycle() == _top) return; + + // try to extend to upper stack frames + while (_top && (max-- >0)) { + l = _top->callers(); + call = 0; + most = 0; + for (c=l.first();c;c=l.next()) { + // no cycle calls in stack: could be deleted without notice + if (c->caller()->cycle() == c->caller()) continue; + // no simple recursions + if (c->caller() == _top) continue; + + if (c->caller()->name().isEmpty()) continue; + SubCost sc = c->subCost(0); // FIXME + if (sc == 0) continue; + + if (sc > most) { + most = sc; + call = c; + } + } + if (!call) + break; + + _calls.prepend(call); + _top = call->caller(); + } +} + +TraceFunction* Stack::caller(TraceFunction* fn, bool extend) +{ + TraceFunction* f; + TraceCall* c; + + if (extend && (_top == fn)) { + // extend at top + extendTop(); + f = _top; + } + + for (c=_calls.first();c;c=_calls.next()) { + f = c->called(); + if (f == fn) + return c->caller(); + } + return 0; +} + +TraceFunction* Stack::called(TraceFunction* fn, bool extend) +{ + TraceFunction* f; + TraceCall* c; + + for (c=_calls.first();c;c=_calls.next()) { + f = c->caller(); + if (f == fn) + return c->called(); + } + + if (extend && (c->called() == fn)) { + // extend at bottom + extendBottom(); + + // and search again + for (c=_calls.first();c;c=_calls.next()) { + f = c->caller(); + if (f == fn) + return c->called(); + } + } + + return 0; +} + +bool Stack::contains(TraceFunction* fn) +{ + // cycles are listed on there own + if (fn->cycle() == fn) return false; + if (_top->cycle() == _top) return false; + + if (fn == _top) + return true; + + TraceFunction* f = _top; + TraceCall* c; + + for (c=_calls.first();c;c=_calls.next()) { + f = c->called(); + if (f == fn) + return true; + } + + TraceCallList l; + + // try to extend at bottom (even if callCount 0) + l = f->callings(); + for (c=l.first();c;c=l.next()) { + f = c->called(); + if (f == fn) + break; + } + + if (c) { + _calls.append(c); + + // extend at bottom after found one + extendBottom(); + return true; + } + + // try to extend at top (even if callCount 0) + l = _top->callers(); + for (c=l.first();c;c=l.next()) { + f = c->caller(); + if (f == fn) + break; + } + + if (c) { + _calls.prepend(c); + + // extend at top after found one + extendTop(); + return true; + } + + return false; +} + +Stack* Stack::split(TraceFunction* f) +{ + TraceCallList calls = _calls; + TraceCall *c, *c2; + + // cycles are listed on there own + if (f->cycle() == f) return 0; + if (_top->cycle() == _top) return false; + + for (c=calls.first();c;c=calls.next()) { + TraceCallList l = c->called()->callings(); + for (c2=l.first();c2;c2=l.next()) { + if (c2 == c) continue; + if (c2->called() == f) + break; + } + if (c2) + break; + } + + if (!c) + return 0; + + // remove bottom part + calls.last(); + while (calls.current() && calls.current()!=c) + calls.removeLast(); + + calls.append(c2); + return new Stack(_top, calls ); +} + +QString Stack::toString() +{ + QString res = _top->name(); + TraceCall *c; + for (c=_calls.first();c;c=_calls.next()) + res += "\n > " + c->called()->name(); + + return res; +} + + +// HistoryItem + +HistoryItem::HistoryItem(Stack* stack, TraceFunction* function) +{ + _stack = stack; + _function = function; + if (_stack) + _stack->ref(); + + _last = 0; + _next = 0; + +/* + qDebug("New Stack History Item (sRef %d): %s\n %s", + _stack->refCount(), _function->name().ascii(), + _stack->toString().ascii()); +*/ +} + +HistoryItem::~HistoryItem() +{ + if (0) qDebug("Deleting Stack History Item (sRef %d): %s", + _stack->refCount(), + _function->name().ascii()); + + if (_last) + _last->_next = _next; + if (_stack) { + if (_stack->deref() == 0) + delete _stack; + } +} + + +// StackBrowser + +StackBrowser::StackBrowser() +{ + _current = 0; +} + +StackBrowser::~StackBrowser() +{ + delete _current; +} + +HistoryItem* StackBrowser::select(TraceFunction* f) +{ + if (!_current) { + Stack* s = new Stack(f); + _current = new HistoryItem(s, f); + } + else if (_current->function() != f) { + // make current item the last one + HistoryItem* item = _current; + if (item->next()) { + item = item->next(); + item->last()->setNext(0); + + while (item->next()) { + item = item->next(); + delete item->last(); + } + delete item; + } + + Stack* s = _current->stack(); + if (!s->contains(f)) { + s = s->split(f); + if (!s) + s = new Stack(f); + } + + item = _current; + _current = new HistoryItem(s, f); + item->setNext(_current); + _current->setLast(item); + } + + // qDebug("Selected %s in StackBrowser", f->name().ascii()); + + return _current; +} + +HistoryItem* StackBrowser::goBack() +{ + if (_current && _current->last()) + _current = _current->last(); + + return _current; +} + +HistoryItem* StackBrowser::goForward() +{ + if (_current && _current->next()) + _current = _current->next(); + + return _current; +} + +HistoryItem* StackBrowser::goUp() +{ + if (_current) { + TraceFunction* f = _current->stack()->caller(_current->function(), true); + if (f) + _current = select(f); + } + + return _current; +} + +HistoryItem* StackBrowser::goDown() +{ + if (_current) { + TraceFunction* f = _current->stack()->called(_current->function(), true); + if (f) + _current = select(f); + } + + return _current; +} + +bool StackBrowser::canGoBack() +{ + return _current && _current->last(); +} + +bool StackBrowser::canGoForward() +{ + return _current && _current->next(); +} + +bool StackBrowser::canGoUp() +{ + if (!_current) return false; + + return _current->stack()->caller(_current->function(), false); +} + +bool StackBrowser::canGoDown() + { + if (!_current) return false; + + return _current->stack()->called(_current->function(), false); +} diff --git a/kcachegrind/kcachegrind/stackbrowser.h b/kcachegrind/kcachegrind/stackbrowser.h new file mode 100644 index 00000000..a533f426 --- /dev/null +++ b/kcachegrind/kcachegrind/stackbrowser.h @@ -0,0 +1,109 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef STACKBROWSER_H +#define STACKBROWSER_H + +#include "tracedata.h" + +// A history of selected functions within stacks + +class Stack +{ +public: + Stack(TraceFunction*); + + // extend the stack at top/bottom if possible + bool contains(TraceFunction*); + + void extendBottom(); + void extendTop(); + + // search for a function on stack calling specified function. + // if found, return upper part with new function call + Stack* split(TraceFunction*); + + // increment reference count + void ref() { _refCount++; } + // decrement reference count + bool deref() { return --_refCount; } + int refCount() { return _refCount; } + + TraceFunction* top() { return _top; } + TraceCallList calls() { return _calls; } + TraceFunction* caller(TraceFunction*, bool extend); + TraceFunction* called(TraceFunction*, bool extend); + + QString toString(); + +private: + Stack(TraceFunction* top, TraceCallList list); + + // at the top of the stack we have a function... + TraceFunction* _top; + // list ordered from top to bottom + TraceCallList _calls; + int _refCount; +}; + +class HistoryItem +{ +public: + HistoryItem(Stack*, TraceFunction*); + ~HistoryItem(); + + Stack* stack() { return _stack; } + TraceFunction* function() { return _function; } + HistoryItem* last() { return _last; } + HistoryItem* next() { return _next; } + void setLast(HistoryItem* h) { _last = h; } + void setNext(HistoryItem* h) { _next = h; } + +private: + + HistoryItem *_last, *_next; + Stack* _stack; + TraceFunction* _function; +}; + + +class StackBrowser +{ +public: + StackBrowser(); + ~StackBrowser(); + + // A function was selected. This creates a new history entry + HistoryItem* select(TraceFunction*); + + HistoryItem* current() { return _current; } + bool canGoBack(); + bool canGoForward(); + bool canGoUp(); + bool canGoDown(); + HistoryItem* goBack(); + HistoryItem* goForward(); + HistoryItem* goUp(); + HistoryItem* goDown(); + +private: + HistoryItem* _current; +}; + + +#endif diff --git a/kcachegrind/kcachegrind/stackitem.cpp b/kcachegrind/kcachegrind/stackitem.cpp new file mode 100644 index 00000000..7ffdd2d3 --- /dev/null +++ b/kcachegrind/kcachegrind/stackitem.cpp @@ -0,0 +1,116 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Items of stack dockable. + */ + +#include +#include + +#include "configuration.h" +#include "listutils.h" +#include "stackitem.h" +#include "stackselection.h" + +// StackItem + +StackItem::StackItem(StackSelection* ss, + QListView* parent, TraceFunction* f) + :QListViewItem(parent) +{ + _view = ss; + _function = f; + _call = 0; + + updateGroup(); + updateCost(); + + setText(2, QString("-- ")); + setText(3, f->prettyName()); +} + +StackItem::StackItem(StackSelection* ss, + QListView* parent, TraceCall* call) + :QListViewItem(parent) +{ + _view = ss; + _call = call; + _function = call->called(); + + updateGroup(); + updateCost(); + + setText(3, _function->prettyName()); +} + + +void StackItem::updateGroup() +{ + QColor c = Configuration::functionColor(_view->groupType(), + _function); + setPixmap(3, colorPixmap(10, 10, c)); +} + +void StackItem::updateCost() +{ + if (!_call) return; + + setText(2, _call->prettyCallCount()); + + TraceCostType* ct = _view->costType(); + _sum = _call->subCost(ct); + double total = _call->called()->data()->subCost(ct); + if (total == 0.0) { + setText(0, "-"); + setPixmap(0, QPixmap()); + } + else { + double sum = 100.0 * _sum / total; + + if (Configuration::showPercentage()) + setText(0, QString("%1") + .arg(sum, 0, 'f', Configuration::percentPrecision())); + else + setText(0, _call->prettySubCost(ct)); + + setPixmap(0, costPixmap(ct, _call, total, false)); + } + + // if _costType2 is 0, column1 is hidden, no change needed + TraceCostType* ct2 = _view->costType2(); + if (!ct2) return; + + _sum = _call->subCost(ct2); + total = _call->called()->data()->subCost(ct2); + if (total == 0.0) { + setText(1, "-"); + setPixmap(1, QPixmap()); + } + else { + double sum = 100.0 * _sum / total; + + if (Configuration::showPercentage()) + setText(1, QString("%1") + .arg(sum, 0, 'f', Configuration::percentPrecision())); + else + setText(1, _call->prettySubCost(ct2)); + + setPixmap(1, costPixmap(ct2, _call, total, false)); + } +} diff --git a/kcachegrind/kcachegrind/stackitem.h b/kcachegrind/kcachegrind/stackitem.h new file mode 100644 index 00000000..a3a3a4c8 --- /dev/null +++ b/kcachegrind/kcachegrind/stackitem.h @@ -0,0 +1,56 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003, 2004 + Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Items of stack dockable. + */ + +#ifndef STACKITEM_H +#define STACKITEM_H + +#include +#include "tracedata.h" + +class StackSelection; + + +// for the stack browser + +class StackItem: public QListViewItem +{ +public: + // for top + StackItem(StackSelection* ss, QListView* parent, TraceFunction* f); + StackItem(StackSelection* ss, QListView* parent, TraceCall* c); + + TraceFunction* function() { return _function; } + TraceCall* call() { return _call; } + void updateGroup(); + void updateCost(); + +private: + StackSelection* _view; + SubCost _sum; + TraceFunction* _function; + TraceCall* _call; +}; + + + +#endif diff --git a/kcachegrind/kcachegrind/stackselection.cpp b/kcachegrind/kcachegrind/stackselection.cpp new file mode 100644 index 00000000..c01098e0 --- /dev/null +++ b/kcachegrind/kcachegrind/stackselection.cpp @@ -0,0 +1,230 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * StackSelection for KCachegrind + * For function selection of a most expected stack, + * to be put into a QDockWindow + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "stackbrowser.h" +#include "stackselection.h" +#include "stackitem.h" + +StackSelection::StackSelection( QWidget* parent, const char* name) + : StackSelectionBase(parent, name) +{ + _data = 0; + _browser = new StackBrowser(); + _item = 0; + _function = 0; + _costType = 0; + _costType2 = 0; + _groupType = TraceItem::Function; + + stackList->setSorting(-1); + stackList->setAllColumnsShowFocus(true); + stackList->setResizeMode(QListView::LastColumn); + stackList->setColumnAlignment(0, Qt::AlignRight); + stackList->setColumnAlignment(1, Qt::AlignRight); + stackList->setColumnAlignment(2, Qt::AlignRight); + stackList->setColumnWidth(0, 50); + // 2nd cost column hidden at first (_costType2 == 0) + stackList->setColumnWidth(1, 0); + stackList->setColumnWidth(2, 50); + + connect(stackList, SIGNAL(selectionChanged(QListViewItem*)), + this, SLOT(stackSelected(QListViewItem*))); +} + +StackSelection::~StackSelection() +{ + delete _browser; +} + +void StackSelection::setData(TraceData* data) +{ + if (_data == data) return; + + _data = data; + + stackList->clear(); + delete _browser; + _browser = new StackBrowser(); + _function = 0; +} + + +void StackSelection::setFunction(TraceFunction* f) +{ + if (_function == f) return; + _function = f; + + if (!_data || !_function) return; + + //kdDebug() << "StackSelection::setFunction " << f->name() << endl; + + HistoryItem* item = _browser->current(); + if (!item || item->function() != f) { + _browser->select(f); + rebuildStackList(); + } +} + + +void StackSelection::rebuildStackList() +{ + HistoryItem* item = _browser->current(); + stackList->clear(); + stackList->setColumnWidth(0, 50); + stackList->setColumnWidth(1, _costType2 ? 50:0); + stackList->setColumnWidth(2, 50); + if (!item || !item->stack()) return; + + TraceFunction* top = item->stack()->top(); + if (!top) return; + + stackList->setColumnWidthMode(1, QListView::Maximum); + + TraceCallList l = item->stack()->calls(); + TraceCall* call; + for (call=l.last();call;call=l.prev()) + new StackItem(this, stackList, call); + + new StackItem(this, stackList, top); + + // select current function + QListViewItem* i = stackList->firstChild(); + for (;i;i=i->nextSibling()) + if (((StackItem*)i)->function() == item->function()) + break; + + if (i) { + // this calls stackFunctionSelected() + stackList->setCurrentItem(i); + stackList->ensureItemVisible(i); + } + + if (!_costType2) { + stackList->setColumnWidthMode(1, QListView::Manual); + stackList->setColumnWidth(1, 0); + } +} + +void StackSelection::stackSelected(QListViewItem* i) +{ + if (!i) return; + + TraceFunction* f = ((StackItem*)i)->function(); + emit functionSelected(f); +} + + +void StackSelection::browserBack() +{ + if (_browser && _browser->canGoBack()) { + _browser->goBack(); + rebuildStackList(); + } +} + +void StackSelection::browserForward() +{ + if (_browser && _browser->canGoForward()) { + _browser->goForward(); + rebuildStackList(); + } +} + +void StackSelection::browserUp() +{ + if (_browser) { + _browser->goUp(); + rebuildStackList(); + } +} + +void StackSelection::browserDown() +{ + if (_browser) { + _browser->goDown(); + rebuildStackList(); + } +} + +void StackSelection::refresh() +{ + QListViewItem* item = stackList->firstChild(); + for(;item;item = item->nextSibling()) + ((StackItem*)item)->updateCost(); +} + +void StackSelection::setCostType(TraceCostType* ct) +{ + if (ct == _costType) return; + _costType = ct; + + stackList->setColumnWidth(0, 50); + if (_costType) + stackList->setColumnText(0, _costType->name()); + + QListViewItem* item = stackList->firstChild(); + for(;item;item = item->nextSibling()) + ((StackItem*)item)->updateCost(); +} + +void StackSelection::setCostType2(TraceCostType* ct) +{ + if (ct == _costType2) return; + _costType2 = ct; + + stackList->setColumnWidth(1, 50); + stackList->setColumnWidthMode(1, QListView::Maximum); + if (_costType2) + stackList->setColumnText(1, _costType2->name()); + + QListViewItem* item = stackList->firstChild(); + for(;item;item = item->nextSibling()) + ((StackItem*)item)->updateCost(); + + if (!_costType2) { + stackList->setColumnWidthMode(1, QListView::Manual); + stackList->setColumnWidth(1, 0); + } +} + +void StackSelection::setGroupType(TraceItem::CostType gt) +{ + if (_groupType == gt) return; + _groupType = gt; + + QListViewItem* item = stackList->firstChild(); + for(;item;item = item->nextSibling()) + ((StackItem*)item)->updateGroup(); +} + +#include "stackselection.moc" diff --git a/kcachegrind/kcachegrind/stackselection.h b/kcachegrind/kcachegrind/stackselection.h new file mode 100644 index 00000000..80c7ed0e --- /dev/null +++ b/kcachegrind/kcachegrind/stackselection.h @@ -0,0 +1,80 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * StackSelection for KCachegrind + * For function selection of a most expected stack, + * to be put into a QDockWindow + */ + +#ifndef STACKSELECTION_H +#define STACKSELECTION_H + +#include "stackselectionbase.h" +#include "tracedata.h" + +class TraceFunction; +class TraceData; +class StackBrowser; +class NestedAreaItem; + +class StackSelection : public StackSelectionBase +{ + Q_OBJECT + +public: + StackSelection( QWidget* parent = 0, const char* name = 0); + ~StackSelection(); + + TraceData* data() const { return _data; } + void setData(TraceData*); + StackBrowser* browser() const { return _browser; } + TraceCostType* costType() { return _costType; } + TraceCostType* costType2() { return _costType2; } + TraceItem::CostType groupType() { return _groupType; } + +signals: + void functionSelected(TraceItem*); + +public slots: + void setFunction(TraceFunction*); + void setCostType(TraceCostType*); + void setCostType2(TraceCostType*); + void setGroupType(TraceItem::CostType); + + void stackSelected( QListViewItem* ); + void browserBack(); + void browserForward(); + void browserUp(); + void browserDown(); + void refresh(); + void rebuildStackList(); + +private: + void selectFunction(); + + TraceData* _data; + StackBrowser* _browser; + QListViewItem* _item; + TraceFunction* _function; + TraceCostType* _costType; + TraceCostType* _costType2; + TraceItem::CostType _groupType; +}; + +#endif diff --git a/kcachegrind/kcachegrind/stackselectionbase.ui b/kcachegrind/kcachegrind/stackselectionbase.ui new file mode 100644 index 00000000..291153bc --- /dev/null +++ b/kcachegrind/kcachegrind/stackselectionbase.ui @@ -0,0 +1,80 @@ + +StackSelectionBase + + + StackSelectionBase + + + + 0 + 0 + 168 + 108 + + + + Stack Selection + + + + unnamed + + + 3 + + + 6 + + + + + Cost + + + true + + + true + + + + + Cost2 + + + true + + + true + + + + + Calls + + + true + + + true + + + + + Function + + + true + + + true + + + + stackList + + + + + + diff --git a/kcachegrind/kcachegrind/subcost.cpp b/kcachegrind/kcachegrind/subcost.cpp new file mode 100644 index 00000000..5352fe25 --- /dev/null +++ b/kcachegrind/kcachegrind/subcost.cpp @@ -0,0 +1,62 @@ +/* This file is part of KCachegrind. + Copyright (C) 2004 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include + +#include "subcost.h" + +//--------------------------------------------------- +// SubCost + +bool SubCost::set(const char** ps) +{ + const char* s = *ps; + if (!s || (*s < '0') || (*s > '9')) return false; + + v = *s - '0'; + s++; + while(*s >= '0' && *s <= '9') { + v = 10* v + (*s-'0'); + s++; + } + while(*s == ' ') s++; + *ps = s; + + return true; +} + +QString SubCost::pretty() +{ + unsigned long long n = v; + + if (n==0) return QString(" 0"); + + int i = 0; + QString res = ""; + + while (n) { + if ((i>0) && !(i%3)) res = " " + res; + i++; + res = QChar('0'+int(n%10)) + res; + n /= 10; + } + res = " " + res; + return res; +} + + diff --git a/kcachegrind/kcachegrind/subcost.h b/kcachegrind/kcachegrind/subcost.h new file mode 100644 index 00000000..5f24a733 --- /dev/null +++ b/kcachegrind/kcachegrind/subcost.h @@ -0,0 +1,66 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002-2004 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef SUBCOST_H +#define SUBCOST_H + +#include "utils.h" + +typedef unsigned long long uint64; + +/** + * Cost event counter, simple wrapper around a 64bit entity + */ +class SubCost +{ + public: + SubCost() {} + SubCost(uint64 i) { v=i; } + SubCost(unsigned i) { v=i; } + SubCost(int i) { v=(unsigned)i; } + SubCost(double d) { v= (uint64)(d + .5); } + + SubCost& operator=(uint64 i) { v = i; return *this; } + SubCost& operator=(unsigned i) { v = i; return *this; } + SubCost& operator=(int i) { v = i; return *this; } + SubCost& operator=(double d) { v = (uint64)(d + .5); return *this; } + + bool set(const char** s); + bool set(FixString& s) { return s.stripUInt64(v); } + + operator uint64&() { return v; } + + bool operator==(unsigned i) const { return v == i; } + bool operator==(int i) const { return v == (unsigned)i; } + bool operator<(unsigned i) const { return v < i; } + bool operator<(int i) const { return v < (unsigned)i; } + bool operator<(const SubCost& s) const { return v < s.v; } + bool operator>(unsigned i) const { return v > i; } + bool operator>(int i) const { return v > (unsigned)i; } + bool operator>(const SubCost& s) const { return v > s.v; } + + /** + * Convert SubCost value into a QString, + * spaced every 3 digits. + */ + QString pretty(); + + uint64 v; +}; + +#endif diff --git a/kcachegrind/kcachegrind/tabview.cpp b/kcachegrind/kcachegrind/tabview.cpp new file mode 100644 index 00000000..3a644a15 --- /dev/null +++ b/kcachegrind/kcachegrind/tabview.cpp @@ -0,0 +1,890 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Tab View, enclosing detailed views for one trace item in + * two tab widgets, separated by a splitter + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "tabview.h" +#include "costtypeview.h" +#include "partview.h" +#include "callview.h" +#include "coverageview.h" +#include "callmapview.h" +#include "instrview.h" +#include "sourceview.h" +#include "callgraphview.h" + +// TabBar + +TabBar::TabBar(TabView* v, QTabWidget* parent, const char *name) + : QTabBar(parent, name) +{ + _tabWidget = parent; + _tabView = v; +} + +void TabBar::mousePressEvent(QMouseEvent *e) +{ + if (e->button() == RightButton) { + QTab *tab = selectTab( e->pos() ); + QWidget* page; + page = tab ? _tabWidget->page( indexOf( tab->identifier() ) ) :0; + + QPopupMenu popup, popup1, popup2, popup3; + if (page) { + TraceItemView::Position p = _tabView->tabPosition(page); + if (p != TraceItemView::Top) { + popup.insertItem(i18n("Move to Top"), 81); + popup2.insertItem(i18n("Top"), 91); + } + if (p != TraceItemView::Right) { + popup.insertItem(i18n("Move to Right"), 82); + popup2.insertItem(i18n("Right"), 92); + } + if (p != TraceItemView::Bottom) { + popup.insertItem(i18n("Move to Bottom"), 83); + popup2.insertItem(i18n("Bottom"), 93); + } + if (p != TraceItemView::Left) { + popup.insertItem(i18n("Move to Bottom Left"), 84); + popup2.insertItem(i18n("Bottom Left"), 94); + } + popup.insertItem(i18n("Move Area To"), &popup2, 2); + popup.insertSeparator(); + popup.insertItem(i18n("Hide This Tab"), 80); + popup.insertItem(i18n("Hide Area"), 90); + + if (_tabView->visibleTabs() <2) { + popup.setItemEnabled(80, false); + popup.setItemEnabled(90, false); + } + else if (_tabView->visibleAreas() <2) + popup.setItemEnabled(90, false); + } + popup3.insertItem(i18n("Top"), 101); + popup3.insertItem(i18n("Right"), 102); + popup3.insertItem(i18n("Bottom"), 103); + popup3.insertItem(i18n("Bottom Left"), 104); + popup.insertItem(i18n("Show Hidden On"), &popup3, 3); + + int r = popup.exec( mapToGlobal( e->pos() ) ); + + TraceItemView::Position p = TraceItemView::Hidden; + if ((r % 10) == 1) p = TraceItemView::Top; + if ((r % 10) == 2) p = TraceItemView::Right; + if ((r % 10) == 3) p = TraceItemView::Bottom; + if ((r % 10) == 4) p = TraceItemView::Left; + + if (r>=80 && r<100) _tabView->moveTab(page, p, r>=90); + if (r>=100 && r<110) _tabView->moveTab(0, p, true); + } + + QTabBar::mousePressEvent( e ); +} + + +// +// Splitter +// + +Splitter::Splitter(Orientation o, QWidget* parent, const char* name) + : QSplitter(o, parent, name) +{} + +void Splitter::moveEvent(QMoveEvent* e) +{ + QSplitter::moveEvent(e); + + if (0) qDebug("Splitter %s: Move", name()); + checkVisiblity(); +} + +void Splitter::checkVisiblity() +{ + const QObjectList *l = children(); + QObjectListIt it( *l ); + QObject *obj; + while ( (obj = it.current()) != 0 ) { + ++it; + if (obj->isA("Splitter")) ((Splitter*)obj)->checkVisiblity(); + else if (obj->isA("TabWidget")) ((TabWidget*)obj)->checkVisibility(); + } +} + + + + +// +// TabWidget +// + +TabWidget::TabWidget(TabView* v, QWidget* parent, + const char* name, WFlags f) + : QTabWidget(parent, name, f) +{ + _hasVisibleRect = false; + setTabBar(new TabBar(v, this)); +} + +void TabWidget::checkVisibility() +{ + bool hasVisibleRect = (visibleRect().width()>1) && + (visibleRect().height()>1); + + if (0) qDebug("TabWidget %s: VR (%dx%d) HasVisibleRect: %s => %s", + name(), + visibleRect().width(), visibleRect().height(), + _hasVisibleRect ? "Yes":"No", + hasVisibleRect ? "Yes":"No"); + + if (hasVisibleRect != _hasVisibleRect) { + _hasVisibleRect = hasVisibleRect; + emit visibleRectChanged(this); + } +} + +void TabWidget::resizeEvent(QResizeEvent *e) +{ + QTabWidget::resizeEvent(e); + if (0) qDebug("TabWidget %s:\n Resize from (%d/%d) to (%d/%d)", + name(), + e->oldSize().width(), e->oldSize().height(), + e->size().width(), e->size().height()); + checkVisibility(); +} + +void TabWidget::showEvent(QShowEvent* e) +{ + QTabWidget::showEvent(e); + + if (0) qDebug("TabWidget %s: Show", name()); + checkVisibility(); +} + +void TabWidget::hideEvent(QHideEvent* e) +{ + QTabWidget::hideEvent(e); + + if (0) qDebug("TabWidget %s: Hide", name()); + checkVisibility(); +} + +void TabWidget::moveEvent(QMoveEvent* e) +{ + QTabWidget::moveEvent(e); + + if (0) qDebug("TabWidget %s: Move", name()); + checkVisibility(); +} + + + +// +// TabView +// + +/* + * Areas for child views + * + * leftSplitter + * | + * | ----- ----- + * | _/ \_______________/ \____ + * | | Top | TopRight | + * | | | | + * -> |---------------------| | + * | BottomLeft | Bottom | | + * | | | | + * -\_____/------\____/-------------- + * + * ^ ^ + * bottomSplitter mainSplitter + */ + +TabView::TabView(TraceItemView* parentView, + QWidget* parent, const char* name) + : QWidget(parent, name), TraceItemView(parentView) +{ + setFocusPolicy(QWidget::StrongFocus); + + _isCollapsed = true; + + QVBoxLayout* vbox = new QVBoxLayout( this, 6, 6); + + _nameLabel = new KSqueezedTextLabel( this, "nameLabel" ); + _nameLabel->setText(i18n("(No profile data file loaded)")); + vbox->addWidget( _nameLabel ); + + _mainSplitter = new QSplitter(Qt::Horizontal, this); + _leftSplitter = new Splitter(Qt::Vertical, _mainSplitter, "Left"); + vbox->addWidget( _mainSplitter ); + + _rightTW = new TabWidget(this, _mainSplitter, "Right"); + connect(_rightTW, SIGNAL(currentChanged(QWidget*)), + this, SLOT(tabChanged(QWidget*))); + connect(_rightTW, SIGNAL(visibleRectChanged(TabWidget*)), + this, SLOT(visibleRectChangedSlot(TabWidget*))); + + _topTW = new TabWidget(this, _leftSplitter, "Top"); + connect(_topTW, SIGNAL(currentChanged(QWidget*)), + this, SLOT(tabChanged(QWidget*))); + connect(_topTW, SIGNAL(visibleRectChanged(TabWidget*)), + this, SLOT(visibleRectChangedSlot(TabWidget*))); + + _bottomSplitter = new Splitter(Qt::Horizontal, + _leftSplitter, "Bottom"); + + _leftTW = new TabWidget(this, _bottomSplitter, "Left"); + _leftTW->setTabPosition(QTabWidget::Bottom); + connect(_leftTW, SIGNAL(currentChanged(QWidget*)), + this, SLOT(tabChanged(QWidget*))); + connect(_leftTW, SIGNAL(visibleRectChanged(TabWidget*)), + this, SLOT(visibleRectChangedSlot(TabWidget*))); + + _bottomTW = new TabWidget(this, _bottomSplitter, "Bottom"); + _bottomTW->setTabPosition(QTabWidget::Bottom); + connect(_bottomTW, SIGNAL(currentChanged(QWidget*)), + this, SLOT(tabChanged(QWidget*))); + connect(_bottomTW, SIGNAL(visibleRectChanged(TabWidget*)), + this, SLOT(visibleRectChangedSlot(TabWidget*))); + + + // default positions... + + addTop( addTab( i18n("Types"), + new CostTypeView(this, _topTW, + "CostTypeView"))); + addTop( addTab( i18n("Callers"), + new CallView(true, this, _topTW, + "CallerView"))); + addTop( addTab( i18n("All Callers"), + new CoverageView(true, this, _topTW, + "AllCallerView"))); + addTop( addTab( i18n("Caller Map"), + new CallMapView(true, this, _bottomTW, + "CallerMapView"))); + addTop( addTab( i18n("Source"), + new SourceView(this, _topTW, + "SourceView"))); + + addBottom( addTab( i18n("Parts"), + new PartView(this, _bottomTW, + "PartView"))); + addBottom( addTab( i18n("Call Graph"), + new CallGraphView(this, _bottomTW, + "CallGraphView"))); + addBottom( addTab( i18n("Callees"), + new CallView(false, this, _bottomTW, + "CalleeView"))); + addBottom( addTab( i18n("All Callees"), + new CoverageView(false, this, _bottomTW, + "AllCalleeView"))); + + addBottom( addTab( i18n("Callee Map"), + new CallMapView(false, this, _topTW, + "CalleeMapView"))); + addBottom( addTab( i18n("Assembler"), + new InstrView(this, _bottomTW, + "InstrView"))); + + // after all child widgets are created... + _lastFocus = 0; + _active = false; + installFocusFilters(); + + updateVisibility(); + + QWhatsThis::add( this, whatsThis() ); +} + +void TabView::setData(TraceData* d) +{ + TraceItemView::setData(d); + + TraceItemView* v; + for (v=_tabs.first();v;v=_tabs.next()) + v->setData(d); +} + +TraceItemView* TabView::addTab(QString label, TraceItemView* view) +{ + view->setTitle(label); + _tabs.append(view); + return view; +} + +void TabView::addTop(TraceItemView* view) +{ + view->setPosition(TraceItemView::Top); + _topTW->insertTab(view->widget(), view->title()); +} + +void TabView::addBottom(TraceItemView* view) +{ + view->setPosition(TraceItemView::Bottom); + _bottomTW->insertTab(view->widget(), view->title()); +} + +TraceItemView::Position TabView::tabPosition(QWidget* w) +{ + TraceItemView* v; + for (v=_tabs.first();v;v=_tabs.next()) + if (v->widget() == w) return v->position(); + + return Hidden; +} + +int TabView::visibleTabs() +{ + int c = 0; + TraceItemView* v; + for (v=_tabs.first();v;v=_tabs.next()) { + if (v->position() == Hidden) continue; + c++; + } + return c; +} + + +int TabView::visibleAreas() +{ + int c = 0, t = 0, b = 0, r = 0, l = 0; + TraceItemView* v; + for (v=_tabs.first();v;v=_tabs.next()) { + switch(v->position()) { + case TraceItemView::Top: t++; break; + case TraceItemView::Bottom: b++; break; + case TraceItemView::Left: l++; break; + case TraceItemView::Right: r++; break; + default: break; + } + } + if (t>0) c++; + if (b>0) c++; + if (l>0) c++; + if (r>0) c++; + + return c; +} + + + +// This hides/shows splitters and tabwidgets according to tab childs +void TabView::updateVisibility() +{ + // calculate count of tabs in areas + int t = 0, b = 0, r = 0, l = 0; + TraceItemView* v; + for (v=_tabs.first();v;v=_tabs.next()) { + switch(v->position()) { + case TraceItemView::Top: t++; break; + case TraceItemView::Bottom: b++; break; + case TraceItemView::Left: l++; break; + case TraceItemView::Right: r++; break; + default: break; + } + } + + if (0) qDebug("TabView::updateVisiblity t %d, b %d, l %d, r %d", + t, b, l, r); + + QValueList s; + s.append(100); + + + // children of mainSplitter + if (_rightTW->isHidden() != (r == 0)) { + if (r == 0) { + _rightTW->hide(); + + if (!_topTW->hasVisibleRect() && + !_bottomTW->hasVisibleRect() && + !_leftTW->hasVisibleRect()) _mainSplitter->setSizes(s); + } + else + _rightTW->show(); + } + if (_leftSplitter->isHidden() != (t+b+l == 0)) { + if (t+b+l == 0) { + _leftSplitter->hide(); + + if (!_rightTW->hasVisibleRect()) _mainSplitter->setSizes(s); + } + else + _leftSplitter->show(); + } + + // children of leftSplitter + if (_topTW->isHidden() != (t == 0)) { + if (t == 0) { + _topTW->hide(); + + if (!_bottomTW->hasVisibleRect() && + !_leftTW->hasVisibleRect()) _leftSplitter->setSizes(s); + } + else + _topTW->show(); + } + + if (_bottomSplitter->isHidden() != (b+l == 0)) { + if (b+l == 0) { + _bottomSplitter->hide(); + + if (!_topTW->hasVisibleRect()) _leftSplitter->setSizes(s); + } + else + _bottomSplitter->show(); + } + + // children of bottomSplitter + if (_bottomTW->isHidden() != (b == 0)) { + if (b == 0) { + _bottomTW->hide(); + + if (!_leftTW->hasVisibleRect()) _bottomSplitter->setSizes(s); + } + else + _bottomTW->show(); + } + if (_leftTW->isHidden() != (l == 0)) { + if (l == 0) { + _leftTW->hide(); + + if (!_bottomTW->hasVisibleRect()) _bottomSplitter->setSizes(s); + } + else + _leftTW->show(); + } +} + +TabWidget* TabView::tabWidget(Position p) +{ + switch(p) { + case TraceItemView::Top: return _topTW; + case TraceItemView::Bottom: return _bottomTW; + case TraceItemView::Left: return _leftTW; + case TraceItemView::Right: return _rightTW; + default: break; + } + return 0; +} + +void TabView::moveTab(QWidget* w, Position p, bool wholeArea) +{ + TraceItemView *v; + Position origPos = Hidden; + if (w) { + for (v=_tabs.first();v;v=_tabs.next()) + if (v->widget() == w) break; + + if (!v) return; + origPos = v->position(); + } + if (origPos == p) return; + + TabWidget *from, *to; + from = tabWidget(origPos); + to = tabWidget(p); + + QPtrList tabs; + for (v=_tabs.first();v;v=_tabs.next()) + if ((v->position() == origPos) && + (wholeArea || (v->widget() == w))) tabs.append(v); + + bool isEnabled; + for (v=tabs.first();v;v=tabs.next()) { + v->setPosition(p); + w = v->widget(); + + if (from) { + isEnabled = from->isTabEnabled(w); + from->removePage(w); + } + else isEnabled = (v->canShow(_activeItem)!=0); + + if (to) { + TraceItemView *vv; + int idx = -1, i; + for(vv = _tabs.first(); vv && (vv!=v); vv = _tabs.next()) { + i = to->indexOf(vv->widget()); + if (i>=0) idx = i; + } + to->insertTab(w, v->title(), idx+1); + to->setTabEnabled(w, isEnabled); + if (isEnabled) { + to->showPage(w); + v->updateView(); + } + } + } + updateVisibility(); +} + + +QString TabView::whatsThis() const +{ + return i18n( "Information Tabs" + "

This widget shows information for the " + "current selected function in different tabs: " + "

    " + "
  • The Costs tab shows a list of available event types " + "and the inclusive and self costs regarding to these types.
  • " + "
  • The Parts tab shows a list of trace parts " + "if the trace consists of more than one part (otherwise, " + "this tab is hided). " + "The cost of the selected function spent in the different " + "parts together with the calls happening is shown.
  • " + "
  • The Call Lists tab shows direct callers and " + "callees of the function in more detail.
  • " + "
  • The Coverage tab shows the same is the Call " + "Lists tab, but not only direct callers and callees " + "but also indirect ones.
  • " + "
  • The Call Graph tab shows a graphical " + "visualization of the calls done by this function.
  • " + "
  • The Source tab presents annotated source code " + "if debugging information and the source file " + "is available.
  • " + "
  • The Assembler tab presents annotated assembler code " + "if trace information on instruction level " + "is available.
" + "For more information, see the What's This? " + "help of the corresponding tab widget

"); +} + +void TabView::installFocusFilters() +{ + QObjectList *l = queryList("QWidget"); + QObjectListIt it( *l ); + QObject *obj; + + while ( (obj = it.current()) != 0 ) { + ++it; + if ( ((QWidget*)obj)->isFocusEnabled() ) + obj->installEventFilter(this); + } + delete l; +} + + +bool TabView::eventFilter(QObject* o, QEvent* e) +{ + if (e->type() == QEvent::FocusIn) { + _lastFocus = o->isWidgetType() ? (QWidget*) o : 0; + setActive(_lastFocus != 0); + } + return QWidget::eventFilter(o,e); +} + +void TabView::mousePressEvent(QMouseEvent*) +{ + if (_lastFocus) + _lastFocus->setFocus(); + setActive(true); +} + +void TabView::setActive(bool a) +{ + if (a == _active) return; + _active = a; + + QFont nameLabel_font( _nameLabel->font() ); + nameLabel_font.setBold(a); + _nameLabel->setFont( nameLabel_font ); + + if (0) qDebug("%s::setActive(%s)", name(), a ? "true":"false"); + + if (a) emit activated(this); +} + +void TabView::doUpdate(int changeType) +{ + if (changeType & (activeItemChanged | configChanged | dataChanged)) + + _nameLabel->setText( !_data ? i18n("(No Data loaded)") : + !_activeItem ? i18n("(No function selected)") : + _activeItem->prettyName()); + + + // we use our own list iterators because setTabEnabled can + // invoke tabChanged, which mangles with the lists, too + bool canShow; + TraceItemView *v; + QPtrListIterator it( _tabs ); + while ( (v=it.current()) != 0) { + ++it; + + TabWidget *tw = 0; + switch(v->position()) { + case TraceItemView::Top: tw = _topTW; break; + case TraceItemView::Bottom: tw = _bottomTW; break; + case TraceItemView::Left: tw = _leftTW; break; + case TraceItemView::Right: tw = _rightTW; break; + default: break; + } + + // update even if hidden + if (tw) { + if (!tw->hasVisibleRect()) continue; + } + canShow = v->set(changeType, _data, _costType, _costType2, + _groupType, _partList, + _activeItem, _selectedItem); + v->notifyChange(changeType); + + if (!tw) continue; + if (tw->isTabEnabled(v->widget()) != canShow) + tw->setTabEnabled(v->widget(), canShow); + + if (v->widget() == tw->currentPage()) + v->updateView(); + } +} + + +void TabView::tabChanged(QWidget* w) +{ + TraceItemView *v; + for (v=_tabs.first();v;v=_tabs.next()) + if (v->widget() == w) v->updateView(); +} + +void TabView::visibleRectChangedSlot(TabWidget* tw) +{ + if (0) qDebug("%s: %svisible !", + tw->name(), tw->hasVisibleRect() ? "":"un"); + + if (tw->hasVisibleRect()) doUpdate(0); +} + +void TabView::resizeEvent(QResizeEvent* e) +{ + QWidget::resizeEvent(e); + + bool collapsed = (e->size().width()<=1) || (e->size().height()<=1); + if (_isCollapsed != collapsed) { + _isCollapsed = collapsed; + updateView(); + } + + if (0) qDebug("TabView::Resize from (%d/%d) to (%d/%d)", + e->oldSize().width(), e->oldSize().height(), + e->size().width(), e->size().height()); +} + +void TabView::selected(TraceItemView*, TraceItem* s) +{ + // we set selected item for our own children + select(s); + updateView(); + + // still forward to parent + if (_parentView) _parentView->selected(this, s); +} + + +void TabView::readViewConfig(KConfig* c, + QString prefix, QString postfix, + bool withOptions) +{ + if (0) qDebug("%s::readConfig(%s%s)", name(), + prefix.ascii(), postfix.ascii()); + + KConfigGroup* g = configGroup(c, prefix, postfix); + + _mainSplitter->setSizes(g->readIntListEntry("MainSizes")); + _leftSplitter->setSizes(g->readIntListEntry("LeftSizes")); + _bottomSplitter->setSizes(g->readIntListEntry("BottomSizes")); + + QString activeT = g->readEntry("ActiveTop", "CallerView"); + QString activeB = g->readEntry("ActiveBottom", "CalleeView"); + QString activeL = g->readEntry("ActiveLeft", ""); + QString activeR = g->readEntry("ActiveRight", ""); + + QStringList topTabs = g->readListEntry("TopTabs"); + QStringList bottomTabs = g->readListEntry("BottomTabs"); + QStringList leftTabs = g->readListEntry("LeftTabs"); + QStringList rightTabs = g->readListEntry("RightTabs"); + + if (topTabs.isEmpty() && bottomTabs.isEmpty() && + rightTabs.isEmpty() && leftTabs.isEmpty()) { + // no tabs visible ?! Reset to default + topTabs << "CostTypeView" << "CallerView" << "AllCallerView" + << "CalleeMapView" << "SourceView"; + bottomTabs << "PartView" << "CalleeView" << "CallGraphView" + << "AllCalleeView" << "CallerMapView" << "InstrView"; + } + + TraceItemView *activeTop = 0, *activeBottom = 0; + TraceItemView *activeLeft = 0, *activeRight = 0; + + moveTab(0, TraceItemView::Top, true); + TraceItemView *v; + QPtrListIterator it( _tabs ); + while ( (v=it.current()) != 0) { + ++it; + + QString n = QString(v->widget()->name()); + if (topTabs.contains(n)) { + moveTab(v->widget(), TraceItemView::Top); + if (n == activeT) activeTop = v; + } + else if (bottomTabs.contains(n)) { + moveTab(v->widget(), TraceItemView::Bottom); + if (n == activeB) activeBottom = v; + } + else if (leftTabs.contains(n)) { + moveTab(v->widget(), TraceItemView::Left); + if (n == activeL) activeLeft = v; + } + else if (rightTabs.contains(n)) { + moveTab(v->widget(), TraceItemView::Right); + if (n == activeR) activeRight = v; + } + else moveTab(v->widget(), Hidden); + + if (withOptions) + v->readViewConfig(c, QString("%1-%2") + .arg(prefix).arg(v->widget()->name()), + postfix, true); + } + if (activeTop) _topTW->showPage(activeTop->widget()); + if (activeBottom)_bottomTW->showPage(activeBottom->widget()); + if (activeLeft) _leftTW->showPage(activeLeft->widget()); + if (activeRight) _rightTW->showPage(activeRight->widget()); + + QString activeType = g->readEntry("ActiveItemType", ""); + QString activeName = g->readEntry("ActiveItemName", ""); + QString selectedType = g->readEntry("SelectedItemType", ""); + QString selectedName = g->readEntry("SelectedItemName", ""); + delete g; + + if (!_data) return; + + if (withOptions) { + // restore active item + TraceItem::CostType t = TraceItem::costType(activeType); + if (t==TraceItem::NoCostType) t = TraceItem::Function; + TraceCost* activeItem = _data->search(t, activeName, _costType); + if (!activeItem) return; + activate(activeItem); + + // restore selected item + t = TraceItem::costType(selectedType); + if (t==TraceItem::NoCostType) t = TraceItem::Function; + TraceCost* selectedItem = _data->search(t, selectedName, + _costType, activeItem); + if (selectedItem) select(selectedItem); + } + + updateView(); +} + +void TabView::saveViewConfig(KConfig* c, + QString prefix, QString postfix, + bool withOptions) +{ + KConfigGroup g(c, (prefix+postfix).ascii()); + + g.writeEntry("MainSizes", _mainSplitter->sizes()); + g.writeEntry("LeftSizes", _leftSplitter->sizes()); + g.writeEntry("BottomSizes", _bottomSplitter->sizes()); + + QString a; + if ((_topTW->count()>0) && + (_topTW->isTabEnabled(_topTW->currentPage()))) + a = QString(_topTW->currentPage()->name()); + g.writeEntry("ActiveTop", a); + + a.setLength(0); + if ((_bottomTW->count()>0) && + (_bottomTW->isTabEnabled(_bottomTW->currentPage()))) + a = QString(_bottomTW->currentPage()->name()); + g.writeEntry("ActiveBottom", a); + + a.setLength(0); + if ((_leftTW->count()>0) && + (_leftTW->isTabEnabled(_leftTW->currentPage()))) + a = QString(_leftTW->currentPage()->name()); + g.writeEntry("ActiveLeft", a); + + a.setLength(0); + if ((_rightTW->count()>0) && + (_rightTW->isTabEnabled(_rightTW->currentPage()))) + a = QString(_rightTW->currentPage()->name()); + g.writeEntry("ActiveRight", a); + + if (withOptions) + if (_activeItem) { + g.writeEntry("ActiveItemType", + TraceItem::typeName(_activeItem->type())); + g.writeEntry("ActiveItemName", _activeItem->name()); + if (_selectedItem) { + g.writeEntry("SelectedItemType", + TraceItem::typeName(_selectedItem->type())); + g.writeEntry("SelectedItemName", _selectedItem->name()); + } + } + + QStringList topList, bottomList, leftList, rightList; + TraceItemView *v; + for (v=_tabs.first();v;v=_tabs.next()) { + switch(v->position()) { + case TraceItemView::Top: + topList << QString(v->widget()->name()); + break; + + case TraceItemView::Bottom: + bottomList << QString(v->widget()->name()); + break; + + case TraceItemView::Left: + leftList << QString(v->widget()->name()); + break; + + case TraceItemView::Right: + rightList << QString(v->widget()->name()); + break; + + default: break; + } + } + + g.writeEntry("TopTabs", topList); + g.writeEntry("BottomTabs", bottomList); + g.writeEntry("LeftTabs", leftList); + g.writeEntry("RightTabs", rightList); + + if (withOptions) + for (v=_tabs.first();v;v=_tabs.next()) + v->saveViewConfig(c, QString("%1-%2").arg(prefix) + .arg(v->widget()->name()), postfix, true); +} + +#include "tabview.moc" diff --git a/kcachegrind/kcachegrind/tabview.h b/kcachegrind/kcachegrind/tabview.h new file mode 100644 index 00000000..946f9225 --- /dev/null +++ b/kcachegrind/kcachegrind/tabview.h @@ -0,0 +1,170 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Tab View, enclosing detailed views for one trace item in + * two tab widgets, separated by a splitter + */ + +#ifndef TABVIEW_H +#define TABVIEW_H + +#include +#include +#include +#include +#include +#include "traceitemview.h" + +class QSplitter; +class TabView; + +/** + * Subclass of QTabBar to enable context menu on tabs + */ +class TabBar : public QTabBar +{ + Q_OBJECT + + public: + TabBar(TabView*, QTabWidget* parent, const char *name = 0); + protected: + void mousePressEvent(QMouseEvent *e); + + private: + QTabWidget* _tabWidget; + TabView* _tabView; +}; + + +/** + * Own Splitter: + * Call checkVisiblity for all TabWidget children of the splitter + * on a MoveEvent. This typically is produced when collapsing the widget + * inside of another splitter. + */ +class Splitter: public QSplitter +{ + Q_OBJECT + +public: + Splitter(Orientation o, QWidget* parent = 0, const char* name = 0); + void checkVisiblity(); + +protected: + void moveEvent(QMoveEvent *); +}; + + +/** + * Own TabView: + * - A QTabWidget able to track its visible rect via resizeEvents. + * This is needed to track if this widget is collapsed in a QSplitter. + * - Use own TabBar for context menu + */ +class TabWidget: public QTabWidget +{ + Q_OBJECT + +public: + + TabWidget(TabView*, QWidget* parent = 0, + const char* name = 0, WFlags f = 0); + + bool hasVisibleRect() { return _hasVisibleRect; } + void checkVisibility(); + +signals: + void visibleRectChanged(TabWidget*); + +protected: + void resizeEvent(QResizeEvent *); + void showEvent(QShowEvent *); + void hideEvent(QHideEvent *); + void moveEvent(QMoveEvent *); + +private: + bool _hasVisibleRect; +}; + + + +class TabView : public QWidget, public TraceItemView +{ + Q_OBJECT + +public: + + TabView(TraceItemView* parentView, + QWidget* parent = 0, const char* name = 0); + + virtual QWidget* widget() { return this; } + QString whatsThis() const ; + void setData(TraceData*); + bool isViewVisible() const { return !_isCollapsed; } + void selected(TraceItemView*, TraceItem*); + bool active() const { return _active; } + void setActive(bool); + + /** + * Rearrange tabs + * if == 0, move hidden tabs + */ + void moveTab(QWidget* w, Position, bool wholeArea = false); + + Position tabPosition(QWidget*); + int visibleTabs(); + int visibleAreas(); + + void readViewConfig(KConfig*, QString prefix, QString postfix, bool); + void saveViewConfig(KConfig*, QString prefix, QString postfix, bool); + +public slots: + void tabChanged(QWidget*); + void visibleRectChangedSlot(TabWidget*); + +signals: + void activated(TabView*); + +protected: + void resizeEvent(QResizeEvent *); + bool eventFilter(QObject*, QEvent*); + void mousePressEvent(QMouseEvent*); + +private: + TraceItemView* addTab(QString, TraceItemView*); + void addTop(TraceItemView*); + void addBottom(TraceItemView*); + TabWidget* tabWidget(Position); + void updateVisibility(); + void doUpdate(int); + void installFocusFilters(); + + // this is true if width or height <= 1, and no child updates are done + bool _isCollapsed; + + KSqueezedTextLabel* _nameLabel; + QSplitter *_mainSplitter, *_leftSplitter, *_bottomSplitter; + TabWidget *_topTW, *_leftTW, *_bottomTW, *_rightTW; + QPtrList _tabs; + + QWidget* _lastFocus; + bool _active; +}; + +#endif diff --git a/kcachegrind/kcachegrind/tips b/kcachegrind/kcachegrind/tips new file mode 100644 index 00000000..1f555c0e --- /dev/null +++ b/kcachegrind/kcachegrind/tips @@ -0,0 +1,141 @@ + + +

...that the What's This? help for every GUI widget +in KCachegrind contains detailed usage information for this widget? +It is highly recommended to read at least these help texts on first +use. Request What's This? help by pressing +Shift+F1 and clicking on the widget.

+ +
+ + + +

...that you can get profile information at instruction level +with Calltree when you provide the option --dump-instr=yes? +Use the Assembler View for the instruction annotations. +

+ +
+ + + +

...that you can use Alt-Left/Right keys of your keyboard to go +back/forward in the active object history ?

+ +
+ + + +

...that you can navigate in the Callee/Caller Map View using +arrow keys? Use Left/Right to change to siblings of the current +item; use Up/Down to go one nesting level up/down. To select +the current item, press Space, and to activate it, press Return. +

+ +
+ + + +

...that you can navigate in the Call Graph View using +arrow keys? Use Up/Down to go one calling level up/down, alternating +between calls and functions. Use Left/Right to change to siblings of a current +selected call. To activate the current item, press Return. +

+ +
+ + + +

...that you can rapidly locate a function by entering part of its +name (case-insensitive) into the edit line of the toolbar +and hit return?

+ +
+ + + +

...that you can assign custom colors to +ELF objects/C++ Classes/Source Files for graph coloring +in Settings->Configure KCachegrind...?

+ +
+ + + +

...that you can see if debug info is available for a selected +function by looking at the location label in the Info tab or +the source listing header in the source tab?

+

There must be the name of the source file (with extension). +If KCachegrind still doesn't show the source, make sure that you +have added the directory of the source file to the +Source Directories list in the configuration. + + + + + +

...that you can configure whether KCachgrind should +show absolute event counts or relative ones (percentage display)?

+ +
+ + + +

...that you can configure the maximum number of items +for all function lists in KCachegrind? Limiting the number +of items is done to get a fast reacting GUI. The last item in +the list will show you the number of skipped functions, together +with a cost condition for these skipped functions.

+

To activate a function with small costs, search for it and select +it in the flat profile. Selecting functions with small cost will +temporarily add them to the flat profile list.

+ +
+ + + +

...that the Coverage tab - in contrast to the Call Lists tab - +shows all functions that are calling the selected function +(upper part) / are called by the selected function (bottom part), +no matter how many function are between them on the stack?

+

Examples:

+

An entry in the upper list for function foo1() with a value of 50% +with function bar() selected means that 50% of all the cost of function +bar() happened while called from function foo1().

+

An entry in the bottom list for function foo2() with a value of 50% +with function bar() selected means that 50% of all the cost of function +bar() happened while calling foo2() from bar().

+ +
+ + + +

...that waiting for the tool tip inside of a tree map +shows the list of names of the nested rectangles the mouse +pointer is over?

+

Items from this list can be selected by pressing the right +mouse button.

+ +
+ + + +

...that you can constrain the cost counts shown to only a +few parts of the whole trace by selecting these parts in the +"Trace Selection" Dockable?

+

To generate multiple parts in a profiling run with +cachegrind, use e.g. option --cachedumps=xxx for parts +of a length of xxx basic blocks (A basic block is a run +of not-branching assembler statements inside of your program +code).

+ +
+ + +

...that by splitting the view to show information of +two functions simultaniously, selecting a function in +one panel shows the information for that function +in the other panel?

+ +
+ diff --git a/kcachegrind/kcachegrind/toplevel.cpp b/kcachegrind/kcachegrind/toplevel.cpp new file mode 100644 index 00000000..328a6bdb --- /dev/null +++ b/kcachegrind/kcachegrind/toplevel.cpp @@ -0,0 +1,2399 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002, 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * KCachegrind top level window + */ + +#define TRACE_UPDATES 0 +#define ENABLE_DUMPDOCK 0 + +#include // for system() + +#include +#include +#include +#include +#include +#include +#include +#include + +// With Qt 3.1, we can disallow user interaction with long tasks. +// This needs QEventLoop. Otherwise, QApplication::processEvents is used. +#if (QT_VERSION-0 >= 0x030100) +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if ENABLE_DUMPDOCK +#include "dumpselection.h" +#endif + +#include "toplevel.h" +#include "partselection.h" +#include "functionselection.h" +#include "stackselection.h" +#include "stackbrowser.h" +#include "tracedata.h" +#include "configuration.h" +#include "configdlg.h" +#include "multiview.h" +#include "callgraphview.h" + + +TopLevel::TopLevel(const char *name) + : KMainWindow(0, name), DCOPObject("KCachegrindIface") +{ + init(); + + createDocks(); + + _multiView = new MultiView(this, this, "MultiView"); + setCentralWidget(_multiView); + + createActions(); + + _partDockShown->setChecked(!_partDock->isHidden()); + _stackDockShown->setChecked(!_stackDock->isHidden()); + _functionDockShown->setChecked(!_functionDock->isHidden()); + + connect(_partDock, SIGNAL(visibilityChanged(bool)), + this, SLOT(partVisibilityChanged(bool))); + connect(_stackDock, SIGNAL(visibilityChanged(bool)), + this, SLOT(stackVisibilityChanged(bool))); + connect(_functionDock, SIGNAL(visibilityChanged(bool)), + this, SLOT(functionVisibilityChanged(bool))); + +#if ENABLE_DUMPDOCK + _dumpDockShown->setChecked(!_dumpDock->isHidden()); + connect(_dumpDock, SIGNAL(visibilityChanged(bool)), + this, SLOT(dumpVisibilityChanged(bool))); +#endif + + _statusbar = statusBar(); + _statusLabel = new QLabel(_statusbar); +#if 0 + // how to do avoid main window resizing on large statusbar label? + QSizePolicy p(QSizePolicy::Fixed, QSizePolicy::Expanding); + _statusLabel->setSizePolicy(p); + _statusbar->setSizePolicy(p); +#endif + _statusbar->addWidget(_statusLabel, 1); + + KConfig* kconfig = KGlobal::config(); + Configuration::readOptions( kconfig ); + _openRecent->loadEntries( kconfig ); + + // set toggle after reading configuration + _showPercentage = Configuration::showPercentage(); + _showExpanded = Configuration::showExpanded(); + _showCycles = Configuration::showCycles(); + _taPercentage->setChecked(_showPercentage); + _taExpanded->setChecked(_showExpanded); + _taCycles->setChecked(_showCycles); + + setupPartSelection(_partSelection); + + // KCachegrind for KDE 3.0.x does not allow to hide toolbars... +#if KDE_VERSION >= 308 // KDE 3.1 + setStandardToolBarMenuEnabled(true); +#endif + + // QT dock windows are created before (using QT position restoring) + createGUI(); + + setAutoSaveSettings(); + + // restore current state settings (not configuration options) + restoreCurrentState(QString::null); + + // if this is the first toplevel, show tip of day + if (memberList->count() == 1) + QTimer::singleShot( 200, this, SLOT(slotShowTipOnStart()) ); +} + +void TopLevel::init() +{ + _activeParts.clear(); + _hiddenParts.clear(); + + _progressBar = 0; + + _data = 0; + _function = 0; + _costType = 0; + _costType2 = 0; + _groupType = TraceCost::NoCostType; + _group = 0; + + _layoutCurrent = 0; + _layoutCount = 1; + + // for delayed slots + _traceItemDelayed = 0; + _costTypeDelayed = 0; + _costType2Delayed = 0; + _groupTypeDelayed = TraceCost::NoCostType; + _groupDelayed = 0; + _directionDelayed = TraceItemView::None; + _lastSender = 0; +} + + +/** + * Setup the part selection widget. + * Statusbar has to be created before. + */ +void TopLevel::setupPartSelection(PartSelection* ps) +{ + // setup connections from the part selection widget + + connect(ps, SIGNAL(activePartsChanged(const TracePartList&)), + this, SLOT(activePartsChangedSlot(const TracePartList&))); + connect(ps, SIGNAL(groupChanged(TraceCostItem*)), + this, SLOT(setGroupDelayed(TraceCostItem*))); + connect(ps, SIGNAL(functionChanged(TraceItem*)), + this, SLOT(setTraceItemDelayed(TraceItem*))); + + connect(ps, SIGNAL(goBack()), + _stackSelection, SLOT(browserBack())); + + connect(ps, SIGNAL(partsHideSelected()), + this, SLOT(partsHideSelectedSlotDelayed())); + connect(ps, SIGNAL(partsUnhideAll()), + this, SLOT(partsUnhideAllSlotDelayed())); + + connect(ps, SIGNAL(showMessage(const QString&, int)), + _statusbar, SLOT(message(const QString&, int))); +} + +/** + * This saves the current state of the main window and + * sub widgets. + * + * No positions are saved. These is done automatically for + * KToolbar, and manually in queryExit() for QT docks. + */ +void TopLevel::saveCurrentState(QString postfix) +{ + KConfig* kconfig = KGlobal::config(); + QCString pf = postfix.ascii(); + + KConfigGroup psConfig(kconfig, QCString("PartOverview")+pf); + _partSelection->saveVisualisationConfig(&psConfig); + + KConfigGroup stateConfig(kconfig, QCString("CurrentState")+pf); + stateConfig.writeEntry("CostType", + _costType ? _costType->name() : QString("?")); + stateConfig.writeEntry("CostType2", + _costType2 ? _costType2->name() : QString("?")); + stateConfig.writeEntry("GroupType", TraceItem::typeName(_groupType)); + + _multiView->saveViewConfig(kconfig, QString("MainView"), postfix, true); +} + +/** + * This function is called when a trace is closed. + * Save browsing position for later restoring + */ +void TopLevel::saveTraceSettings() +{ + QString key = traceKey(); + + KConfigGroup pConfig(KGlobal::config(), QCString("TracePositions")); + pConfig.writeEntry(QString("CostType%1").arg(key), + _costType ? _costType->name() : QString("?")); + pConfig.writeEntry(QString("CostType2%1").arg(key), + _costType2 ? _costType2->name() : QString("?")); + pConfig.writeEntry(QString("GroupType%1").arg(key), + TraceItem::typeName(_groupType)); + + if (!_data) return; + + KConfigGroup aConfig(KGlobal::config(), QCString("Layouts")); + aConfig.writeEntry(QString("Count%1").arg(key), _layoutCount); + aConfig.writeEntry(QString("Current%1").arg(key), _layoutCurrent); + + saveCurrentState(key); + pConfig.writeEntry(QString("Group%1").arg(key), + _group ? _group->name() : QString::null); +} + +/** + * This restores the current state of the main window and + * sub widgets. + * + * This does NOT restore any positions. This is done automatically for + * KToolbar, and manually in the createDocks() for QT docks.. + */ +void TopLevel::restoreCurrentState(QString postfix) +{ + KConfig* kconfig = KGlobal::config(); + QStringList gList = kconfig->groupList(); + QCString pf = postfix.ascii(); + + // dock properties (not position, this should be have done before) + QCString group = QCString("PartOverview"); + if (gList.contains(group+pf)) group += pf; + KConfigGroup psConfig(kconfig, group); + _partSelection->readVisualisationConfig(&psConfig); + + _multiView->readViewConfig(kconfig, QString("MainView"), postfix, true); + _taSplit->setChecked(_multiView->childCount()>1); + _taSplitDir->setEnabled(_multiView->childCount()>1); + _taSplitDir->setChecked(_multiView->orientation() == Qt::Horizontal); +} + + +void TopLevel::createDocks() +{ + _partDock = new QDockWindow(QDockWindow::InDock, this); + _partDock->setCaption(i18n("Parts Overview")); + _partDock->setCloseMode( QDockWindow::Always ); + _partSelection = new PartSelection(_partDock, "partSelection"); + _partDock->setWidget(_partSelection); + _partDock->setResizeEnabled(true); + _partDock->setFixedExtentWidth(200); + QWhatsThis::add( _partSelection, i18n( + "The Parts Overview" + "

A trace consists of multiple trace parts when " + "there are several profile data files from one profile run. " + "The Trace Part Overview dockable shows these, " + "horizontally ordered in execution time; " + "the rectangle sizes are proportional to the total " + "cost spent in the parts. You can select one or several " + "parts to constrain all costs shown to these parts only." + "

" + "

The parts are further subdivided: there is a " + "partitioning and an callee split mode: " + "

  • Partitioning: You see the " + "partitioning into groups for a trace part, according to " + "the group type selected. E.g. if ELF object groups are " + "selected, you see colored rectangles for each " + "used ELF object (shared library or executable), sized " + "according to the cost spent therein.
  • " + "
  • Callee: A rectangle showing the inclusive " + "cost of the current selected function in the trace part " + "is shown. " + "This is split up into smaller rectangles to show the costs of its " + "callees.

")); + + _stackDock = new QDockWindow(QDockWindow::InDock, this); + _stackDock->setResizeEnabled(true); + // Why is the caption only correct with a close button? + _stackDock->setCloseMode( QDockWindow::Always ); + _stackSelection = new StackSelection(_stackDock, "stackSelection"); + _stackDock->setWidget(_stackSelection); + _stackDock->setFixedExtentWidth(200); + _stackDock->setCaption(i18n("Top Cost Call Stack")); + QWhatsThis::add( _stackSelection, i18n( + "The Top Cost Call Stack" + "

This is a purely fictional 'most probable' call stack. " + "It is built up by starting with the current selected " + "function and adds the callers/callees with highest cost " + "at the top and to bottom.

" + "

The Cost and Calls columns show the " + "cost used for all calls from the function in the line " + "above.

")); + + connect(_stackSelection, SIGNAL(functionSelected(TraceItem*)), + this, SLOT(setTraceItemDelayed(TraceItem*))); + + _functionDock = new QDockWindow(QDockWindow::InDock, this); + _functionDock->setCaption(i18n("Flat Profile")); + _functionDock->setCloseMode( QDockWindow::Always ); + _functionSelection = new FunctionSelection(this, _functionDock, + "functionSelection"); + _functionSelection->setTopLevel(this); + + _functionDock->setWidget(_functionSelection); + _functionDock->setResizeEnabled(true); + _functionDock->setFixedExtentWidth(200); + QWhatsThis::add( _functionSelection, i18n( + "The Flat Profile" + "

The flat profile contains a group and a function " + "selection list. The group list contains all groups " + "where costs " + "are spent in, depending on the chosen group type. " + "The group list is hidden when group type 'Function' " + "is selected.

" + "

The function list contains the functions of the " + "selected group (or all for 'Function' group type), " + "ordered by the costs spent therein. Functions with " + "costs less than 1% are hidden on default.

")); + +#if ENABLE_DUMPDOCK + _dumpDock = new QDockWindow(QDockWindow::InDock, this); + _dumpDock->setCaption(i18n("Profile Dumps")); + _dumpDock->setCloseMode( QDockWindow::Always ); + _dumpSelection = new DumpSelection(this, _dumpDock, + "dumpSelection"); + _dumpSelection->setTopLevel(this); + + _dumpDock->setWidget(_dumpSelection); + _dumpDock->setResizeEnabled(true); + _dumpDock->setFixedExtentWidth(200); + QWhatsThis::add( _dumpSelection, i18n( + "Profile Dumps" + "

This dockable shows in the top part the list of " + "loadable profile dumps in all subdirectories of: " + "

  • current working directory of KCachegrind, " + "i.e. where it was started from, and " + "
  • the default profile dump directory given in the " + "configuration.
" + "The list is sorted according the the target command " + "profiled in the corresponding dump.

" + "

On selecting a profile dump, information for it " + "is shown in the bottom area of the dockable: " + "

  • Options allows you to view the profiled " + "command and profile options of this dump. By changing " + "any item, a new (yet unexisting) profile template " + "is created. Press Run Profile to start a" + "profile run with these options in the background. " + "
  • Info gives detailed info on the selected " + "dump like event cost summary and properties of the " + "simulated cache. " + "
  • State is only available for current happening " + "profiles runs. Press Update to see different " + "counters of the run, and a stack trace of the current " + "position in the program profiled. Check the Every " + "option to let KCachegrind regularly poll these data. " + "Check the Sync option to let the dockable activate " + "the top function in the current loaded dump.

")); +#endif + + // Restore QT Dock positions... + KConfigGroup dockConfig(KGlobal::config(), QCString("Docks")); + QString str = dockConfig.readEntry("Position", QString::null); + if (0) qDebug("Docks/Position: '%s'", str.ascii()); + if (str.isEmpty()) { + // default positions + addDockWindow(_partDock, DockLeft); + addDockWindow(_stackDock, DockLeft); + addDockWindow(_functionDock, DockLeft); + _stackDock->hide(); +#if ENABLE_DUMPDOCK + addDockWindow(_dumpDock, DockLeft); + _dumpDock->hide(); +#endif + } + else { + QTextStream ts( &str, IO_ReadOnly ); + ts >> *this; + } + _forcePartDock = dockConfig.readBoolEntry("ForcePartDockVisible", false); + +#if 0 + // dock context menu + setAppropriate(_partDock, true); + setAppropriate(_stackDock, true); + setAppropriate(_dumpDock, true); + setAppropriate(_functionDock, true); + + connect( _partDock, SIGNAL(contextMenuRequested(const QPoint &)), + this, SLOT(showDockMenu(const QPoint &))); +#endif +} + + +TopLevel::~TopLevel() +{ + delete _data; +} + + +void TopLevel::saveProperties(KConfig* c) +{ + c->writeEntry("TraceName", _data->traceName()); +} + +void TopLevel::readProperties(KConfig* c) +{ + QString traceName = c->readEntry("TraceName"); + if (!traceName.isEmpty()) { + TraceData* d = new TraceData(this); + d->load(traceName); + setData(d); + } +} + +void TopLevel::createLayoutActions() +{ + QString hint; + KAction* action; + + action = new KAction( i18n( "&Duplicate" ), + KShortcut(KKey("Ctrl+Plus")), + this, SLOT(layoutDuplicate()), + actionCollection(), "layout_duplicate" ); + hint = i18n("Duplicate Current Layout" + "

Make a copy of the current layout.

"); + action->setWhatsThis( hint ); + + action = new KAction( i18n( "&Remove" ), KShortcut(), + this, SLOT(layoutRemove()), + actionCollection(), "layout_remove" ); + hint = i18n("Remove Current Layout" + "

Delete current layout and make the previous active.

"); + action->setWhatsThis( hint ); + + action = new KAction( i18n( "&Go to Next" ), + KShortcut(KKey("Ctrl+Right")), + this, SLOT(layoutNext()), + actionCollection(), "layout_next" ); + hint = i18n("Go to Next Layout"); + action->setWhatsThis( hint ); + + action = new KAction( i18n( "&Go to Previous" ), + KShortcut(KKey("Ctrl+Left")), + this, SLOT(layoutPrevious()), + actionCollection(), "layout_previous" ); + hint = i18n("Go to Previous Layout"); + action->setWhatsThis( hint ); + + action = new KAction( i18n( "&Restore to Default" ), KShortcut(), + this, SLOT(layoutRestore()), + actionCollection(), "layout_restore" ); + hint = i18n("Restore Layouts to Default"); + action->setWhatsThis( hint ); + + action = new KAction( i18n( "&Save as Default" ), KShortcut(), + this, SLOT(layoutSave()), + actionCollection(), "layout_save" ); + hint = i18n("Save Layouts as Default"); + action->setWhatsThis( hint ); +} + +// TODO: split this up... +void TopLevel::createMiscActions() +{ + QString hint; + KAction* action; + + action = KStdAction::openNew(this, SLOT(newWindow()), actionCollection()); + hint = i18n("New

Open new empty KCachegrind window.

"); + action->setWhatsThis( hint ); + + action = new KAction( i18n( "&Add..." ), KShortcut(), + this, SLOT(addTrace()), + actionCollection(), "file_add" ); + hint = i18n("Add Profile Data" + "

This opens an additional profile data file in the current window.

"); + action->setWhatsThis( hint ); + + action = new KAction( i18n( "&Reload" ), "reload", +#if KDE_VERSION > 0x030190 + // for KDE 3.2: KStdAccel::key is deprecated + KStdAccel::shortcut(KStdAccel::Reload), +#else + KStdAccel::key(KStdAccel::Reload), +#endif + this, SLOT( reload() ), actionCollection(), "reload" ); + hint = i18n("Reload Profile Data" + "

This loads any new created parts, too.

"); + action->setWhatsThis( hint ); + + action = new KAction( i18n( "&Export Graph" ), KShortcut(), + this, SLOT(exportGraph()), + actionCollection(), "export" ); + + hint = i18n("Export Call Graph" + "

Generates a file with extension .dot for the tools " + "of the GraphViz package.

"); + action->setWhatsThis( hint ); + + + _taDump = new KToggleAction( i18n( "&Force Dump" ), "redo", +#if KDE_VERSION > 0x030190 + // for KDE 3.2: KStdAccel::key is deprecated + KStdAccel::shortcut(KStdAccel::Redo), +#else + KStdAccel::key(KStdAccel::Redo), +#endif + this, SLOT( forceTrace() ), + actionCollection(), "dump" ); + hint = i18n("Force Dump" + "

This forces a dump for a Callgrind profile run " + "in the current directory. This action is checked while " + "KCachegrind looks for the dump. If the dump is " + "finished, it automatically reloads the current trace. " + "If this is the one from the running Callgrind, the new " + "created trace part will be loaded, too.

" + "

Force dump creates a file 'callgrind.cmd', and " + "checks every second for its existence. A running " + "Callgrind will detect this file, dump a trace part, " + "and delete 'callgrind.cmd'. " + "The deletion is detected by KCachegrind, " + "and it does a Reload. If there's no Callgrind " + "running, press 'Force Dump' again to cancel the dump " + "request. This deletes 'callgrind.cmd' itself and " + "stops polling for a new dump.

" + "

Note: A Callgrind run only detects " + "existence of 'callgrind.cmd' when actively running " + "a few milliseconds, i.e. " + "not sleeping. Tip: For a profiled GUI program, " + "you can awake Callgrind e.g. by resizing a window " + "of the program.

"); + _taDump->setWhatsThis( hint ); + + action = KStdAction::open(this, SLOT(loadTrace()), actionCollection()); + hint = i18n("Open Profile Data" + "

This opens a profile data file, with possible multiple parts

"); + action->setToolTip( hint ); + action->setWhatsThis( hint ); + + _openRecent = KStdAction::openRecent(this, SLOT(loadTrace(const KURL&)), + actionCollection()); + + KStdAction::showStatusbar(this, + SLOT(toggleStatusBar()), actionCollection()); + + _partDockShown = new KToggleAction(i18n("Parts Overview"), KShortcut(), + this, SLOT(togglePartDock()), + actionCollection(), + "settings_show_partdock"); + + hint = i18n("Show/Hide the Parts Overview Dockable"); + _partDockShown->setToolTip( hint ); + _partDockShown->setWhatsThis( hint ); + + _stackDockShown = new KToggleAction(i18n("Call Stack"), KShortcut(), + this, SLOT(toggleStackDock()), + actionCollection(), + "settings_show_stackdock"); + + hint = i18n("Show/Hide the Call Stack Dockable"); + _stackDockShown->setToolTip( hint ); + _stackDockShown->setWhatsThis( hint ); + + _functionDockShown = new KToggleAction(i18n("Function Profile"), KShortcut(), + this, SLOT(toggleFunctionDock()), + actionCollection(), + "settings_show_profiledock"); + + hint = i18n("Show/Hide the Function Profile Dockable"); + _functionDockShown->setToolTip( hint ); + _functionDockShown->setWhatsThis( hint ); + +#if ENABLE_DUMPDOCK + _dumpDockShown = new KToggleAction(i18n("Profile Dumps"), KShortcut(), + this, SLOT(toggleDumpDock()), + actionCollection(), + "settings_show_dumpdock"); + + hint = i18n("Show/Hide the Profile Dumps Dockable"); + _dumpDockShown->setToolTip( hint ); + _dumpDockShown->setWhatsThis( hint ); +#endif + + _taPercentage = new KToggleAction(i18n("Show Relative Costs"), "percent", + KShortcut(), + this, SLOT(togglePercentage()), + actionCollection(), + "view_percentage"); +#if KDE_VERSION >= 0x030290 + // for KDE 3.3: show another text instead of a checkmark + _taPercentage->setCheckedState(i18n("Show Absolute Costs")); +#endif + + hint = i18n("Show relative instead of absolute costs"); + _taPercentage->setToolTip( hint ); + _taPercentage->setWhatsThis( hint ); + + _taExpanded = new KToggleAction(i18n("Percentage Relative to Parent"), "move", + KShortcut(), + this, SLOT(toggleExpanded()), + actionCollection(), + "view_expanded"); + + hint = i18n("Show percentage costs relative to parent"); + _taExpanded->setToolTip( hint ); + _taExpanded->setWhatsThis( hint ); + + hint = i18n("Show percentage costs relative to parent" + "

If this is switched off, percentage costs are always shown " + "relative to the total cost of the profile part(s) that are " + "currently browsed. By turning on this option, percentage cost " + "of shown cost items will be relative to the parent cost item." + "

    %1
    %1 %2 %3 %4
    %1%1server()->attachmentViewLink( (*it).id ).url() + + "\">" + i18n("View") + "server()->attachmentEditLink( (*it).id ).url() + + "\">" + i18n("Edit") + "
    " + name + "" + value + "
    " + "" + "" + "" + "" + "" + "
    Cost TypeParent Cost
    Function CumulativeTotal
    Function SelfFunction Group (*) / Total
    CallFunction Cumulative
    Source LineFunction Cumulative
    " + "

    (*) Only if function grouping is switched on (e.g. ELF object grouping)."); + _taExpanded->setWhatsThis( hint ); + + _taCycles = new KToggleAction( i18n( "Do Cycle Detection" ), "undo", + KShortcut(), + this, SLOT( toggleCycles() ), actionCollection(), + "view_cycles" ); +#if KDE_VERSION >= 0x030290 + // for KDE 3.3: show another text instead of a checkmark + _taCycles->setCheckedState(i18n("Skip Cycle Detection")); +#endif + + hint = i18n("Detect recursive cycles" + "

    If this is switched off, the treemap drawing will show " + "black areas when a recursive call is made instead of drawing the " + "recursion ad infinitum. Note that " + "the size of black areas often will be wrong, as inside recursive " + "cycles the cost of calls cannot be determined; the error is small, " + "however, for false cycles (see documentation)." + "

    The correct handling for cycles is to detect them and collapse all " + "functions of a cycle into a virtual function, which is done when this " + "option is selected. Unfortunately, with GUI applications, this often will " + "lead to huge false cycles, making the analysis impossible; therefore, there " + "is the option to switch this off."); + _taCycles->setWhatsThis( hint ); + + KStdAction::quit(this, SLOT(close()), actionCollection()); + KStdAction::preferences(this, SLOT(configure()), actionCollection()); + KStdAction::keyBindings(this, SLOT(configureKeys()), actionCollection()); + KStdAction::configureToolbars(this,SLOT(configureToolbars()), + actionCollection()); +#if 0 + action = KStdAction::back(_stackSelection, SLOT(browserBack()), + actionCollection()); + hint = i18n("Go back in function selection history"); + action->setToolTip( hint ); + action->setWhatsThis( hint ); + + action = KStdAction::forward(_stackSelection, SLOT(browserForward()), + actionCollection()); + hint = i18n("Go forward in function selection history"); + action->setToolTip( hint ); + action->setWhatsThis( hint ); + + action = KStdAction::up(_stackSelection, SLOT(browserUp()), + actionCollection()); + hint = i18n("Go Up" + "

    Go to last selected caller of current function. " + "If no caller was visited, use that with highest cost.

    "); + action->setToolTip( hint ); + action->setWhatsThis( hint ); +#else + _paUp = new KToolBarPopupAction( i18n( "&Up" ), "up", + ALT+Key_Up, + _stackSelection, SLOT( browserUp() ), + actionCollection(), "go_up" ); + connect( _paUp->popupMenu(), SIGNAL( aboutToShow() ), + this, SLOT( upAboutToShow() ) ); + connect( _paUp->popupMenu(), SIGNAL( activated( int ) ), + this, SLOT( upActivated( int ) ) ); + hint = i18n("Go Up" + "

    Go to last selected caller of current function. " + "If no caller was visited, use that with highest cost.

    "); + _paUp->setToolTip( hint ); + _paUp->setWhatsThis( hint ); + + QPair< KGuiItem, KGuiItem > backForward = KStdGuiItem::backAndForward(); + _paBack = new KToolBarPopupAction( backForward.first, ALT+Key_Left, + _stackSelection, SLOT(browserBack()), + actionCollection(), "go_back" ); + connect( _paBack->popupMenu(), SIGNAL( aboutToShow() ), + this, SLOT( backAboutToShow() ) ); + connect( _paBack->popupMenu(), SIGNAL( activated( int ) ), + this, SLOT( backActivated( int ) ) ); + hint = i18n("Go back in function selection history"); + _paBack->setToolTip( hint ); + _paBack->setWhatsThis( hint ); + + _paForward = new KToolBarPopupAction( backForward.second, ALT+Key_Right, + _stackSelection, + SLOT(browserForward()), + actionCollection(), "go_forward" ); + connect( _paForward->popupMenu(), SIGNAL( aboutToShow() ), + this, SLOT( forwardAboutToShow() ) ); + connect( _paForward->popupMenu(), SIGNAL( activated( int ) ), + this, SLOT( forwardActivated( int ) ) ); + hint = i18n("Go forward in function selection history"); + _paForward->setToolTip( hint ); + _paForward->setWhatsThis( hint ); +#endif + + _saCost = new KSelectAction( i18n("Primary Event Type"), KShortcut(), + actionCollection(), "view_cost_type"); + hint = i18n("Select primary event type of costs"); + _saCost->setComboWidth(300); + _saCost->setToolTip( hint ); + _saCost->setWhatsThis( hint ); + + // cost types are dependent on loaded data, thus KSelectAction + // is filled in setData() + connect( _saCost, SIGNAL(activated(const QString&)), + this, SLOT(costTypeSelected(const QString&))); + + _saCost2 = new KSelectAction( i18n("Secondary Event Type"), KShortcut(), + actionCollection(), "view_cost_type2"); + hint = i18n("Select secondary event type for cost e.g. shown in annotations"); + _saCost2->setComboWidth(300); + _saCost2->setToolTip( hint ); + _saCost2->setWhatsThis( hint ); + + connect( _saCost2, SIGNAL(activated(const QString&)), + this, SLOT(costType2Selected(const QString&))); + + saGroup = new KSelectAction( i18n("Grouping"), KShortcut(), + actionCollection(), "view_group_type"); + + hint = i18n("Select how functions are grouped into higher level cost items"); + saGroup->setToolTip( hint ); + saGroup->setWhatsThis( hint ); + + QStringList args; + + args << i18n("(No Grouping)") + << TraceCost::i18nTypeName(TraceItem::Object) + << TraceCost::i18nTypeName(TraceItem::File) + << TraceCost::i18nTypeName(TraceItem::Class) + << TraceCost::i18nTypeName(TraceItem::FunctionCycle); + + saGroup->setItems(args); + connect( saGroup, SIGNAL(activated(int)), + this, SLOT(groupTypeSelected(int))); + + _taSplit = new KToggleAction(i18n("Split"), "view_left_right", KShortcut(), + this, SLOT(splitSlot()), + actionCollection(), "view_split"); + + hint = i18n("Show two information panels"); + _taSplit->setToolTip( hint ); + _taSplit->setWhatsThis( hint ); + + _taSplitDir = new KToggleAction(i18n("Split Horizontal"), + "view_left_right", KShortcut(), + this, SLOT(splitDirSlot()), + actionCollection(), "view_split_dir"); + + hint = i18n("Change Split Orientation when main window is split."); + _taSplitDir->setToolTip( hint ); + _taSplitDir->setWhatsThis( hint ); + + // copied from KMail... +#if KDE_VERSION >= 308 // KDE 3.1 + KStdAction::tipOfDay( this, SLOT( slotShowTip() ), actionCollection() ); +#else + (void) new KAction( KGuiItem( i18n("Tip of the &Day..."), "idea", + i18n("Show \"Tip of the Day\"") ), + 0, this, SLOT(slotShowTip()), + actionCollection(), "help_show_tip" ); +#endif +} + +void TopLevel::createActions() +{ + createMiscActions(); + createLayoutActions(); +} + +void TopLevel::toggleStatusBar() +{ + if (statusBar()->isVisible()) + statusBar()->hide(); + else + statusBar()->show(); +} + +void TopLevel::togglePartDock() +{ + if (!_partDock->isVisible()) + _partDock->show(); + else + _partDock->hide(); +} + +void TopLevel::toggleStackDock() +{ + if (!_stackDock->isVisible()) + _stackDock->show(); + else + _stackDock->hide(); +} + +void TopLevel::toggleDumpDock() +{ +#if ENABLE_DUMPDOCK + if (!_dumpDock->isVisible()) + _dumpDock->show(); + else + _dumpDock->hide(); +#endif +} + +void TopLevel::toggleFunctionDock() +{ + if (!_functionDock->isVisible()) + _functionDock->show(); + else + _functionDock->hide(); +} + +void TopLevel::togglePercentage() +{ + setPercentage(_taPercentage->isChecked()); +} + +void TopLevel::setAbsoluteCost() +{ + setPercentage(false); +} + +void TopLevel::setRelativeCost() +{ + setPercentage(true); +} + +void TopLevel::setPercentage(bool show) +{ + if (_showPercentage == show) return; + _showPercentage = show; + if (_taPercentage->isChecked() != show) + _taPercentage->setChecked(show); + + // FIXME: Delete when no view gets this config from Configuration + Configuration::setShowPercentage(_showPercentage); + + _partSelection->refresh(); + _stackSelection->refresh(); + + _functionSelection->notifyChange(TraceItemView::configChanged); + _functionSelection->updateView(); + + _multiView->notifyChange(TraceItemView::configChanged); + _multiView->updateView(); +} + +void TopLevel::toggleExpanded() +{ + bool show = _taExpanded->isChecked(); + if (_showExpanded == show) return; + _showExpanded = show; + + // FIXME: Delete when no view gets this config from Configuration + Configuration::setShowExpanded(_showExpanded); + + _partSelection->refresh(); + _stackSelection->refresh(); + + _functionSelection->notifyChange(TraceItemView::configChanged); + _functionSelection->updateView(); + + _multiView->notifyChange(TraceItemView::configChanged); + _multiView->updateView(); +} + +void TopLevel::toggleCycles() +{ + bool show = _taCycles->isChecked(); + if (_showCycles == show) return; + _showCycles = show; + + // FIXME: Delete when no view gets this config from Configuration + Configuration::setShowCycles(_showCycles); + + if (!_data) return; + + _data->invalidateDynamicCost(); + _data->updateFunctionCycles(); + + _partSelection->refresh(); + _stackSelection->rebuildStackList(); + + _functionSelection->notifyChange(TraceItemView::configChanged); + _functionSelection->updateView(); + + _multiView->notifyChange(TraceItemView::configChanged); + _multiView->updateView(); +} + +void TopLevel::partVisibilityChanged(bool v) +{ + _partDockShown->setChecked(v); +} + +void TopLevel::stackVisibilityChanged(bool v) +{ + _stackDockShown->setChecked(v); +} + +#if ENABLE_DUMPDOCK +void TopLevel::dumpVisibilityChanged(bool v) +#else +void TopLevel::dumpVisibilityChanged(bool) +#endif +{ +#if ENABLE_DUMPDOCK + _dumpDockShown->setChecked(v); +#endif +} + +void TopLevel::functionVisibilityChanged(bool v) +{ + _functionDockShown->setChecked(v); + if (v) + _functionSelection->updateView(); +} + + +void TopLevel::querySlot() +{ + _functionSelection->query(queryLineEdit->text()); +} + +void TopLevel::configureKeys() +{ +#if KDE_VERSION > 0x030190 + // for KDE 3.2: KKeyDialog::configureKeys is deprecated + KKeyDialog::configure(actionCollection(), this, true); +#else + KKeyDialog::configureKeys(actionCollection(), xmlFile(), true, this); +#endif +} + + +void TopLevel::configureToolbars() +{ + KEditToolbar *dlg = new KEditToolbar(guiFactory(),this); + + if (dlg->exec()) + createGUI(); + + delete dlg; +} + + +void TopLevel::newTrace() +{ + // start cachegrind on command... +} + +void TopLevel::newWindow() +{ + TopLevel* t = new TopLevel(0); + t->show(); +} + + +void TopLevel::loadTrace() +{ + KURL url = KFileDialog::getOpenURL(":", + i18n("cachegrind.out* callgrind.out*|Callgrind Profile Data\n*|All Files"), + this, + i18n("Select Callgrind Profile Data")); + loadTrace(url); +} + +void TopLevel::loadTrace(const KURL& url) +{ + if (url.isEmpty()) return; + + // network transparancy + QString tmpFile; +#if KDE_VERSION > 0x030190 + // for KDE 3.2: KIO::NetAccess::download with 2 args is deprecated + if(KIO::NetAccess::download( url, tmpFile, this )) { +#else + if(KIO::NetAccess::download( url, tmpFile )) { +#endif + _openRecent->addURL(url); + _openRecent->saveEntries( KGlobal::config() ); + + loadTrace(tmpFile); + KIO::NetAccess::removeTempFile( tmpFile ); + } +} + +void TopLevel::loadTrace(QString file) +{ + if (file.isEmpty()) return; + + if (_data && _data->parts().count()>0) { + + // In new window + TopLevel* t = new TopLevel(); + t->show(); + t->loadDelayed(file); + return; + } + + // this constructor enables progress bar callbacks + TraceData* d = new TraceData(this); + d->load(file); + setData(d); +} + + +void TopLevel::addTrace() +{ + KURL url = KFileDialog::getOpenURL(QString::null, + i18n("cachegrind.out* callgrind.out*|Callgrind Profile Data\n*|All Files"), + this, + i18n("Add Callgrind Profile Data")); + addTrace(url); +} + +void TopLevel::addTrace(const KURL& url) +{ + if (url.isEmpty()) return; + + // network transparancy + QString tmpFile; +#if KDE_VERSION > 0x030190 + // for KDE 3.2: KIO::NetAccess::download with 2 args is deprecated + if(KIO::NetAccess::download( url, tmpFile, this )) { +#else + if(KIO::NetAccess::download( url, tmpFile )) { +#endif + _openRecent->addURL(url); + _openRecent->saveEntries( KGlobal::config() ); + + addTrace(tmpFile); + KIO::NetAccess::removeTempFile( tmpFile ); + } +} + +void TopLevel::addTrace(QString file) +{ + if (file.isEmpty()) return; + + if (_data) { + _data->load(file); + + // GUI update for added data + configChanged(); + return; + } + + // this constructor enables progress bar callbacks + TraceData* d = new TraceData(this); + d->load(file); + setData(d); +} + + + +void TopLevel::loadDelayed(QString file) +{ + _loadTraceDelayed = file; + QTimer::singleShot(0, this, SLOT(loadTraceDelayed())); +} + +void TopLevel::loadTraceDelayed() +{ + if (_loadTraceDelayed.isEmpty()) return; + + loadTrace(_loadTraceDelayed); + _loadTraceDelayed = QString::null; +} + + +void TopLevel::reload() +{ + QString trace; + if (!_data || _data->parts().count()==0) + trace = "."; // open first trace found in dir + else + trace = _data->traceName(); + + // this also keeps sure we have the same browsing position... + TraceData* d = new TraceData(this); + d->load(trace); + setData(d); +} + +void TopLevel::exportGraph() +{ + if (!_data || !_function) return; + + QString n = QString("callgraph.dot"); + GraphExporter ge(_data, _function, _costType, _groupType, n); + ge.writeDot(); + + QString cmd = QString("(dot %1 -Tps > %2.ps; kghostview %3.ps)&") + .arg(n).arg(n).arg(n); + system(QFile::encodeName( cmd )); +} + + +bool TopLevel::setCostType(QString s) +{ + TraceCostType* ct; + + ct = (_data) ? _data->mapping()->type(s) : 0; + + // if costtype with given name not found, use first available + if (!ct && _data) ct = _data->mapping()->type(0); + + return setCostType(ct); +} + +bool TopLevel::setCostType2(QString s) +{ + TraceCostType* ct; + + // Special type i18n("(Hidden)") gives 0 + ct = (_data) ? _data->mapping()->type(s) : 0; + + return setCostType2(ct); +} + +void TopLevel::costTypeSelected(const QString& s) +{ + TraceCostType* ct; + + ct = (_data) ? _data->mapping()->typeForLong(s) : 0; + setCostType(ct); +} + +void TopLevel::costType2Selected(const QString& s) +{ + TraceCostType* ct; + + ct = (_data) ? _data->mapping()->typeForLong(s) : 0; + setCostType2(ct); +} + +bool TopLevel::setCostType(TraceCostType* ct) +{ + if (_costType == ct) return false; + _costType = ct; + + if (ct) { + int idx=0; + QStringList l = _saCost->items(); + for (QStringList::Iterator it = l.begin(); it != l.end(); ++it, ++idx ) { + if (*it == ct->longName()) + _saCost->setCurrentItem(idx); + } + } + + _partSelection->setCostType(_costType); + _stackSelection->setCostType(_costType); + + _functionSelection->setCostType(_costType); + _functionSelection->updateView(); + + _multiView->setCostType(_costType); + _multiView->updateView(); + + updateStatusBar(); + + return true; +} + +bool TopLevel::setCostType2(TraceCostType* ct) +{ + if (_costType2 == ct) return false; + _costType2 = ct; + + QString longName = ct ? ct->longName() : i18n("(Hidden)"); + + int idx=0; + QStringList l = _saCost2->items(); + for (QStringList::Iterator it = l.begin(); it != l.end(); ++it, ++idx ) { + if (*it == longName) + _saCost2->setCurrentItem(idx); + } + + _partSelection->setCostType2(_costType2); + _stackSelection->setCostType2(_costType2); + + _functionSelection->setCostType2(_costType2); + _functionSelection->updateView(); + + _multiView->setCostType2(_costType2); + _multiView->updateView(); + + updateStatusBar(); + + return true; +} + + +void TopLevel::groupTypeSelected(int cg) +{ + switch(cg) { + case 0: setGroupType( TraceItem::Function ); break; + case 1: setGroupType( TraceItem::Object ); break; + case 2: setGroupType( TraceItem::File ); break; + case 3: setGroupType( TraceItem::Class ); break; + case 4: setGroupType( TraceItem::FunctionCycle ); break; + default: break; + } +} + +bool TopLevel::setGroupType(QString s) +{ + TraceItem::CostType gt; + + gt = (_data) ? _data->costType(s) : TraceData::costType(s); + // only allow Function/Object/File/Class as grouptype + switch(gt) { + case TraceItem::Object: + case TraceItem::File: + case TraceItem::Class: + case TraceItem::FunctionCycle: + break; + default: + gt = TraceItem::Function; + } + + return setGroupType(gt); +} + +bool TopLevel::setGroupType(TraceItem::CostType gt) +{ + if (_groupType == gt) return false; + _groupType = gt; + + int idx = -1; + switch(gt) { + case TraceItem::Function: idx = 0; break; + case TraceItem::Object: idx = 1; break; + case TraceItem::File: idx = 2; break; + case TraceItem::Class: idx = 3; break; + case TraceItem::FunctionCycle: idx = 4; break; + default: + break; + } + + if (idx==-1) return false; + + if (saGroup->currentItem() != idx) + saGroup->setCurrentItem(idx); + + _stackSelection->setGroupType(_groupType); + _partSelection->setGroupType(_groupType); + + _functionSelection->set(_groupType); + _functionSelection->updateView(); + + _multiView->set(_groupType); + _multiView->updateView(); + + updateStatusBar(); + + return true; +} + +bool TopLevel::setGroup(QString s) +{ + return true; + TraceCostItem* ci = _functionSelection->group(s); + if (!ci) + return false; + + return setGroup(ci); +} + + +bool TopLevel::setGroup(TraceCostItem* g) +{ + _multiView->activate(g); + _multiView->updateView(); + _functionSelection->activate(g); + _functionSelection->updateView(); + + if (_group == g) return false; + _group = g; + + + updateStatusBar(); + + return true; +} + +bool TopLevel::setFunction(QString s) +{ + if (!_data) return false; + + TraceCost* f = _data->search(TraceItem::Function, s, _costType); + if (!f) return false; + + return setFunction((TraceFunction*)f); +} + +bool TopLevel::setFunction(TraceFunction* f) +{ + _multiView->activate(f); + _multiView->updateView(); + + _functionSelection->activate(f); + _functionSelection->updateView(); + + if (_function == f) return false; + _function = f; + + _partSelection->setFunction(_function); + _stackSelection->setFunction(_function); + + StackBrowser* b = _stackSelection->browser(); + if (b) { + // don't disable up: a press forces stack-up extending... + _paForward->setEnabled(b->canGoForward()); + _paBack->setEnabled(b->canGoBack()); + } + +#if TRACE_UPDATES + qDebug("TopLevel::setFunction(%s), lastSender %s", + f ? f->prettyName().ascii() : "0", + _lastSender ? _lastSender->name() :"0" ); +#endif + + return true; +} + + +/** + * Delayed versions. + * We always have a pair of slots: One receiver to start the + * delay with a singleShot Timer. It stores the parameter into a + * temporary variable. And one parameterless slot for + * forwarding, using this temporary. + */ +void TopLevel::setCostTypeDelayed(TraceCostType* ct) +{ + _costTypeDelayed = ct; + QTimer::singleShot (0, this, SLOT(setCostTypeDelayed())); +} + +void TopLevel::setCostType2Delayed(TraceCostType* ct) +{ + _costType2Delayed = ct; + QTimer::singleShot (0, this, SLOT(setCostType2Delayed())); +} + +void TopLevel::setCostTypeDelayed() +{ + setCostType(_costTypeDelayed); +} + +void TopLevel::setCostType2Delayed() +{ + setCostType2(_costType2Delayed); +} + +void TopLevel::setGroupTypeDelayed(TraceItem::CostType gt) +{ + _groupTypeDelayed = gt; + QTimer::singleShot (0, this, SLOT(setGroupTypeDelayed())); +} + +void TopLevel::setGroupTypeDelayed() +{ + setGroupType(_groupTypeDelayed); +} + +void TopLevel::setGroupDelayed(TraceCostItem* g) +{ +#if TRACE_UPDATES + qDebug("TopLevel::setGroupDelayed(%s), sender %s", + g ? g->prettyName().ascii() : "0", + _lastSender ? _lastSender->name() :"0" ); +#endif + + _groupDelayed = g; + QTimer::singleShot (0, this, SLOT(setGroupDelayed())); +} + +void TopLevel::setGroupDelayed() +{ + setGroup(_groupDelayed); +} + +void TopLevel::setDirectionDelayed(TraceItemView::Direction d) +{ + _directionDelayed = d; + QTimer::singleShot (0, this, SLOT(setDirectionDelayed())); +} + +void TopLevel::setDirectionDelayed() +{ + switch(_directionDelayed) { + case TraceItemView::Back: + _stackSelection->browserBack(); + break; + + case TraceItemView::Forward: + _stackSelection->browserForward(); + break; + + case TraceItemView::Up: + { + StackBrowser* b = _stackSelection ? _stackSelection->browser() : 0; + HistoryItem* hi = b ? b->current() : 0; + TraceFunction* f = hi ? hi->function() : 0; + + if (!f) break; + f = hi->stack()->caller(f, false); + if (f) setFunction(f); + } + break; + + default: break; + } + + _directionDelayed = TraceItemView::None; +} + + +void TopLevel::setTraceItemDelayed(TraceItem* i) +{ + // no need to select same item a 2nd time... + if (_traceItemDelayed == i) return; + _traceItemDelayed = i; + _lastSender = sender(); + + kdDebug() << "Selected " << (i ? i->prettyName() : "(none)") << endl; + +#if TRACE_UPDATES + qDebug("TopLevel::setTraceItemDelayed(%s), sender %s", + i ? i->prettyName().ascii() : "0", + _lastSender ? _lastSender->name() :"0" ); +#endif + + QTimer::singleShot (0, this, SLOT(setTraceItemDelayed())); +} + +void TopLevel::setTraceItemDelayed() +{ + if (!_traceItemDelayed) return; + + switch(_traceItemDelayed->type()) { + case TraceItem::Function: + case TraceItem::FunctionCycle: + setFunction((TraceFunction*)_traceItemDelayed); + break; + + case TraceItem::Object: + case TraceItem::File: + case TraceItem::Class: + setGroup((TraceCostItem*)_traceItemDelayed); + break; + +#if 0 + // this conflicts with the selection policy of InstrView ?!? + case TraceItem::Instr: + case TraceItem::Line: + // only for multiview + _multiView->activate(_traceItemDelayed); + _multiView->updateView(); + break; +#endif + + default: break; + } + + _traceItemDelayed = 0; + _lastSender = 0; +} + +/** + * A TraceData object cannot be viewed many times in different + * toplevel windows. Thus, this toplevel window takes ownership + * of the TraceData object: on closing the window or opening + * another trace, the object is destroyed. + */ +void TopLevel::setData(TraceData* data) +{ + if (data == _data) return; + + _lastSender = 0; + + saveTraceSettings(); + + if (_data) { + _partSelection->setData(0); + _stackSelection->setData(0); + + _functionSelection->setData(0); + _functionSelection->updateView(); + _multiView->setData(0); + _multiView->updateView(); + + // we are the owner... + delete _data; + } + + // reset members + init(); + + _data = data; + + // fill cost type list + QStringList types; + + if (_data) { + /* add all supported virtual types */ + TraceCostMapping* m = _data->mapping(); + m->addKnownVirtualTypes(); + + /* first, fill selection list with available cost types */ + for (int i=0;irealCount();i++) + types << m->realType(i)->longName(); + for (int i=0;ivirtualCount();i++) + types << m->virtualType(i)->longName(); + } + _saCost->setItems(types); + _saCost->setComboWidth(300); + + if (types.count()>0) { + // second type list gets an additional "(Hidden)" + types.prepend(i18n("(Hidden)")); + } + _saCost2->setItems(types); + _saCost2->setComboWidth(300); + // default is hidden + if (types.count()>0) + _saCost2->setCurrentItem(0); + + _partSelection->setData(_data); + _stackSelection->setData(_data); + _functionSelection->setData(_data); + _functionSelection->updateView(); + _multiView->setData(_data); + _multiView->updateView(); + + /* this is needed to let the other widgets know the types */ + restoreTraceTypes(); + + restoreTraceSettings(); + + QString caption; + if (_data) { + caption = _data->traceName(); + if (!_data->command().isEmpty()) + caption += " [" + _data->command() + "]"; + } + setCaption(caption); + + if (!_data || (!_forcePartDock && _data->parts().count()<2)) { + _partDock->hide(); + _partDockShown->setChecked(false); + } + else { + _partDock->show(); + _partDockShown->setChecked(true); + } + + updateStatusBar(); +} + +void TopLevel::addCostMenu(QPopupMenu* popup, bool withCost2) +{ + if (_data) { + QPopupMenu *popup1 = new QPopupMenu(popup); + QPopupMenu *popup2 = 0; + popup1->setCheckable(true); + + if (withCost2) { + popup2 = new QPopupMenu(popup); + popup2->setCheckable(true); + + if (_costType2) { + popup2->insertItem(i18n("Hide"),199); + popup2->insertSeparator(); + } + } + + TraceCostMapping* m = _data->mapping(); + TraceCostType* ct; + for (int i=0;irealCount();i++) { + ct = m->realType(i); + popup1->insertItem(ct->longName(), 100+i); + if (_costType == ct) popup1->setItemChecked(100+i,true); + if (popup2) { + popup2->insertItem(ct->longName(), 100+i); + if (_costType2 == ct) popup2->setItemChecked(100+i,true); + } + } + for (int i=0;ivirtualCount();i++) { + ct = m->virtualType(i); + popup1->insertItem(ct->longName(), 200+i); + if (_costType == ct) popup1->setItemChecked(200+i,true); + if (popup2) { + popup2->insertItem(ct->longName(), 200+i); + if (_costType2 == ct) popup2->setItemChecked(200+i,true); + } + } + popup->insertItem(i18n("Primary Event Type"), popup1); + connect(popup1,SIGNAL(activated(int)),this,SLOT(setCostType(int))); + if (popup2) { + popup->insertItem(i18n("Secondary Event Type"), popup2); + connect(popup2,SIGNAL(activated(int)),this,SLOT(setCostType2(int))); + } + } + if (_showPercentage) + popup->insertItem(i18n("Show Absolute Cost"), + this, SLOT(setAbsoluteCost())); + else + popup->insertItem(i18n("Show Relative Cost"), + this, SLOT(setRelativeCost())); +} + +bool TopLevel::setCostType(int id) +{ + if (!_data) return false; + + TraceCostMapping* m = _data->mapping(); + TraceCostType* ct=0; + if (id >=100 && id<199) ct = m->realType(id-100); + if (id >=200 && id<299) ct = m->virtualType(id-200); + + return ct ? setCostType(ct) : false; +} + +bool TopLevel::setCostType2(int id) +{ + if (!_data) return false; + + TraceCostMapping* m = _data->mapping(); + TraceCostType* ct=0; + if (id >=100 && id<199) ct = m->realType(id-100); + if (id >=200 && id<299) ct = m->virtualType(id-200); + + return setCostType2(ct); +} + +void TopLevel::addGoMenu(QPopupMenu* popup) +{ + popup->insertItem(i18n("Go Back"), this, SLOT(goBack())); + popup->insertItem(i18n("Go Forward"), this, SLOT(goForward())); + popup->insertItem(i18n("Go Up"), this, SLOT(goUp())); +} + +void TopLevel::goBack() +{ + setDirectionDelayed(TraceItemView::Back); +} + +void TopLevel::goForward() +{ + setDirectionDelayed(TraceItemView::Forward); +} + +void TopLevel::goUp() +{ + setDirectionDelayed(TraceItemView::Up); +} + +QString TopLevel::traceKey() +{ + if (!_data || _data->command().isEmpty()) return QString::null; + + QString name = _data->command(); + QString key; + for (unsigned int l=0;litems().isEmpty()) + costTypeSelected(_saCost->items().first()); + + KConfigGroup aConfig(KGlobal::config(), QCString("Layouts")); + _layoutCount = aConfig.readNumEntry(QString("Count%1").arg(key), 0); + _layoutCurrent = aConfig.readNumEntry(QString("Current%1").arg(key), 0); + if (_layoutCount == 0) layoutRestore(); + updateLayoutActions(); +} + + +/** + * This must be called after setting group/cost types in the function + * selection widget, because the group/function choosing depends on + * filled lists in the function selection widget + */ +void TopLevel::restoreTraceSettings() +{ + if (!_data) return; + + QString key = traceKey(); + + KConfigGroup pConfig(KGlobal::config(), QCString("TracePositions")); + QString group = pConfig.readEntry(QString("Group%1").arg(key)); + if (!group.isEmpty()) setGroup(group); + + restoreCurrentState(key); + + // restoreCurrentState() usually leads to a call to setTraceItemDelayed() + // to restore last active item... + if (!_traceItemDelayed) { + // function not available any more.. try with "main" + if (!setFunction("main")) + _functionSelection->setTopFunction(); + } +} + + +/* Layout */ + +void TopLevel::layoutDuplicate() +{ + // save current and allocate a new slot + _multiView->saveViewConfig(KGlobal::config(), + QString("Layout%1-MainView").arg(_layoutCurrent), + traceKey(), false); + _layoutCurrent = _layoutCount; + _layoutCount++; + + updateLayoutActions(); + + kdDebug() << "TopLevel::layoutDuplicate: count " << _layoutCount << endl; +} + +void TopLevel::layoutRemove() +{ + if (_layoutCount <2) return; + + int from = _layoutCount-1; + if (_layoutCurrent == from) { _layoutCurrent--; from--; } + // restore from last and decrement count + _multiView->readViewConfig(KGlobal::config(), + QString("Layout%1-MainView").arg(from), + traceKey(), false); + _layoutCount--; + + updateLayoutActions(); +} + +void TopLevel::layoutNext() +{ + if (_layoutCount <2) return; + + KConfig* config = KGlobal::config(); + QString key = traceKey(); + + _multiView->saveViewConfig(config, + QString("Layout%1-MainView").arg(_layoutCurrent), + key, false); + _layoutCurrent++; + if (_layoutCurrent == _layoutCount) _layoutCurrent = 0; + + _multiView->readViewConfig(config, + QString("Layout%1-MainView").arg(_layoutCurrent), + key, false); + + if (0) kdDebug() << "TopLevel::layoutNext: current " + << _layoutCurrent << endl; +} + +void TopLevel::layoutPrevious() +{ + if (_layoutCount <2) return; + + KConfig* config = KGlobal::config(); + QString key = traceKey(); + + _multiView->saveViewConfig(config, + QString("Layout%1-MainView").arg(_layoutCurrent), + key, false); + _layoutCurrent--; + if (_layoutCurrent <0) _layoutCurrent = _layoutCount-1; + + _multiView->readViewConfig(config, + QString("Layout%1-MainView").arg(_layoutCurrent), + key, false); + + if (0) kdDebug() << "TopLevel::layoutPrevious: current " + << _layoutCurrent << endl; +} + +void TopLevel::layoutSave() +{ + KConfig* config = KGlobal::config(); + QString key = traceKey(); + + _multiView->saveViewConfig(config, + QString("Layout%1-MainView").arg(_layoutCurrent), + key, false); + + for(int i=0;i<_layoutCount;i++) { + _multiView->readViewConfig(config, + QString("Layout%1-MainView").arg(i), + key, false); + _multiView->saveViewConfig(config, + QString("Layout%1-MainView").arg(i), + QString(), false); + } + + _multiView->readViewConfig(config, + QString("Layout%1-MainView").arg(_layoutCurrent), + key, false); + + KConfigGroup aConfig(config, QCString("Layouts")); + aConfig.writeEntry("DefaultCount", _layoutCount); + aConfig.writeEntry("DefaultCurrent", _layoutCurrent); +} + +void TopLevel::layoutRestore() +{ + KConfig* config = KGlobal::config(); + KConfigGroup aConfig(config, QCString("Layouts")); + _layoutCount = aConfig.readNumEntry("DefaultCount", 0); + _layoutCurrent = aConfig.readNumEntry("DefaultCurrent", 0); + if (_layoutCount == 0) { + _layoutCount++; + return; + } + + QString key = traceKey(); + for(int i=0;i<_layoutCount;i++) { + _multiView->readViewConfig(config, + QString("Layout%1-MainView").arg(i), + QString(), false); + _multiView->saveViewConfig(config, + QString("Layout%1-MainView").arg(i), + key, false); + } + + _multiView->readViewConfig(config, + QString("Layout%1-MainView").arg(_layoutCurrent), + key, false); + + updateLayoutActions(); +} + + +void TopLevel::updateLayoutActions() +{ + KAction* ka; + + ka = actionCollection()->action("layout_next"); + if (ka) ka->setEnabled(_layoutCount>1); + + ka = actionCollection()->action("layout_previous"); + if (ka) ka->setEnabled(_layoutCount>1); + + ka = actionCollection()->action("layout_remove"); + if (ka) ka->setEnabled(_layoutCount>1); + + _statusbar->message(i18n("Layout Count: %1").arg(_layoutCount), 1000); +} + + +void TopLevel::updateStatusBar() +{ + if (!_data || _data->parts().count()==0) { + _statusLabel->setText(i18n("No profile data file loaded.")); + return; + } + + QString status = QString("%1 [%2] - ") + .arg(_data->shortTraceName()) + .arg(_data->activePartRange()); + + if (_costType) { + status += i18n("Total %1 Cost: %2") + .arg(_costType->longName()) + .arg(_data->prettySubCost(_costType)); + + /* this gets too long... + if (_costType2 && (_costType2 != _costType)) + status += i18n(", %1 Cost: %2") + .arg(_costType2->longName()) + .arg(_data->prettySubCost(_costType2)); + */ + } + else + status += i18n("No event type selected"); + + /* Not working... should give group of selected function + + if (_groupType != TraceItem::Function) { + status += QString(" - %1 '%2'") + .arg(TraceItem::i18nTypeName(_groupType)) + .arg(_group ? _group->prettyName() : i18n("(None)")); + } + */ + + _statusLabel->setText(status); +} + +void TopLevel::configure() +{ + if (ConfigDlg::configure(Configuration::config(), _data, this)) { + Configuration::saveOptions(KGlobal::config()); + + configChanged(); + } + else + Configuration::readOptions(KGlobal::config()); +} + +bool TopLevel::queryClose() +{ + saveTraceSettings(); + + return true; +} + +bool TopLevel::queryExit() +{ + // save current toplevel options as defaults... + Configuration::setShowPercentage(_showPercentage); + Configuration::setShowExpanded(_showExpanded); + Configuration::setShowCycles(_showCycles); + Configuration::saveOptions(KGlobal::config()); + + saveCurrentState(QString::null); + + // save QT dock positions... + + // We don't want to save the KToolbar position here. + // Its already stored. + delete toolBar(); + + KConfigGroup dockConfig(KGlobal::config(), QCString("Docks")); + QString str; + QTextStream ts( &str, IO_WriteOnly ); + ts << *this; +#if 1 + dockConfig.writeEntry("Position", str); +#else + /* We store this with a localized key because for dock positions, + * QT uses the localized captions of docks. + * This way, when changing languages, you don't loose dock position + * settings. + * For the retrieval to work, we need to store a non-localized. + */ + dockConfig.writeEntry("Position", str, true, false, true); +#endif + + // if part dock was chosen visible even for only 1 part loaded, + // keep this choice... + _forcePartDock = false; + if (_data && (_data->parts().count()<2) && _partDock->isVisible()) + _forcePartDock=true; + dockConfig.writeEntry("ForcePartDockVisible", _forcePartDock); + + return true; +} + + +void TopLevel::splitSlot() +{ + int count = _multiView->childCount(); + if (count<1) count = 1; + if (count>2) count = 2; + count = 3-count; + _multiView->setChildCount(count); + + _taSplit->setChecked(count>1); + _taSplitDir->setEnabled(count>1); + _taSplitDir->setChecked(_multiView->orientation() == Qt::Horizontal); +} + +void TopLevel::splitDirSlot() +{ + _multiView->setOrientation( _taSplitDir->isChecked() ? + Qt::Horizontal : Qt::Vertical ); +} + + + +// this is called after a config change in the dialog +void TopLevel::configChanged() +{ + //qDebug("TopLevel::configChanged"); + //_showPercentage->setChecked(Configuration::showPercentage()); + + // invalidate found/cached dirs of source files + _data->resetSourceDirs(); + + _partSelection->refresh(); + _stackSelection->refresh(); + + _functionSelection->notifyChange(TraceItemView::configChanged); + _functionSelection->updateView(); + + _multiView->notifyChange(TraceItemView::configChanged); + _multiView->updateView(); +} + +void TopLevel::slotShowTipOnStart() { + KTipDialog::showTip(this); +} + +void TopLevel::slotShowTip() { + KTipDialog::showTip( this, QString::null, true ); +} + +void TopLevel::dummySlot() +{ +} + +void TopLevel::activePartsChangedSlot(const TracePartList& list) +{ + if (!_data) return; + + if (!_data->activateParts(list)) { +// qDebug("TopLevel::activePartsChangedSlot: No Change!"); + return; + } + _activeParts = list; + + _partSelection->activePartsChangedSlot(list); + + _multiView->set(list); + _multiView->updateView(); + + _functionSelection->set(list); + _functionSelection->updateView(); + + _stackSelection->refresh(); + + updateStatusBar(); +} + +void TopLevel::partsHideSelectedSlotDelayed() +{ + QTimer::singleShot( 0, this, SLOT(partsHideSelectedSlot()) ); +} + +// this puts selected parts into hidden list, +// deselects them and makes the remaining parts selected +void TopLevel::partsHideSelectedSlot() +{ + if (!_data) return; + + TracePart* part; + TracePartList newHidden, newActive; + TracePartList l = _data->parts(); + for (part=l.first();part;part=l.next()) { + if ((_activeParts.findRef(part)>=0) || + (_hiddenParts.findRef(part)>=0)) + newHidden.append(part); + else + newActive.append(part); + } + + _hiddenParts = newHidden; + _partSelection->hiddenPartsChangedSlot(_hiddenParts); + +#if 0 + _mainWidget1->hiddenPartsChangedSlot(_hiddenParts); + _mainWidget2->hiddenPartsChangedSlot(_hiddenParts); +#endif + + activePartsChangedSlot(newActive); +} + +void TopLevel::partsUnhideAllSlotDelayed() +{ + QTimer::singleShot( 0, this, SLOT(partsUnhideAllSlot()) ); +} + +// this unhides all hidden parts. Does NOT change selection +void TopLevel::partsUnhideAllSlot() +{ + if (!_data) return; + + _hiddenParts.clear(); + _partSelection->hiddenPartsChangedSlot(_hiddenParts); +#if 0 + _mainWidget1->hiddenPartsChangedSlot(_hiddenParts); + _mainWidget2->hiddenPartsChangedSlot(_hiddenParts); +#endif +} + +void TopLevel::forceTrace() +{ +// qDebug("forceTrace"); + + // Needs Callgrind now... + QFile cmd("callgrind.cmd"); + if (!cmd.exists()) { + cmd.open(IO_WriteOnly); + cmd.writeBlock("DUMP\n", 5); + cmd.close(); + } + if (_taDump->isChecked()) + QTimer::singleShot( 1000, this, SLOT(forceTraceReload()) ); + else { + // cancel request + cmd.remove(); + } + +} + +void TopLevel::forceTraceReload() +{ +// qDebug("forceTraceReload"); + + QFile cmd("callgrind.cmd"); + if (cmd.exists()) { + if (_taDump->isChecked()) + QTimer::singleShot( 1000, this, SLOT(forceTraceReload()) ); + return; + } + _taDump->setChecked(false); + reload(); +} + +void TopLevel::forwardAboutToShow() +{ + QPopupMenu *popup = _paForward->popupMenu(); + + popup->clear(); + StackBrowser* b = _stackSelection ? _stackSelection->browser() : 0; + HistoryItem* hi = b ? b->current() : 0; + TraceFunction* f; + + if (!hi) { + popup->insertItem(i18n("(No Stack)")); + return; + } + + hi = hi->next(); + if (!hi) { + popup->insertItem(i18n("(No next function)")); + return; + } + + int count = 1; + while (countfunction(); + if (!f) break; + + QString name = f->prettyName(); + if ((int)name.length()>Configuration::maxSymbolLength()) + name = name.left(Configuration::maxSymbolLength()) + "..."; + + //qDebug("forward: Adding %s", name.ascii()); + popup->insertItem(name, count); + hi = hi->next(); + count++; + } +} + +void TopLevel::backAboutToShow() +{ + QPopupMenu *popup = _paBack->popupMenu(); + + popup->clear(); + StackBrowser* b = _stackSelection ? _stackSelection->browser() : 0; + HistoryItem* hi = b ? b->current() : 0; + TraceFunction* f; + + if (!hi) { + popup->insertItem(i18n("(No Stack)")); + return; + } + + hi = hi->last(); + if (!hi) { + popup->insertItem(i18n("(No previous function)")); + return; + } + + int count = 1; + while (countfunction(); + if (!f) break; + + QString name = f->prettyName(); + if ((int)name.length()>Configuration::maxSymbolLength()) + name = name.left(Configuration::maxSymbolLength()) + "..."; + + //qDebug("back: Adding %s", name.ascii()); + popup->insertItem(name, count); + hi = hi->last(); + count++; + } +} + +void TopLevel::upAboutToShow() +{ + QPopupMenu *popup = _paUp->popupMenu(); + + popup->clear(); + StackBrowser* b = _stackSelection ? _stackSelection->browser() : 0; + HistoryItem* hi = b ? b->current() : 0; + TraceFunction* f = hi ? hi->function() : 0; + + if (!f) { + popup->insertItem(i18n("(No Stack)")); + return; + } + f = hi->stack()->caller(f, false); + if (!f) { + popup->insertItem(i18n("(No Function Up)")); + return; + } + + int count = 1; + while (countprettyName(); + if ((int)name.length()>Configuration::maxSymbolLength()) + name = name.left(Configuration::maxSymbolLength()) + "..."; + + popup->insertItem(name, count); + f = hi->stack()->caller(f, false); + count++; + } + +} + +void TopLevel::forwardActivated(int id) +{ + //qDebug("forwardActivated: %d", id); + + StackBrowser* b = _stackSelection ? _stackSelection->browser() : 0; + if (!b) return; + + while (id>1) { + b->goForward(); + id--; + } + _stackSelection->browserForward(); +} + +void TopLevel::backActivated(int id) +{ + //qDebug("backActivated: %d", id); + + StackBrowser* b = _stackSelection ? _stackSelection->browser() : 0; + if (!b) return; + + while (id>1) { + b->goBack(); + id--; + } + _stackSelection->browserBack(); +} + +void TopLevel::upActivated(int id) +{ + //qDebug("upActivated: %d", id); + + StackBrowser* b = _stackSelection ? _stackSelection->browser() : 0; + HistoryItem* hi = b ? b->current() : 0; + if (!hi) return; + + TraceFunction* f = hi->function(); + + while (id>0 && f) { + f = hi->stack()->caller(f, false); + id--; + } + + //qDebug("upActivated: %s", f ? f->prettyName().ascii() : "??" ); + if (f) + setFunction(f); + +} + +void TopLevel::showMessage(const QString& msg, int ms) +{ + if (_statusbar) + _statusbar->message(msg, ms); +} + +void TopLevel::showStatus(QString msg, int progress) +{ + static bool msgUpdateNeeded = true; + + if (msg.isEmpty()) { + if (_progressBar) { + _statusbar->removeWidget(_progressBar); + delete _progressBar; + _progressBar = 0; + } + _statusbar->clear(); + _progressMsg = msg; + return; + } + + if (_progressMsg.isEmpty()) _progressStart.start(); + + if (msg != _progressMsg) { + _progressMsg = msg; + msgUpdateNeeded = true; + } + + // do nothing if last change was less than 0.5 seconds ago + if (_progressStart.elapsed() < 500) return; + + if (!_progressBar) { + _progressBar = new QProgressBar(_statusbar); + _progressBar->setMaximumSize(200, _statusbar->height()-4); + _statusbar->addWidget(_progressBar, 1, true); + _progressBar->show(); + msgUpdateNeeded = true; + } + + _progressStart.restart(); + + if (msgUpdateNeeded) { + _statusbar->message(msg); + msgUpdateNeeded = false; + } + _progressBar->setProgress(progress); + + // let the progress bar update itself +#if (QT_VERSION-0 >= 0x030100) + QEventLoop* l = qApp->eventLoop(); + if (l) l->processEvents(QEventLoop::ExcludeUserInput); +#else + // for Qt 3.0.x. This allows user input and thus potentially races + qApp->processEvents(); +#endif +} + +#include "toplevel.moc" diff --git a/kcachegrind/kcachegrind/toplevel.h b/kcachegrind/kcachegrind/toplevel.h new file mode 100644 index 00000000..8662768e --- /dev/null +++ b/kcachegrind/kcachegrind/toplevel.h @@ -0,0 +1,274 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002, 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * KCachegrind top level window + */ + +#ifndef TOPLEVEL_H +#define TOPLEVEL_H + +#include + +#include +#include + +#include "traceitemview.h" +#include "tracedata.h" + +class MultiView; +class QLineEdit; +class QDockWidget; +class QLabel; +class QProgressBar; +class QPopupMenu; + +class KURL; +class KSelectAction; +class KToggleAction; +class KToolBarPopupAction; + +class TraceData; +class KRecentFilesAction; +class MainWidget; +class PartSelection; +class FunctionSelection; +class DumpSelection; +class StackSelection; +class TraceFunction; + +class TopLevel : public KMainWindow, public DCOPObject +{ + Q_OBJECT + +public: + TopLevel(const char *name = 0); + ~TopLevel(); + + TraceData* data() { return _data; } + void setData(TraceData*); + + virtual void saveProperties(KConfig*); + virtual void readProperties(KConfig*); + + void createActions(); + void createDocks(); + + TraceItem::CostType groupType() { return _groupType; } + TraceCostType* costType() { return _costType; } + TraceCostType* costType2() { return _costType2; } + TracePartList activeParts() { return _activeParts; } + TracePartList hiddenParts() { return _hiddenParts; } + + // current config + bool showPercentage() const { return _showPercentage; } + bool showExpanded() const { return _showExpanded; } + bool showCycles() const { return _showCycles; } + + /* convenience functions for often used context menu items */ + void addCostMenu(QPopupMenu*,bool); + void addGoMenu(QPopupMenu*); + +public slots: + void newTrace(); + void loadTrace(); + void loadTrace(const KURL&); + void loadTrace(QString); + void addTrace(); + void addTrace(const KURL&); + void addTrace(QString); + + // for quick showing the main window... + void loadDelayed(QString); + + void reload(); + void exportGraph(); + void newWindow(); + void configure(); + void querySlot(); + void dummySlot(); + + // layouts + void layoutDuplicate(); + void layoutRemove(); + void layoutNext(); + void layoutPrevious(); + void layoutSave(); + void layoutRestore(); + void updateLayoutActions(); + + void updateStatusBar(); + void costTypeSelected(const QString&); + void costType2Selected(const QString&); + void groupTypeSelected(int); + void splitSlot(); + void splitDirSlot(); + void configureToolbars(); + void configureKeys(); + bool queryExit(); + bool queryClose(); + void togglePartDock(); + void toggleStackDock(); + void toggleFunctionDock(); + void toggleDumpDock(); + void toggleStatusBar(); + void partVisibilityChanged(bool); + void dumpVisibilityChanged(bool); + void stackVisibilityChanged(bool); + void functionVisibilityChanged(bool); + void togglePercentage(); + void setPercentage(bool); + void setAbsoluteCost(); + void setRelativeCost(); + void toggleExpanded(); + void toggleCycles(); + void forceTrace(); + void forceTraceReload(); + void forwardAboutToShow(); + void backAboutToShow(); + void upAboutToShow(); + void forwardActivated(int); + void backActivated(int); + void upActivated(int); + + bool setCostType(TraceCostType*); + bool setCostType2(TraceCostType*); + bool setCostType(QString); + bool setCostType2(QString); + bool setCostType(int); + bool setCostType2(int); + bool setGroupType(TraceItem::CostType); + bool setGroupType(QString); + bool setGroup(TraceCostItem*); + bool setGroup(QString); + bool setFunction(TraceFunction*); + bool setFunction(QString); + void activePartsChangedSlot(const TracePartList& list); + void partsHideSelectedSlot(); + void partsUnhideAllSlot(); + + /* These go back to mainloop first by using a timer. + * So they can be called from event handlers that + * aren't allowed to delete list entries. + */ + void setCostTypeDelayed(TraceCostType*); + void setCostType2Delayed(TraceCostType*); + void setGroupTypeDelayed(TraceItem::CostType); + void setGroupDelayed(TraceCostItem*); + void setTraceItemDelayed(TraceItem*); + void partsHideSelectedSlotDelayed(); + void partsUnhideAllSlotDelayed(); + void goBack(); + void goForward(); + void goUp(); + void setDirectionDelayed(TraceItemView::Direction); + + /* SingleShot Slots (without parameters) for the delayed versions */ + void setCostTypeDelayed(); + void setCostType2Delayed(); + void setGroupTypeDelayed(); + void setGroupDelayed(); + void setTraceItemDelayed(); + void loadTraceDelayed(); + void setDirectionDelayed(); + + // configuration has changed + void configChanged(); + + //void refresh(); + void slotShowTipOnStart(); + void slotShowTip(); + + // progress in status bar, empty message disables progress display + void showStatus(QString msg, int progress); + void showMessage(const QString&, int msec); + +private: + void init(); + void createLayoutActions(); + void createMiscActions(); + void setupMainWidget(MainWidget*); + void setupPartSelection(PartSelection*); + void restoreCurrentState(QString postfix); + void saveCurrentState(QString postfix); + void saveTraceSettings(); + QString traceKey(); + void restoreTraceTypes(); + void restoreTraceSettings(); + + KStatusBar* _statusbar; + QLabel* _statusLabel; + KRecentFilesAction* _openRecent; + bool _twoMainWidgets; + Orientation _spOrientation; + + MultiView* _multiView; + FunctionSelection* _functionSelection; + DumpSelection* _dumpSelection; + PartSelection* _partSelection; + StackSelection* _stackSelection; + QLineEdit* queryLineEdit; + + QDockWindow *_partDock, *_stackDock, *_functionDock, *_dumpDock; + bool _forcePartDock; + + KSelectAction *_saCost, *_saCost2, *saGroup; + KToggleAction *_partDockShown, *_stackDockShown; + KToggleAction *_functionDockShown, *_dumpDockShown; + KToggleAction *_taPercentage, *_taExpanded, *_taCycles; + KToggleAction *_taDump, *_taSplit, *_taSplitDir; + KToolBarPopupAction *_paForward, *_paBack, *_paUp; + + TraceFunction* _function; + const QObject* _lastSender; + + // trace data shown in this window + TraceData* _data; + // subcost types used for visualisation + TraceCostType* _costType; + TraceCostType* _costType2; + // grouping of function list + TraceItem::CostType _groupType; + // selected group + TraceCostItem* _group; + // selected parts + TracePartList _activeParts; + // hidden parts + TracePartList _hiddenParts; + // layouts + int _layoutCurrent, _layoutCount; + + // for delayed slots + TraceCostType* _costTypeDelayed; + TraceCostType* _costType2Delayed; + TraceItem::CostType _groupTypeDelayed; + TraceCostItem* _groupDelayed; + TraceItem* _traceItemDelayed; + QString _loadTraceDelayed; + TraceItemView::Direction _directionDelayed; + + // for status progress display + QString _progressMsg; + QTime _progressStart; + QProgressBar* _progressBar; + + // toplevel configuration options + bool _showPercentage, _showExpanded, _showCycles; +}; + +#endif diff --git a/kcachegrind/kcachegrind/tracedata.cpp b/kcachegrind/kcachegrind/tracedata.cpp new file mode 100644 index 00000000..85f4f2e1 --- /dev/null +++ b/kcachegrind/kcachegrind/tracedata.cpp @@ -0,0 +1,5072 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002, 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + + +#include + +#include +#include +#include +#include + +#include +#include + +#include "tracedata.h" +#include "toplevel.h" +#include "loader.h" +#include "configuration.h" +#include "utils.h" +#include "fixcost.h" + + +#define TRACE_DEBUG 0 +#define TRACE_ASSERTIONS 0 + +const int TraceCost::MaxRealIndex = MaxRealIndexValue; +const int TraceCost::InvalidIndex = -1; + +//--------------------------------------------------- +// Addr + +bool Addr::set(FixString& s) +{ + return s.stripUInt64(_v); +} + +int Addr::set(const char *s) +{ + int n = 0; + _v = 0; + + while((n<16) && *s) { + if ((*s>='0') && (*s<='9')) + _v = 16*_v + (*s-'0'); + else if ((*s>='a') && (*s<='f')) + _v = 16*_v + 10 + (*s-'a'); + else if ((*s>='A') && (*s<='F')) + _v = 16*_v + 10 + (*s-'A'); + else break; + s++; + n++; + } + + return n; +} + + +QString Addr::toString() const +{ + if (_v == 0) return QString("0"); + + uint64 n = _v; + QString hex; +#if (QT_VERSION-0 >= 0x030200) + hex.reserve(16); +#endif + + while(n>0) { + int d = (n & 15); + hex = QChar((d<10) ? ('0'+d) : ('A'-10+d)) + hex; + n /= 16; + } + + return hex; +} + +QString Addr::pretty() const +{ + if (_v == 0) return QString("0"); + + uint64 n = _v; + int p = 0; + QString hex; +#if (QT_VERSION-0 >= 0x030200) + hex.reserve(20); +#endif + + while(n>0) { + int d = (n & 15); + if ((p>0) && ((p%4)==0)) hex = " " + hex; + hex = QChar((d<10) ? ('0'+d) : ('A'-10+d)) + hex; + n /= 16; + p++; + } + + return hex; +} + +bool Addr::isInRange(Addr a, int distance) +{ + uint64 diff = (a._v > _v) ? (a._v - _v) : (_v - a._v); + uint64 dist = (distance<0) ? distance : -distance; + return (diff < dist); +} + +//--------------------------------------------------- +// TraceItem + +QString* TraceItem::_typeName = 0; +QString* TraceItem::_i18nTypeName = 0; + +TraceItem::TraceItem() +{ + _position = 0; + _dep = 0; + _dirty = true; +} + +TraceItem::~TraceItem() +{} + +void TraceItem::cleanup() +{ + if (_typeName) { + delete [] _typeName; + _typeName = 0; + } + if (_i18nTypeName) { + delete [] _i18nTypeName; + _i18nTypeName = 0; + } +} + +QString TraceItem::typeName(CostType t) +{ + if (!_typeName) { + _typeName = new QString [MaxCostType+1]; + QString* strs = _typeName; + for(int i=0;i<=MaxCostType;i++) + strs[i] = QString("?"); + + strs[Item] = I18N_NOOP("Abstract Item"); + strs[Cost] = I18N_NOOP("Cost Item"); + strs[PartLine] = I18N_NOOP("Part Source Line"); + strs[Line] = I18N_NOOP("Source Line"); + strs[PartLineCall] = I18N_NOOP("Part Line Call"); + strs[LineCall] = I18N_NOOP("Line Call"); + strs[PartLineJump] = I18N_NOOP("Part Jump"); + strs[LineJump] = I18N_NOOP("Jump"); + strs[PartInstr] = I18N_NOOP("Part Instruction"); + strs[Instr] = I18N_NOOP("Instruction"); + strs[PartInstrJump] = I18N_NOOP("Part Instruction Jump"); + strs[InstrJump] = I18N_NOOP("Instruction Jump"); + strs[PartInstrCall] = I18N_NOOP("Part Instruction Call"); + strs[InstrCall] = I18N_NOOP("Instruction Call"); + strs[PartCall] = I18N_NOOP("Part Call"); + strs[Call] = I18N_NOOP("Call"); + strs[PartFunction] = I18N_NOOP("Part Function"); + strs[FunctionSource] = I18N_NOOP("Function Source File"); + strs[Function] = I18N_NOOP("Function"); + strs[FunctionCycle] = I18N_NOOP("Function Cycle"); + strs[PartClass] = I18N_NOOP("Part Class"); + strs[Class] = I18N_NOOP("Class"); + strs[PartFile] = I18N_NOOP("Part Source File"); + strs[File] = I18N_NOOP("Source File"); + strs[PartObject] = I18N_NOOP("Part ELF Object"); + strs[Object] = I18N_NOOP("ELF Object"); + strs[Part] = I18N_NOOP("Profile Part"); + strs[Data] = I18N_NOOP("Program Trace"); + } + if (t<0 || t> MaxCostType) t = MaxCostType; + return _typeName[t]; +} + +TraceItem::CostType TraceItem::costType(QString s) +{ + // This is the default cost Type + if (s.isEmpty()) return Function; + + CostType type; + for (int i=0; i MaxCostType) t = MaxCostType; + return _i18nTypeName[t]; +} + +TraceItem::CostType TraceItem::i18nCostType(QString s) +{ + // This is the default cost Type + if (s.isEmpty()) return Function; + + CostType type; + for (int i=0; iname()) + .arg(part()->name()); + } + + if (_dep) + return _dep->name(); + + return i18n("(unknown)"); +} + +QString TraceItem::prettyName() const +{ + if (name().isEmpty()) return i18n("(unknown)"); + return name(); +} + + +QString TraceItem::fullName() const +{ + return QString("%1 %2") + .arg(typeName(type())).arg(prettyName()); +} + +QString TraceItem::toString() +{ + return QString("%1\n [%3]").arg(fullName()).arg(costString(0)); +} + +void TraceItem::invalidate() +{ + if (_dirty) return; + _dirty = true; + + if (_dep) + _dep->invalidate(); +} + +void TraceItem::update() +{ + _dirty = false; +} + +TracePart* TraceItem::part() +{ + return _position ? _position->part() : 0; +} + +const TracePart* TraceItem::part() const +{ + return _position ? _position->part() : 0; +} + +TraceData* TraceItem::data() +{ + return _position ? _position->data() : 0; +} + +const TraceData* TraceItem::data() const +{ + return _position ? _position->data() : 0; +} + + +//--------------------------------------------------- +// TraceCost + +TraceCost::TraceCost() + : TraceItem() +{ + _cachedType = 0; // no virtual value cached + + TraceCost::clear(); +} + +TraceCost::~TraceCost() +{} + + +void TraceCost::clear() +{ + // simple set usage count to 0 + _count = 0; + + TraceItem::clear(); +} + + + +void TraceCost::set(TraceSubMapping* sm, const char* s) +{ + if (!sm) return; + if (!s) { + if (_count>0) clear(); + return; + } + + while(*s == ' ') s++; + + if (sm->isIdentity()) { + int i = 0; + while(icount()) { + if (!_cost[i].set(&s)) break; + i++; + } + _count = i; + } + else { + int i = 0, maxIndex = 0, index; + while(1) { + index = sm->realIndex(i); + if (maxIndexfirstUnused(); i<=maxIndex; i=sm->nextUnused(i)) + _cost[i] = 0; + _count = maxIndex; + } + // a cost change has to be propagated (esp. in subclasses) + invalidate(); +} + +void TraceCost::set(TraceSubMapping* sm, FixString & s) +{ + if (!sm) return; + + s.stripSpaces(); + + if (sm->isIdentity()) { + int i = 0; + while(icount()) { + if (!s.stripUInt64(_cost[i])) break; + i++; + } + _count = i; + } + else { + int i = 0, maxIndex = 0, index; + while(1) { + index = sm->realIndex(i); + if (maxIndexfirstUnused(); i<=maxIndex; i=sm->nextUnused(i)) + _cost[i] = 0; + _count = maxIndex+1; + } + invalidate(); +} + + +void TraceCost::addCost(TraceSubMapping* sm, const char* s) +{ + if (!sm || !s) return; + + SubCost v; + + if (sm->isIdentity()) { + int i = 0; + while(icount()) { + if (!v.set(&s)) break; + if (i<_count) + _cost[i] += v; + else + _cost[i] = v; + i++; + } + if (i > _count) _count = i; + } + else { + int i = 0, maxIndex = 0, index; + while(1) { + if (!v.set(&s)) break; + index = sm->realIndex(i); + if (maxIndex= _count) { + /* we have to set all costs of unused indexes in the interval + * [_count;maxIndex] to zero */ + for(i=sm->nextUnused(_count-1); i<=maxIndex; i=sm->nextUnused(i)) + _cost[i] = 0; + _count = maxIndex+1; + } + } + + // a cost change has to be propagated (esp. in subclasses) + invalidate(); + +#if TRACE_DEBUG + _dirty = false; // don't recurse ! + qDebug("%s\n now %s", fullName().ascii(), + TraceCost::costString(0).ascii()); + _dirty = true; // because of invalidate() +#endif +} + +void TraceCost::addCost(TraceSubMapping* sm, FixString & s) +{ + if (!sm) return; + + s.stripSpaces(); + + SubCost v; + + if (sm->isIdentity()) { + int i = 0; + while(icount()) { + if (!s.stripUInt64(v)) break; + if (i<_count) + _cost[i] += v; + else + _cost[i] = v; + i++; + } + if (i > _count) _count = i; + } + else { + int i = 0, maxIndex = 0, index; + while(1) { + if (!s.stripUInt64(v)) break; + index = sm->realIndex(i); + if (maxIndex= _count) { + /* we have to set all costs of unused indexes in the interval + * [_count;maxIndex] to zero */ + for(i=sm->nextUnused(_count-1); i<=maxIndex; i=sm->nextUnused(i)) + _cost[i] = 0; + _count = maxIndex+1; + } + } + + invalidate(); + +#if TRACE_DEBUG + _dirty = false; // don't recurse ! + qDebug("%s\n now %s", fullName().ascii(), + TraceCost::costString(0).ascii()); + _dirty = true; // because of invalidate() +#endif +} + + +// update each subcost to be maximum of old and given costs +void TraceCost::maxCost(TraceSubMapping* sm, FixString & s) +{ + if (!sm) return; + + s.stripSpaces(); + + SubCost v; + + if (sm->isIdentity()) { + int i = 0; + while(icount()) { + if (!s.stripUInt64(v)) break; + if (i<_count) { + if (v>_cost[i]) _cost[i] = v; + } + else + _cost[i] = v; + i++; + } + if (i > _count) _count = i; + } + else { + int i = 0, maxIndex = 0, index; + while(1) { + if (!s.stripUInt64(v)) break; + index = sm->realIndex(i); + if (maxIndex_cost[index]) _cost[index] = v; + } + else + _cost[index] = v; + i++; + } + if (maxIndex >= _count) { + /* we have to set all costs of unused indexes in the interval + * [_count;maxIndex] to zero */ + for(i=sm->nextUnused(_count-1); i<=maxIndex; i=sm->nextUnused(i)) + _cost[i] = 0; + _count = maxIndex+1; + } + } + + invalidate(); + +#if TRACE_DEBUG + _dirty = false; // don't recurse ! + qDebug("%s\n now %s", fullName().ascii(), + TraceCost::costString(0).ascii()); + _dirty = true; // because of invalidate() +#endif +} + + +void TraceCost::addCost(TraceCost* item) +{ + int i; + + if (!item) return; + + // we have to update the other item if needed + // because we access the item costs directly + if (item->_dirty) item->update(); + + if (item->_count < _count) { + for (i = 0; i_count; i++) + _cost[i] += item->_cost[i]; + } + else { + for (i = 0; i<_count; i++) + _cost[i] += item->_cost[i]; + for (; i_count; i++) + _cost[i] = item->_cost[i]; + _count = item->_count; + } + + // a cost change has to be propagated (esp. in subclasses) + invalidate(); + +#if TRACE_DEBUG + _dirty = false; // don't recurse ! + qDebug("%s added cost item\n %s\n now %s", + fullName().ascii(), item->fullName().ascii(), + TraceCost::costString(0).ascii()); + _dirty = true; // because of invalidate() +#endif +} + +void TraceCost::maxCost(TraceCost* item) +{ + int i; + + if (!item) return; + + // we have to update the other item if needed + // because we access the item costs directly + if (item->_dirty) item->update(); + + if (item->_count < _count) { + for (i = 0; i_count; i++) + if (_cost[i] < item->_cost[i]) _cost[i] = item->_cost[i]; + } + else { + for (i = 0; i<_count; i++) + if (_cost[i] < item->_cost[i]) _cost[i] = item->_cost[i]; + for (; i_count; i++) + _cost[i] = item->_cost[i]; + _count = item->_count; + } + + // a cost change has to be propagated (esp. in subclasses) + invalidate(); + +#if TRACE_DEBUG + _dirty = false; // don't recurse ! + qDebug("%s added cost item\n %s\n now %s", + fullName().ascii(), item->fullName().ascii(), + TraceCost::costString(0).ascii()); + _dirty = true; // because of invalidate() +#endif +} + +void TraceCost::addCost(int type, SubCost value) +{ + if (type<0 || type>=MaxRealIndex) return; + if (type<_count) + _cost[type] += value; + else { + for(int i=_count;i=MaxRealIndex) return; + if (type<_count) { + if (value>_cost[type]) _cost[type] = value; + } + else { + for(int i=_count;i_dirty) item->update(); + + int maxCount = (item->_count > _count) ? item->_count : _count; + + res._count = maxCount; + for (int i=0; isubCost(i) - subCost(i); + + return res; +} + +QString TraceCost::costString(TraceCostMapping* m) +{ + QString res; + + if (_dirty) update(); + + int maxIndex = m ? m->realCount() : TraceCost::MaxRealIndex; + for (int i = 0; itype(i)->name() + " "; + + res += subCost(i).pretty(); + } + return res; +} + + +void TraceCost::invalidate() +{ + if (_dirty) return; + _dirty = true; + _cachedType = 0; // cached value is invalid, too + + if (_dep) + _dep->invalidate(); +} + +void TraceCost::update() +{ + _dirty = false; +} + +// this is only for real types +SubCost TraceCost::subCost(int idx) +{ + if (idx<0) return 0; + + /* update if needed as cost could be calculated dynamically in subclasses + * this can change _count !! */ + if (_dirty) update(); + if (idx>=_count) return 0; + + return _cost[idx]; +} + +SubCost TraceCost::subCost(TraceCostType* t) +{ + if (!t) return 0; + if (_cachedType != t) { + _cachedType = t; + _cachedCost = t->subCost(this); + } + return _cachedCost; +} + +QString TraceCost::prettySubCost(TraceCostType* t) +{ + return subCost(t).pretty(); +} + + + +//--------------------------------------------------- +// TraceJumpCost + +TraceJumpCost::TraceJumpCost() + :TraceItem() +{ + TraceJumpCost::clear(); +} + +TraceJumpCost::~TraceJumpCost() +{} + +SubCost TraceJumpCost::executedCount() +{ + if (_dirty) update(); + + return _executedCount; +} + +SubCost TraceJumpCost::followedCount() +{ + if (_dirty) update(); + + return _followedCount; +} + +QString TraceJumpCost::costString(TraceCostMapping*) +{ + if (_dirty) update(); + + return QString("%1/%2") + .arg(_followedCount.pretty()) + .arg(_executedCount.pretty()); +} + +void TraceJumpCost::clear() +{ + _followedCount = 0; + _executedCount = 0; +} + +void TraceJumpCost::addCost(TraceJumpCost* item) +{ + if (item->_dirty) item->update(); + + _followedCount += item->followedCount(); + _executedCount += item->executedCount(); +} + + +//--------------------------------------------------- +// TraceCostType + +QPtrList* TraceCostType::_knownTypes = 0; + +TraceCostType::TraceCostType(QString name, QString longName, QString formula) +{ + _name = name; + _longName = longName; + _formula = formula; + _mapping = 0; + _realIndex = TraceCost::InvalidIndex; + _parsed = false; + _inParsing = false; + + for (int i=0; iTraceCost::MaxRealIndex) + i=TraceCost::InvalidIndex; + + _realIndex = i; + _formula = QString::null; +} + +// checks for existing types and sets coefficients +bool TraceCostType::parseFormula() +{ + if (_parsed) return true; + if (_inParsing) { + qDebug("TraceCostType::parseFormula: Recursion detected."); + return false; + } + + if (!_mapping) { + qDebug("TraceCostType::parseFormula: No mapping set!"); + return false; + } + + _inParsing = true; + + for (int i=0; itype(costName); + if (!costType) { + // qDebug("Cost type '%s': In formula cost '%s' unknown.", + // _name.ascii(), costName.ascii()); + + _inParsing = false; + return false; + } + + factor = (rx.cap(2).isEmpty()) ? 1 : rx.cap(2).toInt(); + if (rx.cap(1) == "-") factor = -factor; + + if (costType->isReal()) + _coefficient[costType->realIndex()] += factor; + else { + costType->parseFormula(); + for (int i=0; i_coefficient[i]; + } + } + + _inParsing = false; + _parsed = true; + + return true; +} + +QString TraceCostType::parsedFormula() +{ + QString res; + + if (!parseFormula()) return res; + + for (int i=0; i0) res += "+ "; + } + if (c<0) { res += "- "; c = -c; } + res += QString::number(c); + + TraceCostType* t = _mapping->type(i); + if (!t) continue; + + if (!t->name().isEmpty()) + res += QString(" * %1").arg(t->name()); + } + + return res; +} + +SubCost TraceCostType::subCost(TraceCost* c) +{ + if (_realIndex != TraceCost::InvalidIndex) + return c->subCost(_realIndex); + + if (!_parsed) { + if (!parseFormula()) return 0; + } + SubCost res = 0; + + int rc = _mapping->realCount(); + for (int i = 0;isubCost(i); + + return res; +} + +int TraceCostType::histCost(TraceCost* c, double total, double* hist) +{ + if (total == 0.0) return 0; + + if (!_parsed) { + if (!parseFormula()) return 0; + } + + int rc = _mapping->realCount(); + for (int i = 0;isubCost(i) / total; + else + hist[i] = 0.0; + } + + return rc; +} + + + + +TraceCostType* TraceCostType::knownRealType(QString n) +{ + if (!_knownTypes) return 0; + + TraceCostType* t; + for (t=_knownTypes->first();t;t=_knownTypes->next()) + if (t->isReal() && (t->name() == n)) { + TraceCostType* type = new TraceCostType(*t); + return type; + } + + return 0; +} + +TraceCostType* TraceCostType::knownVirtualType(QString n) +{ + if (!_knownTypes) return 0; + + TraceCostType* t; + for (t=_knownTypes->first();t;t=_knownTypes->next()) + if (!t->isReal() && (t->name() == n)) { + TraceCostType* type = new TraceCostType(*t); + return type; + } + + return 0; +} + +// we take ownership +void TraceCostType::add(TraceCostType* t) +{ + if (!t) return; + + t->setMapping(0); + + if (!_knownTypes) + _knownTypes = new QPtrList; + + /* Already known? */ + TraceCostType* kt; + for (kt=_knownTypes->first();kt;kt=_knownTypes->next()) + if (kt->name() == t->name()) break; + + if (kt) { + // Overwrite old type + if (!t->longName().isEmpty() && + (t->longName() != t->name())) kt->setLongName(t->longName()); + if (!t->formula().isEmpty()) kt->setFormula(t->formula()); + + delete t; + } + else { + if (t->longName().isEmpty()) t->setLongName(t->name()); + _knownTypes->append(t); + } +} + + +int TraceCostType::knownTypeCount() +{ + if (!_knownTypes) return 0; + + return _knownTypes->count(); +} + +bool TraceCostType::remove(QString n) +{ + if (!_knownTypes) return false; + + TraceCostType* t; + for (t=_knownTypes->first();t;t=_knownTypes->next()) + if (!t->isReal() && (t->name() == n)) { + _knownTypes->removeRef(t); + delete t; + return true; + } + + return false; +} + +TraceCostType* TraceCostType::knownType(int i) +{ + if (!_knownTypes) return 0; + if (i<0 || i>=(int)_knownTypes->count()) return 0; + + return _knownTypes->at(i); +} + +QColor TraceCostType::color() +{ + if (!_mapping) return QColor(); + return _mapping->realColors()[_realIndex]; +} + + +//--------------------------------------------------- +// TraceCostMapping + +TraceCostMapping::TraceCostMapping() +{ + _realCount = 0; + _virtualCount = 0; + for (int i=0;i0)) return 0; + + if (newCount+_realCount > TraceCost::MaxRealIndex) { + kdDebug() << "TraceCostMapping::subMapping: No space for " + << newCount << " sub costs." << endl; + return 0; + } + + TraceSubMapping* sm = new TraceSubMapping(this); + + pos = 0; + while (1) { + // skip space + while((posappend(addReal(types.mid(pos,pos2-pos))); + + pos = pos2; + } + + return sm; +} + + +int TraceCostMapping::addReal(QString t) +{ + int index = realIndex(t); + if (index>=0) return index; + + TraceCostType* ct = TraceCostType::knownRealType(t); + if (!ct) ct = new TraceCostType(t, t); + + // make it real + ct->setRealIndex(); + + return add(ct); +} + +// add a cost type to a mapping +// this transfers ownership of the type! +int TraceCostMapping::add(TraceCostType* ct) +{ + if (!ct) return TraceCost::InvalidIndex; + + ct->setMapping(this); + + if (ct->isReal()) { + if (_realCount >= TraceCost::MaxRealIndex) { + qDebug("WARNING: Maximum for real cost types reached (on adding '%s')", + ct->name().ascii()); + return TraceCost::InvalidIndex; + } + _real[_realCount] = ct; + ct->setRealIndex(_realCount); + _realColor[_realCount] = Configuration::costTypeColor(ct); + + _realCount++; + return _realCount-1; + } + + if (_virtualCount >= TraceCost::MaxRealIndex) { + qDebug("WARNING: Maximum for virtual cost types reached (on adding '%s')", + ct->name().ascii()); + return TraceCost::InvalidIndex; + } + _virtual[_virtualCount] = ct; + _virtualCount++; + return _virtualCount-1; +} + +// we delete the type: t is invalid when returning true! +bool TraceCostMapping::remove(TraceCostType* t) +{ + if (!t) return false; + if (t->mapping() != this) return false; + + // don't delete real types + if (t->isReal()) return false; + + int i; + for(i=0;i<_virtualCount;i++) + if (_virtual[i] == t) break; + + // not found? + if (i == _virtualCount) return false; + + // delete known type with same name + TraceCostType::remove(t->name()); + + // delete this type + _virtual[i] = 0; + delete t; + if (i+1 == _virtualCount) { + // we can reuse the last index + _virtualCount--; + } + return true; +} + + +TraceCostType* TraceCostMapping::realType(int t) +{ + if (t<0 || t>=_realCount) return 0; + return _real[t]; +} + +TraceCostType* TraceCostMapping::virtualType(int t) +{ + if (t<0 || t>=_virtualCount) return 0; + return _virtual[t]; +} + + +TraceCostType* TraceCostMapping::type(int t) +{ + if (t<0) return 0; + if (t<_realCount) return _real[t]; + + t -= TraceCost::MaxRealIndex; + if (t<0) return 0; + if (t<_virtualCount) return _virtual[t]; + + return 0; +} + +TraceCostType* TraceCostMapping::type(QString name) +{ + for (int i=0;i<_realCount;i++) + if (_real[i] && (_real[i]->name() == name)) + return _real[i]; + + for (int i=0;i<_virtualCount;i++) + if (_virtual[i] && (_virtual[i]->name() == name)) + return _virtual[i]; + + return 0; +} + +TraceCostType* TraceCostMapping::typeForLong(QString name) +{ + for (int i=0;i<_realCount;i++) + if (_real[i] && (_real[i]->longName() == name)) + return _real[i]; + + for (int i=0;i<_virtualCount;i++) + if (_virtual[i] && (_virtual[i]->longName() == name)) + return _virtual[i]; + + return 0; +} + + +int TraceCostMapping::realIndex(QString name) +{ + for (int i=0;i<_realCount;i++) + if (_real[i] && (_real[i]->name() == name)) + return i; + + return TraceCost::InvalidIndex; +} + +int TraceCostMapping::index(QString name) +{ + for (int i=0;i<_realCount;i++) + if (_real[i] && (_real[i]->name() == name)) + return i; + + for (int i=0;i<_virtualCount;i++) + if (_virtual[i] && (_virtual[i]->name() == name)) + return TraceCost::MaxRealIndex + 1 + i; + + return TraceCost::InvalidIndex; +} + +int TraceCostMapping::addKnownVirtualTypes() +{ + int addCount = 0; + int addDiff, i; + int knownCount = TraceCostType::knownTypeCount(); + + while (1) { + addDiff = 0; + for (i=0; iisReal()) continue; + if (index(t->name()) != TraceCost::InvalidIndex) continue; + t->setMapping(this); + if (t->parseFormula()) { + addDiff++; + add(new TraceCostType(t->name(), t->longName(), t->formula())); + } + t->setMapping(0); + } + if (addDiff == 0) break; + addCount += addDiff; + } + return addCount; +} + + +//--------------------------------------------------- +// TraceSubMapping + +TraceSubMapping::TraceSubMapping(TraceCostMapping* mapping) +{ + _mapping = mapping; + clear(); +} + +void TraceSubMapping::clear() +{ + _count = 0; + _isIdentity = true; + _firstUnused = 0; + for(int i=0;iaddReal(type) : _mapping->realIndex(type); + + return append(index); +} + +bool TraceSubMapping::append(int type) +{ + if (!_mapping) return false; + if ((type<0) || (type >= _mapping->realCount())) return false; + + if ( _count >= TraceCost::MaxRealIndex) return false; + + _realIndex[_count] = type; + + if (_isIdentity && (_count != type)) _isIdentity = false; + if (type == _firstUnused) + _firstUnused = _nextUnused[type]; + for(int i=0;i=0) { + qDebug("addDep: %s already in list!", + dep->fullName().ascii()); + return; + } +#endif + + _deps.append(dep); + _lastDep = dep; + invalidate(); + +#if TRACE_DEBUG + qDebug("%s added\n %s (now %d)", + fullName().ascii(), dep->fullName().ascii(), + _deps.count()); +#endif +} + +TraceCost* TraceListCost::findDepFromPart(TracePart* part) +{ + if (_lastDep && _lastDep->part() == part) + return _lastDep; + + TraceCost* dep; + for (dep = _deps.first(); dep; dep = _deps.next()) + if (dep->part() == part) { + _lastDep = dep; + return dep; + } + return 0; +} + + +void TraceListCost::update() +{ + if (!_dirty) return; + +#if TRACE_DEBUG + qDebug("update %s (count %d)", + fullName().ascii(), _deps.count()); +#endif + + clear(); + TraceCost* item; + for (item = _deps.first(); item; item = _deps.next()) { + if (onlyActiveParts()) + if (!item->part() || !item->part()->isActive()) continue; + + addCost(item); + } + + _dirty = false; + +#if TRACE_DEBUG + qDebug(" > %s", costString(0).ascii()); +#endif +} + + + +//--------------------------------------------------- +// TraceJumpListCost + +TraceJumpListCost::TraceJumpListCost() +{ + _lastDep = 0; +} + +TraceJumpListCost::~TraceJumpListCost() +{} + +void TraceJumpListCost::addDep(TraceJumpCost* dep) +{ +#if TRACE_ASSERTIONS + if (_deps.findRef(dep)>=0) { + qDebug("addDep: %s already in list!", + dep->fullName().ascii()); + return; + } +#endif + + _deps.append(dep); + _lastDep = dep; + invalidate(); + +#if TRACE_DEBUG + qDebug("%s added\n %s (now %d)", + fullName().ascii(), dep->fullName().ascii(), + _deps.count()); +#endif +} + +TraceJumpCost* TraceJumpListCost::findDepFromPart(TracePart* part) +{ + if (_lastDep && _lastDep->part() == part) + return _lastDep; + + TraceJumpCost* dep; + for (dep = _deps.first(); dep; dep = _deps.next()) + if (dep->part() == part) { + _lastDep = dep; + return dep; + } + return 0; +} + + +void TraceJumpListCost::update() +{ + if (!_dirty) return; + +#if TRACE_DEBUG + qDebug("update %s (count %d)", + fullName().ascii(), _deps.count()); +#endif + + clear(); + TraceJumpCost* item; + for (item = _deps.first(); item; item = _deps.next()) { + if (onlyActiveParts()) + if (!item->part() || !item->part()->isActive()) continue; + + addCost(item); + } + + _dirty = false; + +#if TRACE_DEBUG + qDebug(" > %s", costString(0).ascii()); +#endif +} + + + +//--------------------------------------------------- +// TraceCallListCost + +TraceCallListCost::TraceCallListCost() +{ + _lastDep = 0; +} + +TraceCallListCost::~TraceCallListCost() +{} + +void TraceCallListCost::addDep(TraceCallCost* dep) +{ +#if TRACE_ASSERTIONS + if (_deps.findRef(dep)>=0) { + qDebug("addDep: %s already in list!", + dep->fullName().ascii()); + return; + } +#endif + + _deps.append(dep); + _lastDep = dep; + invalidate(); + +#if TRACE_DEBUG + qDebug("%s added\n %s (now %d)", + fullName().ascii(), dep->fullName().ascii(), + _deps.count()); +#endif +} + +TraceCallCost* TraceCallListCost::findDepFromPart(TracePart* part) +{ + if (_lastDep && _lastDep->part() == part) + return _lastDep; + + TraceCallCost* dep; + for (dep = _deps.first(); dep; dep = _deps.next()) + if (dep->part() == part) { + _lastDep = dep; + return dep; + } + return 0; +} + + +void TraceCallListCost::update() +{ + if (!_dirty) return; + +#if TRACE_DEBUG + qDebug("update %s (count %d)", + fullName().ascii(), _deps.count()); +#endif + + /* Without dependent cost items, assume fixed costs, + * i.e. don't change cost */ + if (_deps.count()>0) { + clear(); + TraceCallCost* item; + for (item = _deps.first(); item; item = _deps.next()) { + if (onlyActiveParts()) + if (!item->part() || !item->part()->isActive()) continue; + + addCost(item); + addCallCount(item->callCount()); + } + } + + _dirty = false; + +#if TRACE_DEBUG + qDebug(" > %s", costString(0).ascii()); +#endif +} + + +//--------------------------------------------------- +// TraceInclusiveListCost + +TraceInclusiveListCost::TraceInclusiveListCost() +{ + _lastDep = 0; +} + +TraceInclusiveListCost::~TraceInclusiveListCost() +{} + + +void TraceInclusiveListCost::addDep(TraceInclusiveCost* dep) +{ +#if TRACE_ASSERTIONS + if (_deps.findRef(dep)>=0) { + qDebug("addDep: %s already in list!", + dep->fullName().ascii()); + return; + } +#endif + + _deps.append(dep); + _lastDep = dep; + invalidate(); + +#if TRACE_DEBUG + qDebug("%s added\n %s (now %d)", + fullName().ascii(), dep->fullName().ascii(), + _deps.count()); +#endif +} + +TraceInclusiveCost* TraceInclusiveListCost::findDepFromPart(TracePart* part) +{ + if (_lastDep && _lastDep->part() == part) + return _lastDep; + + TraceInclusiveCost* dep; + for (dep = _deps.first(); dep; dep = _deps.next()) + if (dep->part() == part) { + _lastDep = dep; + return dep; + } + return 0; +} + +void TraceInclusiveListCost::update() +{ + if (!_dirty) return; + +#if TRACE_DEBUG + qDebug("update %s (count %d)", + fullName().ascii(), _deps.count()); +#endif + + clear(); + TraceInclusiveCost* item; + for (item = _deps.first(); item; item = _deps.next()) { + if (onlyActiveParts()) + if (!item->part() || !item->part()->isActive()) continue; + + addCost(item); + addInclusive(item->inclusive()); + } + + _dirty = false; + +#if TRACE_DEBUG + qDebug(" > %s", costString(0).ascii()); +#endif +} + + + +//--------------------------------------------------- +// TracePartInstrJump + +TracePartInstrJump::TracePartInstrJump(TraceInstrJump* instrJump, + TracePartInstrJump* next) +{ + _dep = instrJump; + _next = next; +} + +TracePartInstrJump::~TracePartInstrJump() +{} + + +//--------------------------------------------------- +// TracePartInstrCall + +TracePartInstrCall::TracePartInstrCall(TraceInstrCall* instrCall) +{ + _dep = instrCall; +} + +TracePartInstrCall::~TracePartInstrCall() +{} + + + +//--------------------------------------------------- +// TracePartInstr + +TracePartInstr::TracePartInstr(TraceInstr* instr) +{ + _dep = instr; +} + +TracePartInstr::~TracePartInstr() +{} + + + +//--------------------------------------------------- +// TracePartLineJump + +TracePartLineJump::TracePartLineJump(TraceLineJump* lineJump) +{ + _dep = lineJump; +} + +TracePartLineJump::~TracePartLineJump() +{} + + +//--------------------------------------------------- +// TracePartLineCall + +TracePartLineCall::TracePartLineCall(TraceLineCall* lineCall) +{ + _dep = lineCall; +} + +TracePartLineCall::~TracePartLineCall() +{} + + +//--------------------------------------------------- +// TracePartLine + +TracePartLine::TracePartLine(TraceLine* line) +{ + _dep = line; +} + +TracePartLine::~TracePartLine() +{} + + + + +//--------------------------------------------------- +// TracePartCall + +TracePartCall::TracePartCall(TraceCall* call) +{ + _dep = call; + + _firstFixCallCost = 0; +} + +TracePartCall::~TracePartCall() +{} + +bool TracePartCall::isRecursion() +{ + return call()->isRecursion(); +} + +void TracePartCall::update() +{ +#if !USE_FIXCOST + TraceCallListCost::update(); +#else + + if (!_dirty) return; + +#if TRACE_DEBUG + qDebug("update %s", fullName().ascii()); +#endif + + /* Without dependent cost items, assume fixed costs, + * i.e. don't change cost */ + if (_firstFixCallCost) { + clear(); + FixCallCost* item; + for (item = _firstFixCallCost; item; item = item->nextCostOfPartCall()) + item->addTo(this); + } + + _dirty = false; + +#if TRACE_DEBUG + qDebug(" > %s", costString(0).ascii()); +#endif + +#endif // USE_FIXCOST +} + + +//--------------------------------------------------- +// TracePartFunction + +TracePartFunction::TracePartFunction(TraceFunction* function, + TracePartObject* partObject, + TracePartFile *partFile) +{ + _dep = function; + _partObject = partObject; + _partFile = partFile; + _partClass = 0; + + _calledCount = 0; + _callingCount = 0; + _calledContexts = 0; + _callingContexts = 0; + + _firstFixCost = 0; + _firstFixJump = 0; +} + +TracePartFunction::~TracePartFunction() +{} + +QString TracePartFunction::prettyCalledCount() +{ + return _calledCount.pretty(); +} + +QString TracePartFunction::prettyCallingCount() +{ + return _callingCount.pretty(); +} + +QString TracePartFunction::costString(TraceCostMapping* m) +{ + update(); + + QString res = TraceInclusiveCost::costString(m); + res += QString(", called from %1: %2") + .arg(_calledContexts).arg(prettyCalledCount()); + res += QString(", calling from %1: %2") + .arg(_callingContexts).arg(prettyCallingCount()); + + return res; +} + + +void TracePartFunction::addPartInstr(TracePartInstr* ref) +{ +#if TRACE_ASSERTIONS + if (_partInstr.findRef(ref)>=0) { + qDebug("TracePartFunction::addPartInstr: %s already in list!", + ref->name().ascii()); + return; + } +#endif + + _partInstr.append(ref); + invalidate(); + +#if TRACE_DEBUG + qDebug("%s added\n %s (now %d)", + fullName().ascii(), ref->fullName().ascii(), + _partInstr.count()); +#endif +} + + +void TracePartFunction::addPartLine(TracePartLine* ref) +{ +#if TRACE_ASSERTIONS + if (_partLines.findRef(ref)>=0) { + qDebug("TracePartFunction::addPartLine: %s already in list!", + ref->name().ascii()); + return; + } +#endif + + _partLines.append(ref); + invalidate(); + +#if TRACE_DEBUG + qDebug("%s added\n %s (now %d)", + fullName().ascii(), ref->fullName().ascii(), + _partLines.count()); +#endif +} + + +void TracePartFunction::addPartCaller(TracePartCall* ref) +{ +#if TRACE_ASSERTIONS + if (_partCallers.findRef(ref)>=0) { + qDebug("TracePartFunction::addPartCaller: %s already in list!", + ref->name().ascii()); + return; + } +#endif + + _partCallers.append(ref); + invalidate(); + +#if TRACE_DEBUG + qDebug("%s added Caller\n %s (now %d)", + fullName().ascii(), ref->fullName().ascii(), + _partCallers.count()); +#endif +} + + +void TracePartFunction::addPartCalling(TracePartCall* ref) +{ +#if TRACE_ASSERTIONS + if (_partCallings.findRef(ref)>=0) { + qDebug("TracePartFunction::addPartCalling: %s already in list!", + ref->name().ascii()); + return; + } +#endif + + _partCallings.append(ref); + invalidate(); + +#if TRACE_DEBUG + qDebug("%s added Calling\n %s (now %d)", + fullName().ascii(), ref->fullName().ascii(), + _partCallings.count()); +#endif +} + +SubCost TracePartFunction::calledCount() +{ + if (_dirty) update(); + + return _calledCount; +} + +int TracePartFunction::calledContexts() +{ + if (_dirty) update(); + + return _calledContexts; +} + +SubCost TracePartFunction::callingCount() +{ + if (_dirty) update(); + + return _callingCount; +} + + +int TracePartFunction::callingContexts() +{ + if (_dirty) update(); + + return _callingContexts; +} + + +void TracePartFunction::update() +{ + if (!_dirty) return; + +#if TRACE_DEBUG + qDebug("TracePartFunction::update %s (Callers %d, Callings %d, lines %d)", + name().ascii(), _partCallers.count(), _partCallings.count(), + _partLines.count()); +#endif + + _calledCount = 0; + _callingCount = 0; + _calledContexts = 0; + _callingContexts = 0; + + // calculate additional cost metrics + TracePartCall *caller, *calling; + for (caller=_partCallers.first();caller;caller=_partCallers.next()) { + + // FIXME + if (caller->subCost(0)>0) + _calledContexts++; + + SubCost c = caller->callCount(); + if (c>0) { + _calledCount += c; + } + } + for (calling=_partCallings.first();calling;calling=_partCallings.next()) { + // FIXME + if (calling->subCost(0)>0) + _callingContexts++; + + SubCost c = calling->callCount(); + if (c>0) { + _callingCount += c; + } + } + + // self cost +#if !USE_FIXCOST + if (_partLines.count()>0) { + TraceCost::clear(); + + TracePartLine* line; + for (line = _partLines.first(); line; line = _partLines.next()) + addCost(line); + } +#else + if (_firstFixCost) { + TraceCost::clear(); + + FixCost* item; + for (item = _firstFixCost; item; item = item->nextCostOfPartFunction()) + item->addTo(this); + } +#endif + + + /* There are two possibilities to calculate inclusive cost: + * 1) sum of call costs to this function + * 2) sum of call costs from this function + self cost + * + * 1) is wrong if a function was called spontaneous, but also by a call. + * This eventually can happen with thread/process startup functions, + * and signal handlers. + * + * 2) is wrong with "skipped PLT" and the calltree skin, because + * cost of PLT is attributed to called function (?) + * + * For now, do 1) if there are callers, otherwise 2). + * Should this be fixed to take the maximum of 1) and 2) ? + */ + _inclusive.clear(); + if (_calledCount>0) { + // inclusive cost: if possible, use caller sums + for (caller=_partCallers.first();caller;caller=_partCallers.next()) { + // detect simple recursion (no cycle) + if (caller->isRecursion()) continue; + + addInclusive(caller); + } + } + else { + // without caller info, use calling sum + line costs + for (calling=_partCallings.first();calling;calling=_partCallings.next()) { + // detect simple recursion (no cycle) + if (calling->isRecursion()) continue; + + addInclusive(calling); + } + _dirty = false; // don't recurse! + addInclusive(this); + } + + _dirty = false; + +#if TRACE_DEBUG + qDebug(" > %s", costString(0).ascii()); +#endif +} + + + +//--------------------------------------------------- +// TracePartClass + +TracePartClass::TracePartClass(TraceClass* cls) +{ + _dep = cls; +} + +TracePartClass::~TracePartClass() +{} + +QString TracePartClass::prettyName() const +{ + return QString("%1 from %2") + .arg( _dep->name().isEmpty() ? QString("(global)") : _dep->name()) + .arg(part()->name()); +} + +//--------------------------------------------------- +// TracePartFile + +TracePartFile::TracePartFile(TraceFile* file) +{ + _dep = file; +} + +TracePartFile::~TracePartFile() +{} + + +//--------------------------------------------------- +// TracePartObject + +TracePartObject::TracePartObject(TraceObject* object) +{ + _dep = object; +} + +TracePartObject::~TracePartObject() +{} + + + + +//--------------------------------------------------- +// TraceInstrJump + +TraceInstrJump::TraceInstrJump(TraceInstr* instrFrom, TraceInstr* instrTo, + bool isCondJump) +{ + _first = 0; + + _instrFrom = instrFrom; + _instrTo = instrTo; + _isCondJump = isCondJump; +} + +TraceInstrJump::~TraceInstrJump() +{ + // we are the owner of the TracePartInstrJump's generated in our factory + TracePartInstrJump* item = _first, *next; + while(item) { + next = item->next(); + delete item; + item = next; + } +} + +TracePartInstrJump* TraceInstrJump::partInstrJump(TracePart* part) +{ + static TracePartInstrJump* item = 0; + + // shortcut + if (item && (item->instrJump()==this) && (item->part() == part)) return item; + + for(item = _first; item; item = item->next()) + if (item->part() == part) break; + + if (!item) { + item = new TracePartInstrJump(this, _first); + item->setPosition(part); + _first = item; + } + return item; +} + +void TraceInstrJump::update() +{ + if (!_dirty) return; + + clear(); + TracePartInstrJump* item; + for (item = _first; item; item = item->next()) { + if (!item->part() || !item->part()->isActive()) continue; + + addCost(item); + } + _dirty = false; + +#if TRACE_DEBUG + qDebug("updated %s", fullName().ascii()); +#endif + +#if TRACE_DEBUG + qDebug(" > %s", costString(0).ascii()); +#endif +} + +QString TraceInstrJump::name() const +{ + return QString("jump at 0x%1 to 0x%2") + .arg(_instrFrom->addr().toString()) + .arg(_instrTo->addr().toString()); +} + + +//--------------------------------------------------- +// TraceInstrJumpList + + +int TraceInstrJumpList::compareItems ( Item item1, Item item2 ) +{ + TraceInstrJump* ij1 = (TraceInstrJump*) item1; + TraceInstrJump* ij2 = (TraceInstrJump*) item2; + + Addr addr1Low = ij1->instrFrom()->addr(); + Addr addr2Low = ij2->instrFrom()->addr(); + Addr addr1High = ij1->instrTo()->addr(); + Addr addr2High = ij2->instrTo()->addr(); + Addr t; + + if (addr1Low > addr1High) { + t = addr1Low; + addr1Low = addr1High; + addr1High = t; + } + + if (addr2Low > addr2High) { + t = addr2Low; + addr2Low = addr2High; + addr2High = t; + } + + if (_sortLow) { + // we sort according to smallest instruction address + if (addr1Low != addr2Low) return (addr1Low > addr2Low) ? 1:-1; + // jump ends come before jump starts + if (addr1Low == ij1->instrTo()->addr()) return -1; + if (addr2Low == ij2->instrTo()->addr()) return 1; + return (addr1High > addr2High) ? 1:-1; + } + + // we sort according to highest instruction address + if (addr1High != addr2High) return (addr1High > addr2High) ? 1:-1; + // jump ends come before jump starts + if (addr1High == ij1->instrTo()->addr()) return -1; + if (addr2High == ij2->instrTo()->addr()) return 1; + return (addr1Low > addr2Low) ? 1:-1; +} + + +//--------------------------------------------------- +// TraceLineJump + +TraceLineJump::TraceLineJump(TraceLine* lineFrom, TraceLine* lineTo, + bool isCondJump) +{ + // we are the owner of TracePartLineJump's generated in our factory + _deps.setAutoDelete(true); + + _lineFrom = lineFrom; + _lineTo = lineTo; + _isCondJump = isCondJump; +} + +TraceLineJump::~TraceLineJump() +{} + + +TracePartLineJump* TraceLineJump::partLineJump(TracePart* part) +{ + TracePartLineJump* item = (TracePartLineJump*) findDepFromPart(part); + if (!item) { + item = new TracePartLineJump(this); + item->setPosition(part); + addDep(item); + } + return item; +} + + +QString TraceLineJump::name() const +{ + return QString("jump at %1 to %2") + .arg(_lineFrom->prettyName()) + .arg(_lineTo->prettyName()); +} + + +//--------------------------------------------------- +// TraceLineJumpList + + +int TraceLineJumpList::compareItems ( Item item1, Item item2 ) +{ + TraceLineJump* lj1 = (TraceLineJump*) item1; + TraceLineJump* lj2 = (TraceLineJump*) item2; + + uint line1Low = lj1->lineFrom()->lineno(); + uint line2Low = lj2->lineFrom()->lineno(); + uint line1High = lj1->lineTo()->lineno(); + uint line2High = lj2->lineTo()->lineno(); + uint t; + + if (line1Low > line1High) { + t = line1Low; line1Low = line1High; line1High = t; + } + if (line2Low > line2High) { + t = line2Low; line2Low = line2High; line2High = t; + } + + if (_sortLow) { + // we sort according to smallest line number + if (line1Low != line2Low) return line1Low - line2Low; + // jump ends come before jump starts + if (line1Low == lj1->lineTo()->lineno()) return -1; + if (line2Low == lj2->lineTo()->lineno()) return 1; + return line1High - line2High; + } + + // we sort according to highest line number + if (line1High != line2High) return line1High - line2High; + // jump ends come before jump starts + if (line1High == lj1->lineTo()->lineno()) return -1; + if (line2High == lj2->lineTo()->lineno()) return 1; + return line1Low - line2Low; +} + + +//--------------------------------------------------- +// TraceInstrCall + +TraceInstrCall::TraceInstrCall(TraceCall* call, TraceInstr* instr) +{ + // we are the owner of TracePartInstrCall's generated in our factory + _deps.setAutoDelete(true); + + _call = call; + _instr = instr; +} + +TraceInstrCall::~TraceInstrCall() +{} + + +TracePartInstrCall* TraceInstrCall::partInstrCall(TracePart* part, + TracePartCall*) +{ + TracePartInstrCall* item = (TracePartInstrCall*) findDepFromPart(part); + if (!item) { + item = new TracePartInstrCall(this); + item->setPosition(part); + addDep(item); + // instruction calls are not registered in function calls + // as together with line calls calls are duplicated + //partCall->addDep(item); + } + return item; +} + + +QString TraceInstrCall::name() const +{ + return QString("%1 at %2").arg(_call->name()).arg(_instr->name()); +} + + +//--------------------------------------------------- +// TraceLineCall + +TraceLineCall::TraceLineCall(TraceCall* call, TraceLine* line) +{ + // we are the owner of TracePartLineCall's generated in our factory + _deps.setAutoDelete(true); + + _call = call; + _line = line; +} + +TraceLineCall::~TraceLineCall() +{} + + +TracePartLineCall* TraceLineCall::partLineCall(TracePart* part, + TracePartCall* partCall) +{ + TracePartLineCall* item = (TracePartLineCall*) findDepFromPart(part); + if (!item) { + item = new TracePartLineCall(this); + item->setPosition(part); + addDep(item); + partCall->addDep(item); + } + return item; +} + + +QString TraceLineCall::name() const +{ + return QString("%1 at %2").arg(_call->name()).arg(_line->name()); +} + + +//--------------------------------------------------- +// TraceCall + +TraceCall::TraceCall(TraceFunction* caller, TraceFunction* called) +{ + // we are the owner of all items generated in our factory + _deps.setAutoDelete(true); + _lineCalls.setAutoDelete(true); + + _caller = caller; + _called = called; +} + + +TraceCall::~TraceCall() +{} + +TracePartCall* TraceCall::partCall(TracePart* part, + TracePartFunction* partCaller, + TracePartFunction* partCalling) +{ + TracePartCall* item = (TracePartCall*) findDepFromPart(part); + if (!item) { + item = new TracePartCall(this); + item->setPosition(part); + addDep(item); + partCaller->addPartCalling(item); + partCalling->addPartCaller(item); + } + return item; +} + +TraceInstrCall* TraceCall::instrCall(TraceInstr* i) +{ + TraceInstrCall* icall; + for (icall=_instrCalls.first();icall;icall=_instrCalls.next()) + if (icall->instr() == i) + break; + + if (!icall) { + icall = new TraceInstrCall(this, i); + + _instrCalls.append(icall); + invalidate(); + +#if TRACE_DEBUG + qDebug("Created %s [TraceCall::instrCall]", icall->fullName().ascii()); +#endif + i->addInstrCall(icall); + } + return icall; +} + + +TraceLineCall* TraceCall::lineCall(TraceLine* l) +{ + TraceLineCall* lcall; + for (lcall=_lineCalls.first();lcall;lcall=_lineCalls.next()) + if (lcall->line() == l) + break; + + if (!lcall) { + lcall = new TraceLineCall(this, l); + + _lineCalls.append(lcall); + invalidate(); + +#if TRACE_DEBUG + qDebug("Created %s [TraceCall::lineCall]", lcall->fullName().ascii()); +#endif + l->addLineCall(lcall); + } + return lcall; +} + + +void TraceCall::invalidateDynamicCost() +{ + TraceLineCall* lc; + for (lc=_lineCalls.first();lc;lc=_lineCalls.next()) + lc->invalidate(); + + TraceInstrCall* ic; + for (ic=_instrCalls.first();ic;ic=_instrCalls.next()) + ic->invalidate(); + + invalidate(); +} + + +QString TraceCall::name() const +{ + return QString("%1 => %2") + .arg(_caller->name()) + .arg(_called->name()); +} + +int TraceCall::inCycle() +{ + if (!_caller || !_called) return 0; + if (!_caller->cycle()) return 0; + if (_caller == _caller->cycle()) return 0; + if (_caller->cycle() != _called->cycle()) return 0; + + return _caller->cycle()->cycleNo(); +} + +void TraceCall::update() +{ + if (!_dirty) return; + + // special handling for cycles + if (_caller && _caller->cycle() && _caller==_caller->cycle()) { + + // we have no part calls: use inclusive cost of called function + clear(); + if (_called) + addCost(_called->inclusive()); + _dirty = false; + return; + } + + TraceCallListCost::update(); +} + +TraceFunction* TraceCall::caller(bool /*skipCycle*/) const +{ + return _caller; +} + +TraceFunction* TraceCall::called(bool skipCycle) const +{ + if (!skipCycle && _called) { + // if this is a call to a cycle member from outside of the cycle, + // fake it to be a call to the whole cycle + if (_called->cycle() && _caller && + (_caller->cycle() != _called->cycle())) + return _called->cycle(); + } + + return _called; +} + +QString TraceCall::callerName(bool skipCycle) const +{ + if (!_caller) return i18n("(no caller)"); + + if (!skipCycle) { + // if this call goes into a cycle, add the entry function + TraceFunctionCycle* c = _called->cycle(); + if (c && _caller && (_caller->cycle() != c)) { + QString via = _called->prettyName(); + return i18n("%1 via %2").arg(_caller->prettyName()).arg(via); + } + } + + return _caller->prettyName(); +} + +QString TraceCall::calledName(bool skipCycle) const +{ + if (!_called) return i18n("(no callee)"); + + if (!skipCycle) { + // if this call goes into a cycle, add the entry function + TraceFunctionCycle* c = _called->cycle(); + if (c && _caller && (_caller->cycle() != c)) { + // HACK to get rid of cycle postfix... + _called->setCycle(0); + QString via = _called->prettyName(); + _called->setCycle(c); + return i18n("%1 via %2").arg(c->name()).arg(via); + } + } + return _called->prettyName(); +} + + +//--------------------------------------------------- +// TraceInstr + +TraceInstr::TraceInstr() +{ + // we are the owner of TracePartInstr's generated in our factory + _deps.setAutoDelete(true); + _instrJumps.setAutoDelete(true); + + _addr = 0; + _line = 0; + _function = 0; +} + +TraceInstr::~TraceInstr() +{} + +bool TraceInstr::hasCost(TraceCostType* ct) +{ + bool res = subCost(ct) > 0; + if (!res) { + TraceInstrCall* ic; + for(ic=_instrCalls.first();ic;ic=_instrCalls.next()) + if (ic->subCost(ct) > 0) break; + res = (ic != 0); + if (!res) { + TraceInstrJump* ij; + for(ij=_instrJumps.first();ij;ij=_instrJumps.next()) + if (ij->executedCount() > 0) break; + res = (ij != 0); + } + } + + return res; +} + +TracePartInstr* TraceInstr::partInstr(TracePart* part, + TracePartFunction* partFunction) +{ + TracePartInstr* item = (TracePartInstr*) findDepFromPart(part); + if (!item) { + item = new TracePartInstr(this); + item->setPosition(part); + addDep(item); + //part->addDep(item); + partFunction->addPartInstr(item); + } + return item; +} + +TraceInstrJump* TraceInstr::instrJump(TraceInstr* to, bool isJmpCond) +{ + TraceInstrJump* jump; + for (jump=_instrJumps.first();jump;jump=_instrJumps.next()) + if (jump->instrTo() == to) + break; + + if (!jump) { + jump = new TraceInstrJump(this, to, isJmpCond); + + _instrJumps.append(jump); + } + return jump; +} + + + +void TraceInstr::addInstrCall(TraceInstrCall* instrCall) +{ +#if TRACE_ASSERTIONS + if (_instrCalls.findRef(instrCall)>=0) return; + + if (instrCall->instr() != this) { + qDebug("Can't add instruction call to another instruction!"); + return; + } +#endif + + _instrCalls.append(instrCall); + invalidate(); + +#if TRACE_DEBUG + qDebug("%s added\n %s (now %d)", + fullName().ascii(), + instrCall->fullName().ascii(), _instrCalls.count()); +#endif +} + + +QString TraceInstr::name() const +{ + return QString("0x%1").arg(_addr.toString()); +} + +QString TraceInstr::prettyName() const +{ + return QString("0x%1").arg(_addr.toString()); +} + + +//--------------------------------------------------- +// TraceLine + +TraceLine::TraceLine() +{ + // we are the owner of TracePartLine's generated in our factory + _deps.setAutoDelete(true); + _lineJumps.setAutoDelete(true); + + _lineno = 0; + _sourceFile = 0; +} + +TraceLine::~TraceLine() +{} + +bool TraceLine::hasCost(TraceCostType* ct) +{ + bool res = subCost(ct) > 0; + if (!res) { + TraceLineCall* lc; + for(lc=_lineCalls.first();lc;lc=_lineCalls.next()) + if (lc->subCost(ct) > 0) break; + res = (lc != 0); + if (!res) { + TraceLineJump* lj; + for(lj=_lineJumps.first();lj;lj=_lineJumps.next()) + if (lj->executedCount() > 0) break; + res = (lj != 0); + } + } + + return res; +} + +TracePartLine* TraceLine::partLine(TracePart* part, + TracePartFunction* partFunction) +{ + TracePartLine* item = (TracePartLine*) findDepFromPart(part); + if (!item) { + item = new TracePartLine(this); + item->setPosition(part); + addDep(item); +#if !USE_FIXCOST + part->addDep(item); +#endif + partFunction->addPartLine(item); + } + return item; +} + +TraceLineJump* TraceLine::lineJump(TraceLine* to, bool isJmpCond) +{ + TraceLineJump* jump; + for (jump=_lineJumps.first();jump;jump=_lineJumps.next()) + if (jump->lineTo() == to) + break; + + if (!jump) { + jump = new TraceLineJump(this, to, isJmpCond); + + _lineJumps.append(jump); + } + return jump; +} + + +void TraceLine::addLineCall(TraceLineCall* lineCall) +{ +#if TRACE_ASSERTIONS + if (_lineCalls.findRef(lineCall)>=0) return; + + if (lineCall->line() != this) { + qDebug("Can't add line call to another line!"); + return; + } +#endif + + TraceFunction* caller = lineCall->call()->caller(); + TraceFunction* function = _sourceFile->function(); + if (caller != function) { + // We regard 2 functions as the same if they have + // same class, name, object + if ((caller->cls() != function->cls()) || + (caller->name() != function->name()) || + (caller->object() != function->object())) { + + qDebug("ERROR: Adding line call, line %d\n of %s to\n %s ?!", + lineCall->line()->lineno(), + caller->info().ascii(), function->info().ascii()); + } + } + + _lineCalls.append(lineCall); + invalidate(); + +#if TRACE_DEBUG + qDebug("%s added\n %s (now %d)", + fullName().ascii(), + lineCall->fullName().ascii(), _lineCalls.count()); +#endif +} + + +QString TraceLine::name() const +{ + QString fileShortName = _sourceFile->file()->shortName(); + if (fileShortName.isEmpty()) + return i18n("(unknown)"); + + return QString("%1:%2") + .arg(fileShortName).arg(_lineno); +} + +QString TraceLine::prettyName() const +{ + return QString("%1 [%2]") + .arg(name()).arg(_sourceFile->function()->prettyName()); +} + +//--------------------------------------------------- +// TraceCostItem + +TraceCostItem::TraceCostItem() +{ +} + +TraceCostItem::~TraceCostItem() +{} + + +//--------------------------------------------------- +// TraceFunctionSource + +TraceFunctionSource::TraceFunctionSource(TraceFunction* function, + TraceFile* file) +{ + _file = file; + _function = function; + + // the function is dependent from our cost sum + _dep = _function; + + _lineMap = 0; + _lineMapFilled = false; + _line0 = 0; +} + +TraceFunctionSource::~TraceFunctionSource() +{ + if (_lineMap) delete _lineMap; + if (_line0) delete _line0; +} + +QString TraceFunctionSource::name() const +{ + return QString("%1 for %2").arg(_file->name()).arg(_function->name()); +} + +uint TraceFunctionSource::firstLineno() +{ + // lazy generate the map if not done up to now + TraceLineMap* map = lineMap(); + // ignore line 0 here + if (!map || map->count() == 0) return 0; + TraceLineMap::Iterator it = map->begin(); + return (*it).lineno(); +} + +uint TraceFunctionSource::lastLineno() +{ + // lazy generate the map if not done up to now + TraceLineMap* map = lineMap(); + // ignore line 0 here + if (!map || map->count() == 0) return 0; + TraceLineMap::Iterator it = map->end(); + --it; + return (*it).lineno(); +} + +/* factory */ +TraceLine* TraceFunctionSource::line(uint lineno, bool createNew) +{ + if (lineno == 0) { + if (!_line0) { + if (!createNew) return 0; + _line0 = new TraceLine; + _line0->setSourceFile(this); + _line0->setLineno(0); + } + return _line0; + } + + if (!createNew) { + if (!_lineMap) return 0; + TraceLineMap::Iterator it = _lineMap->find(lineno); + if (it == _lineMap->end()) return 0; + return &(it.data()); + } + + if (!_lineMap) _lineMap = new TraceLineMap; + + TraceLine& l = (*_lineMap)[lineno]; + if (!l.isValid()) { + l.setSourceFile(this); + l.setLineno(lineno); + +#if TRACE_DEBUG + qDebug("Created %s [TraceFunctionSource::line]", + l.fullName().ascii()); +#endif + } + return &l; +} + +void TraceFunctionSource::update() +{ + if (!_dirty) return; + + clear(); + + // no need to create lineMap if not already created + if (_lineMap) { + TraceLineMap::Iterator lit; + for ( lit = _lineMap->begin(); + lit != _lineMap->end(); ++lit ) + addCost( &(*lit) ); + } + + _dirty = false; +} + +void TraceFunctionSource::invalidateDynamicCost() +{ + // no need to create lineMap if not already created + if (_lineMap) { + TraceLineMap::Iterator lit; + for ( lit = _lineMap->begin(); + lit != _lineMap->end(); ++lit ) + (*lit).invalidate(); + } + + invalidate(); +} + +TraceLineMap* TraceFunctionSource::lineMap() +{ +#if USE_FIXCOST + + if (_lineMapFilled) return _lineMap; + _lineMapFilled = true; + if (!_lineMap) + _lineMap = new TraceLineMap; + + TraceLine* l = 0; + TracePartLine* pl = 0; + TraceLineCall* lc = 0; + TracePartLineCall* plc = 0; + + /* go over all part objects for this function, and + * - build TraceLines (the line map) using FixCost objects + * - build TraceJumpLines using FixJump objects + */ + TraceInclusiveCostList pfList = _function->deps(); + TracePartFunction* pf = (TracePartFunction*) pfList.first(); + for(; pf; pf = (TracePartFunction*) pfList.next()) { + + if (0) qDebug("PartFunction %s:%d", + pf->function()->name().ascii(), pf->part()->partNumber()); + + FixCost* fc = pf->firstFixCost(); + for(; fc; fc = fc->nextCostOfPartFunction()) { + if (fc->line() == 0) continue; + if (fc->functionSource() != this) continue; + + if (!l || l->lineno() != fc->line()) { + l = &(*_lineMap)[fc->line()]; + if (!l->isValid()) { + l->setSourceFile(this); + l->setLineno(fc->line()); + } + pl = 0; + } + if (!pl || pl->part() != fc->part()) + pl = l->partLine(fc->part(), pf); + fc->addTo(pl); + } + + TraceLine* to = 0; + TraceLineJump* lj; + TracePartLineJump* plj; + FixJump* fj = pf->firstFixJump(); + for(; fj; fj = fj->nextJumpOfPartFunction()) { + if (fj->line() == 0) continue; + if (fj->source() != this) continue; + if (!fj->targetSource()) { + // be robust against buggy loaders + continue; + } + + // don't display jumps to same or following line + if ((fj->line() == fj->targetLine()) || + (fj->line()+1 == fj->targetLine())) continue; + + if (!l || l->lineno() != fj->line()) { + l = &(*_lineMap)[fj->line()]; + if (!l->isValid()) { + l->setSourceFile(this); + l->setLineno(fj->line()); + } + } + + to = fj->targetSource()->line(fj->targetLine(), true); + + lj = l->lineJump(to, fj->isCondJump()); + plj = lj->partLineJump(fj->part()); + + fj->addTo(plj); + } + + + TracePartCallList pcList = pf->partCallings(); + TracePartCall* pc = pcList.first(); + for(; pc; pc = pcList.next()) { + + if (0) qDebug("PartCall %s:%d", + pc->call()->name().ascii(), + pf->part()->partNumber()); + + FixCallCost* fcc = pc->firstFixCallCost(); + for(; fcc; fcc = fcc->nextCostOfPartCall()) { + if (fcc->line() == 0) continue; + if (fcc->functionSource() != this) continue; + + if (!l || l->lineno() != fcc->line()) { + l = &(*_lineMap)[fcc->line()]; + if (!l->isValid()) { + l->setSourceFile(this); + l->setLineno(fcc->line()); + } + } + if (!lc || lc->call() != pc->call() || lc->line() != l) { + lc = pc->call()->lineCall(l); + plc = 0; + } + if (!plc || plc->part() != fcc->part()) + plc = lc->partLineCall(fcc->part(), pc); + + fcc->addTo(plc); + if (0) qDebug("Add FixCallCost %s:%d/0x%s, CallCount %s", + fcc->functionSource()->file()->shortName().ascii(), + fcc->line(), fcc->addr().toString().ascii(), + fcc->callCount().pretty().ascii()); + } + } + } + +#endif + + return _lineMap; +} + + + +//--------------------------------------------------- +// TraceAssoziation + +TraceAssoziation::TraceAssoziation() +{ + _function = 0; + _valid = false; +} + +TraceAssoziation::~TraceAssoziation() +{ + // don't delete from TraceFunction + if (_function) _function->removeAssoziation(this); +} + +bool TraceAssoziation::isAssoziated() +{ + if (!_function) return false; + + return _function->assoziation(rtti())==this; +} + +bool TraceAssoziation::setFunction(TraceFunction* f) +{ + if (_function == f) + return isAssoziated(); + + if (_function) { + // don't delete ourself + _function->removeAssoziation(this); + } + + _function = f; + if (f && f->assoziation(rtti()) == 0) { + f->addAssoziation(this); + return true; + } + return false; +} + +void TraceAssoziation::clear(TraceData* d, int rtti) +{ + TraceFunctionMap::Iterator it; + for ( it = d->functionMap().begin(); + it != d->functionMap().end(); ++it ) + (*it).removeAssoziation(rtti); +} + +void TraceAssoziation::invalidate(TraceData* d, int rtti) +{ + TraceFunctionMap::Iterator it; + for ( it = d->functionMap().begin(); + it != d->functionMap().end(); ++it ) + (*it).invalidateAssoziation(rtti); +} + + +//--------------------------------------------------- +// TraceFunction + +TraceFunction::TraceFunction() +{ + _object = 0; + _file = 0; + _cls = 0; + _cycle = 0; + + // we are the owner of items generated in our factory + _deps.setAutoDelete(true); + _callings.setAutoDelete(true); + _sourceFiles.setAutoDelete(true); + + _calledCount = 0; + _callingCount = 0; + _calledContexts = 0; + _callingContexts = 0; + + _instrMap = 0; + _instrMapFilled = false; +} + + +TraceFunction::~TraceFunction() +{ + _assoziations.setAutoDelete(true); + _assoziations.clear(); + + if (_instrMap) delete _instrMap; +} + +// no unique check is done! +void TraceFunction::addAssoziation(TraceAssoziation* a) +{ + if (!a) return; + _assoziations.append(a); +} + +void TraceFunction::removeAssoziation(TraceAssoziation* a) +{ + _assoziations.removeRef(a); +} + +void TraceFunction::removeAssoziation(int rtti, bool reallyDelete) +{ + if (rtti==0) { + if (reallyDelete) + _assoziations.setAutoDelete(true); + _assoziations.clear(); + _assoziations.setAutoDelete(false); + return; + } + + TraceAssoziation* a; + for (a=_assoziations.first();a;a=_assoziations.next()) + if (a->rtti() == rtti) { + if (reallyDelete) delete a; + _assoziations.remove(); + return; + } +} + +void TraceFunction::invalidateAssoziation(int rtti) +{ + TraceAssoziation* a; + for (a=_assoziations.first();a;a=_assoziations.next()) + if ((rtti==0) || (a->rtti() == rtti)) + a->invalidate(); +} + +TraceAssoziation* TraceFunction::assoziation(int rtti) +{ + TraceAssoziation* a; + for (a=_assoziations.first();a;a=_assoziations.next()) + if (a->rtti() == rtti) + return a; + return 0; +} + + +// helper for prettyName +bool TraceFunction::isUniquePrefix(QString prefix) const +{ + TraceFunctionMap::ConstIterator it, it2; + it = it2 = _myMapIterator; + if (it != data()->functionBeginIterator()) { + it2--; + if ((*it2).name().startsWith(prefix)) return false; + } + if (it != data()->functionEndIterator()) { + it++; + if ((*it).name().startsWith(prefix)) return false; + } + return true; +} + + +QString TraceFunction::prettyName() const +{ + QString res = _name; + + if (_name.isEmpty()) + return i18n("(unknown)"); + + int p = _name.find('('); + if (p>0) { + // handle C++ "operator()" correct + if ((_name[p+1] == ')') && (_name[p+2] == '(')) p+=2; + + // we have a C++ symbol with argument types: + // check for unique function name (inclusive '(' !) + if (isUniquePrefix(_name.left(p+1))) + res = _name.left(p); + } + + // cycle members + if (_cycle) { + if (_cycle != this) + res = QString("%1 ").arg(res).arg(_cycle->cycleNo()); + else + res = QString("").arg(_cycle->cycleNo()); + } + + + return res; +} + +/* + * Returns location string: ELF object and source file(s). + */ +QString TraceFunction::location(int maxFiles) const +{ + QString loc; + + // add object file with address range + if (_object) { + loc = _object->shortName(); + +#if 0 + uint from = firstAddress(); + uint to = lastAddress(); + if (from != 0 && to != 0) { + if (from == to) + loc += QString(" (0x%1)").arg(to, 0, 16); + else + loc += QString(" (0x%1-0x%2)").arg(from, 0, 16).arg(to, 0, 16); + } +#endif + } + + // add all source files + int filesAdded = 0; + TraceFunctionSourceList list = _sourceFiles; + TraceFunctionSource* sourceFile = list.first(); + for (;sourceFile;sourceFile=list.next()) { + if (!sourceFile->file() || + (sourceFile->file()->name().isEmpty()) ) + continue; + + if (!loc.isEmpty()) + loc += (filesAdded>0) ? ", " : ": "; + filesAdded++; + + if ((maxFiles>0) && (filesAdded>maxFiles)) { + loc += "..."; + break; + } + loc += sourceFile->file()->shortName(); + +#if 0 + from = sourceFile->firstLineno(); + to = sourceFile->lastLineno(); + if (from != 0 && to != 0) { + if (from == to) + loc += QString(" (%1)").arg(to); + else + loc += QString(" (%1-%2)").arg(from).arg(to); + } +#endif + } + + return loc; +} + +// pretty version is allowed to mangle the string... +QString TraceFunction::prettyLocation(int maxFiles) const +{ + QString l = location(maxFiles); + if (l.isEmpty()) return i18n("(unknown)"); + + return l; +} + +void TraceFunction::addPrettyLocation(QString& s, int maxFiles) const +{ + QString l = location(maxFiles); + if (l.isEmpty()) return; + + s += QString(" (%1)").arg(l); +} + +QString TraceFunction::prettyNameWithLocation(int maxFiles) const +{ + QString l = location(maxFiles); + if (l.isEmpty()) return prettyName(); + + return QString("%1 (%2)").arg(prettyName()).arg(l); +} + +QString TraceFunction::info() const +{ + QString l = location(); + if (l.isEmpty()) + return QString("Function %1").arg(name()); + + return QString("Function %1 (location %2)") + .arg(name()).arg(l); +} + + +Addr TraceFunction::firstAddress() const +{ + // ignore address 0 here + if (!_instrMap || _instrMap->count() == 0) return 0; + TraceInstrMap::ConstIterator it = _instrMap->begin(); + return (*it).addr(); +} + +Addr TraceFunction::lastAddress() const +{ + // ignore address 0 here + if (!_instrMap || _instrMap->count() == 0) return 0; + TraceInstrMap::ConstIterator it = _instrMap->end(); + --it; + return (*it).addr(); +} + +/* factory */ +TraceInstr* TraceFunction::instr(Addr addr, bool createNew) +{ + // address 0 not allowed + if (addr == Addr(0)) return 0; + + if (!createNew) { + if (!_instrMap) return 0; + TraceInstrMap::Iterator it = _instrMap->find(addr); + if (it == _instrMap->end()) + return 0; + return &(it.data()); + } + + if (!_instrMap) _instrMap = new TraceInstrMap; + + TraceInstr& i = (*_instrMap)[addr]; + if (!i.isValid()) { + i.setAddr(addr); + i.setFunction(this); + +#if TRACE_DEBUG + qDebug("Created %s [TraceFunction::instr]", + i.fullName().ascii()); +#endif + } + return &i; +} + +void TraceFunction::addCaller(TraceCall* caller) +{ +#if TRACE_ASSERTIONS + if (caller->called() != this) { + qDebug("Can't add call to another line!\n"); + return; + } + + if (_callers.findRef(caller)>=0) return; +#endif + + _callers.append(caller); + invalidate(); + +#if TRACE_DEBUG + qDebug("%s added Caller\n %s (now %d)", + fullName().ascii(), caller->fullName().ascii(), _callers.count()); +#endif +} + + + +TraceCall* TraceFunction::calling(TraceFunction* called) +{ + TraceCallMap::Iterator it = _callingMap.find(called); + TraceCall* calling = (it == _callingMap.end()) ? 0 : it.data(); + + if (!calling) { + calling = new TraceCall(this, called); + + _callingMap.insert(called, calling); + _callings.append(calling); + + // we have to invalidate ourself so invalidations from item propagate up + invalidate(); + +#if TRACE_DEBUG + qDebug("Created %s [TraceFunction::calling]", calling->fullName().ascii()); +#endif + called->addCaller(calling); + } + return calling; +} + +TraceFunctionSource* TraceFunction::sourceFile(TraceFile* file, + bool createNew) +{ + if (!file) file = _file; + + TraceFunctionSource* sourceFile = _sourceFiles.first(); + for (;sourceFile;sourceFile=_sourceFiles.next()) + if (sourceFile->file() == file) break; + + if (!sourceFile && createNew) { + sourceFile = new TraceFunctionSource(this, file); + + _sourceFiles.append(sourceFile); + + // we have to invalidate ourself so invalidations from item propagate up + invalidate(); + +#if TRACE_DEBUG + qDebug("Created SourceFile %s [TraceFunction::line]", + file->name().ascii()); +#endif + file->addSourceFile(sourceFile); + } + return sourceFile; +} + +TraceLine* TraceFunction::line(TraceFile* file, uint lineno, + bool createNew) +{ + Q_ASSERT(file!=0); + + TraceFunctionSource* sf = sourceFile(file, createNew); + if (!sf) + return 0; + else + return sf->line(lineno, createNew); +} + + +TracePartFunction* TraceFunction::partFunction(TracePart* part, + TracePartFile* partFile, + TracePartObject* partObject) +{ + TracePartFunction* item = (TracePartFunction*) findDepFromPart(part); + if (!item) { + item = new TracePartFunction(this, partObject, partFile); + item->setPosition(part); + addDep(item); +#if USE_FIXCOST + part->addDep(item); +#endif + + if (_cls) { + TracePartClass* partClass = _cls->partClass(part); + partClass->addPartFunction(item); + item->setPartClass(partClass); + } + + partFile->addPartFunction(item); + if (partObject) + partObject->addPartFunction(item); + } + else if (item->partObject()==0 && partObject) { + item->setPartObject(partObject); + partObject->addPartFunction(item); + } + + return item; +} + + +SubCost TraceFunction::calledCount() +{ + if (_dirty) update(); + + return _calledCount; +} + +int TraceFunction::calledContexts() +{ + if (_dirty) update(); + + return _calledContexts; +} + +SubCost TraceFunction::callingCount() +{ + if (_dirty) update(); + + return _callingCount; +} + +int TraceFunction::callingContexts() +{ + if (_dirty) update(); + + return _callingContexts; +} + +QString TraceFunction::prettyCalledCount() +{ + return _calledCount.pretty(); +} + +QString TraceFunction::prettyCallingCount() +{ + return _callingCount.pretty(); +} + + +TraceCallList TraceFunction::callers(bool skipCycle) const +{ + if (skipCycle) return _callers; + + // fake the callers for cycle members + if (_cycle && (_cycle != this)) { + TraceCallList l; + TraceCall* c; + + // inner-cycle-callers + TraceCallList list=_callers; + for (c=list.first();c;c=list.next()) + if (c->caller()->cycle() == _cycle) + l.append(c); + + // call from cycle itself + for (c=_cycle->_callings.first();c;c=_cycle->_callings.next()) + if (c->called() == this) { + l.append(c); + return l; + } + } + + return _callers; +} + +const TraceCallList& TraceFunction::callings(bool /* skipCycle */) const +{ + return _callings; +} + +void TraceFunction::invalidateDynamicCost() +{ + TraceCall* c; + for (c=_callings.first();c;c=_callings.next()) + c->invalidateDynamicCost(); + + TraceFunctionSource* sf; + for (sf=_sourceFiles.first();sf;sf=_sourceFiles.next()) + sf->invalidateDynamicCost(); + + if (_instrMap) { + TraceInstrMap::Iterator iit; + for ( iit = _instrMap->begin(); + iit != _instrMap->end(); ++iit ) + (*iit).invalidate(); + } + + invalidate(); +} + +void TraceFunction::update() +{ + if (!_dirty) return; + +#if TRACE_DEBUG + qDebug("Update %s (Callers %d, sourceFiles %d, instrs %d)", + _name.ascii(), _callers.count(), + _sourceFiles.count(), _instrMap ? _instrMap->count():0); +#endif + + _calledCount = 0; + _callingCount = 0; + _calledContexts = 0; + _callingContexts = 0; + clear(); + + // context count is NOT the sum of part contexts + TraceCall *caller, *calling; + for (caller=_callers.first();caller;caller=_callers.next()) { + // FIXME + if (caller->subCost(0)>0) + _calledContexts++; + _calledCount += caller->callCount(); + } + + for (calling=_callings.first();calling;calling=_callings.next()) { + // FIXME + if (calling->subCost(0)>0) _callingContexts++; + _callingCount += calling->callCount(); + } + + if (data()->inFunctionCycleUpdate() || !_cycle) { + // usual case (no cycle member) + TraceInclusiveCost* item; + for (item=_deps.first();item;item=_deps.next()) { + if (!item->part() || !item->part()->isActive()) continue; + + addCost(item); + addInclusive(item->inclusive()); + } + } + else { + // this is a cycle or cycle member + for (calling=_callings.first();calling;calling=_callings.next()) { + + // ignore inner-cycle member calls for inclusive cost + if ((_cycle != this) && + (calling->inCycle()>0)) continue; + + addInclusive(calling); + } + + // self cost + if (type() == FunctionCycle) { + // cycle: self cost is sum of cycle member self costs, but + // doesn't add to inclusive cost + TraceFunctionList mList = ((TraceFunctionCycle*)this)->members(); + TraceFunction* m; + for (m=mList.first();m;m=mList.next()) + addCost(m); + } + else { + // cycle member + TraceInclusiveCost* item; + for (item=_deps.first();item;item=_deps.next()) { + if (!item->part() || !item->part()->isActive()) continue; + + addCost(item); + } + _dirty = false; // don't recurse + addInclusive(this); + } + } + _dirty = false; + +#if TRACE_DEBUG + qDebug("> %s", costString(0).ascii()); +#endif +} + +bool TraceFunction::isCycle() +{ + return _cycle == this; +} + +bool TraceFunction::isCycleMember() +{ + return _cycle && (_cycle != this); +} + +void TraceFunction::cycleReset() +{ + _cycle = 0; + _cycleStackDown = 0; + _cycleLow = 0; +} + +// this doesn't mark functions calling themself ! +void TraceFunction::cycleDFS(int d, int& pNo, TraceFunction** pTop) +{ + if (_cycleLow != 0) return; + + if (0) + qDebug("%s D%02d > %s (%d)", + QString().fill(' ', d).ascii(), d, prettyName().ascii(), pNo+1); + + + + // initialize with prefix order + pNo++; + int prefixNo = pNo; + _cycleLow = prefixNo; + + // put myself on stack + _cycleStackDown = *pTop; + *pTop = this; + + /* cycle cut heuristic: + * skip calls for cycle detection if they make less than _cycleCut + * percent of the cost of the function. + * FIXME: Which cost type to use for this heuristic ?! + */ + + SubCost base = 0; + if (_callers.count()>0) { + TraceCallList l = _callers; + TraceCall *caller; + + for (caller=l.first();caller;caller=l.next()) + if (caller->subCost(0) > base) + base = caller->subCost(0); + } + else base = inclusive()->subCost(0); + + SubCost cutLimit = SubCost(base * Configuration::cycleCut()); + + if (0) + qDebug("%s Cum. %s, Max Caller %s, cut limit %s", + QString().fill(' ', d).ascii(), + inclusive()->subCost(0).pretty().ascii(), + base.pretty().ascii(), + cutLimit.pretty().ascii()); + + TraceCall *calling; + TraceCallList l = _callings; + for (calling=l.first();calling;calling=l.next()) { + TraceFunction* called = calling->called(); + + // cycle cut heuristic + if (calling->subCost(0) < cutLimit) { + if (0) qDebug("%s Cut call to %s (cum. %s)", + QString().fill(' ', d).ascii(), + called->prettyName().ascii(), + calling->subCost(0).pretty().ascii()); + + continue; + } + + if (called->_cycleLow==0) { + // not visited yet + called->cycleDFS(d+1, pNo, pTop); + if (called->_cycleLow < _cycleLow) + _cycleLow = called->_cycleLow; + } + else if (called->_cycleStackDown) { + // backlink to same SCC (still in stack) + if (called->_cycleLow < _cycleLow) + _cycleLow = called->_cycleLow; + + if (0) + qDebug("%s D%02d - %s (%d)", + QString().fill(' ', d+1).ascii(), d+1, + called->prettyName().ascii(), called->_cycleLow); + } + else { + if (0) + qDebug("%s D%02d - %s (%d) [Not on stack]", + QString().fill(' ', d+1).ascii(), d+1, + called->prettyName().ascii(), called->_cycleLow); + } + } + + if (prefixNo == _cycleLow) { + // this is the base of a SCC. + + if (*pTop == this) { + *pTop = _cycleStackDown; + _cycleStackDown = 0; + } + else { + // a SCC with >1 members + + TraceFunctionCycle* cycle = data()->functionCycle(this); + if (0) qDebug("BASE CYC %d %s", + cycle->cycleNo(), prettyName().ascii()); + while(*pTop) { + TraceFunction* top = *pTop; + cycle->add(top); + + // remove from stack + *pTop = top->_cycleStackDown; + top->_cycleStackDown = 0; + + if (0) qDebug("CYC %s", top->prettyName().ascii()); + if (top == this) break; + } + } + } + if (0) + qDebug("%s D%02d < %s (%d)", + QString().fill(' ', d).ascii(), d, + prettyName().ascii(), _cycleLow); +} + + +TraceInstrMap* TraceFunction::instrMap() +{ +#if USE_FIXCOST + + if (_instrMapFilled) return _instrMap; + _instrMapFilled = true; + if (!_instrMap) + _instrMap = new TraceInstrMap; + + TraceLine* l = 0; + TraceInstr* i = 0; + TracePartInstr* pi = 0; + TraceInstrCall* ic = 0; + TracePartInstrCall* pic = 0; + + TraceInclusiveCostList pfList = deps(); + TracePartFunction* pf = (TracePartFunction*) pfList.first(); + for(; pf; pf = (TracePartFunction*) pfList.next()) { + + if (0) qDebug("PartFunction %s:%d", + pf->function()->name().ascii(), pf->part()->partNumber()); + + FixCost* fc = pf->firstFixCost(); + for(; fc; fc = fc->nextCostOfPartFunction()) { + if (fc->addr() == 0) continue; + + if (!l || (l->lineno() != fc->line()) || + (l->functionSource() != fc->functionSource())) + l = fc->functionSource()->line(fc->line(),true); + + if (!i || i->addr() != fc->addr()) { + i = &(*_instrMap)[fc->addr()]; + if (!i->isValid()) { + i->setFunction(this); + i->setAddr(fc->addr()); + i->setLine(l); + } + pi = 0; + } + if (!pi || pi->part() != fc->part()) + pi = i->partInstr(fc->part(), pf); + fc->addTo(pi); + } + + TraceInstr* to = 0; + TraceInstrJump* ij; + TracePartInstrJump* pij; + FixJump* fj = pf->firstFixJump(); + for(; fj; fj = fj->nextJumpOfPartFunction()) { + if (fj->addr() == 0) continue; + + if (!l || (l->lineno() != fj->line()) || + (l->functionSource() != fj->source())) + l = fj->source()->line(fj->line(),true); + + if (!i || i->addr() != fj->addr()) { + i = &(*_instrMap)[fj->addr()]; + if (!i->isValid()) { + i->setFunction(this); + i->setAddr(fj->addr()); + i->setLine(l); + } + } + + to = fj->targetFunction()->instr(fj->targetAddr(), true); + + ij = i->instrJump(to, fj->isCondJump()); + pij = ij->partInstrJump(fj->part()); + + fj->addTo(pij); + } + + TracePartCallList pcList = pf->partCallings(); + TracePartCall* pc = pcList.first(); + for(; pc; pc = pcList.next()) { + + if (0) qDebug("PartCall %s:%d", + pc->call()->name().ascii(), + pf->part()->partNumber()); + + FixCallCost* fcc = pc->firstFixCallCost(); + for(; fcc; fcc = fcc->nextCostOfPartCall()) { + if (fcc->addr() == 0) continue; + + if (!l || (l->lineno() != fcc->line()) || + (l->functionSource() != fcc->functionSource())) + l = fcc->functionSource()->line(fcc->line(),true); + + if (!i || i->addr() != fcc->addr()) { + i = &(*_instrMap)[fcc->addr()]; + if (!i->isValid()) { + i->setFunction(this); + i->setAddr(fcc->addr()); + i->setLine(l); + } + } + if (!ic || ic->call() != pc->call() || ic->instr() != i) { + ic = pc->call()->instrCall(i); + pic = 0; + } + if (!pic || pic->part() != fcc->part()) + pic = ic->partInstrCall(fcc->part(), pc); + + fcc->addTo(pic); + if (0) qDebug("Add FixCallCost %s:%d/0x%s, CallCount %s", + fcc->functionSource()->file()->shortName().ascii(), + fcc->line(), fcc->addr().toString().ascii(), + fcc->callCount().pretty().ascii()); + } + } + } + +#endif + + return _instrMap; +} + + + +//--------------------------------------------------- +// TraceFunctionCycle + +TraceFunctionCycle::TraceFunctionCycle(TraceFunction* f, int n) +{ + _base = f; + _cycleNo = n; + _cycle = this; + + setPosition(f->data()); + setName(QString("").arg(n)); + + // reset to attributes of base function + setFile(_base->file()); + setClass(_base->cls()); + setObject(_base->object()); +} + +void TraceFunctionCycle::init() +{ + _members.clear(); + _memberSet.clear(); + _callers.clear(); + // this deletes all TraceCall's to members + _callings.clear(); + + invalidate(); +} + +void TraceFunctionCycle::add(TraceFunction* f) +{ + _members.append(f); + _memberSet.insert(f,1); +} + +void TraceFunctionCycle::setup() +{ + if (_members.count()==0) return; + + TraceFunction* f; + for (f=_members.first();f;f=_members.next()) { + + // the cycle takes all outside callers from its members + TraceCall *call; + TraceCallList l = f->callers(); + for (call=l.first();call;call=l.next()) { + if ( _memberSet.contains(call->caller()) ) continue; + _callers.append(call); + } + + // the cycle has a call to each member + call = new TraceCall(this, f); + call->invalidate(); + _callings.append(call); + + // now do some faking... + f->setCycle(this); + } + invalidate(); +} + + +//--------------------------------------------------- +// TraceClass + +TraceClass::TraceClass() +{ + // we are the owner of items generated in our factory + _deps.setAutoDelete(true); +} + +TraceClass::~TraceClass() +{} + +QString TraceClass::prettyName() const +{ + if (_name.isEmpty()) + return QString("(global)"); + return _name; +} + +TracePartClass* TraceClass::partClass(TracePart* part) +{ + TracePartClass* item = (TracePartClass*) findDepFromPart(part); + if (!item) { + item = new TracePartClass(this); + item->setPosition(part); + addDep(item); + } + return item; +} + +void TraceClass::addFunction(TraceFunction* function) +{ +#if TRACE_ASSERTIONS + if (function->cls() != this) { + qDebug("Can't add function to a class not enclosing this function\n"); + return; + } + + if (_functions.findRef(function)>=0) return; +#endif + + _functions.append(function); + + invalidate(); + +#if TRACE_DEBUG + qDebug("%s added\n %s (now %d)", + fullName().ascii(), + function->fullName().ascii(), _functions.count()); +#endif +} + + + +//--------------------------------------------------- +// TraceFile + +TraceFile::TraceFile() +{ + // we are the owner of items generated in our factory + _deps.setAutoDelete(true); +} + +TraceFile::~TraceFile() +{} + +TracePartFile* TraceFile::partFile(TracePart* part) +{ + TracePartFile* item = (TracePartFile*) findDepFromPart(part); + if (!item) { + item = new TracePartFile(this); + item->setPosition(part); + addDep(item); + } + return item; +} + +void TraceFile::addFunction(TraceFunction* function) +{ +#if TRACE_ASSERTIONS + if (function->file() != this) { + qDebug("Can't add function to a file not enclosing this function\n"); + return; + } + + if (_functions.findRef(function)>=0) return; +#endif + + _functions.append(function); + + invalidate(); + +#if TRACE_DEBUG + qDebug("%s added\n %s (now %d)", + fullName().ascii(), + function->fullName().ascii(), _functions.count()); +#endif +} + + +void TraceFile::addSourceFile(TraceFunctionSource* sourceFile) +{ +#if TRACE_ASSERTIONS + if (sourceFile->file() != this) { + qDebug("Can't add sourceFile to a file not having lines for it\n"); + return; + } +#endif + + _sourceFiles.append(sourceFile); + // not truely needed, as we don't use the sourceFiles for cost update + invalidate(); + +#if TRACE_DEBUG + qDebug("%s \n added SourceFile %s (now %d)", + fullName().ascii(), sourceFile->fullName().ascii(), + _sourceFiles.count()); +#endif +} + + + +void TraceFile::setDirectory(const QString& dir) +{ + if (dir.endsWith("/")) + _dir = dir.left(dir.length()-1); + else + _dir = dir; +} + +QString TraceFile::directory() +{ + if (!_dir.isEmpty()) return _dir; + + int lastIndex = 0, index; + while ( (index=_name.find("/", lastIndex)) >=0) + lastIndex = index+1; + + if (lastIndex==0) return QString::null; + + // without ending "/" + return _name.left(lastIndex-1); +} + + +QString TraceFile::shortName() const +{ + int lastIndex = 0, index; + while ( (index=_name.find("/", lastIndex)) >=0) + lastIndex = index+1; + + return _name.mid(lastIndex); +} + +QString TraceFile::prettyName() const +{ + QString sn = shortName(); + + if (sn.isEmpty()) + return i18n("(unknown)"); + + return sn; +} + +QString TraceFile::prettyLongName() const +{ + if (_name.isEmpty()) + return i18n("(unknown)"); + return _name; +} + + +//--------------------------------------------------- +// TraceObject + +TraceObject::TraceObject() +{ + // we are the owner of items generated in our factory + _deps.setAutoDelete(true); +} + +TraceObject::~TraceObject() +{} + +TracePartObject* TraceObject::partObject(TracePart* part) +{ + TracePartObject* item = (TracePartObject*) findDepFromPart(part); + if (!item) { + item = new TracePartObject(this); + item->setPosition(part); + addDep(item); + } + return item; +} + +void TraceObject::addFunction(TraceFunction* function) +{ +#if TRACE_ASSERTIONS + if (function->object() != this) { + qDebug("Can't add function to an object not enclosing this function\n"); + return; + } + + if (_functions.findRef(function)>=0) return; +#endif + + _functions.append(function); + + invalidate(); + +#if TRACE_DEBUG + qDebug("%s added\n %s (now %d)", + fullName().ascii(), + function->fullName().ascii(), _functions.count()); +#endif +} + +// strip path +void TraceObject::setName(const QString& name) +{ + _name = name; + + int lastIndex = 0, index; + while ( (index=_name.find("/", lastIndex)) >=0) + lastIndex = index+1; + + _shortName = _name.mid(lastIndex); +} + +QString TraceObject::prettyName() const +{ + if (_shortName.isEmpty()) + return i18n("(unknown)"); + + return _shortName; +} + +//--------------------------------------------------- +// TracePart + +TracePart::TracePart(TraceData* data, QFile* file) +{ + setPosition(data); + + _dep = data; + _file = file; + if (_file) + _name = _file->name(); + _active = true; + + _number = 0; + _tid = 0; + _pid = 0; + + _fixSubMapping = 0; +} + +TracePart::~TracePart() +{ + delete _file; + + delete _fixSubMapping; +} + +void TracePart::setPartNumber(int n) +{ + if (data()->maxPartNumber() setMaxPartNumber(n); + _number = n; +} + +void TracePart::setThreadID(int tid) +{ + if (data()->maxThreadID() setMaxThreadID(tid); + _tid = tid; +} + +void TracePart::setProcessID(int pid) +{ + _pid = pid; +} + + + +// strip path +QString TracePart::shortName() const +{ + int lastIndex = 0, index; + while ( (index=_name.find("/", lastIndex)) >=0) + lastIndex = index+1; + + return _name.mid(lastIndex); +} + +QString TracePart::prettyName() const +{ + QString name = QString("%1.%2").arg(_pid).arg(_number); + if (data()->maxThreadID()>1) + name += QString("-%3").arg(_tid); + return name; +} + +bool TracePart::activate(bool active) +{ + if (_active == active) return false; + _active = active; + + // to be done by the client of this function + // data()->invalidateDynamicCost(); + // So better use the TraceData functions... + + return true; +} + + + +//--------------------------------------------------- +// TracePartList + +int TracePartList::compareItems ( Item item1, Item item2 ) +{ + TracePart* p1 = (TracePart*) item1; + TracePart* p2 = (TracePart*) item2; + int mTID = p1->data()->maxThreadID()+1; + int mNum = p1->data()->maxPartNumber()+1; + + return + (p1->processID() - p2->processID()) * mTID * mNum + + (p1->partNumber() - p2->partNumber()) * mTID + + (p1->threadID() - p2->threadID()); +} + +QString TracePartList::names() const +{ + QString res; + TracePart* p; + TracePartList l = *this; + for (p=l.first();p;p=l.next()) { + if (!res.isEmpty()) res += ", "; + res += p->shortName(); + } + + return res; +} + + + +//--------------------------------------------------- +// TraceData + + +// create vectors with reasonable default sizes, but not wasting memory +TraceData::TraceData(TopLevel* top) +{ + _topLevel = top; + init(); +} + +TraceData::TraceData(const QString& base) +{ + _topLevel = 0; + init(); + load(base); +} + +void TraceData::init() +{ + _parts.setAutoDelete(true); + + _functionCycleCount = 0; + _inFunctionCycleUpdate = false; + + _maxThreadID = 0; + _maxPartNumber = 0; + _fixPool = 0; + _dynPool = 0; +} + +TraceData::~TraceData() +{ + if (_fixPool) delete _fixPool; + if (_dynPool) delete _dynPool; +} + +QString TraceData::shortTraceName() const +{ + int lastIndex = 0, index; + while ( (index=_traceName.find("/", lastIndex)) >=0) + lastIndex = index+1; + + return _traceName.mid(lastIndex); +} + +FixPool* TraceData::fixPool() +{ + if (!_fixPool) + _fixPool = new FixPool(); + + return _fixPool; +} + +DynPool* TraceData::dynPool() +{ + if (!_dynPool) + _dynPool = new DynPool(); + + return _dynPool; +} + + +/** + * Two cases: + * + * - is a directory: Load first profile data file available + * - is a file name without part/thread suffixes + */ +void TraceData::load(const QString& base) +{ + bool baseExisting = true; + + _traceName = base; + QFileInfo finfo(base); + QString file = finfo.fileName(); + QDir dir = finfo.dir(); + + if (!finfo.exists()) { + baseExisting = false; + } + else if (finfo.isDir()) { + // search for first profile data file in directory + dir = QDir(base); + + QStringList prefixList; + prefixList << "callgrind.out" << "cachegrind.out"; + for ( QStringList::Iterator it = prefixList.begin(); + it != prefixList.end(); ++it ) { + file = *it; + + // search for ".pid" + QStringList strList = dir.entryList(file+".*", QDir::Files); + if (strList.count()>0) { + int l = file.length(); + file = strList.first(); + l++; + while(file[l] >= '0' && file[l] <= '9') l++; + file = file.left(l); + break; + } + } + + _traceName = dir.path() + "/" + file; + } + + QStringList strList; + strList += dir.entryList(file+".*", QDir::Files); + strList += dir.entryList(file+"-*", QDir::Files); + + baseExisting = QFile::exists(_traceName); + if (baseExisting) + strList << file; + + if (strList.count() == 0) { + _traceName = base + "/" + file + " " + i18n("(not found)"); + return; + } + + + // try to guess pid from file name + unsigned int pos = file.length(); + unsigned int pid = 0, f=1; + pos--; + while(pos>0) { + if (file[pos] < '0' || file[pos] > '9') break; + pid += f * (file[pos].latin1() - '0'); + pos--; + f *= 10; + } + + QStringList::Iterator it; + unsigned int maxNumber = 0; + for (it = strList.begin(); it != strList.end(); ++it ) { + TracePart* p = addPart( dir.path(), *it ); + + if (!p) { + kdDebug() << "Error loading " << *it << endl; + continue; + } + + const QString& str = *it; + unsigned int pos = file.length(); + + // try to guess part number from file name + unsigned int n = 0; + if ((str.length() > pos) && (str[pos] == '.')) { + pos++; + while(str.length()>pos) { + if (str[pos] < '0' || str[pos] > '9') break; + n = 10*n + (str[pos++] - '0'); + } + } + + // try to guess thread number from file name + unsigned int t = 0; + if ((str.length() > pos) && (str[pos] == '-')) { + pos++; + while(str.length()>pos) { + if (str[pos] < '0' || str[pos] > '9') break; + t = 10*t + (str[pos++] - '0'); + } + } + + //qDebug("File %s: Part %d, Thread %d", (*it).ascii(), n, t); + + if (p->partNumber()>0) n = p->partNumber(); + if (n>maxNumber) maxNumber = n; + if (n==0) n = maxNumber+1; + p->setPartNumber(n); + + if (p->threadID()==0) p->setThreadID(t); + if (p->processID()==0) p->setProcessID(pid); + + _parts.append(p); + } + _parts.sort(); + + invalidateDynamicCost(); + updateFunctionCycles(); + + // clear loading messages from status bar + if (_topLevel) _topLevel->showStatus(QString::null, 0); +} + +TracePart* TraceData::addPart(const QString& dir, const QString& name) +{ + QString filename = QString("%1/%2").arg(dir).arg(name); +#if TRACE_DEBUG + qDebug("TraceData::addPart('%s')", filename.ascii()); +#endif + + QFile* file = new QFile(filename); + + Loader* l = Loader::matchingLoader(file); + if (!l) return 0; + + if (_topLevel) + _topLevel->connect(l, SIGNAL(updateStatus(QString, int)), + SLOT(showStatus(QString, int))); + + TracePart* part = new TracePart(this, file); + + if (! l->loadTrace(part)) { + delete part; + part = 0; + } + + if (_topLevel) l->disconnect(_topLevel); + + return part; +} + +bool TraceData::activateParts(const TracePartList& l) +{ + bool changed = false; + + TracePart* part; + for (part=_parts.first();part;part=_parts.next()) + if (part->activate(l.containsRef(part)>0)) + changed = true; + + if (changed) { + // because active parts have changed, throw away calculated + // costs... + invalidateDynamicCost(); + updateFunctionCycles(); + } + + return changed; +} + + +bool TraceData::activateParts(TracePartList l, bool active) +{ + bool changed = false; + + TracePart* part; + for (part=l.first();part;part=l.next()) + if (_parts.findRef(part)>=0) + if (part->activate(active)) + changed = true; + + if (changed) { + invalidateDynamicCost(); + updateFunctionCycles(); + } + + return changed; +} + +bool TraceData::activatePart(TracePart* p, bool active) +{ + return p->activate(active); +} + +bool TraceData::activateAll(bool active) +{ + return activateParts(_parts, active); +} + + +TracePart* TraceData::part(QString& name) +{ + TracePart* part; + for (part=_parts.first();part;part=_parts.next()) + if (part->name() == name) + return part; + return 0; +} + +QString TraceData::activePartRange() +{ + QString res; + int r1=-1, r2=-1, count=1; + TracePart* part; + for (part=_parts.first();part;part=_parts.next(), count++) + if (part->isActive()) { + if (r1<0) { r1 = r2 = count; } + else if (r2 == count-1) { r2 = count; } + else { + if (!res.isEmpty()) res += ";"; + if (r1==r2) res += QString::number(r1); + else res += QString("%1-%2").arg(r1).arg(r2); + r1 = r2 = count; + } + } + if (r1>=0) { + if (!res.isEmpty()) res += ";"; + if (r1==r2) res += QString::number(r1); + else res += QString("%1-%2").arg(r1).arg(r2); + } + + return res; +} + +void TraceData::invalidateDynamicCost() +{ + // invalidate all dynamic costs + + TraceObjectMap::Iterator oit; + for ( oit = _objectMap.begin(); + oit != _objectMap.end(); ++oit ) + (*oit).invalidate(); + + TraceClassMap::Iterator cit; + for ( cit = _classMap.begin(); + cit != _classMap.end(); ++cit ) + (*cit).invalidate(); + + TraceFileMap::Iterator fit; + for ( fit = _fileMap.begin(); + fit != _fileMap.end(); ++fit ) + (*fit).invalidate(); + + TraceFunctionMap::Iterator it; + for ( it = _functionMap.begin(); + it != _functionMap.end(); ++it ) { + (*it).invalidateDynamicCost(); + } + + invalidate(); + +} + + +TraceObject* TraceData::object(const QString& name) +{ + TraceObject& o = _objectMap[name]; + if (!o.data()) { + // was created + o.setPosition(this); + o.setName(name); + +#if TRACE_DEBUG + qDebug("Created %s [TraceData::object]", + o.fullName().ascii()); +#endif + } + return &o; +} + + +TraceFile* TraceData::file(const QString& name) +{ + TraceFile& f = _fileMap[name]; + if (!f.data()) { + // was created + f.setPosition(this); + f.setName(name); + +#if TRACE_DEBUG + qDebug("Created %s [TraceData::file]", + f.fullName().ascii()); +#endif + } + return &f; +} + + +// usually only called by function() +TraceClass* TraceData::cls(const QString& fnName, QString& shortName) +{ + int lastIndex = 0, index, pIndex; + + // we ignore any "::" after a '(' or a space + pIndex=fnName.find("(", 0); + +#if 0 + int sIndex=fnName.find(" ", 0); + if (sIndex>=0) + if ((pIndex == -1) || (sIndex < pIndex)) + pIndex = sIndex; +#endif + + while ((index=fnName.find("::", lastIndex)) >=0) { + if (pIndex>=0 && pIndexshortName(); + + TraceFunctionMap::Iterator it; + it = _functionMap.find(key); + if (it == _functionMap.end()) { + it = _functionMap.insert(key, TraceFunction()); + TraceFunction& f = it.data(); + + f.setPosition(this); + f.setName(name); + f.setClass(c); + f.setObject(object); + f.setFile(file); + f.setMapIterator(it); + +#if TRACE_DEBUG + qDebug("Created %s [TraceData::function]\n for %s, %s, %s", + f.fullName().ascii(), + c->fullName().ascii(), file->fullName().ascii(), + object ? object->fullName().ascii() : "(unknown object)"); +#endif + + c->addFunction(&f); + object->addFunction(&f); + file->addFunction(&f); + } + + return &(it.data()); +} + +TraceFunctionMap::Iterator TraceData::functionIterator(TraceFunction* f) +{ + + // IMPORTANT: build as SAME key as used in function() above !! + QString key; + + if (f->cls()) key = f->cls()->name() + "::"; + key += f->name(); + key += f->object()->shortName(); + + return _functionMap.find(key); +} + +TraceFunctionMap::ConstIterator TraceData::functionBeginIterator() const +{ + return _functionMap.begin(); +} + +TraceFunctionMap::ConstIterator TraceData::functionEndIterator() const +{ + return _functionMap.end(); +} + + +void TraceData::resetSourceDirs() +{ + TraceFileMap::Iterator fit; + for ( fit = _fileMap.begin(); + fit != _fileMap.end(); ++fit ) + (*fit).resetDirectory(); +} + +void TraceData::update() +{ + if (!_dirty) return; + + clear(); + _totals.clear(); + + TracePart* part; + for (part=_parts.first();part;part=_parts.next()) { + _totals.addCost(part->totals()); + if (part->isActive()) + addCost(part->totals()); + } + + _dirty = false; +} + +TraceCost* TraceData::search(TraceItem::CostType t, QString name, + TraceCostType* ct, TraceCost* parent) +{ + TraceCost* result = 0; + TraceItem::CostType pt = parent ? parent->type() : NoCostType; + SubCost sc, scTop = 0; + + switch(t) { + case Function: + { + TraceFunction *f; + TraceFunctionMap::Iterator it; + for ( it = _functionMap.begin(); + it != _functionMap.end(); ++it ) { + f = &(*it); + + if (f->name() != name) continue; + + if ((pt == Class) && (parent != f->cls())) continue; + if ((pt == File) && (parent != f->file())) continue; + if ((pt == Object) && (parent != f->object())) continue; + + if (ct) { + sc = f->inclusive()->subCost(ct); + if (sc <= scTop) continue; + scTop = sc; + } + + result = f; + } + } + break; + + case File: + { + TraceFile *f; + TraceFileMap::Iterator it; + for ( it = _fileMap.begin(); + it != _fileMap.end(); ++it ) { + f = &(*it); + if (f->name() != name) continue; + if (ct) { + sc = f->subCost(ct); + if (sc <= scTop) continue; + scTop = sc; + } + result = f; + } + } + break; + + case Class: + { + TraceClass *c; + TraceClassMap::Iterator it; + for ( it = _classMap.begin(); + it != _classMap.end(); ++it ) { + c = &(*it); + if (c->name() != name) continue; + if (ct) { + sc = c->subCost(ct); + if (sc <= scTop) continue; + scTop = sc; + } + result = c; + } + } + break; + + case Object: + { + TraceObject *o; + TraceObjectMap::Iterator it; + for ( it = _objectMap.begin(); + it != _objectMap.end(); ++it ) { + o = &(*it); + if (o->name() != name) continue; + if (ct) { + sc = o->subCost(ct); + if (sc <= scTop) continue; + scTop = sc; + } + result = o; + } + } + break; + + case Instr: + if (pt == Function) { + TraceInstrMap* instrMap = ((TraceFunction*)parent)->instrMap(); + if (!instrMap) break; + + TraceInstr *instr; + TraceInstrMap::Iterator it; + for ( it = instrMap->begin(); + it != instrMap->end(); ++it ) { + instr = &(*it); + if (instr->name() != name) continue; + result = instr; + } + } + break; + + case Line: + { + TraceFunctionSourceList sList; + if (pt == Function) + sList = ((TraceFunction*)parent)->sourceFiles(); + else if (pt == FunctionSource) + sList.append((TraceFunctionSource*) parent); + else break; + + TraceLineMap* lineMap; + TraceLine* line; + TraceLineMap::Iterator it; + TraceFunctionSource* fs; + for(fs = sList.first(); fs; fs = sList.next()) { + lineMap = fs->lineMap(); + if (!lineMap) continue; + + for ( it = lineMap->begin(); + it != lineMap->end(); ++it ) { + line = &(*it); + if (line->name() != name) continue; + result = line; + } + } + } + break; + + default: + break; + } + + return result; +} + + +TraceFunctionCycle* TraceData::functionCycle(TraceFunction* f) +{ + TraceFunctionCycle* cycle; + for (cycle=_functionCycles.first();cycle;cycle=_functionCycles.next()) + if (cycle->base() == f) return cycle; + + _functionCycleCount++; + cycle = new TraceFunctionCycle(f, _functionCycleCount); + + _functionCycles.append(cycle); + return cycle; +} + + +void TraceData::updateFunctionCycles() +{ + //qDebug("Updating cycles..."); + + // init cycle info + TraceFunctionCycle* cycle; + for (cycle=_functionCycles.first();cycle;cycle=_functionCycles.next()) + cycle->init(); + + TraceFunctionMap::Iterator it; + for ( it = _functionMap.begin(); it != _functionMap.end(); ++it ) + (*it).cycleReset(); + + if (!Configuration::showCycles()) return; + + _inFunctionCycleUpdate = true; + + +#if 0 + int fCount = _functionMap.size(), fNo = 0, progress=0, p; + QString msg = i18n("Recalculating Function Cycles..."); + if (_topLevel) _topLevel->showStatus(msg,0); +#endif + + // DFS and collapse strong connected components (Tarjan) + int pNo = 0; + TraceFunction* stackTop; + for ( it = _functionMap.begin(); it != _functionMap.end(); ++it ) { + +#if 0 + if (_topLevel) { + fNo++; + p = 100*fNo/fCount; + if (p> progress) { + progress = p; + _topLevel->showStatus(msg, p); + } + } +#endif + + stackTop = 0; + (*it).cycleDFS(1, pNo, &stackTop); + } + + // postprocess cycles + for (cycle=_functionCycles.first();cycle;cycle=_functionCycles.next()) + cycle->setup(); + + _inFunctionCycleUpdate = false; + // we have to invalidate costs because cycles are now taken into account + invalidateDynamicCost(); + +#if 0 + if (0) if (_topLevel) _topLevel->showStatus(QString::null,0); +#endif +} + +void TraceData::updateObjectCycles() +{ +} + + +void TraceData::updateClassCycles() +{ +} + + +void TraceData::updateFileCycles() +{ +} + + diff --git a/kcachegrind/kcachegrind/tracedata.h b/kcachegrind/kcachegrind/tracedata.h new file mode 100644 index 00000000..21922a05 --- /dev/null +++ b/kcachegrind/kcachegrind/tracedata.h @@ -0,0 +1,1967 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002, 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Classes holding profiling data for + * multiple tracefiles for one command. + * See class TraceData first. + */ + +#ifndef TRACEDATA_H +#define TRACEDATA_H + +#include +#include +#include +#include +#include +#include + +#include "subcost.h" +#include "utils.h" + +class QFile; + +/** + * All cost items are classes prefixed with "Trace". + * "TraceCost" holds basic cost metrics for the simplest, smallest + * trace entity: These are events counted for an instruction at + * a specific memory address of the traced program. + * All other cost items are derived from TraceCost, and add needed + * cost metrics, e.g. for a call the number of calls that happened. + * + * Abstract, i.e. never instantiated cost items are + * - TraceCost: Basic cost metrics (instr/read/write access + cache events) + * - TraceCallCost: Additional call count cost metric. + * - TraceInclusiveCost: Additional TraceCost aggregated. + * - TraceListCost: Adds dependency to a list of TraceCost's + * - TraceCallListCost: same for list of TraceCallCost's + * - TraceInclusiveListCost: same for list of TraceInclusiveCost's + * - TraceCostItem: Base for cost items for "interesting" costs: + * TraceFunction, TraceClass, TraceFile, TraceObject + * + * The smallest Cachegrind output is trace data indexed by a source + * line number, a TracePartLine. Another one is a call from one + * source line of a function to another function, a TracePartLineCall. + * All other cost items derive the value by summation of cost metrics + * from TraceLineItem and TracePartLineCall costs; their cost is + * calculated lazy on demand and cached afterwards. + * + * For cost items, which are sums over all trace files read in, the + * summed cost metrics change when e.g. a new trace file is read. + * Thus, their cached costs are invalidated, and again recalculated + * only on demand. In the following list, theses cost items are called + * "dynamic", the other "fixed" (but neverless calculated lazy). + * + * Cost Item Type Summation of ... + * + * TracePartLineCall fixed Read from trace file + * TracePartLine fixed Read from trace file + * TracePartCall fixed TracePartLineCall's + * TraceLineCall dynamic TracePartLineCall's + * TraceCall dynamic TraceLineCall's + * TraceLine dynamic TracePartLine's and TraceLineCall's + * TracePartFunction fixed TracePartLine's / TracePartCall's + * TraceFunction dynamic TraceLine's / TraceCall's (called from) + * TracePartClass fixed TracePartFunction's + * TraceClass dynamic TraceFunction's + * TracePartFile fixed TracePartFunction's + * TraceFile dynamic TraceFunction's + * TracePartObject fixed TracePartFunction's + * TraceObject dynamic TraceFunction's + * TracePart fixed TracePartLine's + * TraceData dynamic TracePart's + * + * As there exists only one TraceData object for a traced program, its the + * owner of some "high level" cost items. The following shows the owner + * relationship of the cost item classes, together with references. + * + * Cost Item Owner (& back ref) Other References to + * + * TracePartLineCall TraceLineCall + * TracePartCall TraceCall TracePartLineCall's + * TracePartLine TraceLine TracePartLineCall's + * TracePartFunction TraceFunction + * TracePartClass TraceClass TracePart + * TracePartFile TraceFile TracePart + * TracePartObject TraceObject TracePart + * TraceLineCall TraceCall TracePartLineCall's + * TraceCall TraceFunction TracePartCall's + * TraceLine TraceData TraceLineCall's + * TraceFunction TraceData TraceCall's (calling) + * TraceClass TraceData + * TraceFile TraceData + * TraceObject TraceData + * TracePart TraceData + * TraceData Main Application + * + * Convention: + * - The owner has a factory method for owned objects, + * and calls addXXX() to install references in other objects + * - The owner is first arg in a constructor. + */ + + +class FixString; +class FixCost; +class FixCallCost; +class FixJump; +class FixPool; +class DynPool; +class TopLevel; + +class TraceCost; +class TraceCostType; +class TraceCostMapping; +class TraceSubMapping; +class TraceJumpCost; +class TraceCallCost; +class TraceInclusiveCost; + +class TracePartInstr; +class TracePartInstrCall; +class TracePartLine; +class TracePartLineCall; +class TracePartCall; +class TracePartLineRegion; +class TracePartFunction; +class TracePartClass; +class TracePartObject; +class TracePartFile; + +class TraceInstr; +class TraceInstrJump; +class TraceInstrCall; +class TraceLine; +class TraceLineJump; +class TraceLineCall; +class TraceCall; +class TraceLineRegion; +class TraceFunctionSource; +class TraceFunction; +class TraceFunctionCycle; +class TraceClass; +class TraceObject; +class TraceFile; +class TracePart; +class TraceData; + +typedef QPtrList TraceCostList; +typedef QPtrList TraceJumpCostList; +typedef QPtrList TraceCallCostList; +typedef QPtrList TraceInclusiveCostList; + +typedef QPtrList TracePartCallList; +typedef QPtrList TracePartInstrList; +typedef QPtrList TracePartLineList; +typedef QPtrList TracePartLineRegionList; +typedef QPtrList TracePartFunctionList; +typedef QPtrList TracePartInstrCallList; +typedef QPtrList TracePartLineCallList; + + +typedef QPtrList TraceInstrList; +typedef QPtrList TraceLineList; +typedef QPtrList TraceInstrCallList; +typedef QPtrList TraceLineCallList; +typedef QPtrList TraceCallList; +typedef QPtrList TraceFileList; +typedef QPtrList TraceLineRegionList; +typedef QPtrList TraceFunctionSourceList; +typedef QPtrList TraceFunctionList; +typedef QPtrList TraceFunctionCycleList; +typedef QMap TraceObjectMap; +typedef QMap TraceClassMap; +typedef QMap TraceFileMap; +typedef QMap TraceFunctionMap; +typedef QMap TraceLineMap; + + +/** + * Addresses are 64bit values like costs to be able + * to always load profile data produced on 64bit + * architectures. + */ +class Addr +{ + public: + Addr() { _v=0; } + Addr(uint64 v) { _v = v; } + + // Interpretes char data at s as hex (without "0x" prefix) + // and return number of interpreted chars. + int set(const char *s); + bool set(FixString& s); + QString toString() const; + // similar to toString(), but adds a space every 4 digits + QString pretty() const; + + // returns true if this address is in [a-distance;a+distance] + bool isInRange(Addr a, int distance); + + bool operator==(const Addr& a) const { return (_v == a._v); } + bool operator!=(const Addr& a) const { return (_v != a._v); } + bool operator>(const Addr& a) const { return _v > a._v; } + bool operator>=(const Addr& a) const { return _v >= a._v; } + bool operator<(const Addr& a) const { return _v < a._v; } + bool operator<=(const Addr& a) const { return _v <= a._v; } + + Addr operator+(int d) const { return Addr(_v + d); } + Addr operator-(int d) const { return Addr(_v - d); } + + private: + uint64 _v; +}; + +typedef QMap TraceInstrMap; + + +/** + * Base class for cost items. + */ +class TraceItem +{ +public: + + // RTTI for trace item classes, using type() method + enum CostType { Item, Cost, + PartInstr, Instr, + PartLine, Line, + PartInstrJump, InstrJump, + PartLineJump, LineJump, + PartInstrCall, InstrCall, + PartLineCall, LineCall, + PartCall, Call, + PartLineRegion, LineRegion, + PartFunction, FunctionSource, Function, FunctionCycle, + PartClass, Class, ClassCycle, + PartFile, File, FileCycle, + PartObject, Object, ObjectCycle, + Part, Data, + MaxCostType, NoCostType }; + + TraceItem(); + virtual ~TraceItem(); + + virtual CostType type() const { return Item; } + + // conversion of item type to locale independent string (e.g. for config) + static QString typeName(CostType); + static CostType costType(QString); + // the versions below should be used for user visible strings, as + // these use localisation settings + static QString i18nTypeName(CostType); + static CostType i18nCostType(QString); + // clean up some static data + static void cleanup(); + + /** + * Returns dynamic name info (without type) + */ + virtual QString name() const; + + /** + * Same as name, but sometimes nicer for humans :-) + */ + virtual QString prettyName() const; + + /** + * Returns text of all cost metrics + */ + virtual QString costString(TraceCostMapping*); + + /** + * Returns type name + dynamic name + */ + QString fullName() const; + + /** + * Returns full name + cost text + */ + QString toString(); + + /** + * Set all cost counters to zero + */ + virtual void clear(); + + /** Invalidate the cost attributes. + * An invalidated object needs to be recalculated when a cost + * attribute is requested (e.g. by subCost()). + * Has to be overwritten by subclasses when the cost influences costs of + * other cost items. If only one item depends on the cost of this item, + * it can by set with setDependant() without a need for overwriting. + */ + virtual void invalidate(); + + /** + * Sets a dependant to be invalidated when this cost is invalidated. + * Call this function directly after the constructor. + */ + void setDependant(TraceItem* d) { _dep = d; } + + TraceItem* dependant() { return _dep; } + + /** + * If this item is from a single profile data file, position + * points to a TracePart, otherwise to a TraceData object. + */ + void setPosition(TraceItem* p) { _position = p; } + + // getters for specific positions, to be overwritten + virtual TracePart* part(); + virtual const TracePart* part() const; + virtual TraceData* data(); + virtual const TraceData* data() const; + + protected: + /** Updates cost attributes. + * This has to be called by subclasses that access cost attributes + * directly + */ + virtual void update(); + + bool _dirty; + + TraceItem* _position; + TraceItem* _dep; + + private: + static QString *_typeName, *_i18nTypeName; +}; + + + +/** + * An array of basic cost metrics for a trace item. + * + * The semantic of specific indexes is stored in the + * TraceCostMapping of the TraceData object holding this TraceCost. + */ +class TraceCost: public TraceItem +{ +public: + /** + * The maximal number of subcosts a TraceCost can have. + */ + static const int MaxRealIndex; +#define MaxRealIndexValue 13 + static const int InvalidIndex; + + + TraceCost(); + virtual ~TraceCost(); + + virtual CostType type() const { return Cost; } + virtual QString costString(TraceCostMapping*); + + virtual void clear(); + + // set the cost according to a submapping and a list of ASCII numbers + void set(TraceSubMapping*, const char*); + void set(TraceSubMapping*, FixString&); + // add a cost according to a submapping and a list of ASCII numbers + void addCost(TraceSubMapping*, const char*); + void addCost(TraceSubMapping*, FixString&); + // add the cost of another item + void addCost(TraceCost* item); + void addCost(int index, SubCost value); + + // maximal cost + void maxCost(TraceSubMapping*, FixString&); + void maxCost(TraceCost* item); + void maxCost(int index, SubCost value); + TraceCost diff(TraceCost* item); + + virtual void invalidate(); + + /** Returns a sub cost. This automatically triggers + * a call to update() if needed. + */ + SubCost subCost(TraceCostType*); + + /** + * Same as above, but only for real types + */ + SubCost subCost(int); + + /** Returns a cost attribute converted to a string + * (with space after every 3 digits) + */ + QString prettySubCost(TraceCostType*); + + protected: + virtual void update(); + + SubCost _cost[MaxRealIndexValue]; + int _count; // only _count first indexes of _cost are used + + // cache last virtual subcost for faster access + SubCost _cachedCost; + TraceCostType* _cachedType; +}; + + + +/** + * A cost type, e.g. "L1 Read Miss", short "l1rm". + * + * We distinguish "real" cost types, where the values come + * from the trace file, and "virtual" cost types, which + * are calculated from the real ones. + * + * For a virtual cost type, set a formula to calculate it: + * e.g. for "Read Misses" : "l1rm + l2rm". + * To allow for parsing, you must specify a TraceCostMapping + * with according cost types (e.g. "l1rm" and "l2rm" for above formula). + * + * The cost type with empty name is the "const" cost type. + */ +class TraceCostType +{ +public: + + /** + * is a short (non-localized) identifier for the cost type, + * e.g. "l1rm". + * is a long localized string, e.g. "L1 Read Miss" + * uses short names to reference other types + */ + TraceCostType(QString name, + QString longName = QString::null, + QString formula = QString::null); + + void setName(QString n) { _name = n; } + void setLongName(QString n) { _longName = n; } + void setMapping(TraceCostMapping* m); + void setFormula(QString); + // default arg is for specifying a real type, but index unknown + void setRealIndex(int r = TraceCost::MaxRealIndex); + + const QString& name() { return _name; } + const QString& longName() { return _longName; } + const QString& formula() { return _formula; } + TraceCostMapping* mapping() { return _mapping; } + int realIndex() { return _realIndex; } + bool isReal() { return _formula.isEmpty(); } + QColor color(); + + /* + * returns true if all cost type names can be resolved in formula + */ + bool parseFormula(); + QString parsedFormula(); + + SubCost subCost(TraceCost*); + + /* + * For virtual costs, returns a histogram for use with + * partitionPixmap(). + * Returns maximal real index. + */ + int histCost(TraceCost* c, double total, double* hist); + + // application wide known types, referenced by short name + // next 2 functions return a new type object instance + static TraceCostType* knownRealType(QString); + static TraceCostType* knownVirtualType(QString); + static void add(TraceCostType*); + static bool remove(QString); + static int knownTypeCount(); + static TraceCostType* knownType(int); + +private: + + QString _name, _longName, _formula; + TraceCostMapping* _mapping; + bool _parsed, _inParsing; + // index MaxRealIndex is for constant addition + int _coefficient[MaxRealIndexValue]; + int _realIndex; + + static QPtrList* _knownTypes; +}; + + +/** + * A class for managing a set of cost types. + * + * Each cost type has an index: + * - Real costs are in range [0 .. TraceCost:MaxRealIndex[ + * - Virtual costs are in range [MaxRealIndex, ...] + */ +class TraceCostMapping +{ +public: + TraceCostMapping(); + ~TraceCostMapping(); + + /** + * Defines a sub mapping with a list of real types + * If is false, checks if this is a existing sub mapping. + */ + TraceSubMapping* subMapping(QString types, bool create = true); + + // "knows" about some real types + int addReal(QString); + int add(TraceCostType*); + bool remove(TraceCostType*); + int realCount() { return _realCount; } + int virtualCount() { return _virtualCount; } + int minVirtualIndex() { return TraceCost::MaxRealIndex; } + TraceCostType* type(int); + TraceCostType* realType(int); + TraceCostType* virtualType(int); + TraceCostType* type(QString); + TraceCostType* typeForLong(QString); + int realIndex(QString); + int index(QString); + QColor* realColors() { return _realColor; } + + /** + * Adds all known virtual types that can be parsed + */ + int addKnownVirtualTypes(); + +private: + // we support only a fixed number of real and virtual types + TraceCostType* _real[MaxRealIndexValue]; + QColor _realColor[MaxRealIndexValue]; + TraceCostType* _virtual[MaxRealIndexValue]; + int _realCount, _virtualCount; +}; + +/** + * A submapping of a TraceCostMapping + * + * This is a fixed ordered list of indexes for real cost types + * in a mapping. + * + * You can define a mapping by requesting submappings. Undefined cost + * types will get a new real type index. + * TraceCostMapping m; + * sm1 = m.subMapping("Event1 Cost1 Cost2"); // returns submap [0,1,2] + * sm2 = m.subMapping("Event2 Cost3 Event1"); // returns submap [3,4,0] + * Real types of m will be: + * (0:Event1, 1:Cost1, 2:Cost2, 3:Event2, 4:Cost3) + */ +class TraceSubMapping +{ +public: + TraceSubMapping(TraceCostMapping*); + + bool append(QString, bool create=true); + bool append(int); + void clear(); + + /** + * Get number of used indexes + */ + int count() { return _count; } + + /** + * Is this submapping the identity( i.e. realIndex(i)=i ) ? + * This often allows for optimizations. + */ + bool isIdentity() { return _isIdentity; } + int realIndex(int i) + { return (i<0 || i>=_count) ? TraceCost::InvalidIndex : _realIndex[i]; } + + /* + * Allows an iteration over the sorted list of all real indexes not used in + * this submapping, up to topIndex (use TraceCost::MaxRealIndex for all). + * Usage: for(i = firstUnused(); i < topIndex; i = nextUnused(i)) { LOOP } + */ + int firstUnused() { return _firstUnused; } + int nextUnused(int i) { + if (i<0 || i>=TraceCost::MaxRealIndex) return TraceCost::InvalidIndex; + return _nextUnused[i]; } + +private: + TraceCostMapping* _mapping; + int _count, _firstUnused; + bool _isIdentity; + int _realIndex[MaxRealIndexValue]; + int _nextUnused[MaxRealIndexValue]; +}; + + +/** + * Cost of a (conditional) jump. + */ +class TraceJumpCost: public TraceItem +{ + public: + TraceJumpCost(); + virtual ~TraceJumpCost(); + + // reimplementations for cost addition + virtual QString costString(TraceCostMapping* m); + virtual void clear(); + + void addCost(TraceJumpCost*); + + // additional cost metrics + SubCost followedCount(); + SubCost executedCount(); + void addFollowedCount(SubCost c) { _followedCount += c; } + void addExecutedCount(SubCost c) { _executedCount += c; } + + protected: + SubCost _executedCount, _followedCount; +}; + + + +/** + * Cost item with additional call count metric. + */ +class TraceCallCost: public TraceCost +{ + public: + TraceCallCost(); + virtual ~TraceCallCost(); + + // reimplementations for cost addition + virtual QString costString(TraceCostMapping* m); + virtual void clear(); + + // additional cost metric + SubCost callCount(); + QString prettyCallCount(); + void addCallCount(SubCost c); + + protected: + SubCost _callCount; +}; + + +/** + * Cost item with additional inclusive metric + */ +class TraceInclusiveCost: public TraceCost +{ + public: + TraceInclusiveCost(); + virtual ~TraceInclusiveCost(); + + // reimplementations for cost addition + virtual QString costString(TraceCostMapping* m); + virtual void clear(); + + // additional cost metric + TraceCost* inclusive(); + void addInclusive(TraceCost*); + + protected: + TraceCost _inclusive; +}; + + +/** + * Cost Item + * dependend on a list of cost items. + */ +class TraceListCost: public TraceCost +{ + public: + TraceListCost(); + virtual ~TraceListCost(); + + // reimplementation for dependency list + virtual void update(); + + TraceCostList& deps() { return _deps; } + void addDep(TraceCost*); + TraceCost* findDepFromPart(TracePart*); + + protected: + // overwrite in subclass to change update behaviour + virtual bool onlyActiveParts() { return false; } + + TraceCostList _deps; + + private: + // very temporary: cached + TraceCost* _lastDep; +}; + + +/** + * Jump Cost Item + * dependend on a list of Jump cost items. + */ +class TraceJumpListCost: public TraceJumpCost +{ + public: + TraceJumpListCost(); + virtual ~TraceJumpListCost(); + + // reimplementation for dependency list + virtual void update(); + + TraceJumpCostList deps() { return _deps; } + void addDep(TraceJumpCost*); + TraceJumpCost* findDepFromPart(TracePart*); + + protected: + // overwrite in subclass to change update behaviour + virtual bool onlyActiveParts() { return false; } + + TraceJumpCostList _deps; + + private: + // very temporary: cached + TraceJumpCost* _lastDep; +}; + + + + +/** + * Call Cost Item + * dependend on a list of Call cost items. + */ +class TraceCallListCost: public TraceCallCost +{ + public: + TraceCallListCost(); + virtual ~TraceCallListCost(); + + // reimplementation for dependency list + virtual void update(); + + TraceCallCostList deps() { return _deps; } + void addDep(TraceCallCost*); + TraceCallCost* findDepFromPart(TracePart*); + + protected: + // overwrite in subclass to change update behaviour + virtual bool onlyActiveParts() { return false; } + + TraceCallCostList _deps; + + private: + // very temporary: cached + TraceCallCost* _lastDep; +}; + + +/** + * Inclusive Cost Item dependend on a list of inclusive cost items. + */ +class TraceInclusiveListCost: public TraceInclusiveCost +{ + public: + TraceInclusiveListCost(); + virtual ~TraceInclusiveListCost(); + + // reimplementation for dependency + virtual void update(); + + TraceInclusiveCostList deps() { return _deps; } + void addDep(TraceInclusiveCost*); + TraceInclusiveCost* findDepFromPart(TracePart*); + + protected: + // overwrite in subclass to change update behaviour + virtual bool onlyActiveParts() { return false; } + + TraceInclusiveCostList _deps; + + private: + // very temporary: cached + TraceInclusiveCost* _lastDep; +}; + + + + + +/*----------------------------------------------------------------- + * Classes for cost items of one trace file, i.e. a "trace part" + *----------------------------------------------------------------- + */ + +/** + * Cost of jump at a instruction code address from a trace file. + */ +class TracePartInstrJump: public TraceJumpCost +{ + public: + TracePartInstrJump(TraceInstrJump*, TracePartInstrJump*); + virtual ~TracePartInstrJump(); + + virtual CostType type() const { return PartInstrJump; } + // fix cost item + virtual void update() {} + TraceInstrJump* instrJump() const { return (TraceInstrJump*) _dep; } + TracePartInstrJump* next() const { return _next; } + + private: + // chaining all parts for InstrJump + TracePartInstrJump* _next; +}; + + +/** + * Cost of a call at a instruction code address from a trace file. + * Cost is always up to date, no lazy update needed. + */ +class TracePartInstrCall: public TraceCallCost +{ +public: + TracePartInstrCall(TraceInstrCall*); + virtual ~TracePartInstrCall(); + + virtual CostType type() const { return PartInstrCall; } + // fix cost item + virtual void update() {} + TraceInstrCall* instrCall() const { return (TraceInstrCall*) _dep; } +}; + + +/** + * Cost of a code instruction address from a trace file. + * Cost is always up to date, no lazy update needed. + */ +class TracePartInstr: public TraceCost +{ +public: + TracePartInstr(TraceInstr*); + virtual ~TracePartInstr(); + + virtual CostType type() const { return PartInstr; } + // fix cost item + virtual void update() {} + + TraceInstr* instr() const { return (TraceInstr*)_dep; } +}; + + +/** + * Cost of jump at a source line from a trace file. + */ +class TracePartLineJump: public TraceJumpCost +{ + public: + TracePartLineJump(TraceLineJump*); + virtual ~TracePartLineJump(); + + virtual CostType type() const { return PartLineJump; } + // fix cost item + virtual void update() {} + TraceLineJump* lineJump() const { return (TraceLineJump*) _dep; } +}; + + +/** + * Cost of a call at a line from a trace file. + * Cost is always up to date, no lazy update needed. + */ +class TracePartLineCall: public TraceCallCost +{ +public: + TracePartLineCall(TraceLineCall*); + virtual ~TracePartLineCall(); + + virtual CostType type() const { return PartLineCall; } + // fix cost item + virtual void update() {} + TraceLineCall* lineCall() const { return (TraceLineCall*) _dep; } +}; + + + +/** + * Cost of a line from a trace file. + * Cost is always up to date, no lazy update needed. + */ +class TracePartLine: public TraceCost +{ +public: + TracePartLine(TraceLine*); + virtual ~TracePartLine(); + + virtual CostType type() const { return PartLine; } + // fix cost item + virtual void update() {} + + TraceLine* line() const { return (TraceLine*)_dep; } +}; + + +/** + * Cost of a source region. + */ +class TracePartLineRegion: public TraceInclusiveCost +{ +public: + TracePartLineRegion(TraceLineRegion*); + virtual ~TracePartLineRegion(); + + virtual CostType type() const { return PartLineRegion; } + virtual void update(); + + TraceLineRegion* region() const { return (TraceLineRegion*)_dep; } +}; + + +/** + * Cost of a call at a function to another function, + * from a single trace file. + */ +class TracePartCall: public TraceCallListCost +{ +public: + TracePartCall(TraceCall* call); + virtual ~TracePartCall(); + + virtual CostType type() const { return PartCall; } + // calls a function itself? + bool isRecursion(); + + // reimplementation for dependency list + virtual void update(); + + TraceCall* call() const { return (TraceCall*)_dep; } + + FixCallCost* setFirstFixCallCost(FixCallCost* fc) + { FixCallCost* t = _firstFixCallCost; _firstFixCallCost = fc; return t; } + FixCallCost* firstFixCallCost() const { return _firstFixCallCost; } + +private: + FixCallCost* _firstFixCallCost; +}; + + +/** + * Cost of a function, + * from a single trace file. + */ +class TracePartFunction: public TraceInclusiveCost +{ +public: + TracePartFunction(TraceFunction*, + TracePartObject*, TracePartFile*); + virtual ~TracePartFunction(); + + virtual CostType type() const { return PartFunction; } + virtual void update(); + virtual QString costString(TraceCostMapping* m); + + void addPartInstr(TracePartInstr*); + void addPartLine(TracePartLine*); + void addPartCaller(TracePartCall*); + void addPartCalling(TracePartCall*); + + TraceFunction* function() { return (TraceFunction*) _dep; } + TracePartObject* partObject() { return _partObject; } + TracePartClass* partClass() { return _partClass; } + TracePartFile* partFile() { return _partFile; } + const TracePartCallList& partCallers() { return _partCallers; } + const TracePartCallList& partCallings() { return _partCallings; } + void setPartObject(TracePartObject* o) { _partObject = o; } + void setPartClass(TracePartClass* c) { _partClass = c; } + void setPartFile(TracePartFile* f) { _partFile = f; } + + /* for linked list of FixXXX objects */ + FixCost* setFirstFixCost(FixCost* fc) + { FixCost* t = _firstFixCost; _firstFixCost = fc; return t; } + FixCost* firstFixCost() const { return _firstFixCost; } + FixJump* setFirstFixJump(FixJump* fj) + { FixJump* t = _firstFixJump; _firstFixJump = fj; return t; } + FixJump* firstFixJump() const { return _firstFixJump; } + + // additional cost metrics + SubCost calledCount(); + SubCost callingCount(); + QString prettyCalledCount(); + QString prettyCallingCount(); + int calledContexts(); + int callingContexts(); + +private: + TracePartObject* _partObject; + TracePartClass* _partClass; + TracePartFile* _partFile; + + TracePartCallList _partCallings; + TracePartCallList _partCallers; + TracePartInstrList _partInstr; + TracePartLineList _partLines; + + // cached + SubCost _calledCount, _callingCount; + int _calledContexts, _callingContexts; + + FixCost* _firstFixCost; + FixJump* _firstFixJump; +}; + + +/** + * Cost of a class, + * from a single trace file. + */ +class TracePartClass: public TraceInclusiveListCost +{ +public: + TracePartClass(TraceClass*); + virtual ~TracePartClass(); + + virtual CostType type() const { return PartClass; } + QString prettyName() const; + + TraceClass* cls() { return (TraceClass*)_dep; } + void addPartFunction(TracePartFunction* f) { addDep(f); } +}; + + +/** + * Cost of a source file, + * from a single trace file. + */ +class TracePartFile: public TraceInclusiveListCost +{ +public: + TracePartFile(TraceFile*); + virtual ~TracePartFile(); + + virtual CostType type() const { return PartFile; } + TraceFile* file() { return (TraceFile*)_dep; } + void addPartFunction(TracePartFunction* f) { addDep(f); } +}; + + +/** + * Cost of a object, + * from a single trace file. + */ +class TracePartObject: public TraceInclusiveListCost +{ +public: + TracePartObject(TraceObject*); + virtual ~TracePartObject(); + + virtual CostType type() const { return PartObject; } + TraceObject* object() const { return (TraceObject*)_dep; } + void addPartFunction(TracePartFunction* f) { addDep(f); } +}; + + + +/** + * A Trace Part: All data read from a trace file, containing all costs + * that happened in a specified time interval of the executed command. + */ +class TracePart: public TraceListCost +{ +public: + TracePart(TraceData*, QFile* file); + virtual ~TracePart(); + + virtual CostType type() const { return Part; } + virtual TracePart* part() { return this; } + virtual const TracePart* part() const { return this; } + + QString shortName() const; + QString prettyName() const; + QFile* file() const { return _file; } + QString name() const { return _name; } + QString description() const { return _descr; } + QString trigger() const { return _trigger; } + QString timeframe() const { return _timeframe; } + QString version() const { return _version; } + int partNumber() { return _number; } + int threadID() { return _tid; } + int processID() { return _pid; } + void setDescription(const QString& d) { _descr = d; } + void setTrigger(const QString& t) { _trigger = t; } + void setTimeframe(const QString& t) { _timeframe = t; } + void setVersion(const QString& v) { _version = v; } + void setPartNumber(int n); + void setThreadID(int t); + void setProcessID(int p); + TraceCost* totals() { return &_totals; } + /* we get owner of the submapping */ + void setFixSubMapping(TraceSubMapping* sm) { _fixSubMapping = sm; } + TraceSubMapping* fixSubMapping() { return _fixSubMapping; } + + // returns true if something changed + bool activate(bool); + bool isActive() { return _active; } + +private: + QFile* _file; + QString _name; + QString _descr; + QString _trigger; + QString _timeframe; + QString _version; + + int _number, _tid, _pid; + + bool _active; + + // the totals line + TraceCost _totals; + + // submapping for all fix costs of this part + TraceSubMapping* _fixSubMapping; +}; + + +class TracePartList: public QPtrList +{ + public: + QString names() const; + protected: + int compareItems ( Item item1, Item item2 ); +}; + + +/*----------------------------------------------------------------- + * Classes for cost items summed up from multiple trace parts + *----------------------------------------------------------------- + */ + + +/** + * A jump from an instruction to another inside of a function + */ +class TraceInstrJump: public TraceJumpCost +{ +public: + TraceInstrJump(TraceInstr* instrFrom, TraceInstr* instrTo, + bool isCondJump); + virtual ~TraceInstrJump(); + + virtual CostType type() const { return InstrJump; } + virtual QString name() const; + + virtual void update(); + + TraceInstr* instrFrom() const { return _instrFrom; } + TraceInstr* instrTo() const { return _instrTo; } + bool isCondJump() const { return _isCondJump; } + + // part factory + TracePartInstrJump* partInstrJump(TracePart*); + + private: + TraceInstr *_instrFrom, *_instrTo; + bool _isCondJump; + // list of parts for this InstrJump + TracePartInstrJump* _first; +}; + +class TraceInstrJumpList: public QPtrList +{ + public: + TraceInstrJumpList() { _sortLow = true; } + void setSortLow(bool s) { _sortLow = s; } + + protected: + int compareItems ( Item item1, Item item2 ); + + private: + bool _sortLow; +}; + + +/** + * A jump from one line to another inside of a function. + */ +class TraceLineJump: public TraceJumpListCost +{ + public: + TraceLineJump(TraceLine* lineFrom, TraceLine* lineTo, + bool isCondJump); + virtual ~TraceLineJump(); + + virtual CostType type() const { return LineJump; } + virtual QString name() const; + + TraceLine* lineFrom() const { return _lineFrom; } + TraceLine* lineTo() const { return _lineTo; } + bool isCondJump() { return _isCondJump; } + + // part factory + TracePartLineJump* partLineJump(TracePart*); + + protected: + bool onlyActiveParts() { return true; } + + private: + TraceLine *_lineFrom, *_lineTo; + bool _isCondJump; +}; + + +class TraceLineJumpList: public QPtrList +{ + public: + TraceLineJumpList() { _sortLow = true; } + void setSortLow(bool s) { _sortLow = s; } + + protected: + int compareItems ( Item item1, Item item2 ); + + private: + bool _sortLow; +}; + + +/** + * A call from an instruction of one function to another function + */ +class TraceInstrCall: public TraceCallListCost +{ + public: + TraceInstrCall(TraceCall* call, TraceInstr* instr); + virtual ~TraceInstrCall(); + + virtual CostType type() const { return InstrCall; } + virtual QString name() const; + + TraceInstr* instr() const { return _instr; } + TraceCall* call() const { return _call; } + + // part factory + TracePartInstrCall* partInstrCall(TracePart*, TracePartCall*); + + protected: + bool onlyActiveParts() { return true; } + + private: + TraceInstr* _instr; + TraceCall* _call; +}; + + +/** + * A call from a line of one function to another function. + */ +class TraceLineCall: public TraceCallListCost +{ + public: + TraceLineCall(TraceCall* call, TraceLine* line); + virtual ~TraceLineCall(); + + virtual CostType type() const { return LineCall; } + virtual QString name() const; + + TraceLine* line() const { return _line; } + TraceCall* call() const { return _call; } + + // part factory + TracePartLineCall* partLineCall(TracePart*, TracePartCall*); + + protected: + bool onlyActiveParts() { return true; } + + private: + TraceLine* _line; + TraceCall* _call; +}; + + +/** + * A call from one to another function. + * Consists of a list a TraceLineCalls + */ +class TraceCall: public TraceCallListCost +{ + public: + TraceCall(TraceFunction* caller, TraceFunction* called); + virtual ~TraceCall(); + + virtual CostType type() const { return Call; } + virtual QString name() const; + + // calls a function itself? + bool isRecursion() { return _caller == _called; } + + // return cycle number >0 if call is inside of a cycle + int inCycle(); + // we need some special handling for cycle calls + void update(); + + void invalidateDynamicCost(); + + // factories + TracePartCall* partCall(TracePart*, + TracePartFunction*, TracePartFunction*); + TraceLineCall* lineCall(TraceLine*); + TraceInstrCall* instrCall(TraceInstr*); + + TraceFunction* caller(bool skipCycle=false) const; + TraceFunction* called(bool skipCycle=false) const; + QString callerName(bool skipCycle=false) const; + QString calledName(bool skipCycle=false) const; + const TraceLineCallList& lineCalls() const { return _lineCalls; } + const TraceInstrCallList& instrCalls() const { return _instrCalls; } + + FixCallCost* setFirstFixCost(FixCallCost* fc) + { FixCallCost* t = _firstFixCost; _firstFixCost = fc; return t; } + + protected: + bool onlyActiveParts() { return true; } + + private: + TraceInstrCallList _instrCalls; + TraceLineCallList _lineCalls; + TraceFunction* _caller; + TraceFunction* _called; + + FixCallCost* _firstFixCost; +}; + + +/** + * A code instruction address of the program. + * Consists of a list a TracePartInstr from different trace files + * and a list of TraceInstrCalls if there are calls from this address. + */ +class TraceInstr: public TraceListCost +{ + public: + TraceInstr(); + virtual ~TraceInstr(); + + virtual CostType type() const { return Instr; } + virtual QString name() const; + QString prettyName() const; + + bool isValid() { return _addr != Addr(0); } + + // factories + TracePartInstr* partInstr(TracePart* part, + TracePartFunction* partFunction); + TraceInstrJump* instrJump(TraceInstr* to, bool isCondJump); + + void addInstrCall(TraceInstrCall*); + + Addr addr() const { return _addr; } + TraceFunction* function() const { return _function; } + TraceLine* line() const { return _line; } + const TraceInstrJumpList& instrJumps() const { return _instrJumps; } + const TraceInstrCallList& instrCalls() const { return _instrCalls; } + bool hasCost(TraceCostType*); + + // only to be called after default constructor + void setAddr(const Addr addr) { _addr = addr; } + void setFunction(TraceFunction* f) { _function = f; } + void setLine(TraceLine* l) { _line = l; } + + protected: + bool onlyActiveParts() { return true; } + + private: + Addr _addr; + TraceFunction* _function; + TraceLine* _line; + + TraceInstrJumpList _instrJumps; + TraceInstrCallList _instrCalls; +}; + + +/** + * A source line of the program. + * Consists of a list a TracePartLines from different trace files + * and a list of TraceLineCalls if there are calls from this line. + */ +class TraceLine: public TraceListCost +{ +public: + TraceLine(); + virtual ~TraceLine(); + + virtual CostType type() const { return Line; } + virtual QString name() const; + QString prettyName() const; + + // factories + TracePartLine* partLine(TracePart* part, + TracePartFunction* partFunction); + TraceLineJump* lineJump(TraceLine* to, bool isCondJump); + + void addLineCall(TraceLineCall*); + + + bool isValid() { return _sourceFile != 0; } + bool hasCost(TraceCostType*); + TraceFunctionSource* functionSource() const { return _sourceFile; } + uint lineno() const { return _lineno; } + const TraceLineCallList& lineCalls() const { return _lineCalls; } + const TraceLineJumpList& lineJumps() const { return _lineJumps; } + + // only to be called after default constructor + void setSourceFile(TraceFunctionSource* sf) { _sourceFile = sf; } + void setLineno(uint lineno) { _lineno = lineno; } + + protected: + bool onlyActiveParts() { return true; } + + private: + TraceFunctionSource* _sourceFile; + uint _lineno; + + TraceLineJumpList _lineJumps; + TraceLineCallList _lineCalls; +}; + + +/* + * Base class for all costs which + * represent "interesting" items or group of items + * with settable name and inclusive cost + */ +class TraceCostItem: public TraceInclusiveListCost +{ + public: + TraceCostItem(); + virtual ~TraceCostItem(); + + virtual QString name() const { return _name; } + virtual void setName(const QString& name) { _name = name; } + + protected: + bool onlyActiveParts() { return true; } + + protected: + QString _name; +}; + + +/** + * Cost of a source region. + */ +class TraceLineRegion: public TraceInclusiveListCost +{ +public: + TraceLineRegion(uint from, uint to, QString name); + virtual ~TraceLineRegion(); + + virtual CostType type() const { return LineRegion; } + virtual void update(); + + uint from() const { return _from; } + uint to() const { return _to; } + QString name() const { return _name; } + + // factories + TracePartLine* partLineRegion(TracePart* part, + TracePartFunction* partFunction); + private: + uint _from, _to; + QString _name; +}; + + +/** + * A container helper class for TraceFunction for source lines + * where a function is implemented in. + * With inlining, lines of the same function can come from + * different source files. + * An instance of this class holds all lines of one source file + * for a function in a map + */ +class TraceFunctionSource: public TraceCost +{ +public: + TraceFunctionSource(TraceFunction*, TraceFile*); + virtual ~TraceFunctionSource(); + + virtual CostType type() const { return FunctionSource; } + virtual QString name() const; + + // reimplementation for dependency map + virtual void update(); + + TraceFile* file() const { return _file; } + TraceFunction* function() const { return _function; } + uint firstLineno(); + uint lastLineno(); + TraceLineMap* lineMap(); + + void invalidateDynamicCost(); + + /* factories */ + TraceLine* line(uint lineno, bool createNew = true); + TraceLineRegion* region(uint from, uint to, QString name, + bool createNew = true); + + private: + TraceFile* _file; + TraceFunction* _function; + TraceLineMap* _lineMap; + TraceLine* _line0; + TraceLineRegionList* _regions; + + bool _lineMapFilled; +}; + + +/** + * For temporary assoziation of objects with TraceFunctions. + * Used in coverage analysis and TreeMap drawing. + */ +class TraceAssoziation +{ + public: + /** + * Creates an invalid assoziation. + */ + TraceAssoziation(); + virtual ~TraceAssoziation(); + + // for runtime detection + virtual int rtti() { return 0; } + + /** + * Could we set the function assoziation to ourself? + * This only can return false if this is a unique assoziation. + */ + bool isAssoziated(); + + /** + * reset function to assoziate this object to. + * returns true if assoziation could be established + */ + bool setFunction(TraceFunction*); + TraceFunction* function() { return _function; } + + void invalidate() { _valid = false; } + bool isValid() { return _valid; } + + /** + * Delete all assoziations in TraceFunctions of data with + * rtti runtime info. rtti = 0: delete ALL assoziations. + */ + static void clear(TraceData* data, int rtti); + + /** + * Invalidate all assoziations in TraceFunctions of data with + * rtti runtime info. rtti = 0: Invalidate ALL assoziations. + */ + static void invalidate(TraceData* data, int rtti); + + protected: + TraceFunction* _function; + bool _valid; +}; + +typedef QPtrList TraceAssoziationList; +typedef QMap TraceCallMap; + +/** + * A traced function + * + * References to functions are stored in + * (1) a function map in TraceData (by value) + * (2) a TraceClass + */ +class TraceFunction: public TraceCostItem +{ + public: + TraceFunction(); + TraceFunction(TraceData* data, const QString& name, + TraceClass* cls, TraceFile* file, TraceObject* object); + virtual ~TraceFunction(); + + virtual CostType type() const { return Function; } + virtual void update(); + + // this invalidate all subcosts of function depending on + // active status of parts + void invalidateDynamicCost(); + + void addCaller(TraceCall*); + + // factories + TraceCall* calling(TraceFunction* called); + TraceLine* line(TraceFile*, uint lineno, bool createNew = true); + TraceInstr* instr(Addr addr, bool createNew = true); + TracePartFunction* partFunction(TracePart*, + TracePartFile*, TracePartObject*); + + /** + * Returns empty string if location is fully unknown. + * Use prettyLocation for single user-visible string. + * A function can have a lot of code from different sources (inlined); + * maxItems limits this list. Default is full list + */ + QString location(int maxFiles = 0) const; + + QString prettyName() const; + QString prettyLocation(int maxFiles = 0) const; + QString prettyNameWithLocation(int maxFiles = 1) const; + void addPrettyLocation(QString&, int maxFiles = 1) const; + // type + name + location + QString info() const; + + TraceClass* cls() const { return _cls; } + TraceFile* file() const { return _file; } + TraceObject* object() const { return _object; } + // get the source file with lines from function declaration (not inlined) + TraceFunctionSource* sourceFile(TraceFile* file = 0, + bool createNew = false); + const TraceFunctionSourceList& sourceFiles() const + { return _sourceFiles; } + TraceCallList callers(bool skipCycle=false) const; + const TraceCallList& callings(bool skipCycle=false) const; + + Addr firstAddress() const; + Addr lastAddress() const; + TraceInstrMap* instrMap(); + + // cost metrics + SubCost calledCount(); + SubCost callingCount(); + QString prettyCalledCount(); + QString prettyCallingCount(); + int calledContexts(); + int callingContexts(); + + // only to be called after default constructor + void setFile(TraceFile* file) { _file = file; } + void setObject(TraceObject* object) { _object = object; } + void setClass(TraceClass* cls) { _cls = cls; } + void setMapIterator(TraceFunctionMap::Iterator it) { _myMapIterator = it; } + + // see TraceFunctionAssoziation + void addAssoziation(TraceAssoziation* a); + void removeAssoziation(TraceAssoziation* a); + void removeAssoziation(int rtti, bool reallyDelete = true); + void invalidateAssoziation(int rtti); + TraceAssoziation* assoziation(int rtti); + + // cycles + void setCycle(TraceFunctionCycle* c) { _cycle = c; } + TraceFunctionCycle* cycle() { return _cycle; } + bool isCycle(); + bool isCycleMember(); + void cycleReset(); + void cycleDFS(int d, int& pNo, TraceFunction** pTop); + + protected: + TraceCallList _callers; // list of calls we are called from + TraceCallList _callings; // list of calls we are calling (we are owner) + TraceCallMap _callingMap; // contains the same as _callings in a map + TraceFunctionCycle* _cycle; + + private: + bool isUniquePrefix(QString) const; + TraceFunctionMap::Iterator _myMapIterator; + + TraceClass* _cls; + TraceObject* _object; + TraceFile* _file; + + TraceFunctionSourceList _sourceFiles; // we are owner + TraceInstrMap* _instrMap; // we are owner + bool _instrMapFilled; + + // see TraceAssoziation + TraceAssoziationList _assoziations; + + // for cycle detection + int _cycleLow; + TraceFunction* _cycleStackDown; + + // cached + SubCost _calledCount, _callingCount; + int _calledContexts, _callingContexts; +}; + +typedef QMap TraceFunctionSet; + +/** + * A cycle of recursive calling functions. + * + * This is itself shown as a function + */ +class TraceFunctionCycle: public TraceFunction +{ + public: + TraceFunctionCycle(TraceFunction*, int n); + + virtual CostType type() const { return FunctionCycle; } + + // this removes all members from this cycle + void init(); + void add(TraceFunction*); + // this sets up the cycle once members are added + void setup(); + + TraceFunction* base() const { return _base; } + int cycleNo() const { return _cycleNo; } + const TraceFunctionList& members() const { return _members; } + + private: + TraceFunction* _base; + int _cycleNo; + + TraceFunctionList _members; + TraceFunctionSet _memberSet; +}; + + +/** + * A C++ Class / Namespace + * + * If a function symbol has a prefix ending in "::", + * the prefix is supposed to be a class/namespace specifier. + * Without such a prefix, we put a symbol in the "(global)" namespace. + */ +class TraceClass: public TraceCostItem +{ + public: + TraceClass(); + virtual ~TraceClass(); + + virtual CostType type() const { return Class; } + virtual QString prettyName() const; + + void addFunction(TraceFunction*); + const TraceFunctionList& functions() const { return _functions; } + + // part factory + TracePartClass* partClass(TracePart*); + + private: + TraceFunctionList _functions; +}; + + + +/** + * A source file containing function definitions + */ +class TraceFile: public TraceCostItem +{ + public: + TraceFile(); + virtual ~TraceFile(); + + virtual CostType type() const { return File; } + void setDirectory(const QString& dir); + void resetDirectory() { _dir = QString::null; } + QString directory(); + + void addFunction(TraceFunction*); + void addSourceFile(TraceFunctionSource*); + + // without path + QString shortName() const; + QString prettyName() const; + QString prettyLongName() const; + const TraceFunctionList& functions() const { return _functions; } + const TraceFunctionSourceList& sourceFiles() const + { return _sourceFiles; } + + // part factory + TracePartFile* partFile(TracePart*); + + private: + TraceFunctionList _functions; + TraceFunctionSourceList _sourceFiles; + QString _dir; +}; + + +/** + * A object containing a text segment (shared lib/executable) + * with defined functions + */ +class TraceObject: public TraceCostItem +{ + public: + TraceObject(); + virtual ~TraceObject(); + + virtual CostType type() const { return Object; } + + void addFunction(TraceFunction*); + + virtual void setName(const QString& name); + QString shortName() const { return _shortName; } + QString prettyName() const; + const TraceFunctionList& functions() const { return _functions; } + + // part factory + TracePartObject* partObject(TracePart*); + + private: + TraceFunctionList _functions; + QString _shortName; +}; + + + +/** + * This class holds profiling data of multiple tracefiles + * generated with cachegrind on one command. + * + */ +class TraceData: public TraceCost +{ + public: + TraceData(TopLevel* top = 0); + TraceData(const QString& base); + virtual ~TraceData(); + + virtual CostType type() const { return Data; } + virtual TraceData* data() { return this; } + virtual const TraceData* data() const { return this; } + + /** + * Loads a trace file. + * + * This adjusts the TraceCostMapping according to given cost types + */ + void load(const QString&); + + /** returns true if something changed. These do NOT + * invalidate the dynamic costs on a activation change, + * i.e. all cost items dependend on active parts. + * This has to be done by the caller when true is returned by + * calling invalidateDynamicCost(). + */ + bool activateParts(const TracePartList&); + bool activateParts(TracePartList, bool active); + bool activatePart(TracePart*, bool active); + bool activateAll(bool active=true); + + TracePartList parts() const { return _parts; } + TracePart* part(QString& name); + + // with path + QString traceName() const { return _traceName; } + + // without path + QString shortTraceName() const; + QString activePartRange(); + + TraceCostMapping* mapping() { return &_mapping; } + + // memory pools + FixPool* fixPool(); + DynPool* dynPool(); + + // factories for object/file/class/function/line instances + TraceObject* object(const QString& name); + TraceFile* file(const QString& name); + TraceClass* cls(const QString& fnName, QString& shortName); + // function creation involves class creation if needed + TraceFunction* function(const QString& name, TraceFile*, TraceObject*); + // factory for function cycles + TraceFunctionCycle* functionCycle(TraceFunction*); + + /** + * Search for item with given name and highest subcost of given cost type. + * + * For some items, they will only be found if the parent cost is given: + * Instr, Line, Call => need parent of type Function + * For Function, a parent of type Obj/File/Class can be given, but + * isn't needed. + */ + TraceCost* search(TraceItem::CostType, QString, + TraceCostType* ct = 0, TraceCost* parent = 0); + + // for pretty function names without signature if unique... + TraceFunctionMap::Iterator functionIterator(TraceFunction*); + TraceFunctionMap::ConstIterator functionBeginIterator() const; + TraceFunctionMap::ConstIterator functionEndIterator() const; + + TraceObjectMap& objectMap() { return _objectMap; } + TraceFileMap& fileMap() { return _fileMap; } + TraceClassMap& classMap() { return _classMap; } + TraceFunctionMap& functionMap() { return _functionMap; } + + const TraceFunctionCycleList& functionCycles() { return _functionCycles; } + + TraceCost* callMax() { return &_callMax; } + + void setCommand(const QString& command) { _command = command; } + QString command() const { return _command; } + TraceCost* totals() { return &_totals; } + void setMaxThreadID(int tid) { _maxThreadID = tid; } + int maxThreadID() const { return _maxThreadID; } + void setMaxPartNumber(int n) { _maxPartNumber = n; } + int maxPartNumber() const { return _maxPartNumber; } + + // reset all manually set directories for source files + void resetSourceDirs(); + + virtual void update(); + + // invalidates all cost items dependant on active state of parts + void invalidateDynamicCost(); + + // cycle detection + void updateFunctionCycles(); + void updateObjectCycles(); + void updateClassCycles(); + void updateFileCycles(); + bool inFunctionCycleUpdate() { return _inFunctionCycleUpdate; } + + private: + void init(); + // add trace part: events from one trace file + TracePart* addPart(const QString& dir, const QString& file); + + // for progress bar callbacks + TopLevel* _topLevel; + + TracePartList _parts; + + // The mapping for all costs + TraceCostMapping _mapping; + + FixPool* _fixPool; + DynPool* _dynPool; + + // always the trace totals (not dependent on active parts) + TraceCost _totals; + int _maxThreadID; + int _maxPartNumber; + + TraceObjectMap _objectMap; + TraceClassMap _classMap; + TraceFileMap _fileMap; + TraceFunctionMap _functionMap; + QString _command; + QString _traceName; + + // Max of all costs of calls: This allows to see if the incl. cost can + // be hidden for a cost type, as it's always the same as self cost + TraceCost _callMax; + + // cycles + TraceFunctionCycleList _functionCycles; + int _functionCycleCount; + bool _inFunctionCycleUpdate; +}; + + + +#endif diff --git a/kcachegrind/kcachegrind/traceitemview.cpp b/kcachegrind/kcachegrind/traceitemview.cpp new file mode 100644 index 00000000..8ee2faac --- /dev/null +++ b/kcachegrind/kcachegrind/traceitemview.cpp @@ -0,0 +1,443 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Trace Item View + */ + +#include +#include +#include +#include + +#include "traceitemview.h" +#include "toplevel.h" + +#define TRACE_UPDATES 0 + +TraceItemView::TraceItemView(TraceItemView* parentView, TopLevel* top) +{ + _parentView = parentView; + _topLevel = top ? top : parentView->topLevel(); + + _data = _newData = 0; + // _partList and _newPartList is empty + _activeItem = _newActiveItem = 0; + _selectedItem = _newSelectedItem = 0; + _costType = _newCostType = 0; + _costType2 = _newCostType2 = 0; + _groupType = _newGroupType = TraceItem::NoCostType; + + _status = nothingChanged; + _inUpdate = false; + _pos = Hidden; +} + +QString TraceItemView::whatsThis() const +{ + return i18n("No description available"); +} + +void TraceItemView::select(TraceItem* i) +{ + _newSelectedItem = i; +} + +KConfigGroup* TraceItemView::configGroup(KConfig* c, + QString group, QString post) +{ + QStringList gList = c->groupList(); + if (gList.contains((group+post).ascii()) ) group += post; + return new KConfigGroup(c, group); +} + +void TraceItemView::writeConfigEntry(KConfigBase* c, const char* pKey, + QString value, const char* def, bool bNLS) +{ + if (!c) return; + if ((value.isEmpty() && ((def == 0) || (*def == 0))) || + (value == QString(def))) + c->deleteEntry(pKey); + else + c->writeEntry(pKey, value, true, false, bNLS); +} + +void TraceItemView::writeConfigEntry(KConfigBase* c, const char* pKey, + int value, int def) +{ + if (!c) return; + if (value == def) + c->deleteEntry(pKey); + else + c->writeEntry(pKey, value); +} + +void TraceItemView::writeConfigEntry(KConfigBase* c, const char* pKey, + double value, double def) +{ + if (!c) return; + if (value == def) + c->deleteEntry(pKey); + else + c->writeEntry(pKey, value); +} + +void TraceItemView::writeConfigEntry(KConfigBase* c, const char* pKey, + bool value, bool def) +{ + if (!c) return; + if (value == def) + c->deleteEntry(pKey); + else + c->writeEntry(pKey, value); +} + +void TraceItemView::readViewConfig(KConfig*, QString, QString, bool) +{} + +#if 1 +void TraceItemView::saveViewConfig(KConfig*, QString, QString, bool) +{} +#else +void TraceItemView::saveViewConfig(KConfig* c, + QString prefix, QString postfix, bool) +{ + // write a dummy config entry to see missing virtual functions + KConfigGroup g(c, (prefix+postfix).ascii()); + g.writeEntry("SaveNotImplemented", true); +} +#endif + +bool TraceItemView::activate(TraceItem* i) +{ + i = canShow(i); + _newActiveItem = i; + + return (i != 0); +} + +TraceFunction* TraceItemView::activeFunction() +{ + TraceItem::CostType t = _activeItem->type(); + switch(t) { + case TraceItem::Function: + case TraceItem::FunctionCycle: + return (TraceFunction*) _activeItem; + default: + break; + } + return 0; +} + +bool TraceItemView::set(int changeType, TraceData* d, + TraceCostType* t1, TraceCostType* t2, + TraceItem::CostType g, const TracePartList& l, + TraceItem* a, TraceItem* s) +{ + _status |= changeType; + _newData = d; + _newGroupType = g; + _newCostType = t1; + _newCostType2 = t2; + _newPartList = l; + _newSelectedItem = s; + _newActiveItem = canShow(a); + if (!_newActiveItem) { + _newSelectedItem = 0; + return false; + } + + return true; +} + + +bool TraceItemView::isViewVisible() +{ + QWidget* w = widget(); + if (w) + return w->isVisible(); + return false; +} + +void TraceItemView::setData(TraceData* d) +{ + _newData = d; + + // invalidate all pointers to old data + _activeItem = _newActiveItem = 0; + _selectedItem = _newSelectedItem = 0; + _costType = _newCostType = 0; + _costType2 = _newCostType2 = 0; + _groupType = _newGroupType = TraceItem::NoCostType; + _partList.clear(); + _newPartList.clear(); + + // updateView will change this to dataChanged + _status = nothingChanged; +} + +void TraceItemView::updateView(bool force) +{ + if (!force && !isViewVisible()) return; + + if (_newData != _data) { + _status |= dataChanged; + _data = _newData; + } + else { + _status &= ~dataChanged; + + // if there's no data change and data is 0, no update needed + if (!_data) return; + } + + if (!(_newPartList == _partList)) { + _status |= partsChanged; + _partList = _newPartList; + } + else + _status &= ~partsChanged; + + if (_newActiveItem != _activeItem) { + + // when setting a new active item, there's no selection + _selectedItem = 0; + + _status |= activeItemChanged; + _activeItem = _newActiveItem; + } + else + _status &= ~activeItemChanged; + + if (_newCostType != _costType) { + _status |= costTypeChanged; + _costType = _newCostType; + } + else + _status &= ~costTypeChanged; + + if (_newCostType2 != _costType2) { + _status |= costType2Changed; + _costType2 = _newCostType2; + } + else + _status &= ~costType2Changed; + + if (_newGroupType != _groupType) { + _status |= groupTypeChanged; + _groupType = _newGroupType; + } + else + _status &= ~groupTypeChanged; + + + if (_newSelectedItem != _selectedItem) { + _status |= selectedItemChanged; + _selectedItem = _newSelectedItem; + } + else + _status &= ~selectedItemChanged; + + + if (!force && (_status == nothingChanged)) return; + +#if TRACE_UPDATES + kdDebug() << (widget() ? widget()->name() : "TraceItemView") + << "::doUpdate ( " + << ((_status & dataChanged) ? "data ":"") + << ((_status & configChanged) ? "config ":"") + << ")" << endl; + + if (_status & partsChanged) + kdDebug() << " Part List " + << _partList.names() + << endl; + + if (_status & costTypeChanged) + kdDebug() << " Cost type " + << (_costType ? _costType->name().ascii() : "?") + << endl; + + if (_status & costType2Changed) + kdDebug() << " Cost type 2 " + << (_costType2 ? _costType2->name().ascii() : "?") + << endl; + + if (_status & groupTypeChanged) + kdDebug() << " Group type " + << TraceItem::typeName(_groupType) + << endl; + + if (_status & activeItemChanged) + kdDebug() << " Active: " + << (_activeItem ? _activeItem->fullName().ascii() : "?") + << endl; + + if (_status & selectedItemChanged) + kdDebug() << " Selected: " + << (_selectedItem ? _selectedItem->fullName().ascii() : "?") + << endl; +#endif + + int st = _status; + _status = nothingChanged; + doUpdate(st); + return; + + if (_inUpdate) return; + _inUpdate = true; + doUpdate(_status); + _inUpdate = false; +} + + +void TraceItemView::selected(TraceItemView* /*sender*/, TraceItem* i) +{ +#if TRACE_UPDATES + kdDebug() << (widget() ? widget()->name() : "TraceItemView") + << "::selected " + << (i ? i->name().ascii(): "(nil)") + << ", sender " + << sender->widget()->name() << endl; +#endif + + if (_parentView) _parentView->selected(this, i); +} + +void TraceItemView::selected(TraceItemView* /*sender*/, const TracePartList& l) +{ +#if TRACE_UPDATES + kdDebug() << (widget() ? widget()->name() : "TraceItemView") + << "::selected " + << l.names() + << ", sender " + << sender->widget()->name() << endl; +#endif + + if (_parentView) + _parentView->selected(this, l); + else + if (_topLevel) _topLevel->activePartsChangedSlot(l); +} + +void TraceItemView::activated(TraceItemView* /*sender*/, TraceItem* i) +{ +#if TRACE_UPDATES + kdDebug() << (widget() ? widget()->name() : "TraceItemView") + << "::activated " + << (i ? i->name().ascii(): "(nil)") + << ", sender " + << sender->widget()->name() << endl; +#endif + + if (_parentView) + _parentView->activated(this, i); + else + if (_topLevel) _topLevel->setTraceItemDelayed(i); +} + +void TraceItemView::selectedCostType(TraceItemView*, TraceCostType* t) +{ + if (_parentView) + _parentView->selectedCostType(this, t); + else + if (_topLevel) _topLevel->setCostTypeDelayed(t); +} + +void TraceItemView::selectedCostType2(TraceItemView*, TraceCostType* t) +{ + if (_parentView) + _parentView->selectedCostType2(this, t); + else + if (_topLevel) _topLevel->setCostType2Delayed(t); +} + +void TraceItemView::activated(TraceItemView*, Direction d) +{ + if (_parentView) + _parentView->activated(this, d); + else + if (_topLevel) _topLevel->setDirectionDelayed(d); +} + +void TraceItemView::doUpdate(int) +{ +} + +void TraceItemView::selected(TraceItem* i) +{ + if (_parentView) + _parentView->selected(this, i); + +} + +void TraceItemView::selected(const TracePartList& l) +{ + if (_parentView) + _parentView->selected(this, l); + else + if (_topLevel) _topLevel->activePartsChangedSlot(l); +} + +void TraceItemView::activated(TraceItem* i) +{ +#if TRACE_UPDATES + kdDebug() << (widget() ? widget()->name() : "TraceItemView") + << "::activated " + << (i ? i->name().ascii(): "(nil)") << endl; +#endif + + if (_parentView) + _parentView->activated(this, i); + else + if (_topLevel) _topLevel->setTraceItemDelayed(i); +} + +void TraceItemView::selectedCostType(TraceCostType* t) +{ + if (_parentView) + _parentView->selectedCostType(this, t); + else + if (_topLevel) _topLevel->setCostTypeDelayed(t); +} + +void TraceItemView::selectedCostType2(TraceCostType* t) +{ + if (_parentView) + _parentView->selectedCostType2(this, t); + else + if (_topLevel) _topLevel->setCostType2Delayed(t); +} + +void TraceItemView::activated(Direction d) +{ + if (_parentView) + _parentView->activated(this, d); + else + if (_topLevel) _topLevel->setDirectionDelayed(d); +} + +void TraceItemView::addCostMenu(QPopupMenu* p, bool withCost2) +{ + if (_topLevel) _topLevel->addCostMenu(p, withCost2); +} + +void TraceItemView::addGoMenu(QPopupMenu* p) +{ + if (_topLevel) _topLevel->addGoMenu(p); +} diff --git a/kcachegrind/kcachegrind/traceitemview.h b/kcachegrind/kcachegrind/traceitemview.h new file mode 100644 index 00000000..e74a009c --- /dev/null +++ b/kcachegrind/kcachegrind/traceitemview.h @@ -0,0 +1,206 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Trace Item View + */ + +#ifndef TRACEITEMVIEW_H +#define TRACEITEMVIEW_H + +#include "tracedata.h" + +class QWidget; +class QPopupMenu; + +class KConfig; +class KConfigGroup; +class KConfigBase; + +class TopLevel; + +/** + * Abstract Base Class for KCachegrind Views + * + * This class delivers the shared functionality of all KCachegrind + * Views for one TraceItem (like Function, Object...), the "active" + * item. Additional view attributes are current primary cost type, + * an optional secondary cost type, group type, + * and possibly a selected costitem in this view. + * Note that there is a difference in changing the selected item of + * a view (this usually changes selection in other views, too), and + * activating that item. + */ +class TraceItemView +{ +public: + + /** + * Change type for update functions + * - is used if e.g. cycles are recalculated + */ + enum { nothingChanged = 0, + costTypeChanged = 1, + costType2Changed = 2, + groupTypeChanged = 4, + partsChanged = 8, + activeItemChanged = 16, + selectedItemChanged = 32, + dataChanged = 64, + configChanged = 128 }; + + enum Direction { None, Back, Forward, Up }; + + // a TraceItemView can have a position in a parent container + enum Position { Hidden, Top, Right, Left, Bottom }; + + TraceItemView(TraceItemView* parentView, TopLevel* top = 0); + virtual ~TraceItemView() {} + + virtual QString whatsThis() const; + + static KConfigGroup* configGroup(KConfig*, QString prefix, QString postfix); + static void writeConfigEntry(KConfigBase*, const char* pKey, QString value, + const char* def, bool bNLS = false); + static void writeConfigEntry(KConfigBase*, const char* pKey, + int value, int def); + static void writeConfigEntry(KConfigBase*, const char* pKey, + bool value, bool def); + static void writeConfigEntry(KConfigBase*, const char* pKey, + double value, double def); + virtual void readViewConfig(KConfig*, QString prefix, QString postfix, + bool withOptions); + virtual void saveViewConfig(KConfig*, QString prefix, QString postfix, + bool withOptions); + + // Immediate remove all references to old data, and set the new. + // This resets the visualization state. + // A GUI update has to be triggered with updateView(). + // Overwrite in container views to also set new data for all members. + virtual void setData(TraceData* d); + + // change from parent, call updateView() to update lazily (only if visible) + void setCostType(TraceCostType* t) { _newCostType = t; } + void setCostType2(TraceCostType* t) { _newCostType2 = t; } + void set(TraceItem::CostType g) { _newGroupType = g; } + void set(const TracePartList& l) { _newPartList = l; } + // returns false if nothing can be shown for this trace item + bool activate(TraceItem* i); + void select(TraceItem* i); + void notifyChange(int changeType) { _status |= changeType; } + // all in one + bool set(int, TraceData*, TraceCostType*, TraceCostType*, + TraceItem::CostType, const TracePartList&, + TraceItem*, TraceItem*); + + // general update request, call if view is/gets visible + void updateView(bool force = false); + + /** + * Notification from child views. + * Default implementation notifies parent + */ + virtual void selected(TraceItemView* sender, TraceItem*); + virtual void selected(TraceItemView* sender, const TracePartList&); + virtual void activated(TraceItemView* sender, Direction); + virtual void selectedCostType(TraceItemView* sender, TraceCostType*); + virtual void selectedCostType2(TraceItemView* sender, TraceCostType*); + virtual void activated(TraceItemView* sender, TraceItem*); + + // getters... + // always get the newest values + TraceData* data() const { return _newData; } + TraceItem* activeItem() const { return _newActiveItem; } + TraceItem* selectedItem() const { return _newSelectedItem; } + TraceCostType* costType() const { return _newCostType; } + TraceCostType* costType2() const { return _newCostType2; } + TraceItem::CostType groupType() const { return _newGroupType; } + const TracePartList& partList() const { return _newPartList; } + + TraceFunction* activeFunction(); + int status() const { return _status; } + + // pointer to top level window to e.g. show status messages + void setTopLevel(TopLevel* t) { _topLevel = t; } + TopLevel* topLevel() const { return _topLevel; } + + void setPosition(Position p) { _pos = p; } + Position position() const { return _pos; } + + void setTitle(QString t) { _title = t; } + QString title() const { return _title; } + + // We depend on derived class to be a widget. + // Force overiding by making this abstract. + virtual QWidget* widget() = 0; + + /** + * This function is called when a new item should become active. + * Reimplement this in subclasses. + * + * Returns the real item to become active. You can call select() here. + * Return 0 if nothing can be shown. + * Use the methods like data() instead of _data here, as + * _data possibly will give old/wrong information. + */ + virtual TraceItem* canShow(TraceItem* i) { return i; } + + /* convenience functions for often used context menu items */ + void addCostMenu(QPopupMenu*,bool withCost2 = true); + void addGoMenu(QPopupMenu*); + +protected: + // helpers to call selected()/activated() of parentView + void selected(TraceItem*); + void selected(const TracePartList&); + void activated(TraceItem*); + void selectedCostType(TraceCostType*); + void selectedCostType2(TraceCostType*); + void activated(Direction); + + /* Is this view visible? + * if not, doUpdate() won't be called by updateView() + */ + virtual bool isViewVisible(); + + // update handler (to be reimplemented) + virtual void doUpdate(int changeType); + + TraceItemView* _parentView; + TopLevel* _topLevel; + + TraceData* _data; + TracePartList _partList; + TraceItem *_activeItem, *_selectedItem; + TraceCostType *_costType, *_costType2; + TraceItem::CostType _groupType; + +private: + TraceData* _newData; + TracePartList _newPartList; + TraceItem *_newActiveItem, *_newSelectedItem; + TraceCostType *_newCostType, *_newCostType2; + TraceItem::CostType _newGroupType; + + QString _title; + int _status; + bool _inUpdate; + Position _pos; +}; + +#endif diff --git a/kcachegrind/kcachegrind/treemap.cpp b/kcachegrind/kcachegrind/treemap.cpp new file mode 100644 index 00000000..3ccc34f9 --- /dev/null +++ b/kcachegrind/kcachegrind/treemap.cpp @@ -0,0 +1,3214 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002, 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/* + * A Widget for visualizing hierarchical metrics as areas. + * The API is similar to QListView. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "treemap.h" + + +// set this to 1 to enable debug output +#define DEBUG_DRAWING 0 +#define MAX_FIELD 12 + + +// +// StoredDrawParams +// +StoredDrawParams::StoredDrawParams() +{ + _selected = false; + _current = false; + _shaded = true; + _rotated = false; + + _backColor = Qt::white; + + // field array has size 0 +} + +StoredDrawParams::StoredDrawParams(QColor c, + bool selected, bool current) +{ + _backColor = c; + + _selected = selected; + _current = current; + _shaded = true; + _rotated = false; + _drawFrame = true; + + // field array has size 0 +} + +QString StoredDrawParams::text(int f) const +{ + if ((f<0) || (f >= (int)_field.size())) + return QString::null; + + return _field[f].text; +} + +QPixmap StoredDrawParams::pixmap(int f) const +{ + if ((f<0) || (f >= (int)_field.size())) + return QPixmap(); + + return _field[f].pix; +} + +DrawParams::Position StoredDrawParams::position(int f) const +{ + if ((f<0) || (f >= (int)_field.size())) + return Default; + + return _field[f].pos; +} + +int StoredDrawParams::maxLines(int f) const +{ + if ((f<0) || (f >= (int)_field.size())) + return 0; + + return _field[f].maxLines; +} + +const QFont& StoredDrawParams::font() const +{ + static QFont* f = 0; + if (!f) f = new QFont(QApplication::font()); + + return *f; +} + +void StoredDrawParams::ensureField(int f) +{ + static Field* def = 0; + if (!def) { + def = new Field(); + def->pos = Default; + def->maxLines = 0; + } + + if (f<0 || f>=MAX_FIELD) return; + + if ((int)_field.size() < f+1) _field.resize(f+1, *def); +} + + +void StoredDrawParams::setField(int f, const QString& t, QPixmap pm, + Position p, int maxLines) +{ + if (f<0 || f>=MAX_FIELD) return; + ensureField(f); + + _field[f].text = t; + _field[f].pix = pm; + _field[f].pos = p; + _field[f].maxLines = maxLines; +} + +void StoredDrawParams::setText(int f, const QString& t) +{ + if (f<0 || f>=MAX_FIELD) return; + ensureField(f); + + _field[f].text = t; +} + +void StoredDrawParams::setPixmap(int f, const QPixmap& pm) +{ + if (f<0 || f>=MAX_FIELD) return; + ensureField(f); + + _field[f].pix = pm; +} + +void StoredDrawParams::setPosition(int f, Position p) +{ + if (f<0 || f>=MAX_FIELD) return; + ensureField(f); + + _field[f].pos = p; +} + +void StoredDrawParams::setMaxLines(int f, int m) +{ + if (f<0 || f>=MAX_FIELD) return; + ensureField(f); + + _field[f].maxLines = m; +} + + + +// +// RectDrawing +// + +RectDrawing::RectDrawing(QRect r) +{ + _fm = 0; + _dp = 0; + setRect(r); +} + + +RectDrawing::~RectDrawing() +{ + delete _fm; + delete _dp; +} + +DrawParams* RectDrawing::drawParams() +{ + if (!_dp) + _dp = new StoredDrawParams(); + + return _dp; +} + + +void RectDrawing::setDrawParams(DrawParams* dp) +{ + if (_dp) delete _dp; + _dp = dp; +} + +void RectDrawing::setRect(QRect r) +{ + _rect = r; + + _usedTopLeft = 0; + _usedTopCenter = 0; + _usedTopRight = 0; + _usedBottomLeft = 0; + _usedBottomCenter = 0; + _usedBottomRight = 0; + + _fontHeight = 0; +} + +QRect RectDrawing::remainingRect(DrawParams* dp) +{ + if (!dp) dp = drawParams(); + + if ((_usedTopLeft >0) || + (_usedTopCenter >0) || + (_usedTopRight >0)) { + if (dp->rotated()) + _rect.setLeft(_rect.left() + _fontHeight); + else + _rect.setTop(_rect.top() + _fontHeight); + } + + if ((_usedBottomLeft >0) || + (_usedBottomCenter >0) || + (_usedBottomRight >0)) { + if (dp->rotated()) + _rect.setRight(_rect.right() - _fontHeight); + else + _rect.setBottom(_rect.bottom() - _fontHeight); + } + return _rect; +} + + +void RectDrawing::drawBack(QPainter* p, DrawParams* dp) +{ + if (!dp) dp = drawParams(); + if (_rect.width()<=0 || _rect.height()<=0) return; + + QRect r = _rect; + QColor normal = dp->backColor(); + if (dp->selected()) normal = normal.light(); + bool isCurrent = dp->current(); + + if (dp->drawFrame() || isCurrent) { + // 3D raised/sunken frame effect... + QColor high = normal.light(); + QColor low = normal.dark(); + p->setPen( isCurrent ? low:high); + p->drawLine(r.left(), r.top(), r.right(), r.top()); + p->drawLine(r.left(), r.top(), r.left(), r.bottom()); + p->setPen( isCurrent ? high:low); + p->drawLine(r.right(), r.top(), r.right(), r.bottom()); + p->drawLine(r.left(), r.bottom(), r.right(), r.bottom()); + r.setRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2); + } + if (r.width()<=0 || r.height()<=0) return; + + if (dp->shaded()) { + // some shading + bool goDark = qGray(normal.rgb())>128; + int rBase, gBase, bBase; + normal.rgb(&rBase, &gBase, &bBase); + p->setBrush(QBrush::NoBrush); + + // shade parameters: + int d = 7; + float factor = 0.1, forth=0.7, back1 =0.9, toBack2 = .7, back2 = 0.97; + + // coefficient corrections because of rectangle size + int s = r.width(); + if (s > r.height()) s = r.height(); + if (s<100) { + forth -= .3 * (100-s)/100; + back1 -= .2 * (100-s)/100; + back2 -= .02 * (100-s)/100; + } + + + // maximal color difference + int rDiff = goDark ? -rBase/d : (255-rBase)/d; + int gDiff = goDark ? -gBase/d : (255-gBase)/d; + int bDiff = goDark ? -bBase/d : (255-bBase)/d; + + QColor shadeColor; + while (factor<.95) { + shadeColor.setRgb((int)(rBase+factor*rDiff+.5), + (int)(gBase+factor*gDiff+.5), + (int)(bBase+factor*bDiff+.5)); + p->setPen(shadeColor); + p->drawRect(r); + r.setRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2); + if (r.width()<=0 || r.height()<=0) return; + factor = 1.0 - ((1.0 - factor) * forth); + } + + // and back (1st half) + while (factor>toBack2) { + shadeColor.setRgb((int)(rBase+factor*rDiff+.5), + (int)(gBase+factor*gDiff+.5), + (int)(bBase+factor*bDiff+.5)); + p->setPen(shadeColor); + p->drawRect(r); + r.setRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2); + if (r.width()<=0 || r.height()<=0) return; + factor = 1.0 - ((1.0 - factor) / back1); + } + + // and back (2nd half) + while ( factor>.01) { + shadeColor.setRgb((int)(rBase+factor*rDiff+.5), + (int)(gBase+factor*gDiff+.5), + (int)(bBase+factor*bDiff+.5)); + p->setPen(shadeColor); + p->drawRect(r); + r.setRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2); + if (r.width()<=0 || r.height()<=0) return; + + factor = factor * back2; + } + } + + // fill inside + p->setPen(QPen::NoPen); + p->setBrush(normal); + p->drawRect(r); +} + + +bool RectDrawing::drawField(QPainter* p, int f, DrawParams* dp) +{ + if (!dp) dp = drawParams(); + + if (!_fm) { + _fm = new QFontMetrics(dp->font()); + _fontHeight = _fm->height(); + } + + QRect r = _rect; + + if (0) kdDebug(90100) << "DrawField: Rect " << r.x() << "/" << r.y() + << " - " << r.width() << "x" << r.height() << endl; + + int h = _fontHeight; + bool rotate = dp->rotated(); + int width = (rotate ? r.height() : r.width()) -4; + int height = (rotate ? r.width() : r.height()); + int lines = height / h; + + // stop if we have no space available + if (lines<1) return false; + + // calculate free space in first line () + int pos = dp->position(f); + if (pos == DrawParams::Default) { + switch(f%4) { + case 0: pos = DrawParams::TopLeft; break; + case 1: pos = DrawParams::TopRight; break; + case 2: pos = DrawParams::BottomRight; break; + case 3: pos = DrawParams::BottomLeft; break; + } + } + + int unused = 0; + bool isBottom = false; + bool isCenter = false; + bool isRight = false; + int* used = 0; + switch(pos) { + case DrawParams::TopLeft: + used = &_usedTopLeft; + if (_usedTopLeft == 0) { + if (_usedTopCenter) + unused = (width - _usedTopCenter)/2; + else + unused = width - _usedTopRight; + } + break; + + case DrawParams::TopCenter: + isCenter = true; + used = &_usedTopCenter; + if (_usedTopCenter == 0) { + if (_usedTopLeft > _usedTopRight) + unused = width - 2 * _usedTopLeft; + else + unused = width - 2 * _usedTopRight; + } + break; + + case DrawParams::TopRight: + isRight = true; + used = &_usedTopRight; + if (_usedTopRight == 0) { + if (_usedTopCenter) + unused = (width - _usedTopCenter)/2; + else + unused = width - _usedTopLeft; + } + break; + + case DrawParams::BottomLeft: + isBottom = true; + used = &_usedBottomLeft; + if (_usedBottomLeft == 0) { + if (_usedBottomCenter) + unused = (width - _usedBottomCenter)/2; + else + unused = width - _usedBottomRight; + } + break; + + case DrawParams::BottomCenter: + isCenter = true; + isBottom = true; + used = &_usedBottomCenter; + if (_usedBottomCenter == 0) { + if (_usedBottomLeft > _usedBottomRight) + unused = width - 2 * _usedBottomLeft; + else + unused = width - 2 * _usedBottomRight; + } + break; + + case DrawParams::BottomRight: + isRight = true; + isBottom = true; + used = &_usedBottomRight; + if (_usedBottomRight == 0) { + if (_usedBottomCenter) + unused = (width - _usedBottomCenter)/2; + else + unused = width - _usedBottomLeft; + } + break; + } + + if (isBottom) { + if ((_usedTopLeft >0) || + (_usedTopCenter >0) || + (_usedTopRight >0)) + lines--; + } + else if (!isBottom) { + if ((_usedBottomLeft >0) || + (_usedBottomCenter >0) || + (_usedBottomRight >0)) + lines--; + } + if (lines<1) return false; + + + int y = isBottom ? height - h : 0; + + if (unused < 0) unused = 0; + if (unused == 0) { + // no space available in last line at this position + y = isBottom ? (y-h) : (y+h); + lines--; + + if (lines<1) return false; + + // new line: reset used space + if (isBottom) + _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0; + else + _usedTopLeft = _usedTopCenter = _usedTopRight = 0; + + unused = width; + } + + // stop as soon as possible when there's no space for "..." + static int dotW = 0; + if (!dotW) dotW = _fm->width("..."); + if (width < dotW) return false; + + // get text and pixmap now, only if we need to, because it is possible + // that they are calculated on demand (and this can take some time) + QString name = dp->text(f); + if (name.isEmpty()) return 0; + QPixmap pix = dp->pixmap(f); + + // check if pixmap can be drawn + int pixW = pix.width(); + int pixH = pix.height(); + int pixY = 0; + bool pixDrawn = true; + if (pixW>0) { + pixW += 2; // X distance from pix + if ((width < pixW + dotW) || (height < pixH)) { + // don't draw + pixW = 0; + } + else + pixDrawn = false; + } + + // width of text and pixmap to be drawn + int w = pixW + _fm->width(name); + + if (0) kdDebug(90100) << " For '" << name << "': Unused " << unused + << ", StrW " << w << ", Width " << width << endl; + + // if we have limited space at 1st line: + // use it only if whole name does fit in last line... + if ((unused < width) && (w > unused)) { + y = isBottom ? (y-h) : (y+h); + lines--; + + if (lines<1) return false; + + // new line: reset used space + if (isBottom) + _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0; + else + _usedTopLeft = _usedTopCenter = _usedTopRight = 0; + } + + p->save(); + p->setPen( (qGray(dp->backColor().rgb())>100) ? Qt::black : Qt::white); + p->setFont(dp->font()); + if (rotate) { + //p->translate(r.x()+2, r.y()+r.height()); + p->translate(r.x(), r.y()+r.height()-2); + p->rotate(270); + } + else + p->translate(r.x()+2, r.y()); + + + // adjust available lines according to maxLines + int max = dp->maxLines(f); + if ((max > 0) && (lines>max)) lines = max; + + /* loop over name parts to break up string depending on available width. + * every char category change is supposed a possible break, + * with the exception Uppercase=>Lowercase. + * It's good enough for numbers, Symbols... + * + * If the text is to be written at the bottom, we start with the + * end of the string (so everything is reverted) + */ + QString remaining; + int origLines = lines; + while (lines>0) { + + if (w>width && lines>1) { + int lastBreakPos = name.length(), lastWidth = w; + int len = name.length(); + QChar::Category caOld, ca; + + if (!isBottom) { + // start with comparing categories of last 2 chars + caOld = name[len-1].category(); + while (len>2) { + len--; + ca = name[len-1].category(); + if (ca != caOld) { + // "Aa" has no break between... + if (ca == QChar::Letter_Uppercase && + caOld == QChar::Letter_Lowercase) { + caOld = ca; + continue; + } + caOld = ca; + lastBreakPos = len; + w = pixW + _fm->width(name, len); + lastWidth = w; + if (w <= width) break; + } + } + w = lastWidth; + remaining = name.mid(lastBreakPos); + // remove space on break point + if (name[lastBreakPos-1].category() == QChar::Separator_Space) + name = name.left(lastBreakPos-1); + else + name = name.left(lastBreakPos); + } + else { // bottom + int l = len; + caOld = name[l-len].category(); + while (len>2) { + len--; + ca = name[l-len].category(); + + if (ca != caOld) { + // "Aa" has no break between... + if (caOld == QChar::Letter_Uppercase && + ca == QChar::Letter_Lowercase) { + caOld = ca; + continue; + } + caOld = ca; + lastBreakPos = len; + w = pixW + _fm->width(name.right(len)); + lastWidth = w; + if (w <= width) break; + } + } + w = lastWidth; + remaining = name.left(l-lastBreakPos); + // remove space on break point + if (name[l-lastBreakPos].category() == QChar::Separator_Space) + name = name.right(lastBreakPos-1); + else + name = name.right(lastBreakPos); + } + } + else + remaining = QString::null; + + /* truncate and add ... if needed */ + if (w>width) { + int len = name.length(); + w += dotW; + while (len>2 && (w > width)) { + len--; + w = pixW + _fm->width(name, len) + dotW; + } + // stop drawing: we cannot draw 2 chars + "..." + if (w>width) break; + + name = name.left(len) + "..."; + } + + int x = 0; + if (isCenter) + x = (width - w)/2; + else if (isRight) + x = width - w; + + if (!pixDrawn) { + pixY = y+(h-pixH)/2; // default: center vertically + if (pixH > h) pixY = isBottom ? y-(pixH-h) : y; + + p->drawPixmap( x, pixY, pix); + + // for distance to next text + pixY = isBottom ? (pixY - h - 2) : (pixY + pixH + 2); + pixDrawn = true; + } + + + if (0) kdDebug(90100) << " Drawing '" << name << "' at " + << x+pixW << "/" << y << endl; + + p->drawText( x+pixW, y, + width - pixW, h, + Qt::AlignLeft, name); + y = isBottom ? (y-h) : (y+h); + lines--; + + if (remaining.isEmpty()) break; + name = remaining; + w = pixW + _fm->width(name); + } + + // make sure the pix stays visible + if (pixDrawn && (pixY>0)) { + if (isBottom && (pixYy)) y = pixY; + } + + if (origLines > lines) { + // if only 1 line written, don't reset _used* vars + if (lines - origLines >1) { + if (isBottom) + _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0; + else + _usedTopLeft = _usedTopCenter = _usedTopRight = 0; + } + + // take back one line + y = isBottom ? (y+h) : (y-h); + if (used) *used = w; + } + + // update free space + if (!isBottom) { + if (rotate) + _rect.setRect(r.x()+y, r.y(), r.width()-y, r.height()); + else + _rect.setRect(r.x(), r.y()+y, r.width(), r.height()-y); + } + else { + if (rotate) + _rect.setRect(r.x(), r.y(), y+h, r.height()); + else + _rect.setRect(r.x(), r.y(), r.width(), y+h); + } + + p->restore(); + + return true; +} + + + + + + +// +// TreeMapItemList +// + +int TreeMapItemList::compareItems ( Item item1, Item item2 ) +{ + bool ascending; + int result; + + TreeMapItem* parent = ((TreeMapItem*)item1)->parent(); + // shouldn't happen + if (!parent) return 0; + + int textNo = parent->sorting(&ascending); + + if (textNo < 0) { + double diff = ((TreeMapItem*)item1)->value() - + ((TreeMapItem*)item2)->value(); + result = (diff < -.9) ? -1 : (diff > .9) ? 1 : 0; + } + else + result = (((TreeMapItem*)item1)->text(textNo) < + ((TreeMapItem*)item2)->text(textNo)) ? -1 : 1; + + return ascending ? result : -result; +} + + +TreeMapItem* TreeMapItemList::commonParent() +{ + TreeMapItem* parent, *item; + parent = first(); + if (parent) + while( (item = next()) != 0) + parent = parent->commonParent(item); + + return parent; +} + + +// TreeMapItem + +TreeMapItem::TreeMapItem(TreeMapItem* parent, double value) +{ + _value = value; + _parent = parent; + + _sum = 0; + _children = 0; + _widget = 0; + _index = -1; + _depth = -1; // not set + _unused_self = 0; + _freeRects = 0; + + if (_parent) { + // take sorting from parent + _sortTextNo = _parent->sorting(&_sortAscending); + _parent->addItem(this); + } + else { + _sortAscending = false; + _sortTextNo = -1; // default: no sorting + } +} + + +TreeMapItem::TreeMapItem(TreeMapItem* parent, double value, + QString text1, QString text2, + QString text3, QString text4) +{ + _value = value; + _parent = parent; + + // this resizes the text vector only if needed + if (!text4.isEmpty()) setText(3, text4); + if (!text3.isEmpty()) setText(2, text3); + if (!text2.isEmpty()) setText(1, text2); + setText(0, text1); + + _sum = 0; + _children = 0; + _widget = 0; + _index = -1; + _depth = -1; // not set + _unused_self = 0; + _freeRects = 0; + + if (_parent) _parent->addItem(this); +} + +TreeMapItem::~TreeMapItem() +{ + if (_children) delete _children; + if (_freeRects) delete _freeRects; + + // finally, notify widget about deletion + if (_widget) _widget->deletingItem(this); +} + +void TreeMapItem::setParent(TreeMapItem* p) +{ + _parent = p; + if (p) _widget = p->_widget; +} + +bool TreeMapItem::isChildOf(TreeMapItem* item) +{ + if (!item) return false; + + TreeMapItem* i = this; + while (i) { + if (item == i) return true; + i = i->_parent; + } + return false; +} + +TreeMapItem* TreeMapItem::commonParent(TreeMapItem* item) +{ + while (item && !isChildOf(item)) { + item = item->parent(); + } + return item; +} + +void TreeMapItem::redraw() +{ + if (_widget) + _widget->redraw(this); +} + +void TreeMapItem::clear() +{ + if (_children) { + // delete selected items below this item from selection + if (_widget) _widget->clearSelection(this); + + delete _children; + _children = 0; + } +} + + +// invalidates current children and forces redraw +// this is only usefull when children are created on demand in items() +void TreeMapItem::refresh() +{ + clear(); + redraw(); +} + + +QStringList TreeMapItem::path(int textNo) const +{ + QStringList list(text(textNo)); + + TreeMapItem* i = _parent; + while (i) { + QString text = i->text(textNo); + if (!text.isEmpty()) + list.prepend(i->text(textNo)); + i = i->_parent; + } + return list; +} + +int TreeMapItem::depth() const +{ + if (_depth>0) return _depth; + + if (_parent) + return _parent->depth() + 1; + return 1; +} + + +bool TreeMapItem::initialized() +{ + if (!_children) { + _children = new TreeMapItemList; + _children->setAutoDelete(true); + return false; + } + return true; +} + +void TreeMapItem::addItem(TreeMapItem* i) +{ + if (!i) return; + + if (!_children) { + _children = new TreeMapItemList; + _children->setAutoDelete(true); + } + i->setParent(this); + + if (sorting(0) == -1) + _children->append(i); // preserve insertion order + else + _children->inSort(i); +} + + +// default implementations of virtual functions + +double TreeMapItem::value() const +{ + return _value; +} + +double TreeMapItem::sum() const +{ + return _sum; +} + +DrawParams::Position TreeMapItem::position(int f) const +{ + Position p = StoredDrawParams::position(f); + if (_widget && (p == Default)) + p = _widget->fieldPosition(f); + + return p; +} + +// use widget font +const QFont& TreeMapItem::font() const +{ + return _widget->currentFont(); +} + + +bool TreeMapItem::isMarked(int) const +{ + return false; +} + + +int TreeMapItem::borderWidth() const +{ + if (_widget) + return _widget->borderWidth(); + + return 2; +} + +int TreeMapItem::sorting(bool* ascending) const +{ + if (ascending) *ascending = _sortAscending; + return _sortTextNo; +} + +// do *not* set sorting recursively +void TreeMapItem::setSorting(int textNo, bool ascending) +{ + if (_sortTextNo == textNo) { + if(_sortAscending == ascending) return; + if (textNo == -1) { + // when no sorting is done, order change doesn't do anything + _sortAscending = ascending; + return; + } + } + _sortAscending = ascending; + _sortTextNo = textNo; + + if (_children && _sortTextNo != -1) _children->sort(); +} + +void TreeMapItem::resort(bool recursive) +{ + if (!_children) return; + + if (_sortTextNo != -1) _children->sort(); + + if (recursive) + for (TreeMapItem* i=_children->first(); i; i=_children->next()) + i->resort(recursive); +} + + +TreeMapItem::SplitMode TreeMapItem::splitMode() const +{ + if (_widget) + return _widget->splitMode(); + + return Best; +} + +int TreeMapItem::rtti() const +{ + return 0; +} + +TreeMapItemList* TreeMapItem::children() +{ + if (!_children) { + _children = new TreeMapItemList; + _children->setAutoDelete(true); + } + return _children; +} + +void TreeMapItem::clearItemRect() +{ + _rect = QRect(); + clearFreeRects(); +} + +void TreeMapItem::clearFreeRects() +{ + if (_freeRects) _freeRects->clear(); +} + +void TreeMapItem::addFreeRect(const QRect& r) +{ + // don't add invalid rects + if ((r.width() < 1) || (r.height() < 1)) return; + + if (!_freeRects) { + _freeRects = new QPtrList; + _freeRects->setAutoDelete(true); + } + + if (0) kdDebug(90100) << "addFree(" << path(0).join("/") << ", " + << r.x() << "/" << r.y() << "-" + << r.width() << "x" << r.height() << ")" << endl; + + QRect* last = _freeRects->last(); + if (!last) { + _freeRects->append(new QRect(r)); + return; + } + + // join rect with last rect if possible + // this saves memory and doesn't make the tooltip flicker + + bool replaced = false; + if ((last->left() == r.left()) && (last->width() == r.width())) { + if ((last->bottom()+1 == r.top()) || (r.bottom()+1 == last->top())) { + *last |= r; + replaced = true; + } + } + else if ((last->top() == r.top()) && (last->height() == r.height())) { + if ((last->right()+1 == r.left()) || (r.right()+1 == last->left())) { + *last |= r; + replaced = true; + } + } + + if (!replaced) { + _freeRects->append(new QRect(r)); + return; + } + + if (0) kdDebug(90100) << " united with last to (" + << last->x() << "/" << last->y() << "-" + << last->width() << "x" << last->height() << ")" << endl; +} + + +// Tooltips for TreeMapWidget + +class TreeMapTip: public QToolTip +{ +public: + TreeMapTip( QWidget* p ):QToolTip(p) {} + +protected: + void maybeTip( const QPoint & ); +}; + +void TreeMapTip::maybeTip( const QPoint& pos ) +{ + if ( !parentWidget()->inherits( "TreeMapWidget" ) ) + return; + + TreeMapWidget* p = (TreeMapWidget*)parentWidget(); + TreeMapItem* i; + i = p->item(pos.x(), pos.y()); + QPtrList* rList = i ? i->freeRects() : 0; + if (rList) { + QRect* r; + for(r=rList->first();r;r=rList->next()) + if (r->contains(pos)) + tip(*r, p->tipString(i)); + } +} + + + +// TreeMapWidget + +TreeMapWidget::TreeMapWidget(TreeMapItem* base, + QWidget* parent, const char* name) + : QWidget(parent, name) +{ + _base = base; + _base->setWidget(this); + + _font = font(); + _fontHeight = fontMetrics().height(); + + + // default behaviour + _selectionMode = Single; + _splitMode = TreeMapItem::AlwaysBest; + _visibleWidth = 2; + _reuseSpace = false; + _skipIncorrectBorder = false; + _drawSeparators = false; + _allowRotation = true; + _borderWidth = 2; + _shading = true; // beautiful is default! + _maxSelectDepth = -1; // unlimited + _maxDrawingDepth = -1; // unlimited + _minimalArea = -1; // unlimited + _markNo = 0; + + for(int i=0;i<4;i++) { + _drawFrame[i] = true; + _transparent[i] = false; + } + + // _stopAtText will be unset on resizing (per default) + // _textVisible will be true on resizing (per default) + // _forceText will be false on resizing (per default) + + // start state: _selection is an empty list + _current = 0; + _oldCurrent = 0; + _pressed = 0; + _lastOver = 0; + _needsRefresh = _base; + + setBackgroundMode(Qt::NoBackground); + setFocusPolicy(QWidget::StrongFocus); + _tip = new TreeMapTip(this); +} + +TreeMapWidget::~TreeMapWidget() +{ + delete _base; + delete _tip; +} + +const QFont& TreeMapWidget::currentFont() const +{ + return _font; +} + +void TreeMapWidget::setSplitMode(TreeMapItem::SplitMode m) +{ + if (_splitMode == m) return; + + _splitMode = m; + redraw(); +} + +TreeMapItem::SplitMode TreeMapWidget::splitMode() const +{ + return _splitMode; +} + +bool TreeMapWidget::setSplitMode(QString mode) +{ + if (mode == "Bisection") setSplitMode(TreeMapItem::Bisection); + else if (mode == "Columns") setSplitMode(TreeMapItem::Columns); + else if (mode == "Rows") setSplitMode(TreeMapItem::Rows); + else if (mode == "AlwaysBest") setSplitMode(TreeMapItem::AlwaysBest); + else if (mode == "Best") setSplitMode(TreeMapItem::Best); + else if (mode == "HAlternate") setSplitMode(TreeMapItem::HAlternate); + else if (mode == "VAlternate") setSplitMode(TreeMapItem::VAlternate); + else if (mode == "Horizontal") setSplitMode(TreeMapItem::Horizontal); + else if (mode == "Vertical") setSplitMode(TreeMapItem::Vertical); + else return false; + + return true; +} + +QString TreeMapWidget::splitModeString() const +{ + QString mode; + switch(splitMode()) { + case TreeMapItem::Bisection: mode = "Bisection"; break; + case TreeMapItem::Columns: mode = "Columns"; break; + case TreeMapItem::Rows: mode = "Rows"; break; + case TreeMapItem::AlwaysBest: mode = "AlwaysBest"; break; + case TreeMapItem::Best: mode = "Best"; break; + case TreeMapItem::HAlternate: mode = "HAlternate"; break; + case TreeMapItem::VAlternate: mode = "VAlternate"; break; + case TreeMapItem::Horizontal: mode = "Horizontal"; break; + case TreeMapItem::Vertical: mode = "Vertical"; break; + default: mode = "Unknown"; break; + } + return mode; +} + + +void TreeMapWidget::setShadingEnabled(bool s) +{ + if (_shading == s) return; + + _shading = s; + redraw(); +} + +void TreeMapWidget::drawFrame(int d, bool b) +{ + if ((d<0) || (d>=4) || (_drawFrame[d]==b)) return; + + _drawFrame[d] = b; + redraw(); +} + +void TreeMapWidget::setTransparent(int d, bool b) +{ + if ((d<0) || (d>=4) || (_transparent[d]==b)) return; + + _transparent[d] = b; + redraw(); +} + +void TreeMapWidget::setAllowRotation(bool enable) +{ + if (_allowRotation == enable) return; + + _allowRotation = enable; + redraw(); +} + +void TreeMapWidget::setVisibleWidth(int width, bool reuseSpace) +{ + if (_visibleWidth == width && _reuseSpace == reuseSpace) return; + + _visibleWidth = width; + _reuseSpace = reuseSpace; + redraw(); +} + +void TreeMapWidget::setSkipIncorrectBorder(bool enable) +{ + if (_skipIncorrectBorder == enable) return; + + _skipIncorrectBorder = enable; + redraw(); +} + +void TreeMapWidget::setBorderWidth(int w) +{ + if (_borderWidth == w) return; + + _borderWidth = w; + redraw(); +} + +void TreeMapWidget::setMaxDrawingDepth(int d) +{ + if (_maxDrawingDepth == d) return; + + _maxDrawingDepth = d; + redraw(); +} + +QString TreeMapWidget::defaultFieldType(int f) const +{ + return i18n("Text %1").arg(f+1); +} + +QString TreeMapWidget::defaultFieldStop(int) const +{ + return QString(); +} + +bool TreeMapWidget::defaultFieldVisible(int f) const +{ + return (f<2); +} + +bool TreeMapWidget::defaultFieldForced(int) const +{ + return false; +} + +DrawParams::Position TreeMapWidget::defaultFieldPosition(int f) const +{ + switch(f%4) { + case 0: return DrawParams::TopLeft; + case 1: return DrawParams::TopRight; + case 2: return DrawParams::BottomRight; + case 3: return DrawParams::BottomLeft; + default:break; + } + return DrawParams::TopLeft; +} + +bool TreeMapWidget::resizeAttr(int size) +{ + if (size<0 || size>=MAX_FIELD) return false; + + if (size>(int)_attr.size()) { + struct FieldAttr a; + int oldSize = _attr.size(); + _attr.resize(size, a); + while (oldSize -1) + _selection.remove(); + + while(_tmpSelection.findRef(i) > -1) + _tmpSelection.remove(); + + if (_current == i) _current = 0; + if (_oldCurrent == i) _oldCurrent = 0; + if (_pressed == i) _pressed = 0; + if (_lastOver == i) _lastOver = 0; + + // don't redraw a deleted item + if (_needsRefresh == i) { + // we can savely redraw the parent, as deleting order is + // from child to parent; i.e. i->parent() is existing. + _needsRefresh = i->parent(); + } +} + + +QString TreeMapWidget::tipString(TreeMapItem* i) const +{ + QString tip, itemTip; + + while (i) { + if (!i->text(0).isEmpty()) { + itemTip = i->text(0); + if (!i->text(1).isEmpty()) + itemTip += " (" + i->text(1) + ")"; + + if (!tip.isEmpty()) + tip += "\n"; + + tip += itemTip; + } + i = i->parent(); + } + return tip; +} + +TreeMapItem* TreeMapWidget::item(int x, int y) const +{ + TreeMapItem* p = _base; + TreeMapItem* i; + + if (!rect().contains(x, y)) return 0; + if (DEBUG_DRAWING) kdDebug(90100) << "item(" << x << "," << y << "):" << endl; + + while (1) { + TreeMapItemList* list = p->children(); + if (!list) + i = 0; + else { + int idx=0; + for (i=list->first();i;i=list->next(),idx++) { + + if (DEBUG_DRAWING) + kdDebug(90100) << " Checking " << i->path(0).join("/") << " (" + << i->itemRect().x() << "/" << i->itemRect().y() + << "-" << i->itemRect().width() + << "x" << i->itemRect().height() << ")" << endl; + + if (i->itemRect().contains(x, y)) { + + if (DEBUG_DRAWING) kdDebug(90100) << " .. Got. Index " << idx << endl; + + p->setIndex(idx); + break; + } + } + } + + if (!i) { + static TreeMapItem* last = 0; + if (p != last) { + last = p; + + if (DEBUG_DRAWING) + kdDebug(90100) << "item(" << x << "," << y << "): Got " + << p->path(0).join("/") << " (Size " + << p->itemRect().width() << "x" << p->itemRect().height() + << ", Val " << p->value() << ")" << endl; + } + + return p; + } + p = i; + } + return 0; +} + +TreeMapItem* TreeMapWidget::possibleSelection(TreeMapItem* i) const +{ + if (i) { + if (_maxSelectDepth>=0) { + int depth = i->depth(); + while(i && depth > _maxSelectDepth) { + i = i->parent(); + depth--; + } + } + } + return i; +} + +TreeMapItem* TreeMapWidget::visibleItem(TreeMapItem* i) const +{ + if (i) { + /* Must have a visible area */ + while(i && ((i->itemRect().width() <1) || + (i->itemRect().height() <1))) { + TreeMapItem* p = i->parent(); + if (!p) break; + int idx = p->children()->findRef(i); + idx--; + if (idx<0) + i = p; + else + i = p->children()->at(idx); + } + } + return i; +} + +void TreeMapWidget::setSelected(TreeMapItem* item, bool selected) +{ + item = possibleSelection(item); + setCurrent(item); + + TreeMapItem* changed = setTmpSelected(item, selected); + if (!changed) return; + + _selection = _tmpSelection; + if (_selectionMode == Single) + emit selectionChanged(item); + emit selectionChanged(); + redraw(changed); + + if (0) kdDebug(90100) << (selected ? "S":"Des") << "elected Item " + << (item ? item->path(0).join("") : QString("(null)")) + << " (depth " << (item ? item->depth() : -1) + << ")" << endl; +} + +void TreeMapWidget::setMarked(int markNo, bool redrawWidget) +{ + // if there's no marking, return + if ((_markNo == 0) && (markNo == 0)) return; + + _markNo = markNo; + if (!clearSelection() && redrawWidget) redraw(); +} + +/* Returns all items which appear only in one of the given lists */ +TreeMapItemList TreeMapWidget::diff(TreeMapItemList& l1, + TreeMapItemList& l2) +{ + TreeMapItemList l; + TreeMapItemListIterator it1(l1), it2(l2); + + TreeMapItem* item; + while ( (item = it1.current()) != 0 ) { + ++it1; + if (l2.containsRef(item) > 0) continue; + l.append(item); + } + while ( (item = it2.current()) != 0 ) { + ++it2; + if (l1.containsRef(item) > 0) continue; + l.append(item); + } + + return l; +} + +/* Only modifies _tmpSelection. + * Returns 0 when no change happened, otherwise the TreeMapItem that has + * to be redrawn for all changes. + */ +TreeMapItem* TreeMapWidget::setTmpSelected(TreeMapItem* item, bool selected) +{ + if (!item) return 0; + if (_selectionMode == NoSelection) return 0; + + TreeMapItemList old = _tmpSelection; + + if (_selectionMode == Single) { + _tmpSelection.clear(); + if (selected) _tmpSelection.append(item); + } + else { + if (selected) { + TreeMapItem* i=_tmpSelection.first(); + while (i) { + if (i->isChildOf(item) || item->isChildOf(i)) { + _tmpSelection.remove(); + i = _tmpSelection.current(); + } + else + i = _tmpSelection.next(); + } + _tmpSelection.append(item); + } + else + _tmpSelection.removeRef(item); + } + + return diff(old, _tmpSelection).commonParent(); +} + + +bool TreeMapWidget::clearSelection(TreeMapItem* parent) +{ + TreeMapItemList old = _selection; + + TreeMapItem* i=_selection.first(); + while (i) { + if (i->isChildOf(parent)) { + _selection.remove(); + i = _selection.current(); + } + else + i = _selection.next(); + } + + TreeMapItem* changed = diff(old, _selection).commonParent(); + if (changed) { + changed->redraw(); + emit selectionChanged(); + } + return (changed != 0); +} + +bool TreeMapWidget::isSelected(TreeMapItem* i) const +{ + return _selection.containsRef(i)>0; +} + +bool TreeMapWidget::isTmpSelected(TreeMapItem* i) +{ + return _tmpSelection.containsRef(i)>0; +} + + +void TreeMapWidget::setCurrent(TreeMapItem* i, bool kbd) +{ + TreeMapItem* old = _current; + _current = i; + + if (_markNo >0) { + // remove mark + _markNo = 0; + + if (1) kdDebug(90100) << "setCurrent(" << i->path(0).join("/") + << ") - mark removed" << endl; + + // always complete redraw needed to remove mark + redraw(); + + if (old == _current) return; + } + else { + if (old == _current) return; + + if (old) old->redraw(); + if (i) i->redraw(); + } + + //kdDebug(90100) << "Current Item " << (i ? i->path().ascii() : "(null)") << endl; + + emit currentChanged(i, kbd); +} + +void TreeMapWidget::setRangeSelection(TreeMapItem* i1, + TreeMapItem* i2, bool selected) +{ + i1 = possibleSelection(i1); + i2 = possibleSelection(i2); + setCurrent(i2); + + TreeMapItem* changed = setTmpRangeSelection(i1, i2, selected); + if (!changed) return; + + _selection = _tmpSelection; + if (_selectionMode == Single) + emit selectionChanged(i2); + emit selectionChanged(); + redraw(changed); +} + +TreeMapItem* TreeMapWidget::setTmpRangeSelection(TreeMapItem* i1, + TreeMapItem* i2, + bool selected) +{ + if ((i1 == 0) && (i2 == 0)) return 0; + if ((i1 == 0) || i1->isChildOf(i2)) return setTmpSelected(i2, selected); + if ((i2 == 0) || i2->isChildOf(i1)) return setTmpSelected(i1, selected); + + TreeMapItem* changed = setTmpSelected(i1, selected); + TreeMapItem* changed2 = setTmpSelected(i2, selected); + if (changed2) changed = changed2->commonParent(changed); + + TreeMapItem* commonParent = i1; + while (commonParent && !i2->isChildOf(commonParent)) { + i1 = commonParent; + commonParent = commonParent->parent(); + } + if (!commonParent) return changed; + while (i2 && i2->parent() != commonParent) + i2 = i2->parent(); + if (!i2) return changed; + + TreeMapItemList* list = commonParent->children(); + if (!list) return changed; + + TreeMapItem* i = list->first(); + bool between = false; + while (i) { + if (between) { + if (i==i1 || i==i2) break; + changed2 = setTmpSelected(i, selected); + if (changed2) changed = changed2->commonParent(changed); + } + else if (i==i1 || i==i2) + between = true; + i = list->next(); + } + + return changed; +} + +void TreeMapWidget::contextMenuEvent( QContextMenuEvent* e ) +{ + //kdDebug(90100) << "TreeMapWidget::contextMenuEvent" << endl; + + if ( receivers( SIGNAL(contextMenuRequested(TreeMapItem*, const QPoint &)) ) ) + e->accept(); + + if ( e->reason() == QContextMenuEvent::Keyboard ) { + QRect r = (_current) ? _current->itemRect() : _base->itemRect(); + QPoint p = QPoint(r.left() + r.width()/2, r.top() + r.height()/2); + emit contextMenuRequested(_current, p); + } + else { + TreeMapItem* i = item(e->x(), e->y()); + emit contextMenuRequested(i, e->pos()); + } +} + + +void TreeMapWidget::mousePressEvent( QMouseEvent* e ) +{ + //kdDebug(90100) << "TreeMapWidget::mousePressEvent" << endl; + + _oldCurrent = _current; + + TreeMapItem* i = item(e->x(), e->y()); + + _pressed = i; + + _inShiftDrag = e->state() & ShiftButton; + _inControlDrag = e->state() & ControlButton; + _lastOver = _pressed; + + TreeMapItem* changed = 0; + TreeMapItem* item = possibleSelection(_pressed); + + switch(_selectionMode) { + case Single: + changed = setTmpSelected(item, true); + break; + case Multi: + changed = setTmpSelected(item, !isTmpSelected(item)); + break; + case Extended: + if (_inControlDrag) + changed = setTmpSelected(item, !isTmpSelected(item)); + else if (_inShiftDrag) { + TreeMapItem* sCurrent = possibleSelection(_current); + changed = setTmpRangeSelection(sCurrent, item, + !isTmpSelected(item)); + } + else { + _selectionMode = Single; + changed = setTmpSelected(item, true); + _selectionMode = Extended; + } + break; + default: + break; + } + + // item under mouse always selected on right button press + if (e->button() == RightButton) { + TreeMapItem* changed2 = setTmpSelected(item, true); + if (changed2) changed = changed2->commonParent(changed); + } + + setCurrent(_pressed); + + if (changed) + redraw(changed); + + if (e->button() == RightButton) { + + // emit selection change + if (! (_tmpSelection == _selection)) { + _selection = _tmpSelection; + if (_selectionMode == Single) + emit selectionChanged(_lastOver); + emit selectionChanged(); + } + _pressed = 0; + _lastOver = 0; + emit rightButtonPressed(i, e->pos()); + } +} + +void TreeMapWidget::mouseMoveEvent( QMouseEvent* e ) +{ + //kdDebug(90100) << "TreeMapWidget::mouseMoveEvent" << endl; + + if (!_pressed) return; + TreeMapItem* over = item(e->x(), e->y()); + if (_lastOver == over) return; + + setCurrent(over); + if (over == 0) { + _lastOver = 0; + return; + } + + TreeMapItem* changed = 0; + TreeMapItem* item = possibleSelection(over); + + switch(_selectionMode) { + case Single: + changed = setTmpSelected(item, true); + break; + case Multi: + changed = setTmpSelected(item, !isTmpSelected(item)); + break; + case Extended: + if (_inControlDrag) + changed = setTmpSelected(item, !isTmpSelected(item)); + else { + TreeMapItem* sLast = possibleSelection(_lastOver); + changed = setTmpRangeSelection(sLast, item, true); + } + break; + + default: + break; + } + + _lastOver = over; + + if (changed) + redraw(changed); +} + +void TreeMapWidget::mouseReleaseEvent( QMouseEvent* ) +{ + //kdDebug(90100) << "TreeMapWidget::mouseReleaseEvent" << endl; + + if (!_pressed) return; + + if (!_lastOver) { + // take back + setCurrent(_oldCurrent); + TreeMapItem* changed = diff(_tmpSelection, _selection).commonParent(); + _tmpSelection = _selection; + if (changed) + redraw(changed); + } + else { + if (! (_tmpSelection == _selection)) { + _selection = _tmpSelection; + if (_selectionMode == Single) + emit selectionChanged(_lastOver); + emit selectionChanged(); + } + if (!_inControlDrag && !_inShiftDrag && (_pressed == _lastOver)) + emit clicked(_lastOver); + } + + _pressed = 0; + _lastOver = 0; +} + + +void TreeMapWidget::mouseDoubleClickEvent( QMouseEvent* e ) +{ + TreeMapItem* over = item(e->x(), e->y()); + + emit doubleClicked(over); +} + + +/* returns -1 if nothing visible found */ +int nextVisible(TreeMapItem* i) +{ + TreeMapItem* p = i->parent(); + if (!p || p->itemRect().isEmpty()) return -1; + + int idx = p->children()->findRef(i); + if (idx<0) return -1; + + while (idx < (int)p->children()->count()-1) { + idx++; + QRect r = p->children()->at(idx)->itemRect(); + if (r.width()>1 && r.height()>1) + return idx; + } + return -1; +} + +/* returns -1 if nothing visible found */ +int prevVisible(TreeMapItem* i) +{ + TreeMapItem* p = i->parent(); + if (!p || p->itemRect().isEmpty()) return -1; + + int idx = p->children()->findRef(i); + if (idx<0) return -1; + + while (idx > 0) { + idx--; + QRect r = p->children()->at(idx)->itemRect(); + if (r.width()>1 && r.height()>1) + return idx; + } + return -1; +} + + + + +void TreeMapWidget::keyPressEvent( QKeyEvent* e ) +{ + if (e->key() == Key_Escape && _pressed) { + + // take back + if (_oldCurrent != _lastOver) + setCurrent(_oldCurrent); + if (! (_tmpSelection == _selection)) { + TreeMapItem* changed = diff(_tmpSelection, _selection).commonParent(); + _tmpSelection = _selection; + if (changed) + redraw(changed); + } + _pressed = 0; + _lastOver = 0; + } + + if ((e->key() == Key_Space) || + (e->key() == Key_Return)) { + + switch(_selectionMode) { + case NoSelection: + break; + case Single: + setSelected(_current, true); + break; + case Multi: + setSelected(_current, !isSelected(_current)); + break; + case Extended: + if ((e->state() & ControlButton) || (e->state() & ShiftButton)) + setSelected(_current, !isSelected(_current)); + else { + _selectionMode = Single; + setSelected(_current, true); + _selectionMode = Extended; + } + } + + if (_current && (e->key() == Key_Return)) + emit returnPressed(_current); + + return; + } + + if (!_current) { + if (e->key() == Key_Down) { + setCurrent(_base, true); + } + return; + } + + TreeMapItem* old = _current, *newItem; + TreeMapItem* p = _current->parent(); + + bool goBack; + if (_current->sorting(&goBack) == -1) { + // noSorting + goBack = false; + } + + + if ((e->key() == Key_Backspace) || + (e->key() == Key_Up)) { + newItem = visibleItem(p); + setCurrent(newItem, true); + } + else if (e->key() == Key_Left) { + int newIdx = goBack ? nextVisible(_current) : prevVisible(_current); + if (p && newIdx>=0) { + p->setIndex(newIdx); + setCurrent(p->children()->at(newIdx), true); + } + } + else if (e->key() == Key_Right) { + int newIdx = goBack ? prevVisible(_current) : nextVisible(_current); + if (p && newIdx>=0) { + p->setIndex(newIdx); + setCurrent(p->children()->at(newIdx), true); + } + } + else if (e->key() == Key_Down) { + if (_current->children() && _current->children()->count()>0) { + int newIdx = _current->index(); + if (newIdx<0) + newIdx = goBack ? (_current->children()->count()-1) : 0; + if (newIdx>=(int)_current->children()->count()) + newIdx = _current->children()->count()-1; + newItem = visibleItem(_current->children()->at(newIdx)); + setCurrent(newItem, true); + } + } + + if (old == _current) return; + if (! (e->state() & ControlButton)) return; + if (! (e->state() & ShiftButton)) return; + + switch(_selectionMode) { + case NoSelection: + break; + case Single: + setSelected(_current, true); + break; + case Multi: + setSelected(_current, !isSelected(_current)); + break; + case Extended: + if (e->state() & ControlButton) + setSelected(_current, !isSelected(_current)); + else + setSelected(_current, isSelected(old)); + } +} + +void TreeMapWidget::fontChange( const QFont& ) +{ + redraw(); +} + + +void TreeMapWidget::resizeEvent( QResizeEvent * ) +{ + // this automatically redraws (as size is changed) + drawTreeMap(); +} + +void TreeMapWidget::paintEvent( QPaintEvent * ) +{ + drawTreeMap(); +} + +void TreeMapWidget::showEvent( QShowEvent * ) +{ + // refresh only if needed + drawTreeMap(); +} + +// Updates screen from shadow buffer, +// but redraws before if needed +void TreeMapWidget::drawTreeMap() +{ + // no need to draw if hidden + if (!isVisible()) return; + + if (_pixmap.size() != size()) + _needsRefresh = _base; + + if (_needsRefresh) { + + if (DEBUG_DRAWING) + kdDebug(90100) << "Redrawing " << _needsRefresh->path(0).join("/") << endl; + + if (_needsRefresh == _base) { + // redraw whole widget + _pixmap = QPixmap(size()); + _pixmap.fill(backgroundColor()); + } + QPainter p(&_pixmap); + if (_needsRefresh == _base) { + p.setPen(black); + p.drawRect(QRect(2, 2, QWidget::width()-4, QWidget::height()-4)); + _base->setItemRect(QRect(3, 3, QWidget::width()-6, QWidget::height()-6)); + } + else { + // only subitem + if (!_needsRefresh->itemRect().isValid()) return; + } + + // reset cached font object; it could have been changed + _font = font(); + _fontHeight = fontMetrics().height(); + + drawItems(&p, _needsRefresh); + _needsRefresh = 0; + } + + bitBlt( this, 0, 0, &_pixmap, 0, 0, + QWidget::width(), QWidget::height(), CopyROP, true); + + if (hasFocus()) { + QPainter p(this); + style().drawPrimitive( QStyle::PE_FocusRect, &p, + QRect(0, 0, QWidget::width(), QWidget::height()), + colorGroup() ); + } +} + + + +void TreeMapWidget::redraw(TreeMapItem* i) +{ + if (!i) return; + + if (!_needsRefresh) + _needsRefresh = i; + else { + if (!i->isChildOf(_needsRefresh)) + _needsRefresh = _needsRefresh->commonParent(i); + } + + if (isVisible()) { + // delayed drawing if we have multiple redraw requests + update(); + } +} + +void TreeMapWidget::drawItem(QPainter* p, + TreeMapItem* item) +{ + bool isSelected = false; + TreeMapItem* i; + + if (_markNo>0) { + for(i = item;i;i=i->parent()) + if (i->isMarked(_markNo)) break; + + isSelected = (i!=0); + } + else { + for (i=_tmpSelection.first();i;i=_tmpSelection.next()) + if (item->isChildOf(i)) break; + + isSelected = (i!=0); + } + + bool isCurrent = _current && item->isChildOf(_current); + int dd = item->depth(); + if (isTransparent(dd)) return; + + RectDrawing d(item->itemRect()); + item->setSelected(isSelected); + item->setCurrent(isCurrent); + item->setShaded(_shading); + item->drawFrame(drawFrame(dd)); + d.drawBack(p, item); +} + + +bool TreeMapWidget::horizontal(TreeMapItem* i, const QRect& r) +{ + switch(i->splitMode()) { + case TreeMapItem::HAlternate: + return (i->depth()%2)==1; + case TreeMapItem::VAlternate: + return (i->depth()%2)==0; + case TreeMapItem::Horizontal: + return true; + case TreeMapItem::Vertical: + return false; + default: + return r.width() > r.height(); + } + return false; +} + + +/** + * Draw TreeMapItems recursive, starting from item + */ +void TreeMapWidget::drawItems(QPainter* p, + TreeMapItem* item) +{ + if (DEBUG_DRAWING) + kdDebug(90100) << "+drawItems(" << item->path(0).join("/") << ", " + << item->itemRect().x() << "/" << item->itemRect().y() + << "-" << item->itemRect().width() << "x" + << item->itemRect().height() << "), Val " << item->value() + << ", Sum " << item->sum() << endl; + + drawItem(p, item); + item->clearFreeRects(); + + QRect origRect = item->itemRect(); + int bw = item->borderWidth(); + QRect r = QRect(origRect.x()+bw, origRect.y()+bw, + origRect.width()-2*bw, origRect.height()-2*bw); + + TreeMapItemList* list = item->children(); + TreeMapItem* i; + + bool stopDrawing = false; + + // only subdivide if there are children + if (!list || list->count()==0) + stopDrawing = true; + + // only subdivide if there is enough space + if (!stopDrawing && (r.width()<=0 || r.height()<=0)) + stopDrawing = true; + + // stop drawing if maximum depth is reached + if (!stopDrawing && + (_maxDrawingDepth>=0 && item->depth()>=_maxDrawingDepth)) + stopDrawing = true; + + // stop drawing if stopAtText is reached + if (!stopDrawing) + for (int no=0;no<(int)_attr.size();no++) { + QString stopAt = fieldStop(no); + if (!stopAt.isEmpty() && (item->text(no) == stopAt)) { + stopDrawing = true; + break; + } + } + + // area size is checked later... +#if 0 + // stop drawing if minimal area size is reached + if (!stopDrawing && + (_minimalArea > 0) && + (r.width() * r.height() < _minimalArea)) stopDrawing = true; +#endif + + if (stopDrawing) { + if (list) { + // invalidate rects + for (i=list->first();i;i=list->next()) + i->clearItemRect(); + } + // tooltip apears on whole item rect + item->addFreeRect(item->itemRect()); + + // if we have space for text... + if ((r.height() < _fontHeight) || (r.width() < _fontHeight)) return; + + RectDrawing d(r); + item->setRotated(_allowRotation && (r.height() > r.width())); + for (int no=0;no<(int)_attr.size();no++) { + if (!fieldVisible(no)) continue; + d.drawField(p, no, item); + } + r = d.remainingRect(item); + + if (DEBUG_DRAWING) + kdDebug(90100) << "-drawItems(" << item->path(0).join("/") << ")" << endl; + return; + } + + double user_sum, child_sum, self; + + // user supplied sum + user_sum = item->sum(); + + // own sum + child_sum = 0; + for (i=list->first();i;i=list->next()) { + child_sum += i->value(); + if (DEBUG_DRAWING) + kdDebug(90100) << " child: " << i->text(0) << ", value " + << i->value() << endl; + } + + QRect orig = r; + + // if we have space for text... + if ((r.height() >= _fontHeight) && (r.width() >= _fontHeight)) { + + RectDrawing d(r); + item->setRotated(_allowRotation && (r.height() > r.width())); + for (int no=0;no<(int)_attr.size();no++) { + if (!fieldVisible(no)) continue; + if (!fieldForced(no)) continue; + d.drawField(p, no, item); + } + r = d.remainingRect(item); + } + + if (orig.x() == r.x()) { + // Strings on top + item->addFreeRect(QRect(orig.x(), orig.y(), + orig.width(), orig.height()-r.height())); + } + else { + // Strings on the left + item->addFreeRect(QRect(orig.x(), orig.y(), + orig.width()-r.width(), orig.height())); + } + + if (user_sum == 0) { + // user didn't supply any sum + user_sum = child_sum; + self = 0; + } + else { + self = user_sum - child_sum; + + if (user_sum < child_sum) { + //kdDebug(90100) << "TreeMWidget " << + // item->path() << ": User sum " << user_sum << " < Child Items sum " << child_sum << endl; + + // invalid user supplied sum: ignore and use calculate sum + user_sum = child_sum; + self = 0.0; + } + else { + // Try to put the border waste in self + // percent of wasted space on border... + float borderArea = origRect.width() * origRect.height(); + borderArea = (borderArea - r.width()*r.height())/borderArea; + unsigned borderValue = (unsigned)(borderArea * user_sum); + + if (borderValue > self) { + if (_skipIncorrectBorder) { + r = origRect; + // should add my self to nested self and set my self =0 + } + else + self = 0.0; + } + else + self -= borderValue; + + user_sum = child_sum + self; + } + } + + bool rotate = (_allowRotation && (r.height() > r.width())); + int self_length = (int)( ((rotate) ? r.width() : r.height()) * + self / user_sum + .5); + if (self_length > 0) { + // take space for self cost + QRect sr = r; + if (rotate) { + sr.setWidth( self_length ); + r.setRect(r.x()+sr.width(), r.y(), r.width()-sr.width(), r.height()); + } + else { + sr.setHeight( self_length ); + r.setRect(r.x(), r.y()+sr.height(), r.width(), r.height()-sr.height()); + } + + // set selfRect (not occupied by children) for tooltip + item->addFreeRect(sr); + + if (0) kdDebug(90100) << "Item " << item->path(0).join("/") << ": SelfR " + << sr.x() << "/" << sr.y() << "-" << sr.width() + << "/" << sr.height() << ", self " << self << "/" + << user_sum << endl; + + if ((sr.height() >= _fontHeight) && (sr.width() >= _fontHeight)) { + + RectDrawing d(sr); + item->setRotated(_allowRotation && (r.height() > r.width())); + for (int no=0;no<(int)_attr.size();no++) { + if (!fieldVisible(no)) continue; + if (fieldForced(no)) continue; + d.drawField(p, no, item); + } + } + + user_sum -= self; + } + + bool goBack; + if (item->sorting(&goBack) == -1) { + // noSorting + goBack = false; + } + + TreeMapItemListIterator it(*list); + if (goBack) it.toLast(); + + if (item->splitMode() == TreeMapItem::Columns) { + int len = list->count(); + bool drawDetails = true; + + while (len>0 && user_sum>0) { + TreeMapItemListIterator first = it; + double valSum = 0; + int lenLeft = len; + int columns = (int)(sqrt((double)len * r.width()/r.height())+.5); + if (columns==0) columns = 1; //should never be needed + + while (lenLeft>0 && ((double)valSum*(len-lenLeft) < + (double)len*user_sum/columns/columns)) { + valSum += it.current()->value(); + if (goBack) --it; else ++it; + lenLeft--; + } + + // we always split horizontally + int nextPos = (int)((double)r.width() * valSum / user_sum); + QRect firstRect = QRect(r.x(), r.y(), nextPos, r.height()); + + if (nextPos < _visibleWidth) { + if (item->sorting(0) == -1) { + // fill current rect with hash pattern + drawFill(item, p, firstRect); + } + else { + // fill rest with hash pattern + drawFill(item, p, r, first, len, goBack); + break; + } + } + else { + drawDetails = drawItemArray(p, item, firstRect, + valSum, first, len-lenLeft, goBack); + } + r.setRect(r.x()+nextPos, r.y(), r.width()-nextPos, r.height()); + user_sum -= valSum; + len = lenLeft; + + if (!drawDetails) { + if (item->sorting(0) == -1) + drawDetails = true; + else { + drawFill(item, p, r, it, len, goBack); + break; + } + } + } + } + else if (item->splitMode() == TreeMapItem::Rows) { + int len = list->count(); + bool drawDetails = true; + + while (len>0 && user_sum>0) { + TreeMapItemListIterator first = it; + double valSum = 0; + int lenLeft = len; + int rows = (int)(sqrt((double)len * r.height()/r.width())+.5); + if (rows==0) rows = 1; //should never be needed + + while (lenLeft>0 && ((double)valSum*(len-lenLeft) < + (double)len*user_sum/rows/rows)) { + valSum += it.current()->value(); + if (goBack) --it; else ++it; + lenLeft--; + } + + // we always split horizontally + int nextPos = (int)((double)r.height() * valSum / user_sum); + QRect firstRect = QRect(r.x(), r.y(), r.width(), nextPos); + + if (nextPos < _visibleWidth) { + if (item->sorting(0) == -1) { + drawFill(item, p, firstRect); + } + else { + drawFill(item, p, r, first, len, goBack); + break; + } + } + else { + drawDetails = drawItemArray(p, item, firstRect, + valSum, first, len-lenLeft, goBack); + } + r.setRect(r.x(), r.y()+nextPos, r.width(), r.height()-nextPos); + user_sum -= valSum; + len = lenLeft; + + if (!drawDetails) { + if (item->sorting(0) == -1) + drawDetails = true; + else { + drawFill(item, p, r, it, len, goBack); + break; + } + } + } + } + else + drawItemArray(p, item, r, user_sum, it, list->count(), goBack); + + if (DEBUG_DRAWING) + kdDebug(90100) << "-drawItems(" << item->path(0).join("/") << ")" << endl; +} + +// fills area with a pattern if to small to draw children +void TreeMapWidget::drawFill(TreeMapItem* i, QPainter* p, QRect& r) +{ + p->setBrush(Qt::Dense4Pattern); + p->setPen(Qt::NoPen); + p->drawRect(r); + i->addFreeRect(r); +} + +// fills area with a pattern if to small to draw children +void TreeMapWidget::drawFill(TreeMapItem* i, QPainter* p, QRect& r, + TreeMapItemListIterator it, int len, bool goBack) +{ + if (DEBUG_DRAWING) + kdDebug(90100) << " +drawFill(" << r.x() << "/" << r.y() + << "-" << r.width() << "x" << r.height() + << ", len " << len << ")" << endl; + + p->setBrush(Qt::Dense4Pattern); + p->setPen(Qt::NoPen); + p->drawRect(r); + i->addFreeRect(r); + + // reset rects + while (len>0 && it.current()) { + + if (DEBUG_DRAWING) + kdDebug(90100) << " Reset Rect " << (*it)->path(0).join("/") << endl; + + (*it)->clearItemRect(); + if (goBack) --it; else ++it; + len--; + } + if (DEBUG_DRAWING) + kdDebug(90100) << " -drawFill(" << r.x() << "/" << r.y() + << "-" << r.width() << "x" << r.height() + << ", len " << len << ")" << endl; +} + +// returns false if rect gets to small +bool TreeMapWidget::drawItemArray(QPainter* p, TreeMapItem* item, + QRect& r, double user_sum, + TreeMapItemListIterator it, int len, + bool goBack) +{ + if (user_sum == 0) return false; + + static bool b2t = true; + + // stop recursive bisection for small rectangles + if (((r.height() < _visibleWidth) && + (r.width() < _visibleWidth)) || + ((_minimalArea > 0) && + (r.width() * r.height() < _minimalArea))) { + + drawFill(item, p, r, it, len, goBack); + return false; + } + + if (DEBUG_DRAWING) + kdDebug(90100) << " +drawItemArray(" << item->path(0).join("/") + << ", " << r.x() << "/" << r.y() << "-" << r.width() + << "x" << r.height() << ")" << endl; + + if (len>2 && (item->splitMode() == TreeMapItem::Bisection)) { + + TreeMapItemListIterator first = it; + double valSum = 0; + int lenLeft = len; + //while (lenLeft>0 && valSumlen/2) { + valSum += it.current()->value(); + if (goBack) --it; else ++it; + lenLeft--; + } + + // draw first half... + bool drawOn; + + if (r.width() > r.height()) { + int halfPos = (int)((double)r.width() * valSum / user_sum); + QRect firstRect = QRect(r.x(), r.y(), halfPos, r.height()); + drawOn = drawItemArray(p, item, firstRect, + valSum, first, len-lenLeft, goBack); + r.setRect(r.x()+halfPos, r.y(), r.width()-halfPos, r.height()); + } + else { + int halfPos = (int)((double)r.height() * valSum / user_sum); + QRect firstRect = QRect(r.x(), r.y(), r.width(), halfPos); + drawOn = drawItemArray(p, item, firstRect, + valSum, first, len-lenLeft, goBack); + r.setRect(r.x(), r.y()+halfPos, r.width(), r.height()-halfPos); + } + + // if no sorting, don't stop drawing + if (item->sorting(0) == -1) drawOn = true; + + // second half + if (drawOn) + drawOn = drawItemArray(p, item, r, user_sum - valSum, + it, lenLeft, goBack); + else { + drawFill(item, p, r, it, len, goBack); + } + + if (DEBUG_DRAWING) + kdDebug(90100) << " -drawItemArray(" << item->path(0).join("/") + << ")" << endl; + + return drawOn; + } + + bool hor = horizontal(item,r); + + TreeMapItem* i; + while (len>0) { + i = it.current(); + if (user_sum <= 0) { + + if (DEBUG_DRAWING) + kdDebug(90100) << "drawItemArray: Reset " << i->path(0).join("/") << endl; + + i->clearItemRect(); + if (goBack) --it; else ++it; + len--; + continue; + } + + // stop drawing for small rectangles + if (((r.height() < _visibleWidth) && + (r.width() < _visibleWidth)) || + ((_minimalArea > 0) && + (r.width() * r.height() < _minimalArea))) { + + drawFill(item, p, r, it, len, goBack); + if (DEBUG_DRAWING) + kdDebug(90100) << " -drawItemArray(" << item->path(0).join("/") + << "): Stop" << endl; + return false; + } + + if (i->splitMode() == TreeMapItem::AlwaysBest) + hor = r.width() > r.height(); + + int lastPos = hor ? r.width() : r.height(); + double val = i->value(); + int nextPos = (user_sum <= 0.0) ? 0: (int)(lastPos * val / user_sum +.5); + if (nextPos>lastPos) nextPos = lastPos; + + if ((item->sorting(0) != -1) && (nextPos < _visibleWidth)) { + drawFill(item, p, r, it, len, goBack); + if (DEBUG_DRAWING) + kdDebug(90100) << " -drawItemArray(" << item->path(0).join("/") + << "): Stop" << endl; + return false; + } + + QRect currRect = r; + + if (hor) + currRect.setWidth(nextPos); + else { + if (b2t) + currRect.setRect(r.x(), r.bottom()-nextPos+1, r.width(), nextPos); + else + currRect.setHeight(nextPos); + } + + // don't draw very small rectangles: + if (nextPos >= _visibleWidth) { + i->setItemRect(currRect); + drawItems(p, i); + } + else { + i->clearItemRect(); + drawFill(item, p, currRect); + } + + // draw Separator + if (_drawSeparators && (nextPossetPen(black); + if (hor) { + if (r.top()<=r.bottom()) + p->drawLine(r.x() + nextPos, r.top(), r.x() + nextPos, r.bottom()); + } + else { + if (r.left()<=r.right()) + p->drawLine(r.left(), r.y() + nextPos, r.right(), r.y() + nextPos); + } + nextPos++; + } + + if (hor) + r.setRect(r.x() + nextPos, r.y(), lastPos-nextPos, r.height()); + else { + if (b2t) + r.setRect(r.x(), r.y(), r.width(), lastPos-nextPos); + else + r.setRect(r.x(), r.y() + nextPos, r.width(), lastPos-nextPos); + } + + user_sum -= val; + if (goBack) --it; else ++it; + len--; + } + + if (DEBUG_DRAWING) + kdDebug(90100) << " -drawItemArray(" << item->path(0).join("/") + << "): Continue" << endl; + + return true; +} + + +/*---------------------------------------------------------------- + * Popup menus for option setting + */ + +void TreeMapWidget::splitActivated(int id) +{ + if (id == _splitID) setSplitMode(TreeMapItem::Bisection); + else if (id == _splitID+1) setSplitMode(TreeMapItem::Columns); + else if (id == _splitID+2) setSplitMode(TreeMapItem::Rows); + else if (id == _splitID+3) setSplitMode(TreeMapItem::AlwaysBest); + else if (id == _splitID+4) setSplitMode(TreeMapItem::Best); + else if (id == _splitID+5) setSplitMode(TreeMapItem::VAlternate); + else if (id == _splitID+6) setSplitMode(TreeMapItem::HAlternate); + else if (id == _splitID+7) setSplitMode(TreeMapItem::Horizontal); + else if (id == _splitID+8) setSplitMode(TreeMapItem::Vertical); +} + + +void TreeMapWidget::addSplitDirectionItems(QPopupMenu* popup, int id) +{ + _splitID = id; + popup->setCheckable(true); + + connect(popup, SIGNAL(activated(int)), + this, SLOT(splitActivated(int))); + + popup->insertItem(i18n("Recursive Bisection"), id); + popup->insertItem(i18n("Columns"), id+1); + popup->insertItem(i18n("Rows"), id+2); + popup->insertItem(i18n("Always Best"), id+3); + popup->insertItem(i18n("Best"), id+4); + popup->insertItem(i18n("Alternate (V)"), id+5); + popup->insertItem(i18n("Alternate (H)"), id+6); + popup->insertItem(i18n("Horizontal"), id+7); + popup->insertItem(i18n("Vertical"), id+8); + + switch(splitMode()) { + case TreeMapItem::Bisection: popup->setItemChecked(id,true); break; + case TreeMapItem::Columns: popup->setItemChecked(id+1,true); break; + case TreeMapItem::Rows: popup->setItemChecked(id+2,true); break; + case TreeMapItem::AlwaysBest: popup->setItemChecked(id+3,true); break; + case TreeMapItem::Best: popup->setItemChecked(id+4,true); break; + case TreeMapItem::VAlternate: popup->setItemChecked(id+5,true); break; + case TreeMapItem::HAlternate: popup->setItemChecked(id+6,true); break; + case TreeMapItem::Horizontal: popup->setItemChecked(id+7,true); break; + case TreeMapItem::Vertical: popup->setItemChecked(id+8,true); break; + default: break; + } +} + +void TreeMapWidget::visualizationActivated(int id) +{ + if (id == _visID+2) setSkipIncorrectBorder(!skipIncorrectBorder()); + else if (id == _visID+3) setBorderWidth(0); + else if (id == _visID+4) setBorderWidth(1); + else if (id == _visID+5) setBorderWidth(2); + else if (id == _visID+6) setBorderWidth(3); + else if (id == _visID+10) setAllowRotation(!allowRotation()); + else if (id == _visID+11) setShadingEnabled(!isShadingEnabled()); + else if (id<_visID+19 || id>_visID+100) return; + + id -= 20+_visID; + int f = id/10; + if ((id%10) == 1) setFieldVisible(f, !fieldVisible(f)); + else if ((id%10) == 2) setFieldForced(f, !fieldForced(f)); + else if ((id%10) == 3) setFieldPosition(f, DrawParams::TopLeft); + else if ((id%10) == 4) setFieldPosition(f, DrawParams::TopCenter); + else if ((id%10) == 5) setFieldPosition(f, DrawParams::TopRight); + else if ((id%10) == 6) setFieldPosition(f, DrawParams::BottomLeft); + else if ((id%10) == 7) setFieldPosition(f, DrawParams::BottomCenter); + else if ((id%10) == 8) setFieldPosition(f, DrawParams::BottomRight); +} + +void TreeMapWidget::addVisualizationItems(QPopupMenu* popup, int id) +{ + _visID = id; + + popup->setCheckable(true); + + QPopupMenu* bpopup = new QPopupMenu(); + bpopup->setCheckable(true); + + connect(popup, SIGNAL(activated(int)), + this, SLOT(visualizationActivated(int))); + connect(bpopup, SIGNAL(activated(int)), + this, SLOT(visualizationActivated(int))); + + QPopupMenu* spopup = new QPopupMenu(); + addSplitDirectionItems(spopup, id+100); + popup->insertItem(i18n("Nesting"), spopup, id); + + popup->insertItem(i18n("Border"), bpopup, id+1); + bpopup->insertItem(i18n("Correct Borders Only"), id+2); + bpopup->insertSeparator(); + bpopup->insertItem(i18n("Width %1").arg(0), id+3); + bpopup->insertItem(i18n("Width %1").arg(1), id+4); + bpopup->insertItem(i18n("Width %1").arg(2), id+5); + bpopup->insertItem(i18n("Width %1").arg(3), id+6); + bpopup->setItemChecked(id+2, skipIncorrectBorder()); + bpopup->setItemChecked(id+3, borderWidth()==0); + bpopup->setItemChecked(id+4, borderWidth()==1); + bpopup->setItemChecked(id+5, borderWidth()==2); + bpopup->setItemChecked(id+6, borderWidth()==3); + + popup->insertItem(i18n("Allow Rotation"), id+10); + popup->setItemChecked(id+10,allowRotation()); + popup->insertItem(i18n("Shading"), id+11); + popup->setItemChecked(id+11,isShadingEnabled()); + + if (_attr.size() ==0) return; + + popup->insertSeparator(); + int f; + QPopupMenu* tpopup; + id += 20; + for (f=0;f<(int)_attr.size();f++, id+=10) { + tpopup = new QPopupMenu(); + tpopup->setCheckable(true); + popup->insertItem(_attr[f].type, tpopup, id); + tpopup->insertItem(i18n("Visible"), id+1); + tpopup->insertItem(i18n("Take Space From Children"), id+2); + tpopup->insertSeparator(); + tpopup->insertItem(i18n("Top Left"), id+3); + tpopup->insertItem(i18n("Top Center"), id+4); + tpopup->insertItem(i18n("Top Right"), id+5); + tpopup->insertItem(i18n("Bottom Left"), id+6); + tpopup->insertItem(i18n("Bottom Center"), id+7); + tpopup->insertItem(i18n("Bottom Right"), id+8); + + tpopup->setItemChecked(id+1,_attr[f].visible); + tpopup->setItemEnabled(id+2,_attr[f].visible); + tpopup->setItemEnabled(id+3,_attr[f].visible); + tpopup->setItemEnabled(id+4,_attr[f].visible); + tpopup->setItemEnabled(id+5,_attr[f].visible); + tpopup->setItemEnabled(id+6,_attr[f].visible); + tpopup->setItemEnabled(id+7,_attr[f].visible); + tpopup->setItemEnabled(id+8,_attr[f].visible); + tpopup->setItemChecked(id+2,_attr[f].forced); + tpopup->setItemChecked(id+3,_attr[f].pos == DrawParams::TopLeft); + tpopup->setItemChecked(id+4,_attr[f].pos == DrawParams::TopCenter); + tpopup->setItemChecked(id+5,_attr[f].pos == DrawParams::TopRight); + tpopup->setItemChecked(id+6,_attr[f].pos == DrawParams::BottomLeft); + tpopup->setItemChecked(id+7,_attr[f].pos == DrawParams::BottomCenter); + tpopup->setItemChecked(id+8,_attr[f].pos == DrawParams::BottomRight); + + connect(tpopup, SIGNAL(activated(int)), + this, SLOT(visualizationActivated(int))); + } +} + +void TreeMapWidget::selectionActivated(int id) +{ + TreeMapItem* i = _menuItem; + id -= _selectionID; + while (id>0 && i) { + i=i->parent(); + id--; + } + if (i) + setSelected(i, true); +} + +void TreeMapWidget::addSelectionItems(QPopupMenu* popup, + int id, TreeMapItem* i) +{ + if (!i) return; + + _selectionID = id; + _menuItem = i; + + connect(popup, SIGNAL(activated(int)), + this, SLOT(selectionActivated(int))); + + while (i) { + QString name = i->text(0); + if (name.isEmpty()) break; + popup->insertItem(i->text(0), id++); + i = i->parent(); + } +} + +void TreeMapWidget::fieldStopActivated(int id) +{ + if (id == _fieldStopID) setFieldStop(0, QString::null); + else { + TreeMapItem* i = _menuItem; + id -= _fieldStopID+1; + while (id>0 && i) { + i=i->parent(); + id--; + } + if (i) + setFieldStop(0, i->text(0)); + } +} + +void TreeMapWidget::addFieldStopItems(QPopupMenu* popup, + int id, TreeMapItem* i) +{ + _fieldStopID = id; + + connect(popup, SIGNAL(activated(int)), + this, SLOT(fieldStopActivated(int))); + + popup->insertItem(i18n("No %1 Limit").arg(fieldType(0)), id); + popup->setItemChecked(id, fieldStop(0).isEmpty()); + _menuItem = i; + bool foundFieldStop = false; + if (i) { + popup->insertSeparator(); + + while (i) { + id++; + QString name = i->text(0); + if (name.isEmpty()) break; + popup->insertItem(i->text(0), id); + if (fieldStop(0) == i->text(0)) { + popup->setItemChecked(id, true); + foundFieldStop = true; + } + i = i->parent(); + } + } + + if (!foundFieldStop && !fieldStop(0).isEmpty()) { + popup->insertSeparator(); + popup->insertItem(fieldStop(0), id+1); + popup->setItemChecked(id+1, true); + } +} + +void TreeMapWidget::areaStopActivated(int id) +{ + if (id == _areaStopID) setMinimalArea(-1); + else if (id == _areaStopID+1) { + int area = _menuItem ? (_menuItem->width() * _menuItem->height()) : -1; + setMinimalArea(area); + } + else if (id == _areaStopID+2) setMinimalArea(100); + else if (id == _areaStopID+3) setMinimalArea(400); + else if (id == _areaStopID+4) setMinimalArea(1000); + else if (id == _areaStopID+5) setMinimalArea(minimalArea()*2); + else if (id == _areaStopID+6) setMinimalArea(minimalArea()/2); +} + +void TreeMapWidget::addAreaStopItems(QPopupMenu* popup, + int id, TreeMapItem* i) +{ + _areaStopID = id; + _menuItem = i; + + connect(popup, SIGNAL(activated(int)), + this, SLOT(areaStopActivated(int))); + + bool foundArea = false; + + popup->insertItem(i18n("No Area Limit"), id); + popup->setItemChecked(id, minimalArea() == -1); + + if (i) { + int area = i->width() * i->height(); + popup->insertSeparator(); + popup->insertItem(i18n("Area of '%1' (%2)") + .arg(i->text(0)).arg(area), id+1); + if (area == minimalArea()) { + popup->setItemChecked(id+1, true); + foundArea = true; + } + } + + popup->insertSeparator(); + int area = 100, count; + for (count=0;count<3;count++) { + popup->insertItem(i18n("1 Pixel", "%n Pixels", area), id+2+count); + if (area == minimalArea()) { + popup->setItemChecked(id+2+count, true); + foundArea = true; + } + area = (area==100) ? 400 : (area==400) ? 1000 : 4000; + } + + if (minimalArea()>0) { + popup->insertSeparator(); + if (!foundArea) { + popup->insertItem(i18n("1 Pixel", "%n Pixels", minimalArea()), id+10); + popup->setItemChecked(id+10, true); + } + + popup->insertItem(i18n("Double Area Limit (to %1)") + .arg(minimalArea()*2), id+5); + popup->insertItem(i18n("Halve Area Limit (to %1)") + .arg(minimalArea()/2), id+6); + } +} + + +void TreeMapWidget::depthStopActivated(int id) +{ + if (id == _depthStopID) setMaxDrawingDepth(-1); + else if (id == _depthStopID+1) { + int d = _menuItem ? _menuItem->depth() : -1; + setMaxDrawingDepth(d); + } + else if (id == _depthStopID+2) setMaxDrawingDepth(maxDrawingDepth()-1); + else if (id == _depthStopID+3) setMaxDrawingDepth(maxDrawingDepth()+1); +} + +void TreeMapWidget::addDepthStopItems(QPopupMenu* popup, + int id, TreeMapItem* i) +{ + _depthStopID = id; + _menuItem = i; + + connect(popup, SIGNAL(activated(int)), + this, SLOT(depthStopActivated(int))); + + bool foundDepth = false; + + popup->insertItem(i18n("No Depth Limit"), id); + popup->setItemChecked(id, maxDrawingDepth() == -1); + + if (i) { + int d = i->depth(); + popup->insertSeparator(); + popup->insertItem(i18n("Depth of '%1' (%2)") + .arg(i->text(0)).arg(d), id+1); + if (d == maxDrawingDepth()) { + popup->setItemChecked(id+1, true); + foundDepth = true; + } + } + + if (maxDrawingDepth()>1) { + popup->insertSeparator(); + if (!foundDepth) { + popup->insertItem(i18n("Depth %1").arg(maxDrawingDepth()), id+10); + popup->setItemChecked(id+10, true); + } + + popup->insertItem(i18n("Decrement (to %1)") + .arg(maxDrawingDepth()-1), id+2); + popup->insertItem(i18n("Increment (to %1)") + .arg(maxDrawingDepth()+1), id+3); + } +} + + + +/*---------------------------------------------------------------- + * Option saving/restoring + */ + +void TreeMapWidget::saveOptions(KConfigGroup* config, QString prefix) +{ + config->writeEntry(prefix+"Nesting", splitModeString()); + config->writeEntry(prefix+"AllowRotation", allowRotation()); + config->writeEntry(prefix+"ShadingEnabled", isShadingEnabled()); + config->writeEntry(prefix+"OnlyCorrectBorder", skipIncorrectBorder()); + config->writeEntry(prefix+"BorderWidth", borderWidth()); + config->writeEntry(prefix+"MaxDepth", maxDrawingDepth()); + config->writeEntry(prefix+"MinimalArea", minimalArea()); + + int f, fCount = _attr.size(); + config->writeEntry(prefix+"FieldCount", fCount); + for (f=0;fwriteEntry(QString(prefix+"FieldVisible%1").arg(f), + _attr[f].visible); + config->writeEntry(QString(prefix+"FieldForced%1").arg(f), + _attr[f].forced); + config->writeEntry(QString(prefix+"FieldStop%1").arg(f), + _attr[f].stop); + config->writeEntry(QString(prefix+"FieldPosition%1").arg(f), + fieldPositionString(f)); + } +} + + +void TreeMapWidget::restoreOptions(KConfigGroup* config, QString prefix) +{ + bool enabled; + int num; + QString str; + + str = config->readEntry(prefix+"Nesting"); + if (!str.isEmpty()) setSplitMode(str); + + if (config->hasKey(prefix+"AllowRotation")) { + enabled = config->readBoolEntry(prefix+"AllowRotation", true); + setAllowRotation(enabled); + } + + if (config->hasKey(prefix+"ShadingEnabled")) { + enabled = config->readBoolEntry(prefix+"ShadingEnabled", true); + setShadingEnabled(enabled); + } + + if (config->hasKey(prefix+"OnlyCorrectBorder")) { + enabled = config->readBoolEntry(prefix+"OnlyCorrectBorder", false); + setSkipIncorrectBorder(enabled); + } + + num = config->readNumEntry(prefix+"BorderWidth", -2); + if (num!=-2) setBorderWidth(num); + + num = config->readNumEntry(prefix+"MaxDepth", -2); + if (num!=-2) setMaxDrawingDepth(num); + + num = config->readNumEntry(prefix+"MinimalArea", -2); + if (num!=-2) setMinimalArea(num); + + num = config->readNumEntry(prefix+"FieldCount", -2); + if (num<=0 || num>MAX_FIELD) return; + + int f; + for (f=0;fhasKey(str)) + setFieldVisible(f, config->readBoolEntry(str)); + + str = QString(prefix+"FieldForced%1").arg(f); + if (config->hasKey(str)) + setFieldForced(f, config->readBoolEntry(str)); + + str = config->readEntry(QString(prefix+"FieldStop%1").arg(f)); + setFieldStop(f, str); + + str = config->readEntry(QString(prefix+"FieldPosition%1").arg(f)); + if (!str.isEmpty()) setFieldPosition(f, str); + } +} + +#include "treemap.moc" diff --git a/kcachegrind/kcachegrind/treemap.h b/kcachegrind/kcachegrind/treemap.h new file mode 100644 index 00000000..4bb0b4cd --- /dev/null +++ b/kcachegrind/kcachegrind/treemap.h @@ -0,0 +1,758 @@ +/* This file is part of KCachegrind. + Copyright (C) 2002, 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * A Widget for visualizing hierarchical metrics as areas. + * The API is similar to QListView. + * + * This file defines the following classes: + * DrawParams, RectDrawing, TreeMapItem, TreeMapWidget + * + * DrawParams/RectDrawing allows reusing of TreeMap drawing + * functions in other widgets. + */ + +#ifndef TREEMAP_H +#define TREEMAP_H + +#include +#include +#include +#include +#include +#include +#include +#include + +class QPopupMenu; +class TreeMapTip; +class TreeMapWidget; +class TreeMapItem; +class TreeMapItemList; +class QString; + +class KConfigGroup; + + +/** + * Drawing parameters for an object. + * A Helper Interface for RectDrawing. + */ +class DrawParams +{ +public: + /** + * Positions for drawing into a rectangle. + * + * The specified position assumes no rotation. + * If there is more than one text for one position, it is put + * nearer to the center of the item. + * + * Drawing at top positions cuts free space from top, + * drawing at bottom positions cuts from bottom. + * Default usually gives positions clockwise according to field number. + */ + enum Position { TopLeft, TopCenter, TopRight, + BottomLeft, BottomCenter, BottomRight, + Default, Unknown}; + + // no constructor as this is an abstract class + virtual ~DrawParams() {} + + virtual QString text(int) const = 0; + virtual QPixmap pixmap(int) const = 0; + virtual Position position(int) const = 0; + // 0: no limit, negative: leave at least -maxLines() free + virtual int maxLines(int) const { return 0; } + virtual int fieldCount() const { return 0; } + + virtual QColor backColor() const { return Qt::white; } + virtual const QFont& font() const = 0; + + virtual bool selected() const { return false; } + virtual bool current() const { return false; } + virtual bool shaded() const { return true; } + virtual bool rotated() const { return false; } + virtual bool drawFrame() const { return true; } +}; + + +/* + * DrawParam with attributes stored + */ +class StoredDrawParams: public DrawParams +{ +public: + StoredDrawParams(); + StoredDrawParams(QColor c, + bool selected = false, bool current = false); + + // getters + QString text(int) const; + QPixmap pixmap(int) const; + Position position(int) const; + int maxLines(int) const; + int fieldCount() const { return _field.size(); } + + QColor backColor() const { return _backColor; } + bool selected() const { return _selected; } + bool current() const { return _current; } + bool shaded() const { return _shaded; } + bool rotated() const { return _rotated; } + bool drawFrame() const { return _drawFrame; } + + const QFont& font() const; + + // attribute setters + void setField(int f, const QString& t, QPixmap pm = QPixmap(), + Position p = Default, int maxLines = 0); + void setText(int f, const QString&); + void setPixmap(int f, const QPixmap&); + void setPosition(int f, Position); + void setMaxLines(int f, int); + void setBackColor(const QColor& c) { _backColor = c; } + void setSelected(bool b) { _selected = b; } + void setCurrent(bool b) { _current = b; } + void setShaded(bool b) { _shaded = b; } + void setRotated(bool b) { _rotated = b; } + void drawFrame(bool b) { _drawFrame = b; } + +protected: + QColor _backColor; + bool _selected :1; + bool _current :1; + bool _shaded :1; + bool _rotated :1; + bool _drawFrame :1; + +private: + // resize field array if needed to allow to access field + void ensureField(int f); + + struct Field { + QString text; + QPixmap pix; + Position pos; + int maxLines; + }; + + QValueVector _field; +}; + + +/* State for drawing on a rectangle. + * + * Following drawing functions are provided: + * - background drawing with shading and 3D frame + * - successive pixmap/text drawing at various positions with wrap-around + * optimized for minimal space usage (e.g. if a text is drawn at top right + * after text on top left, the same line is used if space allows) + * + */ +class RectDrawing +{ +public: + RectDrawing(QRect); + ~RectDrawing(); + + // The default DrawParams object used. + DrawParams* drawParams(); + // we take control over the given object (i.e. delete at destruction) + void setDrawParams(DrawParams*); + + // draw on a given QPainter, use this class as info provider per default + void drawBack(QPainter*, DrawParams* dp = 0); + /* Draw field at position() from pixmap()/text() with maxLines(). + * Returns true if something was drawn + */ + bool drawField(QPainter*, int f, DrawParams* dp = 0); + + // resets rectangle for free space + void setRect(QRect); + + // Returns the rectangle area still free of text/pixmaps after + // a number of drawText() calls. + QRect remainingRect(DrawParams* dp = 0); + +private: + int _usedTopLeft, _usedTopCenter, _usedTopRight; + int _usedBottomLeft, _usedBottomCenter, _usedBottomRight; + QRect _rect; + + // temporary + int _fontHeight; + QFontMetrics* _fm; + DrawParams* _dp; +}; + + +class TreeMapItemList: public QPtrList +{ +public: + TreeMapItem* commonParent(); +protected: + int compareItems ( Item item1, Item item2 ); +}; + +typedef QPtrListIterator TreeMapItemListIterator; + + +/** + * Base class of items in TreeMap. + * + * This class supports an arbitrary number of text() strings + * positioned counterclock-wise starting at TopLeft. Each item + * has its own static value(), sum() and sorting(). The + * splitMode() and borderWidth() is taken from a TreeMapWidget. + * + * If you want more flexibility, reimplement TreeMapItem and + * override the corresponding methods. For dynamic creation of child + * items on demand, reimplement children(). + */ +class TreeMapItem: public StoredDrawParams +{ +public: + + /** + * Split direction for nested areas: + * AlwaysBest: Choose split direction for every subitem according to + * longest side of rectangle left for drawing + * Best: Choose split direction for all subitems of an area + * depending on longest side + * HAlternate: Horizontal at top; alternate direction on depth step + * VAlternate: Vertical at top; alternate direction on depth step + * Horizontal: Always horizontal split direction + * Vertical: Always vertical split direction + */ + enum SplitMode { Bisection, Columns, Rows, + AlwaysBest, Best, + HAlternate, VAlternate, + Horizontal, Vertical }; + + TreeMapItem(TreeMapItem* parent = 0, double value = 1.0 ); + TreeMapItem(TreeMapItem* parent, double value, + QString text1, QString text2 = QString::null, + QString text3 = QString::null, QString text4 = QString::null); + virtual ~TreeMapItem(); + + bool isChildOf(TreeMapItem*); + + TreeMapItem* commonParent(TreeMapItem* item); + + // force a redraw of this item + void redraw(); + + // delete all children + void clear(); + + // force new child generation & refresh + void refresh(); + + // call in a reimplemented items() method to check if already called + // after a clear(), this will return false + bool initialized(); + + /** + * Adds an item to a parent. + * When no sorting is used, the item is appended (drawn at bottom). + * This is only needed if the parent was not already specified in the + * construction of the item. + */ + void addItem(TreeMapItem*); + + /** + * Returns a list of text strings of specified text number, + * from root up to this item. + */ + QStringList path(int) const; + + /** + * Depth of this item. This is the distance to root. + */ + int depth() const; + + /** + * Parent Item + */ + TreeMapItem* parent() const { return _parent; } + + /** + * Temporary rectangle used for drawing this item the last time. + * This is internally used to map from a point to an item. + */ + void setItemRect(const QRect& r) { _rect = r; } + void clearItemRect(); + const QRect& itemRect() const { return _rect; } + int width() const { return _rect.width(); } + int height() const { return _rect.height(); } + + /** + * Temporary rectangle list of free space of this item. + * Used internally to enable tooltip. + */ + void clearFreeRects(); + QPtrList* freeRects() const { return _freeRects; } + void addFreeRect(const QRect& r); + + /** + * Temporary child item index of the child that was current() recently. + */ + int index() const { return _index; } + void setIndex(int i) { _index = i; } + + + /** + * TreeMap widget this item is put in. + */ + TreeMapWidget* widget() const { return _widget; } + + void setParent(TreeMapItem* p); + void setWidget(TreeMapWidget* w) { _widget = w; } + void setSum(double s) { _sum = s; } + void setValue(double s) { _value = s; } + + virtual double sum() const; + virtual double value() const; + // replace "Default" position with setting from TreeMapWidget + virtual Position position(int) const; + virtual const QFont& font() const; + virtual bool isMarked(int) const; + + virtual int borderWidth() const; + + /** + * Returns the text number after that sorting is done or + * -1 for no sorting, -2 for value() sorting (default). + * If ascending != 0, a bool value is written at that location + * to indicate if sorting should be ascending. + */ + virtual int sorting(bool* ascending) const; + + /** + * Set the sorting for child drawing. + * + * Default is no sorting: = -1 + * For value() sorting, use = -2 + * + * For fast sorting, set this to -1 before child insertions and call + * again after inserting all children. + */ + void setSorting(int textNo, bool ascending = true); + + /** + * Resort according to the already set sorting. + * + * This has to be done if the sorting base changes (e.g. text or values + * change). If this is only true for the children of this item, you can + * set the recursive parameter to false. + */ + void resort(bool recursive = true); + + virtual SplitMode splitMode() const; + virtual int rtti() const; + // not const as this can create children on demand + virtual TreeMapItemList* children(); + +protected: + TreeMapItemList* _children; + double _sum, _value; + +private: + TreeMapWidget* _widget; + TreeMapItem* _parent; + + int _sortTextNo; + bool _sortAscending; + + // temporary layout + QRect _rect; + QPtrList* _freeRects; + int _depth; + + // temporary self value (when using level skipping) + double _unused_self; + + // index of last active subitem + int _index; +}; + + +/** + * Class for visualization of a metric of hierarchically + * nested items as 2D areas. + */ +class TreeMapWidget: public QWidget +{ + Q_OBJECT + +public: + + /** + * Same as in QListBox/QListView + */ + enum SelectionMode { Single, Multi, Extended, NoSelection }; + + /* The widget becomes owner of the base item */ + TreeMapWidget(TreeMapItem* base, QWidget* parent=0, const char* name=0); + ~TreeMapWidget(); + + /** + * Returns the TreeMapItem filling out the widget space + */ + TreeMapItem* base() const { return _base; } + + /** + * Returns a reference to the current widget font. + */ + const QFont& currentFont() const; + + /** + * Returns the area item at position x/y, independent from any + * maxSelectDepth setting. + */ + TreeMapItem* item(int x, int y) const; + + /** + * Returns the nearest item with a visible area; this + * can be the given item itself. + */ + TreeMapItem* visibleItem(TreeMapItem*) const; + + /** + * Returns the item possible for selection. this returns the + * given item itself or a parent thereof, + * depending on setting of maxSelectDepth(). + */ + TreeMapItem* possibleSelection(TreeMapItem*) const; + + /** + * Selects or unselects an item. + * In multiselection mode, the constrain that a selected item + * has no selected children or parents stays true. + */ + void setSelected(TreeMapItem*, bool selected = true); + + /** + * Switches on the marking . Marking 0 switches off marking. + * This is mutually exclusive to selection, and is automatically + * switched off when selection is changed (also by the user). + * Marking is visually the same as selection, and is based on + * TreeMapItem::isMarked(). + * This enables to programmatically show multiple selected items + * at once even in single selection mode. + */ + void setMarked(int markNo = 1, bool redraw = true); + + /** + * Clear selection of all selected items which are children of + * parent. When parent == 0, clears whole selection + * Returns true if selection changed. + */ + bool clearSelection(TreeMapItem* parent = 0); + + /** + * Selects or unselects items in a range. + * This is needed internally for Shift-Click in Extented mode. + * Range means for a hierarchical widget: + * - select/unselect i1 and i2 according selected + * - search common parent of i1 and i2, and select/unselect the + * range of direct children between but excluding the child + * leading to i1 and the child leading to i2. + */ + void setRangeSelection(TreeMapItem* i1, + TreeMapItem* i2, bool selected); + + /** + * Sets the current item. + * The current item is mainly used for keyboard navigation. + */ + void setCurrent(TreeMapItem*, bool kbd=false); + + /** + * Set the maximal depth a selected item can have. + * If you try to select a item with higher depth, the ancestor holding + * this condition is used. + * + * See also possibleSelection(). + */ + void setMaxSelectDepth(int d) { _maxSelectDepth = d; } + + + void setSelectionMode(SelectionMode m) { _selectionMode = m; } + + /** + * for setting/getting global split direction + */ + void setSplitMode(TreeMapItem::SplitMode m); + TreeMapItem::SplitMode splitMode() const; + // returns true if string was recognized + bool setSplitMode(QString); + QString splitModeString() const; + + + /* + * Shading of rectangles enabled ? + */ + void setShadingEnabled(bool s); + bool isShadingEnabled() const { return _shading; } + + /* Setting for a whole depth level: draw 3D frame (default) or solid */ + void drawFrame(int d, bool b); + bool drawFrame(int d) const { return (d<4)?_drawFrame[d]:true; } + + /* Setting for a whole depth level: draw items (default) or transparent */ + void setTransparent(int d, bool b); + bool isTransparent(int d) const { return (d<4)?_transparent[d]:false; } + + /** + * Items usually have a size proportional to their value(). + * With , you can give the minimum width + * of the resulting rectangle to still be drawn. + * For space not used because of to small items, you can specify + * with if the background should shine through or + * the space will be used to enlarge the next item to be drawn + * at this level. + */ + void setVisibleWidth(int width, bool reuseSpace = false); + + /** + * If a children value() is almost the parents sum(), + * it can happen that the border to be drawn for visibilty of + * nesting relations takes to much space, and the + * parent/child size relation can not be mapped to a correct + * area size relation. + * + * Either + * (1) Ignore the incorrect drawing, or + * (2) Skip drawing of the parent level alltogether. + */ + void setSkipIncorrectBorder(bool enable = true); + bool skipIncorrectBorder() const { return _skipIncorrectBorder; } + + /** + * Maximal nesting depth + */ + void setMaxDrawingDepth(int d); + int maxDrawingDepth() const { return _maxDrawingDepth; } + + /** + * Minimal area for rectangles to draw + */ + void setMinimalArea(int area); + int minimalArea() const { return _minimalArea; } + + /* defaults for text attributes */ + QString defaultFieldType(int) const; + QString defaultFieldStop(int) const; + bool defaultFieldVisible(int) const; + bool defaultFieldForced(int) const; + DrawParams::Position defaultFieldPosition(int) const; + + /** + * Set the type name of a field. + * This is important for the visualization menu generated + * with visualizationMenu() + */ + void setFieldType(int, QString); + QString fieldType(int) const; + + /** + * Stop drawing at item with name + */ + void setFieldStop(int, QString); + QString fieldStop(int) const; + + /** + * Should the text with number textNo be visible? + * This is only done if remaining space is enough to allow for + * proportional size constrains. + */ + void setFieldVisible(int, bool); + bool fieldVisible(int) const; + + /** + * Should the drawing of the name into the rectangle be forced? + * This enables drawing of the name before drawing subitems, and + * thus destroys proportional constrains. + */ + void setFieldForced(int, bool); + bool fieldForced(int) const; + + /** + * Set the field position in the area. See TreeMapItem::Position + */ + void setFieldPosition(int, DrawParams::Position); + DrawParams::Position fieldPosition(int) const; + void setFieldPosition(int, QString); + QString fieldPositionString(int) const; + + /** + * Do we allow the texts to be rotated by 90 degrees for better fitting? + */ + void setAllowRotation(bool); + bool allowRotation() const { return _allowRotation; } + + void setBorderWidth(int w); + int borderWidth() const { return _borderWidth; } + + /** + * Save/restore options. + */ + void saveOptions(KConfigGroup*, QString prefix = QString::null); + void restoreOptions(KConfigGroup*, QString prefix = QString::null); + + /** + * These functions populate given popup menus. + * The added items are already connected to handlers. + * + * The int is the menu id where to start for the items (100 IDs reserved). + */ + void addSplitDirectionItems(QPopupMenu*, int); + void addSelectionItems(QPopupMenu*, int, TreeMapItem*); + void addFieldStopItems(QPopupMenu*, int, TreeMapItem*); + void addAreaStopItems(QPopupMenu*, int, TreeMapItem*); + void addDepthStopItems(QPopupMenu*, int, TreeMapItem*); + void addVisualizationItems(QPopupMenu*, int); + + TreeMapWidget* widget() { return this; } + TreeMapItem* current() const { return _current; } + TreeMapItemList selection() const { return _selection; } + bool isSelected(TreeMapItem* i) const; + int maxSelectDepth() const { return _maxSelectDepth; } + SelectionMode selectionMode() const { return _selectionMode; } + + /** + * Return tooltip string to show for a item (can be rich text) + * Default implementation gives lines with "text0 (text1)" going to root. + */ + virtual QString tipString(TreeMapItem* i) const; + + /** + * Redraws an item with all children. + * This takes changed values(), sums(), colors() and text() into account. + */ + void redraw(TreeMapItem*); + void redraw() { redraw(_base); } + + /** + * Resort all TreeMapItems. See TreeMapItem::resort(). + */ + void resort() { _base->resort(true); } + + // internal + void drawTreeMap(); + + // used internally when items are destroyed + void deletingItem(TreeMapItem*); + +protected slots: + void splitActivated(int); + void selectionActivated(int); + void fieldStopActivated(int); + void areaStopActivated(int); + void depthStopActivated(int); + void visualizationActivated(int); + +signals: + void selectionChanged(); + void selectionChanged(TreeMapItem*); + + /** + * This signal is emitted if the current item changes. + * If the change is done because of keyboard navigation, + * the is set to true + */ + void currentChanged(TreeMapItem*, bool keyboard); + void clicked(TreeMapItem*); + void returnPressed(TreeMapItem*); + void doubleClicked(TreeMapItem*); + void rightButtonPressed(TreeMapItem*, const QPoint &); + void contextMenuRequested(TreeMapItem*, const QPoint &); + +protected: + void mousePressEvent( QMouseEvent * ); + void contextMenuEvent( QContextMenuEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseMoveEvent( QMouseEvent * ); + void mouseDoubleClickEvent( QMouseEvent * ); + void keyPressEvent( QKeyEvent* ); + void paintEvent( QPaintEvent * ); + void resizeEvent( QResizeEvent * ); + void showEvent( QShowEvent * ); + void fontChange( const QFont& ); + +private: + TreeMapItemList diff(TreeMapItemList&, TreeMapItemList&); + // returns true if selection changed + TreeMapItem* setTmpSelected(TreeMapItem*, bool selected = true); + TreeMapItem* setTmpRangeSelection(TreeMapItem* i1, + TreeMapItem* i2, bool selected); + bool isTmpSelected(TreeMapItem* i); + + void drawItem(QPainter* p, TreeMapItem*); + void drawItems(QPainter* p, TreeMapItem*); + bool horizontal(TreeMapItem* i, const QRect& r); + void drawFill(TreeMapItem*,QPainter* p, QRect& r); + void drawFill(TreeMapItem*,QPainter* p, QRect& r, + TreeMapItemListIterator it, int len, bool goBack); + bool drawItemArray(QPainter* p, TreeMapItem*, QRect& r, double, + TreeMapItemListIterator it, int len, bool); + bool resizeAttr(int); + + TreeMapItem* _base; + TreeMapItem *_current, *_pressed, *_lastOver, *_oldCurrent; + TreeMapTip* _tip; + int _maxSelectDepth, _maxDrawingDepth; + + // attributes for field, per textNo + struct FieldAttr { + QString type, stop; + bool visible, forced; + DrawParams::Position pos; + }; + QValueVector _attr; + + SelectionMode _selectionMode; + TreeMapItem::SplitMode _splitMode; + int _visibleWidth, _stopArea, _minimalArea, _borderWidth; + bool _reuseSpace, _skipIncorrectBorder, _drawSeparators, _shading; + bool _allowRotation; + bool _transparent[4], _drawFrame[4]; + TreeMapItem * _needsRefresh; + TreeMapItemList _selection; + int _markNo; + + // for the context menus: start IDs + int _splitID, _selectionID, _visID; + int _fieldStopID, _areaStopID, _depthStopID; + TreeMapItem* _menuItem; + + // temporary selection while dragging, used for drawing + // most of the time, _selection == _tmpSelection + TreeMapItemList _tmpSelection; + bool _inShiftDrag, _inControlDrag; + + // temporary widget font metrics while drawing + QFont _font; + int _fontHeight; + + // back buffer pixmap + QPixmap _pixmap; +}; + +#endif diff --git a/kcachegrind/kcachegrind/utils.cpp b/kcachegrind/kcachegrind/utils.cpp new file mode 100644 index 00000000..618fce7b --- /dev/null +++ b/kcachegrind/kcachegrind/utils.cpp @@ -0,0 +1,483 @@ +/* This file is part of KCachegrind. + Copyright (C) 2003 Josef Weidendorfer + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Utility classes for KCachegrind + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_MMAP +#include +#include +#endif + +#include +#include + +#include "utils.h" + + +// class FixString + +FixString::FixString(const char* str, int len) +{ + _str = str; + _len = len; +} + +bool FixString::stripFirst(char& c) +{ + if (!_len) { + c = 0; + return false; + } + + c = *_str; + _str++; + _len--; + return true; + } + +bool FixString::stripPrefix(const char* p) +{ + if (_len == 0) return false; + if (!p || (*p != *_str)) return false; + + const char* s = _str+1; + int l = _len-1; + p++; + while(*p) { + if (l==0) return false; + if (*s != *p) return false; + p++; + s++; + l--; + } + _str = s; + _len = l; + return true; +} + + +// this parses hexadecimal (with prefix '0x' too) +bool FixString::stripUInt(unsigned int& v, bool stripSpaces) +{ + if (_len==0) { + v = 0; + return false; + } + + char c = *_str; + if (c<'0' || c>'9') { + v = 0; + return false; + } + + v = c-'0'; + const char* s = _str+1; + int l = _len-1; + c = *s; + + if ((l>0) && (c == 'x') && (v==0)) { + // hexadecimal + s++; + c = *s; + l--; + + while(l>0) { + if (c>='0' && c<='9') + v = 16*v + (c-'0'); + else if (c>='a' && c<='f') + v = 16*v + 10 + (c-'a'); + else if (c>='A' && c<='F') + v = 16*v + 10 + (c-'A'); + else + break; + s++; + c = *s; + l--; + } + } + else { + // decimal + + while(l>0) { + if (c<'0' || c>'9') break; + v = 10*v + (c-'0'); + s++; + c = *s; + l--; + } + } + + if (stripSpaces) + while(l>0) { + if (c != ' ') break; + s++; + c = *s; + l--; + } + + _str = s; + _len = l; + return true; +} + + +void FixString::stripSurroundingSpaces() +{ + if (_len==0) return; + + // leading spaces + while((_len>0) && (*_str==' ')) { + _len--; + _str++; + } + + // trailing spaces + while((_len>0) && (_str[_len-1]==' ')) { + _len--; + } +} + +void FixString::stripSpaces() +{ + while((_len>0) && (*_str==' ')) { + _len--; + _str++; + } +} + +bool FixString::stripName(FixString& s) +{ + if (_len==0) return false; + + // first char has to be a letter or "_" + if (!QChar(*_str).isLetter() && (*_str != '_')) return false; + + int newLen = 1; + const char* newStr = _str; + + _str++; + _len--; + + while(_len>0) { + if (!QChar(*_str).isLetterOrNumber() + && (*_str != '_')) break; + + newLen++; + _str++; + _len--; + } + + s.set(newStr, newLen); + return true; +} + +FixString FixString::stripUntil(char c) +{ + if (_len == 0) return FixString(); + + const char* newStr = _str; + int newLen = 0; + + while(_len>0) { + if (*_str == c) { + _str++; + _len--; + break; + } + + _str++; + _len--; + newLen++; + } + return FixString(newStr, newLen); +} + +bool FixString::stripUInt64(uint64& v, bool stripSpaces) +{ + if (_len==0) { + v = 0; + return false; + } + + char c = *_str; + if (c<'0' || c>'9') { + v = 0; + return false; + } + + v = c-'0'; + const char* s = _str+1; + int l = _len-1; + c = *s; + + if ((l>0) && (c == 'x') && (v==0)) { + // hexadecimal + s++; + c = *s; + l--; + + while(l>0) { + if (c>='0' && c<='9') + v = 16*v + (c-'0'); + else if (c>='a' && c<='f') + v = 16*v + 10 + (c-'a'); + else if (c>='A' && c<='F') + v = 16*v + 10 + (c-'A'); + else + break; + s++; + c = *s; + l--; + } + } + else { + // decimal + while(l>0) { + if (c<'0' || c>'9') break; + v = 10*v + (c-'0'); + s++; + c = *s; + l--; + } + } + + if (stripSpaces) + while(l>0) { + if (c != ' ') break; + s++; + c = *s; + l--; + } + + _str = s; + _len = l; + return true; +} + + +bool FixString::stripInt64(int64& v, bool stripSpaces) +{ + if (_len==0) { + v = 0; + return false; + } + + char c = *_str; + if (c<'0' || c>'9') { + v = 0; + return false; + } + + v = c-'0'; + const char* s = _str+1; + int l = _len-1; + c = *s; + + if ((l>0) && (c == 'x') && (v==0)) { + // hexadecimal + s++; + c = *s; + l--; + + while(l>0) { + if (c>='0' && c<='9') + v = 16*v + (c-'0'); + else if (c>='a' && c<='f') + v = 16*v + 10 + (c-'a'); + else if (c>='A' && c<='F') + v = 16*v + 10 + (c-'A'); + else + break; + s++; + c = *s; + l--; + } + } + else { + // decimal + + while(l>0) { + if (c<'0' || c>'9') break; + v = 10*v + (c-'0'); + s++; + c = *s; + l--; + } + } + + if (stripSpaces) + while(l>0) { + if (c != ' ') break; + s++; + c = *s; + l--; + } + + _str = s; + _len = l; + return true; +} + + + +// class FixFile + +FixFile::FixFile(QFile* file) +{ + if (!file) { + _len = 0; + _currentLeft = 0; + _openError = true; + return; + } + + _filename = file->name(); + if (!file->isOpen() && !file->open( IO_ReadOnly ) ) { + qWarning( "%s: %s", (const char*) QFile::encodeName(_filename), + strerror( errno ) ); + _len = 0; + _currentLeft = 0; + _openError = true; + return; + } + + _openError = false; + _used_mmap = false; + +#ifdef HAVE_MMAP + char *addr = 0; + size_t len = file->size(); + if (len>0) addr = (char *) mmap( addr, len, + PROT_READ, MAP_PRIVATE, + file->handle(), 0 ); + if (addr && (addr != MAP_FAILED)) { + // mmap succeeded + _base = addr; + _len = len; + _used_mmap = true; + + if (0) qDebug("Mapped '%s'", _filename.ascii()); + } else { +#endif // HAVE_MMAP + // try reading the data into memory instead + _data = file->readAll(); + _base = _data.data(); + _len = _data.size(); +#ifdef HAVE_MMAP + } +#endif // HAVE_MMAP + + _current = _base; + _currentLeft = _len; +} + +FixFile::~FixFile() +{ + // if the file was read into _data, it will be deleted automatically + +#ifdef HAVE_MMAP + if (_used_mmap) { + if (0) qDebug("Unmapping '%s'", _filename.ascii()); + if (munmap(_base, _len) != 0) + qWarning( "munmap: %s", strerror( errno ) ); + } +#endif // HAVE_MMAP +} + +bool FixFile::nextLine(FixString& str) +{ + if (_currentLeft == 0) return false; + + unsigned left = _currentLeft; + char* current = _current; + + while(left>0) { + if (*current == 0 || *current == '\n') break; + current++; + left--; + } + + if (0) { + char tmp[200]; + int l = _currentLeft-left; + if (l>199) l = 199; + strncpy(tmp, _current, l); + tmp[l] = 0; + qDebug("[FixFile::nextLine] At %d, len %d: '%s'", + _current - _base, _currentLeft-left, tmp); + } + + str.set(_current, _currentLeft-left); + + if (*current == '\n') { + current++; + left--; + } + _current = current; + _currentLeft = left; + + return true; +} + +bool FixFile::setCurrent(unsigned pos) +{ + if (pos > _len) return false; + + _current = _base + pos; + _currentLeft = _len - pos; + return true; +} + + +#if 0 + +// class AppendList + + +AppendList::AppendList() +{ + _next = 0; + _current = 0; + _last = 0; + + _count = 0; + _currentIndex = 0; + _lastIndex = 0; + _autoDelete = false; +} + + +void AppendList::clear() +{ + int count = _count; + int i; + + if (count <= firstLen) { + if (_autoDelete) + for (i=0;i + + KCachegrind is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* + * Utility classes for KCachegrind + */ + +#ifndef UTILS_H +#define UTILS_H + +#include + +class QFile; + +typedef unsigned long long uint64; +typedef long long int64; + +/** + * A simple, constant string class + * + * For use with zero-copy strings from mapped files. + */ +class FixString { + + public: + // constructor for an invalid string + FixString() { _len = 0; _str = 0; } + + /** + * FixString never does a deep copy! You have to make sure that + * the string starting at the char pointer is valid trough the + * lifetime of FixString. + */ + FixString(const char*, int len); + + int len() { return _len; } + const char* ascii() { return _str; } + bool isEmpty() { return _len == 0; } + bool isValid() { return _str != 0; } + + // sets to first character and returns true if length >0 + bool first(char& c) + { if (_len==0) return false; c=_str[0]; return true; } + + void set(const char* s, int l) { _str=s; _len=l; } + bool stripFirst(char&); + bool stripPrefix(const char*); + + /** + * Strip leading and trailing spaces + */ + void stripSurroundingSpaces(); + + /** + * Strip leading spaces + */ + void stripSpaces(); + + /** + * Strip name: [A-Za-z_][0-9A_Za-z_]* + */ + bool stripName(FixString&); + + /** + * Strip string until char appears or end. Strips char, too. + */ + FixString stripUntil(char); + + bool stripUInt(uint&, bool stripSpaces = true); + bool stripUInt64(uint64&, bool stripSpaces = true); + bool stripInt64(int64&, bool stripSpaces = true); + + operator QString() const + { return QString::fromLatin1(_str,_len); } + + private: + const char* _str; + int _len; +}; + + +/** + * A class for fast line by line reading of a read-only ASCII file + */ +class FixFile { + + public: + FixFile(QFile*); + ~FixFile(); + + /** + * Read next line into . Returns false on error or EOF. + */ + bool nextLine(FixString& str); + bool exists() { return !_openError; } + unsigned len() { return _len; } + unsigned current() { return _current - _base; } + bool setCurrent(unsigned pos); + void rewind() { setCurrent(0); } + + private: + char *_base, *_current; + QByteArray _data; + unsigned _len, _currentLeft; + bool _used_mmap, _openError; + QString _filename; +}; + + +/** + * A list of pointers, only able to append items. + * Optimized for speed, not space. + */ +template +class AppendList { + + public: + AppendList(); + ~AppendList() { clear(); } + + void setAutoDelete(bool); + void clear(); + void append(const type*); + + unsigned count() const { return _count; } + unsigned containsRef(const type*) const; + + type* current(); + type* first(); + type* next(); + + private: + static const int firstLen = 8; + static const int maxLen = 256; + + struct AppendListChunk { + int size; + struct AppendListChunk* next; + type* data[1]; + }; + + struct AppendListChunk *_next, *_current, *_last; + int _count, _currentIndex, _lastIndex; + bool _autoDelete; + type* _first[firstLen]; +}; + + +#endif diff --git a/kcachegrind/kcachegrind/x-kcachegrind.desktop b/kcachegrind/kcachegrind/x-kcachegrind.desktop new file mode 100644 index 00000000..671f89ac --- /dev/null +++ b/kcachegrind/kcachegrind/x-kcachegrind.desktop @@ -0,0 +1,44 @@ +[Desktop Entry] +Comment=Cachegrind/Callgrind Profile Dump +Comment[ca]=Resultat del anàlisis de Cachegrind/Callgring +Comment[cs]=Data profilace Cachegrind/Callgrind +Comment[cy]=Tomen Proffil Cachegrind/Callgrind +Comment[da]=Cachegrind/Callgrind profile-dump +Comment[de]=Cachegrind/Callgrind Profil-Ausgabe +Comment[el]=Αποτύπωση προφίλ Cachegrind/Callgrind +Comment[es]=Resultado de análisis de Cachegrind/Callgring +Comment[et]=Cachegrind/Callgrind profileerimistõmmis +Comment[eu]=Cachegrind/Callgrind profil iraulketa +Comment[fa]=تخلیۀ Profile Cachegrind/Callgrind +Comment[fi]=Cachegrind/Callgrind-profiilivedos +Comment[fr]=Dépôt de profil Cachegrind / Callgrind +Comment[gl]=Resultado da análise de Cachegrind/Callgrind +Comment[hi]=केश-ग्रिंड/काल-ग्रिंड प्रोफ़ाइल डम्प +Comment[hu]=Cachegrind/Callgrind teljesítményprofil-fájl +Comment[is]=Niðurstaða afkastakönnunar á Cachegrind/Callgrind +Comment[it]=Dump del profilo di Cachegrind/Callgrind +Comment[ja]=Callgrind/Callgrind プロファイルダンプ +Comment[ka]=Cachegrind/Callgrind პროფილის დამპი +Comment[kk]=Cachegrind/Callgrind профилінің дампы +Comment[nds]=Cachegrind/Callgrind-Profilutgaav +Comment[ne]=Cachegrind/Callgrind प्रोफाइल डम्प +Comment[nl]=Cachegrind/Callgrind Profieldump +Comment[nn]=Cachegrind/Callgrind-profildump +Comment[pl]=Zrzut profilowania Cachegrind/Callgrind +Comment[pt]=Resultado da Análise do Cachegrind/Callgrind +Comment[pt_BR]=Depósito de Perfil Cachegrind/Callgrind +Comment[ru]=Дамп профилирования Cachegrind/Callgrind +Comment[sk]=Výpis volaní Cachegrind/Callgrind +Comment[sr]=Cachegrind-ов/Callgrind-ов избачај профила +Comment[sr@Latn]=Cachegrind-ov/Callgrind-ov izbačaj profila +Comment[sv]=Profileringsdump från Cachegrind/Callgrind +Comment[ta]=இடைமாற்றகட்டம்/ அழைப்பு கட்டம் விவரக்குறி திணிப்பு +Comment[tg]=Дампи профилкунии Cachegrind/Callgrind +Comment[uk]=Звалювання профілювання Cachegrind/Callgrind +Comment[zh_CN]=Cachegrind/Callgrind 配置文件转存 +Comment[zh_TW]=Cachegrind/Callgrind 分析資料傾印 +DefaultApp=kcachegrind +Icon=kcachegrind +Type=MimeType +MimeType=application/x-kcachegrind +Patterns=cachegrind.out*;callgrind.out* diff --git a/kcachegrind/pics/Makefile.am b/kcachegrind/pics/Makefile.am new file mode 100644 index 00000000..a899c7b2 --- /dev/null +++ b/kcachegrind/pics/Makefile.am @@ -0,0 +1,3 @@ +kcachegrindicondir = $(kde_datadir)/kcachegrind/icons +kcachegrindicon_ICON = AUTO +SUBDIRS = hicolor diff --git a/kcachegrind/pics/hicolor/Makefile.am b/kcachegrind/pics/hicolor/Makefile.am new file mode 100644 index 00000000..be8b0c26 --- /dev/null +++ b/kcachegrind/pics/hicolor/Makefile.am @@ -0,0 +1,2 @@ +kcachegrindicondir = $(kde_datadir)/kcachegrind/icons +kcachegrindicon_ICON = AUTO diff --git a/kcachegrind/pics/hicolor/hi16-action-fromrec.png b/kcachegrind/pics/hicolor/hi16-action-fromrec.png new file mode 100644 index 00000000..a5cb430d Binary files /dev/null and b/kcachegrind/pics/hicolor/hi16-action-fromrec.png differ diff --git a/kcachegrind/pics/hicolor/hi16-action-percent.png b/kcachegrind/pics/hicolor/hi16-action-percent.png new file mode 100644 index 00000000..7a4ba47e Binary files /dev/null and b/kcachegrind/pics/hicolor/hi16-action-percent.png differ diff --git a/kcachegrind/pics/hicolor/hi16-action-recrec.png b/kcachegrind/pics/hicolor/hi16-action-recrec.png new file mode 100644 index 00000000..ec11bfa4 Binary files /dev/null and b/kcachegrind/pics/hicolor/hi16-action-recrec.png differ diff --git a/kcachegrind/pics/hicolor/hi16-action-torec.png b/kcachegrind/pics/hicolor/hi16-action-torec.png new file mode 100644 index 00000000..c092c018 Binary files /dev/null and b/kcachegrind/pics/hicolor/hi16-action-torec.png differ diff --git a/kcachegrind/pics/hicolor/hi22-action-percent.png b/kcachegrind/pics/hicolor/hi22-action-percent.png new file mode 100644 index 00000000..c64a3785 Binary files /dev/null and b/kcachegrind/pics/hicolor/hi22-action-percent.png differ diff --git a/kcachegrind/pics/hicolor/hi32-action-percent.png b/kcachegrind/pics/hicolor/hi32-action-percent.png new file mode 100644 index 00000000..e876c30d Binary files /dev/null and b/kcachegrind/pics/hicolor/hi32-action-percent.png differ diff --git a/kcachegrind/tests/cg-badcompression1 b/kcachegrind/tests/cg-badcompression1 new file mode 100644 index 00000000..6076bf92 --- /dev/null +++ b/kcachegrind/tests/cg-badcompression1 @@ -0,0 +1,17 @@ +# Test with bad callgrind format +# Expected: +# :13 - Redefinition of compressed file index 2 (was 'file1.c') to '' +# :14 - Redefinition of compressed function index 1 (was 'main') to 'main2' +# :16 - Undefined compressed function index 2 +# :16 - Invalid function, setting to unknown + +events: Ir + +fl=(2) file1.c +fn=(1) main +10 9 +fl=(2 ) +fn=(1) main2 +11 1 +fn=(2) +12 1 diff --git a/kcachegrind/tests/cg-badcostline1 b/kcachegrind/tests/cg-badcostline1 new file mode 100644 index 00000000..224ff670 --- /dev/null +++ b/kcachegrind/tests/cg-badcostline1 @@ -0,0 +1,11 @@ +# Test with bad callgrind format +# Expected: +# :10 - ignored garbage at end of cost line ('30') +# :11 - ignored garbage at end of cost line ('hello') + +events: Ir + +fn=main +10 20 30 +11 hello +12 10 diff --git a/kcachegrind/tests/cg-badposition b/kcachegrind/tests/cg-badposition new file mode 100644 index 00000000..1be582c7 --- /dev/null +++ b/kcachegrind/tests/cg-badposition @@ -0,0 +1,15 @@ +# Test with bad callgrind format +# Expected: +# :11 - Negative line number -20 +# :12 - Garbage at end of cost line ('a 21') +# :13 - Negative line number -91 +# :15 - Invalid line 'aa 40' + +events: Ir + +fn=main +-20 1 +9a 21 +-100 20 +0x9a 30 +aa 40 diff --git a/kcachegrind/version.h.in b/kcachegrind/version.h.in new file mode 100644 index 00000000..21f758c7 --- /dev/null +++ b/kcachegrind/version.h.in @@ -0,0 +1 @@ +#define KCACHEGRIND_VERSION "@KCACHEGRIND_VERSION@" diff --git a/kdeaccounts-plugin/Makefile.am b/kdeaccounts-plugin/Makefile.am new file mode 100644 index 00000000..7be8cea3 --- /dev/null +++ b/kdeaccounts-plugin/Makefile.am @@ -0,0 +1,17 @@ +INCLUDES = $(all_includes) + +noinst_HEADERS = kdeaccountsformat.h +kde_module_LTLIBRARIES = kabcformat_kdeaccounts.la +kabcformat_kdeaccounts_la_SOURCES = kdeaccountsformat.cpp +kabcformat_kdeaccounts_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) +kabcformat_kdeaccounts_la_LIBADD = -lkabc + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kabcformat_kdeaccounts.pot + +linkdir = $(kde_datadir)/kabc/formats +link_DATA = kdeaccountsplugin.desktop +EXTRA_DIST = $(link_DATA) README diff --git a/kdeaccounts-plugin/README b/kdeaccounts-plugin/README new file mode 100644 index 00000000..1319ab18 --- /dev/null +++ b/kdeaccounts-plugin/README @@ -0,0 +1,15 @@ +This is a KDE Addressbook Plugin, that is able to parse the file containing +names and email addresses of all KDE CVS accounts and puts them into your +addressbook. Very handy being able to auto-complete all those KDE developers +in KMail :) + +After installing, fire up kcontrol, open the Addressbook configuration, +select Add -> "file", enter a descriptive name like "KDE CVS Accounts", +check the "Read-only" option and select "KDE CVS Accounts" from the +dropdown list. Finally, enter the full path to the "accounts" file +(in CVS, it's in the "kde-common" module). + +That should be all :) + +Carsten Pfeiffer + diff --git a/kdeaccounts-plugin/kdeaccountsformat.cpp b/kdeaccounts-plugin/kdeaccountsformat.cpp new file mode 100644 index 00000000..d445e311 --- /dev/null +++ b/kdeaccounts-plugin/kdeaccountsformat.cpp @@ -0,0 +1,90 @@ +#include "kdeaccountsformat.h" + +#include +#include + +#include +#include +#include + +extern "C" +{ + KDE_EXPORT KABC::FormatPlugin *format() + { + return new KDEAccountsFormat(); + } +} + +/** + * Loads addresses of the kde-common/accounts file-> The format is + * pfeiffer Carsten Pfeiffer pfeiffer@kde.org + */ + +bool KDEAccountsFormat::loadAll( KABC::AddressBook *book, + KABC::Resource *resource, + QFile *file ) +{ + if ( !book || !file ) // eh? + return false; + + QString uuid = "KDEAccountsEntry."; + int id = 0; + + QByteArray array = file->readAll(); + file->close(); + + QByteArray::ConstIterator it = array.begin(); + QByteArray::ConstIterator end = array.end(); + QByteArray::ConstIterator startLine; + QString line; + char eol = '\n'; + char delim = ' '; + + for ( ; it < end; it++ ) + { + startLine = it; + + for ( ; it && it < end && *it != eol; it++ ) + { } // find eol + + uint length = it - startLine; + line = QString::fromUtf8( startLine, length ).simplifyWhiteSpace(); + + QString nickName; + QString name; + QString email; + + int firstSpace = line.find( delim ); + if ( firstSpace > 0 ) + { + nickName = line.left( firstSpace ); + + int lastSpace = line.findRev( delim ); + if ( lastSpace > firstSpace ) + { + email = line.mid( lastSpace +1 ); + + int start = firstSpace + 1; + int length = lastSpace - start; + name = line.mid( start, length ); + + if ( !email.isEmpty() ) + { + KABC::Addressee address; + address.setNickName( nickName ); + address.setNameFromString( name ); + address.setOrganization("KDE Project"); + address.insertCategory("KDE Developer"); + address.insertEmail( email ); + address.setUid( uuid + QString::number( id++ )); + + address.setResource( resource ); + book->insertAddressee( address ); + } + } + } + } + + return true; +} + diff --git a/kdeaccounts-plugin/kdeaccountsformat.h b/kdeaccounts-plugin/kdeaccountsformat.h new file mode 100644 index 00000000..6eb07c7c --- /dev/null +++ b/kdeaccounts-plugin/kdeaccountsformat.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** $Id$ +** +** Created : 2001 +** +** Copyright (C) 2001 Carsten Pfeiffer +** +****************************************************************************/ + +#ifndef KDEACCOUNTSPARSER_H +#define KDEACCOUNTSPARSER_H + +#include + +namespace KABC { + class AddressBook; +} + +class KDEAccountsFormat : public KABC::FormatPlugin +{ +public: + KDEAccountsFormat() {} + ~KDEAccountsFormat() {} + + virtual bool loadAll( KABC::AddressBook *, + KABC::Resource *resource, QFile *file ); + + virtual bool load( KABC::Addressee&, QFile *) + { + qDebug("*** KDE Accounts format: load single entry not supported."); + return false; + } + virtual void save( const KABC::Addressee&, QFile *) + { + qDebug("*** KDE Accounts format: save not supported."); + } + virtual void saveAll( KABC::AddressBook *, KABC::Resource *, QFile *) + { + qDebug("*** KDE Accounts format: save not supported."); + } + virtual bool checkFormat( QFile *file ) const + { + if ( file->name().endsWith( "/accounts" ) ) + return true; // lame, but works for me :) + + return false; + } + +}; + +#endif // KDEACCOUNTSPARSER_H diff --git a/kdeaccounts-plugin/kdeaccountsplugin.desktop b/kdeaccounts-plugin/kdeaccountsplugin.desktop new file mode 100644 index 00000000..03abf9a2 --- /dev/null +++ b/kdeaccounts-plugin/kdeaccountsplugin.desktop @@ -0,0 +1,44 @@ +[Misc] +Name=KDE Repository Accounts +Name[bg]=Сметки в хранилището на KDE +Name[ca]=Comptes del repositori de KDE +Name[cs]=Účty z KDE repository +Name[da]=KDE lager-konti +Name[de]=KDE Repositorium-Zugänge +Name[el]=Λογαριασμοί χώρου αποθήκευσης του KDE +Name[es]=Cuentas del repositorio de KDE +Name[et]=KDE hoidla kontod +Name[eu]=KDE-ren biltegiaren kontuak +Name[fa]=حسابهای مخزن KDE +Name[fi]=KDE:n versionhallinnan käyttäjätunnukset +Name[fr]=Comptes du référentiel de KDE +Name[gl]=Contas no repositorio de KDE +Name[hu]=KDE SVN-azonosítók +Name[is]=KDE geymslu aðgangur +Name[it]=Account del deposito di KDE +Name[ja]=KDE リポジトリアカウント +Name[ka]=KDE რეპოზიტორიის ანგარიში +Name[kk]=KDE қоймасының тіркелгілері +Name[lt]=KDE saugyklos paskyros +Name[nb]=KDE-lagerkontoer +Name[nds]=KDE-Archivkontos +Name[ne]=केडीई भण्डार खाता +Name[nl]=KDE Repository gebruikersnamen +Name[nn]=KDE-lagerkontoar +Name[pa]=KDE ਰਿਪੋਜ਼ਟਰੀ ਖਾਤੇ +Name[pl]=Konta w repozytorium KDE +Name[pt]=Contas do Repositório do KDE +Name[pt_BR]=Contas do Repositório do KDE +Name[ru]=Учётные записи репозитория KDE +Name[sk]=Účty KDE archívu +Name[sl]=Računi za skladišče KDE +Name[sr]=Налози KDE складишта +Name[sr@Latn]=Nalozi KDE skladišta +Name[sv]=KDE-arkivkonton +Name[uk]=Рахунки сховища KDE +Name[zh_CN]=KDE 仓库账号 +Name[zh_TW]=KDE 主目錄帳號 + +[Plugin] +Type=text +X-KDE-Library=kabcformat_kdeaccounts diff --git a/kdepalettes/KDE_Gimp b/kdepalettes/KDE_Gimp new file mode 100644 index 00000000..796420c1 --- /dev/null +++ b/kdepalettes/KDE_Gimp @@ -0,0 +1,44 @@ +GIMP Palette +# KDE Standard -- GIMP Palette file +0 0 0 Black +48 48 48 Untitled +88 88 88 Untitled +128 128 128 Untitled +160 160 160 Untitled +195 195 195 Untitled +220 220 220 Untitled +64 0 0 Untitled +128 0 0 Untitled +192 0 0 Untitled +255 0 0 Untitled +255 192 192 Untitled +0 64 0 Untitled +0 128 0 Untitled +0 192 0 Untitled +0 255 0 Untitled +192 255 192 Untitled +0 0 0 Untitled +0 0 128 Untitled +0 0 192 Untitled +0 0 255 Untitled +192 192 255 Untitled +64 64 0 Untitled +128 128 0 Untitled +192 192 0 Untitled +255 255 0 Untitled +255 255 192 Untitled +0 64 64 Untitled +0 128 128 Untitled +0 192 192 Untitled +0 255 255 Untitled +192 255 255 Untitled +0 0 0 Untitled +128 0 128 Untitled +192 0 192 Untitled +255 0 255 Untitled +255 192 255 Untitled +255 128 0 Untitled +192 88 0 Untitled +255 168 88 Untitled +255 220 168 Untitled +255 255 255 Untitled diff --git a/kdepalettes/README b/kdepalettes/README new file mode 100644 index 00000000..2f9f1336 --- /dev/null +++ b/kdepalettes/README @@ -0,0 +1,16 @@ +These are palettes for both the Gimp and Xpaint that match the KDE +standard color palette. I thought they maybe useful to others designing +themes and icons. + +INSTALLATION: +Gimp + Copy the file KDE_Gimp to ~/.gimp/palettes/. You can now access the + palette by selecting File->Dialogs->Palette. +XPaint + To make the KDE palette the default palette copy kde_xpaintrc to + ~/.XPaintrc. Otherwise you can load it by selecting File-> + Load Palette from the image's file menu. + +Daniel M. Duley + + diff --git a/kdepalettes/kde_xpaintrc b/kdepalettes/kde_xpaintrc new file mode 100644 index 00000000..b4ab0b32 --- /dev/null +++ b/kdepalettes/kde_xpaintrc @@ -0,0 +1,128 @@ +reset + +pattern BeginData +P1 +2 2 +01 +10 + +EndData +pattern BeginData +P1 +4 4 +1000 +0001 +0010 +0100 + +EndData +pattern BeginData +P1 +4 4 +0001 +1000 +0100 +0010 + +EndData +pattern BeginData +P1 +2 2 +00 +01 + +EndData +pattern BeginData +P1 +4 4 +1000 +0000 +0000 +0000 + +EndData +pattern BeginData +P1 +4 6 +1000 +0100 +0010 +0001 +0010 +0100 + +EndData +pattern BeginData +P1 +8 8 +10000000 +10000000 +10000000 +11111111 +00001000 +00001000 +00001000 +11111111 + +EndData +pattern BeginData +P1 +8 16 +00111000 +01000100 +10000010 +10000010 +10000010 +01000100 +00111000 +00000000 +10000011 +01000100 +00101000 +00101000 +00101000 +01000100 +10000011 +00000000 + +EndData +solid #303030 +solid #585858 +solid #808080 +solid #a0a0a0 +solid #c3c3c3 +solid #dcdcdc +solid #400000 +solid #800000 +solid #c00000 +solid #ff0000 +solid #ffc0c0 +solid #004000 +solid #008000 +solid #00c000 +solid #00ff00 +solid #c0ffc0 +solid #000000 +solid #000080 +solid #0000c0 +solid #0000ff +solid #c0c0ff +solid #404000 +solid #808000 +solid #c0c000 +solid #ffff00 +solid #ffffc0 +solid #004040 +solid #008080 +solid #00c0c0 +solid #00ffff +solid #c0ffff +solid #800080 +solid #c000c0 +solid #ff00ff +solid #ffc0ff +solid #ff8000 +solid #c05800 +solid #ffa858 +solid #ffdca8 +solid #ffffff diff --git a/kdesdk.lsm b/kdesdk.lsm new file mode 100644 index 00000000..ecb2a710 --- /dev/null +++ b/kdesdk.lsm @@ -0,0 +1,11 @@ +Begin4 +Title: kdesdk +Version: 3.5.10 +Entered-date: 2008-08-26 +Description: K Desktop Environment (KDE) SDK +Keywords: KDE X11 desktop Qt +Author: http://bugs.kde.org/ (KDE Bugtracking System) +Primary-site: http://www.kde.org/download/ +Platforms: Unix, Qt +Copying-policy: GPL, Artistic +End diff --git a/kfile-plugins/Makefile.am b/kfile-plugins/Makefile.am new file mode 100644 index 00000000..aa4d2f43 --- /dev/null +++ b/kfile-plugins/Makefile.am @@ -0,0 +1 @@ +SUBDIRS=diff c++ ts diff --git a/kfile-plugins/c++/Makefile.am b/kfile-plugins/c++/Makefile.am new file mode 100644 index 00000000..4770b509 --- /dev/null +++ b/kfile-plugins/c++/Makefile.am @@ -0,0 +1,22 @@ +## Makefile.am for c++ file meta info plugin + +# set the include path for X, qt and KDE +INCLUDES = $(all_includes) + +# these are the headers for your project +noinst_HEADERS = kfile_cpp.h + +kde_module_LTLIBRARIES = kfile_cpp.la + +kfile_cpp_la_SOURCES = kfile_cpp.cpp +kfile_cpp_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) +kfile_cpp_la_LIBADD = $(LIB_KIO) + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +messages: + $(XGETTEXT) *.cpp -o $(podir)/kfile_cpp.pot + +services_DATA = kfile_cpp.desktop kfile_h.desktop +servicesdir = $(kde_servicesdir) diff --git a/kfile-plugins/c++/kfile_cpp.cpp b/kfile-plugins/c++/kfile_cpp.cpp new file mode 100644 index 00000000..6c854a00 --- /dev/null +++ b/kfile-plugins/c++/kfile_cpp.cpp @@ -0,0 +1,130 @@ +/* This file is part of the KDE project + * Copyright (C) 2002 Rolf Magnus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "kfile_cpp.h" + +#include +#include +#include +#include + +#include +#include + +typedef KGenericFactory CppFactory; + +K_EXPORT_COMPONENT_FACTORY(kfile_cpp, CppFactory("kfile_cpp")) + +KCppPlugin::KCppPlugin(QObject *parent, const char *name, + const QStringList &args) + : KFilePlugin(parent, name, args) +{ + kdDebug(7034) << "c++ plugin\n"; + makeMimeTypeInfo("text/x-c++src"); + makeMimeTypeInfo("text/x-chdr"); +} + +void KCppPlugin::makeMimeTypeInfo(const QString& mimetype) +{ + KFileMimeTypeInfo* info = addMimeTypeInfo( mimetype ); + + KFileMimeTypeInfo::GroupInfo* group = + addGroupInfo(info, "General", i18n("General")); + + KFileMimeTypeInfo::ItemInfo* item; + item = addItemInfo(group, "Lines", i18n("Lines"), QVariant::Int); + setAttributes(item, KFileMimeTypeInfo::Averaged); + item = addItemInfo(group, "Code", i18n("Code"), QVariant::Int); + setAttributes(item, KFileMimeTypeInfo::Averaged); + item = addItemInfo(group, "Comment", i18n("Comment"), QVariant::Int); + setAttributes(item, KFileMimeTypeInfo::Averaged); + item = addItemInfo(group, "Blank", i18n("Blank"), QVariant::Int); + setAttributes(item, KFileMimeTypeInfo::Averaged); + item = addItemInfo(group, "Strings", i18n("Strings"), QVariant::Int); + setAttributes(item, KFileMimeTypeInfo::Averaged); + item = addItemInfo(group, "i18n Strings", i18n("i18n Strings"), QVariant::Int); + setAttributes(item, KFileMimeTypeInfo::Averaged); + item = addItemInfo(group, "Included Files", i18n("Included Files"), QVariant::Int); + setAttributes(item, KFileMimeTypeInfo::Averaged); +} + +bool KCppPlugin::readInfo( KFileMetaInfo& info, uint ) +{ + QFile f(info.path()); + if (!f.open(IO_ReadOnly)) + return false; + + int codeLines = 0; + int commentLines = 0; + int totalLines = 0; + int emptyLines = 0; + int Strings = 0; + int Stringsi18n = 0; + int Includes = 0; + + bool inComment = false; + + QString line; + + QTextStream stream( &f ); + while (!stream.eof()) + { + line = stream.readLine(); + totalLines++; + + if (line.stripWhiteSpace().isEmpty()) + { + emptyLines++; + continue; + } + + if (line.contains("/*")) inComment = true; + + if (!inComment) + { + codeLines++; + if (line.contains(QRegExp("^\\s*#\\s*include"))) Includes++; + + int pos = line.find("//"); + if (pos>=0) commentLines++; + // truncate the comment - we don't want to count strings in it + line.truncate(pos); + + Strings+=line.contains(QRegExp("\".*\"")); + Stringsi18n+=line.contains(QRegExp("(?:i18n|I18N_NOOP)\\s*\\(")); + } + else + commentLines++; + + if (line.contains("*/")) inComment = false; + } + + KFileMetaInfoGroup group = appendGroup(info, "General"); + + appendItem(group, "Lines", int(totalLines)); + appendItem(group, "Code", int(codeLines)); + appendItem(group, "Comment", int(commentLines)); + appendItem(group, "Blank", int(emptyLines)); + appendItem(group, "Strings", int(Strings)); + appendItem(group, "i18n Strings", int(Stringsi18n)); + appendItem(group, "Included Files", int(Includes)); + return true; +} + +#include "kfile_cpp.moc" diff --git a/kfile-plugins/c++/kfile_cpp.desktop b/kfile-plugins/c++/kfile_cpp.desktop new file mode 100644 index 00000000..2792aa9f --- /dev/null +++ b/kfile-plugins/c++/kfile_cpp.desktop @@ -0,0 +1,60 @@ +[Desktop Entry] +Type=Service +Name=C++ Info +Name[af]=C++ Inligting +Name[bg]=Изходен код на C++ +Name[br]=Titouroù C++ +Name[ca]=Informació C++ +Name[cs]=C++ info +Name[cy]=Gwybodaeth C++ +Name[da]=C++-info +Name[de]=C++-Info +Name[el]=Πληροφορίες C++ +Name[eo]=C++-informo +Name[es]=Info de C++ +Name[et]=C++ info +Name[eu]=C++ informazioa +Name[fa]=اطلاعات C++ +Name[fi]=C++-tiedot +Name[fo]=C++-upplýsingar +Name[fr]=Informations C++ +Name[ga]=Eolas C++ +Name[gl]=Información de C++ +Name[he]=מידע ++C +Name[hi]=C++ जानकारी +Name[hr]=C++ informacije +Name[hu]=C++-jellemzők +Name[is]=C++ upplýsingar +Name[it]=Informazioni C++ +Name[ja]=C++ 情報 +Name[ka]=C++ ინფორმაცია +Name[kk]=C++ мәліметі +Name[lt]=C++ Informacija +Name[ms]=Info C++ +Name[nds]=C++-Datei-Info +Name[nl]=C++-info +Name[nn]=C++-info +Name[pa]=C++ ਜਾਣਕਾਰੀ +Name[pl]=Informacja z C++ +Name[pt]=Informação de C++ +Name[pt_BR]=Informações C++ +Name[ro]=Informaţii C++ +Name[ru]=Информация C++ +Name[sk]=Informácie o C++ +Name[sl]=Informacije o C++ +Name[sr]=C++ информације +Name[sr@Latn]=C++ informacije +Name[sv]=C++-information +Name[ta]= =C/C++ தகவல் +Name[tg]=Ахбороти C++ +Name[th]=ข้อมูล C++ +Name[tr]=C++ Bilgisi +Name[uk]=Інформація про C++ +Name[xh]=C++ Ulwazi +Name[zh_CN]=C++ 信息 +Name[zh_TW]=C++ 資訊 +ServiceTypes=KFilePlugin +X-KDE-Library=kfile_cpp +MimeType=text/x-c++src;text/x-chdr +PreferredGroups=General +PreferredItems=Lines,Code,Comment,Blank,Strings,i18n Strings,Included Files diff --git a/kfile-plugins/c++/kfile_cpp.h b/kfile-plugins/c++/kfile_cpp.h new file mode 100644 index 00000000..d238858d --- /dev/null +++ b/kfile-plugins/c++/kfile_cpp.h @@ -0,0 +1,40 @@ +/* This file is part of the KDE project + * Copyright (C) 2002 Rolf Magnus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef __KFILE_CPP_H__ +#define __KFILE_CPP_H__ + +#include +#include + +class QStringList; + +class KCppPlugin: public KFilePlugin +{ + Q_OBJECT + +public: + KCppPlugin(QObject *parent, const char *name, const QStringList& args); + virtual bool readInfo(KFileMetaInfo& info, uint what); + +private: + void makeMimeTypeInfo(const QString& mimetype); +}; + +#endif diff --git a/kfile-plugins/c++/kfile_h.desktop b/kfile-plugins/c++/kfile_h.desktop new file mode 100644 index 00000000..b0766114 --- /dev/null +++ b/kfile-plugins/c++/kfile_h.desktop @@ -0,0 +1,58 @@ +[Desktop Entry] +Type=Service +Name=C/C++ Header Info +Name[af]=C/C++ Opskrif Inligting +Name[bg]=Информация за заглавната част на C/C++ +Name[br]=Titouriñ diwar-benn ar reollin C/C++ +Name[ca]=Informació de capçaleres C/C++ +Name[cs]=Informace o C/C++ hlavičce +Name[cy]=Gwybodaeth Pennawd C/C++ +Name[da]=C/C++-headerinfo +Name[de]=C++-Header-Info +Name[el]=Πληροφορίες κεφαλίδων C/C++ +Name[es]=Info de cabecera C/C++ +Name[et]=C/C++ päise info +Name[eu]=C/C++ goiburuen informazioa +Name[fa]=اطلاعات سرآیند C/C++ +Name[fi]=C/C++-otsikkotiedot +Name[fr]=Informations d'en-tête C/C++ +Name[gl]=Información da cabeceira de C/C++ +Name[he]=מידע כותרות ++C/C +Name[hi]=C/C++ हेडर जानकारी +Name[hr]=Informacije o C++ zaglavljima +Name[hu]=C/C++ header fájl jellemzői +Name[is]=C/C++ haus upplýsingar +Name[it]=Informazioni intestazioni C/C++ +Name[ja]=C/C++ ヘッダ情報 +Name[ka]=C/C++ ზედა კოლონტიტულის ინფორმაცია +Name[kk]=C/C++ айдар мәліметі +Name[lt]=C/C++ antraščių informacija +Name[ms]=Info Pengepala C/C++ +Name[nb]=C++-deklarasjonsfilinfo +Name[nds]=C/C++-Koppdatei-Info +Name[ne]=C/C++ Header info +Name[nl]=C/C++ Header-info +Name[nn]=C/C++-deklarasjonsinfo +Name[pa]=C/C++ Header ਜਾਣਕਾਰੀ +Name[pl]=Informacja z nagłówka C/C++ +Name[pt]=Informações do Cabeçalho de C/C++ +Name[pt_BR]=Informações sobre Cabeçalhos C/C++ +Name[ro]=Informaţii antet C/C++ +Name[ru]=Информация о файлах заголовков C/C++ +Name[sk]=Informácie o hlavičkách C/C++ +Name[sl]=Informacije o glavi C/C++ +Name[sr]=Информације о C/C++ заглављу +Name[sr@Latn]=Informacije o C/C++ zaglavlju +Name[sv]=Information om C/C++-deklarationsfiler +Name[ta]= =C/C++ தலைப்பு தகவல் +Name[tg]=Ахборот дар бораи сарлавҳаи файлҳои C/C++ +Name[th]=ข้อมูลแฟ้มส่วนหัว C/C++ +Name[tr]=C/C++ Başlık Bilgisi +Name[uk]=Інформація по заголовкам C++ +Name[xh]=C/C++ Ulwazi lokubhaliweyo okuphezulu +Name[zh_CN]=C/C++ 头文件信息 +Name[zh_TW]=C/C++ 檔頭資訊 +ServiceTypes=KFilePlugin +X-KDE-Library=kfile_cpp +MimeType=text/x-chdr +PreferredItems=Lines,Code,Comment,Blank,Strings,i18n Strings,Included Files diff --git a/kfile-plugins/diff/Makefile.am b/kfile-plugins/diff/Makefile.am new file mode 100644 index 00000000..ea6cccc0 --- /dev/null +++ b/kfile-plugins/diff/Makefile.am @@ -0,0 +1,22 @@ +## Makefile.am for diff file meta info plugin + +# set the include path for X, qt and KDE +INCLUDES = $(all_includes) + +# these are the headers for your project +noinst_HEADERS = kfile_diff.h + +kde_module_LTLIBRARIES = kfile_diff.la + +kfile_diff_la_SOURCES = kfile_diff.cpp +kfile_diff_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) +kfile_diff_la_LIBADD = $(LIB_KSYCOCA) + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +messages: + $(XGETTEXT) *.cpp *.h -o $(podir)/kfile_diff.pot + +services_DATA = kfile_diff.desktop +servicesdir = $(kde_servicesdir) diff --git a/kfile-plugins/diff/kfile_diff.cpp b/kfile-plugins/diff/kfile_diff.cpp new file mode 100644 index 00000000..0f2fa02d --- /dev/null +++ b/kfile-plugins/diff/kfile_diff.cpp @@ -0,0 +1,610 @@ +/************************************************************************** +** kfile_diff.cpp +** ------------------- +** begin : Sun Jan 20 23:25:44 2002 +** copyright : (C) 2002-2003 by Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +/* +** Patch by Volker Augustin for empty diff files. Februari 8, 2002 +** +** Patched to work with CVS from after February 26, 2002 Otto +** +** Patched to work with CVS from after March 24, 2002 Otto +** +** Added support for Perforce diffs, April 26, 2003 Otto Bruggeman +** +** Added support for Subversion diffs, September 11, 2003 Otto Bruggeman +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "kfile_diff.h" + +K_EXPORT_COMPONENT_FACTORY(kfile_diff, KGenericFactory("kfile_diff")) + +KDiffPlugin::KDiffPlugin(QObject *parent, const char *name, + const QStringList &preferredItems) + : KFilePlugin(parent, name, preferredItems) +{ + kdDebug(7034) << "diff plugin" << endl; + + KFileMimeTypeInfo* info = addMimeTypeInfo( "text/x-diff" ); + + KFileMimeTypeInfo::GroupInfo* group; + group = addGroupInfo( info, "General", i18n( "General" ) ); + addItemInfo( group, "Files", i18n( "Files" ), QVariant::UInt ); + addItemInfo( group, "First", i18n( "First File" ), QVariant::String ); + addItemInfo( group, "Format", i18n( "Format" ), QVariant::String ); + addItemInfo( group, "DiffProgram", i18n( "Diff Program" ), QVariant::String ); + addItemInfo( group, "Hunks", i18n( "Hunks" ), QVariant::UInt ); + group = addGroupInfo( info, "Statistics", i18n( "Statistics" ) ); + addItemInfo( group, "Insert", i18n( "Insertions" ), QVariant::UInt ); + addItemInfo( group, "Modify", i18n( "Changes" ), QVariant::UInt ); + addItemInfo( group, "Delete", i18n( "Deletions" ), QVariant::UInt ); +} + +bool KDiffPlugin::readInfo( KFileMetaInfo& info, uint what ) +{ + // This is a hack to avoid using the what stuff, since it is not yet implemented + what = 0; + + // Used to determine if false or true should be returned + bool dataSet = false; + + KFileMetaInfoGroup group; + + QFile file( info.path() ); + QStringList lines; + + if( file.open( IO_ReadOnly ) ) + { + QTextStream stream( &file ); + while (!stream.eof()) + { + lines.append( stream.readLine() ); + } + file.close(); + } + + QString format; + QString program; + + enum KDiffPlugin::Format diffFormat; + enum KDiffPlugin::DiffProgram diffProgram; + + diffFormat = determineDiffFormat ( lines ); + + format = determineI18nedFormat( diffFormat ); + + diffProgram = determineDiffProgram( lines ); + + program = determineI18nedProgram( diffProgram ); + + int numberOfAdditions = 0; + int numberOfDeletions = 0; + int numberOfChanges = 0; + int numberOfHunks = 0; + int numberOfFiles = 0; + + + if ( what != KFileMetaInfo::Fastest ) + { + determineDiffInfo( lines, diffFormat, &numberOfFiles, &numberOfHunks, &numberOfAdditions, &numberOfChanges, &numberOfDeletions ); + } + + QString filename; + QRegExp firstFile( "^Index: (.*)" ); + QStringList::ConstIterator it = lines.begin(); + + it = lines.begin(); + while ( it != lines.end() ) + { + if ( firstFile.exactMatch( (*it) ) ) + { + filename = firstFile.cap(1); + // only interested in the first filename + break; + } + ++it; + } + + kdDebug(7034) << "Diff Format : " << format << endl; // i18n-ed but that is not a problem unless i get i18n-ed debug output ah well, we'll figure something out when then happens + + if (what != KFileMetaInfo::Fastest ) + { + // These dont get calculated in fastest mode... + kdDebug(7034) << "Number of additions : " << numberOfAdditions << endl; + kdDebug(7034) << "Number of deletions : " << numberOfDeletions << endl; + kdDebug(7034) << "Number of changes : " << numberOfChanges << endl; + kdDebug(7034) << "Number of hunks : " << numberOfHunks << endl; + } + + group = appendGroup( info, "General" ); + + if ( numberOfFiles != 0 && what != KFileMetaInfo::Fastest ) + { + appendItem( group, "Files", numberOfFiles ); + dataSet = true; + } + + if ( !filename.isEmpty() ) + { + appendItem( group, "First", filename ); + dataSet = true; + } + + if ( !format.isEmpty() ) + { + appendItem( group, "Format", format ); + dataSet = true; + } + + if ( !program.isEmpty() ) + { + appendItem( group, "DiffProgram", program ); + dataSet = true; + } + + if ( numberOfHunks != 0 && what != KFileMetaInfo::Fastest ) + { + appendItem( group, "Hunks", numberOfHunks ); + dataSet = true; + } + + group = appendGroup( info, "Statistics" ); + + if ( numberOfAdditions != 0 && what != KFileMetaInfo::Fastest ) + { + appendItem( group, "Insert", numberOfAdditions ); + dataSet = true; + } + + if ( numberOfChanges != 0 && what != KFileMetaInfo::Fastest ) + { + appendItem( group, "Modify", numberOfChanges ); + dataSet = true; + } + + if ( numberOfDeletions != 0 && what != KFileMetaInfo::Fastest ) + { + appendItem( group, "Delete", numberOfDeletions ); + dataSet = true; + } + + return dataSet; +} + +enum KDiffPlugin::Format KDiffPlugin::determineDiffFormat( const QStringList lines ) const +{ + QString line; + + if ( lines.count() == 0 ) + { + return KDiffPlugin::Empty; + } + + QStringList::ConstIterator it = lines.begin(); + + while ( it != lines.end() ) + { + line = (*it); + if ( line.find( QRegExp( "^[0-9]+[0-9,]*[acd][0-9]+[0-9,]*$" ), 0 ) == 0 ) + { + return KDiffPlugin::Normal; + } + else if ( line.find( QRegExp( "^--- " ), 0 ) == 0 ) + { + // unified has first a '^--- ' line, then a '^+++ ' line + return KDiffPlugin::Unified; + } + else if ( line.find( QRegExp( "^\\*\\*\\* [^\\t]+\\t" ), 0 ) == 0 ) + { + // context has first a '^*** ' line, then a '^--- ' line + return KDiffPlugin::Context; + } + else if ( line.find( QRegExp( "^[acd][0-9]+ [0-9]+" ), 0 ) == 0 ) + { + return KDiffPlugin::RCS; + } + else if ( line.find( QRegExp( "^[0-9]+[0-9,]*[acd]" ), 0 ) == 0 ) + { + return KDiffPlugin::Ed; + } + ++it; + } + return KDiffPlugin::Unknown; +} + +enum KDiffPlugin::DiffProgram KDiffPlugin::determineDiffProgram( const QStringList lines ) const +{ + if ( lines.count() == 0 ) + { + return KDiffPlugin::Undeterminable; + } + + QStringList::ConstIterator it = lines.begin(); + // very crude, might need some more refining + QRegExp diffRE( "^diff .*" ); + QRegExp p4sRE("^==== "); + + bool indexFound = false; + + while ( it != lines.end() ) + { + if ( (*it).startsWith( "Index:" ) ) + indexFound = true; + else if ( (*it).startsWith( "retrieving revision") ) + return KDiffPlugin::CVSDiff; + else if ( diffRE.exactMatch( *it ) ) + return KDiffPlugin::Diff; + else if ( p4sRE.exactMatch( *it ) ) + return KDiffPlugin::Perforce; + + ++it; + } + + if ( indexFound ) // but no "retrieving revision" found like only cvs diff adds. + return KDiffPlugin::SubVersion; + + return KDiffPlugin::Undeterminable; +} + +const QString KDiffPlugin::determineI18nedFormat( enum KDiffPlugin::Format diffFormat ) const +{ + QString format; + switch( diffFormat ) + { + case KDiffPlugin::Context: + format = i18n( "Context" ); + break; + case KDiffPlugin::Ed: + format = i18n( "Ed" ); + break; + case KDiffPlugin::Normal: + format = i18n( "Normal" ); + break; + case KDiffPlugin::RCS: + format = i18n( "RCS" ); + break; + case KDiffPlugin::Unified: + format = i18n( "Unified" ); + break; + case KDiffPlugin::Empty: + format = i18n( "Not Available (file empty)" ); + break; + case KDiffPlugin::Unknown: + format = i18n( "Unknown" ); + break; + case KDiffPlugin::SideBySide: + format = i18n( "Side by Side" ); + } + return format; +} + +const QString KDiffPlugin::determineI18nedProgram( enum KDiffPlugin::DiffProgram diffProgram ) const +{ + QString program; + + switch( diffProgram ) + { + case KDiffPlugin::CVSDiff: + program = i18n( "CVSDiff" ); + break; + case KDiffPlugin::Diff: + program = i18n( "Diff" ); + break; + case KDiffPlugin::Diff3: + program = i18n( "Diff3" ); + break; + case KDiffPlugin::Perforce: + program = i18n( "Perforce" ); + break; + case KDiffPlugin::SubVersion: + program = i18n( "SubVersion" ); + break; + case KDiffPlugin::Undeterminable: + program = i18n( "Unknown" ); + break; + } + return program; +} + +void KDiffPlugin::determineDiffInfo( const QStringList lines, + enum KDiffPlugin::Format diffFormat, + int* numberOfFiles, + int* numberOfHunks, + int* numberOfAdditions, + int* numberOfChanges, + int* numberOfDeletions ) +{ + QString line; + + QRegExp edAdd( "([0-9]+)(|,([0-9]+))a" ); + QRegExp edDel( "([0-9]+)(|,([0-9]+))d" ); + QRegExp edMod( "([0-9]+)(|,([0-9]+))c" ); + + QRegExp normalAdd( "[0-9]+a([0-9]+)(|,([0-9]+))" ); + QRegExp normalDel( "([0-9]+)(|,([0-9]+))d(|[0-9]+)" ); + QRegExp normalMod( "([0-9]+)(|,([0-9]+))c([0-9]+)(|,([0-9]+))" ); + + QRegExp rcsAdd( "a[0-9]+ ([0-9]+)" ); + QRegExp rcsDel( "d[0-9]+ ([0-9]+)" ); + + QStringList::ConstIterator it = lines.begin(); + + switch( diffFormat ) + { + case KDiffPlugin::Context: + while ( it != lines.end() ) + { + if ( (*it).startsWith("***************") ) + { + (*numberOfHunks)++; +// kdDebug(7034) << "Context Hunk : " << (*it) << endl; + } + else if ( (*it).startsWith("***") ) + { + (*numberOfFiles)++; +// kdDebug(7034) << "Context File : " << (*it) << endl; + } + else if ( (*it).startsWith("---") ) {} // ignore + else if ( (*it).startsWith("+") ) + { + (*numberOfAdditions)++; +// kdDebug(7034) << "Context Insertion : " << (*it) << endl; + } + else if ( (*it).startsWith("-") ) + { + (*numberOfDeletions)++; +// kdDebug(7034) << "Context Deletion : " << (*it) << endl; + } + else if ( (*it).startsWith("!") ) + { + (*numberOfChanges)++; +// kdDebug(7034) << "Context Modified : " << (*it) << endl; + } + else if ( (*it).startsWith(" ") ) + { +// kdDebug(7034) << "Context Context : " << (*it) << endl; + } + else + { +// kdDebug(7034) << "Context Unknown : " << (*it) << endl; + } + + ++it; + } + (*numberOfChanges) /= 2; // changes are in both parts of the hunks + (*numberOfFiles) -= (*numberOfHunks); // it counts old parts of a hunk as files :( + break; + case KDiffPlugin::Ed: + while ( it != lines.end() ) + { + if ( (*it).startsWith( "diff" ) ) + { + (*numberOfFiles)++; +// kdDebug(7034) << "Ed File : " << (*it) << endl; + } + else if ( edAdd.exactMatch( (*it) ) ) + { +// kdDebug(7034) << "Ed Insertion : " << (*it) << endl; + (*numberOfHunks)++; + ++it; + while( it != lines.end() && !(*it).startsWith(".") ) + { + (*numberOfAdditions)++; +// kdDebug(7034) << "Ed Insertion : " << (*it) << endl; + ++it; + } + } + else if ( edDel.exactMatch( (*it) ) ) + { +// kdDebug(7034) << "Ed Deletion : " << (*it) << endl; + (*numberOfHunks)++; + (*numberOfDeletions) += (edDel.cap(3).isEmpty() ? 1 : edDel.cap(3).toInt() - edDel.cap(1).toInt() + 1); +// kdDebug(7034) << "Ed noOfLines : " << (edDel.cap(3).isEmpty() ? 1 : edDel.cap(3).toInt() - edDel.cap(1).toInt() + 1) << endl; + } + else if ( edMod.exactMatch( (*it) ) ) + { +// kdDebug(7034) << "Ed Modification : " << (*it) << endl; + if ( edMod.cap(3).isEmpty() ) + (*numberOfDeletions)++; + else + (*numberOfDeletions) += edMod.cap(3).toInt() - edMod.cap(1).toInt() + 1; + (*numberOfHunks)++; + ++it; + while( it != lines.end() && !(*it).startsWith(".") ) + { + (*numberOfAdditions)++; +// kdDebug(7034) << "Ed Modification : " << (*it) << endl; + ++it; + } + } + else + { +// kdDebug(7034) << "Ed Unknown : " << (*it) << endl; + } + + ++it; + } + break; + case KDiffPlugin::Normal: + while ( it != lines.end() ) + { + if ( (*it).startsWith( "diff" ) ) + { + (*numberOfFiles)++; +// kdDebug(7034) << "Normal File : " << (*it) << endl; + } + else if ( normalAdd.exactMatch( *it ) ) + { +// kdDebug(7034) << "Normal Insertion : " << (*it) << endl; + (*numberOfHunks)++; + if ( normalAdd.cap(3).isEmpty() ) + { + (*numberOfAdditions)++; +// kdDebug(7034) << "Normal Addition : " << 1 << endl; + } + else + { + (*numberOfAdditions) += normalAdd.cap(3).toInt() - normalAdd.cap(1).toInt() + 1; +// kdDebug(7034) << "Normal Addition : " << normalAdd.cap(3).toInt() - normalAdd.cap(1).toInt() + 1 << endl; + } + } + else if ( normalDel.exactMatch( *it ) ) + { +// kdDebug(7034) << "Normal Deletion : " << (*it) << endl; + (*numberOfHunks)++; + if ( normalDel.cap(3).isEmpty() ) + { + (*numberOfDeletions)++; +// kdDebug(7034) << "Normal Deletion : " << 1 << endl; + } + else + { + (*numberOfDeletions) += normalDel.cap(3).toInt() - normalDel.cap(1).toInt() + 1; +// kdDebug(7034) << "Normal Deletion : " << normalDel.cap(3).toInt() - normalDel.cap(1).toInt() + 1 << endl; + } + } + else if ( normalMod.exactMatch( *it ) ) + { +// kdDebug(7034) << "Normal Modification : " << (*it) << endl; + (*numberOfHunks)++; + if ( normalMod.cap(3).isEmpty() ) + { + (*numberOfDeletions)++; +// kdDebug(7034) << "Normal Deletion : " << 1 << endl; + } + else + { + (*numberOfDeletions) += normalMod.cap(3).toInt() - normalMod.cap(1).toInt() + 1; +// kdDebug(7034) << "Normal Deletion : " << normalMod.cap(3).toInt() - normalMod.cap(1).toInt() + 1 << endl; + } + if ( normalMod.cap(6).isEmpty() ) + { + (*numberOfAdditions)++; +// kdDebug(7034) << "Normal Addition : " << 1 << endl; + } + else + { + (*numberOfAdditions) += normalMod.cap(6).toInt() - normalMod.cap(4).toInt() + 1; +// kdDebug(7034) << "Normal Addition : " << normalMod.cap(6).toInt() - normalMod.cap(4).toInt() + 1 << endl; + } + } + else if ( (*it).startsWith(">") ) + { +// numberOfAdditions++; +// kdDebug(7034) << "Normal Insertion : " << (*it) << endl; + } + else if ( (*it).startsWith("<") ) + { +// numberOfDeletions++; +// kdDebug(7034) << "Normal Deletion : " << (*it) << endl; + } + else + { +// kdDebug(7034) << "Normal Unknown : " << (*it) << endl; + } + + ++it; + } + break; + case KDiffPlugin::RCS: + while ( it != lines.end() ) + { + if ( (*it).startsWith( "diff" ) ) // works for cvs diff, have to test for normal diff + { +// kdDebug(7034) << "RCS File : " << (*it) << endl; + (*numberOfFiles)++; + } + else if ( rcsAdd.exactMatch( *it ) ) + { +// kdDebug(7034) << "RCS Insertion : " << (*it) << endl; + (*numberOfHunks)++; + (*numberOfAdditions) += rcsAdd.cap(1).toInt(); +// kdDebug(7034) << "RCS noOfLines : " << rcsAdd.cap(1).toInt() << endl; + } + else if ( rcsDel.exactMatch( *it ) ) + { +// kdDebug(7034) << "RCS Deletion : " << (*it) << endl; + (*numberOfHunks)++; + (*numberOfDeletions) += rcsDel.cap(1).toInt(); +// kdDebug(7034) << "RCS noOfLines : " << rcsDel.cap(1).toInt() << endl; + } + else + { +// kdDebug(7034) << "RCS Unknown : " << (*it) << endl; + } + + ++it; + } + break; + case KDiffPlugin::Unified: + while ( it != lines.end() ) + { + if ( (*it).startsWith("@@ ") ) + { + (*numberOfHunks)++; +// kdDebug(7034) << "Unified Hunk : " << (*it) << endl; + } + else if ( (*it).startsWith("---") ) + { + (*numberOfFiles)++; +// kdDebug(7034) << "Unified File : " << (*it) << endl; + } + else if ( (*it).startsWith("+++") ) {} // ignore (dont count as insertion) + else if ( (*it).startsWith("+") ) + { + (*numberOfAdditions)++; +// kdDebug(7034) << "Unified Insertion : " << (*it) << endl; + } + else if ( (*it).startsWith("-") ) + { + (*numberOfDeletions)++; +// kdDebug(7034) << "Unified Deletion : " << (*it) << endl; + } + else if ( (*it).startsWith(" ") ) + { +// kdDebug(7034) << "Unified Context : " << (*it) << endl; + } + else + { +// kdDebug(7034) << "Unified Unknown : " << (*it) << endl; + } + + ++it; + } + break; + case KDiffPlugin::Empty: + case KDiffPlugin::Unknown: + case KDiffPlugin::SideBySide: + break; + } +} + +#include "kfile_diff.moc" + +/* vim: set ts=4 sw=4 noet: */ + diff --git a/kfile-plugins/diff/kfile_diff.desktop b/kfile-plugins/diff/kfile_diff.desktop new file mode 100644 index 00000000..95b0bb78 --- /dev/null +++ b/kfile-plugins/diff/kfile_diff.desktop @@ -0,0 +1,57 @@ +[Desktop Entry] +Type=Service +Name=Diff Stats +Name[af]=Diff Statistiek +Name[bg]=Статистика за разлики +Name[br]=Stadegoù Diff +Name[bs]=Diff statistika +Name[ca]=Estadístiques de diff +Name[cs]=Statistika rozdílů +Name[cy]=Ystadegau Gwahaniaethau +Name[da]=Diff-stats +Name[de]=Diff-Statistiken +Name[el]=Στατιστικά diff +Name[es]=Estadísticas Diff +Name[et]=Võrdlemise tulemused +Name[eu]=Desberdintasun estatistikak +Name[fa]=آمارهای Diff +Name[fi]=Diff-tilastot +Name[fr]=Statistiques de diff +Name[ga]=Staitistic Diff +Name[gl]=Estatísticas de diff +Name[he]=סטטיסטיקת Diff +Name[hi]=डिफ स्टेट्स +Name[hr]=Diff statistike +Name[hu]=Diff statisztika +Name[it]=Statistiche di confronto +Name[ja]=Diff 統計 +Name[kk]=Diff статистикасы +Name[lt]=Skirtumų statistika +Name[ms]=Stat Diff +Name[nb]=Diff-statistikk +Name[nds]=Verscheelstatistiken +Name[ne]=भिन्न तथ्याङ्क +Name[nl]=Diff-statistieken +Name[nn]=Diff-statistikk +Name[pa]=ਅੰਤਰ ਹਾਲਤ +Name[pl]=Statystyka różnic +Name[pt]=Estatísticas do diff +Name[pt_BR]=Estados do Diff +Name[ro]=Statistici Diff +Name[ru]=Статистика различий +Name[sk]=Štatistiky Diff +Name[sl]=Statistike diff +Name[sr]=Статистика разликовања +Name[sr@Latn]=Statistika razlikovanja +Name[sv]=Diff-statistik +Name[ta]=டிப் ஸ்டட்ஸ் +Name[tg]=Статистикаи фарқият +Name[th]=สถานะแตกต่าง +Name[tr]=Diff İstatistikleri +Name[uk]=Статистика відмінностей +Name[zh_CN]=Diff 统计 +Name[zh_TW]=Diff 狀態 +ServiceTypes=KFilePlugin +X-KDE-Library=kfile_diff +MimeType=text/x-diff +PreferredItems=Files,First,Format,Hunks,Insert,Modify,Delete diff --git a/kfile-plugins/diff/kfile_diff.h b/kfile-plugins/diff/kfile_diff.h new file mode 100644 index 00000000..d32e176c --- /dev/null +++ b/kfile-plugins/diff/kfile_diff.h @@ -0,0 +1,52 @@ +/************************************************************************** +** kfile_diff.h +** ------------------- +** begin : Sun Jan 20 23:25:29 2002 +** copyright : (C) 2002 by Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef __KFILE_PDF_H__ +#define __KFILE_PDF_H__ + +#include + +class QStringList; + +class KDiffPlugin: public KFilePlugin +{ + Q_OBJECT + +public: + KDiffPlugin( QObject *parent, const char *name, + const QStringList& preferredItems ); + + + virtual bool readInfo( KFileMetaInfo& info, uint what ); + +public: + enum Format { Context, Ed, Normal, RCS, Unified, Empty, SideBySide, Unknown }; + enum DiffProgram { CVSDiff, Diff, Diff3, Perforce, SubVersion, Undeterminable }; // cant use Unknown again :( + +private: + enum Format determineDiffFormat ( const QStringList lines ) const; + enum DiffProgram determineDiffProgram ( const QStringList lines ) const; + const QString determineI18nedFormat ( enum KDiffPlugin::Format diffFormat ) const; + const QString determineI18nedProgram( enum KDiffPlugin::DiffProgram diffProgram ) const; + // yes ugly, it's better to use a struct or classmembers to pass these parameters around + void determineDiffInfo ( const QStringList lines, + enum KDiffPlugin::Format diffFormat, int* numberOfFiles, + int* numberOfHunks, int* numberOfAdditions, + int* numberOfChanges, int* numberOfDeletions ); +}; + +#endif diff --git a/kfile-plugins/ts/Makefile.am b/kfile-plugins/ts/Makefile.am new file mode 100644 index 00000000..dd64ec48 --- /dev/null +++ b/kfile-plugins/ts/Makefile.am @@ -0,0 +1,21 @@ +## Makefile.am for text file meta info plugin + +# set the include path for X, qt and KDE +INCLUDES = $(all_includes) + +noinst_HEADERS = kfile_ts.h + +kde_module_LTLIBRARIES = kfile_ts.la + +kfile_ts_la_SOURCES = kfile_ts.cpp +kfile_ts_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) +kfile_ts_la_LIBADD = $(LIB_KIO) + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +messages: + $(XGETTEXT) *.cpp -o $(podir)/kfile_ts.pot + +services_DATA = kfile_ts.desktop +servicesdir = $(kde_servicesdir) diff --git a/kfile-plugins/ts/kfile_ts.cpp b/kfile-plugins/ts/kfile_ts.cpp new file mode 100644 index 00000000..d6783fd7 --- /dev/null +++ b/kfile-plugins/ts/kfile_ts.cpp @@ -0,0 +1,93 @@ +/* This file is part of the KDE project + * Copyright (C) 2002 Carsten Niehaus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "kfile_ts.h" + +#include +#include + +#include +#include + +typedef KGenericFactory TsFactory; + +K_EXPORT_COMPONENT_FACTORY(kfile_ts, TsFactory("kfile_ts")) + +KTsPlugin::KTsPlugin(QObject *parent, const char *name, + const QStringList &args) : KFilePlugin(parent, name, args) +{ + makeMimeTypeInfo( "application/x-linguist" ); +} + +void KTsPlugin::makeMimeTypeInfo(const QString& mimeType) +{ + KFileMimeTypeInfo* info = addMimeTypeInfo(mimeType); + + KFileMimeTypeInfo::GroupInfo* group = + addGroupInfo(info, "General", i18n("General")); + + KFileMimeTypeInfo::ItemInfo* item; + item = addItemInfo(group, "Messages", i18n("Messages"), QVariant::Int); + setAttributes(item, KFileMimeTypeInfo::Averaged); + item = addItemInfo(group, "Translated", i18n("Translated"), QVariant::Int); + setAttributes(item, KFileMimeTypeInfo::Averaged); + item = addItemInfo(group, "Untranslated", i18n("Untranslated"), QVariant::Int); + setAttributes(item, KFileMimeTypeInfo::Averaged); + item = addItemInfo(group, "Obsolete", i18n("Obsolete"), QVariant::Int); +} + +bool KTsPlugin::readInfo(KFileMetaInfo& info, uint) +{ + QFile f(info.path()); + if (!f.open(IO_ReadOnly)) + return false; + + int messages = 0; + int untranslated = 0; + int obsolete = 0; + + QTextStream stream( &f ); + QString line = stream.readLine(); + + // is it really a linguist file? + if (!line.contains("", false)) + return false; + + while (!stream.eof()) + { + line = stream.readLine(); + + if (line.contains("type=\"obsolete\"")) obsolete++; + + if (line.contains("")) messages++; + + if (line.contains("type=\"unfinished\"")) untranslated++; + + } + + KFileMetaInfoGroup group = appendGroup(info, "General"); + appendItem(group, "Messages", messages); + appendItem(group, "Translated", messages-untranslated-obsolete); + appendItem(group, "Untranslated", untranslated); + appendItem(group, "Obsolete", obsolete); + + return true; +} + +#include "kfile_ts.moc" diff --git a/kfile-plugins/ts/kfile_ts.desktop b/kfile-plugins/ts/kfile_ts.desktop new file mode 100644 index 00000000..09c3ae7d --- /dev/null +++ b/kfile-plugins/ts/kfile_ts.desktop @@ -0,0 +1,54 @@ +[Desktop Entry] +Type=Service +Name=Qt Linguist File Info +Name[bg]=Информация за файла Qt Linguist +Name[br]=Restr titouroù Qt Linguist +Name[bs]=Qt Linguist informacije o datoteci +Name[ca]=Informació fitxer de Qt Linguist +Name[cs]=Info o souboru Qt Linguist +Name[cy]=Gwybodaeth Ffeil Qt Linguist +Name[da]=Qt Linguist Fil-info +Name[de]=Qt Linguist-Dateiinfo +Name[el]=Πληροφορίες αρχείου Qt Linguist +Name[es]=Información de archivo de Qt Linguist +Name[et]=Qt Linguisti faili info +Name[eu]=Qt Linguist fitxategi informazioa +Name[fa]=اطلاعات پروندۀ زبان‌شناسQt +Name[fi]=Qt Linquist -tiedoston tiedot +Name[fr]=Fichier d'informations Qt Linguist +Name[ga]=Eolas faoi Chomhad Qt Linguist +Name[gl]=Información do Ficheiro de Qt Linguist +Name[hi]=क्यूटी लिंग्विस्ट फ़ाइल जानकारी +Name[hu]=Qt Linguist fájljellemzők +Name[is]=Qt Linguist skráarupplýsingar +Name[it]=Informazioni file per Qt Linguist +Name[ja]=Qt Linguist ファイル情報 +Name[ka]=Qt Linguist ფაილის ინფორმაცია +Name[kk]=Qt Linguist файл мәліметі +Name[lt]=Qt Linguist bylos informacija +Name[nb]=Informasjon om Qt Linguist-fil +Name[nds]=QtLinguist-Datei-Info +Name[ne]=Qt बहुभाषी फाइल सूचना +Name[nl]=Qt Linquïst-bestandsinformatie +Name[nn]=Informasjon om Qt Linguist-fil +Name[pa]=Qt Linguist ਫਾਇਲ ਜਾਣਕਾਰੀ +Name[pl]=Informacja pliku Qt Linguist +Name[pt]=Informação do Ficheiro do Qt Linguist +Name[pt_BR]=Arquivo de Informações de Lingüística do Qt +Name[ru]=Информация о файле в формате Qt Linguist +Name[sk]=Informácie pre Qt Linguist +Name[sl]=Informacije o Qt Linguist +Name[sr]=Информације о фајлу Qt Linguist-а +Name[sr@Latn]=Informacije o fajlu Qt Linguist-a +Name[sv]=Qt Linguist-filinformation +Name[ta]=Qt மொழியியல் கோப்புகள் தகவல் +Name[tg]=Ахборот дар бораи файл дар формати Qt Linguist +Name[tr]=Qt Dilbilimci Dosya Bilgisi +Name[uk]=інформація про файл формату Qt Linguist +Name[zh_CN]=Qt 语言大师文件信息 +Name[zh_TW]=Qt 語言檔資訊 +ServiceTypes=KFilePlugin +X-KDE-Library=kfile_ts +MimeType=application/x-linguist +PreferredGroups=General +PreferredItems=Messages,Translated,Untranslated,Fuzzi diff --git a/kfile-plugins/ts/kfile_ts.h b/kfile-plugins/ts/kfile_ts.h new file mode 100644 index 00000000..0f976bbf --- /dev/null +++ b/kfile-plugins/ts/kfile_ts.h @@ -0,0 +1,39 @@ +/* This file is part of the KDE project + * Copyright (C) 2002 Carsten Niehaus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef __KFILE_TS_H_ +#define __KFILE_TS_H_ + +#include + +class QStringList; + +class KTsPlugin: public KFilePlugin +{ + Q_OBJECT + +public: + KTsPlugin(QObject *parent, const char *name, const QStringList& args); + virtual bool readInfo(KFileMetaInfo& info, uint what); + +private: + void makeMimeTypeInfo(const QString& mimeType); +}; + +#endif diff --git a/kioslave/Makefile.am b/kioslave/Makefile.am new file mode 100644 index 00000000..93c6570c --- /dev/null +++ b/kioslave/Makefile.am @@ -0,0 +1,6 @@ +if include_kioslave_svn +SVN_SUBDIR = svn +endif + +SUBDIRS= $(SVN_SUBDIR) + diff --git a/kioslave/svn/AUTHORS b/kioslave/svn/AUTHORS new file mode 100644 index 00000000..75a6fbc9 --- /dev/null +++ b/kioslave/svn/AUTHORS @@ -0,0 +1 @@ +Mickael Marchand diff --git a/kioslave/svn/COPYING b/kioslave/svn/COPYING new file mode 100644 index 00000000..f5030495 --- /dev/null +++ b/kioslave/svn/COPYING @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/kioslave/svn/Makefile.am b/kioslave/svn/Makefile.am new file mode 100644 index 00000000..1253820d --- /dev/null +++ b/kioslave/svn/Makefile.am @@ -0,0 +1,15 @@ +SUBDIRS = ksvnd svnhelper icons + +INCLUDES = $(SVN_INCLUDE) $(all_includes) + +kde_module_LTLIBRARIES = kio_svn.la + +kio_svn_la_SOURCES = svn.cpp +kio_svn_la_LIBADD = -lkio +kio_svn_la_LDFLAGS = -avoid-version -module $(all_libraries) $(KDE_PLUGIN) $(SVNLD) $(SVN_LIB) + +protocol_DATA = svn+http.protocol svn+https.protocol svn+file.protocol svn+ssh.protocol svn.protocol +protocoldir = $(kde_servicesdir) + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kio_svn.pot diff --git a/kioslave/svn/README b/kioslave/svn/README new file mode 100644 index 00000000..6d851afa --- /dev/null +++ b/kioslave/svn/README @@ -0,0 +1,40 @@ +This is a kio_subversion. It allows you to browse a (remote) Subversion repository. +The expected URLs are something like : svn+http://svn.collab.net/repos/svn +This will show you the current HEAD version of the repository (just like the one you would get +with http://svn.collab.net/repos/svn). +You can also try : svn+http://svn.collab.net/repos/svn?rev=500 so that it displays the revision 500. + +REQUIREMENTS: +------------ +Subversion libraries >= 0.37.0 (http://subversion.tigris.org) + +FEATURES: +--------- +Supported protocols : +svn+http://hostname/path?rev=revision_number +svn+https://hostname/path?rev=revision_number +svn+ssh://hostname/path?rev=revision_number +svn://hostname/path?rev=revision_number +svn+file://repository/path?rev=revision_number + +INSTALL: +-------- +you need the svn client library which is distributed by subversion. +on Debian : apt-get install libsvn0-dev will do everything you need. + +you can pass the following arguments to configure to help it find your +subversion/apr installations : + +--with-apr-config=FILE Use the given path to apr-config when determining APR configuration; defaults to "apr-config" +--with-apu-config=FILE Use the given path to apu-config when determining APR util configuration; defaults to "apu-config" +--with-subversion-dir=DIR where Subversion is installed +--with-svn-include=DIR Use the given path to the subversion headers. +--with-svn-lib=DIR Use the given path to the subversion libraries. + +BUGS: +----- +- file modifications time are wrong in konqui +- kio_svn crashes often with subversion 1.2-rc2, needs investigation +any other ? +please report + diff --git a/kioslave/svn/TODO b/kioslave/svn/TODO new file mode 100644 index 00000000..b5bbe683 --- /dev/null +++ b/kioslave/svn/TODO @@ -0,0 +1,74 @@ +For the menu options, tortoise SVN has something like below. +We can follow this to some extent: + +Folder not in SVN at all: + +SVN Checkout +Svn: + Export + Create Repository here + Import + ---- + Settings... + Help + +A folder and a file in an SVN folder and in the SVN repository + +SVN Update +SVN Commit.... +Svn: + Show Log + Check for modifications + Revision Graph + ---- + Update to revision... + Rename... + Delete + Revert + Cleanup + ---- + Branch/tag... + Switch... + Merge... + Export... + Relocate... + ---- + Add (?) + ---- + Create Patch... + Apply Patch... + ---- + Settings... + Help + +A file in an SVN folder and in SVN repository + +SVN Update +SVN Commit.... +Svn: + Show Log + Check for modifications + Revision Graph + ---- + Update to revision... + Rename... + Delete + Revert + Cleanup + ---- + Branch/tag... + Switch... + Merge... + Blame... + ---- + Settings... + Help + + +A file in SVN folder, but not in repos + +Svn: + Add + Add to ignore list + + diff --git a/kioslave/svn/configure.in.bot b/kioslave/svn/configure.in.bot new file mode 100644 index 00000000..ccab94db --- /dev/null +++ b/kioslave/svn/configure.in.bot @@ -0,0 +1,9 @@ +if test "x$with_subversion" = xcheck && test -z "$SVN_SUBDIR"; then + echo "" + echo "You're missing Subversion libraries (1.x)" + echo "KDE will not be able to browse Subversion repositories without it," + echo "consider installing it." + echo "Look at kioslave/svn/README for more information" + echo "" + all_tests=bad +fi diff --git a/kioslave/svn/configure.in.in b/kioslave/svn/configure.in.in new file mode 100644 index 00000000..9e962c08 --- /dev/null +++ b/kioslave/svn/configure.in.in @@ -0,0 +1,157 @@ +AC_ARG_WITH(subversion, + [AC_HELP_STRING(--with-subversion, + [enable support for subversion @<:@default=check@:>@])], + [], with_subversion=check) + +SVN_SUBDIR="" +if test "x$with_subversion" != xno; then + +APR_CONFIGS="apr-config apr-1-config /usr/local/apr/bin/apr-config" +SVN_SUBDIR="svn" +AC_ARG_WITH(apr-config, +[[ --with-apr-config=FILE Use the given path to apr-config when determining + APR configuration; defaults to "apr-config"]], +[ + if test "$withval" != "yes" -a "$withval" != ""; then + APR_CONFIGS=$withval + fi +]) +AC_MSG_CHECKING([for APR]) +APR_CONFIG="" +for VALUE in $APR_CONFIGS ; do + if $VALUE --cflags > /dev/null; then + APR_CONFIG=$VALUE + break + fi +done +test $VALUE && APR_CONFIG=$VALUE +if test $APR_CONFIG ; then + AC_MSG_RESULT([found]) +else + AC_MSG_RESULT([not found]) + SVN_SUBDIR= +dnl AC_MSG_ERROR([APR is required. Try --with-apr-config.]) +fi + +APR_CPPFLAGS="`$APR_CONFIG --cppflags`" +APR_INCLUDE="`$APR_CONFIG --includes`" +APR_LIBS="`$APR_CONFIG --link-ld --libs`" + +dnl +dnl APR util +dnl + +APU_CONFIGS="apu-config apu-1-config /usr/local/apr/bin/apu-config" +AC_ARG_WITH(apu-config, +[[ --with-apu-config=FILE Use the given path to apu-config when determining + APR util configuration; defaults to "apu-config"]], +[ + if test "$withval" != "yes" -a "$withval" != ""; then + APU_CONFIGS=$withval + fi +]) +AC_MSG_CHECKING([for APR util]) +APU_CONFIG="" +for VALUE in $APU_CONFIGS ; do + if $VALUE --includes > /dev/null; then + APU_CONFIG=$VALUE + break + fi +done +if test $APU_CONFIG ; then + AC_MSG_RESULT([found]) +else + AC_MSG_RESULT([not found]) + SVN_SUBDIR= +dnl AC_MSG_ERROR([APR util is required. Try --with-apu-config.]) +fi +dnl APR_CPPFLAGS="$APR_CPPFLAGS `$APU_CONFIG --includes`" +APR_INCLUDE="$APR_INCLUDE `$APU_CONFIG --includes`" +APR_LIBS="$APR_LIBS `$APU_CONFIG --link-ld --libs`" + +AC_MSG_CHECKING(for Subversion svn-config) +AC_ARG_WITH(subversion-dir, + [ --with-subversion-dir=DIR where Subversion is installed ], + [ + SVNCONFIG="$withval/bin/svn-config" + ]) + +if test -z "$SVNCONFIG"; then + _SVNCONFIG="`svn-config --prefix 2> /dev/null`" + if test -n "$_SVNCONFIG"; then + SVNCONFIG="$_SVNCONFIG/bin/svn-config" + fi +fi + +AC_SUBST(SVNCONFIG) +if test -x "$SVNCONFIG"; then + SVNLD="`$SVNCONFIG --ldflags`" + SVN_LIB="`$SVNCONFIG --libs` -lsvn_client-1" + SVN_CPPFLAGS="`$SVNCONFIG --cppflags`" + dnl ugly hack for subversion svn-config problems in 0.14.x, to be removed when svn-config is fixed + SVN_INCLUDE="`$SVNCONFIG --includes` -I$_SVNCONFIG/include/subversion-1/" + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(not found) + SVNLD="" + dnl just a fallback to debian's config so that it works for me :) + SVN_INCLUDES="/usr/local/include /usr/include" + AC_ARG_WITH(svn-include, + [[ --with-svn-include=DIR Use the given path to the subversion headers.]], + [ + if test "$withval" != "yes" -a "$withval" != ""; then + SVN_INCLUDES=$withval + fi + ]) + AC_MSG_CHECKING([for Subversion headers]) + SVN_INCLUDE="" + for VALUE in $SVN_INCLUDES ; do + if test -f $VALUE/subversion-1/svn_types.h ; then + SVN_INCLUDE="-I$VALUE" + break + fi + done + if test $SVN_INCLUDE ; then + AC_MSG_RESULT([found]) + else + AC_MSG_RESULT([not found]) + SVN_SUBDIR= +dnl AC_MSG_ERROR([Subversion headers are required. Try --with-svn-include.]) + fi + SVN_LIBS="/usr/local/lib /usr/lib /usr/lib64" + AC_ARG_WITH(svn-lib, + [[ --with-svn-lib=DIR Use the given path to the subversion libraries.]], + [ + if test "$withval" != "yes" -a "$withval" != ""; then + SVN_LIBS=$withval + fi + ]) + AC_MSG_CHECKING([for Subversion libraries]) + SVN_LIB="" + for VALUE in $SVN_LIBS ; do + if ls $VALUE/libsvn_client-1.* 1>/dev/null 2>&1; then + SVN_LIB="-L$VALUE" + break + fi + done + if test $SVN_LIB ; then + AC_MSG_RESULT([found]) + else + AC_MSG_RESULT([not found]) + SVN_SUBDIR= + fi +fi +SVN_LIB="$SVN_LIB $APR_LIBS -lsvn_client-1" +SVN_INCLUDE="$SVN_INCLUDE $APR_INCLUDE" +SVN_CPPFLAGS="$APR_CPPFLAGS $SVN_CPPFLAGS" + +if test "x$with_subversion" != xcheck && test -z "$SVN_SUBDIR"; then + AC_MSG_ERROR([--with-subversion was given, but test for subversion failed]) +fi +fi + +AC_SUBST(SVN_INCLUDE) +AC_SUBST(SVN_LIB) +AC_SUBST(SVN_CPPFLAGS) +AC_SUBST(SVNLD) +AM_CONDITIONAL(include_kioslave_svn, test -n "$SVN_SUBDIR") diff --git a/kioslave/svn/icons/Makefile.am b/kioslave/svn/icons/Makefile.am new file mode 100644 index 00000000..a4b97f06 --- /dev/null +++ b/kioslave/svn/icons/Makefile.am @@ -0,0 +1 @@ +KDE_ICON=AUTO diff --git a/kioslave/svn/icons/cr128-action-svn_add.png b/kioslave/svn/icons/cr128-action-svn_add.png new file mode 100644 index 00000000..0b26fdbc Binary files /dev/null and b/kioslave/svn/icons/cr128-action-svn_add.png differ diff --git a/kioslave/svn/icons/cr128-action-svn_branch.png b/kioslave/svn/icons/cr128-action-svn_branch.png new file mode 100644 index 00000000..9fafce71 Binary files /dev/null and b/kioslave/svn/icons/cr128-action-svn_branch.png differ diff --git a/kioslave/svn/icons/cr128-action-svn_merge.png b/kioslave/svn/icons/cr128-action-svn_merge.png new file mode 100644 index 00000000..8d534b3a Binary files /dev/null and b/kioslave/svn/icons/cr128-action-svn_merge.png differ diff --git a/kioslave/svn/icons/cr128-action-svn_remove.png b/kioslave/svn/icons/cr128-action-svn_remove.png new file mode 100644 index 00000000..2af59d2f Binary files /dev/null and b/kioslave/svn/icons/cr128-action-svn_remove.png differ diff --git a/kioslave/svn/icons/cr128-action-svn_status.png b/kioslave/svn/icons/cr128-action-svn_status.png new file mode 100644 index 00000000..bcbfaaa3 Binary files /dev/null and b/kioslave/svn/icons/cr128-action-svn_status.png differ diff --git a/kioslave/svn/icons/cr128-action-svn_switch.png b/kioslave/svn/icons/cr128-action-svn_switch.png new file mode 100644 index 00000000..74fbded1 Binary files /dev/null and b/kioslave/svn/icons/cr128-action-svn_switch.png differ diff --git a/kioslave/svn/icons/cr16-action-svn_add.png b/kioslave/svn/icons/cr16-action-svn_add.png new file mode 100644 index 00000000..70769a36 Binary files /dev/null and b/kioslave/svn/icons/cr16-action-svn_add.png differ diff --git a/kioslave/svn/icons/cr16-action-svn_branch.png b/kioslave/svn/icons/cr16-action-svn_branch.png new file mode 100644 index 00000000..f8c701f0 Binary files /dev/null and b/kioslave/svn/icons/cr16-action-svn_branch.png differ diff --git a/kioslave/svn/icons/cr16-action-svn_merge.png b/kioslave/svn/icons/cr16-action-svn_merge.png new file mode 100644 index 00000000..1321a5fd Binary files /dev/null and b/kioslave/svn/icons/cr16-action-svn_merge.png differ diff --git a/kioslave/svn/icons/cr16-action-svn_remove.png b/kioslave/svn/icons/cr16-action-svn_remove.png new file mode 100644 index 00000000..8f6f3040 Binary files /dev/null and b/kioslave/svn/icons/cr16-action-svn_remove.png differ diff --git a/kioslave/svn/icons/cr16-action-svn_status.png b/kioslave/svn/icons/cr16-action-svn_status.png new file mode 100644 index 00000000..8ad00fd7 Binary files /dev/null and b/kioslave/svn/icons/cr16-action-svn_status.png differ diff --git a/kioslave/svn/icons/cr16-action-svn_switch.png b/kioslave/svn/icons/cr16-action-svn_switch.png new file mode 100644 index 00000000..c28b3b98 Binary files /dev/null and b/kioslave/svn/icons/cr16-action-svn_switch.png differ diff --git a/kioslave/svn/icons/cr22-action-svn_add.png b/kioslave/svn/icons/cr22-action-svn_add.png new file mode 100644 index 00000000..249005eb Binary files /dev/null and b/kioslave/svn/icons/cr22-action-svn_add.png differ diff --git a/kioslave/svn/icons/cr22-action-svn_branch.png b/kioslave/svn/icons/cr22-action-svn_branch.png new file mode 100644 index 00000000..db729c2a Binary files /dev/null and b/kioslave/svn/icons/cr22-action-svn_branch.png differ diff --git a/kioslave/svn/icons/cr22-action-svn_merge.png b/kioslave/svn/icons/cr22-action-svn_merge.png new file mode 100644 index 00000000..08b1a53f Binary files /dev/null and b/kioslave/svn/icons/cr22-action-svn_merge.png differ diff --git a/kioslave/svn/icons/cr22-action-svn_remove.png b/kioslave/svn/icons/cr22-action-svn_remove.png new file mode 100644 index 00000000..3005c2ac Binary files /dev/null and b/kioslave/svn/icons/cr22-action-svn_remove.png differ diff --git a/kioslave/svn/icons/cr22-action-svn_status.png b/kioslave/svn/icons/cr22-action-svn_status.png new file mode 100644 index 00000000..5561af4b Binary files /dev/null and b/kioslave/svn/icons/cr22-action-svn_status.png differ diff --git a/kioslave/svn/icons/cr22-action-svn_switch.png b/kioslave/svn/icons/cr22-action-svn_switch.png new file mode 100644 index 00000000..e6a92dcc Binary files /dev/null and b/kioslave/svn/icons/cr22-action-svn_switch.png differ diff --git a/kioslave/svn/icons/cr32-action-svn_add.png b/kioslave/svn/icons/cr32-action-svn_add.png new file mode 100644 index 00000000..63b987bd Binary files /dev/null and b/kioslave/svn/icons/cr32-action-svn_add.png differ diff --git a/kioslave/svn/icons/cr32-action-svn_branch.png b/kioslave/svn/icons/cr32-action-svn_branch.png new file mode 100644 index 00000000..4fbd9c3f Binary files /dev/null and b/kioslave/svn/icons/cr32-action-svn_branch.png differ diff --git a/kioslave/svn/icons/cr32-action-svn_merge.png b/kioslave/svn/icons/cr32-action-svn_merge.png new file mode 100644 index 00000000..0b2b42e2 Binary files /dev/null and b/kioslave/svn/icons/cr32-action-svn_merge.png differ diff --git a/kioslave/svn/icons/cr32-action-svn_remove.png b/kioslave/svn/icons/cr32-action-svn_remove.png new file mode 100644 index 00000000..5a213d57 Binary files /dev/null and b/kioslave/svn/icons/cr32-action-svn_remove.png differ diff --git a/kioslave/svn/icons/cr32-action-svn_status.png b/kioslave/svn/icons/cr32-action-svn_status.png new file mode 100644 index 00000000..5fb3df96 Binary files /dev/null and b/kioslave/svn/icons/cr32-action-svn_status.png differ diff --git a/kioslave/svn/icons/cr32-action-svn_switch.png b/kioslave/svn/icons/cr32-action-svn_switch.png new file mode 100644 index 00000000..28724155 Binary files /dev/null and b/kioslave/svn/icons/cr32-action-svn_switch.png differ diff --git a/kioslave/svn/icons/cr48-action-svn_add.png b/kioslave/svn/icons/cr48-action-svn_add.png new file mode 100644 index 00000000..4f4f7f34 Binary files /dev/null and b/kioslave/svn/icons/cr48-action-svn_add.png differ diff --git a/kioslave/svn/icons/cr48-action-svn_branch.png b/kioslave/svn/icons/cr48-action-svn_branch.png new file mode 100644 index 00000000..d7fe093e Binary files /dev/null and b/kioslave/svn/icons/cr48-action-svn_branch.png differ diff --git a/kioslave/svn/icons/cr48-action-svn_merge.png b/kioslave/svn/icons/cr48-action-svn_merge.png new file mode 100644 index 00000000..a052acb1 Binary files /dev/null and b/kioslave/svn/icons/cr48-action-svn_merge.png differ diff --git a/kioslave/svn/icons/cr48-action-svn_remove.png b/kioslave/svn/icons/cr48-action-svn_remove.png new file mode 100644 index 00000000..df562606 Binary files /dev/null and b/kioslave/svn/icons/cr48-action-svn_remove.png differ diff --git a/kioslave/svn/icons/cr48-action-svn_status.png b/kioslave/svn/icons/cr48-action-svn_status.png new file mode 100644 index 00000000..f98d11eb Binary files /dev/null and b/kioslave/svn/icons/cr48-action-svn_status.png differ diff --git a/kioslave/svn/icons/cr48-action-svn_switch.png b/kioslave/svn/icons/cr48-action-svn_switch.png new file mode 100644 index 00000000..131d897d Binary files /dev/null and b/kioslave/svn/icons/cr48-action-svn_switch.png differ diff --git a/kioslave/svn/icons/cr64-action-svn_add.png b/kioslave/svn/icons/cr64-action-svn_add.png new file mode 100644 index 00000000..54091c32 Binary files /dev/null and b/kioslave/svn/icons/cr64-action-svn_add.png differ diff --git a/kioslave/svn/icons/cr64-action-svn_branch.png b/kioslave/svn/icons/cr64-action-svn_branch.png new file mode 100644 index 00000000..74deadc6 Binary files /dev/null and b/kioslave/svn/icons/cr64-action-svn_branch.png differ diff --git a/kioslave/svn/icons/cr64-action-svn_merge.png b/kioslave/svn/icons/cr64-action-svn_merge.png new file mode 100644 index 00000000..b73ec742 Binary files /dev/null and b/kioslave/svn/icons/cr64-action-svn_merge.png differ diff --git a/kioslave/svn/icons/cr64-action-svn_remove.png b/kioslave/svn/icons/cr64-action-svn_remove.png new file mode 100644 index 00000000..db521dc3 Binary files /dev/null and b/kioslave/svn/icons/cr64-action-svn_remove.png differ diff --git a/kioslave/svn/icons/cr64-action-svn_status.png b/kioslave/svn/icons/cr64-action-svn_status.png new file mode 100644 index 00000000..fba134bc Binary files /dev/null and b/kioslave/svn/icons/cr64-action-svn_status.png differ diff --git a/kioslave/svn/icons/cr64-action-svn_switch.png b/kioslave/svn/icons/cr64-action-svn_switch.png new file mode 100644 index 00000000..84fd6ee5 Binary files /dev/null and b/kioslave/svn/icons/cr64-action-svn_switch.png differ diff --git a/kioslave/svn/icons/crsc-action-svn_add.svgz b/kioslave/svn/icons/crsc-action-svn_add.svgz new file mode 100644 index 00000000..8092028e Binary files /dev/null and b/kioslave/svn/icons/crsc-action-svn_add.svgz differ diff --git a/kioslave/svn/icons/crsc-action-svn_branch.svgz b/kioslave/svn/icons/crsc-action-svn_branch.svgz new file mode 100644 index 00000000..1622c9f6 Binary files /dev/null and b/kioslave/svn/icons/crsc-action-svn_branch.svgz differ diff --git a/kioslave/svn/icons/crsc-action-svn_merge.svgz b/kioslave/svn/icons/crsc-action-svn_merge.svgz new file mode 100644 index 00000000..56f18c44 Binary files /dev/null and b/kioslave/svn/icons/crsc-action-svn_merge.svgz differ diff --git a/kioslave/svn/icons/crsc-action-svn_remove.svgz b/kioslave/svn/icons/crsc-action-svn_remove.svgz new file mode 100644 index 00000000..cf0db832 Binary files /dev/null and b/kioslave/svn/icons/crsc-action-svn_remove.svgz differ diff --git a/kioslave/svn/icons/crsc-action-svn_status.svgz b/kioslave/svn/icons/crsc-action-svn_status.svgz new file mode 100644 index 00000000..1a6ba518 Binary files /dev/null and b/kioslave/svn/icons/crsc-action-svn_status.svgz differ diff --git a/kioslave/svn/icons/crsc-action-svn_switch.svgz b/kioslave/svn/icons/crsc-action-svn_switch.svgz new file mode 100644 index 00000000..4717aac2 Binary files /dev/null and b/kioslave/svn/icons/crsc-action-svn_switch.svgz differ diff --git a/kioslave/svn/ksvnd/Makefile.am b/kioslave/svn/ksvnd/Makefile.am new file mode 100644 index 00000000..a466be08 --- /dev/null +++ b/kioslave/svn/ksvnd/Makefile.am @@ -0,0 +1,13 @@ +#INCLUDES= -I$(top_srcdir)/kwallet/client $(all_includes) +INCLUDES= $(all_includes) + +kde_module_LTLIBRARIES = kded_ksvnd.la + +kded_ksvnd_la_SOURCES = commitdlg.ui ksvnd.cpp ksvnd.skel +kded_ksvnd_la_METASOURCES = AUTO +kded_ksvnd_la_LDFLAGS = $(all_libraries) -module -avoid-version +#kded_ksvnd_la_LIBADD = $(top_builddir)/kwallet/client/libkwalletclient.la $(LIB_KIO) +kded_ksvnd_la_LIBADD = $(LIB_KIO) + +kded_DATA = ksvnd.desktop +kdeddir = $(kde_servicesdir)/kded diff --git a/kioslave/svn/ksvnd/commitdlg.ui b/kioslave/svn/ksvnd/commitdlg.ui new file mode 100644 index 00000000..7425e5c9 --- /dev/null +++ b/kioslave/svn/ksvnd/commitdlg.ui @@ -0,0 +1,116 @@ + +CommitDlg + + + CommitDlg + + + + 0 + 0 + 451 + 337 + + + + Log Message + + + + unnamed + + + + layout2 + + + + unnamed + + + + textMessage + + + + + listMessage + + + true + + + + + layout1 + + + + unnamed + + + + spacer1 + + + Horizontal + + + Expanding + + + + 220 + 20 + + + + + + pushButton1 + + + &OK + + + + + pushButton2 + + + &Cancel + + + + + + + + + + + pushButton1 + clicked() + CommitDlg + accept() + + + pushButton2 + clicked() + CommitDlg + reject() + + + + commitdlg.ui.h + + + setLog( const QString & comment ) + logMessage() const + + + + ktextedit.h + ktextedit.h + + diff --git a/kioslave/svn/ksvnd/commitdlg.ui.h b/kioslave/svn/ksvnd/commitdlg.ui.h new file mode 100644 index 00000000..296d55c0 --- /dev/null +++ b/kioslave/svn/ksvnd/commitdlg.ui.h @@ -0,0 +1,30 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Mickael Marchand + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + + +void CommitDlg::setLog( const QString & comment ) +{ +listMessage->setText(comment); +} + + +QString CommitDlg::logMessage() const +{ +return textMessage->text(); +} diff --git a/kioslave/svn/ksvnd/ksvnd.cpp b/kioslave/svn/ksvnd/ksvnd.cpp new file mode 100644 index 00000000..c9701647 --- /dev/null +++ b/kioslave/svn/ksvnd/ksvnd.cpp @@ -0,0 +1,351 @@ +/* + This file is part of the KDE Project + + Copyright (C) 2003, 2004 Mickael Marchand + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + + This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#include "ksvnd.h" +#include "commitdlg.h" + +extern "C" { + KDE_EXPORT KDEDModule *create_ksvnd(const QCString &name) { + return new KSvnd(name); + } +} + +KSvnd::KSvnd(const QCString &name) + : KDEDModule(name) { +} + +KSvnd::~KSvnd() { +} + +QString KSvnd::commitDialog(QString modifiedFiles) { + CommitDlg commitDlg; + commitDlg.setLog( modifiedFiles ); + int result = commitDlg.exec(); + if ( result == QDialog::Accepted ) { + return commitDlg.logMessage(); + } else + return QString::null; +} + +bool KSvnd::AreAnyFilesInSvn( const KURL::List& wclist ) { + for ( QValueListConstIterator it = wclist.begin(); it != wclist.end() ; ++it ) { + kdDebug( 7128 ) << "Checking file " << ( *it ) << endl; + QDir bdir ( ( *it ).path() ); + if ( bdir.exists() && QFile::exists( ( *it ).path() + "/.svn/entries" ) ) { + return true; + } else if ( !bdir.exists() ) { + if ( isFileInSvnEntries( ( *it ).fileName(), ( *it ).directory() + "/.svn/entries" ) || isFileInExternals ( ( *it ).fileName(), ( *it ).directory()+"/.svn/dir-props" ) ) + return true; + } + } + return false; +} + +bool KSvnd::AreAnyFilesNotInSvn( const KURL::List& wclist ) { + for ( QValueListConstIterator it = wclist.begin(); it != wclist.end() ; ++it ) { + kdDebug( 7128 ) << "Checking file " << ( *it ) << endl; + QDir bdir ( ( *it ).path() ); + if ( bdir.exists() && !QFile::exists( ( *it ).path() + "/.svn/entries" ) ) { + return true; + } else if ( !bdir.exists() ) { + if ( !isFileInSvnEntries( ( *it ).fileName(),( *it ).directory() + "/.svn/entries" ) && !isFileInExternals ( ( *it ).fileName(), ( *it ).directory()+"/.svn/dir-props" ) ) + return true; + } + } + return false; +} + +bool KSvnd::AreAllFilesInSvn( const KURL::List& wclist ) { + for ( QValueListConstIterator it = wclist.begin(); it != wclist.end() ; ++it ) { + kdDebug( 7128 ) << "Checking file " << ( *it ) << endl; + QDir bdir ( ( *it ).path() ); + if ( bdir.exists() && !QFile::exists( ( *it ).path() + "/.svn/entries" ) ) { + return false; + } else if ( !bdir.exists() ) { + if ( !isFileInSvnEntries( ( *it ).fileName(),( *it ).directory() + "/.svn/entries" ) && !isFileInExternals ( ( *it ).fileName(), ( *it ).directory()+"/.svn/dir-props" ) ) + return false; + } + } + return true; +} + +bool KSvnd::AreAllFilesNotInSvn( const KURL::List& wclist ) { + for ( QValueListConstIterator it = wclist.begin(); it != wclist.end() ; ++it ) { + kdDebug( 7128 ) << "Checking file " << ( *it ) << endl; + QDir bdir ( ( *it ).path() ); + if ( bdir.exists() && QFile::exists( ( *it ).path() + "/.svn/entries" ) ) { + return false; + } else if ( !bdir.exists() ) { + if ( isFileInSvnEntries( ( *it ).fileName(),( *it ).directory() + "/.svn/entries" ) || isFileInExternals ( ( *it ).fileName(), ( *it ).directory()+"/.svn/dir-props" ) ) + return false; + } + } + return true; +} + +bool KSvnd::isFileInSvnEntries ( const QString filename, const QString entfile ) { + QFile file( entfile ); + if ( file.open( IO_ReadOnly ) ) { + QTextStream stream( &file ); + QString line; + while ( !stream.atEnd() ) { + line = stream.readLine().simplifyWhiteSpace(); + if ( line == "name=\""+ filename + "\"" ) { + file.close(); + return true; + } + } + file.close(); + } + return false; +} + +bool KSvnd::isFileInExternals ( const QString filename, const QString propfile ) { + QFile file( propfile ); + if ( file.open( IO_ReadOnly ) ) { + QTextStream stream( &file ); + QStringList line; + while ( !stream.atEnd() ) + line << stream.readLine().simplifyWhiteSpace(); + for ( uint i = 0 ; i < line.count(); i++ ) { + if ( line[ i ] == "K 13" && line[ i+1 ] == "svn:externals" ) { //Key 13 : svn:externals + //next line should be "V xx" + if ( line [ i+2 ].startsWith( "V " ) ) { + //ok browse the values now + i+=2; + while ( i < line.count() ) { + if ( line[ i ].startsWith( filename+" " ) ) { //found it ! + file.close( ); + return true; + } else if ( line[ i ].isEmpty() ) { + file.close( ); + return false; //we are out of svn:externals now... + } + i++; + } + } + } + } + file.close(); + } + return false; +} + +bool KSvnd::anyNotValidWorkingCopy( const KURL::List& wclist ) { + bool result = true; //one negative match is enough + for ( QValueListConstIterator it = wclist.begin(); it != wclist.end() ; ++it ) { + //exception for .svn dirs + if ( ( *it ).path(-1).endsWith( "/.svn" ) ) + return true; + //if is a directory check whether it contains a .svn/entries file + QDir dir( ( *it ).path() ); + if ( dir.exists() ) { //it's a dir + if ( !QFile::exists( ( *it ).path() + "/.svn/entries" ) ) + result = false; + } + + //else check if ./.svn/entries exists + if ( !QFile::exists( ( *it ).directory() + "/.svn/entries" ) ) + result = false; + } + return result; +} + +bool KSvnd::anyValidWorkingCopy( const KURL::List& wclist ) { + for ( QValueListConstIterator it = wclist.begin(); it != wclist.end() ; ++it ) { + //skip .svn dirs + if ( ( *it ).path(-1).endsWith( "/.svn" ) ) + continue; + //if is a directory check whether it contains a .svn/entries file + QDir dir( ( *it ).path() ); + if ( dir.exists() ) { //it's a dir + if ( QFile::exists( ( *it ).path() + "/.svn/entries" ) ) + return true; + } + + //else check if ./.svn/entries exists + if ( QFile::exists( ( *it ).directory() + "/.svn/entries" ) ) + return true; + } + return false; +} + +int KSvnd::getStatus( const KURL::List& list ) { + int result = 0; + uint files = 0, folders = 0, parentsentries = 0, parentshavesvn = 0, subdirhavesvn = 0, external = 0; + for ( QValueListConstIterator it = list.begin(); it != list.end() ; ++it ) { + if ( isFolder ( ( *it ) ) ) { + folders++; + } else { + files++; + } + if ( isFileInSvnEntries ( (*it).filename(),( *it ).directory() + "/.svn/entries" ) ) { // normal subdir known in the working copy + parentsentries++; + } else if ( isFolder( *it ) ) { // other subfolders (either another module checkouted or an external, or something not known at all) + if ( QFile::exists( ( *it ).path() + "/.svn/entries" ) ) + subdirhavesvn++; + if ( isFileInExternals( (*it).filename(), ( *it ).directory() + "/.svn/dir-props" ) ) { + external++; + } + } + if ( ( isFolder( ( *it ) ) && QFile::exists( ( *it ).directory() + "../.svn/entries" ) ) || QFile::exists( ( *it ).directory() + "/.svn/entries" ) ) //parent has a .svn ? + parentshavesvn++; + } + if ( files > 0 ) + result |= SomeAreFiles; + if ( folders == list.count() ) { + result |= AllAreFolders; + result |= SomeAreFolders; + } + if ( folders > 0 ) + result |= SomeAreFolders; + if ( parentsentries == list.count() ) { + result |= AllAreInParentsEntries; + result |= SomeAreInParentsEntries; + } else if ( parentsentries != 0 ) + result |= SomeAreInParentsEntries; + if ( parentshavesvn == list.count() ) { + result |= AllParentsHaveSvn; + result |= SomeParentsHaveSvn; + } else if ( parentshavesvn > 0 ) + result |= SomeParentsHaveSvn; + if ( subdirhavesvn == list.count() ) { + result |= AllHaveSvn; + result |= SomeHaveSvn; + } else if ( subdirhavesvn > 0 ) + result |= SomeHaveSvn; + if ( external == list.count() ) { + result |= AllAreExternalToParent; + result |= SomeAreExternalToParent; + } else if ( external > 0 ) + result |= SomeAreExternalToParent; + + return result; +} + +bool KSvnd::isFolder( const KURL& url ) { + QDir d( url.path() ); + return d.exists(); +} + +QStringList KSvnd::getActionMenu ( const KURL::List &list ) { + QStringList result; + int listStatus = getStatus( list ); + + if ( !(listStatus & SomeAreInParentsEntries) && + !(listStatus & SomeAreExternalToParent) && + !(listStatus & SomeHaveSvn)) { + if( list.size() == 1 && listStatus & SomeAreFolders) { + result << "Checkout"; + result << "Export"; +// result << "CreateRepository"; + result << "Import"; + } + } else if ( (listStatus & AllAreInParentsEntries) ) { + result << "Diff"; + //In SVN +// result << "ShowLog"; +// result << "CheckForModifications"; +// result << "RevisionGraph"; +// result << "_SEPARATOR_"; +// result << "Update to revision..." + result << "Rename"; + result << "Delete"; + if( listStatus & SomeAreFolders && !(listStatus & SomeAreFiles)) { + result << "Revert"; +// result << "Cleanup"; + } + result << "_SEPARATOR_"; +// result << "BranchTag"; + result << "Switch"; + result << "Merge"; + if( listStatus & SomeAreFolders && !(listStatus & SomeAreFiles)) { +// result << "Export"; +// result << "Relocate"; + result << "_SEPARATOR_"; + result << "Add"; + } + result << "_SEPARATOR_"; + if( listStatus & SomeAreFiles && !(listStatus & SomeAreFolders)) { + result << "Blame"; + } + result << "CreatePatch"; + + if( list.size() == 1 && listStatus & SomeAreFolders) { +// result << "ApplyPatchToFolder"; + } + } + return result; +} + +QStringList KSvnd::getTopLevelActionMenu ( const KURL::List &list ) { + QStringList result; + int listStatus = getStatus( list ); + + + if ( ( listStatus & AllParentsHaveSvn && + ( ( listStatus & SomeAreExternalToParent ) || ( listStatus & SomeAreInParentsEntries ) ) + || ( listStatus & SomeHaveSvn ) ) + ) { + result << "Update"; + result << "Commit"; + } + + return result; +} + +#if 0 +void KSvnd::notify(const QString& path, int action, int kind, const QString& mime_type, int content_state, int prop_state, long int revision, const QString& userstring) { + kdDebug(7128) << "KDED/Subversion : notify " << path << " action : " << action << " mime_type : " << mime_type << " content_state : " << content_state << " prop_state : " << prop_state << " revision : " << revision << " userstring : " << userstring << endl; + QByteArray params; + + QDataStream stream(params, IO_WriteOnly); + stream << path << action << kind << mime_type << content_state << prop_state << revision << userstring; + + emitDCOPSignal( "subversionNotify(QString,int,int,QString,int,int,long int,QString)", params ); +} + +void KSvnd::status(const QString& path, int text_status, int prop_status, int repos_text_status, int repos_prop_status, long int rev ) { + kdDebug(7128) << "KDED/Subversion : status " << path << " " << text_status << " " << prop_status << " " + << repos_text_status << " " << repos_prop_status << " " << rev << endl; + QByteArray params; + + QDataStream stream(params, IO_WriteOnly); + stream << path << text_status << prop_status << repos_text_status << repos_prop_status << rev; + + emitDCOPSignal( "subversionStatus(QString,int,int,int,int,long int)", params ); +} + +void KSvnd::popupMessage( const QString& message ) { + kdDebug(7128) << "KDED/Subversion : popupMessage" << message << endl; + KMessageBox::information(0, message, i18n( "Subversion" ) ); +} +#endif + +#include "ksvnd.moc" diff --git a/kioslave/svn/ksvnd/ksvnd.desktop b/kioslave/svn/ksvnd/ksvnd.desktop new file mode 100644 index 00000000..d2c942d4 --- /dev/null +++ b/kioslave/svn/ksvnd/ksvnd.desktop @@ -0,0 +1,50 @@ +[Desktop Entry] +Type=Service +Name=KDED Subversion Module +Name[bg]=Модул KDED Subversion +Name[br]=Mollad Subversion KDED +Name[ca]=Mòdul KDED de Subversion +Name[cs]=KDED Subversion modul +Name[da]=KDED Subversion modul +Name[de]=Subversion-Unterstützung +Name[el]=Άρθρωμα KDED Subversion +Name[es]=Módulo de Subversion de KDED +Name[et]=KDED Subversioni moodul +Name[eu]=KDED Subversion modulua +Name[fa]=پیمانۀ زیرنسخۀ KDED +Name[fi]=KDED Subversion -moduuli +Name[fr]=Module KDED Subversion +Name[ga]=Modúl Subversion KDED +Name[gl]=Módulo KDED Subversion +Name[hu]=KDED Subversion modul +Name[is]=KDED Subversion eining +Name[it]=Modulo Subversion di KDED +Name[ja]=KDED Subversion モジュール +Name[ka]=KDED Subversion მოდული +Name[kk]=KDED Subversion қызмет модулі +Name[lt]=KDED Subversion modulis +Name[nb]=KDED Subversion-modul +Name[nds]=KDED Subversion-Moduul +Name[ne]=केडीईडी उप संस्करण मोड्युल +Name[nl]=KDED Subversion-module +Name[nn]=KDED Subversion-modul +Name[pa]=KDED ਸਬ-ਵਰਜਨ ਮੈਡੀਊਲ +Name[pl]=Moduł Subversion dla KDED +Name[pt]=Módulo KDED Subversion +Name[pt_BR]=Módulo de Sub-versão KDED +Name[ru]=Служба Subversion +Name[sk]=KDED Subversion modul +Name[sl]=Modul Subversion za KDED +Name[sr]=KDED модул за Subversion +Name[sr@Latn]=KDED modul za Subversion +Name[sv]=KDED Subversion-modul +Name[tr]=KDED Alt Versiyon Modülü +Name[uk]=Модуль KDED Subversion +Name[zh_CN]=KDED Subversion 模块 +Name[zh_TW]=KDED Subversion 模組 +ServiceTypes=KDEDModule +X-KDE-ModuleType=Library +X-KDE-Library=ksvnd +X-KDE-FactoryName=ksvnd +X-KDE-Kded-autoload=true +X-KDE-Kded-load-on-demand=true diff --git a/kioslave/svn/ksvnd/ksvnd.h b/kioslave/svn/ksvnd/ksvnd.h new file mode 100644 index 00000000..20bb7e9a --- /dev/null +++ b/kioslave/svn/ksvnd/ksvnd.h @@ -0,0 +1,69 @@ +/* + This file is part of the KDE Project + + Copyright (C) 2003-2005 Mickael Marchand + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + + This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef KSVND_H +#define KSVND_H + +#include +#include +#include +#include + +class KSvnd : public KDEDModule +{ + Q_OBJECT + K_DCOP + + //note: InSVN means parent is added. InRepos means itself is added + enum { SomeAreFiles = 1, SomeAreFolders = 2, SomeAreInParentsEntries = 4, SomeParentsHaveSvn = 8, SomeHaveSvn = 16, SomeAreExternalToParent = 32, AllAreInParentsEntries = 64, AllParentsHaveSvn = 128, AllHaveSvn = 256, AllAreExternalToParent = 512, AllAreFolders = 1024 }; +public: + KSvnd(const QCString &); + ~KSvnd(); + +k_dcop: +// void addAuthInfo(KIO::AuthInfo, long); + QString commitDialog(QString); + bool anyNotValidWorkingCopy( const KURL::List& wclist ); + bool anyValidWorkingCopy( const KURL::List& wclist ); + bool AreAnyFilesNotInSvn( const KURL::List& wclist ); + bool AreAnyFilesInSvn( const KURL::List& wclist ); + bool AreAllFilesNotInSvn( const KURL::List& wclist ); + bool AreAllFilesInSvn( const KURL::List& wclist ); + QStringList getActionMenu ( const KURL::List& list ); + QStringList getTopLevelActionMenu ( const KURL::List &list ); +// void notify(const QString&, int ,int, const QString& , int , int, long int, const QString&); +// void status(const QString& path, int text_status, int prop_status, int repos_text_status, int repos_prop_status ,long int rev); +// void popupMessage( const QString& message ); + +k_dcop_signals: + //emitted whenever something happens using subversion ;) +// void subversionNotify(const QString&, int ,int, const QString& , int , int, long int, const QString&); +// void subversionStatus(const QString&,int,int,int,int,long int); + +public slots: + +protected: + bool isFileInSvnEntries ( const QString filename, const QString entfile ); + bool isFileInExternals ( const QString filename, const QString propfile ); + bool isFolder( const KURL& url ); + int getStatus( const KURL::List& list ); +}; + +#endif diff --git a/kioslave/svn/svn+file.protocol b/kioslave/svn/svn+file.protocol new file mode 100644 index 00000000..ff8edc5e --- /dev/null +++ b/kioslave/svn/svn+file.protocol @@ -0,0 +1,43 @@ +[Protocol] +exec=kio_svn +protocol=svn+file +input=none +output=filesystem +reading=true +writing=true +deleting=true +makedir=true +linking=false +moving=true +listing=Name,Size,Date,Owner +defaultMimetype=application/octet-stream +Icon=remote +Description=Subversion ioslave +Description[br]=Sklav E/D Subversion +Description[ca]=Ioslave de Subversion +Description[cs]=Subversion protokol +Description[de]=Ein-/Ausgabemodul für Subversion +Description[es]=El ioslave de Subversion +Description[et]=Subversioni IO-moodul +Description[fr]=ioslave subversion +Description[ga]=ioslave Subversion +Description[gl]=Ioslave para Subversíon +Description[hu]=Subversion KDE-protokoll +Description[it]=Slave I/O di Subversion +Description[lt]=Subversion įvesties-išvesties priedas +Description[nb]=Subversion-iu-slave +Description[nds]=In-/Utgaavmoduul för Subversion +Description[ne]=उप संस्करण ioslave +Description[nn]=Subversion-iu-slave +Description[pl]=Wtyczka protokołu Subversion +Description[pt]='Ioslave' para Subversion +Description[pt_BR]=ioslave de Subversão +Description[ru]=Доступ к хранилищу Subversion +Description[sl]=ioslave za Subversion +Description[sr]=IOSlave за Subversion +Description[sr@Latn]=IOSlave za Subversion +Description[sv]=Subversion I/O-slav +Description[tr]=Alt Version ioslave +Description[uk]=Підлеглий В/В Subversion +maxInstances=5 +class=:internet diff --git a/kioslave/svn/svn+http.protocol b/kioslave/svn/svn+http.protocol new file mode 100644 index 00000000..656fa217 --- /dev/null +++ b/kioslave/svn/svn+http.protocol @@ -0,0 +1,43 @@ +[Protocol] +exec=kio_svn +protocol=svn+http +input=none +output=filesystem +reading=true +writing=true +deleting=true +makedir=true +linking=false +moving=true +listing=Name,Size,Date,Owner +defaultMimetype=application/octet-stream +Icon=remote +Description=Subversion ioslave +Description[br]=Sklav E/D Subversion +Description[ca]=Ioslave de Subversion +Description[cs]=Subversion protokol +Description[de]=Ein-/Ausgabemodul für Subversion +Description[es]=El ioslave de Subversion +Description[et]=Subversioni IO-moodul +Description[fr]=ioslave subversion +Description[ga]=ioslave Subversion +Description[gl]=Ioslave para Subversíon +Description[hu]=Subversion KDE-protokoll +Description[it]=Slave I/O di Subversion +Description[lt]=Subversion įvesties-išvesties priedas +Description[nb]=Subversion-iu-slave +Description[nds]=In-/Utgaavmoduul för Subversion +Description[ne]=उप संस्करण ioslave +Description[nn]=Subversion-iu-slave +Description[pl]=Wtyczka protokołu Subversion +Description[pt]='Ioslave' para Subversion +Description[pt_BR]=ioslave de Subversão +Description[ru]=Доступ к хранилищу Subversion +Description[sl]=ioslave za Subversion +Description[sr]=IOSlave за Subversion +Description[sr@Latn]=IOSlave za Subversion +Description[sv]=Subversion I/O-slav +Description[tr]=Alt Version ioslave +Description[uk]=Підлеглий В/В Subversion +maxInstances=5 +class=:internet diff --git a/kioslave/svn/svn+https.protocol b/kioslave/svn/svn+https.protocol new file mode 100644 index 00000000..e299958c --- /dev/null +++ b/kioslave/svn/svn+https.protocol @@ -0,0 +1,43 @@ +[Protocol] +exec=kio_svn +protocol=svn+https +input=none +output=filesystem +reading=true +writing=true +deleting=true +makedir=true +linking=false +moving=true +listing=Name,Size,Date,Owner +defaultMimetype=application/octet-stream +Icon=remote +Description=Subversion ioslave +Description[br]=Sklav E/D Subversion +Description[ca]=Ioslave de Subversion +Description[cs]=Subversion protokol +Description[de]=Ein-/Ausgabemodul für Subversion +Description[es]=El ioslave de Subversion +Description[et]=Subversioni IO-moodul +Description[fr]=ioslave subversion +Description[ga]=ioslave Subversion +Description[gl]=Ioslave para Subversíon +Description[hu]=Subversion KDE-protokoll +Description[it]=Slave I/O di Subversion +Description[lt]=Subversion įvesties-išvesties priedas +Description[nb]=Subversion-iu-slave +Description[nds]=In-/Utgaavmoduul för Subversion +Description[ne]=उप संस्करण ioslave +Description[nn]=Subversion-iu-slave +Description[pl]=Wtyczka protokołu Subversion +Description[pt]='Ioslave' para Subversion +Description[pt_BR]=ioslave de Subversão +Description[ru]=Доступ к хранилищу Subversion +Description[sl]=ioslave za Subversion +Description[sr]=IOSlave за Subversion +Description[sr@Latn]=IOSlave za Subversion +Description[sv]=Subversion I/O-slav +Description[tr]=Alt Version ioslave +Description[uk]=Підлеглий В/В Subversion +maxInstances=5 +class=:internet diff --git a/kioslave/svn/svn+ssh.protocol b/kioslave/svn/svn+ssh.protocol new file mode 100644 index 00000000..7c985da9 --- /dev/null +++ b/kioslave/svn/svn+ssh.protocol @@ -0,0 +1,43 @@ +[Protocol] +exec=kio_svn +protocol=svn+ssh +input=none +output=filesystem +reading=true +writing=true +deleting=true +makedir=true +linking=false +moving=true +listing=Name,Size,Date,Owner +defaultMimetype=application/octet-stream +Icon=remote +Description=Subversion ioslave +Description[br]=Sklav E/D Subversion +Description[ca]=Ioslave de Subversion +Description[cs]=Subversion protokol +Description[de]=Ein-/Ausgabemodul für Subversion +Description[es]=El ioslave de Subversion +Description[et]=Subversioni IO-moodul +Description[fr]=ioslave subversion +Description[ga]=ioslave Subversion +Description[gl]=Ioslave para Subversíon +Description[hu]=Subversion KDE-protokoll +Description[it]=Slave I/O di Subversion +Description[lt]=Subversion įvesties-išvesties priedas +Description[nb]=Subversion-iu-slave +Description[nds]=In-/Utgaavmoduul för Subversion +Description[ne]=उप संस्करण ioslave +Description[nn]=Subversion-iu-slave +Description[pl]=Wtyczka protokołu Subversion +Description[pt]='Ioslave' para Subversion +Description[pt_BR]=ioslave de Subversão +Description[ru]=Доступ к хранилищу Subversion +Description[sl]=ioslave za Subversion +Description[sr]=IOSlave за Subversion +Description[sr@Latn]=IOSlave za Subversion +Description[sv]=Subversion I/O-slav +Description[tr]=Alt Version ioslave +Description[uk]=Підлеглий В/В Subversion +maxInstances=5 +class=:internet diff --git a/kioslave/svn/svn.cpp b/kioslave/svn/svn.cpp new file mode 100644 index 00000000..8ff9496e --- /dev/null +++ b/kioslave/svn/svn.cpp @@ -0,0 +1,1593 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Mickael Marchand + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "svn.h" +#include + +using namespace KIO; + +typedef struct +{ + /* Holds the directory that corresponds to the REPOS_URL at RA->open() + * time. When callbacks specify a relative path, they are joined with + * this base directory. */ + const char *base_dir; + svn_wc_adm_access_t *base_access; + + /* An array of svn_client_commit_item_t * structures, present only + * during working copy commits. */ + apr_array_header_t *commit_items; + + /* A hash of svn_config_t's, keyed off file name (i.e. the contents of + * ~/.subversion/config end up keyed off of 'config'). */ + apr_hash_t *config; + + /* The pool to use for session-related items. */ + apr_pool_t *pool; + +} svn_client__callback_baton_t; + +static svn_error_t * +open_tmp_file (apr_file_t **fp, + void *callback_baton, + apr_pool_t *pool) +{ + svn_client__callback_baton_t *cb = (svn_client__callback_baton_t *) callback_baton; + const char *truepath; + const char *ignored_filename; + + if (cb->base_dir) + truepath = apr_pstrdup (pool, cb->base_dir); + else + truepath = ""; + + /* Tack on a made-up filename. */ + truepath = svn_path_join (truepath, "tempfile", pool); + + /* Open a unique file; use APR_DELONCLOSE. */ + SVN_ERR (svn_io_open_unique_file (fp, &ignored_filename, + truepath, ".tmp", TRUE, pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t *write_to_string(void *baton, const char *data, apr_size_t *len) { + kbaton *tb = ( kbaton* )baton; + svn_stringbuf_appendbytes(tb->target_string, data, *len); + return SVN_NO_ERROR; +} + +static int +compare_items_as_paths (const svn_sort__item_t*a, const svn_sort__item_t*b) { + return svn_path_compare_paths ((const char *)a->key, (const char *)b->key); +} + +kio_svnProtocol::kio_svnProtocol(const QCString &pool_socket, const QCString &app_socket) + : SlaveBase("kio_svn", pool_socket, app_socket) { + kdDebug(7128) << "kio_svnProtocol::kio_svnProtocol()" << endl; + + m_counter = 0; + + apr_initialize(); + // CleanUP ctx preventing crash in svn_client_update and other + memset(&ctx, 0, sizeof(ctx)); + pool = svn_pool_create (NULL); + + svn_error_t *err = svn_client_create_context(&ctx, pool); + if ( err ) { + kdDebug(7128) << "kio_svnProtocol::kio_svnProtocol() create_context ERROR" << endl; + error( KIO::ERR_SLAVE_DEFINED, err->message ); + return; + } + + err = svn_config_ensure (NULL,pool); + if ( err ) { + kdDebug(7128) << "kio_svnProtocol::kio_svnProtocol() configensure ERROR" << endl; + error( KIO::ERR_SLAVE_DEFINED, err->message ); + return; + } + svn_config_get_config (&ctx->config, NULL, pool); + + ctx->log_msg_func = kio_svnProtocol::commitLogPrompt; + ctx->log_msg_baton = this; //pass this so that we can get a dcopClient from it + //TODO + ctx->cancel_func = NULL; + + apr_array_header_t *providers = apr_array_make(pool, 9, sizeof(svn_auth_provider_object_t *)); + + svn_auth_provider_object_t *provider; + + //disk cache + svn_client_get_simple_provider(&provider,pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t*) = provider; + svn_client_get_username_provider(&provider,pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t*) = provider; + + //interactive prompt + svn_client_get_simple_prompt_provider (&provider,kio_svnProtocol::checkAuth,this,2,pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t*) = provider; + //we always ask user+pass, no need for a user only question +/* svn_client_get_username_prompt_provider + * (&provider,kio_svnProtocol::checkAuth,this,2,pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t*) = provider;*/ + + //SSL disk cache, keep that one, because it does nothing bad :) + svn_client_get_ssl_server_trust_file_provider (&provider, pool); + APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider; + svn_client_get_ssl_client_cert_file_provider (&provider, pool); + APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider; + svn_client_get_ssl_client_cert_pw_file_provider (&provider, pool); + APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider; + + //SSL interactive prompt, where things get hard + svn_client_get_ssl_server_trust_prompt_provider (&provider, kio_svnProtocol::trustSSLPrompt, NULL, pool); + APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider; + svn_client_get_ssl_client_cert_prompt_provider (&provider, kio_svnProtocol::clientCertSSLPrompt, NULL, 2, pool); + APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider; + svn_client_get_ssl_client_cert_pw_prompt_provider (&provider, kio_svnProtocol::clientCertPasswdPrompt, NULL, 2, pool); + APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider; + + svn_auth_open(&ctx->auth_baton, providers, pool); +} + +kio_svnProtocol::~kio_svnProtocol(){ + kdDebug(7128) << "kio_svnProtocol::~kio_svnProtocol()" << endl; + svn_pool_destroy(pool); + apr_terminate(); +} + +void kio_svnProtocol::initNotifier(bool is_checkout, bool is_export, bool suppress_final_line, apr_pool_t *spool) { + m_counter=0;//reset counter + ctx->notify_func = kio_svnProtocol::notify; + struct notify_baton *nb = ( struct notify_baton* )apr_palloc(spool, sizeof( *nb ) ); + nb->master = this; + nb->received_some_change = FALSE; + nb->sent_first_txdelta = FALSE; + nb->is_checkout = is_checkout; + nb->is_export = is_export; + nb->suppress_final_line = suppress_final_line; + nb->in_external = FALSE; + nb->had_print_error = FALSE; + nb->pool = svn_pool_create (spool); + + ctx->notify_baton = nb; +} + +svn_error_t* kio_svnProtocol::checkAuth(svn_auth_cred_simple_t **cred, void *baton, const char *realm, const char *username, svn_boolean_t /*may_save*/, apr_pool_t *pool) { + kdDebug(7128) << "kio_svnProtocol::checkAuth() for " << realm << endl; + kio_svnProtocol *p = ( kio_svnProtocol* )baton; + svn_auth_cred_simple_t *ret = (svn_auth_cred_simple_t*)apr_pcalloc (pool, sizeof (*ret)); + +// p->info.keepPassword = true; + p->info.verifyPath=true; + kdDebug(7128 ) << "auth current URL : " << p->myURL.url() << endl; + p->info.url = p->myURL; + p->info.username = username; //( const char* )svn_auth_get_parameter( p->ctx->auth_baton, SVN_AUTH_PARAM_DEFAULT_USERNAME ); +// if ( !p->checkCachedAuthentication( p->info ) ){ + p->openPassDlg( p->info ); +// } + ret->username = apr_pstrdup(pool, p->info.username.utf8()); + ret->password = apr_pstrdup(pool, p->info.password.utf8()); + ret->may_save = true; + *cred = ret; + return SVN_NO_ERROR; +} + +void kio_svnProtocol::recordCurrentURL(const KURL& url) { + myURL = url; +} + +//don't implement mimeType() until we don't need to download the whole file + +void kio_svnProtocol::get(const KURL& url ){ + kdDebug(7128) << "kio_svn::get(const KURL& url)" << endl ; + + QString remoteServer = url.host(); + infoMessage(i18n("Looking for %1...").arg( remoteServer ) ); + + apr_pool_t *subpool = svn_pool_create (pool); + kbaton *bt = (kbaton*)apr_pcalloc(subpool, sizeof(*bt)); + bt->target_string = svn_stringbuf_create("", subpool); + bt->string_stream = svn_stream_create(bt,subpool); + svn_stream_set_write(bt->string_stream,write_to_string); + + QString target = makeSvnURL( url ); + kdDebug(7128) << "SvnURL: " << target << endl; + recordCurrentURL( KURL( target ) ); + + //find the requested revision + svn_opt_revision_t rev; + svn_opt_revision_t endrev; + int idx = target.findRev( "?rev=" ); + if ( idx != -1 ) { + QString revstr = target.mid( idx+5 ); +#if 0 + kdDebug(7128) << "revision string found " << revstr << endl; + if ( revstr == "HEAD" ) { + rev.kind = svn_opt_revision_head; + kdDebug(7128) << "revision searched : HEAD" << endl; + } else { + rev.kind = svn_opt_revision_number; + rev.value.number = revstr.toLong(); + kdDebug(7128) << "revision searched : " << rev.value.number << endl; + } +#endif + svn_opt_parse_revision( &rev, &endrev, revstr.utf8(), subpool ); + target = target.left( idx ); + kdDebug(7128) << "new target : " << target << endl; + } else { + kdDebug(7128) << "no revision given. searching HEAD " << endl; + rev.kind = svn_opt_revision_head; + } + initNotifier(false, false, false, subpool); + + svn_error_t *err = svn_client_cat (bt->string_stream, svn_path_canonicalize( target.utf8(),subpool ),&rev,ctx, subpool); + if ( err ) { + error( KIO::ERR_SLAVE_DEFINED, err->message ); + svn_pool_destroy( subpool ); + return; + } + + // Send the mimeType as soon as it is known + QByteArray *cp = new QByteArray(); + cp->setRawData( bt->target_string->data, bt->target_string->len ); + KMimeType::Ptr mt = KMimeType::findByContent(*cp); + kdDebug(7128) << "KMimeType returned : " << mt->name() << endl; + mimeType( mt->name() ); + + totalSize(bt->target_string->len); + + //send data + data(*cp); + + data(QByteArray()); // empty array means we're done sending the data + finished(); + svn_pool_destroy (subpool); +} + +void kio_svnProtocol::stat(const KURL & url){ + kdDebug(7128) << "kio_svn::stat(const KURL& url) : " << url.url() << endl ; + + void *ra_baton, *session; + svn_ra_plugin_t *ra_lib; + svn_node_kind_t kind; + apr_pool_t *subpool = svn_pool_create (pool); + + QString target = makeSvnURL( url); + kdDebug(7128) << "SvnURL: " << target << endl; + recordCurrentURL( KURL( target ) ); + + //find the requested revision + svn_opt_revision_t rev; + svn_opt_revision_t endrev; + int idx = target.findRev( "?rev=" ); + if ( idx != -1 ) { + QString revstr = target.mid( idx+5 ); +#if 0 + kdDebug(7128) << "revision string found " << revstr << endl; + if ( revstr == "HEAD" ) { + rev.kind = svn_opt_revision_head; + kdDebug(7128) << "revision searched : HEAD" << endl; + } else { + rev.kind = svn_opt_revision_number; + rev.value.number = revstr.toLong(); + kdDebug(7128) << "revision searched : " << rev.value.number << endl; + } +#endif + svn_opt_parse_revision( &rev, &endrev, revstr.utf8( ), subpool ); + target = target.left( idx ); + kdDebug(7128) << "new target : " << target << endl; + } else { + kdDebug(7128) << "no revision given. searching HEAD " << endl; + rev.kind = svn_opt_revision_head; + } + + //init + svn_error_t *err = svn_ra_init_ra_libs(&ra_baton,subpool); + if ( err ) { + kdDebug(7128) << "init RA libs failed : " << err->message << endl; + return; + } + //find RA libs + err = svn_ra_get_ra_library(&ra_lib,ra_baton,svn_path_canonicalize( target.utf8(), subpool ),subpool); + if ( err ) { + kdDebug(7128) << "RA get libs failed : " << err->message << endl; + return; + } + kdDebug(7128) << "RA init completed" << endl; + + //start session + svn_ra_callbacks_t *cbtable = (svn_ra_callbacks_t*)apr_pcalloc(subpool, sizeof(*cbtable)); + kio_svn_callback_baton_t *callbackbt = (kio_svn_callback_baton_t*)apr_pcalloc(subpool, sizeof( *callbackbt )); + + cbtable->open_tmp_file = open_tmp_file; + cbtable->get_wc_prop = NULL; + cbtable->set_wc_prop = NULL; + cbtable->push_wc_prop = NULL; + cbtable->auth_baton = ctx->auth_baton; + + callbackbt->base_dir = target.utf8(); + callbackbt->pool = subpool; + callbackbt->config = ctx->config; + + err = ra_lib->open(&session,svn_path_canonicalize( target.utf8(), subpool ),cbtable,callbackbt,ctx->config,subpool); + if ( err ) { + kdDebug(7128)<< "Open session " << err->message << endl; + return; + } + kdDebug(7128) << "Session opened to " << target << endl; + //find number for HEAD + if (rev.kind == svn_opt_revision_head) { + err = ra_lib->get_latest_revnum(session,&rev.value.number,subpool); + if ( err ) { + kdDebug(7128)<< "Latest RevNum " << err->message << endl; + return; + } + kdDebug(7128) << "Got rev " << rev.value.number << endl; + } + + //get it + ra_lib->check_path(session,"",rev.value.number,&kind,subpool); + kdDebug(7128) << "Checked Path" << endl; + UDSEntry entry; + switch ( kind ) { + case svn_node_file: + kdDebug(7128) << "::stat result : file" << endl; + createUDSEntry(url.filename(),"",0,false,0,entry); + statEntry( entry ); + break; + case svn_node_dir: + kdDebug(7128) << "::stat result : directory" << endl; + createUDSEntry(url.filename(),"",0,true,0,entry); + statEntry( entry ); + break; + case svn_node_unknown: + case svn_node_none: + //error XXX + default: + kdDebug(7128) << "::stat result : UNKNOWN ==> WOW :)" << endl; + ; + } + finished(); + svn_pool_destroy( subpool ); +} + +void kio_svnProtocol::listDir(const KURL& url){ + kdDebug(7128) << "kio_svn::listDir(const KURL& url) : " << url.url() << endl ; + + apr_pool_t *subpool = svn_pool_create (pool); + apr_hash_t *dirents; + + QString target = makeSvnURL( url); + kdDebug(7128) << "SvnURL: " << target << endl; + recordCurrentURL( KURL( target ) ); + + //find the requested revision + svn_opt_revision_t rev; + svn_opt_revision_t endrev; + int idx = target.findRev( "?rev=" ); + if ( idx != -1 ) { + QString revstr = target.mid( idx+5 ); + svn_opt_parse_revision( &rev, &endrev, revstr.utf8(), subpool ); +#if 0 + kdDebug(7128) << "revision string found " << revstr << endl; + if ( revstr == "HEAD" ) { + rev.kind = svn_opt_revision_head; + kdDebug(7128) << "revision searched : HEAD" << endl; + } else { + rev.kind = svn_opt_revision_number; + rev.value.number = revstr.toLong(); + kdDebug(7128) << "revision searched : " << rev.value.number << endl; + } +#endif + target = target.left( idx ); + kdDebug(7128) << "new target : " << target << endl; + } else { + kdDebug(7128) << "no revision given. searching HEAD " << endl; + rev.kind = svn_opt_revision_head; + } + + initNotifier(false, false, false, subpool); + svn_error_t *err = svn_client_ls (&dirents, svn_path_canonicalize( target.utf8(), subpool ), &rev, false, ctx, subpool); + if ( err ) { + error( KIO::ERR_SLAVE_DEFINED, err->message ); + svn_pool_destroy( subpool ); + return; + } + + apr_array_header_t *array; + int i; + + array = svn_sort__hash (dirents, compare_items_as_paths, subpool); + + UDSEntry entry; + for (i = 0; i < array->nelts; ++i) { + entry.clear(); + const char *utf8_entryname, *native_entryname; + svn_dirent_t *dirent; + svn_sort__item_t *item; + + item = &APR_ARRAY_IDX (array, i, svn_sort__item_t); + + utf8_entryname = (const char*)item->key; + + dirent = (svn_dirent_t*)apr_hash_get (dirents, utf8_entryname, item->klen); + + svn_utf_cstring_from_utf8 (&native_entryname, utf8_entryname, subpool); + const char *native_author = NULL; + + //XXX BUGGY +/* apr_time_exp_t timexp; + apr_time_exp_lt(&timexp, dirent->time); + apr_os_exp_time_t *ostime; + apr_os_exp_time_get( &ostime, &timexp); + + time_t mtime = mktime( ostime );*/ + + if (dirent->last_author) + svn_utf_cstring_from_utf8 (&native_author, dirent->last_author, subpool); + + if ( createUDSEntry(QString( native_entryname ), QString( native_author ), dirent->size, + dirent->kind==svn_node_dir ? true : false, 0, entry) ) + listEntry( entry, false ); + } + listEntry( entry, true ); + + finished(); + svn_pool_destroy (subpool); +} + +bool kio_svnProtocol::createUDSEntry( const QString& filename, const QString& user, long long int size, bool isdir, time_t mtime, UDSEntry& entry) { + kdDebug(7128) << "MTime : " << ( long )mtime << endl; + kdDebug(7128) << "UDS filename : " << filename << endl; + UDSAtom atom; + atom.m_uds = KIO::UDS_NAME; + atom.m_str = filename; + entry.append( atom ); + + atom.m_uds = KIO::UDS_FILE_TYPE; + atom.m_long = isdir ? S_IFDIR : S_IFREG; + entry.append( atom ); + + atom.m_uds = KIO::UDS_SIZE; + atom.m_long = size; + entry.append( atom ); + + atom.m_uds = KIO::UDS_MODIFICATION_TIME; + atom.m_long = mtime; + entry.append( atom ); + + atom.m_uds = KIO::UDS_USER; + atom.m_str = user; + entry.append( atom ); + + return true; +} + +void kio_svnProtocol::copy(const KURL & src, const KURL& dest, int /*permissions*/, bool /*overwrite*/) { + kdDebug(7128) << "kio_svnProtocol::copy() Source : " << src.url() << " Dest : " << dest.url() << endl; + + apr_pool_t *subpool = svn_pool_create (pool); + svn_client_commit_info_t *commit_info = NULL; + + KURL nsrc = src; + KURL ndest = dest; + nsrc.setProtocol( chooseProtocol( src.protocol() ) ); + ndest.setProtocol( chooseProtocol( dest.protocol() ) ); + QString srcsvn = nsrc.url(); + QString destsvn = ndest.url(); + + recordCurrentURL( nsrc ); + + //find the requested revision + svn_opt_revision_t rev; + int idx = srcsvn.findRev( "?rev=" ); + if ( idx != -1 ) { + QString revstr = srcsvn.mid( idx+5 ); + kdDebug(7128) << "revision string found " << revstr << endl; + if ( revstr == "HEAD" ) { + rev.kind = svn_opt_revision_head; + kdDebug(7128) << "revision searched : HEAD" << endl; + } else { + rev.kind = svn_opt_revision_number; + rev.value.number = revstr.toLong(); + kdDebug(7128) << "revision searched : " << rev.value.number << endl; + } + srcsvn = srcsvn.left( idx ); + kdDebug(7128) << "new src : " << srcsvn << endl; + } else { + kdDebug(7128) << "no revision given. searching HEAD " << endl; + rev.kind = svn_opt_revision_head; + } + + initNotifier(false, false, false, subpool); + svn_error_t *err = svn_client_copy(&commit_info, srcsvn.utf8(), &rev, destsvn.utf8(), ctx, subpool); + if ( err ) { + error( KIO::ERR_SLAVE_DEFINED, err->message ); + } + + finished(); + svn_pool_destroy (subpool); +} + +void kio_svnProtocol::mkdir( const KURL::List& list, int /*permissions*/ ) { + kdDebug(7128) << "kio_svnProtocol::mkdir(LIST) : " << list << endl; + + apr_pool_t *subpool = svn_pool_create (pool); + svn_client_commit_info_t *commit_info = NULL; + + recordCurrentURL( list[ 0 ] ); + + apr_array_header_t *targets = apr_array_make(subpool, list.count()+1, sizeof(const char *)); + + KURL::List::const_iterator it = list.begin(), end = list.end(); + for ( ; it != end; ++it ) { + QString cur = makeSvnURL( *it ); + kdDebug( 7128 ) << "kio_svnProtocol::mkdir raw url for subversion : " << cur << endl; + const char *_target = apr_pstrdup( subpool, svn_path_canonicalize( apr_pstrdup( subpool, cur.utf8() ), subpool ) ); + (*(( const char ** )apr_array_push(( apr_array_header_t* )targets)) ) = _target; + } + + initNotifier(false, false, false, subpool); + svn_error_t *err = svn_client_mkdir(&commit_info,targets,ctx,subpool); + if ( err ) { + error( KIO::ERR_COULD_NOT_MKDIR, err->message ); + } + + finished(); + svn_pool_destroy (subpool); +} + +void kio_svnProtocol::mkdir( const KURL& url, int /*permissions*/ ) { + kdDebug(7128) << "kio_svnProtocol::mkdir() : " << url.url() << endl; + + apr_pool_t *subpool = svn_pool_create (pool); + svn_client_commit_info_t *commit_info = NULL; + + QString target = makeSvnURL( url); + kdDebug(7128) << "SvnURL: " << target << endl; + recordCurrentURL( KURL( target ) ); + + apr_array_header_t *targets = apr_array_make(subpool, 2, sizeof(const char *)); + (*(( const char ** )apr_array_push(( apr_array_header_t* )targets)) ) = apr_pstrdup( subpool, target.utf8() ); + + initNotifier(false, false, false, subpool); + svn_error_t *err = svn_client_mkdir(&commit_info,targets,ctx,subpool); + if ( err ) { + error( KIO::ERR_COULD_NOT_MKDIR, err->message ); + } + + finished(); + svn_pool_destroy (subpool); +} + +void kio_svnProtocol::del( const KURL& url, bool /*isfile*/ ) { + kdDebug(7128) << "kio_svnProtocol::del() : " << url.url() << endl; + + apr_pool_t *subpool = svn_pool_create (pool); + svn_client_commit_info_t *commit_info = NULL; + + QString target = makeSvnURL(url); + kdDebug(7128) << "SvnURL: " << target << endl; + recordCurrentURL( KURL( target ) ); + + apr_array_header_t *targets = apr_array_make(subpool, 2, sizeof(const char *)); + (*(( const char ** )apr_array_push(( apr_array_header_t* )targets)) ) = apr_pstrdup( subpool, target.utf8() ); + + initNotifier(false, false, false, subpool); + svn_error_t *err = svn_client_delete(&commit_info,targets,false/*force remove locally modified files in wc*/,ctx,subpool); + if ( err ) { + error( KIO::ERR_CANNOT_DELETE, err->message ); + } + + finished(); + svn_pool_destroy (subpool); +} + +void kio_svnProtocol::rename(const KURL& src, const KURL& dest, bool /*overwrite*/) { + kdDebug(7128) << "kio_svnProtocol::rename() Source : " << src.url() << " Dest : " << dest.url() << endl; + + apr_pool_t *subpool = svn_pool_create (pool); + svn_client_commit_info_t *commit_info = NULL; + + KURL nsrc = src; + KURL ndest = dest; + nsrc.setProtocol( chooseProtocol( src.protocol() ) ); + ndest.setProtocol( chooseProtocol( dest.protocol() ) ); + QString srcsvn = nsrc.url(); + QString destsvn = ndest.url(); + + recordCurrentURL( nsrc ); + + //find the requested revision + svn_opt_revision_t rev; + int idx = srcsvn.findRev( "?rev=" ); + if ( idx != -1 ) { + QString revstr = srcsvn.mid( idx+5 ); + kdDebug(7128) << "revision string found " << revstr << endl; + if ( revstr == "HEAD" ) { + rev.kind = svn_opt_revision_head; + kdDebug(7128) << "revision searched : HEAD" << endl; + } else { + rev.kind = svn_opt_revision_number; + rev.value.number = revstr.toLong(); + kdDebug(7128) << "revision searched : " << rev.value.number << endl; + } + srcsvn = srcsvn.left( idx ); + kdDebug(7128) << "new src : " << srcsvn << endl; + } else { + kdDebug(7128) << "no revision given. searching HEAD " << endl; + rev.kind = svn_opt_revision_head; + } + + initNotifier(false, false, false, subpool); + svn_error_t *err = svn_client_move(&commit_info, srcsvn.utf8(), &rev, destsvn.utf8(), false/*force remove locally modified files in wc*/, ctx, subpool); + if ( err ) { + error( KIO::ERR_CANNOT_RENAME, err->message ); + } + + finished(); + svn_pool_destroy (subpool); +} + +void kio_svnProtocol::special( const QByteArray& data ) { + kdDebug(7128) << "kio_svnProtocol::special" << endl; + + QDataStream stream(data, IO_ReadOnly); + int tmp; + + stream >> tmp; + kdDebug(7128) << "kio_svnProtocol::special " << tmp << endl; + + switch ( tmp ) { + case SVN_CHECKOUT: + { + KURL repository, wc; + int revnumber; + QString revkind; + stream >> repository; + stream >> wc; + stream >> revnumber; + stream >> revkind; + kdDebug(7128) << "kio_svnProtocol CHECKOUT from " << repository.url() << " to " << wc.url() << " at " << revnumber << " or " << revkind << endl; + checkout( repository, wc, revnumber, revkind ); + break; + } + case SVN_UPDATE: + { + KURL wc; + int revnumber; + QString revkind; + stream >> wc; + stream >> revnumber; + stream >> revkind; + kdDebug(7128) << "kio_svnProtocol UPDATE " << wc.url() << " at " << revnumber << " or " << revkind << endl; + update(wc, revnumber, revkind ); + break; + } + case SVN_COMMIT: + { + KURL::List wclist; + while ( !stream.atEnd() ) { + KURL tmp; + stream >> tmp; + wclist << tmp; + } + kdDebug(7128) << "kio_svnProtocol COMMIT" << endl; + commit( wclist ); + break; + } + case SVN_LOG: + { + kdDebug(7128) << "kio_svnProtocol LOG" << endl; + int revstart, revend; + QString revkindstart, revkindend; + KURL::List targets; + stream >> revstart; + stream >> revkindstart; + stream >> revend; + stream >> revkindend; + while ( !stream.atEnd() ) { + KURL tmp; + stream >> tmp; + targets << tmp; + } + svn_log( revstart, revkindstart, revend, revkindend, targets ); + break; + } + case SVN_IMPORT: + { + KURL wc,repos; + stream >> repos; + stream >> wc; + kdDebug(7128) << "kio_svnProtocol IMPORT" << endl; + import(repos,wc); + break; + } + case SVN_ADD: + { + KURL wc; + stream >> wc; + kdDebug(7128) << "kio_svnProtocol ADD" << endl; + add(wc); + break; + } + case SVN_DEL: + { + KURL::List wclist; + while ( !stream.atEnd() ) { + KURL tmp; + stream >> tmp; + wclist << tmp; + } + kdDebug(7128) << "kio_svnProtocol DEL" << endl; + wc_delete(wclist); + break; + } + case SVN_REVERT: + { + KURL::List wclist; + while ( !stream.atEnd() ) { + KURL tmp; + stream >> tmp; + wclist << tmp; + } + kdDebug(7128) << "kio_svnProtocol REVERT" << endl; + wc_revert(wclist); + break; + } + case SVN_STATUS: + { + KURL wc; + int checkRepos=false; + int fullRecurse=false; + stream >> wc; + stream >> checkRepos; + stream >> fullRecurse; + kdDebug(7128) << "kio_svnProtocol STATUS" << endl; + wc_status(wc,checkRepos,fullRecurse); + break; + } + case SVN_MKDIR: + { + KURL::List list; + stream >> list; + kdDebug(7128) << "kio_svnProtocol MKDIR" << endl; + mkdir(list,0); + break; + } + case SVN_RESOLVE: + { + KURL url; + bool recurse; + stream >> url; + stream >> recurse; + kdDebug(7128) << "kio_svnProtocol RESOLVE" << endl; + wc_resolve(url,recurse); + break; + } + case SVN_SWITCH: + { + KURL wc,url; + bool recurse; + int revnumber; + QString revkind; + stream >> wc; + stream >> url; + stream >> recurse; + stream >> revnumber; + stream >> revkind; + kdDebug(7128) << "kio_svnProtocol SWITCH" << endl; + svn_switch(wc,url,revnumber,revkind,recurse); + break; + } + case SVN_DIFF: + { + KURL url1,url2; + int rev1, rev2; + bool recurse; + QString revkind1, revkind2; + stream >> url1; + stream >> url2; + stream >> rev1; + stream >> revkind1; + stream >> rev2; + stream >> revkind2; + stream >> recurse; + kdDebug(7128) << "kio_svnProtocol DIFF" << endl; + svn_diff(url1,url2,rev1,rev2,revkind1,revkind2,recurse); + break; + } + default: + { + kdDebug(7128) << "kio_svnProtocol DEFAULT" << endl; + break; + } + } +} + +void kio_svnProtocol::popupMessage( const QString& message ) { + QByteArray params; + QDataStream stream(params, IO_WriteOnly); + stream << message; + + if ( !dcopClient()->send( "kded","ksvnd","popupMessage(QString)", params ) ) + kdWarning() << "Communication with KDED:KSvnd failed" << endl; +} + +void kio_svnProtocol::svn_log( int revstart, const QString& revkindstart, int revend, const QString& revkindend, const KURL::List& targets ) { + kdDebug(7128) << "kio_svn::log : " << targets << " from revision " << revstart << " or " << revkindstart << " to " + " revision " << revend << " or " << revkindend + << endl; + + apr_pool_t *subpool = svn_pool_create (pool); + + svn_opt_revision_t rev1 = createRevision( revstart, revkindstart, subpool ); + svn_opt_revision_t rev2 = createRevision( revend, revkindend, subpool ); + + //TODO + + finished(); + svn_pool_destroy (subpool); +} + +svn_opt_revision_t kio_svnProtocol::createRevision( int revision, const QString& revkind, apr_pool_t *pool ) { + svn_opt_revision_t result,endrev; + + if ( revision != -1 ) { + result.value.number = revision; + result.kind = svn_opt_revision_number; + } else if ( revkind == "WORKING" ) { + result.kind = svn_opt_revision_working; + } else if ( revkind == "BASE" ) { + result.kind = svn_opt_revision_base; + } else if ( !revkind.isNull() ) { + svn_opt_parse_revision(&result,&endrev,revkind.utf8(),pool); + } + return result; +} + +void kio_svnProtocol::svn_diff(const KURL & url1, const KURL& url2,int rev1, int rev2,const QString& revkind1,const QString& revkind2,bool recurse) { + kdDebug(7128) << "kio_svn::diff : " << url1.path() << " at revision " << rev1 << " or " << revkind1 << " with " + << url2.path() << " at revision " << rev2 << " or " << revkind2 + << endl ; + + apr_pool_t *subpool = svn_pool_create (pool); + apr_array_header_t *options = svn_cstring_split( "", "\t\r\n", TRUE, subpool ); + + KURL nurl1 = url1; + KURL nurl2 = url2; + nurl1.setProtocol( chooseProtocol( url1.protocol() ) ); //svn+https -> https for eg + nurl2.setProtocol( chooseProtocol( url2.protocol() ) ); + recordCurrentURL( nurl1 ); + QString source = makeSvnURL( nurl1 ); + QString target = makeSvnURL( nurl2 ); + + const char *path1 = svn_path_canonicalize( apr_pstrdup( subpool, source.utf8() ), subpool ); + const char *path2 = svn_path_canonicalize( apr_pstrdup( subpool, target.utf8() ), subpool ); + //remove file:/// so we can diff for working copies, needs a better check (so we support URL for file:/// _repositories_ ) + if ( nurl1.protocol() == "file" ) { + path1 = svn_path_canonicalize( apr_pstrdup( subpool, nurl1.path().utf8() ), subpool ); + } + if ( nurl2.protocol() == "file" ) { + path2 = svn_path_canonicalize( apr_pstrdup( subpool, nurl2.path().utf8() ), subpool ); + } + kdDebug( 7128 ) << "1 : " << path1 << " 2: " << path2 << endl; + + svn_opt_revision_t revision1,revision2; + revision1 = createRevision(rev1, revkind1, subpool); + revision2 = createRevision(rev2, revkind2, subpool); + + char *templ; + templ = apr_pstrdup ( subpool, "/tmp/tmpfile_XXXXXX" ); + apr_file_t *outfile = NULL; + apr_file_mktemp( &outfile, templ , APR_READ|APR_WRITE|APR_CREATE|APR_TRUNCATE, subpool ); + + initNotifier(false, false, false, subpool); + svn_error_t *err = svn_client_diff (options, path1, &revision1, path2, &revision2, recurse, false, true, outfile, NULL, ctx, subpool); + if ( err ) + error( KIO::ERR_SLAVE_DEFINED, err->message ); + //read the content of the outfile now + QStringList tmp; + apr_file_close(outfile); + QFile file(templ); + if ( file.open( IO_ReadOnly ) ) { + QTextStream stream( &file ); + QString line; + while ( !stream.atEnd() ) { + line = stream.readLine(); + tmp << line; + } + file.close(); + } + for ( QStringList::Iterator itt = tmp.begin(); itt != tmp.end(); itt++ ) { + setMetaData(QString::number( m_counter ).rightJustify( 10,'0' )+ "diffresult", ( *itt ) ); + m_counter++; + } + //delete temp file + file.remove(); + + finished(); + svn_pool_destroy (subpool); +} + +void kio_svnProtocol::svn_switch( const KURL& wc, const KURL& repos, int revnumber, const QString& revkind, bool recurse) { + kdDebug(7128) << "kio_svn::switch : " << wc.path() << " at revision " << revnumber << " or " << revkind << endl ; + + apr_pool_t *subpool = svn_pool_create (pool); + + KURL nurl = repos; + KURL dest = wc; + nurl.setProtocol( chooseProtocol( repos.protocol() ) ); + dest.setProtocol( "file" ); + recordCurrentURL( nurl ); + QString source = dest.path(); + QString target = makeSvnURL( repos ); + + const char *path = svn_path_canonicalize( apr_pstrdup( subpool, source.utf8() ), subpool ); + const char *url = svn_path_canonicalize( apr_pstrdup( subpool, target.utf8() ), subpool ); + + svn_opt_revision_t rev = createRevision( revnumber, revkind, subpool ); + + initNotifier(false, false, false, subpool); + svn_error_t *err = svn_client_switch (NULL/*result revision*/, path, url, &rev, recurse, ctx, subpool); + if ( err ) + error( KIO::ERR_SLAVE_DEFINED, err->message ); + + finished(); + svn_pool_destroy (subpool); +} + +void kio_svnProtocol::update( const KURL& wc, int revnumber, const QString& revkind ) { + kdDebug(7128) << "kio_svn::update : " << wc.path() << " at revision " << revnumber << " or " << revkind << endl ; + + apr_pool_t *subpool = svn_pool_create (pool); + KURL dest = wc; + dest.setProtocol( "file" ); + QString target = dest.path(); + recordCurrentURL( dest ); + + svn_opt_revision_t rev = createRevision( revnumber, revkind, subpool ); + + initNotifier(false, false, false, subpool); + svn_error_t *err = svn_client_update (NULL, svn_path_canonicalize( target.utf8(), subpool ), &rev, true, ctx, subpool); + if ( err ) + error( KIO::ERR_SLAVE_DEFINED, err->message ); + + finished(); + svn_pool_destroy (subpool); +} + +void kio_svnProtocol::import( const KURL& repos, const KURL& wc ) { + kdDebug(7128) << "kio_svnProtocol::import() : " << wc.url() << " into " << repos.url() << endl; + + apr_pool_t *subpool = svn_pool_create (pool); + svn_client_commit_info_t *commit_info = NULL; + bool nonrecursive = false; + + KURL nurl = repos; + KURL dest = wc; + nurl.setProtocol( chooseProtocol( repos.protocol() ) ); + dest.setProtocol( "file" ); + recordCurrentURL( nurl ); + dest.cleanPath( true ); // remove doubled '/' + QString source = dest.path(-1); + QString target = makeSvnURL( repos ); + + const char *path = svn_path_canonicalize( apr_pstrdup( subpool, source.utf8() ), subpool ); + const char *url = svn_path_canonicalize( apr_pstrdup( subpool, target.utf8() ), subpool ); + + initNotifier(false, false, false, subpool); + svn_error_t *err = svn_client_import(&commit_info,path,url,nonrecursive,ctx,subpool); + if ( err ) + error( KIO::ERR_SLAVE_DEFINED, err->message ); + + finished(); + svn_pool_destroy (subpool); +} + +void kio_svnProtocol::checkout( const KURL& repos, const KURL& wc, int revnumber, const QString& revkind ) { + kdDebug(7128) << "kio_svn::checkout : " << repos.url() << " into " << wc.path() << " at revision " << revnumber << " or " << revkind << endl ; + + apr_pool_t *subpool = svn_pool_create (pool); + KURL nurl = repos; + KURL dest = wc; + nurl.setProtocol( chooseProtocol( repos.protocol() ) ); + dest.setProtocol( "file" ); + QString target = makeSvnURL( repos ); + recordCurrentURL( nurl ); + QString dpath = dest.path(); + + //find the requested revision + svn_opt_revision_t rev = createRevision( revnumber, revkind, subpool ); + + initNotifier(true, false, false, subpool); + svn_error_t *err = svn_client_checkout (NULL/* rev actually checkedout */, svn_path_canonicalize( target.utf8(), subpool ), svn_path_canonicalize ( dpath.utf8(), subpool ), &rev, true, ctx, subpool); + if ( err ) + error( KIO::ERR_SLAVE_DEFINED, err->message ); + + finished(); + svn_pool_destroy (subpool); +} + +void kio_svnProtocol::commit(const KURL::List& wc) { + kdDebug(7128) << "kio_svnProtocol::commit() : " << wc << endl; + + apr_pool_t *subpool = svn_pool_create (pool); + svn_client_commit_info_t *commit_info = NULL; + bool nonrecursive = false; + + apr_array_header_t *targets = apr_array_make(subpool, 1+wc.count(), sizeof(const char *)); + + for ( QValueListConstIterator it = wc.begin(); it != wc.end() ; ++it ) { + KURL nurl = *it; + nurl.setProtocol( "file" ); + recordCurrentURL( nurl ); + (*(( const char ** )apr_array_push(( apr_array_header_t* )targets)) ) = svn_path_canonicalize( nurl.path().utf8(), subpool ); + } + + initNotifier(false, false, false, subpool); + svn_error_t *err = svn_client_commit(&commit_info,targets,nonrecursive,ctx,subpool); + if ( err ) + error( KIO::ERR_SLAVE_DEFINED, err->message ); + + if ( commit_info ) { + for ( QValueListConstIterator it = wc.begin(); it != wc.end() ; ++it ) { + KURL nurl = *it; + nurl.setProtocol( "file" ); + + QString userstring = i18n ( "Nothing to commit." ); + if ( SVN_IS_VALID_REVNUM( commit_info->revision ) ) + userstring = i18n( "Committed revision %1." ).arg(commit_info->revision); + setMetaData(QString::number( m_counter ).rightJustify( 10,'0' )+ "path", nurl.path() ); + setMetaData(QString::number( m_counter ).rightJustify( 10,'0' )+ "action", "0" ); + setMetaData(QString::number( m_counter ).rightJustify( 10,'0' )+ "kind", "0" ); + setMetaData(QString::number( m_counter ).rightJustify( 10,'0' )+ "mime_t", "" ); + setMetaData(QString::number( m_counter ).rightJustify( 10,'0' )+ "content", "0" ); + setMetaData(QString::number( m_counter ).rightJustify( 10,'0' )+ "prop", "0" ); + setMetaData(QString::number( m_counter ).rightJustify( 10,'0' )+ "rev" , QString::number( commit_info->revision ) ); + setMetaData(QString::number( m_counter ).rightJustify( 10,'0' )+ "string", userstring ); + m_counter++; + } + } + + finished(); + svn_pool_destroy (subpool); +} + +void kio_svnProtocol::add(const KURL& wc) { + kdDebug(7128) << "kio_svnProtocol::add() : " << wc.url() << endl; + + apr_pool_t *subpool = svn_pool_create (pool); + bool nonrecursive = false; + + KURL nurl = wc; + nurl.setProtocol( "file" ); + QString target = nurl.url(); + recordCurrentURL( nurl ); + + initNotifier(false, false, false, subpool); + svn_error_t *err = svn_client_add(svn_path_canonicalize( nurl.path().utf8(), subpool ),nonrecursive,ctx,subpool); + if ( err ) + error( KIO::ERR_SLAVE_DEFINED, err->message ); + + finished(); + svn_pool_destroy (subpool); +} + +void kio_svnProtocol::wc_delete(const KURL::List& wc) { + kdDebug(7128) << "kio_svnProtocol::wc_delete() : " << wc << endl; + + apr_pool_t *subpool = svn_pool_create (pool); + svn_client_commit_info_t *commit_info = NULL; + bool nonrecursive = false; + + apr_array_header_t *targets = apr_array_make(subpool, 1+wc.count(), sizeof(const char *)); + + for ( QValueListConstIterator it = wc.begin(); it != wc.end() ; ++it ) { + KURL nurl = *it; + nurl.setProtocol( "file" ); + recordCurrentURL( nurl ); + (*(( const char ** )apr_array_push(( apr_array_header_t* )targets)) ) = svn_path_canonicalize( nurl.path().utf8(), subpool ); + } + + initNotifier(false, false, false, subpool); + svn_error_t *err = svn_client_delete(&commit_info,targets,nonrecursive,ctx,subpool); + + if ( err ) + error( KIO::ERR_SLAVE_DEFINED, err->message ); + + finished(); + svn_pool_destroy (subpool); +} + +void kio_svnProtocol::wc_revert(const KURL::List& wc) { + kdDebug(7128) << "kio_svnProtocol::revert() : " << wc << endl; + + apr_pool_t *subpool = svn_pool_create (pool); + bool nonrecursive = false; + + apr_array_header_t *targets = apr_array_make(subpool, 1 + wc.count(), sizeof(const char *)); + + for ( QValueListConstIterator it = wc.begin(); it != wc.end() ; ++it ) { + KURL nurl = *it; + nurl.setProtocol( "file" ); + recordCurrentURL( nurl ); + (*(( const char ** )apr_array_push(( apr_array_header_t* )targets)) ) = svn_path_canonicalize( nurl.path().utf8(), subpool ); + } + + initNotifier(false, false, false, subpool); + svn_error_t *err = svn_client_revert(targets,nonrecursive,ctx,subpool); + if ( err ) + error( KIO::ERR_SLAVE_DEFINED, err->message ); + + finished(); + svn_pool_destroy (subpool); +} + +void kio_svnProtocol::wc_status(const KURL& wc, bool checkRepos, bool fullRecurse, bool getAll, int revnumber, const QString& revkind) { + kdDebug(7128) << "kio_svnProtocol::status() : " << wc.url() << endl; + + apr_pool_t *subpool = svn_pool_create (pool); + svn_revnum_t result_rev; + bool no_ignore = FALSE; + + KURL nurl = wc; + nurl.setProtocol( "file" ); + recordCurrentURL( nurl ); + + svn_opt_revision_t rev = createRevision( revnumber, revkind, subpool ); + + initNotifier(false, false, false, subpool); + + svn_error_t *err = svn_client_status(&result_rev, svn_path_canonicalize( nurl.path().utf8(), subpool ), &rev, kio_svnProtocol::status, this, fullRecurse, getAll, checkRepos, no_ignore, ctx, subpool); + + if ( err ) + error( KIO::ERR_SLAVE_DEFINED, err->message ); + + finished(); + svn_pool_destroy (subpool); +} + +//change the proto and remove trailing / +//remove double / also +QString kio_svnProtocol::makeSvnURL ( const KURL& url ) const { + QString kproto = url.protocol(); + KURL tpURL = url; + tpURL.cleanPath( true ); + QString svnUrl; + if ( kproto == "svn+http" ) { + kdDebug(7128) << "http:/ " << url.url() << endl; + tpURL.setProtocol("http"); + svnUrl = tpURL.url(-1); + return svnUrl; + } + else if ( kproto == "svn+https" ) { + kdDebug(7128) << "https:/ " << url.url() << endl; + tpURL.setProtocol("https"); + svnUrl = tpURL.url(-1); + return svnUrl; + } + else if ( kproto == "svn+ssh" ) { + kdDebug(7128) << "svn+ssh:/ " << url.url() << endl; + tpURL.setProtocol("svn+ssh"); + svnUrl = tpURL.url(-1); + return svnUrl; + } + else if ( kproto == "svn" ) { + kdDebug(7128) << "svn:/ " << url.url() << endl; + tpURL.setProtocol("svn"); + svnUrl = tpURL.url(-1); + return svnUrl; + } + else if ( kproto == "svn+file" ) { + kdDebug(7128) << "file:/ " << url.url() << endl; + tpURL.setProtocol("file"); + svnUrl = tpURL.url(-1); + //hack : add one more / after file:/ + int idx = svnUrl.find("/"); + svnUrl.insert( idx, "//" ); + return svnUrl; + } + return tpURL.url(-1); +} + +QString kio_svnProtocol::chooseProtocol ( const QString& kproto ) const { + if ( kproto == "svn+http" ) return QString( "http" ); + else if ( kproto == "svn+https" ) return QString( "https" ); + else if ( kproto == "svn+ssh" ) return QString( "svn+ssh" ); + else if ( kproto == "svn" ) return QString( "svn" ); + else if ( kproto == "svn+file" ) return QString( "file" ); + return kproto; +} + +svn_error_t *kio_svnProtocol::trustSSLPrompt(svn_auth_cred_ssl_server_trust_t **cred_p, void *, const char */*realm*/, apr_uint32_t /*failures*/, const svn_auth_ssl_server_cert_info_t */*cert_info*/, svn_boolean_t /*may_save*/, apr_pool_t *pool) { + //when ksvnd is ready make it prompt for the SSL certificate ... XXX + *cred_p = (svn_auth_cred_ssl_server_trust_t*)apr_pcalloc (pool, sizeof (**cred_p)); + (*cred_p)->may_save = FALSE; + return SVN_NO_ERROR; +} + +svn_error_t *kio_svnProtocol::clientCertSSLPrompt(svn_auth_cred_ssl_client_cert_t **/*cred_p*/, void *, const char */*realm*/, svn_boolean_t /*may_save*/, apr_pool_t */*pool*/) { + //when ksvnd is ready make it prompt for the SSL certificate ... XXX +/* *cred_p = apr_palloc (pool, sizeof(**cred_p)); + (*cred_p)->cert_file = cert_file;*/ + return SVN_NO_ERROR; +} + +svn_error_t *kio_svnProtocol::clientCertPasswdPrompt(svn_auth_cred_ssl_client_cert_pw_t **/*cred_p*/, void *, const char */*realm*/, svn_boolean_t /*may_save*/, apr_pool_t */*pool*/) { + //when ksvnd is ready make it prompt for the SSL certificate password ... XXX + return SVN_NO_ERROR; +} + +svn_error_t *kio_svnProtocol::commitLogPrompt( const char **log_msg, const char **/*file*/, apr_array_header_t *commit_items, void *baton, apr_pool_t *pool ) { + QCString replyType; + QByteArray params; + QByteArray reply; + QString result; + QStringList slist; + kio_svnProtocol *p = ( kio_svnProtocol* )baton; + svn_stringbuf_t *message = NULL; + + for (int i = 0; i < commit_items->nelts; i++) { + QString list; + svn_client_commit_item_t *item = ((svn_client_commit_item_t **) commit_items->elts)[i]; + const char *path = item->path; + char text_mod = '_', prop_mod = ' '; + + if (! path) + path = item->url; + else if (! *path) + path = "."; + + if (! path) + path = "."; + + if ((item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) && (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD)) + text_mod = 'R'; + else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD) + text_mod = 'A'; + else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) + text_mod = 'D'; + else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_TEXT_MODS) + text_mod = 'M'; + if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_PROP_MODS) + prop_mod = 'M'; + + list += text_mod; + list += " "; + list += prop_mod; + list += " "; + list += path; + kdDebug(7128) << " Commiting items : " << list << endl; + slist << list; + } + + QDataStream stream(params, IO_WriteOnly); + stream << slist.join("\n"); + + if ( !p->dcopClient()->call( "kded","ksvnd","commitDialog(QString)", params, replyType, reply ) ) { + kdWarning() << "Communication with KDED:KSvnd failed" << endl; + return SVN_NO_ERROR; + } + + if ( replyType != "QString" ) { + kdWarning() << "Unexpected reply type" << endl; + return SVN_NO_ERROR; + } + + QDataStream stream2 ( reply, IO_ReadOnly ); + stream2 >> result; + + if ( result.isNull() ) { //cancelled + *log_msg = NULL; + return SVN_NO_ERROR; + } + + message = svn_stringbuf_create( result.utf8(), pool ); + *log_msg = message->data; + + return SVN_NO_ERROR; +} + +void kio_svnProtocol::notify(void *baton, const char *path, svn_wc_notify_action_t action, svn_node_kind_t kind, const char *mime_type, svn_wc_notify_state_t content_state, svn_wc_notify_state_t prop_state, svn_revnum_t revision) { + kdDebug(7128) << "NOTIFY : " << path << " updated at revision " << revision << " action : " << action << ", kind : " << kind << " , content_state : " << content_state << ", prop_state : " << prop_state << endl; + + QString userstring; + struct notify_baton *nb = ( struct notify_baton* ) baton; + + //// Convert notification to a user readable string + switch ( action ) { + case svn_wc_notify_add : //add + if (mime_type && (svn_mime_type_is_binary (mime_type))) + userstring = i18n( "A (bin) %1" ).arg( path ); + else + userstring = i18n( "A %1" ).arg( path ); + break; + case svn_wc_notify_copy: //copy + break; + case svn_wc_notify_delete: //delete + nb->received_some_change = TRUE; + userstring = i18n( "D %1" ).arg( path ); + break; + case svn_wc_notify_restore : //restore + userstring=i18n( "Restored %1." ).arg( path ); + break; + case svn_wc_notify_revert : //revert + userstring=i18n( "Reverted %1." ).arg( path ); + break; + case svn_wc_notify_failed_revert: //failed revert + userstring=i18n( "Failed to revert %1.\nTry updating instead." ).arg( path ); + break; + case svn_wc_notify_resolved: //resolved + userstring=i18n( "Resolved conflicted state of %1." ).arg( path ); + break; + case svn_wc_notify_skip: //skip + if ( content_state == svn_wc_notify_state_missing ) + userstring=i18n("Skipped missing target %1.").arg( path ); + else + userstring=i18n("Skipped %1.").arg( path ); + break; + case svn_wc_notify_update_delete: //update_delete + nb->received_some_change = TRUE; + userstring=i18n( "D %1" ).arg( path ); + break; + case svn_wc_notify_update_add: //update_add + nb->received_some_change = TRUE; + userstring=i18n( "A %1" ).arg( path ); + break; + case svn_wc_notify_update_update: //update_update + { + /* If this is an inoperative dir change, do no notification. + An inoperative dir change is when a directory gets closed + without any props having been changed. */ + if (! ((kind == svn_node_dir) + && ((prop_state == svn_wc_notify_state_inapplicable) + || (prop_state == svn_wc_notify_state_unknown) + || (prop_state == svn_wc_notify_state_unchanged)))) { + nb->received_some_change = TRUE; + + if (kind == svn_node_file) { + if (content_state == svn_wc_notify_state_conflicted) + userstring = "C"; + else if (content_state == svn_wc_notify_state_merged) + userstring = "G"; + else if (content_state == svn_wc_notify_state_changed) + userstring = "U"; + } + + if (prop_state == svn_wc_notify_state_conflicted) + userstring += "C"; + else if (prop_state == svn_wc_notify_state_merged) + userstring += "G"; + else if (prop_state == svn_wc_notify_state_changed) + userstring += "U"; + else + userstring += " "; + + if (! ((content_state == svn_wc_notify_state_unchanged + || content_state == svn_wc_notify_state_unknown) + && (prop_state == svn_wc_notify_state_unchanged + || prop_state == svn_wc_notify_state_unknown))) + userstring += QString( " " ) + path; + } + break; + } + case svn_wc_notify_update_completed: //update_completed + { + if (! nb->suppress_final_line) { + if (SVN_IS_VALID_REVNUM (revision)) { + if (nb->is_export) { + if ( nb->in_external ) + userstring = i18n("Exported external at revision %1.").arg( revision ); + else + userstring = i18n("Exported revision %1.").arg( revision ); + } else if (nb->is_checkout) { + if ( nb->in_external ) + userstring = i18n("Checked out external at revision %1.").arg( revision ); + else + userstring = i18n("Checked out revision %1.").arg( revision); + } else { + if (nb->received_some_change) { + if ( nb->in_external ) + userstring=i18n("Updated external to revision %1.").arg( revision ); + else + userstring = i18n("Updated to revision %1.").arg( revision); + } else { + if ( nb->in_external ) + userstring = i18n("External at revision %1.").arg( revision ); + else + userstring = i18n("At revision %1.").arg( revision); + } + } + } else /* no revision */ { + if (nb->is_export) { + if ( nb->in_external ) + userstring = i18n("External export complete."); + else + userstring = i18n("Export complete."); + } else if (nb->is_checkout) { + if ( nb->in_external ) + userstring = i18n("External checkout complete."); + else + userstring = i18n("Checkout complete."); + } else { + if ( nb->in_external ) + userstring = i18n("External update complete."); + else + userstring = i18n("Update complete."); + } + } + } + } + if (nb->in_external) + nb->in_external = FALSE; + break; + case svn_wc_notify_update_external: //update_external + nb->in_external = TRUE; + userstring = i18n("Fetching external item into %1." ).arg( path ); + break; + case svn_wc_notify_status_completed: //status_completed + if (SVN_IS_VALID_REVNUM (revision)) + userstring = i18n( "Status against revision: %1.").arg( revision ); + break; + case svn_wc_notify_status_external: //status_external + userstring = i18n("Performing status on external item at %1.").arg( path ); + break; + case svn_wc_notify_commit_modified: //commit_modified + userstring = i18n( "Sending %1").arg( path ); + break; + case svn_wc_notify_commit_added: //commit_added + if (mime_type && svn_mime_type_is_binary (mime_type)) { + userstring = i18n( "Adding (bin) %1.").arg( path ); + } else { + userstring = i18n( "Adding %1.").arg( path ); + } + break; + case svn_wc_notify_commit_deleted: //commit_deleted + userstring = i18n( "Deleting %1.").arg( path ); + break; + case svn_wc_notify_commit_replaced: //commit_replaced + userstring = i18n( "Replacing %1.").arg( path ); + break; + case svn_wc_notify_commit_postfix_txdelta: //commit_postfix_txdelta + if (! nb->sent_first_txdelta) { + nb->sent_first_txdelta = TRUE; + userstring=i18n("Transmitting file data "); + } else { + userstring="."; + } + break; + + break; + case svn_wc_notify_blame_revision: //blame_revision + break; + default: + break; + } + //// End convert + + kio_svnProtocol *p = ( kio_svnProtocol* )nb->master; + + p->setMetaData(QString::number( p->counter() ).rightJustify( 10,'0' )+ "path" , QString::fromUtf8( path )); + p->setMetaData(QString::number( p->counter() ).rightJustify( 10,'0' )+ "action", QString::number( action )); + p->setMetaData(QString::number( p->counter() ).rightJustify( 10,'0' )+ "kind", QString::number( kind )); + p->setMetaData(QString::number( p->counter() ).rightJustify( 10,'0' )+ "mime_t", QString::fromUtf8( mime_type )); + p->setMetaData(QString::number( p->counter() ).rightJustify( 10,'0' )+ "content", QString::number( content_state )); + p->setMetaData(QString::number( p->counter() ).rightJustify( 10,'0' )+ "prop", QString::number( prop_state )); + p->setMetaData(QString::number( p->counter() ).rightJustify( 10,'0' )+ "rev", QString::number( revision )); + p->setMetaData(QString::number( p->counter() ).rightJustify( 10,'0' )+ "string", userstring ); + p->incCounter(); +} + +void kio_svnProtocol::status(void *baton, const char *path, svn_wc_status_t *status) { + kdDebug(7128) << "STATUS : " << path << ", wc text status : " << status->text_status + << ", wc prop status : " << status->prop_status + << ", repos text status : " << status->repos_text_status + << ", repos prop status : " << status->repos_prop_status + << endl; + + QByteArray params; + kio_svnProtocol *p = ( kio_svnProtocol* )baton; + + QDataStream stream(params, IO_WriteOnly); + long int rev = status->entry ? status->entry->revision : 0; + stream << QString::fromUtf8( path ) << status->text_status << status->prop_status << status->repos_text_status << status->repos_prop_status << rev; + + p->setMetaData(QString::number( p->counter() ).rightJustify( 10,'0' )+ "path", QString::fromUtf8( path )); + p->setMetaData(QString::number( p->counter() ).rightJustify( 10,'0' )+ "text", QString::number( status->text_status )); + p->setMetaData(QString::number( p->counter() ).rightJustify( 10,'0' )+ "prop", QString::number( status->prop_status )); + p->setMetaData(QString::number( p->counter() ).rightJustify( 10,'0' )+ "reptxt", QString::number( status->repos_text_status )); + p->setMetaData(QString::number( p->counter() ).rightJustify( 10,'0' )+ "repprop", QString::number( status->repos_prop_status )); + p->setMetaData(QString::number( p->counter() ).rightJustify( 10,'0' )+ "rev", QString::number( rev )); + p->incCounter(); +} + + +void kio_svnProtocol::wc_resolve( const KURL& wc, bool recurse ) { + kdDebug(7128) << "kio_svnProtocol::wc_resolve() : " << wc.url() << endl; + + apr_pool_t *subpool = svn_pool_create (pool); + + KURL nurl = wc; + nurl.setProtocol( "file" ); + recordCurrentURL( nurl ); + + initNotifier(false, false, false, subpool); + svn_error_t *err = svn_client_resolved(svn_path_canonicalize( nurl.path().utf8(), subpool ), recurse,ctx,subpool); + if ( err ) + error( KIO::ERR_SLAVE_DEFINED, err->message ); + + finished(); + svn_pool_destroy (subpool); +} + +extern "C" +{ + KDE_EXPORT int kdemain(int argc, char **argv) { + KInstance instance( "kio_svn" ); + + kdDebug(7128) << "*** Starting kio_svn " << endl; + + if (argc != 4) { + kdDebug(7128) << "Usage: kio_svn protocol domain-socket1 domain-socket2" << endl; + exit(-1); + } + + kio_svnProtocol slave(argv[2], argv[3]); + slave.dispatchLoop(); + + kdDebug(7128) << "*** kio_svn Done" << endl; + return 0; + } +} + diff --git a/kioslave/svn/svn.h b/kioslave/svn/svn.h new file mode 100644 index 00000000..50c92d1c --- /dev/null +++ b/kioslave/svn/svn.h @@ -0,0 +1,140 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Mickael Marchand + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _svn_H_ +#define _svn_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class QCString; +class kio_svnProtocol; + +typedef struct kbaton { + svn_stream_t *target_stream; + svn_stringbuf_t *target_string; + svn_stream_t *string_stream; +} kbaton; + +typedef struct kio_svn_callback_baton_t { + const char* base_dir; + apr_hash_t *config; + apr_pool_t *pool; +} kio_svn_callback_baton_t; + +typedef struct notify_baton { + svn_boolean_t received_some_change; + svn_boolean_t is_checkout; + svn_boolean_t is_export; + svn_boolean_t suppress_final_line; + svn_boolean_t sent_first_txdelta; + svn_boolean_t in_external; + svn_boolean_t had_print_error; /* Used to not keep printing error messages + when we've already had one print error. */ + apr_pool_t *pool; /* this pool is cleared after every notification, + so don't keep anything here! */ + kio_svnProtocol *master; +} notify_baton; + + +class kio_svnProtocol : public KIO::SlaveBase +{ + public: + kio_svnProtocol(const QCString &pool_socket, const QCString &app_socket); + virtual ~kio_svnProtocol(); + virtual void special( const QByteArray& data ); + virtual void get(const KURL& url); + virtual void listDir(const KURL& url); + virtual void stat(const KURL& url); + virtual void mkdir(const KURL& url, int permissions); + virtual void mkdir(const KURL::List& list, int permissions); + virtual void del( const KURL& url, bool isfile ); + virtual void copy(const KURL & src, const KURL& dest, int permissions, bool overwrite); + virtual void rename(const KURL& src, const KURL& dest, bool overwrite); + void checkout( const KURL& repos, const KURL& wc, int revnumber, const QString& revkind ); + void import( const KURL& repos, const KURL& wc ); + void svn_switch( const KURL& wc, const KURL& url, int revnumber, const QString& revkind, bool recurse); + void svn_log( int revstart, const QString& revkindstart, int revend, const QString& revkindend, const KURL::List& targets ); + void svn_diff( const KURL& url1, const KURL& url2, int rev1, int rev2, const QString& revkind1, const QString& revkind2, bool recurse); + //TODO fix with svn 1.2 : support a KURL::List -> svn_client_update2() + void update( const KURL& wc, int revnumber, const QString& revkind ); + void commit( const KURL::List& wc ); + void add( const KURL& wc ); + //these work using the working copy + void wc_resolve( const KURL& wc, bool recurse = true ); + void wc_delete( const KURL::List& wc ); + void wc_revert( const KURL::List& wc ); + void wc_status(const KURL& wc, bool checkRepos=false, bool fullRecurse=true, bool getAll=true, int revnumber=-1, const QString& revkind="HEAD"); + + static svn_error_t* checkAuth(svn_auth_cred_simple_t **cred, void *baton, const char *realm, const char *username, svn_boolean_t may_save, apr_pool_t *pool); + static svn_error_t *trustSSLPrompt(svn_auth_cred_ssl_server_trust_t **cred_p, void *, const char *realm, apr_uint32_t failures, const svn_auth_ssl_server_cert_info_t *cert_info, svn_boolean_t may_save, apr_pool_t *pool); + static svn_error_t *clientCertSSLPrompt(svn_auth_cred_ssl_client_cert_t **cred_p, void *, const char *realm, svn_boolean_t may_save, apr_pool_t *pool); + static svn_error_t *clientCertPasswdPrompt(svn_auth_cred_ssl_client_cert_pw_t **cred_p, void *, const char *realm, svn_boolean_t may_save, apr_pool_t *pool); + static svn_error_t *commitLogPrompt( const char **log_msg, const char **tmp_file, apr_array_header_t *commit_items, void *baton, apr_pool_t *pool ); + static void notify(void *baton, const char *path, svn_wc_notify_action_t action, svn_node_kind_t kind, const char *mime_type, svn_wc_notify_state_t content_state, svn_wc_notify_state_t prop_state, svn_revnum_t revision); + static void status(void *baton, const char *path, svn_wc_status_t *status); + + QString chooseProtocol ( const QString& kproto ) const; + QString makeSvnURL ( const KURL& url ) const; + void initNotifier(bool is_checkout, bool is_export, bool suppress_final_line, apr_pool_t *spool); + + void recordCurrentURL(const KURL& url); + void popupMessage( const QString& message ); + int counter() { return m_counter; } + void incCounter() { m_counter++; } + svn_opt_revision_t createRevision( int revision, const QString& revkind, apr_pool_t *pool ); + + KURL myURL; + svn_client_ctx_t *ctx; + KIO::AuthInfo info; + + enum SVN_METHOD { + SVN_CHECKOUT=1, //KURL repository, KURL workingcopy, int revnumber=-1, QString revkind(HEAD, ...) //revnumber==-1 => use of revkind + SVN_UPDATE=2, // KURL wc (svn:///tmp/test, int revnumber=-1, QString revkind(HEAD, ...) // revnumber==-1 => use of revkind + SVN_COMMIT=3, + SVN_LOG=4, + SVN_IMPORT=5, + SVN_ADD=6, + SVN_DEL=7, + SVN_REVERT=8, + SVN_STATUS=9, + SVN_MKDIR=10, + SVN_RESOLVE=11, + SVN_SWITCH=12, + SVN_DIFF=13 + }; + + private: + bool createUDSEntry( const QString& filename, const QString& user, long long int size, bool isdir, time_t mtime, KIO::UDSEntry& entry); + apr_pool_t *pool; + int m_counter; +}; + +#endif diff --git a/kioslave/svn/svn.protocol b/kioslave/svn/svn.protocol new file mode 100644 index 00000000..abd77fb3 --- /dev/null +++ b/kioslave/svn/svn.protocol @@ -0,0 +1,43 @@ +[Protocol] +exec=kio_svn +protocol=svn +input=none +output=filesystem +reading=true +writing=true +deleting=true +makedir=true +linking=false +moving=true +listing=Name,Size,Date,Owner +defaultMimetype=application/octet-stream +Icon=remote +Description=Subversion ioslave +Description[br]=Sklav E/D Subversion +Description[ca]=Ioslave de Subversion +Description[cs]=Subversion protokol +Description[de]=Ein-/Ausgabemodul für Subversion +Description[es]=El ioslave de Subversion +Description[et]=Subversioni IO-moodul +Description[fr]=ioslave subversion +Description[ga]=ioslave Subversion +Description[gl]=Ioslave para Subversíon +Description[hu]=Subversion KDE-protokoll +Description[it]=Slave I/O di Subversion +Description[lt]=Subversion įvesties-išvesties priedas +Description[nb]=Subversion-iu-slave +Description[nds]=In-/Utgaavmoduul för Subversion +Description[ne]=उप संस्करण ioslave +Description[nn]=Subversion-iu-slave +Description[pl]=Wtyczka protokołu Subversion +Description[pt]='Ioslave' para Subversion +Description[pt_BR]=ioslave de Subversão +Description[ru]=Доступ к хранилищу Subversion +Description[sl]=ioslave za Subversion +Description[sr]=IOSlave за Subversion +Description[sr@Latn]=IOSlave za Subversion +Description[sv]=Subversion I/O-slav +Description[tr]=Alt Version ioslave +Description[uk]=Підлеглий В/В Subversion +maxInstances=5 +class=:internet diff --git a/kioslave/svn/svnhelper/Makefile.am b/kioslave/svn/svnhelper/Makefile.am new file mode 100644 index 00000000..bb5afe46 --- /dev/null +++ b/kioslave/svn/svnhelper/Makefile.am @@ -0,0 +1,18 @@ +bin_PROGRAMS = kio_svn_helper + +INCLUDES = $(all_includes) +AM_LDFLAGS = $(all_libraries) + +kio_svn_helper_SOURCES = kio_svn_helper.cpp subversioncheckout.ui subversionswitch.ui subversionlog.ui subversiondiff.ui + +kio_svn_helper_LDFLAGS = $(KDE_RPATH) $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KSYCOCA) $(LIB_KIO) $(all_libraries) + +servicemenudir = \ + $(kde_datadir)/konqueror/servicemenus + +servicemenu_DATA = \ + subversion.desktop subversion_toplevel.desktop + + +METASOURCES = AUTO + diff --git a/kioslave/svn/svnhelper/apply_patch.desktop b/kioslave/svn/svnhelper/apply_patch.desktop new file mode 100644 index 00000000..0c010a5d --- /dev/null +++ b/kioslave/svn/svnhelper/apply_patch.desktop @@ -0,0 +1,94 @@ +[Desktop Entry] +ServiceTypes=text/x-diff +#uncomment when Exec is implemented +#Actions=Apply +X-KDE-Priority=TopLevel + +[Desktop Action Apply] +Name=Apply Patch... +Name[bg]=Прилагане на кръпка... +Name[ca]=Aplica pedaç... +Name[cs]=Aplikovat záplatu... +Name[da]=Anvend rettelse... +Name[de]=Patch einspielen ... +Name[el]=Εφαρμογή επιδιόρθωσης... +Name[eo]=Apliki Flikon... +Name[es]=Aplicar parche... +Name[et]=Paiga rakendamine... +Name[eu]=Aplikatu adabakia... +Name[fa]=اعمال کژنه... +Name[fi]=Toteuta korjaus... +Name[fr]=Appliquer le correctif... +Name[ga]=Cuir Paiste i bhFeidhm... +Name[gl]=Aplicar un parche... +Name[he]=החל את הטלאי... +Name[hu]=Folt alkalmazása... +Name[is]=Virkja plástur... +Name[it]=Applica correzione... +Name[ja]=パッチを適用... +Name[ka]=ბებკის გააქტიურება... +Name[kk]=Жамауды қолдану... +Name[lt]=Pritaikyti pataisymą... +Name[nb]=Bruk lapp … +Name[nds]=Kodeplaster inspelen... +Name[ne]=लागू प्याच... +Name[nl]=Patch toepassen... +Name[nn]=Bruk lapp … +Name[pa]=ਪੈਂਚ ਲਾਗੂ... +Name[pl]=Nałóż łatę... +Name[pt]=Aplicar um 'Patch'... +Name[pt_BR]=Aplicar um 'Patch'... +Name[ru]=Применить заплатку... +Name[sk]=Aplikovať záplatu... +Name[sl]=Uveljavi popravek ... +Name[sr]=Примени закрпу... +Name[sr@Latn]=Primeni zakrpu... +Name[sv]=Utför programfix... +Name[tr]=Yamayı Uygula... +Name[uk]=Застосувати латку... +Name[zh_CN]=应用补丁... +Name[zh_TW]=套用修補... +Icon=apply +Exec= +Comment=Apply the patch to another folder/file +Comment[bg]=Прилагане на кръпка към друга директория/файл +Comment[ca]=Aplica el pedaç a una altra carpeta o fitxer +Comment[cs]=Aplikovat záplatu na jiný soubor/složku +Comment[da]=Anvend rettelsen på en anden mappe/fil +Comment[de]=Spielt den Patch in einen/eine andere(n) Ordner/Datei ein +Comment[el]=Εφαρμογή της επιδιόρθωσης σε άλλο φάκελο/αρχείο +Comment[es]=Aplicar el parche a otra carpeta o archivo +Comment[et]=Paiga rakendamine teisele kataloogile/failile +Comment[eu]=Aplikatu adabakia beste karpeta/fitxategi bati +Comment[fa]=اعمال کژنه به پوشه/پروندۀ دیگر +Comment[fi]=Toteuta korjaus toiseen kansioon/tiedostoon +Comment[fr]=Appliquer le correctif sur un autre dossier / fichier +Comment[ga]=Cuir an paiste i bhfeidhm ar fhillteán/chomhad eile +Comment[gl]=Aplicar o parche noutro cartafol/ficheiro +Comment[hu]=A folt alkalmazása másik könyvtárra vagy fájlra +Comment[is]=Virkja plásturinn á aðra möppu/skrá +Comment[it]=Applica la correzione a un'altra cartella o file +Comment[ja]=他のフォルダやファイルへパッチを適用します。 +Comment[ka]=ბებკის სხვა საქაღალდეზე/ფაილზე გააქტიურება +Comment[kk]=Басқа қапшық/файлға жамауды қолдану +Comment[lt]=Pritaikyti pataisymą kitam aplankui/bylai +Comment[nb]=Bruk lappen på en annen mappe/fil +Comment[nds]=Kodeplaster op en anner Orner/Datei anwennen +Comment[ne]=अन्य फोल्डर/फाइलमा प्याच लागू गर्नुहोस् +Comment[nl]=Patch toepassen op een andere map of een ander bestand +Comment[nn]=Bruk lappen på ei anna mappe/fil +Comment[pa]=ਹੋਰ ਫੋਲਡਰ/ਫਾਇਲ ਲਈ ਪੈਂਚ ਲਾਗੂ +Comment[pl]=Nałożenie łaty na inny plik/katalog +Comment[pt]=Aplicar o 'patch' (actualização) noutra pasta/ficheiro +Comment[pt_BR]=Aplicar o 'patch' (atualização) noutra pasta/arquivo +Comment[ru]=Применить заплатку к другой папке или файлу +Comment[sk]=Aplikovať záplatu na iný priečinok/súbor +Comment[sl]=Uveljavi popravek za drugo mapo/datoteko +Comment[sr]=Примени закрпу на другу фасциклу/фајл +Comment[sr@Latn]=Primeni zakrpu na drugu fasciklu/fajl +Comment[sv]=Utför en programfix för en annan katalog eller fil +Comment[tr]=Yamayı diğer dizine/dosyaya uygula +Comment[uk]=Застосувати латку до іншої теки файла +Comment[zh_CN]=将补丁应用到其它文件夹/文件 +Comment[zh_TW]=套用修補到另一個資料夾/檔案 + diff --git a/kioslave/svn/svnhelper/kio_svn_helper.cpp b/kioslave/svn/svnhelper/kio_svn_helper.cpp new file mode 100644 index 00000000..1b66033b --- /dev/null +++ b/kioslave/svn/svnhelper/kio_svn_helper.cpp @@ -0,0 +1,292 @@ +/* This file is part of the KDE project + Copyright (c) 2005 Mickael Marchand + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kio_svn_helper.h" +#include "subversioncheckout.h" +#include "subversionswitch.h" +#include "subversiondiff.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SvnHelper::SvnHelper():KApplication() { + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + KWinModule wm ( this ); + m_id = wm.activeWindow(); + + KURL::List list; + for ( int i = 0 ; i < args->count() ; i++ ) + list << args->url(i); + + if (args->isSet("u")) { + kdDebug(7128) << "update " << list << endl; + KURL servURL = "svn+http://this_is_a_fake_URL_and_this_is_normal/"; + //FIXME when 1.2 is out (move the loop inside kio_svn's ::update) + for ( QValueListConstIterator it = list.begin(); it != list.end() ; ++it ) { + QByteArray parms; + QDataStream s( parms, IO_WriteOnly ); + int cmd = 2; + int rev = -1; + kdDebug(7128) << "updating : " << (*it).prettyURL() << endl; + s << cmd << *it << rev << QString( "HEAD" ); + KIO::SimpleJob * job = KIO::special(servURL, parms, true); + connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotResult( KIO::Job * ) ) ); + KIO::NetAccess::synchronousRun( job, 0 ); + } + } else if (args->isSet("c")) { + kdDebug(7128) << "commit " << list << endl; + KURL servURL = "svn+http://this_is_a_fake_URL_and_this_is_normal/"; + QByteArray parms; + QDataStream s( parms, IO_WriteOnly ); + int cmd = 3; + s< it = list.begin(); it != list.end() ; ++it ) { + kdDebug(7128) << "commiting : " << (*it).prettyURL() << endl; + s << *it; + } + KIO::SimpleJob * job = KIO::special(servURL, parms, true); + connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotResult( KIO::Job * ) ) ); + KIO::NetAccess::synchronousRun( job, 0 ); + } else if (args->isSet("a")) { + kdDebug(7128) << "add " << list << endl; + KURL servURL = "svn+http://this_is_a_fake_URL_and_this_is_normal/"; + for ( QValueListConstIterator it = list.begin(); it != list.end() ; ++it ) { + QByteArray parms; + QDataStream s( parms, IO_WriteOnly ); + int cmd = 6; + kdDebug(7128) << "adding : " << (*it).prettyURL() << endl; + s << cmd << *it; + KIO::SimpleJob * job = KIO::special(servURL, parms, true); + connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotResult( KIO::Job * ) ) ); + KIO::NetAccess::synchronousRun( job, 0 ); + } + } else if (args->isSet("D")) { + kdDebug(7128) << "diff " << list << endl; + KURL servURL = "svn+http://this_is_a_fake_URL_and_this_is_normal/"; + for ( QValueListConstIterator it = list.begin(); it != list.end() ; ++it ) { + QByteArray parms; + QDataStream s( parms, IO_WriteOnly ); + int cmd = 13; + kdDebug(7128) << "diffing : " << (*it).prettyURL() << endl; + int rev1=-1; + int rev2=-1; + QString revkind1 = "BASE"; + QString revkind2 = "WORKING"; + s << cmd << *it << *it << rev1 << revkind1 << rev2 << revkind2 << true ; + KIO::SimpleJob * job = KIO::special(servURL, parms, true); + connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotResult( KIO::Job * ) ) ); + KIO::NetAccess::synchronousRun( job, 0 ); + if ( diffresult.count() > 0 ) { + //check kompare is available + if ( !KStandardDirs::findExe( "kompare" ).isNull() ) { + KTempFile *tmp = new KTempFile; + tmp->setAutoDelete(true); + QTextStream *stream = tmp->textStream(); + stream->setCodec( QTextCodec::codecForName( "utf8" ) ); + for ( QStringList::Iterator it2 = diffresult.begin();it2 != diffresult.end() ; ++it2 ) { + ( *stream ) << ( *it2 ) << "\n"; + } + tmp->close(); + KProcess *p = new KProcess; + *p << "kompare" << "-n" << "-o" << tmp->name(); + p->start(); + } else { //else do it with message box + Subversion_Diff df; + for ( QStringList::Iterator it2 = diffresult.begin();it2 != diffresult.end() ; ++it2 ) { + df.text->append( *it2 ); + } + QFont f = df.font(); + f.setFixedPitch( true ); + df.text->setFont( f ); + df.exec(); + } + } + diffresult.clear(); + } + } else if (args->isSet("d")) { + kdDebug(7128) << "delete " << list << endl; + KURL servURL = "svn+http://this_is_a_fake_URL_and_this_is_normal/"; + QByteArray parms; + QDataStream s( parms, IO_WriteOnly ); + int cmd = 7; + s< it = list.begin(); it != list.end() ; ++it ) { + kdDebug(7128) << "deleting : " << (*it).prettyURL() << endl; + s << *it; + } + KIO::SimpleJob * job = KIO::special(servURL, parms, true); + connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotResult( KIO::Job * ) ) ); + KIO::NetAccess::synchronousRun( job, 0 ); + } else if (args->isSet("s")) { + kdDebug(7128) << "switch " << list << endl; + SubversionSwitch d; + int result = d.exec(); + if ( result == QDialog::Accepted ) { + for ( QValueListConstIterator it = list.begin(); it != list.end() ; ++it ) { + kdDebug(7128) << "switching : " << (*it).prettyURL() << endl; + KURL servURL = "svn+http://this_is_a_fake_URL_and_this_is_normal/"; + QByteArray parms; + QDataStream s( parms, IO_WriteOnly ); + int revnumber = -1; + QString revkind = "HEAD"; + if ( d.revision->value() != 0 ) { + revnumber = d.revision->value(); + revkind = ""; + } + bool recurse=true; + int cmd = 12; + s << cmd; + s << *it; + s << KURL( d.url->url() ); + s << recurse; + s << revnumber; + s << revkind; + KIO::SimpleJob * job = KIO::special(servURL, parms, true); + connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotResult( KIO::Job * ) ) ); + KIO::NetAccess::synchronousRun( job, 0 ); + } + } + } else if (args->isSet("r")) { + kdDebug(7128) << "revert " << list << endl; + KURL servURL = "svn+http://this_is_a_fake_URL_and_this_is_normal/"; + QByteArray parms; + QDataStream s( parms, IO_WriteOnly ); + int cmd = 8; + s< it = list.begin(); it != list.end() ; ++it ) { + kdDebug(7128) << "reverting : " << (*it).prettyURL() << endl; + s << *it; + } + KIO::SimpleJob * job = KIO::special(servURL, parms, true); + connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotResult( KIO::Job * ) ) ); + KIO::NetAccess::synchronousRun( job, 0 ); + } else if (args->isSet("C")) { + kdDebug(7128) << "checkout " << list << endl; + SubversionCheckout d; + int result = d.exec(); + if ( result == QDialog::Accepted ) { + for ( QValueListConstIterator it = list.begin(); it != list.end() ; ++it ) { + KURL servURL = "svn+http://this_is_a_fake_URL_and_this_is_normal/"; + QByteArray parms; + QDataStream s( parms, IO_WriteOnly ); + int cmd = 1; + int rev = -1; + QString revkind = "HEAD"; + if ( d.revision->value() != 0 ) { + rev = d.revision->value(); + revkind = ""; + } + s<url() ); + s << ( *it ); + s << rev; + s << revkind; + kdDebug(7128) << "checkouting : " << d.url->url() << " into " << (*it).prettyURL() << " at rev : " << rev << " or " << revkind << endl; + KIO::SimpleJob * job = KIO::special(servURL, parms, true); + connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotResult( KIO::Job * ) ) ); + KIO::NetAccess::synchronousRun( job, 0 ); + } + } + } else { + KMessageBox::sorry(0, "Sorry, request not recognised. Perhaps not implemented yet?", "Feature Not Implemented"); + } + QTimer::singleShot( 0, this, SLOT( finished() ) ); +} + +void SvnHelper::slotResult( KIO::Job* job ) { + if ( job->error() ) + job->showErrorDialog( ); + + KIO::MetaData ma = job->metaData(); + QValueList keys = ma.keys(); + qHeapSort( keys ); + QValueList::Iterator begin = keys.begin(), end = keys.end(), it; + + QStringList message; + for ( it = begin; it != end; ++it ) { + // kdDebug(7128) << "METADATA helper : " << *it << ":" << ma[ *it ] << endl; + if ( ( *it ).endsWith( "string" ) ) { + if ( ma[ *it ].length() > 2 ) { + message << ma[ *it ]; + } + } + //extra check to retrieve the diff output in case with run a diff command + if ( ( *it ).endsWith( "diffresult" ) ) { + diffresult << ma[ *it ]; + } + } + if ( message.count() > 0 ) + KMessageBox::informationListWId(m_id, "", message, "Subversion"); +} + +void SvnHelper::finished() { + kapp->quit(); +} + +static KCmdLineOptions options[] = { + { "u", I18N_NOOP("Update given URL"), 0 }, + { "c", I18N_NOOP("Commit given URL"), 0 }, + { "C", I18N_NOOP("Checkout in given directory"), 0 }, + { "a", I18N_NOOP("Add given URL to the working copy"), 0 }, + { "d", I18N_NOOP("Delete given URL from the working copy"), 0 }, + { "s", I18N_NOOP("Switch given working copy to another branch"), 0 }, + { "r", I18N_NOOP("Revert local changes"), 0 }, + { "m", I18N_NOOP("Merge changes between two branches"), 0 }, + { "D", I18N_NOOP("Show locally made changements with diff"), 0 }, + {"!+URL", I18N_NOOP("URL to update/commit/add/delete from Subversion"), 0 }, + KCmdLineLastOption +}; + +int main(int argc, char **argv) { + KCmdLineArgs::init(argc, argv, "kio_svn_helper", I18N_NOOP("Subversion Helper"), "KDE frontend for SVN", "0.1"); + + KCmdLineArgs::addCmdLineOptions( options ); + KGlobal::locale()->setMainCatalogue("kio_svn"); + KApplication::addCmdLineOptions(); + + if ( KCmdLineArgs::parsedArgs()->count()==0 ) + KCmdLineArgs::usage(); + KApplication *app = new SvnHelper(); + +// app->dcopClient()->attach(); + app->exec(); +} + +#include "kio_svn_helper.moc" diff --git a/kioslave/svn/svnhelper/kio_svn_helper.h b/kioslave/svn/svnhelper/kio_svn_helper.h new file mode 100644 index 00000000..519577f2 --- /dev/null +++ b/kioslave/svn/svnhelper/kio_svn_helper.h @@ -0,0 +1,41 @@ +/* This file is part of the KDE project + Copyright (c) 2005 Mickael Marchand + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _KIO_SVN_HELPER_H_ +#define _KIO_SVN_HELPER_H_ + +#include +#include +#include +#include + +class SvnHelper:public KApplication { + Q_OBJECT + +public: + SvnHelper(); +private slots: + void finished(); + void slotResult( KIO::Job *); +private: + WId m_id; + QStringList diffresult; //for diff commands ;) +}; + +#endif diff --git a/kioslave/svn/svnhelper/subversion.desktop b/kioslave/svn/svnhelper/subversion.desktop new file mode 100644 index 00000000..00d206d0 --- /dev/null +++ b/kioslave/svn/svnhelper/subversion.desktop @@ -0,0 +1,919 @@ +[Desktop Entry] +ServiceTypes=inode/directory,all/all +X-KDE-Submenu=Subversion +X-KDE-Submenu[fa]=زیرنسخه +X-KDE-Submenu[ne]=उप संस्करण +X-KDE-Submenu[pa]=ਸਬ-ਵਰਜਨ +X-KDE-Submenu[pt_BR]=Subversão +#X-KDE-ShowIfDcopCall=kded ksvnd anyValidWorkingCopy(KURL::List) + +#Return type of below is a QStringList with a list of Actions which is appended to the Actions above +X-KDE-GetActionMenu=kded ksvnd getActionMenu(KURL::List) + +[Desktop Action Add] +Name=Add to Repository +Name[bg]=Добавяне в хранилището +Name[br]=Ouzhpennañ d'an daveiñ +Name[ca]=Afegeix al repositori +Name[cs]=Přidat do repository +Name[da]=Tilføj til lager +Name[de]=Zum SVN-Archiv hinzufügen +Name[el]=Προσθήκη στο χώρο αποθήκευσης +Name[es]=Añadir al repositorio +Name[et]=Hoidlasse lisamine +Name[eu]=Gehitu biltegira +Name[fa]=افزودن به مخزن +Name[fi]=Lisää versionhallintaan +Name[fr]=Ajouter au référentiel +Name[ga]=Cuir leis an Stór +Name[gl]=Engadir ao repositorio +Name[he]=הוסף למאגר +Name[hu]=Hozzáadás az adattárhoz +Name[is]=Bæta við geymslu +Name[it]=Aggiungi al deposito +Name[ja]=リポジトリへ追加 +Name[ka]=რეპოზიტორიისთვის დმატება +Name[kk]=Қоймасына қосу +Name[lt]=Įdėti į saugyklą +Name[nb]=Legg til lager +Name[nds]=Na't Archiv tofögen +Name[ne]=भण्डारमा थप्नुहोस् +Name[nl]=Toevoegen aan repository +Name[nn]=Legg til lager +Name[pa]=ਰਿਪੋਜ਼ਟਰੀ 'ਚ ਸ਼ਾਮਲ +Name[pl]=Dodaj do repozytorium +Name[pt]=Adicionar ao Repositório +Name[pt_BR]=Adicionar ao Repositório +Name[ru]=Добавить в хранилище +Name[sk]=Pridať do archívu +Name[sl]=Dodaj v skladišče +Name[sr]=Додај у складиште +Name[sr@Latn]=Dodaj u skladište +Name[sv]=Lägg till i arkiv +Name[tr]=Depoya Ekle +Name[uk]=Додати до сховища +Name[zh_CN]=添加到仓库 +Name[zh_TW]=新增到主目錄 +Icon=svn_add +Exec=kio_svn_helper -a %U + +[Desktop Action Delete] +Name=Delete From Repository +Name[bg]=Изтриване от хранилището +Name[br]=Lemel eus an daveiñ +Name[ca]=Elimina del repositori +Name[cs]=Smazat z repository +Name[da]=Slet fra lager +Name[de]=Aus dem SVN-Archiv löschen +Name[el]=Διαγραφή από το χώρο αποθήκευσης +Name[es]=Eliminar del repositorio +Name[et]=Hoidlast kustutamine +Name[eu]=Ezabatu biltegitik +Name[fa]=حذف از مخزن +Name[fi]=Poista versionhallinnasta +Name[fr]=Supprimer du référentiel +Name[ga]=Scrios ón Stór +Name[gl]=Eliminar do repositorio +Name[he]=מחק ממאגר +Name[hu]=Törlés az adattárból +Name[is]=Eyða frá geymslu +Name[it]=Elimina dal deposito +Name[ja]=リポジトリから削除 +Name[ka]=რეპოზირტორიიდან წაშლა +Name[kk]=Қоймасынан өшіру +Name[lt]=Pašalinti iš saugyklos +Name[nb]=Slett fra lager +Name[nds]=Ut Archiv wegdoon +Name[ne]=भण्डारबाट मेट्नुहोस् +Name[nl]=Verwijderen uit repository +Name[nn]=Slett frå lager +Name[pa]=ਰਿਪੋਜ਼ਟਰੀ ਤੋਂ ਹਟਾਓ +Name[pl]=Usuń z repozytorium +Name[pt]=Remover do Repositório +Name[pt_BR]=Remover do Repositório +Name[ru]=Удалить из хранилища +Name[sk]=Odstrániť z archívu +Name[sl]=Izbriši iz skladišča +Name[sr]=Обриши из складишта +Name[sr@Latn]=Obriši iz skladišta +Name[sv]=Ta bort från arkiv +Name[tr]=Depodan Sil +Name[uk]=Видалити зі сховища +Name[zh_CN]=从仓库删除 +Name[zh_TW]=從主目錄刪除 +Icon=svn_remove +Exec=kio_svn_helper -d %U + +[Desktop Action Revert] +Name=Revert Local Changes +Name[bg]=Връщане на локалните промени +Name[ca]=Reverteix els canvis locals +Name[cs]=Vrátit místní změny +Name[da]=Vend lokale ændringer om +Name[de]=Lokale Änderungen zurücknehmen +Name[el]=Επαναφορά τοπικών αλλαγών +Name[es]=Revertir cambios locales +Name[et]=Kohalike muudatuste tühistamine +Name[eu]=Leheneratu aldaketa lokalak +Name[fa]=بازگشت تغییرات محلی +Name[fi]=Palauta paikalliset muutokset +Name[fr]=Annuler les modifications locales +Name[gl]=Anular as modificacións locais +Name[he]=נקה שינויים מקומיים +Name[hu]=A helyi módosítások visszavonása +Name[is]=Afturkalla staðbundnar breytingar +Name[it]=Annulla i cambiamenti locali +Name[ja]=ローカルでの変更を元に戻す +Name[ka]=ლოკალური ცვლილებების შებრუნება +Name[kk]=Жергілікті өзгерістерінен қайту +Name[lt]=Atšaukti vietinius pakeitimus +Name[nb]=Tilbakestill lokale endringer +Name[nds]=Lokaal Ännern torüchnehmen +Name[ne]=उल्टो स्थानीय परिवर्तन +Name[nl]=Lokale wijzigingen ongedaan maken +Name[nn]=Tilbakestill lokale endringar +Name[pa]=ਉਲਟ ਸਥਾਨਕ ਤਬਦੀਲੀਆਂ +Name[pl]=Cofnij lokalne zmiany +Name[pt]=Reverter as Modificações Locais +Name[pt_BR]=Reverter as Modificações Locais +Name[ru]=Отменить локальные изменения +Name[sk]=Vrátiť lokálne zmeny +Name[sl]=Povrni krajevne spremembe +Name[sr]=Одбаци локалне измене +Name[sr@Latn]=Odbaci lokalne izmene +Name[sv]=Återställ lokal ändring +Name[tr]=Yerel Değişiklikleri Ters Çevir +Name[uk]=Повернути локальні зміни +Name[zh_CN]=恢复本地更改 +Name[zh_TW]=回復本地端變更 +Icon=undo +Exec=kio_svn_helper -r %U +Comment=Remove any changes made locally. Warning - this cannot be undone. +Comment[bg]=Премахване на направените локални промени. Предупреждение - данните ще се загубят безвъзвратно. +Comment[ca]=Elimina qualsevol canvi local. Avís: No es pot desfer. +Comment[cs]=Odstraní změny provedené lokálně; nelze vrátit, pozor. +Comment[da]=Fjern alle ændringer der er lavet lokalt. Advarsel - dette kan ikke fortrydes. +Comment[de]=Nimmt alle lokal durchgeführten Änderungen zurück. Warnung: Dieser Vorgang kann nicht rückgängig gemacht werden. +Comment[el]=Αφαίρεση κάθε τροποποίησης που έγινε τοπικά. Προειδοποίηση ότι αυτό δε μπορεί να αναιρεθεί. +Comment[es]=Eliminar cualquier cambio local. Atención: esto no se puede deshacer. +Comment[et]=Eemaldab kõik kohalikud muudatused. Hoiatus: seda ei saa tagasi võtta. +Comment[eu]=Kendu lokalki egindako aldaketak. Abisua: ekintza hau ezin da desegin. +Comment[fa]=حذف همۀ تغییرات ایجادشدۀ محلی. اخطار - این نمی‌تواند انجام نشود. +Comment[fi]=Poista kaikki paikallisesti tehdyt muutokset. Varoitus - muutosta ei voi perua. +Comment[fr]=Annuler toutes les modifications effectuées localement. Attention, cela ne peut pas être annulé. +Comment[gl]=Borra as alteracións feitas a nível local. Atención - isto non pode ser anulado. +Comment[hu]=A helyi módosítások visszavonása. Ez a művelet nem vonható vissza! +Comment[is]=Fjarlægja allar breytingar sem voru gerðar hér. Athugið - það er ekki hægt að afturkalla þetta. +Comment[it]=Rimuovi ogni cambiamento fatto localmente. Attenzione: non si può tornare indietro. +Comment[ja]=ローカルで行われた変更を削除します。注意: この操作は元に戻せません。 +Comment[ka]=ნებისმიერი ლოკალურად გაკეთებული ცვლილებების წაშლა. გაფრთხილება - ამის უკუქცევა შეუძლებელია. +Comment[kk]=Жергілікті (яғни тапсырылмаған) өзгерістерден айну. Абайлаңыз - бұл амалдан қайта алмайсыз. +Comment[lt]=Panaikinti visus vietoje atliktus pakeitimus. Perspėjimas: to nebebus galima atšaukti. +Comment[nb]=Fjern alle endringer som er gjort lokalt. MERK – dette kan ikke angres. +Comment[nds]=All lokaal Ännern wegdoon. Wohrschoen - Dit lett sik nich torüchnehmen. +Comment[ne]=कुनै पनि स्थानीय परिवर्तन हटाउनुहोस् । चेतावनी - यसलाई पूर्वावस्थामा फर्काउन सकिने छैन । +Comment[nl]=Lokale wijzigingen ongedaan maken. Let op: dit kan niet teruggedraaid worden. +Comment[nn]=Fjern alle endringar som er gjorde lokalt. MERK – dette kan ikkje angrast. +Comment[pl]=Usuwa wszystkie zmiany dokonane lokalnie. Uwaga - tej operacji nie można cofnąć. +Comment[pt]=Remover as alterações que tenham sido feitas a nível local. Atenção - isto não pode ser anulado. +Comment[pt_BR]=Remover as alterações que tenham sido feitas localmente. Atenção - isto não pode ser desfeito. +Comment[ru]=Отменить все не опубликованные изменения. Эта операция не подлежит отмене. +Comment[sk]=Odstráni lokálne zmeny. Upozornenie - toto sa nedá už vrátiť. +Comment[sl]=Odstrani vse spremembe, opravljene krajevno. Opozorilo - tega ni mogoče razveljaviti. +Comment[sr]=Уклони све локално направљене измене. Упозорење: ово се не може опозвати. +Comment[sr@Latn]=Ukloni sve lokalno napravljene izmene. Upozorenje: ovo se ne može opozvati. +Comment[sv]=Tar bort alla ändringar som gjorts lokalt. Varning: detta kan inte ångras. +Comment[tr]=Yerel olarak yapılan değişiklikleri kaldır. Dikkat - bu işlem geri alınamaz. +Comment[uk]=Вилучити всі зміни, які було зроблено локально. Попередження - зміни буде вилучено назавжди. +Comment[zh_CN]=删除本地进行的任何更改。警告 - 此操作无法撤消。 +Comment[zh_TW]=移除任何已做的變更。警告:無法再復原。 + +[Desktop Action Rename] +Name=Rename... +Name[bg]=Преименуване... +Name[br]=Adenvel ... +Name[ca]=Reanomena... +Name[cs]=Přejmenovat... +Name[cy]=Ail-enwi... +Name[da]=Omdøb... +Name[de]=Umbenennen ... +Name[el]=Μετονομασία... +Name[eo]=Alinomigi... +Name[es]=Cambiar nombre... +Name[et]=Ümbernimetamine... +Name[eu]=Berrizendatu... +Name[fa]=تغییر نام... +Name[fi]=Nimeä uudelleen... +Name[fr]=Renommer... +Name[ga]=Athainmnigh... +Name[gl]=Mudar o nome... +Name[he]=שנה שם... +Name[hu]=Átnevezés... +Name[is]=Endurnefna... +Name[it]=Rinomina... +Name[ja]=名前変更... +Name[ka]=სახელის გადარქმევა... +Name[kk]=Қайта атау... +Name[lt]=Pervadinti... +Name[nb]=Endre navn … +Name[nds]=Ümnömen... +Name[ne]=पुन: नामकरण गर्नुहोस्... +Name[nl]=Hernoemen... +Name[nn]=Endra namn … +Name[pa]=ਨਾਂ-ਤਬਦੀਲ... +Name[pl]=Zmień nazwę... +Name[pt]=Mudar o Nome... +Name[pt_BR]=Renomear... +Name[ru]=Переименовать... +Name[sk]=Premenovať... +Name[sl]=Preimenuj ... +Name[sr]=Преименуј... +Name[sr@Latn]=Preimenuj... +Name[sv]=Byt namn... +Name[tr]=Yeniden Adlandır... +Name[uk]=Перейменувати... +Name[zh_CN]=重命名... +Name[zh_TW]=重新命名... +Icon=pencil +Exec=kio_svn_helper -r %U +Comment=Rename a file locally and in the repository. Use this rather than adding and deleting to rename a file. +Comment[bg]=Преименуване на файл локално и в хранилището. За предпочитане е да използвате този метод вместо изтриване и добавяне. +Comment[ca]=Reanomena un fitxer localment i en el repositori. Use-ho en comptes d'afegir i eliminar per a reanomenar un fitxer. +Comment[cs]=Přejmenovat soubor lokálně a v repository. Použijte raději než přidání a smazání souboru k docílení jeho přejmenování. +Comment[da]=Omdøb en fil lokalt og i lageret. Brug dette i stedet for at tilføje og slette for at omdøbe en fil. +Comment[de]=Benennt eine Datei lokal und im SVN-Archiv um. Verwenden Sie besser diese Funktion zum Umbenennen einer Datei als Hinzufügen und Löschen. +Comment[el]=Μετονομασία ενός αρχείου τοπικά και στο χώρο αποθήκευσης. Χρησιμοποιήστε αυτό αντί της προσθήκης και αφαίρεσης για τη μετονομασία ενός αρχείου. +Comment[es]=Cambiar el nombre de un archivo localmente y en el repositorio. Use esto en lugar de añadir y eliminar para cambiar el nombre de un archivo. +Comment[et]=Faili ümbernimetamine nii kohalikult kui hoidlas. See on eelistatud viis faili ümbernimetamisel lisamise ja kustutamise asemel. +Comment[eu]=Berrizendatu fitxategi bat lokalki eta biltegian. Erabili hau fitxategia ezabatu eta berriro gehitu ordez. +Comment[fa]=تغییر نام پروندۀ محلی و در مخزن. به جای افزودن و حذف، برای تغییر نام پرونده از این استفاده کنید. +Comment[fi]=Nimeä uudelleen paikallinen ja versionhallinnassa oleva tiedosto. Nimeä tiedosto uudelleen mieluummin näin kuin lisäämällä ja poistamalla. +Comment[fr]=Renommer un fichier localement et dans le référentiel. Utilisez cette option pour renommer un fichier, au lieu de le supprimer puis l'ajouter sous le nouveau nom. +Comment[gl]=Muda o nome dun ficheiro tanto localmente como no repositorio. Use isto en vez de engadir e eliminar o ficheiro para mudar o nome. +Comment[hu]=Fájl átnevezése helyben és az adattárban. Ezt érdemes használni hozzáadás és törlés helyett. +Comment[is]=Endurnefna skrá hér og í geymslunni. Notist heldur við þetta í stað þess að bæta við og eyða til að endurnefna skrá. +Comment[it]=Rinomina un file localmente e nel deposito. Usa questo invece di aggiungere e rimuovere un file per rinominarlo. +Comment[ja]=ローカルとリポジトリのファイルを改名します。名前の変更の際に追加と削除をしないで、この方法を使用してください。 +Comment[ka]=ფაილის სახელის გადარქმევა ლოკალურად და რეპოზიტორიასი. დამატების და წაშლის ნაცვლად ეს გამოიყენეთ. +Comment[kk]=Файлдың жергілікті де, қоймасындағы да атауын өзгерту. Өшіріп қайта қосудан гөрі осыны қолданған дұрыс. +Comment[lt]=Pervadinti bylą vietoje ir saugykloje. Naudokite šią parinktį užuot ištrindami ir įrašydami nauju vardu norimą pervadinti bylą. +Comment[nb]=Gi en fil nytt navn lokalt og i arkivet. Bruk dette heller enn å slette og legge inn på nytt for å endre navn på en fil. +Comment[nds]=En Datei lokaal un in't Archiv ümnömen. Bruuk beter dit, as "Tofögen" un "Wegdoon". +Comment[ne]=फाइलालाई स्थानीय रुपमा र भण्डारमा पुन: नामकरण गर्नुहोस् । फाइललाई पुन: नामकरण गर्न थप्ने र मेट्ने भन्दा यसलाई प्रयोग गर्नुहोस् । +Comment[nl]=Hernoem een bestand lokaal en in de repository. Gebruik dit bij voorkeur boven het verwijderen van een bestand en het toevoegen onder een andere naam. +Comment[nn]=Gi ei fil nytt namn lokalt og i arkivet. Bruk dette heller enn å sletta og leggja inn på nytt for å endra namn på ei fil. +Comment[pl]=Zmienia nazwę pliku lokalnie i w repozytorium. Należy tego używać zamiast dodawania i usuwania pliku. +Comment[pt]=Muda o nome de um ficheiro a nível local e no repositório. Use isto em vez de adicionar e remover o ficheiro para mudar o nome. +Comment[pt_BR]=Muda o nome de um arquivo localmente e no repositório. Use isto em vez de adicionar e remover o arquivo para mudar o nome. +Comment[ru]=Переименовать файл с отражением этого в хранилище. +Comment[sk]=Premenuje súbor lokálne aj v archíve. Použite radšej toto ako pridanie a odstránenie súboru. +Comment[sl]=Preimenuj datoteko krajevno in v skladišču. Uporabite to namesto brisanja in dodajanja datoteke. +Comment[sr]=Преименуј фајл локално и у складишту. Користите ово уместо трика са додавањем и брисањем фајла. +Comment[sr@Latn]=Preimenuj fajl lokalno i u skladištu. Koristite ovo umesto trika sa dodavanjem i brisanjem fajla. +Comment[sv]=Byt namn på en fil lokalt och i arkivet. Använd detta istället för att lägga till och ta bort för att byta namn på en fil. +Comment[uk]=Перейменувати файл локально і в сховищі. Вживайте замість додавання і видалення файла, щоб його перейменувати. +Comment[zh_CN]=在本地和仓库中重命名文件。使用此功能来取代对文件的添加和删除。 +Comment[zh_TW]=在本地端與主目錄中重新命名檔案。不必先新增再刪除檔案。 + +[Desktop Action Import] +Name=Import Repository +Name[bg]=Импортиране на директория +Name[br]=Enporzh an daveiñ +Name[ca]=Importa repositori +Name[cs]=Importovat repository +Name[da]=Importér lager +Name[de]=SVN-Archiv importieren +Name[el]=Εισαγωγή χώρου αποθήκευσης +Name[eo]=Importi Deponejon +Name[es]=Importar repositorio +Name[et]=Hoidla import +Name[eu]=Inportatu biltegia +Name[fa]=مخزن واردات +Name[fi]=Tuo versionhallinta +Name[fr]=Importer dans un référentiel +Name[ga]=Iompórtáil Stór +Name[gl]=Importar un repositorio +Name[hu]=Adattár importálása +Name[is]=Flytja inn geymslu +Name[it]=Importa deposito +Name[ja]=リポジトリのインポート +Name[ka]=რეპოზიტორიის იმპორტი +Name[kk]=Қоймасына импорттау +Name[lt]=Importuoti saugyklą +Name[nb]=Importer lager +Name[nds]=Archiv importeren +Name[ne]=आयात भण्डार +Name[nl]=Repository importeren +Name[nn]=Importer lager +Name[pa]=ਰਿਪੋਜ਼ਟਰੀ ਅਯਾਤ +Name[pl]=Importuj repozytorium +Name[pt]=Importar um Repositório +Name[pt_BR]=Importar um Repositório +Name[ru]=Импортировать в хранилище +Name[sk]=Importovať archív +Name[sl]=Uvozi skladišče +Name[sr]=Увези складиште +Name[sr@Latn]=Uvezi skladište +Name[sv]=Importera arkiv +Name[tr]=Depoyu İçe Aktar +Name[uk]=Імпортувати сховище +Name[zh_CN]=导入仓库 +Name[zh_TW]=匯入主目錄 +Icon=svn_import +Exec=kio_svn_helper -i %U +Comment=Put folder into an existing repository to put it under revision control. +Comment[bg]=Поставяне на директория в съществуващо хранилище. +Comment[ca]=Situa la carpeta en un repositori existent per a posar-la sota el control de revisions. +Comment[cs]=Zařadit složku do repository a správy verzí +Comment[da]=Put mappe ind i et eksisterende lager for at få den ind under revisionskontrol. +Comment[de]=Schiebt den Ordner in ein existierendes SVN-Archiv, um ihn in die Versionsverwaltung aufzunehmen. +Comment[el]=Εισαγωγή φακέλου σε έναν υπάρχον χώρο αποθήκευσης για τον έλεγχο εκδόσεων. +Comment[es]=Situar la carpeta en un repositorio existente para ponerla bajo control de revisión. +Comment[et]=Kataloogi lisamine olemasolevasse versioonikontrolli süsteemi hoidlasse. +Comment[eu]=Jarri karpeta biltegi batean errebisio kontrolpean edukitzeko. +Comment[fa]=گذاشتن پوشه در مخزن موجود جهت قراردادن آن تحت کنترل بازبینی. +Comment[fi]=Laita kansio versionhallintaan viemällä se olemassa olevaan versionhallintavarastoon. +Comment[fr]=Place le dossier dans un référentiel existant afin de le mettre sous contrôle de version. +Comment[gl]=Pon o cartafol nun repositorio existente para pólo baixo control de versións. +Comment[hu]=Könyvtár felvétele a verziókövető rendszer felügyelete alá. +Comment[is]=Setja möppu í geymslu sem finnst fyrir til að setja hana undir breytingarstjórn. +Comment[it]=Metti una cartella in un deposito esistente per metterla sotto controllo di revisione. +Comment[ja]=既存のリポジトリにフォルダを置き、リビジョン管理の対象とします。 +Comment[ka]=რევიზიის კონტროლისთვის ჩადეთ საქაღალდე არსებულ რეპოზიტორიაში. +Comment[kk]=Қапшықты қоймасына, нұсқалар есебін қадағалап, көшіру. +Comment[lt]=Įdėti aplanką į egzistuojančią saugyklą ir įjungti jį į keitimų sekimo sistemą. +Comment[nb]=Legg mappe inn i et eksisterende arkiv slik at det får revisjonskontroll. +Comment[nds]=Verschufft en Orner na en vörhannen Archiv, so dat he ünner Verschoonkuntrull kummt +Comment[ne]=फोल्डलाई पुनरावोलकन नियन्त्रण गर्न अवस्थित भण्डारमा राख्नुहोस् । +Comment[nl]=Plaats een map in een bestaande repository zodat het onder het versiebeheer system valt. +Comment[nn]=Legg ei mappe inn i eit eksisterande lager slik at det får revisjonskontroll. +Comment[pl]=Dodaje katalog do istniejącego repozytorium, aby umieścić go w systemie kontrolowania wersji. +Comment[pt]=Coloca a pasta num repositório existente para a colocar sob controlo de versões. +Comment[pt_BR]=Coloca a pasta num repositório existente para colocá-lo sob controle de versões. +Comment[ru]=Поместить папку в существующее хранилище для добавления этой папки в систему контроля ревизий +Comment[sk]=Vloží priečinok do existujúceho archívu ako novú revíziu. +Comment[sl]=Uvozi mapo v obstoječe skladišče. Mapa tako postane del revizijskega nadzora. +Comment[sr]=Стави фасциклу у постојеће складиште, ради стављања под контролу ревизија. +Comment[sr@Latn]=Stavi fasciklu u postojeće skladište, radi stavljanja pod kontrolu revizija. +Comment[sv]=Lägg till katalog i ett befintligt arkiv för att få den under versionskontroll +Comment[tr]=Dizini başka bie alt düzeltme controlünde var olan bir depo içine koy. +Comment[uk]=Вставити теку в існуюче сховище, щоб уможливити для неї керування версіями. +Comment[zh_CN]=将文件夹放入现有仓库,以便让其受到版本控制。 +Comment[zh_TW]=將資料夾放進現存的主目錄,並開始做版本控制。 + +[Desktop Action Checkout] +Name=Checkout From Repository... +Name[bg]=Изтегляне от хранилището... +Name[ca]=Obtenir del repositori... +Name[cs]=Získat z repository... +Name[da]=Tjek ud fra lager... +Name[de]=Aus SVN-Archiv herausholen ... +Name[el]=Έλεγχος εξόδου από το χώρο αποθήκευσης... +Name[eo]=Ekpreni el Deponejo... +Name[es]=Obtener del repositorio... +Name[et]=Hoidla väljavõte... +Name[eu]=Deskargatu biltegitik... +Name[fa]=وارسی از مخزن... +Name[fi]=Nouda versionhallinnasta... +Name[fr]=Extraire depuis un référentiel... +Name[gl]=Obter do repositorio... +Name[hu]=Kimásolás az adattárból... +Name[is]=Ná í frá geymslu... +Name[it]=Ritira dal deposito... +Name[ja]=リポジトリからチェックアウト... +Name[ka]=რეპოზიტორიიდან ამონიშვნა... +Name[kk]=Қоймасынан көшіріп алу... +Name[lt]=Atsisiųsti iš saugyklos... +Name[nb]=Sjekk ut fra lager … +Name[nds]=Ut Archiv daalladen... +Name[ne]=भण्डारबाट जाँच... +Name[nl]=Repository uitchecken... +Name[nn]=Sjekk ut frå lager … +Name[pa]=ਰਿਪੋਜ਼ਟਰੀ ਤੋਂ ਚੈਕਆਉਟ... +Name[pl]=Pobierz z repozytorium... +Name[pt]=Obter do Repositório... +Name[pt_BR]=Obter do Repositório... +Name[ru]=Загрузить из хранилища... +Name[sk]=Získať z archívu... +Name[sl]=Prevzemi iz skladišča ... +Name[sr]=Довуци из складишта... +Name[sr@Latn]=Dovuci iz skladišta... +Name[sv]=Checka ut från ett arkiv... +Name[tr]=Depodan Kontrol Et... +Name[uk]=Взяти зі сховища... +Name[zh_CN]=从仓库中检出... +Name[zh_TW]=從主目錄取出... +Icon=svn_checkout +Exec=kio_svn_helper -C %U +Comment=Checkout out files from an existing repository into this folder. +Comment[bg]=Изтегляне на файлове от хранилището в текущата директория. +Comment[ca]=Obté fitxers des d'un repositori existent cap aquesta carpeta. +Comment[cs]=Získat soubory z existující repository do této složky. +Comment[da]=Tjek filer ud fra et eksisterende lager til denne mappe. +Comment[de]=Legt Dateien aus einem existierenden SVN-Archiv in diesem Ordner ab. +Comment[el]=΄Έλεγχος εξόδου των αρχείων από έναν υπάρχον χώρο αποθήκευσης σε αυτόν τον φάκελο. +Comment[es]=Descargar archivos de un repositorio existente en esta carpeta. +Comment[et]=Olemasoleva hoidla failide väljavõte sellesse kataloogi. +Comment[eu]=Deskargatu fitxategiak biltegi batetik karpeta honetara. +Comment[fa]=وارسی پرونده‌های بیرونی از مخزن موجود در این پوشه +Comment[fi]=Nouda tiedostot olemassa olevasta versionhallinnasta tähän kansioon. +Comment[fr]=Extraire dans ce dossier les fichiers d'un référentiel existant +Comment[gl]=Obtén todos os ficheiros dun repositorio existente para este cartafol. +Comment[hu]=Fájlok kimásolása az adattárból ebbe a könyvtárba. +Comment[is]=Ná í skrár frá geymslu og setja í þessa möppu. +Comment[it]=Ritira i file da un deposito esistente in questa cartella. +Comment[ja]=既存のリポジトリから、このディレクトリへファイルをチェックアウトします。 +Comment[ka]=არსებული რეპოზიტორიიდან ფაილები ამ საქაღალდეში ამონიშნეთ. +Comment[kk]=Файлдарды қоймасынан көрсетілген қапшыққа көшіріп алу. +Comment[lt]=Atsisiųsti bylas iš egzistuojančios saugyklos į šį aplanką. +Comment[nb]=Sjekk ut filer fra et arkiv inn i denne mappa +Comment[nds]=Laadt Dateien ut en vörhannen Archiv na dissen Orner daal. +Comment[ne]=यो फोल्डरमा अवस्थित भण्डार बाहिरका फाइललाई जाँच गर्नुहोस् । +Comment[nl]=Bestanden van een bestaande repository uitchecken in deze map. +Comment[nn]=Sjekk ut filer frå eit arkiv inn i denne mappa. +Comment[pl]=Pobranie plików z istniejącego repozytorium do tego katalogu. +Comment[pt]=Obtém todos os ficheiros de um repositório existente para esta pasta. +Comment[pt_BR]=Obtém todos os arquivos de um repositório existente para esta pasta. +Comment[ru]=Загрузить файлы из существующего хранилища в указанную папку +Comment[sk]=Získa súbory z existujúceho archívu do tohoto priečinku. +Comment[sl]=Prevzemi datoteke iz obstoječega skladišča v to mapo. +Comment[sr]=Довуци фајлове из постојећег складишта у ову фасциклу. +Comment[sr@Latn]=Dovuci fajlove iz postojećeg skladišta u ovu fasciklu. +Comment[sv]=Checka ut filer från ett befintligt arkiv till katalogen. +Comment[tr]=Bu dizinde var olan bir depodan hatalı dosyaları kontrol et. +Comment[uk]=Взяти файли з існуючого сховища і покласти в цю теку. +Comment[zh_CN]=从已有仓库中检出文件并存放至此文件夹。 +Comment[zh_TW]=從現存的主目錄取出檔案到此資料夾。 + +[Desktop Action Switch] +Name=Switch... +Name[bg]=Превключване... +Name[br]=Gwintañ ... +Name[ca]=Canvia... +Name[cs]=Přepnout... +Name[de]=Wechseln (switch) +Name[el]=Εναλλαγή... +Name[eo]=Ŝalti... +Name[es]=Cambiar... +Name[et]=Lülitumine... +Name[eu]=Aldatu... +Name[fa]=سودهی... +Name[fi]=Vaihda... +Name[fr]=Basculer... +Name[gl]=Mudar... +Name[he]=החלף... +Name[hu]=Váltás... +Name[is]=Skipta... +Name[it]=Passa... +Name[ja]=スイッチ... +Name[ka]=გადრთვა... +Name[kk]=Ауысу... +Name[lt]=Perjungti... +Name[nb]=Bytt … +Name[nds]=Telg wesseln... +Name[ne]=स्विच... +Name[nl]=Omzetten (switch)... +Name[nn]=Byt … +Name[pa]=ਤਬਦੀਲ... +Name[pl]=Przełącz... +Name[pt]=Mudar... +Name[pt_BR]=Mudar... +Name[ru]=Сменить адрес хранилища... +Name[sk]=Vymeniť... +Name[sl]=Preklopi ... +Name[sr]=Пребаци... +Name[sr@Latn]=Prebaci... +Name[sv]=Byt... +Name[tr]=Değiştir... +Name[uk]=Перемкнути... +Name[zh_CN]=切换... +Name[zh_TW]=切換... +Icon=svn_switch +Comment=Switch given working copy to another branch +Comment[bg]=Превключване на работното копие към друго разклонение. +Comment[ca]=Canvia una còpia de treball indicada a una altra branca +Comment[cs]=Přepnout danou pracovní kopii na jinou větev +Comment[da]=Skift given arbejdskopi til en anden gren. +Comment[de]=Wechselt von der vorhandenen Arbeitskopie zu einer anderen Verzweigung (branch). +Comment[el]=Εναλλαγή του δοσμένου αντιγράφου εργασίας σε άλλον κλάδο +Comment[es]=Cambiar una determinada copia de trabajo a otra rama +Comment[et]=Antud töökoopia lülitamine teise harru +Comment[eu]=aldatu laneko kopia bat beste adar batekin +Comment[fa]=سودهی رونوشت کار داده‌شده به شاخۀ دیگر +Comment[fi]=Vaihda annettu työkopio toiseen haaraan +Comment[fr]=Basculer la copie de travail vers une autre branche +Comment[gl]=Muda a copia de traballo actual para outra ramificación +Comment[hu]=A munkamásolat átváltása másik ágra +Comment[is]=Skipta núverandi vinnuafriti yfir í aðra grein +Comment[it]=Passa la copia di lavoro a un altro ramo +Comment[ja]=作業中のコピーを他のブランチへ切り換えます。 +Comment[ka]=მიმდინარე სამუშაო ასლის სხვა ტოტზე გაკეთება +Comment[kk]=Жұмыс көшірмені жобаның басқа саласына (яғни қоймасына) ауыстыру +Comment[lt]=Perjungti esamą vietinį saugyklos aplanką į kitą atšaką +Comment[nb]=Byt arbeidskopien til en annen gren +Comment[nds]=Arbeitkopie na en annern Telg verschuven +Comment[ne]=दिइएको कार्य प्रतिलाई अन्य शाखामा स्विच गर्नुहोस् +Comment[nl]=Zet een bestaande copy om naar een andere branch +Comment[nn]=Byt arbeidskopien til ei anna grein +Comment[pl]=Przełącza katalog roboczy na inną gałąź +Comment[pt]=Muda a cópia de trabalho actual para outra ramificação +Comment[pt_BR]=Muda a cópia de trabalho atual para outra ramificação +Comment[ru]=Сменить адрес хранилища, например перейти от стабильной ветки к ветке разработки +Comment[sk]=Vymení danú pracovnú kópiu zo inú vetvu +Comment[sl]=Preklopi dano delovno kopijo na drugo vejo +Comment[sr]=Пребаци дату радну копију на другу грану +Comment[sr@Latn]=Prebaci datu radnu kopiju na drugu granu +Comment[sv]=Byt angiven arbetskopia till en annan gren +Comment[tr]=Çalışan belirli bir kopyayı başka bölüme değiştir +Comment[uk]=Перемкнути поточну робочу копію на іншу гілку +Comment[zh_CN]=将工作副本切换到另外一个分支 +Comment[zh_TW]=將指定的工作複本切換到另一個分支 +Exec=kio_svn_helper -s %U + +[Desktop Action Merge] +Name=Merge... +Name[bg]=Смесване... +Name[br]=D&astum ... +Name[ca]=Fusiona... +Name[cs]=Sloučit... +Name[da]=Indflet... +Name[de]=Zusammenführen ... +Name[el]=Συγχώνευση... +Name[eo]=Kunfandi... +Name[es]=Mezclar... +Name[et]=Ühendamine... +Name[eu]=Bateratu... +Name[fa]=ادغام... +Name[fi]=Yhdistä... +Name[fr]=Fusionner... +Name[ga]=Cumaisc... +Name[gl]=Fusionar... +Name[he]=מזג... +Name[hu]=Összeolvasztás... +Name[is]=Bræða saman... +Name[it]=Fondi... +Name[ja]=マージ... +Name[ka]=შერწყმა... +Name[kk]=Біріктіру... +Name[lt]=Sulieti... +Name[nb]=Flett … +Name[nds]=Tosamenföhren... +Name[ne]=गाभिनु... +Name[nl]=Samenvoegen... +Name[nn]=Flett … +Name[pa]=ਮਿਲਾਨ... +Name[pl]=Połącz... +Name[pt]=Reunir... +Name[pt_BR]=Mesclar... +Name[ru]=Объединить... +Name[sk]=Spojiť... +Name[sl]=Združi ... +Name[sr]=Стопи... +Name[sr@Latn]=Stopi... +Name[sv]=Sammanfoga... +Name[tr]=Birleştir... +Name[uk]=Об'єднати... +Name[zh_CN]=合并... +Name[zh_TW]=合併... +Icon=svn_merge +Comment=Merge changes between this and another branch +Comment[bg]=Смесване на промените от това разклонение с друго разклонение. +Comment[ca]=Fusiona els canvis entre aquesta i una altra branca +Comment[cs]=Sloučit změny mezi touto a jinou větví +Comment[da]=Indflet ændringer mellem denne og en anden gren +Comment[de]=Führt Änderungen aus dieser und einer anderen Verzweigung zusammen +Comment[el]=Συγχώνευση αλλαγών μεταξύ του τρέχοντος και κάποιου άλλου κλάδου +Comment[es]=Mezclar los cambios entre esta y otra rama +Comment[et]=Selle ja teise haru muudatuste ühendamine +Comment[eu]=Bateratu hau eta beste adar baten arteko aldaketak +Comment[fa]=تغییرات بین این شاخه و شاخۀ دیگر را ادغام می‌کند +Comment[fi]=Yhdistä tämän ja toisen haaran väliset muutokset +Comment[fr]=Fusionner les modifications entre cette branche et une autre +Comment[gl]=Fusiona as modificacións entre esta ramificación e outra +Comment[hu]=A módosítások összefésülése egy másik ággal +Comment[is]=Bræða saman breytingar milli þessarar og annarar greinar +Comment[it]=Fondi i cambiamenti tra questo e un altro ramo +Comment[ja]=このブランチと他のブランチの間で、変更をマージします。 +Comment[ka]=ამ და სხვა ტოტების ცვლილებების შერწყმა +Comment[kk]=Осы және өзге салалардағы өзгерістерін біріктіру +Comment[lt]=Sulieti pakeitimus tarp šios ir kitos atšakos +Comment[nb]=Flett sammen endringer mellom denne og en annen gren +Comment[nds]=Ännern twischen dissen un en annern Telg tosamenföhren +Comment[ne]=यो र अन्य शाखा बीचका परिवर्तन गाभ्नुहोस् +Comment[nl]=Deze en een andere tak samenvoegen +Comment[nn]=Flett saman endringar mellom denne og ei anna grein +Comment[pl]=Łączy zmiany między tą i inną gałęzią +Comment[pt]=Junta as modificações entre esta ramificação e outra +Comment[pt_BR]=Mescla as modificações entre esta ramificação e outra +Comment[ru]=Объеденить изменения между этой и другой ветками +Comment[sk]=Spojí zmeny medzi touto a inou vetvou +Comment[sl]=Združi spremembe med to in drugo vejo +Comment[sr]=Стопи измене између ове и друге гране +Comment[sr@Latn]=Stopi izmene između ove i druge grane +Comment[sv]=Sammanfoga ändringar mellan den här och en annan gren +Comment[tr]=Bu ve başka bölüm arasındaki değişiklikleri birleştir +Comment[uk]=Об'єднати зміни в цій та іншій гілках +Comment[zh_CN]=合并本地和另外一个分支的更改 +Comment[zh_TW]=將這個與另一個分支合併 +Exec=kio_svn_helper -m %U + +[Desktop Action Blame] +Name=Blame... +Name[bg]=Информация... +Name[ca]=Responsabilitza... +Name[cs]=Obvinit... +Name[de]=Blame ... +Name[el]=Συσχέτιση... +Name[eo]=Kulpigi... +Name[es]=Responsabilizar... +Name[et]=Autorsus... +Name[eu]=Erruduna... +Name[fa]=...سرزنش کردن +Name[fr]=Blâmer... +Name[gl]=Autorías... +Name[he]=האשם... +Name[hu]=Ki tette ezt... +Name[is]=Kenna um... +Name[it]=Traccia... +Name[ja]=ブレイム... +Name[ka]=ბრალი... +Name[kk]=Кім екен... +Name[lt]=Nustatyti... +Name[nb]=Skyld på … +Name[nds]=Naspören... +Name[ne]=दोष... +Name[nl]=Annotatie... +Name[nn]=Skuld på … +Name[pa]=ਬਲਾਮੀ... +Name[pl]=Obwiń... +Name[pt]=Culpar... +Name[pt_BR]=Culpar... +Name[ru]=Определить авторов... +Name[sk]=Žalovať... +Name[sl]=Odgovornost ... +Name[sr]=Окриви... +Name[sr@Latn]=Okrivi... +Name[sv]=Klandra... +Name[uk]=Вина... +Name[zh_CN]=历史... +Name[zh_TW]=最後狀態註記... +Icon=svn_blame +Comment=See who wrote each line of the file and in what revision +Comment[bg]=Информация за това кой е написал файла и в коя версия +Comment[ca]=Veu qui va escriure cada línia del fitxer i en qui l'ha revisat +Comment[cs]=Zobrazit, kdo napsal kterou řádku souboru spolu s revizí +Comment[da]=Se hvem der skrev hver linje i filen og i hvilken revision +Comment[de]=Zeigt an, wer die Zeilen einer Datei wann geändert hat. +Comment[el]=Δείτε ποιος έγραψε κάθε γραμμή του αρχείου και σε ποια αναθεώρηση +Comment[es]=Ver quién escribió cada línea del archivo y en qué revisión +Comment[et]=Vaatamine, kes ja millises versioonis mingi faili rea kirjutas +Comment[eu]=Ikusi nork idatzi duen lerro bakoitza eta zer errebisiotan +Comment[fa]=ببینید چه کسی هر خط پرونده را و در چه بازبینی نوشته است +Comment[fi]=Tarkista kuka on kirjoittanut tiedoston rivit missäkin versiossa +Comment[fr]=Voir qui a écrit chacune des lignes du fichier, et dans quelle version. +Comment[gl]=Consulta quen escribiu unha liña dada no ficheiro e en que versión +Comment[hu]=A fájlok készítőinek megtekintése soronként, verzió szerint +Comment[is]=Sjá hver skrifaði hvaða línu og í hvaða útgáfu +Comment[it]=Vedi chi ha scritto ogni riga del file e in quale revisione +Comment[ja]=誰がどのリビジョンの、どのファイルの、どの行を書いたのかを見ます。 +Comment[ka]=ნახეთ თუ ვინ ჩაწერა ფაილის ყოველი ხაზი და რომელ რევიზიაში +Comment[kk]=Файлдағы жолды кім және қай нұсқасында жазғанын білу +Comment[lt]=Patikrinti, kas parašė kiekvieną eilutę ir kurio keitimo metu tai buvo atlikta +Comment[nb]=Se hvem som skrev hver linje i fila og i hvilken revisjon +Comment[nds]=Wiest, wokeen wannehr welke Dateiregen ännert hett +Comment[ne]=प्रत्येक फाइल कसले लेखेको छ र के दोहोरिएको छ हेर्नुहोस् +Comment[nl]=Bekijk wie welke regel van het bestand geschreven heeft en in welke revisie +Comment[nn]=Sjå kven som skreiv kvar linje i fila og i kva revisjon +Comment[pl]=Pokazuje, kto ostatni zmienił każdą linię w pliku i w której wersji +Comment[pt]=Vê quem escreveu uma dada linha no ficheiro e em que versão +Comment[pt_BR]=Vê quem escreveu uma determinada linha no arquivo e em que versão +Comment[ru]=Просмотреть авторов каждой строки в файле и в выбранной ревизии +Comment[sk]=Pozrite sa, kto a v ktorej revízii napísal každý riadok súboru +Comment[sl]=Prikaži, kdo je napisal katero vrstico in v kateri reviziji +Comment[sr]=Прикажи за сваку линију фајла ко ју је написао и у којој ревизији +Comment[sr@Latn]=Prikaži za svaku liniju fajla ko ju je napisao i u kojoj reviziji +Comment[sv]=Se vem som skrev varje rad i filen och för vilken version +Comment[uk]=Подивитись хто написав кожний рядок файла і в якій модифікації +Comment[zh_CN]=查看谁在哪次修订中写了文件的哪一行 +Comment[zh_TW]=看檔案中的每一行是誰寫的,其版本為何 +Exec=kio_svn_helper -b %U + +[Desktop Action CreatePatch] +Name=Create Patch... +Name[bg]=Създаване на кръпка... +Name[ca]=Crea pedaç... +Name[cs]=Vytvořit záplatu... +Name[da]=Lav rettelse... +Name[de]=Patch erstellen ... +Name[el]=Δημιουργία διόρθωσης... +Name[eo]=Krei Flikon... +Name[es]=Crear parche... +Name[et]=Paiga loomine... +Name[eu]=Sortu adabakia... +Name[fa]=ایجاد کژنه... +Name[fi]=Luo korjaus... +Name[fr]=Créer un correctif... +Name[ga]=Cruthaigh Paiste... +Name[gl]=Criar un parche... +Name[he]=צור טלאי... +Name[hu]=Folt készítése... +Name[is]=Búa til plástur... +Name[it]=Crea correzione... +Name[ja]=パッチの作成... +Name[ka]=ბებკის შექმნა... +Name[kk]=Жамауды құру... +Name[lt]=Kurti pataisymą... +Name[nb]=Lag lapp … +Name[nds]=Kodeplaster opstellen... +Name[ne]=प्याच सिर्जना... +Name[nl]=Patch aanmaken... +Name[nn]=Lag lapp … +Name[pa]=ਪੈਂਚ ਬਣਾਓ... +Name[pl]=Stwórz łatę... +Name[pt]=Criar um 'Patch'... +Name[pt_BR]=Criar um 'Patch'... +Name[ru]=Создать заплатку... +Name[sk]=Vytvoriť záplatu... +Name[sl]=Ustvari popravek ... +Name[sr]=Направи закрпу... +Name[sr@Latn]=Napravi zakrpu... +Name[sv]=Skapa programfix... +Name[tr]=Yama Oluştur... +Name[uk]=Створити латку... +Name[zh_CN]=创建补丁... +Name[zh_TW]=建立修補檔... +Exec=kio_svn_helper -p %U + +[Desktop Action Export] +Name=Export... +Name[bg]=Експортиране... +Name[br]=Ezporzh ... +Name[ca]=Exporta... +Name[cs]=Exportovat... +Name[cy]=Allforio... +Name[da]=Eksportér... +Name[de]=Exportieren ... +Name[el]=Εξαγωγή... +Name[eo]=Eksporti... +Name[es]=Exportar... +Name[et]=Eksport... +Name[eu]=Esportatu... +Name[fa]=صادرات... +Name[fi]=Vie... +Name[fr]=Exporter... +Name[ga]=Easpórtáil... +Name[gl]=Exportar... +Name[he]=ייצא.... +Name[hu]=Exportálás... +Name[is]=Flytja út... +Name[it]=Esporta... +Name[ja]=エクスポート... +Name[ka]=ექსპორტი... +Name[kk]=Экспорттау... +Name[lt]=Eksportuoti... +Name[nb]=Eksporter … +Name[nds]=Exporteren... +Name[ne]=निर्यात... +Name[nl]=Exporteren... +Name[nn]=Eksporter … +Name[pa]=ਨਿਰਯਾਤ... +Name[pl]=Eksportuj... +Name[pt]=Exportar... +Name[pt_BR]=Exportar... +Name[ru]=Экспорт... +Name[sk]=Exportovať... +Name[sl]=Izvozi ... +Name[sr]=Извези... +Name[sr@Latn]=Izvezi... +Name[sv]=Exportera... +Name[tr]=Dışa Aktar... +Name[uk]=Експортувати... +Name[zh_CN]=导出... +Name[zh_TW]=匯出... +Icon=svn_export +Exec=kio_svn_helper -e %U +Comment=Checkout out an unversioned copy of a tree from a repository +Comment[bg]=Изтегляне на копие на дървото от хранилището. +Comment[ca]=Exporta una còpia sense versió d'un arbre del repositori +Comment[cs]=Získat z repository kopii stromu bez verze +Comment[da]=Tjek en kopi uden version ud af et træ fra et lager +Comment[de]=Herausholen eines Baums aus dem SVN-Archiv ohne Versionsinformationen +Comment[el]=Έλεγχος εξόδου ενός αντιγράφου χωρίς έκδοση από ένα δέντρο του χώρου αποθήκευσης +Comment[es]=Exporta una copia no versionada de un árbol de un repositorio +Comment[et]=Hoidla failipuu versioonita koopia väljavõte +Comment[eu]=Deskargatu bertsio-gabeko zuhaitz baten kopia bat biltegitik +Comment[fa]=وارسی یک رونوشت کلی درخت از مخزن +Comment[fi]=Nouda versioimaton kopio versionhallinnan puusta +Comment[fr]=Extraire une copie sans contrôle de version d'une arborescence depuis un référentiel +Comment[gl]=Obtén unha copia sen control de versións dunha árbore do repositorio +Comment[hu]=Verzió nélküli másolat készítése az adattárból +Comment[is]=Ná í afrit af tré án útgáfunúmers frá geymslu +Comment[it]=Ritira una copia senza versione di un albero da un deposito +Comment[ja]=リポジトリから非バージョン管理ツリーとしてチェックアウトします。 +Comment[ka]=რეპოზიტტორიიდან ხის უვერსიო ასლის შემოწმება +Comment[kk]=Жоба бұтағын қоймасынан, нұсқалар есебін қадағалауынан тыс көшіріп алу +Comment[lt]=Atsisiųsti bylas iš egzistuojančios saugyklos be keitimų sekimo. +Comment[nb]=Sjekk ut en kopi av et tre uten versjon fra et lager +Comment[nds]=En Boomkopie ahn Verschooninformatschoon ut en Archiv daalladen +Comment[ne]=भण्डारबाट संस्करण ननिकालिएको ट्री बाहिरको प्रति जाँच गर्नुहोस् +Comment[nl]=Een niet onder versiebeheer vallende kopie uitchecken van een tak uit de repository +Comment[nn]=Sjekk ut ein kopi av eit tre utan versjon frå eit lager +Comment[pl]=Pobiera kopię drzewa z repozytorium bez informacji do kontroli wersji +Comment[pt]=Obtém uma cópia sem controlo de versões de uma árvore do repositório +Comment[pt_BR]=Obtém uma cópia sem controle de versões de uma árvore do repositório +Comment[ru]=Загрузить копию дерева без служебной информации системы управления версиями +Comment[sk]=Získa neverzionovanú kópiu stromu z archívu +Comment[sl]=Iz skladišča prevzemi kopijo drevesa brez različice +Comment[sr]=Довуци неверзирану копију стабла из складишта +Comment[sr@Latn]=Dovuci neverziranu kopiju stabla iz skladišta +Comment[sv]=Checka ut en kopia utan versionskontroll från ett arkiv +Comment[tr]=Depodaki bir ağaçtan kusurlu bir versionsuz kopyayı kontrol et +Comment[uk]=Взяти зі сховища копію дерева без версій +Comment[zh_CN]=从仓库中检出无版本副本 +Comment[zh_TW]=從主目錄中取出不含版本資訊的工作複本 + +[Desktop Action Diff] +Name=Diff (local) +Name[bg]=Разлика (локално) +Name[cs]=Rozdíl (místní) +Name[da]=Diff (lokal) +Name[de]=Diff (lokal) +Name[el]=Διαφορές (τοπικά) +Name[es]=Diferencias (locales) +Name[et]=Võrdlemine (kohalik) +Name[eu]=Desberdintasunak (lokala) +Name[fa]=Diff (محلی) +Name[fi]=Diff (paikallinen) +Name[fr]=Différences (locales) +Name[ga]=Diff (logánta) +Name[gl]=Diferenzas (local) +Name[he]=Diff (מקומי) +Name[hu]=Diff (helyi) +Name[is]=Bera saman (staðbundið) +Name[it]=Differenza (locale) +Name[ja]=Diff (ローカル) +Name[ka]=Diff (ლოკალური) +Name[kk]=Жергілікті өзгерістер +Name[lt]=Diff (vietinis) +Name[ms]=Diff (tempatan) +Name[nb]=Diff (lokal) +Name[nds]=Verscheel (lokaal) +Name[ne]=Diff (स्थानीय) +Name[nl]=Diff (lokaal) +Name[nn]=Diff (lokal) +Name[pa]=ਅੰਤਰ (ਸਥਾਨਕ) +Name[pl]=Różnice (lokalne) +Name[pt]=Diferenças (local) +Name[pt_BR]=Diferenças (local) +Name[ru]=Локальные изменения +Name[sk]=Rozdiel (lokálne) +Name[sl]=Diff (krajevno) +Name[sr]=Разликуј (локално) +Name[sr@Latn]=Razlikuj (lokalno) +Name[sv]=Jämför (lokalt) +Name[tr]=Diff (yerel) +Name[uk]=Розбіжності (локальні) +Name[zh_CN]=Diff (本地) +Name[zh_TW]=比較(本地端) +Icon=svn_diff +Exec=kio_svn_helper -D %U +Comment=Show local changes since last update +Comment[bg]=Показване на локалните промени след последното обновяване. +Comment[ca]=Mostra els canvis locals des de l'última actualització +Comment[cs]=Zobrazit lokální změny od poslední aktualizace +Comment[da]=Vis lokale ændringer siden sidste opdatering +Comment[de]=Zeigt die lokal durchgeführten Änderungen seit der letzten Aktualisierung +Comment[el]=Εμφάνιση τοπικών αλλαγών από την τελευταία ενημέρωση +Comment[es]=Mostrar los cambios locales desde la última actualización +Comment[et]=Kohalike muudatuste näitamine pärast viimast uuendamist +Comment[eu]=Erakutsi azken aldaketatik gertatu diren aldaketa lokalak +Comment[fa]=نمایش تغییرات محلی از آخرین به‌روزرسانی +Comment[fi]=Näytä viimeisen päivityksen jälkeen tehdyt paikalliset muutokset +Comment[fr]=Afficher les changements locaux effectués depuis la dernière mise à jour +Comment[gl]=Mostra as modificacións locais desde a última actualización +Comment[hu]=A helyi módosítások mutatása (az utolsó frissítés óta) +Comment[is]=Sýna staðbundnar breytingar frá seinustu uppfærslu +Comment[it]=Mostra i cambiamenti locali dall'ultimo aggiornamento +Comment[ja]=最終アップデートからローカルにどのような変更があったのかを表示します。 +Comment[ka]=უკანასკნელი განახლების შემდეგ ლოკალური ცვლილებების ჩვენება +Comment[kk]=Қоймасына әлі тапсырылмаған жергілікті өзгерістерді көрсету +Comment[lt]=Rodyti vietinius pakeitimus nuo paskutinio atnaujinimo +Comment[nb]=Vis lokale endringer siden siste oppdatering +Comment[nds]=Lokaal Ännern na de verleden Opfrischen wiesen +Comment[ne]=अन्तिम अद्यावधिक पछिका स्थानीय परिवर्तन देखाउनुहोस् +Comment[nl]=Lokale wijzigingen sinds de laatste update tonen +Comment[nn]=Vis lokale endringar sidan siste oppdatering +Comment[pl]=Pokazuje lokalne zmiany od ostatniego uaktualnienia +Comment[pt]=Mostra as modificações locais desde a última actualização +Comment[pt_BR]=Mostra as modificações locais desde a última atualização +Comment[ru]=Определить изменения, не внесенные в общее хранилище +Comment[sk]=Zobrazí lokálne zmeny od poslednej aktualizácie +Comment[sl]=Prikaži krajevne spremembe od zadnje posodobitve +Comment[sr]=Прикажи локалне измене од последњег ажурирања +Comment[sr@Latn]=Prikaži lokalne izmene od poslednjeg ažuriranja +Comment[sv]=Visa lokala ändringar sedan senaste uppdateringen +Comment[tr]=Son güncellemeden sonraki yerel değişiklikleri göster +Comment[uk]=Показати локальні зміни з часу останнього оновлення +Comment[zh_CN]=显示上次更新后本地的更改 +Comment[zh_TW]=顯示最後一次更新之後於本地端所做的改變 + diff --git a/kioslave/svn/svnhelper/subversion_toplevel.desktop b/kioslave/svn/svnhelper/subversion_toplevel.desktop new file mode 100644 index 00000000..bf38f02c --- /dev/null +++ b/kioslave/svn/svnhelper/subversion_toplevel.desktop @@ -0,0 +1,97 @@ +[Desktop Entry] +ServiceTypes=inode/directory,all/all +X-KDE-Priority=TopLevel + +X-KDE-GetActionMenu=kded ksvnd getTopLevelActionMenu(KURL::List) + +[Desktop Action Update] +Name=SVN Update +Name[bg]=Обновяване SVN +Name[ca]=Actualitza SVN +Name[cs]=SVN update +Name[da]=SVN Opdatér +Name[de]=Aktualisieren (SVN) +Name[el]=Ενημέρωση SVN +Name[es]=Actualizar SVN +Name[et]=SVN uuendamine +Name[eu]=SVN eguneratu +Name[fa]=به‌روزرسانی SVN +Name[fi]=SVN-päivitys (Update) +Name[fr]=Mise à jour SVN +Name[gl]=Actualización SVN +Name[he]=עדכון SVN +Name[hu]=SVN frissítés +Name[is]=Uppfæra SVN +Name[it]=Aggiornamento SVN +Name[ja]=SVN アップデート +Name[ka]=SVN განახლება +Name[kk]=SVN жаңарту +Name[lt]=SVN atnaujinti +Name[ms]=Kemaskini SVN +Name[nb]=SVN oppdater +Name[nds]=SVN-Archiv opfrischen +Name[ne]=एसभीएन अद्यावधिक +Name[nl]=SVN bijwerken +Name[nn]=SVN oppdater +Name[pa]=SVN ਅੱਪਡੇਟ +Name[pl]=Uaktualnij +Name[pt]=Actualização do SVN +Name[pt_BR]=Atualização a partir do SVN +Name[ru]=Обновить +Name[sk]=SVN aktualizácia +Name[sl]=Posodobitev SVN +Name[sr]=SVN ажурирање +Name[sr@Latn]=SVN ažuriranje +Name[sv]=SVN-uppdatera +Name[tr]=SVN Güncelleme +Name[uk]=SVN-оновлення +Name[zh_CN]=SVN 更新 +Name[zh_TW]=SVN 更新 +Icon=redo +Exec=kio_svn_helper -u %U + +[Desktop Action Commit] +Name=SVN Commit +Name[bg]=Изпращане SVN +Name[ca]=Entrega SVN +Name[cs]=SVN commit +Name[de]=Einspielen (SVN) +Name[el]=Καταχώρηση SVN +Name[es]=Entrega SVN +Name[et]=SVN sissekanne +Name[eu]=SVN entregatu +Name[fa]=تصدیق SVN +Name[fi]=SVN-toimitus (Commit) +Name[fr]=Validation SVN +Name[ga]=SVN Cur i bhFeidhm +Name[gl]=Entrega SVN +Name[he]=שליחת שינויים של ה־SVN +Name[hu]=SVN eltárolás +Name[is]=Setja inn í SVN +Name[it]=Deposito SVN +Name[ja]=SVN コミット +Name[ka]=SVN შესრულება +Name[kk]=SVN тапсыру +Name[lt]=SVN išsiųsti +Name[nb]=SVN meld inn +Name[nds]=Na SVN-Archiv inspelen +Name[ne]=एसभीएन कमिट +Name[nl]=SVN vastleggen +Name[nn]=SVN meld inn +Name[pa]=SVN ਕਮਿਟ +Name[pl]=Wyślij +Name[pt]=Envio do SVN +Name[pt_BR]=Envio para o SVN +Name[ru]=Опубликовать +Name[sk]=SVN potvrdenie +Name[sl]=Udejanjanje SVN +Name[sr]=SVN предаја +Name[sr@Latn]=SVN predaja +Name[sv]=SVN-arkivera +Name[tr]=SVN Teslim Etme +Name[uk]=SVN-передання +Name[zh_CN]=SVN 提交 +Name[zh_TW]=SVN 提交 +Icon=undo +Exec=kio_svn_helper -c %U + diff --git a/kioslave/svn/svnhelper/subversioncheckout.ui b/kioslave/svn/svnhelper/subversioncheckout.ui new file mode 100644 index 00000000..5b320eee --- /dev/null +++ b/kioslave/svn/svnhelper/subversioncheckout.ui @@ -0,0 +1,204 @@ + +SubversionCheckout + + + SubversionCheckout + + + + 0 + 0 + 498 + 133 + + + + + 1 + 1 + 0 + 0 + + + + Subversion Checkout + + + true + + + + unnamed + + + + layout8 + + + + unnamed + + + + Layout1 + + + + unnamed + + + 0 + + + 6 + + + + buttonHelp + + + &Help + + + F1 + + + true + + + + + Horizontal Spacing2 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + buttonOk + + + &OK + + + + + + true + + + true + + + + + buttonCancel + + + &Cancel + + + + + + true + + + + + + + url + + + + 7 + 5 + 0 + 0 + + + + + + layout4 + + + + unnamed + + + + textLabel1 + + + Revision (0 for HEAD): + + + + + spacer2 + + + Horizontal + + + Expanding + + + + 121 + 20 + + + + + + revision + + + + 3 + 0 + 0 + 0 + + + + + + + + + + + + buttonOk + clicked() + SubversionCheckout + accept() + + + buttonCancel + clicked() + SubversionCheckout + reject() + + + + + kurlrequester.h + klineedit.h + kpushbutton.h + + diff --git a/kioslave/svn/svnhelper/subversiondiff.ui b/kioslave/svn/svnhelper/subversiondiff.ui new file mode 100644 index 00000000..dab4ca0e --- /dev/null +++ b/kioslave/svn/svnhelper/subversiondiff.ui @@ -0,0 +1,100 @@ + +Subversion_Diff + + + Subversion_Diff + + + + 0 + 0 + 511 + 282 + + + + Subversion Diff + + + true + + + + unnamed + + + + text + + + PlainText + + + NoWrap + + + AutoAll + + + + + Layout1 + + + + unnamed + + + 0 + + + 6 + + + + Horizontal Spacing2 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + buttonOk + + + &OK + + + + + + true + + + true + + + + + + + + + buttonOk + clicked() + Subversion_Diff + accept() + + + + diff --git a/kioslave/svn/svnhelper/subversionlog.ui b/kioslave/svn/svnhelper/subversionlog.ui new file mode 100644 index 00000000..2c167d5b --- /dev/null +++ b/kioslave/svn/svnhelper/subversionlog.ui @@ -0,0 +1,100 @@ + +Subversion_Log + + + Subversion_Log + + + + 0 + 0 + 511 + 282 + + + + Subversion Log + + + true + + + + unnamed + + + + text + + + PlainText + + + NoWrap + + + AutoAll + + + + + Layout1 + + + + unnamed + + + 0 + + + 6 + + + + Horizontal Spacing2 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + buttonOk + + + &OK + + + + + + true + + + true + + + + + + + + + buttonOk + clicked() + Subversion_Log + accept() + + + + diff --git a/kioslave/svn/svnhelper/subversionswitch.ui b/kioslave/svn/svnhelper/subversionswitch.ui new file mode 100644 index 00000000..8ee1a37c --- /dev/null +++ b/kioslave/svn/svnhelper/subversionswitch.ui @@ -0,0 +1,204 @@ + +SubversionSwitch + + + SubversionSwitch + + + + 0 + 0 + 498 + 133 + + + + + 1 + 1 + 0 + 0 + + + + Subversion Switch + + + true + + + + unnamed + + + + layout8 + + + + unnamed + + + + Layout1 + + + + unnamed + + + 0 + + + 6 + + + + buttonHelp + + + &Help + + + F1 + + + true + + + + + Horizontal Spacing2 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + buttonOk + + + &OK + + + + + + true + + + true + + + + + buttonCancel + + + &Cancel + + + + + + true + + + + + + + url + + + + 7 + 5 + 0 + 0 + + + + + + layout4 + + + + unnamed + + + + textLabel1 + + + Revision (0 for HEAD) : + + + + + spacer2 + + + Horizontal + + + Expanding + + + + 121 + 20 + + + + + + revision + + + + 3 + 0 + 0 + 0 + + + + + + + + + + + + buttonOk + clicked() + SubversionSwitch + accept() + + + buttonCancel + clicked() + SubversionSwitch + reject() + + + + + kurlrequester.h + klineedit.h + kpushbutton.h + + diff --git a/kmtrace/Makefile.am b/kmtrace/Makefile.am new file mode 100644 index 00000000..68fce88d --- /dev/null +++ b/kmtrace/Makefile.am @@ -0,0 +1,50 @@ +# This file is part of the KDE libraries +# Copyright (C) 1996-1997 Matthias Kalle Dalheimer (kalle@kde.org) +# (C) 1997-1998 Stephan Kulow (coolo@kde.org) + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. + +# You should have received a copy of the GNU Library General Public License +# along with this library; see the file COPYING.LIB. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +AM_CPPFLAGS = -DQT_NO_ASCII_CAST -UQT_NO_COMPAT -O3 +LDADD = $(LIB_KDECORE) -liberty +INCLUDES = $(all_includes) + +bin_PROGRAMS = kmtrace demangle kmmatch +kmtrace_SOURCES = kmtrace.cpp +kmtrace_LDFLAGS = $(all_libraries) + +demangle_SOURCES = demangle.cpp +demangle_LDFLAGS = $(all_libraries) + +kmmatch_SOURCES = match.cpp +kmmatch_LDFLAGS = $(all_libraries) + +bin_SCRIPTS = kminspector + +lib_LIBRARIES = libktrace_s.a +libktrace_s_a_SOURCES = ktrace_s.c + +ktrace_s.c: $(srcdir)/ktrace.c + $(LN_S) $(srcdir)/ktrace.c $@ + +lib_LTLIBRARIES = libktrace.la +libktrace_la_LDFLAGS = -avoid-version +libktrace_la_SOURCES = ksotrace.cpp ktrace.c +libktrace_la_LIBADD = $(LIBDL) + +exclude_DATA = kde.excludes +excludedir = $(kde_datadir)/kmtrace + +include_HEADERS = ktrace.h diff --git a/kmtrace/README b/kmtrace/README new file mode 100644 index 00000000..63e35228 --- /dev/null +++ b/kmtrace/README @@ -0,0 +1,110 @@ +This is a KDE tool to assist with malloc debugging using glibc's "mtrace" +functionality. Unfortunately the mtrace that is part of current (9/9/2000) +glibc versions only logs the return-address of the malloc/free call. +The file mtrace.c in this directory logs a complete backtrace upon malloc/ +free. + +THIS PROGRAM DEPENDS ON GLIBC! It does not pretend to be portable. + +Howto use: + +Install the libktrace.so shared library, the ktrace.h header file, the +and kde.excludes file and the kmtrace processing tool with: + + make install + +There are two ways to activate memory usage loggings by ktrace : + +1) The LD_PRELOAD way + +This way, you can debug any application without having to recompile it, +but you'll have to debug also the memory allocated by KApplication and +friends. + +You can activate malloc logging by starting yourApplication as: + + MALLOC_TRACE=/tmp/ktrace.out LD_PRELOAD=$KDEDIR/lib/libktrace.so yourApplication + +2) The manual way + +Take the KDE application that you want to investigate and add + + #include + +Add as first statement in main(): + + ktrace(); + +Add ktrace_s.a to the LDADD line in your Makefile.am like: + + kicker_LDADD = kicker.la /opt/kde/lib/libktrace_s.a + +Note that the static library is used. +You can now activate malloc logging by starting yourApplication as: + + MALLOC_TRACE=/tmp/ktrace.out yourApplication + +This will generate a huge log in /tmp/ktrace.out. + +You can process this log with: + + kmtrace /tmp/ktrace.out > ktrace.parsed + +By default the trace-output is stored in the current directory +as "ktrace.out". kmtrace also searches it there, so you don't need +to add any commandline options. + +TIPS +==== + +* If you can't be bothered with the stuff that KApplication allocates for you +you might want to put the ktrace() call after the KApplication constructor. +This will lead to a lot of warnings like: + + Freeing unalloacted memory: 0x08056108 + +Which are harmless, it just means that the memory was allocated before you +turned the tracing on. Note that you cannot use this if you're using +LD_PRELOAD to use ktrace. + +* To filter out harmless allocations out of the output file you can specify +with the --exclude option a file with symbols to exclude from output. If a +backtrace contains a symbol that starts with any of the symbols in this file, +this backtrace / leaked block is not shown in the output. + +In the file kde.exclude some example symbols are listed. Usage example: + + kmtrace /tmp/malloc.trace > /tmp/malloc.parsed + +* Be aware that the reported symbols may not be accurate under all +circumstances. E.g. consider the following backtrace: + + 0x405879c1 /lib/libdl.so.2(dlerror+0x1b1) + 0x405873b3 /lib/libdl.so.2(dlopen+0x33) + 0x4053c0b2 /ext/kde2.0/lib/libkdecore.so.3(QXmlSimpleReader::reportParseErro + 0x4053c74b /ext/kde2.0/lib/libkdecore.so.3(lt_dlexit+0x24b) + 0x4053c894 /ext/kde2.0/lib/libkdecore.so.3(lt_dlexit+0x394) + 0x4053dd49 /ext/kde2.0/lib/libkdecore.so.3(lt_dlopen+0x899) + +The QXmlSimpleReader is obviously wrong here. + +* You can use the --tree option to kmtrace to specify a file to print a tree +of the allocations. You can also use --treedepth and --treethreshold options +to hide subtrees that are deeper than the specified depth or allocated less +than the given memory amount. + +* The advantage of using libktrace_s.a (the static library) is that you can +put calls to ktrace() and kuntrace() around a block of code that +interests you. Only allocations and deallocations between the first call +to ktrace() and the first call to kuntrace() will be logged. + + +Have fun. + +Waldo Bastian +bastian@kde.org + +kmtrace.cpp by Waldo Bastian +ktrace.c by Waldo Bastian based on mtrace.c +mtrace.c by Mike Haertel +mtrace.c patched by Andi Kleen diff --git a/kmtrace/configure.in.in b/kmtrace/configure.in.in new file mode 100644 index 00000000..5df3711e --- /dev/null +++ b/kmtrace/configure.in.in @@ -0,0 +1,18 @@ +dnl AC_OUTPUT( kmtrace/kminspector ) + +case "$host" in + *-gnu) + saved_LIBS="$LIBS" + LIBS="$LIBS -Wl,-Bstatic -liberty -Wl,-Bdynamic" + AC_TRY_LINK([], [], [kde_compile_kmtrace=$GCC], [kde_compile_kmtrace=no]) + AC_SUBST(KMTRACE_LIBS, [$LIBS]) + LIBS="$saved_LIBS" + ;; + *) + kde_compile_kmtrace=no + ;; +esac + +if test ! "x$kde_compile_kmtrace" = "xyes"; then + DO_NOT_COMPILE="$DO_NOT_COMPILE kmtrace" +fi diff --git a/kmtrace/demangle.cpp b/kmtrace/demangle.cpp new file mode 100644 index 00000000..81bcff90 --- /dev/null +++ b/kmtrace/demangle.cpp @@ -0,0 +1,60 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +/* Options passed to cplus_demangle (in 2nd parameter). */ + +#define DMGL_NO_OPTS 0 /* For readability... */ +#define DMGL_PARAMS (1 << 0) /* Include function args */ +#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ +#define DMGL_JAVA (1 << 2) /* Demangle as Java rather than C++. */ + +#define DMGL_AUTO (1 << 8) +#define DMGL_GNU (1 << 9) +#define DMGL_LUCID (1 << 10) +#define DMGL_ARM (1 << 11) +#define DMGL_HP (1 << 12) /* For the HP aCC compiler; same as ARM + except for template arguments, etc. */ +#define DMGL_EDG (1 << 13) +#define DMGL_GNU_V3 (1 << 14) +#define DMGL_GNAT (1 << 15) + + +extern char *cplus_demangle(const char *mangled, int options); +} + + +int main(int argc, char **argv) +{ + char buf[1024]; + + while(!feof(stdin)) + { + fgets(buf, 1024, stdin); + QCString line = buf; + line = line.stripWhiteSpace(); + char *res = cplus_demangle(line.data(), DMGL_PARAMS | DMGL_AUTO | DMGL_ANSI ); + if (res) + { + printf("%s\n", res); + free(res); + } + else + { + printf("%s\n", line.data()); + } + } +} + diff --git a/kmtrace/kde.excludes b/kmtrace/kde.excludes new file mode 100644 index 00000000..9e77eb5a --- /dev/null +++ b/kmtrace/kde.excludes @@ -0,0 +1,49 @@ +# We don't care about initialisation done by X11. +XLoadQueryFont +_XInitKeysymDB +IceRegisterForProtocolSetup +KDE_IceRegisterForProtocolSetup +IceOpenConnection +KDE_IceOpenConnection +KDE_IceProtocolSetup +#and we don't care about Xim memleaks +qt_init_internal +QApplication::create_xim +# workaround for Qt 2.2.0 bug +QFontDatabase::QFontDatabase +QFontDatabase::charSets + +#QRexExp does caching(?) +QRegExpEngine::QRegExpEngine +QRegExpEngine::parseExpression + +QStyleSheet::defaultSheet + +QTimer::singleShot +QTextCodec::codecForLocale +# Yeah, yeah we know that this metaobject stuff is never free'ed. +QMetaObject::new_meta +QObject::initMetaObject +QObject::staticMetaObject +QString::sprintf +QWidget::createTLExtra +# static KDE stuff +kdbgstream::flush +KCmdLineArgs::addCmdLineOptions +k_bindtextdomain +_nl_find_domain +KIconTheme::list +KIconTheme::current +KGlobalSettings::toolBarFont +KGlobalSettings::menuFont +KGlobalSettings::fixedFont +KGlobalSettings::generalFont +KGlobalSettings::toolBarFont +KImageIOFactory::self +objMap +# Some C functions that allocate data for initialisation +getpwuid +adjtime +getservbyname +# nothing created by static objects can be a memory leak +__static_initialization_and_destruction_0 diff --git a/kmtrace/kminspector.in b/kmtrace/kminspector.in new file mode 100755 index 00000000..572ed200 --- /dev/null +++ b/kmtrace/kminspector.in @@ -0,0 +1,9 @@ +#! /bin/sh + +export MALLOC_TREE=kminspector.tree +export MALLOC_THRESHOLD=2000 +export LD_PRELOAD=@kde_libraries@/libktrace.so + +$* + +cat kminspector.tree | less diff --git a/kmtrace/kmtrace.cpp b/kmtrace/kmtrace.cpp new file mode 100644 index 00000000..82055e43 --- /dev/null +++ b/kmtrace/kmtrace.cpp @@ -0,0 +1,721 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +/* Options passed to cplus_demangle (in 2nd parameter). */ + +#define DMGL_NO_OPTS 0 /* For readability... */ +#define DMGL_PARAMS (1 << 0) /* Include function args */ +#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ +#define DMGL_JAVA (1 << 2) /* Demangle as Java rather than C++. */ + +#define DMGL_AUTO (1 << 8) +#define DMGL_GNU (1 << 9) +#define DMGL_LUCID (1 << 10) +#define DMGL_ARM (1 << 11) +#define DMGL_HP (1 << 12) /* For the HP aCC compiler; same as ARM + except for template arguments, etc. */ +#define DMGL_EDG (1 << 13) +#define DMGL_GNU_V3 (1 << 14) +#define DMGL_GNAT (1 << 15) + + +extern char *cplus_demangle(const char *mangled, int options); +} + +struct Entry { + int base; + int size; + int signature; + int count; + int total_size; + int backtrace[1]; + + bool operator==(const Entry &e) { return total_size == e.total_size; } + bool operator<(const Entry &e) { return total_size > e.total_size; } +}; + +QIntDict *entryDict = 0; +QIntDict *symbolDict = 0; +QIntDict *formatDict = 0; +QSortedList *entryList = 0; +QStrList *excludes = 0; + +const char * const unknown = ""; +const char * const excluded = ""; +int allocCount = 0; +int leakedCount = 0; +int count = 0; +int maxCount; +int totalBytesAlloced = 0; +int totalBytesLeaked = 0; +int totalBytes = 0; +int maxBytes; + +int fromHex(const char *str); +void parseLine(const QCString &_line, char operation); +void dumpBlocks(); + +int fromHex(const char *str) +{ + if (*str == '[') str++; + str += 2; // SKip "0x" + return strtoll(str, NULL, 16); +} + +// [address0][address1] .... [address] + base size +void parseLine(const QCString &_line, char operation) +{ + char *line= (char *) _line.data(); + const char *cols[200]; + int i = 0; + cols[i++] = line; + while(*line) + { + if (*line == ' ') + { + *line = 0; + line++; + while (*line && (*line==' ')) line++; + if (*line) cols[i++] = line; + } + else line++; + } + int cols_count = i; + if (cols_count > 199) fprintf(stderr, "Error cols_count = %d\n", cols_count); + if (cols_count < 4) return; + switch (operation) + { + case '+': + { + Entry *entry = (Entry *) malloc((cols_count+3) *sizeof(int)); + entry->base = fromHex(cols[cols_count-2]); + entry->size = fromHex(cols[cols_count-1]); + int signature = 0; + for(int i = cols_count-3; i--;) + { + signature += (entry->backtrace[i-1] = fromHex(cols[i])); + } + entry->signature = (signature / 4)+cols_count; + entry->count = 1; + entry->total_size = entry->size; + entry->backtrace[cols_count-4] = 0; + totalBytesAlloced += entry->size; + totalBytes += entry->size; + count++; + if (totalBytes > maxBytes) + maxBytes = totalBytes; + if (count > maxCount) + maxCount = count; + if (entryDict->find(entry->base)) + fprintf(stderr, "\rAllocated twice: 0x%08x \n", entry->base); + entryDict->replace(entry->base, entry); + } break; + case '-': + { + int base = fromHex(cols[cols_count-1]); + Entry *entry = entryDict->take(base); + if (!entry) + { + if (base) + fprintf(stderr, "\rFreeing unalloacted memory: 0x%08x \n", base); + } + else + { + totalBytes -= entry->size; + count--; + free(entry); + } + } break; + default: + break; + } +} + +void sortBlocks() +{ + QIntDictIterator it(*entryDict); + for(;it.current(); ++it) + { + Entry *entry = it.current(); + totalBytesLeaked += entry->total_size; + entryList->append(entry); + for(int i = 0; entry->backtrace[i]; i++) + { + if (!symbolDict->find(entry->backtrace[i])) + symbolDict->insert(entry->backtrace[i], unknown); + } + } + entryList->sort(); +} + +void collectDupes() +{ + QIntDict dupeDict; + QIntDictIterator it(*entryDict); + for(;it.current();) + { + Entry *entry = it.current(); + ++it; + Entry *entry2 = dupeDict.find(entry->signature); + if (entry2) + { + entry2->count++; + entry2->total_size += entry->size; + entryDict->remove(entry->base); + } + else + { + dupeDict.insert(entry->signature, entry); + } + } +} + +int lookupSymbols(FILE *stream) +{ + int i = 0; + int symbols = 0; + char line2[1024]; + while(!feof(stream)) + { + fgets(line2, 1023, stream); + if (line2[0] == '=' ) + { + if(strcmp(line2,"= End") == 0 ) + break; + } + else if (line2[0] == '#') + ; + else if (line2[0] == '@') + ; + else if (line2[0] == '[') + ; + else if (line2[0] == '-') + ; + else if (line2[0] == '<') + ; + else if (line2[0] == '>') + ; + else if (line2[0] == '+') + { + i++; + if (i & 1024) + { + fprintf(stderr, "\rLooking up symbols: %d found %d of %d symbols", i, symbols, symbolDict->count()); + } + } + else + { + char *addr = index(line2, '['); + if (addr) + { + long i_addr = fromHex(addr); + const char* str = symbolDict->find(i_addr); + if (str == unknown) + { + *addr = 0; + char* str; + if( rindex(line2, '/') != NULL ) + str = qstrdup(rindex(line2, '/')+1); + else + str = qstrdup(line2); + symbolDict->replace(i_addr, str); + symbols++; + } + } + } + } + fprintf(stderr, "\rLooking up symbols: %d found %d of %d symbols\n", i, symbols, symbolDict->count()); + return symbolDict->count()-symbols; +} + +void lookupUnknownSymbols(const char *appname) +{ + KTempFile inputFile; + KTempFile outputFile; + inputFile.setAutoDelete(true); + outputFile.setAutoDelete(true); + FILE *fInputFile = inputFile.fstream(); + QIntDict oldDict = *symbolDict; + QIntDictIterator it(oldDict); + for(;it.current(); ++it) + { + fprintf(fInputFile, "%08lx\n", it.currentKey()); + } + inputFile.close(); + QCString command; + command.sprintf("addr2line -e %s -f -C -s < %s > %s", appname, + QFile::encodeName(KProcess::quote(inputFile.name())).data(), + QFile::encodeName(KProcess::quote(outputFile.name())).data()); + system(command.data()); + fInputFile = fopen(QFile::encodeName(outputFile.name()), "r"); + if (!fInputFile) + { + fprintf(stderr, "Error opening temp file.\n"); + return; + } + QIntDictIterator it2(oldDict); + char buffer1[1024]; + char buffer2[1024]; + for(;it2.current(); ++it2) + { + if (feof(fInputFile)) + { + fprintf(stderr, "Premature end of symbol output.\n"); + fclose(fInputFile); + return; + } + if (!fgets(buffer1, 1023, fInputFile)) continue; + if (!fgets(buffer2, 1023, fInputFile)) continue; + buffer1[strlen(buffer1)-1]=0; + buffer2[strlen(buffer2)-1]=0; + QCString symbol; + symbol.sprintf("%s(%s)", buffer2, buffer1); + if(*buffer1 != '?') + symbolDict->replace(it2.currentKey(),qstrdup(symbol.data())); + } + fclose(fInputFile); +} + +int match(const char *s1, const char *s2) +{ + register int result; + while(true) + { + result = *s1 - *s2; + if (result) + return result; + s1++; + s2++; + if (!*s2) return 0; + if (!*s1) return -1; + } + return 0; +} + +const char *lookupAddress(int addr) +{ + char *str = formatDict->find(addr); + if (str) return str; + QCString s = symbolDict->find(addr); + if (s.isEmpty()) + { +fprintf(stderr, "Error!\n"); + exit(1); + } + else + { + int start = s.find('('); + int end = s.findRev('+'); + if (end < 0) + end = s.findRev(')'); + if ((start > 0) && (end > start)) + { + QCString symbol = s.mid(start+1, end-start-1); + char *res = 0; + if (symbol.find(')') == -1) + res = cplus_demangle(symbol.data(), DMGL_PARAMS | DMGL_AUTO | DMGL_ANSI ); + + if (res) + { + symbol = res; + free(res); + } + res = (char *) symbol.data(); + for(const char *it = excludes->first();it;it = excludes->next()) + { + int i = match(res, it); + if (i == 0) + { + formatDict->insert(addr,excluded); + return excluded; + } + } + s.replace(start+1, end-start-1, symbol); + } + } + str = qstrdup(s.data()); + formatDict->insert(addr,str); + return str; +} + +void dumpBlocks() +{ + int filterBytes = 0; + int filterCount = 0; + for(Entry *entry = entryList->first();entry; entry = entryList->next()) + { + for(int i = 0; entry->backtrace[i]; i++) + { + const char *str = lookupAddress(entry->backtrace[i]); + if (str == excluded) + { + entry->total_size = 0; + continue; + } + } + if (!entry->total_size) continue; + filterBytes += entry->total_size; + filterCount++; + } + printf("Leaked memory after filtering: %d bytes in %d blocks.\n", filterBytes, filterCount); + for(Entry *entry = entryList->first();entry; entry = entryList->next()) + { + if (!entry->total_size) continue; + printf("[%d bytes in %d blocks, 1st. block is %d bytes at 0x%08x] ", entry->total_size, entry->count, entry->size, entry->base); + printf("\n"); + for(int i = 0; entry->backtrace[i]; i++) + { + const char *str = lookupAddress(entry->backtrace[i]); + printf(" 0x%08x %s\n", entry->backtrace[i], str); + } + } +} + +struct TreeEntry +{ + int address; // backtrace + int total_size; + int total_count; + typedef QValueList < TreeEntry > TreeList; + TreeList *subentries () const; + mutable TreeList *_subentries; + TreeEntry (int adr = 0, int size = 0, int count = 0, TreeList * sub = NULL ); + bool operator == (const TreeEntry &) const; + bool operator < (const TreeEntry &) const; +}; + +typedef QValueList < TreeEntry > TreeList; + +inline TreeEntry::TreeEntry (int adr, int size, int count, TreeList * sub) + : address (adr), total_size (size), total_count (count), _subentries (sub) +{ +} + +inline bool TreeEntry::operator == (const TreeEntry & r) const +{ // this one is for QValueList + return address == r.address; +} + +inline +bool TreeEntry::operator < (const TreeEntry & r) const +{ // this one is for qBubbleSort() ... yes, ugly hack + // the result is also reversed to get descending order + return total_size > r.total_size; +} + +inline TreeList * TreeEntry::subentries () const +{ // must be allocated only on-demand + if (_subentries == NULL) + _subentries = new TreeList; // this leaks memory, but oh well + return _subentries; +} + +TreeList * treeList = 0; + +void buildTree () +{ + for (Entry * entry = entryList->first (); + entry != NULL; entry = entryList->next ()) + { + if (!entry->total_size) + continue; + TreeList * list = treeList; + int i; + for (i = 0; entry->backtrace[i]; ++i) + ; // find last (topmost) backtrace entry + for (--i; i >= 0; --i) + { + TreeList::Iterator pos = list->find (entry->backtrace[i]); + if (pos == list->end ()) + { + list->prepend (TreeEntry (entry->backtrace[i], entry->total_size, + entry->count)); + pos = list->find (entry->backtrace[i]); + } + else + *pos = TreeEntry (entry->backtrace[i], + entry->total_size + (*pos).total_size, + entry->count + (*pos).total_count, + (*pos)._subentries); + list = (*pos).subentries (); + } + } +} + +void processTree (TreeList * list, int threshold, int maxdepth, int depth) +{ + if (++depth > maxdepth && maxdepth > 0) // maxdepth <= 0 means no limit + return; + for (TreeList::Iterator it = list->begin (); it != list->end ();) + { + if ((*it).subentries ()->count () > 0) + processTree ((*it).subentries (), threshold, maxdepth, depth); + if ((*it).total_size < threshold || (depth > maxdepth && maxdepth > 0)) + { + it = list->remove (it); + continue; + } + ++it; + } + qBubbleSort (*list); +} + +void +dumpTree (const TreeEntry & entry, int level, char *indent, FILE * file) +{ + bool extra_ind = (entry.subentries ()->count () > 0); + if(extra_ind) + indent[level++] = '+'; + indent[level] = '\0'; + char savindent[2]; + const char * str = lookupAddress (entry.address); + fprintf (file, "%s- %d/%d %s[0x%08x]\n", indent, + entry.total_size, entry.total_count, str, entry.address); + if (level > 1) + { + savindent[0] = indent[level - 2]; + savindent[1] = indent[level - 1]; + if (indent[level - 2] == '+') + indent[level - 2] = '|'; + else if (indent[level - 2] == '\\') + indent[level - 2] = ' '; + } + int pos = 0; + int last = entry.subentries ()->count() - 1; + for (TreeList::ConstIterator it = entry.subentries ()->begin (); + it != entry.subentries ()->end (); ++it) + { + if (pos == last) + indent[level - 1] = '\\'; + dumpTree ((*it), level, indent, file); + ++pos; + } + if (level > 1) + { + indent[level - 2] = savindent[0]; + indent[level - 1] = savindent[1]; + } + if (extra_ind) + --level; + indent[level] = '\0'; +} + +void dumpTree (FILE * file) +{ + char indent[1024]; + indent[0] = '\0'; + for (TreeList::ConstIterator it = treeList->begin (); + it != treeList->end (); ++it) + dumpTree (*it, 0, indent, file); +} + +void createTree (const QCString & treefile, int threshold, int maxdepth) +{ + FILE * file = fopen (treefile, "w"); + if (file == NULL) + { + fprintf (stderr, "Can't write tree file.\n"); + return; + } + treeList = new TreeList; + buildTree (); + processTree (treeList, threshold, maxdepth, 0); + dumpTree (file); + fclose (file); +} + +void readExcludeFile(const char *name) +{ + FILE *stream = fopen(name, "r"); + if (!stream) + { + fprintf(stderr, "Error: Can't open %s.\n", name); + exit(1); + } + char line[1024]; + while(!feof(stream)) + { + if (!fgets(line, 1023, stream)) break; + if ((line[0] == 0) || (line[0] == '#')) continue; + line[strlen(line)-1] = 0; + excludes->append(line); + } + fclose(stream); + excludes->sort(); +} + +static KCmdLineOptions options[] = +{ + { "x", 0, 0 }, + { "exclude ", "File containing symbols to exclude from output", 0}, + { "e", 0, 0 }, + { "exe ", "Executable to use for looking up unknown symbols", 0}, + { "+", "Log file to investigate", 0}, + {"t", 0, 0}, + {"tree ", "File to write allocations tree", 0}, + {"th", 0, 0}, + {"treethreshold ", + "Don't print subtrees which allocated less than memory", 0}, + {"td", 0, 0}, + {"treedepth ", + "Don't print subtrees that are deeper than ", 0}, + KCmdLineLastOption +}; + +int main(int argc, char *argv[]) +{ + KInstance instance("kmtrace"); + + KCmdLineArgs::init(argc, argv, "kmtrace", "KDE Memory leak tracer", "v1.0"); + + KCmdLineArgs::addCmdLineOptions(options); + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + (void) args->count(); + const char *logfile; + if(args->count()) + logfile = args->arg(0); + else + logfile = "ktrace.out"; + + QCString exe = args->getOption("exe"); + QCString exclude; + + excludes = new QStrList; + + exclude = QFile::encodeName(locate("data", "kmtrace/kde.excludes")); + if(!exclude.isEmpty()) + readExcludeFile(exclude); + + exclude = args->getOption("exclude"); + if (!exclude.isEmpty()) + { + fprintf(stderr, "Reading %s\n", exclude.data()); + readExcludeFile(exclude); + } + + FILE *stream = fopen(logfile, "r"); + if (!stream) + { + fprintf(stderr, "Can't open %s\n", logfile); + exit(1); + } + + entryDict = new QIntDict(9973); + symbolDict = new QIntDict(9973); + formatDict = new QIntDict(9973); + entryList = new QSortedList; + + fprintf(stderr, "Running\n"); + QCString line; + char line2[1024]; + while(!feof(stream)) + { + fgets(line2, 1023, stream); + line2[strlen(line2)-1] = 0; + if (line2[0] == '=') + { + printf("%s\n", line2); + if( strcmp( line2, "= End" ) == 0 ) + break; + } + else if (line2[0] == '#') + { + QCString app(line2+1); + if(exe.isEmpty()) + { + exe = app.stripWhiteSpace(); + fprintf(stderr, "ktrace.out: malloc trace of %s\n", exe.data()); + } + else if(!app.contains(exe.data())) + { + fprintf(stderr, "trace file was for application '%s', not '%s'\n", app.data(), exe.data()); + exit(1); + } + } + else if (line2[0] == '@') + line = 0; + else if (line2[0] == '[') + line = line + ' ' + line2; + else if (line2[0] == '+') + { + allocCount++; + line = line + ' ' + line2; + parseLine(line, '+'); + line = 0; + if (allocCount & 128) + { + fprintf(stderr, "\rTotal long term allocs: %d still allocated: %d ", allocCount, entryDict->count()); + } + } + else if (line2[0] == '-') + { + line = line + ' ' + line2; + parseLine(line, '-'); + line = 0; + } + else if (line2[0] == '<') + { + line2[0] = '-'; + // First part of realloc (free) + QCString reline = line + ' ' + line2; + parseLine(reline, '-'); + } + else if (line2[0] == '>') + { + line2[0] = '+'; + // Second part of realloc (alloc) + line = line + ' ' + line2; + parseLine(line, '+'); + line = 0; + } + else + { + char *addr = index(line2,'['); + if (addr) + { + line = line + ' ' + addr; + } + } + } + leakedCount = count; + fprintf(stderr, "\rTotal long term allocs: %d still allocated: %d(%d) \n", allocCount, leakedCount, entryDict->count()); + printf("Totals allocated: %d bytes in %d blocks.\n", totalBytesAlloced, allocCount); + printf("Maximum allocated: %d bytes / %d blocks.\n", maxBytes, maxCount); + fprintf(stderr, "Collecting duplicates...\n"); + collectDupes(); + fprintf(stderr, "Sorting...\n"); + sortBlocks(); + printf("Totals leaked: %d bytes in %d blocks.\n", totalBytesLeaked, leakedCount); + fprintf(stderr, "Looking up symbols...\n"); + rewind(stream); + lookupSymbols(stream); + fprintf(stderr, "Looking up unknown symbols...\n"); + lookupUnknownSymbols(exe); + fprintf(stderr, "Printing...\n"); + dumpBlocks(); + QCString treeFile = args->getOption ("tree"); + if (!treeFile.isEmpty ()) + { + fprintf (stderr, "Creating allocation tree...\n"); + createTree (treeFile, args->getOption ("treethreshold").toInt (), + args->getOption ("treedepth").toInt ()); + } + fprintf(stderr, "Done.\n"); + return 0; +} diff --git a/kmtrace/ksotrace.cpp b/kmtrace/ksotrace.cpp new file mode 100644 index 00000000..9f379414 --- /dev/null +++ b/kmtrace/ksotrace.cpp @@ -0,0 +1,11 @@ +#include "ktrace.h" +#include + +class KTraceActivate +{ +public: + KTraceActivate() { setenv("LD_PRELOAD","",1); ktrace(); } + ~KTraceActivate() { kuntrace(); } +} kTraceActivateInstance; + + diff --git a/kmtrace/ktrace.c b/kmtrace/ktrace.c new file mode 100644 index 00000000..044a1d24 --- /dev/null +++ b/kmtrace/ktrace.c @@ -0,0 +1,840 @@ +/* More debugging hooks for `malloc'. + Copyright (C) 1991,92,93,94,96,97,98,99,2000 Free Software Foundation, Inc. + Written April 2, 1991 by John Gilmore of Cygnus Support. + Based on mcheck.c by Mike Haertel. + Hacked by AK + Cleanup and performance improvements by + Chris Schlaeger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. +*/ + +#define MALLOC_HOOKS +#define _GNU_SOURCE + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef USE_IN_LIBIO +# include +# define setvbuf(s, b, f, l) _IO_setvbuf (s, b, f, l) +#endif + +/* This is the most important parameter. It should be set to two times + * the maximum number of mallocs the application uses at a time. Prime + * numbers are very good candidates for this value. I added a list of + * some prime numbers for conveniance. + * + * 10007, 20011, 30013, 40031, 50033, 60037, 70039, 80051, 90053, + * 100057, 110059, 120067, 130069, 140071, 150077, 160079, 170081, + * 180097, 190121, 200131, 210139, 220141, 230143, 240151, 250153, + * 260171, 270191, 280199, 290201, 300221, 310223, 320237, 330241, + * 340261, 350281, 360287, 370373, 380377, 390389 */ +#define TR_CACHE_SIZE 100057 + +/* The DELTA value is also a value for the maximum + * number of iterations during a positive free/realloc + * search. It must NOT divide TR_CACHE_SIZE without + * remainder! */ +#define DELTA 23 + +/* The high and low mark control the flushing algorithm. Whenever the + * hashtable reaches the high mark every DELTAth entry is written to + * disk until the low filling mark is reached. A hash table becomes + * very inefficient when it becomes filled to 50% or more. */ +#define TR_HIGH_MARK ((int) (TR_CACHE_SIZE * 0.5)) +#define TR_LOW_MARK ((int) (TR_HIGH_MARK - (TR_CACHE_SIZE / DELTA))) + +/* Maximum call stack depth. No checking for overflows + * is done. Adjust this value with care! */ +#define TR_BT_SIZE 100 + +#define PROFILE 1 + +/* The hash function. Since the smallest allocated block is probably + * not smaller than 8 bytes we ignore the last 3 LSBs. */ +#define HASHFUNC(a) (((((unsigned long) a) << 1) ^ \ + (((unsigned long) a) >> 3)) % \ + TR_CACHE_SIZE) + +#define TR_HASHTABLE_SIZE 9973 + +#define TR_NONE 0 +#define TR_MALLOC 1 +#define TR_REALLOC 2 +#define TR_FREE 3 + +#define TRACE_BUFFER_SIZE 512 + +typedef struct +{ + void* ptr; + size_t size; + int bt_size; + void** bt; +} tr_entry; + +typedef struct CallerNode +{ + void* funcAdr; + unsigned long mallocs; + unsigned long mallocsSum; + unsigned int noCallees; + unsigned int maxCallees; + struct CallerNode** callees; +} CallerNode; + +void ktrace(void); +void kuntrace(void); + +static void addAllocationToTree(void); + +static void tr_freehook __P ((void*, const void*)); +static void* tr_reallochook __P ((void*, size_t, + const void*)); +static void* tr_mallochook __P ((size_t, const void*)); +/* Old hook values. */ +static void (*tr_old_free_hook) __P ((void* ptr, const void*)); +static void* (*tr_old_malloc_hook) __P ((size_t size, + const void*)); +static void* (*tr_old_realloc_hook) __P ((void* ptr, + size_t size, + const void*)); + +static FILE* mallstream; +static char malloc_trace_buffer[TRACE_BUFFER_SIZE]; + + +/* Address to breakpoint on accesses to... */ +void* mallwatch; + +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + + +static int bt_size; +static void *bt[TR_BT_SIZE + 1]; +static char tr_offsetbuf[20]; +static tr_entry tr_cache[TR_CACHE_SIZE]; +static int tr_cache_level; +static int tr_cache_pos; +static int tr_max_offset = 0; +static void *tr_hashtable[TR_HASHTABLE_SIZE]; +#ifdef PROFILE +static unsigned long tr_mallocs = 0; +static unsigned long tr_logged_mallocs = 0; +static unsigned long tr_frees = 0; +static unsigned long tr_logged_frees = 0; +static unsigned long tr_current_mallocs = 0; +static unsigned long tr_max_mallocs = 0; +static unsigned long tr_flashes = 0; +static unsigned long tr_failed_free_lookups = 0; +static unsigned long tr_malloc_collisions = 0; +#endif + +static CallerNode* CallTree = NULL; +static char* mallTreeFile = NULL; +static FILE* mallTreeStream = NULL; +static long mallThreshold = 2000; + +/* This function is called when the block being alloc'd, realloc'd, or + * freed has an address matching the variable "mallwatch". In a + * debugger, set "mallwatch" to the address of interest, then put a + * breakpoint on tr_break. */ +void tr_break __P ((void)); +void +tr_break() +{ +} + +__inline__ static void +tr_backtrace(void **_bt, int size) +{ + int i; + Dl_info info; + for (i = 0; i < size; i++) + { + long hash = (((unsigned long)_bt[i]) / 4) % TR_HASHTABLE_SIZE; + if ((tr_hashtable[hash]!= _bt[i]) && dladdr(_bt[i], &info) && + info.dli_fname && *info.dli_fname) + { + if (_bt[i] >= (void *) info.dli_saddr) + sprintf(tr_offsetbuf, "+%#lx", (unsigned long) + (_bt[i] - info.dli_saddr)); + else + sprintf(tr_offsetbuf, "-%#lx", (unsigned long) + (info.dli_saddr - _bt[i])); + fprintf(mallstream, "%s%s%s%s%s[%p]\n", + info.dli_fname ?: "", + info.dli_sname ? "(" : "", + info.dli_sname ?: "", + info.dli_sname ? tr_offsetbuf : "", + info.dli_sname ? ")" : "", + _bt[i]); + tr_hashtable[hash] = _bt[i]; + } + else + { + fprintf(mallstream, "[%p]\n", _bt[i]); + } + } +} + +__inline__ static void +tr_log(const void* caller, void* ptr, void* old, + size_t size, int op) +{ + int i, offset; + + switch (op) + { + case TR_FREE: + i = HASHFUNC(ptr); + if ((offset = (i + tr_max_offset + 1)) >= TR_CACHE_SIZE) + offset -= TR_CACHE_SIZE; + do + { + if (tr_cache[i].ptr == ptr) + { + tr_cache[i].ptr = NULL; + free(tr_cache[i].bt); + tr_cache_level--; + return; + } + if (++i >= TR_CACHE_SIZE) + i = 0; +#ifdef PROFILE + tr_failed_free_lookups++; +#endif + } while (i != offset); + + /* We don't know this allocation, so it has been flushed to disk + * already. So flush free as well. */ + fprintf(mallstream, "@\n"); + bt_size = backtrace(bt, TR_BT_SIZE); + tr_backtrace(&(bt[1]), bt_size - 2); + fprintf(mallstream, "- %p\n", ptr); +#ifdef PROFILE + tr_logged_frees++; +#endif + return; + + case TR_REALLOC: + /* If old is 0 it's actually a malloc. */ + if (old) + { + i = HASHFUNC(old); + if ((offset = (i + tr_max_offset + 1)) >= TR_CACHE_SIZE) + offset -= TR_CACHE_SIZE; + do + { + if (tr_cache[i].ptr == old) + { + int j = HASHFUNC(ptr); + /* We move the entry otherwise the free will be + * fairly expensive due to the wrong place in the + * hash table. */ + tr_cache[i].ptr = NULL; + for ( ; ; ) + { + if (tr_cache[j].ptr == NULL) + break; + + if (++j >= TR_CACHE_SIZE) + i = 0; + } + tr_cache[j].ptr = ptr; + if (ptr) + { + tr_cache[j].size = tr_cache[i].size; + tr_cache[j].bt_size = tr_cache[i].bt_size; + tr_cache[j].bt = tr_cache[i].bt; + } + else + tr_cache_level--; + tr_cache[i].size = size; + return; + } + if (++i >= TR_CACHE_SIZE) + i = 0; + } while (i != offset); + + fprintf(mallstream, "@\n"); + bt_size = backtrace(bt, TR_BT_SIZE); + tr_backtrace(&(bt[1]), bt_size - 2); + fprintf(mallstream, "< %p\n", old); + fprintf(mallstream, "> %p %#lx\n", ptr, + (unsigned long) size); + return; + } + + case TR_MALLOC: + if (tr_cache_level >= TR_HIGH_MARK) + { + /* The hash table becomes ineffective when the high mark has + * been reached. We still need some more experience with + * the low mark. It's unclear what reasonable values are. */ +#ifdef PROFILE + tr_flashes++; +#endif + i = HASHFUNC(ptr); + do + { + if (tr_cache[i].ptr) + { +#ifdef PROFILE + tr_logged_mallocs++; +#endif + fprintf(mallstream, "@\n"); + tr_backtrace(&(tr_cache[i].bt[1]), + tr_cache[i].bt_size - 2); + fprintf(mallstream, "+ %p %#lx\n", + tr_cache[i].ptr, + (unsigned long int) + tr_cache[i].size); + tr_cache[i].ptr = NULL; + tr_cache_level--; + } + if ((i += DELTA) >= TR_CACHE_SIZE) + i -= TR_CACHE_SIZE; + } while (tr_cache_level > TR_LOW_MARK); + } + + i = HASHFUNC(ptr); + for ( ; ; ) + { + if (tr_cache[i].ptr == NULL) + break; + + if (++i >= TR_CACHE_SIZE) + i = 0; +#ifdef PROFILE + tr_malloc_collisions++; +#endif + } + if ((offset = (i - HASHFUNC(ptr))) < 0) + offset += TR_CACHE_SIZE; + if (offset > tr_max_offset) + tr_max_offset = offset; + + tr_cache[i].ptr = ptr; + tr_cache[i].size = size; + tr_cache[i].bt = (void**) malloc(TR_BT_SIZE * sizeof(void*)); + tr_cache[i].bt_size = backtrace( + tr_cache[i].bt, TR_BT_SIZE); + tr_cache[i].bt = realloc(tr_cache[i].bt, tr_cache[i].bt_size * sizeof(void*)); + tr_cache_level++; + + return; + + case TR_NONE: + if (tr_cache[tr_cache_pos].ptr) + { +#ifdef PROFILE + tr_logged_mallocs++; +#endif + fprintf(mallstream, "@\n"); + tr_backtrace(&(tr_cache[tr_cache_pos].bt[1]), + tr_cache[tr_cache_pos].bt_size - 2); + fprintf(mallstream, "+ %p %#lx\n", + tr_cache[tr_cache_pos].ptr, + (unsigned long int) + tr_cache[tr_cache_pos].size); + tr_cache[tr_cache_pos].ptr = NULL; + free(tr_cache[tr_cache_pos].bt); + tr_cache_level--; + } + + if (++tr_cache_pos >= TR_CACHE_SIZE) + tr_cache_pos = 0; + break; + } +} + +static void +tr_freehook (ptr, caller) + void* ptr; + const void* caller; +{ + if (ptr == NULL) + return; + if (ptr == mallwatch) + tr_break (); + + pthread_mutex_lock(&lock); +#ifdef PROFILE + tr_frees++; + tr_current_mallocs--; +#endif + + __free_hook = tr_old_free_hook; + + if (tr_old_free_hook != NULL) + (*tr_old_free_hook) (ptr, caller); + else + free(ptr); + tr_log(caller, ptr, 0, 0, TR_FREE); + + __free_hook = tr_freehook; + pthread_mutex_unlock(&lock); +} + +static void* +tr_mallochook (size, caller) + size_t size; + const void* caller; +{ + void* hdr; + + pthread_mutex_lock(&lock); + + __malloc_hook = tr_old_malloc_hook; + __realloc_hook = tr_old_realloc_hook; + __free_hook = tr_old_free_hook; + + if (tr_old_malloc_hook != NULL) + hdr = (void*) (*tr_old_malloc_hook) (size, caller); + else + hdr = (void*) malloc(size); + tr_log(caller, hdr, 0, size, TR_MALLOC); + /* We only build the allocation tree if mallTreeFile has been set. */ + if (mallTreeFile) + addAllocationToTree(); + + __malloc_hook = tr_mallochook; + __realloc_hook = tr_reallochook; + __free_hook = tr_freehook; + +#ifdef PROFILE + tr_mallocs++; + tr_current_mallocs++; + if (tr_current_mallocs > tr_max_mallocs) + tr_max_mallocs = tr_current_mallocs; +#endif + pthread_mutex_unlock(&lock); + + if (hdr == mallwatch) + tr_break (); + + return hdr; +} + +static void* +tr_reallochook (ptr, size, caller) + void* ptr; + size_t size; + const void* caller; +{ + void* hdr; + + if (ptr == mallwatch) + tr_break (); + + pthread_mutex_lock(&lock); + + __free_hook = tr_old_free_hook; + __malloc_hook = tr_old_malloc_hook; + __realloc_hook = tr_old_realloc_hook; + + if (tr_old_realloc_hook != NULL) + hdr = (void*) (*tr_old_realloc_hook) (ptr, size, caller); + else + hdr = (void*) realloc (ptr, size); + + tr_log(caller, hdr, ptr, size, TR_REALLOC); + + __free_hook = tr_freehook; + __malloc_hook = tr_mallochook; + __realloc_hook = tr_reallochook; + +#ifdef PROFILE + /* If ptr is 0 there was no previos malloc of this location */ + if (ptr == NULL) + { + tr_mallocs++; + tr_current_mallocs++; + if (tr_current_mallocs > tr_max_mallocs) + tr_max_mallocs = tr_current_mallocs; + } +#endif + + pthread_mutex_unlock(&lock); + + if (hdr == mallwatch) + tr_break (); + + return hdr; +} + +void +addAllocationToTree(void) +{ + int bt_size; + int i, j; + void *bt[TR_BT_SIZE + 1]; + CallerNode* cn = CallTree; + CallerNode** parent = &CallTree; + + bt_size = backtrace(bt, TR_BT_SIZE); + for (i = bt_size - 1; i >= 4; i--) + { + if (cn == NULL) + { + *parent = cn = (CallerNode*) malloc(sizeof(CallerNode)); + cn->funcAdr = bt[i]; + cn->mallocs = 0; + cn->noCallees = 0; + cn->maxCallees = 0; + cn->callees = NULL; + } + if (i == 4) + cn->mallocs++; + else + { + int knownCallee = 0; + for (j = 0; j < cn->noCallees; j++) + if (bt[i - 1] == cn->callees[j]->funcAdr) + { + parent = &cn->callees[j]; + cn = cn->callees[j]; + knownCallee = 1; + break; + } + if (!knownCallee) + { + if (cn->noCallees == cn->maxCallees) + { + /* Copy callees into new, larger array. */ + CallerNode** tmp; + int newSize = 2 * cn->maxCallees; + if (newSize == 0) + newSize = 4; + tmp = (CallerNode**) malloc(newSize * sizeof(CallerNode*)); + memcpy(tmp, cn->callees, + cn->maxCallees * sizeof(CallerNode*)); + if (cn->callees) + free(cn->callees); + cn->callees = tmp; + memset(&cn->callees[cn->maxCallees], 0, + (newSize - cn->maxCallees) * sizeof(CallerNode*)); + cn->maxCallees = newSize; + } + parent = &cn->callees[cn->noCallees++]; + cn = 0; + } + } + } +} + +static int +removeBranchesBelowThreshold(CallerNode* root) +{ + int i; + int max; + + if (!root) + return (0); + for (i = 0; i < root->noCallees; i++) + { + if (removeBranchesBelowThreshold(root->callees[i])) + { + free(root->callees[i]); + if (root->noCallees > 1) + { + root->callees[i] = root->callees[root->noCallees - 1]; + root->callees[root->noCallees - 1] = 0; + } + else if (root->noCallees == 1) + root->callees[i] = 0; + + root->noCallees--; + i--; + } + } + if (root->noCallees == 0 && root->mallocs < mallThreshold ) + return (1); + + return (0); +} + +static void +dumpCallTree(CallerNode* root, char* indentStr, int rawMode) +{ + int i; + Dl_info info; + char* newIndentStr; + size_t indDepth; + + if (!root || !mallTreeStream) + return; + + if (rawMode) + { + fprintf(mallTreeStream, "-"); + } + else + { + newIndentStr = (char*) malloc(strlen(indentStr) + 2); + strcpy(newIndentStr, indentStr); + if (root->noCallees > 0) + strcat(newIndentStr, "+"); + indDepth = strlen(newIndentStr); + fprintf(mallTreeStream, "%s- ", newIndentStr); + } + + if (dladdr(root->funcAdr, &info) && info.dli_fname && *info.dli_fname) + { + if (root->funcAdr >= (void *) info.dli_saddr) + sprintf(tr_offsetbuf, "+%#lx", (unsigned long) + (root->funcAdr - info.dli_saddr)); + else + sprintf(tr_offsetbuf, "-%#lx", (unsigned long) + (info.dli_saddr - root->funcAdr)); + fprintf(mallTreeStream, "%s%s%s%s%s[%p]", + info.dli_fname ?: "", + info.dli_sname ? "(" : "", + info.dli_sname ?: "", + info.dli_sname ? tr_offsetbuf : "", + info.dli_sname ? ")" : "", + root->funcAdr); + } + else + { + fprintf(mallTreeStream, "[%p]", root->funcAdr); + } + fprintf(mallTreeStream, ": %lu\n", root->mallocs); + if (indDepth > 1 && !rawMode) + { + if (newIndentStr[indDepth - 2] == '+') + newIndentStr[indDepth - 2] = '|'; + else if (newIndentStr[indDepth - 2] == '\\') + newIndentStr[indDepth - 2] = ' '; + } + + for (i = 0; i < root->noCallees; i++) + { + if (rawMode) + dumpCallTree(root->callees[i], "", 1); + else + { + if (i == root->noCallees - 1) + newIndentStr[indDepth - 1] = '\\'; + dumpCallTree(root->callees[i], newIndentStr, rawMode); + } + } + if (rawMode) + fprintf(mallTreeStream, ".\n"); + else + free(newIndentStr); + +} + +#ifdef _LIBC +extern void __libc_freeres (void); + +/* This function gets called to make sure all memory the library + * allocates get freed and so does not irritate the user when studying + * the mtrace output. */ +static void +release_libc_mem (void) +{ + /* Only call the free function if we still are running in mtrace + * mode. */ + /*if (mallstream != NULL) + __libc_freeres ();*/ + + kuntrace(); + write(2, "kuntrace()\n", 11); +} +#endif + +/* We enable tracing if either the environment variable MALLOC_TRACE + * or the variable MALLOC_TREE are set, or if the variable mallwatch + * has been patched to an address that the debugging user wants us to + * stop on. When patching mallwatch, don't forget to set a breakpoint + * on tr_break! */ +void +ktrace() +{ +#ifdef _LIBC + static int added_atexit_handler; +#endif + char* mallfile; + + /* Don't panic if we're called more than once. */ + if (mallstream != NULL) + return; + +#ifdef _LIBC + /* When compiling the GNU libc we use the secure getenv function + * which prevents the misuse in case of SUID or SGID enabled + * programs. */ + mallfile = __secure_getenv("MALLOC_TRACE"); + mallTreeFile = __secure_getenv("MALLOC_TREE"); + if( __secure_getenv("MALLOC_THRESHOLD") != NULL ) + mallThreshold = atol(__secure_getenv("MALLOC_THRESHOLD")); +#else + mallfile = getenv("MALLOC_TRACE"); + mallTreeFile = getenv("MALLOC_TREE"); + if( getenv("MALLOC_THRESHOLD") != NULL ) + mallThreshold = atol(getenv("MALLOC_THRESHOLD")); +#endif + if (mallfile != NULL || mallTreeFile != NULL || mallwatch != NULL) + { + mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "w"); + if (mallstream != NULL) + { + char buf[512]; + + /* Be sure it doesn't malloc its buffer! */ + setvbuf (mallstream, malloc_trace_buffer, _IOFBF, + TRACE_BUFFER_SIZE); + fprintf (mallstream, "= Start\n"); + memset(buf, 0, sizeof(buf)); + readlink("/proc/self/exe", buf, sizeof(buf)); + if(*buf) + fprintf (mallstream, "#%s\n", buf); + + /* Save old hooks and hook in our own functions for all + * malloc, realloc and free calls */ + tr_old_free_hook = __free_hook; + __free_hook = tr_freehook; + tr_old_malloc_hook = __malloc_hook; + __malloc_hook = tr_mallochook; + tr_old_realloc_hook = __realloc_hook; + __realloc_hook = tr_reallochook; + + tr_cache_pos = TR_CACHE_SIZE; + do + { + tr_cache[--tr_cache_pos].ptr = NULL; + } while (tr_cache_pos); + tr_cache_level = 0; + + memset(tr_hashtable, 0, sizeof(void*) * TR_HASHTABLE_SIZE); +#ifdef _LIBC + if (!added_atexit_handler) + { + added_atexit_handler = 1; + atexit (release_libc_mem); + } +#endif + } + } +} + +void +kuntrace() +{ + if (mallstream == NULL) + return; + + /* restore hooks to original values */ + __free_hook = tr_old_free_hook; + __malloc_hook = tr_old_malloc_hook; + __realloc_hook = tr_old_realloc_hook; + + if (removeBranchesBelowThreshold(CallTree)) + CallTree = 0; + if (mallTreeFile) + { + if (mallTreeStream = fopen(mallTreeFile, "w")) + { + dumpCallTree(CallTree, "", 0); + fclose(mallTreeStream); + } + } + + /* Flush cache. */ + while (tr_cache_level) + tr_log(NULL, 0, 0, 0, TR_NONE); + + fprintf (mallstream, "= End\n"); +#ifdef PROFILE + fprintf(mallstream, "\nMax Mallocs: %8ld Cache Size: %8ld" + " Flashes: %8ld\n" + "Mallocs: %8ld Frees: %8ld Leaks: %8ld\n" + "Logged Mallocs: %8ld Logged Frees: %8ld Logged Leaks: %8ld\n" + "Avg. Free lookups: %ld Malloc collisions: %ld Max offset: %ld\n", + tr_max_mallocs, TR_CACHE_SIZE, tr_flashes, + tr_mallocs, tr_frees, tr_current_mallocs, + tr_logged_mallocs, tr_logged_frees, + tr_logged_mallocs - tr_logged_frees, + tr_frees > 0 ? ( tr_failed_free_lookups / tr_frees ) : 0, + tr_malloc_collisions, tr_max_offset); +#endif + fclose (mallstream); + mallstream = NULL; + write(2, "kuntrace()\n", 11); +} + +int fork() +{ + int result; + if (mallstream) + fflush(mallstream); + result = __fork(); + if (result == 0) + { + if (mallstream) + { + close(fileno(mallstream)); + mallstream = NULL; + __free_hook = tr_old_free_hook; + __malloc_hook = tr_old_malloc_hook; + __realloc_hook = tr_old_realloc_hook; + } + } + return result; +} + + +static int my_mcount_lock = 0; +void mcount() +{ + Dl_info info; + int i = 1; + if (my_mcount_lock) return; + my_mcount_lock = 1; + bt_size = backtrace(bt, TR_BT_SIZE); +#if 0 +for(i = 1; (i < 5) && (i < bt_size); i++) +{ +#endif + if (dladdr(bt[i], &info) && info.dli_fname && *info.dli_fname) + { + fprintf(stdout, "%s\n", info.dli_sname ? info.dli_sname : ""); + } + else + { + fprintf(stdout, "[%p]\n", bt[i]); + } +#if 0 +} + fprintf(stdout, "\n"); +#endif + my_mcount_lock = 0; +} + diff --git a/kmtrace/ktrace.h b/kmtrace/ktrace.h new file mode 100644 index 00000000..366f2148 --- /dev/null +++ b/kmtrace/ktrace.h @@ -0,0 +1,10 @@ +#ifndef _KTRACE_H +#define _KTRACE_H + +extern "C" { +/* Activate a standard collection of tracing hooks. */ +extern void ktrace (void); +extern void kuntrace (void); +} + +#endif /* ktrace.h */ diff --git a/kmtrace/match.cpp b/kmtrace/match.cpp new file mode 100644 index 00000000..2a9c74b4 --- /dev/null +++ b/kmtrace/match.cpp @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + char buf[1024]; + if (argc != 3) + { + fprintf(stderr, "Usage: kmmatch \n"); + fprintf(stderr, "\n is a file as output by 'nm'.\n"); + fprintf(stderr, " is a file that contains symbols, e.g. a list of all\n" + "function calls made by a program.\n"); + fprintf(stderr, "The program will print all symbols from that are present\n" + "in , in the same order as they appear in .\n"); + return 1; + } + + int i = 1; + QDict dict(20011); + + FILE *map_file = fopen(argv[1], "r"); + if (!map_file) + { + fprintf(stderr, "Error opening '%s'\n", argv[1]); + return 1; + } + while(!feof(map_file)) + { + fgets(buf, 1024, map_file); + QString line = QString::fromLatin1(buf).stripWhiteSpace(); + QStringList split = QStringList::split(' ', line); + if (split.count() <= 1) + return 1; + + if (split[1] == "T") + { + dict.insert(split[2], &i); + } + } + fclose(map_file); + + FILE *call_file = fopen(argv[2], "r"); + if (!call_file) + { + fprintf(stderr, "Error opening '%s'\n", argv[2]); + return 1; + } + + while(!feof(call_file)) + { + fgets(buf, 1024, call_file); + QString line = QString::fromLatin1(buf).stripWhiteSpace(); + if (dict.find(line)) + { + qWarning("%s", line.latin1()); + } + } + fclose(call_file); + return 0; +} + diff --git a/kmtrace/mtrace.c b/kmtrace/mtrace.c new file mode 100644 index 00000000..26c08ae6 --- /dev/null +++ b/kmtrace/mtrace.c @@ -0,0 +1,383 @@ +/* More debugging hooks for `malloc'. + Copyright (C) 1991,92,93,94,96,97,98,99,2000 Free Software Foundation, Inc. + Written April 2, 1991 by John Gilmore of Cygnus Support. + Based on mcheck.c by Mike Haertel. + Hacked by AK + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + The author may be reached (Email) at the address mike@ai.mit.edu, + or (US mail) as Mike Haertel c/o Free Software Foundation. */ + +#define _LIBC +#define MALLOC_HOOKS + +#ifndef _MALLOC_INTERNAL +#define _MALLOC_INTERNAL +#include +#include +#include +#endif + +#include + +#include +#include +#include + +#include +#include + +#ifdef USE_IN_LIBIO +# include +# define setvbuf(s, b, f, l) _IO_setvbuf (s, b, f, l) +#endif + +#define TRACE_BUFFER_SIZE 512 + +static FILE *mallstream; +static const char mallenv[]= "MALLOC_TRACE"; +static char malloc_trace_buffer[TRACE_BUFFER_SIZE]; + +__libc_lock_define_initialized (static, lock) + +/* Address to breakpoint on accesses to... */ +__ptr_t mallwatch; + +/* File name and line number information, for callers that had + the foresight to call through a macro. */ +char *_mtrace_file; +int _mtrace_line; + +/* Old hook values. */ +static void (*tr_old_free_hook) __P ((__ptr_t ptr, const __ptr_t)); +static __ptr_t (*tr_old_malloc_hook) __P ((__malloc_size_t size, + const __ptr_t)); +static __ptr_t (*tr_old_realloc_hook) __P ((__ptr_t ptr, + __malloc_size_t size, + const __ptr_t)); + +#define TR_PIPELINE_SIZE 16 +#define TR_BT_SIZE 80 +#define TR_HASHTABLE_SIZE 9973 + +#define TR_NONE 0 +#define TR_MALLOC 1 +#define TR_REALLOC 2 +#define TR_FREE 3 + +typedef struct { + int op; + __ptr_t ptr; + __ptr_t old; + __malloc_size_t size; + int bt_size; + void *bt[TR_BT_SIZE+1]; +} tr_entry; + +static tr_entry tr_pipeline[TR_PIPELINE_SIZE]; +static int tr_pipeline_pos; +static void *tr_hashtable[TR_HASHTABLE_SIZE]; + + +/* This function is called when the block being alloc'd, realloc'd, or + freed has an address matching the variable "mallwatch". In a debugger, + set "mallwatch" to the address of interest, then put a breakpoint on + tr_break. */ + +void tr_break __P ((void)); +void +tr_break () +{ +} + +static void +tr_backtrace(void **bt, int size) +{ + char buf[20]; + int i; + Dl_info info; + for(i = 0; i < size; i++) + { + long hash = (((unsigned long)bt[i]) / 4) % TR_HASHTABLE_SIZE; + if ((tr_hashtable[hash]!= bt[i]) && _dl_addr(bt[i], &info) && info.dli_fname && *info.dli_fname) + { + if (bt[i] >= (void *) info.dli_saddr) + sprintf(buf, "+%#lx", (unsigned long)(bt[i] - info.dli_saddr)); + else + sprintf(buf, "-%#lx", (unsigned long)(info.dli_saddr - bt[i])); + fprintf(mallstream, "%s%s%s%s%s[%p]\n", + info.dli_fname ?: "", + info.dli_sname ? "(" : "", + info.dli_sname ?: "", + info.dli_sname ? buf : "", + info.dli_sname ? ")" : "", + bt[i]); + tr_hashtable[hash] = bt[i]; + } + else { + fprintf(mallstream, "[%p]\n", bt[i]); + } + } +} + +static void +inline +tr_log(const __ptr_t caller, __ptr_t ptr, __ptr_t old, __malloc_size_t size, int op) +{ + switch(op) + { + case TR_REALLOC: + break; + case TR_MALLOC: + break; + case TR_FREE: + { + int i = tr_pipeline_pos; + do { + if (i) i--; else i = TR_PIPELINE_SIZE-1; + if (tr_pipeline[i].ptr == ptr) + { + if (tr_pipeline[i].op == TR_MALLOC) + { + tr_pipeline[i].op = TR_NONE; + tr_pipeline[i].ptr = NULL; + return; + } + break; + } + } while (i != tr_pipeline_pos); + } + } + + if (tr_pipeline[tr_pipeline_pos].op) + { + putc('@', mallstream); + putc('\n', mallstream); + /* Generate backtrace... + * We throw out the first frame (tr_mallochook) + * end the last one (_start) + */ + tr_backtrace(&(tr_pipeline[tr_pipeline_pos].bt[1]), + tr_pipeline[tr_pipeline_pos].bt_size-2); + + switch(tr_pipeline[tr_pipeline_pos].op) + { + case TR_MALLOC: + fprintf (mallstream, "+ %p %#lx\n", + tr_pipeline[tr_pipeline_pos].ptr, + (unsigned long int) tr_pipeline[tr_pipeline_pos].size); + break; + case TR_FREE: + fprintf (mallstream, "- %p\n", + tr_pipeline[tr_pipeline_pos].ptr); + break; + case TR_REALLOC: + fprintf (mallstream, "< %p\n", + tr_pipeline[tr_pipeline_pos].old); + fprintf (mallstream, "> %p %#lx\n", + tr_pipeline[tr_pipeline_pos].ptr, + (unsigned long int) tr_pipeline[tr_pipeline_pos].size); + break; + } + } + + tr_pipeline[tr_pipeline_pos].op = op; + tr_pipeline[tr_pipeline_pos].ptr = ptr; + tr_pipeline[tr_pipeline_pos].old = old; + tr_pipeline[tr_pipeline_pos].size = size; + tr_pipeline[tr_pipeline_pos].bt_size = backtrace( + tr_pipeline[tr_pipeline_pos].bt, TR_BT_SIZE); + tr_pipeline_pos++; + if (tr_pipeline_pos == TR_PIPELINE_SIZE) + tr_pipeline_pos = 0; +} + +static void tr_freehook __P ((__ptr_t, const __ptr_t)); +static void +tr_freehook (ptr, caller) + __ptr_t ptr; + const __ptr_t caller; +{ + if (ptr == NULL) + return; + __libc_lock_lock (lock); + tr_log(caller, ptr, 0, 0, TR_FREE ); + __libc_lock_unlock (lock); + if (ptr == mallwatch) + tr_break (); + __libc_lock_lock (lock); + __free_hook = tr_old_free_hook; + if (tr_old_free_hook != NULL) + (*tr_old_free_hook) (ptr, caller); + else + free (ptr); + __free_hook = tr_freehook; + __libc_lock_unlock (lock); +} + +static __ptr_t tr_mallochook __P ((__malloc_size_t, const __ptr_t)); +static __ptr_t +tr_mallochook (size, caller) + __malloc_size_t size; + const __ptr_t caller; +{ + __ptr_t hdr; + + __libc_lock_lock (lock); + + __malloc_hook = tr_old_malloc_hook; + if (tr_old_malloc_hook != NULL) + hdr = (__ptr_t) (*tr_old_malloc_hook) (size, caller); + else + hdr = (__ptr_t) malloc (size); + __malloc_hook = tr_mallochook; + + tr_log(caller, hdr, 0, size, TR_MALLOC); + + __libc_lock_unlock (lock); + + if (hdr == mallwatch) + tr_break (); + + return hdr; +} + +static __ptr_t tr_reallochook __P ((__ptr_t, __malloc_size_t, const __ptr_t)); +static __ptr_t +tr_reallochook (ptr, size, caller) + __ptr_t ptr; + __malloc_size_t size; + const __ptr_t caller; +{ + __ptr_t hdr; + + if (ptr == mallwatch) + tr_break (); + + __libc_lock_lock (lock); + + __free_hook = tr_old_free_hook; + __malloc_hook = tr_old_malloc_hook; + __realloc_hook = tr_old_realloc_hook; + if (tr_old_realloc_hook != NULL) + hdr = (__ptr_t) (*tr_old_realloc_hook) (ptr, size, caller); + else + hdr = (__ptr_t) realloc (ptr, size); + __free_hook = tr_freehook; + __malloc_hook = tr_mallochook; + __realloc_hook = tr_reallochook; + + tr_log(caller, hdr, ptr, size, TR_REALLOC); + + __libc_lock_unlock (lock); + + if (hdr == mallwatch) + tr_break (); + + return hdr; +} + + +#ifdef _LIBC +extern void __libc_freeres (void); + +/* This function gets called to make sure all memory the library + allocates get freed and so does not irritate the user when studying + the mtrace output. */ +static void +release_libc_mem (void) +{ + /* Only call the free function if we still are running in mtrace mode. */ + if (mallstream != NULL) + __libc_freeres (); +} +#endif + + +/* We enable tracing if either the environment variable MALLOC_TRACE + is set, or if the variable mallwatch has been patched to an address + that the debugging user wants us to stop on. When patching mallwatch, + don't forget to set a breakpoint on tr_break! */ + +void +mtrace () +{ +#ifdef _LIBC + static int added_atexit_handler; +#endif + char *mallfile; + + /* Don't panic if we're called more than once. */ + if (mallstream != NULL) + return; + +#ifdef _LIBC + /* When compiling the GNU libc we use the secure getenv function + which prevents the misuse in case of SUID or SGID enabled + programs. */ + mallfile = __secure_getenv (mallenv); +#else + mallfile = getenv (mallenv); +#endif + if (mallfile != NULL || mallwatch != NULL) + { + mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "w"); + if (mallstream != NULL) + { + /* Be sure it doesn't malloc its buffer! */ + setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE); + fprintf (mallstream, "= Start\n"); + tr_old_free_hook = __free_hook; + __free_hook = tr_freehook; + tr_old_malloc_hook = __malloc_hook; + __malloc_hook = tr_mallochook; + tr_old_realloc_hook = __realloc_hook; + __realloc_hook = tr_reallochook; + + tr_pipeline_pos = TR_PIPELINE_SIZE; + for(;tr_pipeline_pos--;) + { + tr_pipeline[tr_pipeline_pos].op = TR_NONE; + tr_pipeline[tr_pipeline_pos].ptr = NULL; + } + memset(tr_hashtable, 0, sizeof(void *)*TR_HASHTABLE_SIZE); +#ifdef _LIBC + if (!added_atexit_handler) + { + added_atexit_handler = 1; + atexit (release_libc_mem); + } +#endif + } + } +} + +void +muntrace () +{ + if (mallstream == NULL) + return; + + fprintf (mallstream, "= End\n"); + fclose (mallstream); + mallstream = NULL; + __free_hook = tr_old_free_hook; + __malloc_hook = tr_old_malloc_hook; + __realloc_hook = tr_old_realloc_hook; +} + + diff --git a/kompare/AUTHORS b/kompare/AUTHORS new file mode 100644 index 00000000..72381951 --- /dev/null +++ b/kompare/AUTHORS @@ -0,0 +1,2 @@ +Otto Bruggeman +John Firebaugh diff --git a/kompare/COPYING b/kompare/COPYING new file mode 100644 index 00000000..8832c184 --- /dev/null +++ b/kompare/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/kompare/ChangeLog b/kompare/ChangeLog new file mode 100644 index 00000000..d9db63b5 --- /dev/null +++ b/kompare/ChangeLog @@ -0,0 +1,423 @@ +Dec 27, 2004 : Jeff Snyder + * Fix for bug 95640 (nothing displayed when kompare is embedded in Ark + fixed by forcing the delivery of childEvents to komparesplitter at + the end of its constructor + +Dec 20, 2004 : Jeff Snyder + * Things that have happened since 3.3: + (this list is not complete) + * Look & feel changed + +Dec 20, 2004 : Jeff Snyder + * Things that were changed sometime between Nov 25, 2003 and KDE 3.3: + (this list is not complete) + * KompareConnectWidget became draggable, by replacing KompareViewFrame + with KompareSplitter. + +Dec 20, 2004 : Jeff Snyder + * This changelog seems to have been neglected for over a year now. I'll + try to retroactively fix this as and when I remember things that have + been fixed - but it'll probably never be complete and accurate for the + Dec 2003 - Nov 2004 period. I'll be making entries concering what i'm + doing with kompare from now onwards. + +Nov 25, 2003 : Otto Bruggeman + * Fix nasty looping to the end of the file when hitting previous difference on the first difference in the first file + +Nov 25, 2003 : Laurent Montel + * Fix memleak, QStringList is implicitly shared so no need for a reference, it is already a pointer to data thing + +Nov 23, 2003 : Otto Bruggeman + * Fixed version string (bug 68872) + * Fix for 68871 (added slotNextDifference to slotApplyDifference()) + * Fix for a crash: dont call blendOriginalIntoModelList with Kompare::ShowingDiff + +Nov 22, 2003 : Otto Bruggeman + * Fix to make the bugs.kde.org dialog pop up instead of sending a mail to John when pressing + Help->Report bug... Also added my homepage since it has always been kompare's home imo. + +Nov 22, 2003 : Otto Bruggeman + * Rework the blendFile method so it actually works and as a bonus is a bit faster + This introduces a new form of show entire file when comparing, one that works + And because of it, it saves files properly now because the entire file is now available even if + you have a single line change in a million line file with only 2 context lines in the diff. + +Nov 22, 2003 : Otto Bruggeman + * Remove the Show entire file option. It only causes problems at the moment + Fixes bug 68729 + +Nov 22, 2003 : Otto Bruggeman + * Commenting out a lot of debug output, it has served it's purpose well in levenshteintable.cpp + +Nov 21, 2003 : Otto Bruggeman + * Also expand tabs to spaces in strings without or after Commands (in the INLINE_DIFFERENCES + code path and yes commands is a shitty name for them but i cant think of something decent) + +Nov 21, 2003 : Otto Bruggeman + * Real Fix (tm) for activating the Swap source with destination action + +Nov 21, 2003 : Otto Bruggeman + * Fix empty line drawing in the INLINE_DIFFERENCES code path + +Nov 20, 2003 : Otto Bruggeman + * When swapping source with destination also change the windows caption and the statusbar text + * Make sure that when swapping and when there are changes, all changes that were made can be + saved, discarded or cancel the whole swap (strings are recycled from the queryClose method) + * Give a better parent to the KIO::NetAccess::download in komparemodellist.cpp + * Added some FIXME's for after the branching to make the urls appear in bold in the error message + * Make queryClose not use the isModified from the part but from the modellist + +Nov 20, 2003 : Otto Bruggeman + * Fix for activating the Swap Source with Destination action. + +Nov 19, 2003 : Otto Bruggeman + * Fixed bug 68570, it needed temp vars otherwise it was overwriting source with destination and then + overwriting that destination with source which was just changed into destination + +Nov 17, 2003 : Otto Bruggeman + * Fix for empty -x and -X arguments. + * Fix bugs 58858 and 58531 by using Kompare::Custom instead of Kompare::Default + * Fix last selected url in the kurlcomboboxes + * Fix for inline differences when there is only 1 char left that still needs to be drawn + * Remove support for the -a Treat all files as text diff option. This caused all sorts of weird crashes + when parsing the diff output now with the custom options. + * Move the per preference page code in the diffprefs constructor into seperate methods per page + +Nov 14, 2003 : Otto Bruggeman + * Fix to make Kompare listen to the kdisplayFontChanged signal and set the font properly and redraw with the new font. + Found by David Faure. + +Nov 09, 2003 : Otto Bruggeman + * Implemented inline differences (deactivated until KDE3.2 has been branched) + * added support for the -x and -X options to diff (deactivated until KDE3.2 has been branched) + * Various other code cleanups/reindenting + +Nov 09, 2003 : Otto Bruggeman + * Code cleanups + +Nov 02, 2003 : Otto Bruggeman + * Fixed some more scrolling problems + lastItem->scrollId(), add lastItem->maxHeight() and substract the minScrollId() + That is the maxScrollId i need in the QScrollBar, took me long enough... + +Oct 05, 2003 : Otto Bruggeman + * Fixed the scrolling problems, a stupid regression i introduced, i cant simplify mathematic expressions apparently + * Added an implementation for double clicking a difference in the view, but it is not properly connected yet + void contentsMouseDoubleClickEvent ( QMouseEvent* ); + * Fixed embedding in Konqueror by implementing openURL() + * Removed m_maxScrollId, it is not necessary and only costs time, QScrollView::contentsHeight() does the same + * Fixed some more warnings about unused variables + * Fixed the initial drawing of the vertical and horizontal scrollbar + +Oct 04, 2003 : Otto Bruggeman + * Added a call to m_modelList->openDirAndDiff to openDirAndDiff + * Fixed some error strings by swapping the %# thingies + * Added some useless debug output + * Fixed KompareModelList::openDirAndDiff to use the right models variable (m_models instead of models) + +Oct 03, 2003 : Otto Bruggeman + * Fixed ApplyAll and UnApplyAll, stupid copy and paste error + * Fixed some warnings about signed and unsigned + * Fixed some warnings about unused variables + * Fixed some redrawing issues in the connection widget + +Sep 27, 2003 : Otto Bruggeman + * Fixed the redrawing problems in the connect widget with a QTimer::singleShot() + * Undid a stupid commit that changed the keyboard shortcuts for next and previous difference + * Fixed another bug in the navigation part that made it emit a signal twice + * Fixed a bug in the listview drawing, still one left that i cant seem to solve :( + +Sep 27, 2003 : Otto Bruggeman + * Moved the apply and navigation actions into the komparemodellist + * Fixed Ingo's problem with the next and prev difference KActions + +Sep 26, 2003 : Otto Bruggeman + * Added a struct Info in the Kompare namespace. This one contains all the info about what kompare is doing + * Fixed splitting the path string in diffmodel + * Fixed showing the path in komparenavtreepart in the directory listviews + +Sep 24, 2003 : Otto Bruggeman + * Fixes opening diffs, comparing files after moving all that code around + +Sep 23, 2003 : Otto Bruggeman + * Moved a lot of url downloading to the kompare part and moved the opening and reading of the downloads to komparemodellist + +Sep 22, 2003 : Otto Bruggeman + * Added openStdin() to KompareShell + * Fixed stupid implicit conversion from QString to QStringList in kompare_part.cpp + * Added openDiff( QStringList ) to the interface and to the part + +Sep 14, 2003 : Otto Bruggeman + * Fixed exit status of the kompare process + +Sep 13, 2003 : Otto Bruggeman + * Removed some files that apparently came back after the merge + +Sep 07, 2003 : Otto Bruggeman + * Some changes to the interface. Made the copy ctor and assignment operator + and added a private d-pointer + * Removed the use of all deprecated methods and replaced them with undeprecated ones :) + +Sep 02, 2003 : Scott Wheeler + * Made the interface pure virtual + +Sep 01, 2003 : Scott Wheeler + * Fixed constness of the KompareModelList constructor + * Fixed another 2 warnings about comapring signed with unsigned ints + * Fixed the initialization of the difault var + +Aug 27, 2003 : Otto Bruggeman + * After shitloads of trouble here finally some fixes for the stupid desktop + file stuff + * Fixes for when there are not enough args for a certain commandline option. + +Aug 22, 2003 : Otto Bruggeman + * Fixed converting tabs to spaces in the view, i totally screwed up + * View settings now get applied to the view after pressing ok. + (Maybe i should make them apply on APlly instead of OK) + +Aug 13, 2003 : Otto Bruggeman + * Komkommertijd :) InitialPreference=10 for kompare.desktop as + requested + +Aug 10, 2003 : Otto Bruggeman + * Backported Helge Deller's changes from head to make_it_cool + (kompare_shell.cpp 1.33 -> 1.34). This is about roaming user fixes. + Thanks Helge ! + +Jul 19, 2003 : Otto Bruggeman + * Backported Ingo Klocker's changes from head to make_it_cool + (kompare_shell.cpp 1.34 -> 1.35). This is about being able to + configure the shortcuts from kompare_part as well. Thanks Ingo ! + +Jun 29, 2003 : Otto Bruggeman + * Fixed bug 58144 by adding a check for comparing dirs, in that case + destinationURL is a directory and not a file name so we need to + recreate the filename. This involved changing some code to use a + different enum value, so i hope i did it the right way, session + management may be broken now when the session was stored with 3.1.2 + and restarted with 3.1.3. But that is unfortunately unfixable with a + kconf_update script. + +Jun 29, 2003 : Otto Bruggeman + * Removed a lot of commented code since it is no longer used and will + never be used again. + * Added 2 methods to the interface: openDiff3(KURL) and + openDiff3(QStringList) + * Fixed context diff parsing as indicated in bugreport 57774 + (the example works now, hope there are no regressions) + * Removed all references to MiscSettings and MiscPrefs. + These classes will disappear RSN. + * Fixed the history saving of the urls in the kompare dialog + * Parser is no longer a static class but one that needs to be + instanciated + * Added ViewSettings to KompareProcess, maybe it is better to merge the + diff and view settings into one class. + +May 3, 2003 : Otto Bruggeman + * Implemented support for -I in the regular diff options (the one in + the kompare options dialog) + * Fixed the braindamage i created in main.cpp so that kompare no + longer stalls because of a missing mainwindow + * Made the kcomparedialog more generic and renamed it to + kompareurldialog so i can reuse it for blending too + * Removed some braindamage in the kompare/Makefile.am + * Some compile fixes because of changes to the CXXFLAGS + (QRegExp::match cant be used anymore, and some other old style stuff) + * Added an action to the menu for blending + * moved Open file (or in this case Open Diff) to the top of the file + menu + * Fixed the accel conflict in the file menu between open diff and + compare files + +Apr 30, 2003 : Otto Bruggeman + * Implemented blending of a diff file with the original file + * Renamed General* View* (more appropriate) + * Renamed m_models into m_modelList since it is more appropriate in komparepart + * Small fixes to the view, but they break more than they fix :( + * Added commandline options for comparing, opening a diff file and + blending + +Apr 20, 2003 : Otto Bruggeman + * Fixed bug 54264 with a statusbar that gets too wide when long + filenames are used + * Fixed the missing endline problem in the parser (bug 56552) + * Fixed all copyright years (probably too many but hey i'll change + those files some time this year so it will be valid :P) + * Added support for using a different diff program (Bug 55573) + * Added support for using a different tabsize in the viewer (Bug 38776) + * The interface is now final i guess so this fixes bug 42849, not + every method is implemented but i'll get to them eventually. + +Apr 19, 2003 : Otto Bruggeman + * Fixed bug 56322 where openURL did not clear the models when called + again with a new diff + +Aug 9, 2002 : Otto Bruggeman + * Fixed the whatsthis text for the compare button in the compare dialog + * Fixed the history of the comboboxes in the compare dialog + * Put the komparemodellist and all needed classes in a Diff2 namespace + * Implemented a better parser design (see parser.cpp/h) + * Removed the need to directly link to the komparepart for the shellapp + * Removed the need to link directly to the komparepart for the navigationpart + * Added support for perforce diffs in the new Parser classes + * Added a push design for the modified status instead of a pull design + * Added an interface to the Komparepart so people can use that to + reuse the komparepart + +Jul 15, 2002 : Otto Bruggeman + * Fixed normal diff a bit more, filenames dont work yet + * Removed some code duplication + * Fixed diff output parsing with Common subdirectories in it + * Fixed Copyright years in the about box (thanks Carsten Niehaus) + * Removed the KShellProcess and replaced it with a KProcess + +Feb 18, 2002 : Otto Bruggeman + * Fixed scrolling with a wheel mouse in the kompare(list)view and + connectwidget and added a config option for the number of lines + that is scrolled per wheelscroll. + * Fixed the history somehow in the compare dialog. + * Implemented the separate directory/file widget. + * Implemented reading from stdin by using - as file on the commandline. + * Partly implemented a better way for ed and rcs parsing, i'll + improve this before KDE 3.0 is released + +Jan 10, 2002 : Otto Bruggeman + Comparing directories works now :) You can select them from the begin + dialog, and select a directory and then press ok. It will enter the + directory but dont select a file so it keeps the directory. + Known bug here is that directories need a trailing slash :( + +Oct 07, 2001 : Otto Bruggeman + Fix crash when part is not found, basically dont use kapp->quit() + but use exit(int). Would be interested to know why it crashes though, + the bt gave nothing meaningful here. I should have compiled kompare with + debug code. + +Sep 17/18, 2001 : Otto Bruggeman + Fixed some stuff dont know what anymore (writing this on oct 7) + Probably some more fixes for the klibloader. + +Sep 17, 2001 : Otto Bruggeman + Moved to kdesdk and renamed to kompare with preservation of history. + Changed almost every occurence of kdiff to kompare (not in this file). + +Sep 08, 2001 : Otto Bruggeman + Removed the qt3back dir, changed everything over to qt3, + qlist->qptrlist, qlistconstiterator->qptrlistconstiterator + +Jul 29, 2001 : John Firebaugh + Add some tests. + Add the qregexp3 backport. + Use qregexp3 for diff parsing -- soooo much cleaner. + All the diff options work. + +Jul 28, 2001 : John Firebaugh + Directories can be selected in the compare dialog + New base clase KDiff, holds some common stuff + Use an enum for format in preferences + Implement a save options dialog, displayed at "Save .diff" + The diff can be run in any directory, the paths to source + and destination will be automatically determined from this. + Save all. + +Jul 25, 2001 : John Firebaugh + Prompt to save changes on close + Show [modified] caption + Clean up internal save mechanism + +Jul 14, 2001 : John Firebaugh + New menu item "Swap source and destination". + Make empty selection work. + +Jul 13, 2001 : John Firebaugh + Text view now works in compare mode. + Fix clicking difference to select it. + Don't scroll to difference when clicking to select it. + Give the diff view a nice frame. + +Jul 12, 2001 : Otto Bruggeman + Stats work now, maybe they need more info but i dont know what yet. + Will think some more about it. + +Jul 12, 2001 : John Firebaugh + When comparing files, you can apply or unapply changes and save + the result. + New menu item "Show Text View" (loads the diff in embedded text viewer). + Better status notification. + Set the window caption when comparing. + +Jun 27, 2001 : John Firebaugh + Ported main view to QListView + Remove obsolete files + Clicking a difference in the main view selects it + Better scrolling + +Jun 24, 2001 : John Firebaugh + Coverted to dock window and added navigation tree in a dock. + Multiple file diffs are now supported. Each file will show up + as an item in the tree, with differences as children. + +Jun 22, 2001 : Otto Bruggeman + Tried implementing rcs and ed but they dont work atm, same for show + diffstats, will fix that asap. + +May 22, 2001 : John Firebaugh + Reworking of most of the view code. Looks pretty. + +May 18, 2001 : Otto Bruggeman + Context seems to work, implemented saving... might have some problems + left (saving that is) + +May 15, 2001 : John Firebaugh + Make the settings work for all windows. Probably some more changes :) + +May 14, 2001 : Otto Bruggeman + context diff does not work atm, there is some problem with the separa- + tion of old and new. Maybe the old and new needs to be reintegrated. + I fixed some functions and now diffmodel does no longer need static + functions. All loading is done from the kdiffpart and that is where + save should go as well. Removed determineDiffFormat because it is not + needed anymore. + +May 13, 2001 : Otto Bruggeman + contextdiff is better implemented it finds all stuff in the diff atm + but it does not work. + +May 04, 2001 : Otto Bruggeman + cleaned up the code by moving the part to a subdir + halfassed implementation of contextdiff, will update later today + +Apr 10, 2001 : John Firebaugh + use new model/view architecture (not completely implemented yet) + NOTE: it will (should) compile, but you won't see any differences... a + work in progress + +Apr 05, 2001 : Otto Bruggeman + Implemented the ability to move from chuck to chunk in the htmlview + Cleaned up the preferences, squashed a few bugs + +Apr 04, 2001 : Otto Bruggeman + Normal format works as well + +Apr 04, 2001 : Otto Bruggeman + Finally implemented the preferences menu... i still lack some nice + icons for it but that will be solved in the near future... + +Mar 25, 2001 : Otto Bruggeman + Moved the application icons to the pics dir + +Mar 20, 2001 : Otto Bruggeman + Fixed a stupid bug that caused the last line in the rightview not + to be colored. + Implemented slots for using the KHistoryCombo in the views to select + files with. + Still a nasty bug with regard to the initial directory in the + KFileDialog, needs to be fixed asap but i dont know the cause. + Still an error in the historylist and completionlist items. They are + not shown correctly. + +Mar 19, 2001 : Otto Bruggeman + Added most of the preferences dialog + Some speed improvements + Some fixes to use the last used directory in KFileDialog diff --git a/kompare/DESIGN b/kompare/DESIGN new file mode 100644 index 00000000..fde8a5c9 --- /dev/null +++ b/kompare/DESIGN @@ -0,0 +1,35 @@ +Kompare General design: + +Kompare is split up into 4 parts: +- A shell around the parts +- A library with the modellist and the parser +- The navigation tree which uses the library +- The view part that also uses the library + +The diffmodel is comparable to a document and the view part is comparable +to the view and the komparemodellist is comparable to a documentmanager. +The navtree can be viewed as a document view manager. +The model is fully separated from the view and all communication goes +through signals and slots. The view gets a model that contains all differences +for the compared files A and B. The view gets this model from the modellist, +the central entity in the part. + +There is an interface to the komparepart that can be used in other programs, +simply link to the libkompareinterface.la and call its methods after you have +instanciated a komparepart. There is also a "hidden" signal and slot interface +that can be connected to from the shell app to get some information, and an +interface for communication between the navigation part and the komparepart. + +There is no need to use the interface for the communication between the +navigation and kompare parts in the shell app. + +Kompare has some debug areas: + +8100 kompare +8101 kompare (libs) +8102 kompare (shell) +8103 kompare (part) +8104 kompare (list view) +8105 kompare (nav view) +8106 kompare (connect widget) + diff --git a/kompare/Makefile.am b/kompare/Makefile.am new file mode 100644 index 00000000..bf5297b5 --- /dev/null +++ b/kompare/Makefile.am @@ -0,0 +1,43 @@ +SUBDIRS = interfaces libdiff2 libdialogpages komparenavtreepart komparepart pics + +INCLUDES = \ + -I$(srcdir)/libdiff2 \ + -I$(srcdir)/libdialogpages \ + -I$(srcdir)/komparenavtreepart \ + -I$(srcdir)/komparepart \ + -I$(srcdir)/interfaces \ + $(all_includes) + +noinst_HEADERS = kompare_shell.h kompareurldialog.h + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +messages: rc.cpp + $(EXTRACTRC) */*.rc */*.ui >> rc.cpp + $(XGETTEXT) `find . -name "*.cpp" -o -name "*.h"` -o $(podir)/kompare.pot + +######################################################################### +# APPLICATION SECTION +######################################################################### +# this is the program that gets installed. it's name is used for all +# of the other Makefile.am variables +bin_PROGRAMS = kompare + +# the application source, library search path, and link libraries +kompare_SOURCES = main.cpp kompare_shell.cpp kompareurldialog.cpp +kompare_LDFLAGS = $(all_libraries) +kompare_LDADD = $(LIB_KPARTS) \ + $(top_builddir)/kompare/interfaces/libkompareinterface.la \ + $(top_builddir)/kompare/libdialogpages/libdialogpages.la \ + -lktexteditor + +# this is where the desktop file will go +xdg_apps_DATA = kompare.desktop + +# this is where the shell's XML-GUI resource file goes +shellrcdir = $(kde_datadir)/kompare +shellrc_DATA = kompareui.rc + +kompareservicedir = $(kde_servicetypesdir) +kompareservice_DATA = komparenavigationpart.desktop kompareviewpart.desktop diff --git a/kompare/README b/kompare/README new file mode 100644 index 00000000..af036f57 --- /dev/null +++ b/kompare/README @@ -0,0 +1,16 @@ +Kompare 2.0 + +Kompare is a program to view the differences between files. Features include: + + * comparison of files or directories via a graphical interface + * bezier-based connection widget lets you see both source and destination + as they really appear + * graphical viewing of patch files in normal, context, unified and + diff formats + * interactive application of differences + * full network transparency + * ability to view plain-text diff output in embedded viewer + * easy navigation of multiple-file diffs with dockable navigation tree + * graphical interface to commonly used diff command line options + * switch source and destination with one command + * diff statistics diff --git a/kompare/TODO b/kompare/TODO new file mode 100644 index 00000000..e87a574f --- /dev/null +++ b/kompare/TODO @@ -0,0 +1,34 @@ +!!! Must do before a merge back into HEAD !!! +* Write a kconfupdate script to convert the kconfigfile + * the diff options now have their own group + * the view options now have their own group + * the Recent Files group has been renamed to Recent Compare Files + +Very important: +* Make sure unified and context actually work again ! +* Implement the rest of the interface (compare3 etc.) +* Make sure the rest works too ! Still some problems parsing some files +* Other various things that need to be fixed asap (before 3.2 is released) + +In descending order of importance: +* Add a kompare manual/document +* add support for handediting conflicts +* session management (should almost work) +* implement normal, ed and rcs format (normal got busted) +* merge text view GUI +* clicking in the connect widget should select a difference (tricky!) +* add support for incremental patch saving (i'll explain if anyone wants to know) +* Add support for diff3 (that is comparing 3 files at once) + +Special requests: +Puetzk: +* Editing the TO pane +Falk: +* Allow only part of a change to be applied to the other file +Harlekin: +* Add search functionality, either in source, dest or both at the same time +HarryF: +* emit clicked() signal when on a diff with line number +aseigo: +* merge from dest to source instead of only from source to dest +(Workaround: swap source with destination and then apply the differences) diff --git a/kompare/interfaces/Makefile.am b/kompare/interfaces/Makefile.am new file mode 100644 index 00000000..ab8f9b93 --- /dev/null +++ b/kompare/interfaces/Makefile.am @@ -0,0 +1,8 @@ +INCLUDES = $(all_includes) + +lib_LTLIBRARIES = libkompareinterface.la + +libkompareinterface_la_LDFLAGS = -no-undefined $(all_libraries) +libkompareinterface_la_SOURCES = kompareinterface.cpp +libkompareinterface_la_LIBADD = $(LIB_QT) + diff --git a/kompare/interfaces/kompareinterface.cpp b/kompare/interfaces/kompareinterface.cpp new file mode 100644 index 00000000..1522bad0 --- /dev/null +++ b/kompare/interfaces/kompareinterface.cpp @@ -0,0 +1,60 @@ +// Blah blah standard LGPL license +// Copyright 2002-2004, Otto Bruggeman + +#include "kompareinterface.h" + +class KompareInterfacePrivate +{ +public: + KompareInterfacePrivate(); + ~KompareInterfacePrivate(); + KompareInterfacePrivate( const KompareInterfacePrivate& ); + KompareInterfacePrivate& operator=( const KompareInterfacePrivate& ); + +protected: + // Add all variables for the KompareInterface class here and access them through the kip pointer +}; + +KompareInterfacePrivate::KompareInterfacePrivate() +{ +} + +KompareInterfacePrivate::~KompareInterfacePrivate() +{ +} + +KompareInterfacePrivate::KompareInterfacePrivate( const KompareInterfacePrivate& /*kip*/ ) +{ +} + +KompareInterfacePrivate& KompareInterfacePrivate::operator=(const KompareInterfacePrivate& /*kip*/ ) +{ + return *this; +} + +KompareInterface::KompareInterface() +{ + kip = new KompareInterfacePrivate(); +} + +KompareInterface::~KompareInterface() +{ + delete kip; +} + +KompareInterface::KompareInterface( const KompareInterface& ki ) +{ + kip = new KompareInterfacePrivate( *(ki.kip) ); +} + +KompareInterface& KompareInterface::operator=( const KompareInterface& ki ) +{ + kip = ki.kip; + return *this; +} + +void KompareInterface::setEncoding( const QString& encoding ) +{ + m_encoding = encoding; +} + diff --git a/kompare/interfaces/kompareinterface.h b/kompare/interfaces/kompareinterface.h new file mode 100644 index 00000000..b9c0fcda --- /dev/null +++ b/kompare/interfaces/kompareinterface.h @@ -0,0 +1,105 @@ +// blah blah standard LGPL license +// Copyright 2002-2003, Otto Bruggeman + +#ifndef _KOMPARE_INTERFACE_H +#define _KOMPARE_INTERFACE_H + +#include +#include +#include + +class KConfig; +class KURL; + +class KompareInterfacePrivate; + +class KDE_EXPORT KompareInterface +{ +public: + KompareInterface(); + virtual ~KompareInterface(); + +protected: + KompareInterface( const KompareInterface& ); + KompareInterface& operator=(const KompareInterface& ); + +public: + /** + * Open and parse the diff file at url. + */ + virtual bool openDiff( const KURL& diffUrl ) = 0; + + /** + * Open and parse the supplied diff output + */ + virtual bool openDiff( const QString& diffOutput ) = 0; + + /** + * Open and parse the diff3 file at url. + */ + virtual bool openDiff3( const KURL& diff3Url ) = 0; + + /** + * Open and parse the supplied diff3 output + */ + virtual bool openDiff3( const QString& diff3Output ) = 0; + + /** + * Compare, with diff, source with destination, can also be used if you dont + * know what source and destination are. The part will try to figure out what + * they are (directory, file, diff output file) and call the + * appropriate method(s) + */ + virtual void compare( const KURL& sourceFile, const KURL& destinationFile ) = 0; + + /** + * Compare, with diff, source with destination files + */ + virtual void compareFiles( const KURL& sourceFile, const KURL& destinationFile ) = 0; + + /** + * Compare, with diff, source with destination directories + */ + virtual void compareDirs ( const KURL& sourceDir, const KURL& destinationDir ) = 0; + + /** + * Compare, with diff3, originalFile with changedFile1 and changedFile2 + */ + virtual void compare3Files( const KURL& originalFile, const KURL& changedFile1, const KURL& changedFile2 ) = 0; + + /** + * This will show the file and the file with the diff applied + */ + virtual void openFileAndDiff( const KURL& file, const KURL& diffFile ) = 0; + + /** + * This will show the directory and the directory with the diff applied + */ + virtual void openDirAndDiff ( const KURL& dir, const KURL& diffFile ) = 0; + + /** + * This will set the encoding to use for all files that are read or for the diffoutput + */ + virtual void setEncoding( const QString& encoding ); + +public: + /** + * Warning this should be in class Part in KDE 4.0, not here ! + * Around that time the methods will disappear here + */ + virtual int readProperties( KConfig* config ) = 0; + virtual int saveProperties( KConfig* config ) = 0; + + /** + * Warning this should be in class ReadWritePart in KDE 4.0, not here ! + * Around that time the method will disappear here + */ + virtual bool queryClose() = 0; + +protected: + // Add all variables to the KompareInterfacePrivate class and access them through the kip pointer + KompareInterfacePrivate* kip; + QString m_encoding; +}; + +#endif /* _KOMPARE_INTERFACE_H */ diff --git a/kompare/kompare.desktop b/kompare/kompare.desktop new file mode 100644 index 00000000..88b7fcc4 --- /dev/null +++ b/kompare/kompare.desktop @@ -0,0 +1,74 @@ +# KDE Config File +[Desktop Entry] +Type=Application +Name=Kompare +Name[af]=K-vergelyk +Name[eo]=Komparilo +Name[hi]=काम्पेयर +Name[lv]=Salīdzināt +Name[ne]=तुलना +Name[pa]=ਕੇ-ਤੁਲਨਾ +Name[ta]=கோம்பெர் +Name[th]=เปรียบเทียบ +Name[tr]=Karşılaştır +GenericName=Diff/Patch Frontend +GenericName[af]=Diff/Lap Voorprogram +GenericName[bs]=Frontend za Diff/Patch +GenericName[ca]=Interfície per a diff/patch +GenericName[cs]=Rozhraní pro Diff/Patch +GenericName[cy]=Blaen Gwahaniaethau/Clytiau +GenericName[da]=Diff/patch-grænseflade +GenericName[de]=Oberfläche für Diff und Patch +GenericName[el]=Πρόγραμμα Diff/Patch +GenericName[eo]=Fasado por la programoj "diff" kaj "patch" +GenericName[es]=Interfaz Diff/Patch +GenericName[et]=Diff/patch kasutajaliides +GenericName[eu]=Desberdintasun/Adabaki interfazea +GenericName[fa]=پایانۀ Diff/کژنه +GenericName[fi]=Diff/Patch-käyttöliittymä +GenericName[fr]=Interface graphique pour Diff et Patch +GenericName[ga]=Comhéadan Diff/Patch +GenericName[gl]=Interface para Diff/Patch +GenericName[he]=ממשק ל-Diff/Patch +GenericName[hi]=डिफ/पैच फ्रन्टएण्ड +GenericName[hr]=Sučelje za Diff/Patch +GenericName[hu]=Grafikus diff/patch +GenericName[is]=Myndrænt viðmót á Diff/Patch +GenericName[it]=Interfaccia per diff e patch +GenericName[ja]=Diff/Patch フロントエンド +GenericName[kk]=Diff/Patch интерфейсі +GenericName[lt]=Diff/Patch naudotojo sąsaja +GenericName[lv]=Diff/Patch Frontends +GenericName[ms]=Bahagian Depan Beza/Tampal +GenericName[nb]=Diff-/Patch-grensesnitt +GenericName[nds]=Böversiet för "diff" un "patch" +GenericName[ne]=Diff/Patch फ्रन्टइन्ड +GenericName[nl]=Diff/Patch-hulpprogramma +GenericName[nn]=Diff-/Patch-grensesnitt +GenericName[pa]=Diff/Patch ਮੁੱਖ +GenericName[pl]=Interfejs dla programów diff i patch +GenericName[pt]=Interface do Diff/Patch +GenericName[pt_BR]=Interface para Diferenças (diff)/Correções +GenericName[ro]=Interfaţă grafică pentru "diff" şi "patch" +GenericName[ru]=Сравнение файлов +GenericName[sk]=Rozhranie Diff/Patch +GenericName[sl]=Vmesnik za diff/patch +GenericName[sr]=Интерфејс за Diff/Patch +GenericName[sr@Latn]=Interfejs za Diff/Patch +GenericName[sv]=Gränssnitt för Diff/Patch +GenericName[ta]=டிப்/ஒட்டு முன்பகுதி +GenericName[tg]=Утилитаи баробаркунии файлҳо +GenericName[th]=ฟรอนต์เอนด์ของ Diff/Patch +GenericName[tr]=Diff/Yama Önyüzü +GenericName[uk]=Інтерфейс до diff/patch +GenericName[zh_CN]=Diff/Patch 前端 +GenericName[zh_TW]=Diff/Patch 前端 +GenericName[zu]=Diff/PatchIsiqalo sokugcina +MimeType=text/x-diff; +Exec=kompare -caption "%c" %i %m -o %U +Icon=kompare +DocPath=kompare/index.html +Terminal=false +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Development; +InitialPreference=10 diff --git a/kompare/kompare_shell.cpp b/kompare/kompare_shell.cpp new file mode 100644 index 00000000..3840e272 --- /dev/null +++ b/kompare/kompare_shell.cpp @@ -0,0 +1,487 @@ +/*************************************************************************** + kompare_shell.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kompare_part.h" +#include "komparenavtreepart.h" +#include "kompareurldialog.h" + +#include "kompare_shell.h" + +#define ID_N_OF_N_DIFFERENCES 1 +#define ID_N_OF_N_FILES 2 +#define ID_GENERAL 3 + +KompareShell::KompareShell() + : KParts::DockMainWindow( 0L, "KompareShell" ), + m_textViewPart( 0 ), + m_textViewWidget( 0 ) +{ + if ( !initialGeometrySet() ) + resize( 800, 480 ); + + // set the shell's ui resource file + setXMLFile("kompareui.rc"); + + // then, setup our actions + setupActions(); + setupStatusBar(); + + KTrader::OfferList offers = KTrader::self()->query( "text/x-diff", + "Kompare/ViewPart", QString::null, QString::null ); +#ifdef NDEBUG + for( int i = 0; i < offers.count(); i++ ) + { + kdDebug(8102) << "One kservicetype checked..." << endl; + KService::Ptr ptr2 = *(offers.at( i )); + QStringList list = ptr2->serviceTypes(); + for ( QStringList::Iterator it2 = list.begin(); it2 != list.end(); ++it2 ) + kdDebug(8102) << *it2 << endl; + } +#endif + if ( offers.count() == 0 ) + { + KMessageBox::error(this, i18n( "Could not find our KompareViewPart." ) ); + exit(1); + } + + KService::Ptr ptr = offers.first(); + + KLibFactory *mainViewFactory = KLibLoader::self()->factory( ptr->library().ascii() ); + if (mainViewFactory) + { + m_mainViewDock = createDockWidget( "View", kapp->icon() ); + // now that the Part is loaded, we cast it to a KomparePart to get + // our hands on it + m_viewPart = static_cast(mainViewFactory->create(m_mainViewDock, + "kompare_part", "KParts::ReadWritePart" )); + + if ( m_viewPart ) + { + m_mainViewDock->setWidget( m_viewPart->widget() ); + setView( m_mainViewDock ); + setMainDockWidget( m_mainViewDock ); + + // and integrate the part's GUI with the shell's + createGUI(m_viewPart); + } + } + else + { + // if we couldn't load our Part, we exit since the Shell by + // itself can't do anything useful + KMessageBox::error(this, i18n( "Could not load our KompareViewPart." ) ); + exit(2); + } + + offers.clear(); + offers = KTrader::self()->query( "text/x-diff", "KParts/ReadOnlyPart", "'Kompare/NavigationPart' in ServiceTypes", QString::null ); + if ( offers.count() == 0 ) + { + KMessageBox::error(this, i18n( "Could not find our KompareNavigationPart." ) ); + exit(3); + } + + ptr = offers.first(); + + KLibFactory *navTreeFactory = KLibLoader::self()->factory( ptr->library().ascii() ); + if (navTreeFactory) + { + m_navTreeDock = createDockWidget( "Navigation", kapp->icon() ); + + m_navTreePart = static_cast(navTreeFactory->create(m_navTreeDock, + "komparenavtreepart", "KParts::ReadOnlyPart" )); + + if ( m_navTreePart ) + { + m_navTreeDock->setWidget( m_navTreePart->widget() ); + m_navTreeDock->manualDock( m_mainViewDock, KDockWidget::DockTop, 20 ); + } + } + else + { + // if we couldn't load our Part, we exit since the Shell by + // itself can't do anything useful + KMessageBox::error(this, i18n( "Could not load our KompareNavigationPart." ) ); + exit(4); + } + + // Hook up the inter part communication + connect( m_viewPart, SIGNAL( modelsChanged(const Diff2::DiffModelList*) ), + m_navTreePart, SLOT( slotModelsChanged( const Diff2::DiffModelList*) ) ); + + connect( m_viewPart, SIGNAL( kompareInfo(Kompare::Info*) ), + m_navTreePart, SLOT( slotKompareInfo(Kompare::Info*) ) ); + + connect( m_navTreePart, SIGNAL( selectionChanged(const Diff2::DiffModel*, const Diff2::Difference*) ), + m_viewPart, SIGNAL( selectionChanged(const Diff2::DiffModel*, const Diff2::Difference*) ) ); + connect( m_viewPart, SIGNAL( setSelection(const Diff2::DiffModel*, const Diff2::Difference*) ), + m_navTreePart, SLOT( slotSetSelection(const Diff2::DiffModel*, const Diff2::Difference*) ) ); + + connect( m_navTreePart, SIGNAL( selectionChanged(const Diff2::Difference*) ), + m_viewPart, SIGNAL( selectionChanged(const Diff2::Difference*) ) ); + connect( m_viewPart, SIGNAL( setSelection(const Diff2::Difference*) ), + m_navTreePart, SLOT( slotSetSelection(const Diff2::Difference*) ) ); + + // This is the interpart interface, it is signal and slot based so no "real" nterface here + // All you have to do is connect the parts from your application. + // These just point to the method with the same name in the KompareModelList or get called + // from the method with the same name in KompareModelList. + + // There is currently no applying possible from the navtreepart to the viewpart + connect( m_viewPart, SIGNAL(applyDifference(bool)), + m_navTreePart, SLOT(slotApplyDifference(bool)) ); + connect( m_viewPart, SIGNAL(applyAllDifferences(bool)), + m_navTreePart, SLOT(slotApplyAllDifferences(bool)) ); + connect( m_viewPart, SIGNAL(applyDifference(const Diff2::Difference*, bool)), + m_navTreePart, SLOT(slotApplyDifference(const Diff2::Difference*, bool)) ); + + // Hook up the KomparePart -> KompareShell communication + connect( m_viewPart, SIGNAL( setStatusBarModelInfo( int, int, int, int, int ) ), + this, SLOT( slotUpdateStatusBar( int, int, int, int, int ) ) ); + connect( m_viewPart, SIGNAL( setStatusBarText(const QString&) ), + this, SLOT( slotSetStatusBarText(const QString&) ) ); + + // Read basic main-view settings, and set to autosave + setAutoSaveSettings( "General Options" ); +} + +KompareShell::~KompareShell() +{ +} + +bool KompareShell::queryClose() +{ + return m_viewPart->queryClose(); +} + +void KompareShell::openDiff(const KURL& url) +{ + kdDebug(8102) << "Url = " << url.prettyURL() << endl; + m_diffURL = url; + m_viewPart->openDiff( url ); +} + +void KompareShell::openStdin() +{ + kdDebug(8102) << "Using stdin to read the diff" << endl; + QFile file; + file.open( IO_ReadOnly, stdin ); + QTextStream stream( &file ); + + QString diff = stream.read(); + + file.close(); + + m_viewPart->openDiff( diff ); + +} + +void KompareShell::compare(const KURL& source,const KURL& destination ) +{ + m_sourceURL = source; + m_destinationURL = destination; + + m_viewPart->compare( source, destination ); +} + +void KompareShell::blend( const KURL& url1, const KURL& diff ) +{ + m_sourceURL = url1; + m_destinationURL = diff; + + m_viewPart->openDirAndDiff( url1, diff ); +} + +void KompareShell::setupActions() +{ + KAction* open = KStdAction::open(this, SLOT(slotFileOpen()), actionCollection()); + open->setText( i18n( "&Open Diff..." ) ); + new KAction( i18n("&Compare Files..."), "fileopen", Qt::CTRL + Qt::Key_C, + this, SLOT(slotFileCompareFiles()), + actionCollection(), "file_compare_files" ); + new KAction( i18n("&Blend URL with Diff..."), "fileblend", Qt::CTRL + Qt::Key_B, + this, SLOT(slotFileBlendURLAndDiff()), + actionCollection(), "file_blend_url" ); + KStdAction::quit( this, SLOT( slotFileClose() ), actionCollection() ); + +#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,90) + createStandardStatusBarAction(); +#endif + setStandardToolBarMenuEnabled(true); + m_showTextView = new KToggleAction( i18n("Show T&ext View"), 0, this, SLOT(slotShowTextView()), + actionCollection(), "options_show_text_view" ); + m_showTextView->setCheckedState(i18n("Hide T&ext View")); + + KStdAction::keyBindings(this, SLOT(optionsConfigureKeys()), actionCollection()); + KStdAction::configureToolbars(this, SLOT(optionsConfigureToolbars()), actionCollection()); +} + +void KompareShell::setupStatusBar() +{ + // Made these entries permanent so they will appear on the right side + statusBar()->insertItem( i18n(" 0 of 0 differences "), ID_N_OF_N_DIFFERENCES, 0, true ); + statusBar()->insertItem( i18n(" 0 of 0 files "), ID_N_OF_N_FILES, 0, true ); + + m_generalLabel = new KSqueezedTextLabel( "", 0, "general_statusbar_label" ); + statusBar()->addWidget( m_generalLabel, 1, false ); + m_generalLabel->setAlignment( Qt::AlignLeft ); +} + +void KompareShell::slotUpdateStatusBar( int modelIndex, int differenceIndex, int modelCount, int differenceCount, int appliedCount ) +{ + kdDebug(8102) << "KompareShell::updateStatusBar()" << endl; + + QString fileStr; + QString diffStr; + + if ( modelIndex >= 0 ) + fileStr = i18n( " %1 of %n file ", " %1 of %n files ", modelCount ).arg( modelIndex + 1 ); + else + fileStr = i18n( " %n file ", " %n files ", modelCount ); + + if ( differenceIndex >= 0 ) + diffStr = i18n( " %1 of %n difference, %2 applied ", " %1 of %n differences, %2 applied ", differenceCount ) + .arg( differenceIndex + 1 ).arg( appliedCount ); + else + diffStr = i18n( " %n difference ", " %n differences ", differenceCount ); + + statusBar()->changeItem( fileStr, ID_N_OF_N_FILES ); + statusBar()->changeItem( diffStr, ID_N_OF_N_DIFFERENCES ); +} + +void KompareShell::slotSetStatusBarText( const QString& text ) +{ + m_generalLabel->setText( text ); +} + +void KompareShell::setCaption( const QString& caption ) +{ +// kdDebug() << kdBacktrace(); + KParts::DockMainWindow::setCaption( caption, m_viewPart->isModified() ); +} + +void KompareShell::saveProperties(KConfig* config) +{ + // The 'config' object points to the session managed + // config file. Anything you write here will be available + // later when this app is restored + if ( m_mode == Kompare::ComparingFiles ) + { + config->writeEntry( "Mode", "ComparingFiles" ); + config->writePathEntry( "SourceUrl", m_sourceURL.url() ); + config->writePathEntry( "DestinationUrl", m_destinationURL.url() ); + } + else if ( m_mode == Kompare::ShowingDiff ) + { + config->writeEntry( "Mode", "ShowingDiff" ); + config->writePathEntry( "DiffUrl", m_diffURL.url() ); + } + + m_viewPart->saveProperties( config ); +} + +void KompareShell::readProperties(KConfig* config) +{ + // The 'config' object points to the session managed + // config file. This function is automatically called whenever + // the app is being restored. Read in here whatever you wrote + // in 'saveProperties' + + QString mode = config->readEntry( "Mode", "ComparingFiles" ); + if ( mode == "ComparingFiles" ) + { + m_mode = Kompare::ComparingFiles; + m_sourceURL = config->readPathEntry( "SourceUrl", "" ); + m_destinationURL = config->readPathEntry( "DestinationFile", "" ); + + m_viewPart->readProperties( config ); + + m_viewPart->compareFiles( m_sourceURL, m_destinationURL ); + } + else if ( mode == "ShowingDiff" ) + { + m_mode = Kompare::ShowingDiff; + m_diffURL = config->readPathEntry( "DiffUrl", "" ); + + m_viewPart->readProperties( config ); + + m_viewPart->openURL( m_diffURL ); + } + else + { // just in case something weird has happened, dont restore the diff then + // Bruggie: or when some idiot like me changes the possible values for mode + // IOW, a nice candidate for a kconf_update thingy :) + m_viewPart->readProperties( config ); + } +} + +void KompareShell::slotFileOpen() +{ + // FIXME: use different filedialog which gets encoding + KURL url = KFileDialog::getOpenURL( QString::null, "text/x-diff", this ); + if( !url.isEmpty() ) { + KompareShell* shell = new KompareShell(); + kapp->ref(); + shell->show(); + shell->openDiff( url ); + } +} + +void KompareShell::slotFileBlendURLAndDiff() +{ + KompareURLDialog* dialog = new KompareURLDialog( this ); + + dialog->setCaption( i18n( "Blend File/Folder with diff Output" ) ); + dialog->setFirstGroupBoxTitle( i18n( "File/Folder" ) ); + dialog->setSecondGroupBoxTitle( i18n( "Diff Output" ) ); + + KGuiItem blendGuiItem( i18n( "Blend" ), QString::null, i18n( "Blend this file or folder with the diff output" ), i18n( "If you have entered a file or folder name and a file that contains diff output in the fields in this dialog then this button will be enabled and pressing it will open kompare's main view where the output of the entered file or files from the folder are mixed with the diff output so you can then apply the difference(s) to a file or to the files. " ) ); + dialog->setButtonOK( blendGuiItem ); + + dialog->setGroup( "Recent Blend Files" ); + + dialog->setFirstURLRequesterMode( KFile::File|KFile::Directory|KFile::ExistingOnly ); + // diff output can not be a directory + dialog->setSecondURLRequesterMode( KFile::File|KFile::ExistingOnly ); + if ( dialog->exec() == QDialog::Accepted ) + { + m_sourceURL = dialog->getFirstURL(); + m_destinationURL = dialog->getSecondURL(); + KompareShell* shell = new KompareShell(); + kapp->ref(); + shell->show(); + shell->m_viewPart->setEncoding( dialog->encoding() ); + shell->blend( m_sourceURL, m_destinationURL ); + } + delete dialog; +} + +void KompareShell::slotFileCompareFiles() +{ + KompareURLDialog* dialog = new KompareURLDialog( this ); + + dialog->setCaption( i18n( "Compare Files or Folders" ) ); + dialog->setFirstGroupBoxTitle( i18n( "Source" ) ); + dialog->setSecondGroupBoxTitle( i18n( "Destination" ) ); + + KGuiItem compareGuiItem( i18n( "Compare" ), QString::null, i18n( "Compare these files or folders" ), i18n( "If you have entered 2 filenames or 2 folders in the fields in this dialog then this button will be enabled and pressing it will start a comparison of the entered files or folders. " ) ); + dialog->setButtonOK( compareGuiItem ); + + dialog->setGroup( "Recent Compare Files" ); + + dialog->setFirstURLRequesterMode( KFile::File|KFile::Directory|KFile::ExistingOnly ); + dialog->setSecondURLRequesterMode( KFile::File|KFile::Directory|KFile::ExistingOnly ); + + if ( dialog->exec() == QDialog::Accepted ) + { + m_sourceURL = dialog->getFirstURL(); + m_destinationURL = dialog->getSecondURL(); + KompareShell* shell = new KompareShell(); + kapp->ref(); + shell->show(); + kdDebug() << "Damn it, i should be called !!! Oh and encoding is: " << dialog->encoding() << endl; + shell->m_viewPart->setEncoding( dialog->encoding() ); + shell->compare( m_sourceURL, m_destinationURL ); + } + delete dialog; +} + +void KompareShell::slotFileClose() +{ + if ( m_viewPart->queryClose() ) + { + delete this; + kapp->deref(); + } +} + +void KompareShell::slotShowTextView() +{ + if ( !m_textViewWidget ) + { + int errCode; + + // FIXME: proper error checking + m_textViewWidget = createDockWidget( i18n("Text View"), SmallIcon( "text") ); + m_textViewPart = KParts::ComponentFactory::createPartInstanceFromQuery( + QString::fromLatin1("KTextEditor/Document"), + QString::null, (QWidget*)this, 0, (QWidget*)this, 0, QStringList(), &errCode ); + if ( m_textViewPart ) + { + m_textView = m_textViewPart->createView( this, 0 ); + m_textViewWidget->setWidget( static_cast(m_textView) ); + m_textEditIface = editInterface( m_textViewPart ); + connect( m_viewPart, SIGNAL(diffString(const QString&)), + this, SLOT(slotSetDiffString(const QString&)) ); + } + } + + m_textViewWidget->manualDock( m_mainViewDock, KDockWidget:: DockCenter ); +} + +void KompareShell::slotSetDiffString( const QString& diffString ) +{ + if ( m_textEditIface ) + m_textEditIface->setText( diffString ); +} + +void KompareShell::optionsConfigureKeys() +{ + KKeyDialog dlg( true, this ); + + dlg.insert( actionCollection() ); + if ( m_viewPart ) + dlg.insert( m_viewPart->actionCollection() ); + + dlg.configure(); +} + +void KompareShell::optionsConfigureToolbars() +{ + saveMainWindowSettings( KGlobal::config(), autoSaveGroup() ); + // use the standard toolbar editor + KEditToolbar dlg(factory()); + connect(&dlg,SIGNAL(newToolbarConfig()),this,SLOT(newToolbarConfig())); + dlg.exec(); +} + +void KompareShell::newToolbarConfig() +{ + applyMainWindowSettings( KGlobal::config(), autoSaveGroup() ); +} + +#include "kompare_shell.moc" diff --git a/kompare/kompare_shell.h b/kompare/kompare_shell.h new file mode 100644 index 00000000..be3bb150 --- /dev/null +++ b/kompare/kompare_shell.h @@ -0,0 +1,149 @@ +/*************************************************************************** + kompare_shell.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef KOMPARESHELL_H +#define KOMPARESHELL_H + +#include +#include + +#include "kompare.h" + +class KToggleAction; + +class KSqueezedTextLabel; +class KomparePart; +class KompareNavTreePart; + +namespace KTextEditor { + class Document; + class EditInterface; + class View; +} + +/** +* This is the application "Shell". It has a menubar, toolbar, and +* statusbar but relies on the "Part" to do all the real work. +* +* Adapted the shell a bit so it now handles seperate view and navigation parts +* +* @short Application Shell +* @author John Firebaugh +* @author Otto Bruggeman +* @version 3.2.90 +*/ +class KompareShell : public KParts::DockMainWindow +{ + Q_OBJECT +public: + /** + * Default Constructor + */ + KompareShell(); + + /** + * Default Destructor + */ + virtual ~KompareShell(); + + /** + * Use this method to load whatever file/URL you have + */ + void openDiff( const KURL& url ); + + /** + * Use this method to load the diff from stdin + */ + void openStdin(); + + /** + * Use this method to compare 2 URLs (files or directories) + */ + void compare( const KURL& source, const KURL& destination ); + + /** + * Use this method to blend diff into url1 (file or directory) + */ + void blend( const KURL& url1, const KURL& diff ); + +public slots: + void slotUpdateStatusBar( int modelIndex, int differenceIndex, int modelCount, int differenceCount, int appliedCount ); + void setCaption( const QString& caption ); + + /** + * This method only exists because i cant make main a frined of this class + */ + KomparePart* viewPart() const { return m_viewPart; } + +protected: + virtual bool queryClose(); + + /** + * This method is called when it is time for the app to save its + * properties for session management purposes. + */ + void saveProperties(KConfig *); + + /** + * This method is called when this app is restored. The KConfig + * object points to the session management config file that was saved + * with @ref saveProperties + */ + void readProperties(KConfig *); + +private slots: + void slotSetStatusBarText( const QString& text ); + void slotFileOpen(); + void slotFileCompareFiles(); + void slotFileBlendURLAndDiff(); + void slotShowTextView(); + void slotFileClose(); + void optionsConfigureKeys(); + void optionsConfigureToolbars(); + void slotSetDiffString( const QString& diffString ); + void newToolbarConfig(); + +private: + void setupAccel(); + void setupActions(); + void setupStatusBar(); + +private: + KURL m_sourceURL; + KURL m_destinationURL; + KURL m_diffURL; + + KomparePart* m_viewPart; + KompareNavTreePart* m_navTreePart; + KTextEditor::Document* m_textViewPart; + KTextEditor::View* m_textView; + KTextEditor::EditInterface* m_textEditIface; + + KDockWidget* m_textViewWidget; + KDockWidget* m_mainViewDock; + KDockWidget* m_navTreeDock; + + KToggleAction* m_showTextView; + + enum Kompare::Mode m_mode; + // This is the statusbarwidget for displaying the general stuff + KSqueezedTextLabel* m_generalLabel; +}; + +#endif // KOMPARE_H diff --git a/kompare/komparenavigationpart.desktop b/kompare/komparenavigationpart.desktop new file mode 100644 index 00000000..1e7f7669 --- /dev/null +++ b/kompare/komparenavigationpart.desktop @@ -0,0 +1,4 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=Kompare/NavigationPart +X-KDE-Derived=KParts/ReadOnlyPart diff --git a/kompare/komparenavtreepart/Makefile.am b/kompare/komparenavtreepart/Makefile.am new file mode 100644 index 00000000..cac18964 --- /dev/null +++ b/kompare/komparenavtreepart/Makefile.am @@ -0,0 +1,29 @@ +######################################################################### +# KPART SECTION +######################################################################### + +INCLUDES = \ + -I$(top_srcdir)/kompare/libdiff2 \ + -I$(top_srcdir)/kompare/komparepart \ + $(all_includes) + +noinst_HEADERS = \ + komparenavtreepart.h + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +kde_module_LTLIBRARIES = libkomparenavtreepart.la + +# the Part's source, library search path, and link libraries +libkomparenavtreepart_la_SOURCES = \ + komparenavtreepart.cpp + +libkomparenavtreepart_la_LDFLAGS = $(KDE_PLUGIN) $(all_libraries) +libkomparenavtreepart_la_LIBADD = $(LIB_KPARTS) ../libdiff2/libdiff2.la + +# this is where the desktop file will go +partdesktopdir = $(kde_servicesdir) +partdesktop_DATA = komparenavtreepart.desktop + + diff --git a/kompare/komparenavtreepart/komparenavtreepart.cpp b/kompare/komparenavtreepart/komparenavtreepart.cpp new file mode 100644 index 00000000..f2e10759 --- /dev/null +++ b/kompare/komparenavtreepart/komparenavtreepart.cpp @@ -0,0 +1,710 @@ +/*************************************************************************** + KompareNavTreePart.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "difference.h" +#include "diffmodel.h" +#include "diffmodellist.h" +#include "komparemodellist.h" + +#include "komparenavtreepart.h" + +#define COL_SOURCE 0 +#define COL_DESTINATION 1 +#define COL_DIFFERENCE 2 + +using namespace Diff2; + +KompareNavTreePart::KompareNavTreePart( QWidget* parent, const char* name ) + : KParts::ReadOnlyPart( parent, name ), + m_splitter( 0 ), + m_modelList( 0 ), + m_srcDirTree( 0 ), + m_destDirTree( 0 ), + m_fileList( 0 ), + m_changesList( 0 ), + m_srcRootItem( 0 ), + m_destRootItem( 0 ), + m_selectedModel( 0 ), + m_selectedDifference( 0 ), + m_source( "" ), + m_destination( "" ), + m_info( 0 ) +{ + m_splitter = new QSplitter( Qt::Horizontal ); + + setWidget( m_splitter ); + + m_srcDirTree = new KListView( m_splitter ); + m_srcDirTree->addColumn( i18n("Source Folder") ); + m_srcDirTree->setRootIsDecorated( false ); + m_srcDirTree->setSorting( 0, true ); + + m_destDirTree = new KListView( m_splitter ); + m_destDirTree->addColumn( i18n("Destination Folder") ); + m_destDirTree->setRootIsDecorated( false ); + m_destDirTree->setSorting( 0, true ); + + m_fileList = new KListView( m_splitter ); + m_fileList->addColumn( i18n("Source File") ); + m_fileList->addColumn( i18n("Destination File") ); + m_fileList->setAllColumnsShowFocus( true ); + m_fileList->setRootIsDecorated( false ); + m_fileList->setSorting( 0, true ); + + m_changesList = new KListView( m_splitter ); + m_changesList->addColumn( i18n("Source Line") ); + m_changesList->addColumn( i18n("Destination Line") ); + m_changesList->addColumn( i18n("Difference") ); + m_changesList->setAllColumnsShowFocus( true ); + m_changesList->setRootIsDecorated( false ); + m_changesList->setSorting( 0, true ); + + connect( m_srcDirTree, SIGNAL(selectionChanged( QListViewItem* )), + this, SLOT(slotSrcDirTreeSelectionChanged( QListViewItem* )) ); + connect( m_destDirTree, SIGNAL(selectionChanged( QListViewItem* )), + this, SLOT(slotDestDirTreeSelectionChanged( QListViewItem* )) ); + connect( m_fileList, SIGNAL(selectionChanged( QListViewItem* )), + this, SLOT(slotFileListSelectionChanged( QListViewItem* )) ); + connect( m_changesList, SIGNAL(selectionChanged( QListViewItem* )), + this, SLOT(slotChangesListSelectionChanged( QListViewItem* )) ); +} + +KompareNavTreePart::~KompareNavTreePart() +{ +} + +void KompareNavTreePart::slotKompareInfo( struct Kompare::Info* info ) +{ + m_info = info; +} + +void KompareNavTreePart::slotModelsChanged( const DiffModelList* modelList ) +{ + kdDebug(8105) << "Models (" << modelList << ") have changed... scanning the models... " << endl; + + if ( modelList ) + { + m_modelList = modelList; + m_srcDirTree->clear(); + m_destDirTree->clear(); + m_fileList->clear(); + m_changesList->clear(); + buildTreeInMemory(); + } + else + { + m_modelList = modelList; + m_srcDirTree->clear(); + m_destDirTree->clear(); + m_fileList->clear(); + m_changesList->clear(); + } +} + +void KompareNavTreePart::buildTreeInMemory() +{ + kdDebug(8105) << "BuildTreeInMemory called" << endl; + + if ( m_modelList->count() == 0 ) + { + kdDebug() << "No models... weird shit..." << endl; + return; // avoids a crash on clear() + } + + if ( m_info == 0 ) + { + kdDebug() << "No Info... weird shit..." << endl; + return; + } + + QString srcBase; + QString destBase; + + DiffModel* model; + model = m_modelList->first(); + m_selectedModel = 0L; + + switch ( m_info->mode ) + { + case Kompare::ShowingDiff: + srcBase = model->sourcePath(); + destBase = model->destinationPath(); + break; + case Kompare::ComparingFiles: + srcBase = model->sourcePath(); + destBase = model->destinationPath(); + break; + case Kompare::ComparingDirs: + srcBase = m_info->localSource; + if ( !srcBase.endsWith( "/" ) ) + srcBase += "/"; + destBase = m_info->localDestination; + if ( !destBase.endsWith( "/" ) ) + destBase += "/"; + break; + case Kompare::BlendingFile: + case Kompare::BlendingDir: + default: + kdDebug(8105) << "Oops needs to implement this..." << endl; + } + +// kdDebug(8105) << "srcBase = " << srcBase << endl; +// kdDebug(8105) << "destBase = " << destBase << endl; + + m_srcRootItem = new KDirLVI( m_srcDirTree, srcBase ); + m_destRootItem = new KDirLVI( m_destDirTree, destBase ); + + QString srcPath; + QString destPath; + + // Create the tree from the models + DiffModelListConstIterator modelIt = m_modelList->begin(); + DiffModelListConstIterator mEnd = m_modelList->end(); + + for ( ; modelIt != mEnd; ++modelIt ) + { + model = *modelIt; + srcPath = model->sourcePath(); + destPath = model->destinationPath(); + + kdDebug(8105) << "srcPath = " << srcPath << endl; + kdDebug(8105) << "destPath = " << destPath << endl; + m_srcRootItem->addModel( srcPath, model, &m_modelToSrcDirItemDict ); + m_destRootItem->addModel( destPath, model, &m_modelToDestDirItemDict ); + } +// m_srcDirTree->setSelected( m_srcDirTree->firstChild(), true ); +} + +void KompareNavTreePart::buildDirectoryTree() +{ +// FIXME: afaict this can be deleted +// kdDebug(8105) << "BuildDirTree called" << endl; +} + +QString KompareNavTreePart::compareFromEndAndReturnSame( + const QString& string1, + const QString& string2 ) +{ + QString result; + + int srcLen = string1.length(); + int destLen = string2.length(); + + while ( srcLen != 0 && destLen != 0 ) + { + if ( string1[--srcLen] == string2[--destLen] ) + result.prepend( string1[srcLen] ); + else + break; + } + + if ( srcLen != 0 && destLen != 0 && result.startsWith( "/" ) ) + result = result.remove( 0, 1 ); // strip leading /, we need it later + + return result; +} + +void KompareNavTreePart::slotSetSelection( const DiffModel* model, const Difference* diff ) +{ + kdDebug(8105) << "KompareNavTreePart::slotSetSelection model = " << model << ", diff = " << diff << endl; + if ( model == m_selectedModel ) + { + // model is the same, so no need to update that... + if ( diff != m_selectedDifference ) + { + m_selectedDifference = diff; + setSelectedDifference( diff ); + } + return; + } + + // model is different so we need to find the right dirs, file and changeitems to select + // if m_selectedModel == NULL then everything needs to be done as well + if ( !m_selectedModel || model->sourcePath() != m_selectedModel->sourcePath() ) + { // dirs are different, so we need to update the dirviews as well + m_selectedModel = model; + m_selectedDifference = diff; + + setSelectedDir( model ); + setSelectedFile( model ); + setSelectedDifference( diff ); + return; + } + + if ( !m_selectedModel || model->sourceFile() != m_selectedModel->sourceFile() ) + { + m_selectedModel = model; + setSelectedFile( model ); + + m_selectedDifference = diff; + setSelectedDifference( diff ); + } +} + +void KompareNavTreePart::setSelectedDir( const DiffModel* model ) +{ + KDirLVI* currentDir; + currentDir = m_modelToSrcDirItemDict[ (void*)model ]; + kdDebug(8105) << "Manually setting selection in srcdirtree with currentDir = " << currentDir << endl; + m_srcDirTree->blockSignals( true ); + m_srcDirTree->setSelected( currentDir, true ); + m_srcDirTree->ensureItemVisible( currentDir ); + m_srcDirTree->blockSignals( false ); + + currentDir = m_modelToDestDirItemDict[ (void*)model ]; + kdDebug(8105) << "Manually setting selection in destdirtree with currentDir = " << currentDir << endl; + m_destDirTree->blockSignals( true ); + m_destDirTree->setSelected( currentDir, true ); + m_destDirTree->ensureItemVisible( currentDir ); + m_destDirTree->blockSignals( false ); + + m_fileList->blockSignals( true ); + currentDir->fillFileList( m_fileList, &m_modelToFileItemDict ); + m_fileList->blockSignals( false ); +} + +void KompareNavTreePart::setSelectedFile( const DiffModel* model ) +{ + KFileLVI* currentFile; + currentFile = m_modelToFileItemDict[ (void*)model ]; + kdDebug(8105) << "Manually setting selection in filelist" << endl; + m_fileList->blockSignals( true ); + m_fileList->setSelected( currentFile, true ); + m_fileList->ensureItemVisible( currentFile ); + m_fileList->blockSignals( false ); + + m_changesList->blockSignals( true ); + currentFile->fillChangesList( m_changesList, &m_diffToChangeItemDict ); + m_changesList->blockSignals( false ); +} + +void KompareNavTreePart::setSelectedDifference( const Difference* diff ) +{ + KChangeLVI* currentDiff; + currentDiff = m_diffToChangeItemDict[ (void*)diff ]; + kdDebug(8105) << "Manually setting selection in changeslist to " << currentDiff << endl; + m_changesList->blockSignals( true ); + m_changesList->setSelected( currentDiff, true ); + m_changesList->ensureItemVisible( currentDiff ); + m_changesList->blockSignals( false ); +} + +void KompareNavTreePart::slotSetSelection( const Difference* diff ) +{ +// kdDebug(8105) << "Scotty i need more power !!" << endl; + if ( m_selectedDifference != diff ) + { +// kdDebug(8105) << "But sir, i am giving you all she's got" << endl; + m_selectedDifference = diff; + setSelectedDifference( diff ); + } +} + +void KompareNavTreePart::slotSrcDirTreeSelectionChanged( QListViewItem* item ) +{ + kdDebug(8105) << "Sent by the sourceDirectoryTree with item = " << item << endl; + m_srcDirTree->ensureItemVisible( item ); + KDirLVI* dir = static_cast(item); + // order the dest tree view to set its selected item to the same as here + QString path; + // We start with an empty path and after the call path contains the full path + path = dir->fullPath( path ); + KDirLVI* selItem = m_destRootItem->setSelected( path ); + m_destDirTree->blockSignals( true ); + m_destDirTree->setSelected( selItem, true ); + m_destDirTree->ensureItemVisible( selItem ); + m_destDirTree->blockSignals( false ); + // fill the changes list + dir->fillFileList( m_fileList, &m_modelToFileItemDict ); +} + +void KompareNavTreePart::slotDestDirTreeSelectionChanged( QListViewItem* item ) +{ + kdDebug(8105) << "Sent by the destinationDirectoryTree with item = " << item << endl; + m_destDirTree->ensureItemVisible( item ); + KDirLVI* dir = static_cast(item); + // order the src tree view to set its selected item to the same as here + QString path; + // We start with an empty path and after the call path contains the full path + path = dir->fullPath( path ); + KDirLVI* selItem = m_srcRootItem->setSelected( path ); + m_srcDirTree->blockSignals( true ); + m_srcDirTree->setSelected( selItem, true ); + m_srcDirTree->ensureItemVisible( selItem ); + m_srcDirTree->blockSignals( false ); + // fill the changes list + dir->fillFileList( m_fileList, &m_modelToFileItemDict ); +} + +void KompareNavTreePart::slotFileListSelectionChanged( QListViewItem* item ) +{ + kdDebug(8105) << "Sent by the fileList with item = " << item << endl; + + KFileLVI* file = static_cast(item); + m_selectedModel = file->model(); + m_changesList->blockSignals( true ); + file->fillChangesList( m_changesList, &m_diffToChangeItemDict ); + m_changesList->blockSignals( false ); + + if ( m_changesList->selectedItem() ) + { + // FIXME: This is ugly... + m_selectedDifference = (static_cast(m_changesList->selectedItem()))->difference(); + } + + emit selectionChanged( m_selectedModel, m_selectedDifference ); +} + +void KompareNavTreePart::slotChangesListSelectionChanged( QListViewItem* item ) +{ + kdDebug(8105) << "Sent by the changesList" << endl; + + KChangeLVI* change = static_cast(item); + m_selectedDifference = change->difference(); + + emit selectionChanged( m_selectedDifference ); +} + +void KompareNavTreePart::slotApplyDifference( bool /*apply*/ ) +{ + KChangeLVI* clvi = m_diffToChangeItemDict[(void*)m_selectedDifference]; + if ( clvi ) + clvi->setDifferenceText(); +} + +void KompareNavTreePart::slotApplyAllDifferences( bool /*apply*/ ) +{ + QPtrDictIterator it( m_diffToChangeItemDict ); + + kdDebug() << "m_diffToChangeItemDict.count() = " << m_diffToChangeItemDict.count() << endl; + + for ( ; it.current(); ++it ) + { + it.current()->setDifferenceText(); + } +} + +void KompareNavTreePart::slotApplyDifference( const Difference* diff, bool /*apply*/ ) +{ + // this applies to the currently selected difference + KChangeLVI* clvi = m_diffToChangeItemDict[(void*)diff]; + if ( clvi ) + clvi->setDifferenceText(); +} + +void KChangeLVI::setDifferenceText() +{ + QString text; + switch( m_difference->type() ) { + case Difference::Change: + // Shouldn't this simply be diff->sourceLineCount() ? + // because you change the _number of lines_ lines in source, not in dest + if( m_difference->applied() ) + text = i18n( "Applied: Changes made to %n line undone", "Applied: Changes made to %n lines undone", + m_difference->sourceLineCount() ); + else + text = i18n( "Changed %n line", "Changed %n lines", + m_difference->sourceLineCount() ); + break; + case Difference::Insert: + if( m_difference->applied() ) + text = i18n( "Applied: Insertion of %n line undone", "Applied: Insertion of %n lines undone", + m_difference->destinationLineCount() ); + else + text = i18n( "Inserted %n line", "Inserted %n lines", + m_difference->destinationLineCount() ); + break; + case Difference::Delete: + if( m_difference->applied() ) + text = i18n( "Applied: Deletion of %n line undone", "Applied: Deletion of %n lines undone", + m_difference->sourceLineCount() ); + else + text = i18n( "Deleted %n line", "Deleted %n lines", + m_difference->sourceLineCount() ); + break; + default: + kdDebug(8105) << "Unknown or Unchanged enum value when checking for diff->type() in KChangeLVI's constructor" << endl; + text = ""; + } + + setText( 2, text ); +} + +KChangeLVI::KChangeLVI( KListView* parent, Difference* diff ) : KListViewItem( parent ) +{ + m_difference = diff; + + setText( 0, QString::number( diff->sourceLineNumber() ) ); + setText( 1, QString::number( diff->destinationLineNumber() ) ); + + setDifferenceText(); +} + +int KChangeLVI::compare( QListViewItem* item, int column, bool ascending ) const +{ + if ( ascending ) + { + if ( this->text(column).length() < item->text(column).length() ) + return -1; + if ( this->text(column).length() > item->text(column).length() ) + return 1; + } + else + { + if ( this->text(column).length() > item->text(column).length() ) + return -1; + if ( this->text(column).length() < item->text(column).length() ) + return 1; + } + + return key( column, ascending ).compare( item->key( column, ascending ) ); +} + +KChangeLVI::~KChangeLVI() +{ +} + +KFileLVI::KFileLVI( KListView* parent, DiffModel* model ) : KListViewItem( parent ) +{ + m_model = model; + + setText( 0, model->sourceFile() ); + setText( 1, model->destinationFile() ); + setPixmap( 0, SmallIcon( "txt" ) ); + setPixmap( 1, SmallIcon( "txt" ) ); + setSelectable( true ); +} + +void KFileLVI::fillChangesList( KListView* changesList, QPtrDict* diffToChangeItemDict ) +{ + changesList->clear(); + diffToChangeItemDict->clear(); + + DifferenceListConstIterator diffIt = m_model->differences()->begin(); + DifferenceListConstIterator dEnd = m_model->differences()->end(); + + for ( ; diffIt != dEnd; ++diffIt ) + { + KChangeLVI* change = new KChangeLVI( changesList, *diffIt ); + diffToChangeItemDict->insert( *diffIt, change ); + } + + changesList->setSelected( changesList->firstChild(), true ); +} + +KFileLVI::~KFileLVI() +{ +} + +KDirLVI::KDirLVI( KListView* parent, QString& dir ) : KListViewItem( parent ) +{ +// kdDebug(8105) << "KDirLVI (KListView) constructor called with dir = " << dir << endl; + m_rootItem = true; + m_dirName = dir; + setPixmap( 0, SmallIcon( "folder" ) ); + setOpen( true ); + setSelectable( true ); + if ( m_dirName.isEmpty() ) + setText( 0, i18n( "Unknown" ) ); + else + setText( 0, m_dirName ); +} + +KDirLVI::KDirLVI( KDirLVI* parent, QString& dir ) : KListViewItem( parent ) +{ +// kdDebug(8105) << "KDirLVI (KDirLVI) constructor called with dir = " << dir << endl; + m_rootItem = false; + m_dirName = dir; + setPixmap( 0, SmallIcon( "folder" ) ); + setOpen( true ); + setSelectable( true ); + setText( 0, m_dirName ); +} + +// addModel always removes it own path from the beginning +void KDirLVI::addModel( QString& path, DiffModel* model, QPtrDict* modelToDirItemDict ) +{ +// kdDebug(8105) << "KDirLVI::addModel called with path = " << path << " from KDirLVI with m_dirName = " << m_dirName << endl; + + if ( !m_dirName.isEmpty() ) + { + if ( path.find( m_dirName ) > -1 ) + path = path.replace( path.find( m_dirName ), m_dirName.length(), "" ); + } + +// kdDebug(8105) << "Path after removal of own dir (\"" << m_dirName << "\") = " << path << endl; + + if ( path.isEmpty() ) { + m_modelList.append( model ); + modelToDirItemDict->insert( model, this ); + return; + } + + KDirLVI* child; + + QString dir = path.mid( 0, path.find( "/", 0 ) + 1 ); + child = findChild( dir ); + if ( !child ) + { + // does not exist yet so make it +// kdDebug(8105) << "KDirLVI::addModel creating new KDirLVI because not found" << endl; + child = new KDirLVI( this, dir ); + } + + child->addModel( path, model, modelToDirItemDict ); +} + +KDirLVI* KDirLVI::findChild( QString dir ) +{ +// kdDebug(8105) << "KDirLVI::findChild called with dir = " << dir << endl; + KDirLVI* child; + if ( ( child = static_cast(this->firstChild()) ) != 0L ) + { // has children, check if dir already exists, if so addModel + do { + if ( dir == child->dirName() ) + return child; + } while ( ( child = static_cast(child->nextSibling()) ) != 0L ); + } + + return 0L; +} + +void KDirLVI::fillFileList( KListView* fileList, QPtrDict* modelToFileItemDict ) +{ + fileList->clear(); + + DiffModelListIterator modelIt = m_modelList.begin(); + DiffModelListIterator mEnd = m_modelList.end(); + for ( ;modelIt != mEnd; ++modelIt ) + { + KFileLVI* file = new KFileLVI( fileList, *modelIt ); + modelToFileItemDict->insert( *modelIt, file ); + } + + fileList->setSelected( fileList->firstChild(), true ); +} + +QString KDirLVI::fullPath( QString& path ) +{ +// if ( !path.isEmpty() ) +// kdDebug(8105) << "KDirLVI::fullPath called with path = " << path << endl; +// else +// kdDebug(8105) << "KDirLVI::fullPath called with empty path..." << endl; + + if ( m_rootItem ) // dont bother adding rootItem's dir... + return path; + + path = path.prepend( m_dirName ); + + KDirLVI* lviParent = dynamic_cast( parent() ); + if ( lviParent ) + { + path = lviParent->fullPath( path ); + } + + return path; +} + +KDirLVI* KDirLVI::setSelected( QString dir ) +{ +// kdDebug(8105) << "KDirLVI::setSelected called with dir = " << dir << endl; + + // root item's dirName is never taken into account... remember that + if ( !m_rootItem ) + { + dir = dir.remove( 0, m_dirName.length() ); + } + + if ( dir.isEmpty() ) + { + return this; + } + KDirLVI* child = static_cast(firstChild()); + if ( !child ) + return 0L; + + do { + if ( dir.startsWith( child->dirName() ) ) + return child->setSelected( dir ); + } while ( ( child = static_cast(child->nextSibling()) ) != 0L ); + + return 0L; +} + +KDirLVI::~KDirLVI() +{ +} + +// part stuff +KInstance* KompareNavTreePartFactory::s_instance = 0L; +KAboutData* KompareNavTreePartFactory::s_about = 0L; + +KompareNavTreePartFactory::KompareNavTreePartFactory() + : KParts::Factory() +{ +} + +KompareNavTreePartFactory::~KompareNavTreePartFactory() +{ + delete s_instance; + delete s_about; + + s_instance = 0L; +} + +KParts::Part* KompareNavTreePartFactory::createPartObject( QWidget* parentWidget, const char* widgetName, + QObject* /*parent*/, const char* /*name*/, + const char* /*classname*/, const QStringList & /*args*/ ) +{ + // Create an instance of our Part + KompareNavTreePart* obj = new KompareNavTreePart( parentWidget, widgetName ); + + KGlobal::locale()->insertCatalogue("kompare"); + + return obj; +} + +KInstance* KompareNavTreePartFactory::instance() +{ + if( !s_instance ) + { + s_about = new KAboutData("komparenavtreepart", I18N_NOOP("KompareNavTreePart"), "1.1"); + s_about->addAuthor("John Firebaugh", "Author", "jfirebaugh@kde.org"); + s_about->addAuthor("Otto Bruggeman", "Author", "otto.bruggeman@home.nl" ); + s_instance = new KInstance(s_about); + } + return s_instance; +} + +extern "C" +{ + KDE_EXPORT void* init_libkomparenavtreepart() + { + return new KompareNavTreePartFactory; + } +} + +#include "komparenavtreepart.moc" diff --git a/kompare/komparenavtreepart/komparenavtreepart.desktop b/kompare/komparenavtreepart/komparenavtreepart.desktop new file mode 100644 index 00000000..eb5ce686 --- /dev/null +++ b/kompare/komparenavtreepart/komparenavtreepart.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Name=KompareNavTreePart +Name[pl]=Komponent drzewa nawigacyjnego Kompare +Name[pt_BR]=Componente da árvore do Kompare +Name[sv]=Kompare-navigeringsträdsdel +Name[ta]=கோம்பெர் மரம் பாகம் +MimeType=text/x-diff +ServiceTypes=Kompare/NavigationPart +X-KDE-Library=libkomparenavtreepart +Type=Service +Icon=kompare diff --git a/kompare/komparenavtreepart/komparenavtreepart.h b/kompare/komparenavtreepart/komparenavtreepart.h new file mode 100644 index 00000000..e2563d63 --- /dev/null +++ b/kompare/komparenavtreepart/komparenavtreepart.h @@ -0,0 +1,191 @@ +/*************************************************************************** + komparenavtreepart.h - description + ------------------- + begin : Mon Feb 26 2002 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef KOMPARENAVTREEPART_H +#define KOMPARENAVTREEPART_H + +#include +#include +#include +#include + +#include +#include + +#include "kompare.h" +#include "diffmodellist.h" + +class KompareModelList; +class KomparePart; +class KListView; + +namespace Diff2 { +class DiffModel; +class Difference; +} + +class KDirLVI; +class KFileLVI; +class KChangeLVI; + +class KompareNavTreePart : public KParts::ReadOnlyPart +{ + Q_OBJECT + +public: + KompareNavTreePart( QWidget* parent = 0L, const char* name = 0L ); + virtual ~KompareNavTreePart(); + +public: + virtual bool openFile() { return false; }; + +public slots: + void slotSetSelection( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void slotSetSelection( const Diff2::Difference* diff ); + void slotModelsChanged( const Diff2::DiffModelList* modelList ); + void slotKompareInfo( Kompare::Info* info ); + +signals: + void selectionChanged( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void selectionChanged( const Diff2::Difference* diff ); + +private slots: + void slotSrcDirTreeSelectionChanged ( QListViewItem* item ); + void slotDestDirTreeSelectionChanged( QListViewItem* item ); + void slotFileListSelectionChanged ( QListViewItem* item ); + void slotChangesListSelectionChanged( QListViewItem* item ); + + void slotApplyDifference( bool apply ); + void slotApplyAllDifferences( bool apply ); + void slotApplyDifference( const Diff2::Difference* diff, bool apply ); + + void buildTreeInMemory(); + +private: + void setSelectedDir( const Diff2::DiffModel* model ); + void setSelectedFile( const Diff2::DiffModel* model ); + void setSelectedDifference( const Diff2::Difference* diff ); + + void buildDirectoryTree(); + + QString compareFromEndAndReturnSame( const QString& string1, const QString& string2 ); + void addDirToTreeView( enum Kompare::Target, const QString& filename ); + + KListViewItem* findDirInDirTree( const KListViewItem* parent, const QString& dir ); + +// KListViewItem* firstItem(); +// KListViewItem* lastItem(); + +private: + QSplitter* m_splitter; + const Diff2::DiffModelList* m_modelList; + + QPtrDict m_diffToChangeItemDict; + QPtrDict m_modelToFileItemDict; + QPtrDict m_modelToSrcDirItemDict; + QPtrDict m_modelToDestDirItemDict; + + KListView* m_srcDirTree; + KListView* m_destDirTree; + KListView* m_fileList; + KListView* m_changesList; + + KDirLVI* m_srcRootItem; + KDirLVI* m_destRootItem; + + const Diff2::DiffModel* m_selectedModel; + const Diff2::Difference* m_selectedDifference; + + QString m_source; + QString m_destination; + + struct Kompare::Info* m_info; +}; + +// These 3 classes are need to store the models into a tree so it is easier +// to extract the info we need for the navigation widgets + +class KChangeLVI : public KListViewItem +{ +public: + KChangeLVI( KListView* parent, Diff2::Difference* diff ); + ~KChangeLVI(); +public: + Diff2::Difference* difference() { return m_difference; }; + virtual int compare( QListViewItem* item, int column, bool ascending ) const; + + void setDifferenceText(); +private: + Diff2::Difference* m_difference; +}; + +class KFileLVI : public KListViewItem +{ +public: + KFileLVI( KListView* parent, Diff2::DiffModel* model ); + ~KFileLVI(); +public: + Diff2::DiffModel* model() { return m_model; }; + void fillChangesList( KListView* changesList, QPtrDict* diffToChangeItemDict ); +private: + Diff2::DiffModel* m_model; +}; + +class KDirLVI : public KListViewItem +{ +public: + KDirLVI( KDirLVI* parent, QString& dir ); + KDirLVI( KListView* parent, QString& dir ); + ~KDirLVI(); +public: + void addModel( QString& dir, Diff2::DiffModel* model, QPtrDict* modelToDirItemDict ); + QString& dirName() { return m_dirName; }; + QString fullPath( QString& path ); + KDirLVI* setSelected( QString dir ); + void fillFileList( KListView* fileList, QPtrDict* modelToFileItemDict ); + bool isRootItem() { return m_rootItem; }; +private: + KDirLVI* findChild( QString dir ); +private: + Diff2::DiffModelList m_modelList; + QString m_dirName; + bool m_rootItem; +}; + +// part stuff +class KInstance; +class KAboutData; + +class KompareNavTreePartFactory : public KParts::Factory +{ + Q_OBJECT +public: + KompareNavTreePartFactory(); + virtual ~KompareNavTreePartFactory(); + virtual KParts::Part* createPartObject( QWidget *parentWidget, const char *widgetName, + QObject *parent, const char *name, + const char *classname, const QStringList &args ); + static KInstance* instance(); + +private: + static KInstance* s_instance; + static KAboutData* s_about; +}; + +#endif diff --git a/kompare/komparepart/Makefile.am b/kompare/komparepart/Makefile.am new file mode 100644 index 00000000..f076448b --- /dev/null +++ b/kompare/komparepart/Makefile.am @@ -0,0 +1,49 @@ +######################################################################### +# KPART SECTION +######################################################################### + +INCLUDES = \ + -I$(top_srcdir)/kompare/libdialogpages \ + -I$(top_srcdir)/kompare/libdiff2 \ + -I$(top_srcdir)/kompare/interfaces \ + $(all_includes) + +noinst_HEADERS = \ + kompare_part.h \ + komparesplitter.h \ + kompareprefdlg.h \ + komparelistview.h \ + kompareconnectwidget.h \ + komparesaveoptionsbase.h \ + komparesaveoptionswidget.h \ + kompare_qsplitter.h + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +kde_module_LTLIBRARIES = libkomparepart.la + +# the Part's source, library search path, and link libraries +libkomparepart_la_SOURCES = \ + kompare_part.cpp \ + kompareconnectwidget.cpp \ + komparesplitter.cpp \ + komparelistview.cpp \ + kompareprefdlg.cpp \ + komparesaveoptionsbase.ui \ + komparesaveoptionswidget.cpp + +libkomparepart_la_LDFLAGS = $(KDE_PLUGIN) $(all_libraries) +libkomparepart_la_LIBADD = $(LIB_KPARTS) $(LIB_KFILE) \ + ../libdialogpages/libdialogpages.la \ + ../libdiff2/libdiff2.la \ + ../interfaces/libkompareinterface.la + +# this is where the desktop file will go +partdesktopdir = $(kde_servicesdir) +partdesktop_DATA = komparepart.desktop + +# this is where the part's XML-GUI resource file goes +partrcdir = $(kde_datadir)/kompare +partrc_DATA = komparepartui.rc + diff --git a/kompare/komparepart/kompare_part.cpp b/kompare/komparepart/kompare_part.cpp new file mode 100644 index 00000000..d74945e6 --- /dev/null +++ b/kompare/komparepart/kompare_part.cpp @@ -0,0 +1,759 @@ +/*************************************************************************** + kompare_part.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + (C) 2004 Jeff Snyder + email : otto.bruggeman@home.nl + jfirebaugh@kde.org + jeff@caffeinated.me.uk +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include "kompare_qsplitter.h" // make sure we get there first + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +#include + +#include "diffmodel.h" +#include "komparelistview.h" +#include "kompareconnectwidget.h" +#include "diffsettings.h" +#include "viewsettings.h" +#include "kompareprefdlg.h" +#include "komparesaveoptionswidget.h" +#include "komparesplitter.h" + +#include "kompare_part.h" + +typedef KParts::GenericFactory KomparePartFactory; +K_EXPORT_COMPONENT_FACTORY( libkomparepart, KomparePartFactory ) + +ViewSettings* KomparePart::m_viewSettings = 0L; +DiffSettings* KomparePart::m_diffSettings = 0L; + +KomparePart::KomparePart( QWidget *parentWidget, const char *widgetName, + QObject *parent, const char *name, const QStringList & /*args*/ ) : + KParts::ReadWritePart(parent, name), + m_tempDiff( 0 ), + m_info() +{ + // we need an instance + setInstance( KomparePartFactory::instance() ); + + if( !m_viewSettings ) { + m_viewSettings = new ViewSettings( 0 ); + } + if( !m_diffSettings ) { + m_diffSettings = new DiffSettings( 0 ); + } + + readProperties( kapp->config() ); + + // This creates the "Model creator" and connects the signals and slots + m_modelList = new Diff2::KompareModelList( m_diffSettings, m_info, this, "komparemodellist" ); + connect( m_modelList, SIGNAL(status( Kompare::Status )), + this, SLOT(slotSetStatus( Kompare::Status )) ); + connect( m_modelList, SIGNAL(setStatusBarModelInfo( int, int, int, int, int )), + this, SIGNAL(setStatusBarModelInfo( int, int, int, int, int )) ); + connect( m_modelList, SIGNAL(error( QString )), + this, SLOT(slotShowError( QString )) ); + connect( m_modelList, SIGNAL(applyAllDifferences( bool )), + this, SLOT(updateActions()) ); + connect( m_modelList, SIGNAL(applyDifference( bool )), + this, SLOT(updateActions()) ); + connect( m_modelList, SIGNAL(applyAllDifferences( bool )), + this, SIGNAL(appliedChanged()) ); + connect( m_modelList, SIGNAL(applyDifference( bool )), + this, SIGNAL(appliedChanged()) ); + + connect( m_modelList, SIGNAL( setModified( bool ) ), + this, SLOT( slotSetModified( bool ) ) ); + + // This is the stuff to connect the "interface" of the kompare part to the model inside + connect( m_modelList, SIGNAL(modelsChanged(const Diff2::DiffModelList*)), + this, SIGNAL(modelsChanged(const Diff2::DiffModelList*)) ); + + connect( m_modelList, SIGNAL(setSelection(const Diff2::DiffModel*, const Diff2::Difference*)), + this, SIGNAL(setSelection(const Diff2::DiffModel*, const Diff2::Difference*)) ); + connect( this, SIGNAL(selectionChanged(const Diff2::DiffModel*, const Diff2::Difference*)), + m_modelList, SLOT(slotSelectionChanged(const Diff2::DiffModel*, const Diff2::Difference*)) ); + + connect( m_modelList, SIGNAL(setSelection(const Diff2::Difference*)), + this, SIGNAL(setSelection(const Diff2::Difference*)) ); + connect( this, SIGNAL(selectionChanged(const Diff2::Difference*)), + m_modelList, SLOT(slotSelectionChanged(const Diff2::Difference*)) ); + + connect( m_modelList, SIGNAL(applyDifference(bool)), + this, SIGNAL(applyDifference(bool)) ); + connect( m_modelList, SIGNAL(applyAllDifferences(bool)), + this, SIGNAL(applyAllDifferences(bool)) ); + connect( m_modelList, SIGNAL(applyDifference(const Diff2::Difference*, bool)), + this, SIGNAL(applyDifference(const Diff2::Difference*, bool)) ); + + // This creates the splitterwidget and connects the signals and slots + m_splitter = new KompareSplitter ( m_viewSettings, parentWidget, widgetName ); + + connect( m_modelList, SIGNAL(setSelection(const Diff2::DiffModel*, const Diff2::Difference*)), + m_splitter, SLOT(slotSetSelection(const Diff2::DiffModel*, const Diff2::Difference*)) ); +// connect( m_splitter, SIGNAL(selectionChanged(const Diff2::Difference*, const Diff2::Difference*)), +// m_modelList, SLOT(slotSelectionChanged(const Diff2::Difference*, const Diff2::Difference*)) ); + connect( m_modelList, SIGNAL(setSelection(const Diff2::Difference*)), + m_splitter, SLOT(slotSetSelection(const Diff2::Difference*)) ); + connect( m_splitter, SIGNAL(selectionChanged(const Diff2::Difference*)), + m_modelList, SLOT(slotSelectionChanged(const Diff2::Difference*)) ); + + connect( m_modelList, SIGNAL(applyDifference(bool)), + m_splitter, SLOT(slotApplyDifference(bool)) ); + connect( m_modelList, SIGNAL(applyAllDifferences(bool)), + m_splitter, SLOT(slotApplyAllDifferences(bool)) ); + connect( m_modelList, SIGNAL(applyDifference(const Diff2::Difference*, bool)), + m_splitter, SLOT(slotApplyDifference(const Diff2::Difference*, bool)) ); + connect( this, SIGNAL(configChanged()), m_splitter, SIGNAL(configChanged()) ); + + // notify the part that this is our internal widget + setWidget( m_splitter->parentWidget() ); + + setupActions(); + + // set our XML-UI resource file + setXMLFile( "komparepartui.rc" ); + + // we are read-write by default -> uhm what if we are opened by lets say konq in RO mode ? + // Then we should not be doing this... + setReadWrite( true ); + + // we are not modified since we haven't done anything yet + setModified( false ); +} + +KomparePart::~KomparePart() +{ + // This is the only place allowed to call cleanUpTemporaryFiles + // because before there might still be a use for them (when swapping) + cleanUpTemporaryFiles(); +} + +void KomparePart::setupActions() +{ + // create our actions + + m_saveAll = new KAction( i18n("Save &All"), "save_all", 0, + this, SLOT(saveAll()), + actionCollection(), "file_save_all" ); + m_saveDiff = new KAction( i18n("Save .&diff..."), 0, + this, SLOT(saveDiff()), + actionCollection(), "file_save_diff" ); + m_swap = new KAction( i18n( "Swap Source with Destination" ), 0, + this, SLOT(slotSwap()), + actionCollection(), "file_swap" ); + m_diffStats = new KAction( i18n( "Show Statistics" ), 0, + this, SLOT(slotShowDiffstats()), + actionCollection(), "file_diffstats" ); + + KStdAction::preferences(this, SLOT(optionsPreferences()), actionCollection()); +} + +void KomparePart::updateActions() +{ + m_saveAll->setEnabled ( m_modelList->isModified() ); + m_saveDiff->setEnabled ( m_modelList->mode() == Kompare::ComparingFiles || m_modelList->mode() == Kompare::ComparingDirs ); + m_swap->setEnabled ( m_modelList->mode() == Kompare::ComparingFiles || m_modelList->mode() == Kompare::ComparingDirs ); + m_diffStats->setEnabled( m_modelList->modelCount() > 0 ); +} + +void KomparePart::setEncoding( const QString& encoding ) +{ + kdDebug() << "Encoding: " << encoding << endl; + m_modelList->setEncoding( encoding ); +} + +bool KomparePart::openDiff( const KURL& url ) +{ + kdDebug(8103) << "Url = " << url.url() << endl; + + emit kompareInfo( &m_info ); + + m_info.mode = Kompare::ShowingDiff; + m_info.source = url; + bool result = false; + m_info.localSource = fetchURL( url ); + if ( !m_info.localSource.isEmpty() ) + { + kdDebug(8103) << "Download succeeded " << endl; + result = m_modelList->openDiff( m_info.localSource ); + updateActions(); + updateCaption(); + updateStatus(); + } + else + { + kdDebug(8103) << "Download failed !" << endl; + } + + return result; +} + +bool KomparePart::openDiff( const QString& diffOutput ) +{ + bool value = false; + + emit kompareInfo( &m_info ); + + m_info.mode = Kompare::ShowingDiff; + + if ( m_modelList->parseDiffOutput( diffOutput ) == 0 ) + { + value = true; + m_modelList->show(); + updateActions(); + updateCaption(); + updateStatus(); + } + + return value; +} + +bool KomparePart::openDiff3( const KURL& diff3Url ) +{ + // FIXME: Implement this !!! + kdDebug(8103) << "Not implemented yet. Filename is: " << diff3Url.url() << endl; + return false; +} + +bool KomparePart::openDiff3( const QString& diff3Output ) +{ + // FIXME: Implement this !!! + kdDebug(8103) << "Not implemented yet. diff3 output is: " << endl; + kdDebug(8103) << diff3Output << endl; + return false; +} + +bool KomparePart::exists( const QString& url ) +{ + QFileInfo fi( url ); + return fi.exists(); +} + +const QString KomparePart::fetchURL( const KURL& url ) +{ + QString tempFileName( "" ); + if ( !url.isLocalFile() ) + { + if ( ! KIO::NetAccess::download( url, tempFileName, widget() ) ) + { + slotShowError( i18n( "The URL %1 cannot be downloaded." ).arg( url.prettyURL() ) ); + tempFileName = ""; + } + return tempFileName; + } + else + { + // is Local already, check if exists + if ( exists( url.path() ) ) + return url.path(); + else + { + slotShowError( i18n( "The URL %1 does not exist on your system." ).arg( url.prettyURL() ) ); + return tempFileName; + } + } +} + +void KomparePart::cleanUpTemporaryFiles() +{ + // i hope a local file will not be removed if it was not downloaded... + if ( !m_info.localSource.isEmpty() ) + KIO::NetAccess::removeTempFile( m_info.localSource ); + if ( !m_info.localDestination.isEmpty() ) + KIO::NetAccess::removeTempFile( m_info.localDestination ); +} + +void KomparePart::compare( const KURL& source, const KURL& destination ) +{ + m_info.source = source; + m_info.destination = destination; + + m_info.localSource = fetchURL( source ); + m_info.localDestination = fetchURL( destination ); + + emit kompareInfo( &m_info ); + + if ( !m_info.localSource.isEmpty() && !m_info.localDestination.isEmpty() ) + { + m_modelList->compare( m_info.localSource, m_info.localDestination ); + updateActions(); + updateCaption(); + updateStatus(); + } +} + +void KomparePart::compareFiles( const KURL& sourceFile, const KURL& destinationFile ) +{ + emit kompareInfo( &m_info ); + + m_info.mode = Kompare::ComparingFiles; + + m_info.source = sourceFile; + m_info.destination = destinationFile; + + m_info.localSource = fetchURL( sourceFile ); + m_info.localDestination = fetchURL( destinationFile ); + + if ( !m_info.localSource.isEmpty() && !m_info.localDestination.isEmpty() ) + { + m_modelList->compareFiles( m_info.localSource, m_info.localDestination ); + updateActions(); + updateCaption(); + updateStatus(); + } +} + +void KomparePart::compareDirs( const KURL& sourceDirectory, const KURL& destinationDirectory ) +{ + emit kompareInfo( &m_info ); + + m_info.mode = Kompare::ComparingDirs; + + m_info.source = sourceDirectory; + m_info.destination = destinationDirectory; + + m_info.localSource = fetchURL( sourceDirectory ); + m_info.localDestination = fetchURL( destinationDirectory ); + + if ( !m_info.localSource.isEmpty() && !m_info.localDestination.isEmpty() ) + { + m_modelList->compareDirs( m_info.localSource, m_info.localDestination ); + updateActions(); + updateCaption(); + updateStatus(); + } +} + +void KomparePart::compare3Files( const KURL& /*originalFile*/, const KURL& /*changedFile1*/, const KURL& /*changedFile2*/ ) +{ + // FIXME: actually implement this some day :) + updateActions(); + updateCaption(); + updateStatus(); +} + +void KomparePart::openFileAndDiff( const KURL& file, const KURL& diffFile ) +{ + emit kompareInfo( &m_info ); + + m_info.source = file; + m_info.destination = diffFile; + + m_info.localSource = fetchURL( file ); + m_info.localDestination = fetchURL( diffFile ); + m_info.mode = Kompare::BlendingFile; + + if ( !m_info.localSource.isEmpty() && !m_info.localDestination.isEmpty() ) + { + m_modelList->openFileAndDiff( m_info.localSource, m_info.localDestination ); + updateActions(); + updateCaption(); + updateStatus(); + } +} + +void KomparePart::openDirAndDiff ( const KURL& dir, const KURL& diffFile ) +{ + emit kompareInfo( &m_info ); + + m_info.source = dir; + m_info.destination = diffFile; + + m_info.localSource = fetchURL( dir ); + m_info.localDestination = fetchURL( diffFile ); + m_info.mode = Kompare::BlendingDir; + + if ( !m_info.localSource.isEmpty() && !m_info.localDestination.isEmpty() ) + { + m_modelList->openDirAndDiff( m_info.localSource, m_info.localDestination ); + updateActions(); + updateCaption(); + updateStatus(); + } +} + +bool KomparePart::openFile() +{ + // This is called from openURL + // This is a little inefficient but i will do it anyway + openDiff( m_url ); + return true; +} + +bool KomparePart::saveAll() +{ + bool result = m_modelList->saveAll(); + updateActions(); + updateCaption(); + updateStatus(); + return result; +} + +void KomparePart::saveDiff() +{ + KDialogBase* dlg = new KDialogBase( widget(), "save_options", + true /* modal */, i18n("Diff Options"), + KDialogBase::Ok|KDialogBase::Cancel ); + KompareSaveOptionsWidget* w = new KompareSaveOptionsWidget( + m_info.localSource, + m_info.localDestination, + m_diffSettings, dlg ); + dlg->setMainWidget( w ); + dlg->setButtonOK( KStdGuiItem::save() ); + + if( dlg->exec() ) { + w->saveOptions(); + KConfig* config = instance()->config(); + saveProperties( config ); + config->sync(); + + while ( 1 ) + { + KURL url = KFileDialog::getSaveURL( m_info.destination.url(), + i18n("*.diff *.dif *.patch|Patch Files"), widget(), i18n( "Save .diff" ) ); + if ( KIO::NetAccess::exists( url, false, widget() ) ) + { + int result = KMessageBox::warningYesNoCancel( widget(), i18n("The file exists or is write-protected; do you want to overwrite it?"), i18n("File Exists"), i18n("Overwrite"), i18n("Do Not Overwrite") ); + if ( result == KMessageBox::Cancel ) + { + break; + } + else if ( result == KMessageBox::No ) + { + continue; + } + else + { + kdDebug(8103) << "URL = " << url.prettyURL() << endl; + kdDebug(8103) << "Directory = " << w->directory() << endl; + kdDebug(8103) << "DiffSettings = " << m_diffSettings << endl; + + m_modelList->saveDiff( url.url(), w->directory(), m_diffSettings ); + break; + } + } + else + { + kdDebug(8103) << "URL = " << url.prettyURL() << endl; + kdDebug(8103) << "Directory = " << w->directory() << endl; + kdDebug(8103) << "DiffSettings = " << m_diffSettings << endl; + + m_modelList->saveDiff( url.url(), w->directory(), m_diffSettings ); + break; + } + } + } + delete dlg; +} + +KAboutData *KomparePart::createAboutData() +{ + KAboutData *about = new KAboutData("kompare", I18N_NOOP("KomparePart"), "3.2"); + about->addAuthor("John Firebaugh", "Author", "jfirebaugh@kde.org"); + about->addAuthor("Otto Bruggeman", "Author", "otto.bruggeman@home.nl" ); + return about; +} + +void KomparePart::slotSetStatus( enum Kompare::Status status ) +{ + updateActions(); + + switch( status ) { + case Kompare::RunningDiff: + emit setStatusBarText( i18n( "Running diff..." ) ); + break; + case Kompare::Parsing: + emit setStatusBarText( i18n( "Parsing diff output..." ) ); + break; + case Kompare::FinishedParsing: + updateStatus(); + break; + case Kompare::FinishedWritingDiff: + updateStatus(); + emit diffURLChanged(); + break; + default: + break; + } +} + +void KomparePart::updateCaption() +{ + QString source = m_info.source.prettyURL(); + QString destination = m_info.destination.prettyURL(); + + QString text; + + switch ( m_info.mode ) + { + case Kompare::ComparingFiles : + case Kompare::ComparingDirs : + case Kompare::BlendingFile : + case Kompare::BlendingDir : + text = source + ":" + destination; + break; + case Kompare::ShowingDiff : + text = source; + break; + default: + break; + } + + emit setWindowCaption( text ); +} + +void KomparePart::updateStatus() +{ + QString source = m_info.source.prettyURL(); + QString destination = m_info.destination.prettyURL(); + + QString text; + + switch ( m_info.mode ) + { + case Kompare::ComparingFiles : + text = i18n( "Comparing file %1 with file %2" ) + .arg( source ) + .arg( destination ); + break; + case Kompare::ComparingDirs : + text = i18n( "Comparing files in %1 with files in %2" ) + .arg( source ) + .arg( destination ); + break; + case Kompare::ShowingDiff : + text = i18n( "Viewing diff output from %1" ).arg( source ); + break; + case Kompare::BlendingFile : + text = i18n( "Blending diff output from %1 into file %2" ) + .arg( source ) + .arg( destination ); + break; + case Kompare::BlendingDir : + text = i18n( "Blending diff output from %1 into folder %2" ) + .arg( m_info.source.prettyURL() ) + .arg( m_info.destination.prettyURL() ); + break; + default: + break; + } + + emit setStatusBarText( text ); +} + +void KomparePart::slotShowError( QString error ) +{ + KMessageBox::error( widget(), error ); +} + +void KomparePart::slotSwap() +{ + if ( isModified() ) + { + int query = KMessageBox::warningYesNoCancel + ( + widget(), + i18n( "You have made changes to the destination file(s).\n" + "Would you like to save them?" ), + i18n( "Save Changes?" ), + KStdGuiItem::save(), + KStdGuiItem::discard() + ); + + if ( query == KMessageBox::Yes ) + m_modelList->saveAll(); + + if ( query == KMessageBox::Cancel ) + return; // Abort prematurely so no swapping + } + + // Swap the info in the Kompare::Info struct + KURL url = m_info.source; + m_info.source = m_info.destination; + m_info.destination = url; + + QString string = m_info.localSource; + m_info.localSource = m_info.localDestination; + m_info.localDestination = string; + + // Update window caption and statusbar text + updateCaption(); + updateStatus(); + + m_modelList->swap(); +} + +void KomparePart::slotShowDiffstats( void ) +{ + // Fetch all the args needed for komparestatsmessagebox + // oldfile, newfile, diffformat, noofhunks, noofdiffs + + QString oldFile; + QString newFile; + QString diffFormat; + int filesInDiff; + int noOfHunks; + int noOfDiffs; + + oldFile = m_modelList->selectedModel() ? m_modelList->selectedModel()->sourceFile() : QString( "" ); + newFile = m_modelList->selectedModel() ? m_modelList->selectedModel()->destinationFile() : QString( "" ); + + if ( m_modelList->selectedModel() ) + { + switch( m_info.format ) { + case Kompare::Unified : + diffFormat = i18n( "Unified" ); + break; + case Kompare::Context : + diffFormat = i18n( "Context" ); + break; + case Kompare::RCS : + diffFormat = i18n( "RCS" ); + break; + case Kompare::Ed : + diffFormat = i18n( "Ed" ); + break; + case Kompare::Normal : + diffFormat = i18n( "Normal" ); + break; + case Kompare::UnknownFormat : + default: + diffFormat = i18n( "Unknown" ); + break; + } + } + else + { + diffFormat = ""; + } + + filesInDiff = m_modelList->modelCount(); + + noOfHunks = m_modelList->selectedModel() ? m_modelList->selectedModel()->hunkCount() : 0; + noOfDiffs = m_modelList->selectedModel() ? m_modelList->selectedModel()->differenceCount() : 0; + + if ( m_modelList->modelCount() == 0 ) { // no diff loaded yet + KMessageBox::information( 0L, i18n( + "No diff file, or no 2 files have been diffed. " + "Therefore no stats are available."), + i18n("Diff Statistics"), QString::null, false ); + } + else if ( m_modelList->modelCount() == 1 ) { // 1 file in diff, or 2 files compared + KMessageBox::information( 0L, i18n( + "Statistics:\n" + "\n" + "Old file: %1\n" + "New file: %2\n" + "\n" + "Format: %3\n" + "Number of hunks: %4\n" + "Number of differences: %5") + .arg(oldFile).arg(newFile).arg(diffFormat) + .arg(noOfHunks).arg(noOfDiffs), + i18n("Diff Statistics"), QString::null, false ); + } else { // more than 1 file in diff, or 2 directories compared + KMessageBox::information( 0L, i18n( + "Statistics:\n" + "\n" + "Number of files in diff file: %1\n" + "Format: %2\n" + "\n" + "Current old file: %3\n" + "Current new file: %4\n" + "\n" + "Number of hunks: %5\n" + "Number of differences: %6") + .arg(filesInDiff).arg(diffFormat).arg(oldFile) + .arg(newFile).arg(noOfHunks).arg(noOfDiffs), + i18n("Diff Statistics"), QString::null, false ); + } +} + +bool KomparePart::queryClose() +{ + if( !isModified() ) return true; + + int query = KMessageBox::warningYesNoCancel + ( + widget(), + i18n("You have made changes to the destination file(s).\n" + "Would you like to save them?" ), + i18n( "Save Changes?" ), + KStdGuiItem::save(), + KStdGuiItem::discard() + ); + + if( query == KMessageBox::Cancel ) + return false; + + if( query == KMessageBox::Yes ) + return m_modelList->saveAll(); + + return true; +} + +int KomparePart::readProperties( KConfig *config ) +{ + m_viewSettings->loadSettings( config ); + m_diffSettings->loadSettings( config ); + emit configChanged(); + return 0; +} + +int KomparePart::saveProperties( KConfig *config ) +{ + m_viewSettings->saveSettings( config ); + m_diffSettings->saveSettings( config ); + return 0; +} + +void KomparePart::optionsPreferences() +{ + // show preferences + KomparePrefDlg* pref = new KomparePrefDlg( m_viewSettings, m_diffSettings ); + + connect( pref, SIGNAL(applyClicked()), this, SIGNAL(configChanged()) ); + + if ( pref->exec() ) + emit configChanged(); +} + +void KomparePart::slotSetModified( bool modified ) +{ + kdDebug() << "KomparePart::slotSetModified( " << modified << " );" << endl; + setModified( modified ); + updateActions(); + updateCaption(); +} + +#include "kompare_part.moc" diff --git a/kompare/komparepart/kompare_part.h b/kompare/komparepart/kompare_part.h new file mode 100644 index 00000000..0a9716c3 --- /dev/null +++ b/kompare/komparepart/kompare_part.h @@ -0,0 +1,223 @@ +/*************************************************************************** + kompare_part.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + (C) 2004 Jeff Snyder + email : otto.bruggeman@home.nl + jfirebaugh@kde.org + jeff@caffeinated.me.uk +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef KOMPAREPART_H +#define KOMPAREPART_H + +#include +#include + +#include "kompare.h" + +#include "kompareinterface.h" + +class QWidget; + +class KTempFile; +class KToggleAction; +class KURL; + +namespace Diff2 { +class Difference; +class DiffModel; +class DiffModelList; +class KompareModelList; +} +class DiffSettings; +class ViewSettings; +class KFileTreeView; +class KompareSplitter; +class KompareProcess; + +/** + * This is a "Part". It does all the real work in a KPart + * application. + * + * @short Main Part + * @author John Firebaugh + * @author Otto Bruggeman + * @version 0.3 + */ +class KomparePart : public KParts::ReadWritePart, + public KompareInterface +{ + Q_OBJECT +public: + /** + * Default constructor + */ + KomparePart( QWidget *parentWidget, const char *widgetName, + QObject *parent, const char *name, const QStringList & /*args*/); + + /** + * Destructor + */ + virtual ~KomparePart(); + + // Sessionmanagement stuff, added to the kompare iface + // because they are not in the Part class where they belong + // Should be added when bic changes are allowed again (kde 4.0) + virtual int readProperties( KConfig *config ); + virtual int saveProperties( KConfig *config ); + // this one is called when the shell_app is about to close. + // we need it now to save the properties of the part when apps dont (can't) + // use the readProperties and saveProperties methods + virtual bool queryClose(); + + // bool isModified() const { return m_modelList->isModified(); } + // Do we really want to expose this ??? + const Diff2::KompareModelList* model() const { return m_modelList; }; + + static KAboutData *createAboutData(); + +public: + // Reimplemented from the KompareInterface + /** + * Open and parse the diff file at diffUrl. + */ + virtual bool openDiff( const KURL& diffUrl ); + + /** Added on request of Harald Fernengel */ + virtual bool openDiff( const QString& diffOutput ); + + /** Open and parse the diff3 file at diff3Url */ + virtual bool openDiff3( const KURL& diff3URL ); + + /** Open and parse the file diff3Output with the output of diff3 */ + virtual bool openDiff3( const QString& diff3Output ); + + /** Compare, with diff, source with destination */ + virtual void compare( const KURL& sourceFile, const KURL& destinationFile ); + + /** Compare, with diff, source with destination */ + virtual void compareFiles( const KURL& sourceFile, const KURL& destinationFile ); + + /** Compare, with diff, source with destination */ + virtual void compareDirs ( const KURL& sourceDir, const KURL& destinationDir ); + + /** Compare, with diff3, originalFile with changedFile1 and changedFile2 */ + virtual void compare3Files( const KURL& originalFile, const KURL& changedFile1, const KURL& changedFile2 ); + + /** This will show the file and the file with the diff applied */ + virtual void openFileAndDiff( const KURL& file, const KURL& diffFile ); + + /** This will show the directory and the directory with the diff applied */ + virtual void openDirAndDiff ( const KURL& dir, const KURL& diffFile ); + + /** Reimplementing this because this one knows more about the real part then the interface */ + virtual void setEncoding( const QString& encoding ); + + // This is the interpart interface, it is signal and slot based so no "real" interface here + // All you have to do is connect the parts from your application. + // These just point to their counterpart in the KompareModelList or get called from their + // counterpart in KompareModelList. +signals: + void modelsChanged( const Diff2::DiffModelList* models ); + + void setSelection( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void setSelection( const Diff2::Difference* diff ); + + void selectionChanged( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void selectionChanged( const Diff2::Difference* diff ); + + void applyDifference( bool apply ); + void applyAllDifferences( bool apply ); + void applyDifference( const Diff2::Difference*, bool apply ); + + void configChanged(); + + /* + ** This is emitted when a difference is clicked in the kompare view. You can connect to + ** it so you can use it to jump to this particular line in the editor in your app. + */ + void differenceClicked( int lineNumber ); + + // Stuff that can probably be removed by putting it in the part where it belongs in my opinion +public slots: + /** Save all destinations. */ + bool saveAll(); + + /** Save the results of a comparison as a diff file. */ + void saveDiff(); + + /** This slot is connected to the setModifed( bool ) signal from the KompareModelList */ + void slotSetModified( bool modified ); + +signals: + void appliedChanged(); + void diffURLChanged(); + void kompareInfo( Kompare::Info* info ); + void setStatusBarModelInfo( int modelIndex, int differenceIndex, int modelCount, int differenceCount, int appliedCount ); +// void setStatusBarText( const QString& text ); + +protected: + /** + * This is the method that gets called when the file is opened, + * when using openURL( const KURL& ) or in our case also openDiff( const KURL& ); + * return true when everything went ok, false if there were problems + */ + virtual bool openFile(); + virtual bool saveFile() { return true; }; + + // patchFile + bool patchFile(KURL&); + bool patchDir(); + +protected slots: + void slotSetStatus( Kompare::Status status ); + void slotShowError( QString error ); + + void slotSwap(); + void slotShowDiffstats(); + void optionsPreferences(); + void updateActions(); + void updateCaption(); + void updateStatus(); + +private: + void cleanUpTemporaryFiles(); + void setupActions(); + bool exists( const QString& url ); + bool isDirectory( const KURL& url ); + const QString fetchURL( const KURL& url ); + +private: + // Uhm why were these static again ??? + // Ah yes, so multiple instances of kompare use the + // same settings after one of them changes them + static ViewSettings* m_viewSettings; + static DiffSettings* m_diffSettings; + + Diff2::KompareModelList* m_modelList; + + KompareSplitter* m_splitter; + + KAction* m_saveAll; + KAction* m_saveDiff; + KAction* m_swap; + KAction* m_diffStats; + + KTempFile* m_tempDiff; + + struct Kompare::Info m_info; +}; + +#endif // KOMPAREPART_H diff --git a/kompare/komparepart/kompare_qsplitter.h b/kompare/komparepart/kompare_qsplitter.h new file mode 100644 index 00000000..1f1be49d --- /dev/null +++ b/kompare/komparepart/kompare_qsplitter.h @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** +** Definition of QSplitter class +** +** Created : 980105 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the widgets module of the Qt GUI Toolkit. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +**********************************************************************/ + +#ifndef QSPLITTER_H +#define QSPLITTER_H + +#ifndef QT_H +#include "qframe.h" +#include "qvaluelist.h" +#endif // QT_H + +#ifndef QT_NO_SPLITTER + + +//class QSplitterHandle; +class QSplitterLayoutStruct; +class QTextStream; + +class QSplitterPrivate +{ +public: + QSplitterPrivate() + : opaque( FALSE ), firstShow( TRUE ), childrenCollapsible( TRUE ), + handleWidth( 0 ) { } + + QPtrList list; + bool opaque : 8; + bool firstShow : 8; + bool childrenCollapsible : 8; + int handleWidth; +}; + +class Q_EXPORT QSplitter : public QFrame +{ + Q_OBJECT + Q_PROPERTY( Orientation orientation READ orientation WRITE setOrientation ) + Q_PROPERTY( bool opaqueResize READ opaqueResize WRITE setOpaqueResize ) + Q_PROPERTY( int handleWidth READ handleWidth WRITE setHandleWidth ) + Q_PROPERTY( bool childrenCollapsible READ childrenCollapsible WRITE setChildrenCollapsible ) + +public: + // ### Qt 4.0: remove Auto from public API + enum ResizeMode { Stretch, KeepSize, FollowSizeHint, Auto }; + + QSplitter( QWidget* parent = 0, const char* name = 0 ); + QSplitter( Orientation, QWidget* parent = 0, const char* name = 0 ); + ~QSplitter(); + + virtual void setOrientation( Orientation ); + Orientation orientation() const { return orient; } + + // ### Qt 4.0: make setChildrenCollapsible() and setCollapsible() virtual + + void setChildrenCollapsible( bool ); + bool childrenCollapsible() const; + + void setCollapsible( QWidget *w, bool ); + virtual void setResizeMode( QWidget *w, ResizeMode ); + virtual void setOpaqueResize( bool = TRUE ); + bool opaqueResize() const; + + void moveToFirst( QWidget * ); + void moveToLast( QWidget * ); + + void refresh() { recalc( TRUE ); } + QSize sizeHint() const; + QSize minimumSizeHint() const; + + QValueList sizes() const; + void setSizes( QValueList ); + + int handleWidth() const; + void setHandleWidth( int ); + +protected: + void childEvent( QChildEvent * ); + + bool event( QEvent * ); + void resizeEvent( QResizeEvent * ); + + int idAfter( QWidget* ) const; + + void moveSplitter( QCOORD pos, int id ); + virtual void drawSplitter( QPainter*, QCOORD x, QCOORD y, + QCOORD w, QCOORD h ); + void styleChange( QStyle& ); + int adjustPos( int, int ); + virtual void setRubberband( int ); + void getRange( int id, int *, int * ); + +public: // private (: + enum { DefaultResizeMode = 3 }; + + void init(); + void recalc( bool update = FALSE ); + void doResize(); + void storeSizes(); + void getRange( int id, int *, int *, int *, int * ); + void addContribution( int, int *, int *, bool ); + int adjustPos( int, int, int *, int *, int *, int * ); + bool collapsible( QSplitterLayoutStruct * ); + void processChildEvents(); + QSplitterLayoutStruct *findWidget( QWidget * ); + QSplitterLayoutStruct *addWidget( QWidget *, bool prepend = FALSE ); + void recalcId(); + void doMove( bool backwards, int pos, int id, int delta, bool upLeft, + bool mayCollapse ); + void setGeo( QWidget *w, int pos, int size, bool splitterMoved ); + int findWidgetJustBeforeOrJustAfter( int id, int delta, int &collapsibleSize ); + void updateHandles(); + + inline QCOORD pick( const QPoint &p ) const + { return orient == Horizontal ? p.x() : p.y(); } + inline QCOORD pick( const QSize &s ) const + { return orient == Horizontal ? s.width() : s.height(); } + + inline QCOORD trans( const QPoint &p ) const + { return orient == Vertical ? p.x() : p.y(); } + inline QCOORD trans( const QSize &s ) const + { return orient == Vertical ? s.width() : s.height(); } + + QSplitterPrivate *d; + + Orientation orient; + friend class QSplitterHandle; + +#ifndef QT_NO_TEXTSTREAM +// moc doesn't like these. +// friend Q_EXPORT QTextStream& operator<<( QTextStream&, const QSplitter& ); +// friend Q_EXPORT QTextStream& operator>>( QTextStream&, QSplitter& ); +#endif + +public: +#if defined(Q_DISABLE_COPY) + QSplitter( const QSplitter & ); + QSplitter& operator=( const QSplitter & ); +#endif +}; + +#ifndef QT_NO_TEXTSTREAM +Q_EXPORT QTextStream& operator<<( QTextStream&, const QSplitter& ); +Q_EXPORT QTextStream& operator>>( QTextStream&, QSplitter& ); +#endif + +class QSplitterHandle : public QWidget +{ + Q_OBJECT +public: + QSplitterHandle( Orientation o, + QSplitter *parent, const char* name=0 ); + void setOrientation( Orientation o ); + Orientation orientation() const { return orient; } + + bool opaque() const { return s->opaqueResize(); } + + QSize sizeHint() const; + + int id() const { return myId; } // d->list.at(id())->wid == this + void setId( int i ) { myId = i; } + +protected: + void paintEvent( QPaintEvent * ); + void mouseMoveEvent( QMouseEvent * ); + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + +public: // private (: + Orientation orient; + bool opaq; + int myId; + + QSplitter *s; +}; + +const uint Default = 2; + +class QSplitterLayoutStruct : public Qt +{ +public: + QCOORD sizer; + uint isHandle : 1; + uint collapsible : 2; + uint resizeMode : 2; + QWidget *wid; + + QSplitterLayoutStruct() + : sizer( -1 ), collapsible( Default ) { } + QCOORD getSizer( Orientation orient ); +}; + +#endif // QT_NO_SPLITTER + +#endif // QSPLITTER_H diff --git a/kompare/komparepart/kompareconnectwidget.cpp b/kompare/komparepart/kompareconnectwidget.cpp new file mode 100644 index 00000000..eed8e99c --- /dev/null +++ b/kompare/komparepart/kompareconnectwidget.cpp @@ -0,0 +1,285 @@ +/*************************************************************************** + kompareconnectwidget.cpp - description + ------------------- + begin : Tue Jun 26 2001 + copyright : (C) 2001-2003 John Firebaugh + (C) 2001-2004 Otto Bruggeman + (C) 2004 Jeff Snyder + email : jfirebaugh@kde.org + otto.bruggeman@home.nl + jeff@caffeinated.me.uk + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include + +#include + +#include "viewsettings.h" +#include "komparemodellist.h" +#include "komparelistview.h" +#include "komparesplitter.h" + +#include "kompareconnectwidget.h" + +using namespace Diff2; + +KompareConnectWidgetFrame::KompareConnectWidgetFrame( KompareListView* left, + KompareListView* right, + ViewSettings* settings, + KompareSplitter* parent, + const char* name ) : + QSplitterHandle(Horizontal, (QSplitter *)parent, name), + m_wid ( left, right, settings, this, name ), + m_label ( "", this ), + m_layout ( this ) +{ + setSizePolicy ( QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored) ); + m_wid.setSizePolicy ( QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored) ); + m_label.setSizePolicy ( QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed) ); + m_label.setMargin(3); + QFrame* bottomLine = new QFrame(this); + bottomLine->setFrameShape(QFrame::HLine); + bottomLine->setFrameShadow ( QFrame::Plain ); + bottomLine->setSizePolicy ( QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed) ); + bottomLine->setFixedHeight(1); + m_layout.setSpacing(0); + m_layout.setMargin(0); + m_layout.addWidget(&m_label); + m_layout.addWidget(bottomLine); + m_layout.addWidget(&m_wid); +} + +KompareConnectWidgetFrame::~KompareConnectWidgetFrame() +{ +} + +QSize KompareConnectWidgetFrame::sizeHint() const +{ + return QSize(50, style().pixelMetric( QStyle::PM_ScrollBarExtent ) ); +} + +static int kMouseOffset; + +void KompareConnectWidgetFrame::mouseMoveEvent( QMouseEvent *e ) +{ + if ( !(e->state()&LeftButton) ) + return; + + QCOORD pos = s->pick( parentWidget()->mapFromGlobal(e->globalPos()) ) + - kMouseOffset; + + ((KompareSplitter*)s)->moveSplitter( pos, id() ); +} + +void KompareConnectWidgetFrame::mousePressEvent( QMouseEvent *e ) +{ + if ( e->button() == LeftButton ) + kMouseOffset = s->pick( e->pos() ); + QSplitterHandle::mousePressEvent(e); +} + +void KompareConnectWidgetFrame::mouseReleaseEvent( QMouseEvent *e ) +{ + if ( !opaque() && e->button() == LeftButton ) { + QCOORD pos = s->pick( parentWidget()->mapFromGlobal(e->globalPos()) ) + - kMouseOffset; + ((KompareSplitter*)s)->moveSplitter( pos, id() ); + } +} + +KompareConnectWidget::KompareConnectWidget( KompareListView* left, KompareListView* right, + ViewSettings* settings, QWidget* parent, const char* name ) + : QWidget(parent, name), + m_settings( settings ), + m_leftView( left ), + m_rightView( right ), + m_selectedModel( 0 ), + m_selectedDifference( 0 ) +{ +// connect( m_settings, SIGNAL( settingsChanged() ), this, SLOT( slotDelayedRepaint() ) ); + setBackgroundMode( NoBackground ); + setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Minimum ) ); + setFocusProxy( parent->parentWidget() ); +} + +KompareConnectWidget::~KompareConnectWidget() +{ +} + +void KompareConnectWidget::slotSetSelection( const DiffModel* model, const Difference* diff ) +{ + if( m_selectedModel == model && m_selectedDifference == diff ) + return; + + if ( m_selectedModel == model && m_selectedDifference != diff ) + { + m_selectedDifference = diff; + slotDelayedRepaint(); + return; + } + + m_selectedModel = model; + m_selectedDifference = diff; + + slotDelayedRepaint(); +} + +void KompareConnectWidget::slotDelayedRepaint() +{ + QTimer::singleShot( 0, this, SLOT( repaint() ) ); +} + +void KompareConnectWidget::slotSetSelection( const Difference* diff ) +{ + if ( m_selectedDifference == diff ) + return; + + m_selectedDifference = diff; + + slotDelayedRepaint(); +} + +void KompareConnectWidget::paintEvent( QPaintEvent* /* e */ ) +{ +// kdDebug(8106) << "KompareConnectWidget::paintEvent()" << endl; + + QPixmap pixbuf(size()); + QPainter paint(&pixbuf, this); + QPainter* p = &paint; + + p->fillRect( 0, 0, pixbuf.width(), pixbuf.height(), white.dark(110) ); + + if ( m_selectedModel ) + { + int firstL = m_leftView->firstVisibleDifference(); + int firstR = m_rightView->firstVisibleDifference(); + int lastL = m_leftView->lastVisibleDifference(); + int lastR = m_rightView->lastVisibleDifference(); + + int first = firstL < 0 ? firstR : QMIN( firstL, firstR ); + int last = lastL < 0 ? lastR : QMAX( lastL, lastR ); + +// kdDebug(8106) << " left: " << firstL << " - " << lastL << endl; +// kdDebug(8106) << " right: " << firstR << " - " << lastR << endl; +// kdDebug(8106) << " drawing: " << first << " - " << last << endl; + if ( first >= 0 && last >= 0 && first <= last ) + { + const DifferenceList* differences = const_cast(m_selectedModel)->differences(); + DifferenceListConstIterator diffIt = differences->at( first ); + DifferenceListConstIterator dEnd = differences->at( last + 1 ); + + QRect leftRect, rightRect; + + for ( int i = first; i <= last; ++diffIt, ++i ) + { + Difference* diff = *diffIt; + bool selected = ( diff == m_selectedDifference ); + + if ( QApplication::reverseLayout() ) + { + leftRect = m_rightView->itemRect( i ); + rightRect = m_leftView->itemRect( i ); + } + else + { + leftRect = m_leftView->itemRect( i ); + rightRect = m_rightView->itemRect( i ); + } + + int tl = leftRect.top(); + int tr = rightRect.top(); + int bl = leftRect.bottom(); + int br = rightRect.bottom(); + + // Bah, stupid 16-bit signed shorts in that crappy X stuff... + tl = tl >= -32768 ? tl : -32768; + tr = tr >= -32768 ? tr : -32768; + bl = bl <= 32767 ? bl : 32767; + br = br <= 32767 ? br : 32767; + +// kdDebug(8106) << "drawing: " << tl << " " << tr << " " << bl << " " << br << endl; + QPointArray topBezier = makeTopBezier( tl, tr ); + QPointArray bottomBezier = makeBottomBezier( bl, br ); + + QColor color = m_settings->colorForDifferenceType( diff->type(), selected, diff->applied() ).dark(110); + p->setPen( color ); + p->setBrush( color ); + p->drawPolygon ( makeConnectPoly( topBezier, bottomBezier ) ); + + if ( selected ) + { + p->setPen( color.dark(135) ); + p->drawPolyline( topBezier ); + p->drawPolyline( bottomBezier ); + } + } + } + } + + p->flush(); + bitBlt(this, 0, 0, &pixbuf); +} + +QPointArray KompareConnectWidget::makeTopBezier( int tl, int tr ) +{ + int l = 0; + int r = width(); + int o = (int)((double)r*0.4); // 40% of width + QPointArray controlPoints; + + if ( tl != tr ) + { + controlPoints.setPoints( 4, l,tl, o,tl, r-o,tr, r,tr ); + return controlPoints.cubicBezier(); + } + else + { + controlPoints.setPoints( 2, l,tl, r,tr ); + return controlPoints; + } +} + +QPointArray KompareConnectWidget::makeBottomBezier( int bl, int br ) +{ + int l = 0; + int r = width(); + int o = (int)((double)r*0.4); // 40% of width + QPointArray controlPoints; + + if ( bl != br ) + { + controlPoints.setPoints( 4, r,br, r-o,br, o,bl, l,bl ); + return controlPoints.cubicBezier(); + } + else + { + controlPoints.setPoints( 2, r,br, l,bl ); + return controlPoints; + } +} + +QPointArray KompareConnectWidget::makeConnectPoly( const QPointArray& topBezier, const QPointArray& bottomBezier ) +{ + QPointArray poly( topBezier.size() + bottomBezier.size() ); + for( uint i = 0; i < topBezier.size(); i++ ) + poly.setPoint( i, topBezier.point( i ) ); + for( uint i = 0; i < bottomBezier.size(); i++ ) + poly.setPoint( i + topBezier.size(), bottomBezier.point( i ) ); + + return poly; +} + +#include "kompareconnectwidget.moc" diff --git a/kompare/komparepart/kompareconnectwidget.h b/kompare/komparepart/kompareconnectwidget.h new file mode 100644 index 00000000..96160c97 --- /dev/null +++ b/kompare/komparepart/kompareconnectwidget.h @@ -0,0 +1,95 @@ +/*************************************************************************** + kompareconnectwidget.h - description + ------------------- + begin : Tue Jun 26 2001 + copyright : (C) 2001-2003 John Firebaugh + (C) 2001-2004 Otto Bruggeman + (C) 2004 Jeff Snyder + email : jfirebaugh@kde.org + otto.bruggeman@home.nl + jeff@caffeinated.me.uk + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KOMPARECONNECTWIDGET_H +#define KOMPARECONNECTWIDGET_H + +#include "kompare_qsplitter.h" +#include + +namespace Diff2 { +class DiffModel; +} +class ViewSettings; +class KompareListView; +class KompareSplitter; + +class KompareConnectWidget : public QWidget +{ + Q_OBJECT + +public: + KompareConnectWidget( KompareListView* left, KompareListView* right, + ViewSettings* settings, QWidget* parent, const char* name = 0 ); + ~KompareConnectWidget(); + +public slots: + void slotSetSelection( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void slotSetSelection( const Diff2::Difference* diff ); + + void slotDelayedRepaint(); + +signals: + void selectionChanged(const Diff2::Difference* diff); + +protected: + void paintEvent( QPaintEvent* e ); + QPointArray makeTopBezier( int tl, int tr ); + QPointArray makeBottomBezier( int bl, int br ); + QPointArray makeConnectPoly( const QPointArray& topBezier, const QPointArray& bottomBezier ); + +private: + ViewSettings* m_settings; + + KompareListView* m_leftView; + KompareListView* m_rightView; + + const Diff2::DiffModel* m_selectedModel; + const Diff2::Difference* m_selectedDifference; +}; + +class KompareConnectWidgetFrame : public QSplitterHandle +{ + Q_OBJECT +public: + KompareConnectWidgetFrame( KompareListView* left, KompareListView* right, + ViewSettings* settings, KompareSplitter* parent, const char* name = 0 ); + ~KompareConnectWidgetFrame(); + + QSize sizeHint() const; + + KompareConnectWidget* wid() { return &m_wid; } + +protected: + // stop the parent QSplitterHandle painting + void paintEvent( QPaintEvent* /* e */ ) { } + + void mouseMoveEvent( QMouseEvent * ); + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + +private: + KompareConnectWidget m_wid; + QLabel m_label; + QVBoxLayout m_layout; +}; + +#endif diff --git a/kompare/komparepart/komparelistview.cpp b/kompare/komparepart/komparelistview.cpp new file mode 100644 index 00000000..b86bdef9 --- /dev/null +++ b/kompare/komparepart/komparelistview.cpp @@ -0,0 +1,783 @@ +/*************************************************************************** + komparelistview.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + (C) 2004 Jeff Snyder + email : otto.bruggeman@home.nl + jfirebaugh@kde.org + jeff@caffeinated.me.uk +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include + +#include "diffmodel.h" +#include "diffhunk.h" +#include "difference.h" +#include "viewsettings.h" +#include "komparemodellist.h" +#include "komparesplitter.h" + +#include "komparelistview.h" + +#define COL_LINE_NO 0 +#define COL_MAIN 1 + +#define BLANK_LINE_HEIGHT 3 +#define HUNK_LINE_HEIGHT 5 + +using namespace Diff2; + +KompareListViewFrame::KompareListViewFrame( bool isSource, + ViewSettings* settings, + KompareSplitter* parent, + const char* name ): + QFrame ( parent, name ), + m_view ( isSource, settings, this, name ), + m_label ( isSource?"Source":"Dest", this ), + m_layout ( this ) +{ + setSizePolicy ( QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored) ); + m_label.setSizePolicy ( QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed) ); + QFrame *bottomLine = new QFrame(this); + bottomLine->setFrameShape(QFrame::HLine); + bottomLine->setFrameShadow ( QFrame::Plain ); + bottomLine->setSizePolicy ( QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed) ); + bottomLine->setFixedHeight(1); + m_label.setMargin(3); + m_layout.setSpacing(0); + m_layout.setMargin(0); + m_layout.addWidget(&m_label); + m_layout.addWidget(bottomLine); + m_layout.addWidget(&m_view); + + connect( &m_view, SIGNAL(differenceClicked(const Diff2::Difference*)), + parent, SLOT(slotDifferenceClicked(const Diff2::Difference*)) ); + + connect( parent, SIGNAL(scrollViewsToId(int)), &m_view, SLOT(scrollToId(int)) ); + connect( parent, SIGNAL(setXOffset(int)), &m_view, SLOT(setXOffset(int)) ); + connect( &m_view, SIGNAL(resized()), parent, SLOT(slotUpdateScrollBars()) ); +} + +void KompareListViewFrame::slotSetModel( const DiffModel* model ) +{ + if( model ) + { + if( view()->isSource() ) { + if( !model->sourceRevision().isEmpty() ) + m_label.setText( model->sourceFile() + " (" + model->sourceRevision() + ")" ); + else + m_label.setText( model->sourceFile() ); + } else { + if( !model->destinationRevision().isEmpty() ) + m_label.setText( model->destinationFile() + " (" + model->destinationRevision() + ")" ); + else + m_label.setText( model->destinationFile() ); + } + } else { + m_label.setText( QString::null ); + } +} + +KompareListView::KompareListView( bool isSource, + ViewSettings* settings, + QWidget* parent, const char* name ) : + KListView( parent, name ), + m_isSource( isSource ), + m_settings( settings ), + m_scrollId( -1 ), + m_selectedModel( 0 ), + m_selectedDifference( 0 ) +{ + header()->hide(); + addColumn( "Line Number", 0 ); + addColumn( "Main" ); + addColumn( "Blank" ); + setColumnAlignment( COL_LINE_NO, AlignRight ); + setAllColumnsShowFocus( true ); + setRootIsDecorated( false ); + setSorting( -1 ); + setItemMargin( 3 ); + setTreeStepSize( 0 ); + setColumnWidthMode( COL_LINE_NO, Maximum ); + setColumnWidthMode( COL_MAIN, Maximum ); + setResizeMode( LastColumn ); + setFrameStyle( QFrame::NoFrame ); + setVScrollBarMode( QScrollView::AlwaysOff ); + setHScrollBarMode( QScrollView::AlwaysOff ); + setFocusPolicy( QWidget::NoFocus ); + setFont( m_settings->m_font ); + setSpaces( m_settings->m_tabToNumberOfSpaces ); + setFocusProxy( parent->parentWidget() ); +} + +KompareListView::~KompareListView() +{ +} + +KompareListViewItem* KompareListView::itemAtIndex( int i ) +{ + return m_items[ i ]; +} + +int KompareListView::firstVisibleDifference() +{ + QListViewItem* item = itemAt( QPoint( 0, 0 ) ); + + if( item == 0 ) + { + kdDebug(8104) << "no item at viewport coordinates (0,0)" << endl; + } + + while( item ) { + KompareListViewLineItem* lineItem = dynamic_cast(item); + if( lineItem && lineItem->diffItemParent()->difference()->type() != Difference::Unchanged ) + break; + item = item->itemBelow(); + } + + if( item ) + return m_items.findIndex( ((KompareListViewLineItem*)item)->diffItemParent() ); + + return -1; +} + +int KompareListView::lastVisibleDifference() +{ + QListViewItem* item = itemAt( QPoint( 0, visibleHeight() - 1 ) ); + + if( item == 0 ) + { + kdDebug(8104) << "no item at viewport coordinates (0," << visibleHeight() - 1 << ")" << endl; + item = lastItem(); + } + + while( item ) { + KompareListViewLineItem* lineItem = dynamic_cast(item); + if( lineItem && lineItem->diffItemParent()->difference()->type() != Difference::Unchanged ) + break; + item = item->itemAbove(); + } + + if( item ) + return m_items.findIndex( ((KompareListViewLineItem*)item)->diffItemParent() ); + + return -1; +} + +QRect KompareListView::itemRect( int i ) +{ + QListViewItem* item = itemAtIndex( i ); + int x = 0; + int y = itemPos( item ); + int vx, vy; + contentsToViewport( x, y, vx, vy ); + return QRect( vx, vy, 0, item->totalHeight() ); +} + +int KompareListView::minScrollId() +{ + return visibleHeight() / 2; +} + +int KompareListView::maxScrollId() +{ + KompareListViewItem* item = (KompareListViewItem*)firstChild(); + if(!item) return 0; + while( item && item->nextSibling() ) { + item = (KompareListViewItem*)item->nextSibling(); + } + int maxId = item->scrollId() + item->maxHeight() - minScrollId(); + kdDebug(8104) << "Max ID = " << maxId << endl; + return maxId; +} + +int KompareListView::contentsWidth() +{ + return ( columnWidth(COL_LINE_NO) + columnWidth(COL_MAIN) ); +} + +void KompareListView::setXOffset( int x ) +{ + kdDebug(8104) << "SetXOffset : Scroll to x position: " << x << endl; + setContentsPos( x, contentsY() ); +} + +void KompareListView::scrollToId( int id ) +{ +// kdDebug(8104) << "ScrollToID : Scroll to id : " << id << endl; + KompareListViewItem* item = (KompareListViewItem*)firstChild(); + while( item && item->nextSibling() ) { + if( ((KompareListViewItem*)item->nextSibling())->scrollId() > id ) + break; + item = (KompareListViewItem*)item->nextSibling(); + } + + if( item ) { + int pos = item->itemPos(); + int itemId = item->scrollId(); + int height = item->totalHeight(); + double r = (double)( id - itemId ) / (double)item->maxHeight(); + int y = pos + (int)( r * (double)height ) - minScrollId(); +// kdDebug(8104) << "scrollToID: " << endl; +// kdDebug(8104) << " id = " << id << endl; +// kdDebug(8104) << " id after = " << ( item->nextSibling() ? QString::number( ((KompareListViewItem*)item->nextSibling())->scrollId() ) : "no such sibling..." ) << endl; +// kdDebug(8104) << " pos = " << pos << endl; +// kdDebug(8104) << " itemId = " << itemId << endl; +// kdDebug(8104) << " r = " << r << endl; +// kdDebug(8104) << " height = " << height << endl; +// kdDebug(8104) << " minID = " << minScrollId() << endl; +// kdDebug(8104) << " y = " << y << endl; +// kdDebug(8104) << "contentsHeight = " << contentsHeight() << endl; +// kdDebug(8104) << " c - y = " << contentsHeight() - y << endl; + setContentsPos( contentsX(), y ); + } + + m_scrollId = id; +} + +int KompareListView::scrollId() +{ + if( m_scrollId < 0 ) + m_scrollId = minScrollId(); + return m_scrollId; +} + +void KompareListView::setSelectedDifference( const Difference* diff, bool scroll ) +{ + kdDebug(8104) << "KompareListView::setSelectedDifference(" << diff << ", " << scroll << ")" << endl; + + // When something other than a click causes this function to be called, + // it'll only get called once, and all is simple. + // + // When the user clicks on a diff, this function will get called once when + // komparesplitter::slotDifferenceClicked runs, and again when the + // setSelection signal from the modelcontroller arrives. + // + // the first call (which will always be from the splitter) will have + // scroll==false, and the the second call will bail out here. + // Which is why clicking on a difference does not cause the listviews to + // scroll. + if ( m_selectedDifference == diff ) + return; + + m_selectedDifference = diff; + + KompareListViewItem* item = m_itemDict[ (void*)diff ]; + if( !item ) { + kdDebug(8104) << "KompareListView::slotSetSelection(): couldn't find our selection!" << endl; + return; + } + + // why does this not happen when the user clicks on a diff? see the comment above. + if( scroll ) + scrollToId(item->scrollId()); + setSelected( item, true ); +} + +void KompareListView::slotSetSelection( const Difference* diff ) +{ + kdDebug(8104) << "KompareListView::slotSetSelection( const Difference* diff )" << endl; + + setSelectedDifference( diff, true ); +} + +void KompareListView::slotSetSelection( const DiffModel* model, const Difference* diff ) +{ + kdDebug(8104) << "KompareListView::slotSetSelection( const DiffModel* model, const Difference* diff )" << endl; + + if( m_selectedModel && m_selectedModel == model ) { + slotSetSelection( diff ); + return; + } + + clear(); + m_items.clear(); + m_itemDict.clear(); + m_selectedModel = model; + + m_itemDict.resize(model->differenceCount()); + + DiffHunkListConstIterator hunkIt = model->hunks()->begin(); + DiffHunkListConstIterator hEnd = model->hunks()->end(); + + KompareListViewItem* item = 0; + Difference* tmpdiff = 0; + DiffHunk* hunk = 0; + + + for ( ; hunkIt != hEnd; ++hunkIt ) + { + hunk = *hunkIt; + + if( item ) + item = new KompareListViewHunkItem( this, item, hunk, model->isBlended() ); + else + item = new KompareListViewHunkItem( this, hunk, model->isBlended() ); + + DifferenceListConstIterator diffIt = hunk->differences().begin(); + DifferenceListConstIterator dEnd = hunk->differences().end(); + + for ( ; diffIt != dEnd; ++diffIt ) + { + tmpdiff = *diffIt; + + item = new KompareListViewDiffItem( this, item, tmpdiff ); + + int type = tmpdiff->type(); + + if ( type != Difference::Unchanged ) + { + m_items.append( (KompareListViewDiffItem*)item ); + m_itemDict.insert( tmpdiff, (KompareListViewDiffItem*)item ); + } + } + } + + slotSetSelection( diff ); +} + +void KompareListView::contentsMousePressEvent( QMouseEvent* e ) +{ + QPoint vp = contentsToViewport( e->pos() ); + KompareListViewLineItem* lineItem = dynamic_cast( itemAt( vp ) ); + if( !lineItem ) + return; + KompareListViewDiffItem* diffItem = lineItem->diffItemParent(); + if( diffItem->difference()->type() != Difference::Unchanged ) { + emit differenceClicked( diffItem->difference() ); + } +} + +void KompareListView::contentsMouseDoubleClickEvent( QMouseEvent* e ) +{ + QPoint vp = contentsToViewport( e->pos() ); + KompareListViewLineItem* lineItem = dynamic_cast( itemAt( vp ) ); + if ( !lineItem ) + return; + KompareListViewDiffItem* diffItem = lineItem->diffItemParent(); + if ( diffItem->difference()->type() != Difference::Unchanged ) { + // FIXME: make a new signal that does both + emit differenceClicked( diffItem->difference() ); + emit applyDifference( !diffItem->difference()->applied() ); + } +} + +void KompareListView::slotApplyDifference( bool apply ) +{ + m_itemDict[ (void*)m_selectedDifference ]->applyDifference( apply ); +} + +void KompareListView::slotApplyAllDifferences( bool apply ) +{ + QPtrDictIterator it ( m_itemDict ); + for( ; it.current(); ++it ) + it.current()->applyDifference( apply ); + repaint(); +} + +void KompareListView::slotApplyDifference( const Difference* diff, bool apply ) +{ + m_itemDict[ (void*)diff ]->applyDifference( apply ); +} + +void KompareListView::setSpaces( int spaces ) +{ + m_spaces.truncate( 0 ); + kdDebug( 8104 ) << "tabToNumberOfSpaces: " << spaces << endl; + for ( int i = 0; i < spaces; i++ ) + m_spaces += " "; + + triggerUpdate(); +} + +void KompareListView::wheelEvent( QWheelEvent* e ) +{ + e->ignore(); // we want the parent to catch wheel events +} + +void KompareListView::resizeEvent( QResizeEvent* e ) +{ + KListView::resizeEvent(e); + emit resized(); + kdDebug() << "resizeEvent " << endl; +} + +KompareListViewItem::KompareListViewItem( KompareListView* parent ) + : QListViewItem( parent ), + m_scrollId( 0 ) +{ +// kdDebug(8104) << "Created KompareListViewItem with scroll id " << m_scrollId << endl; +} + +KompareListViewItem::KompareListViewItem( KompareListView* parent, KompareListViewItem* after ) + : QListViewItem( parent, after ), + m_scrollId( after->scrollId() + after->maxHeight() ) +{ +// kdDebug(8104) << "Created KompareListViewItem with scroll id " << m_scrollId << endl; +} + +KompareListViewItem::KompareListViewItem( KompareListViewItem* parent ) + : QListViewItem( parent ), + m_scrollId( 0 ) +{ +} + +KompareListViewItem::KompareListViewItem( KompareListViewItem* parent, KompareListViewItem* /*after*/ ) + : QListViewItem( parent ), + m_scrollId( 0 ) +{ +} + +KompareListView* KompareListViewItem::kompareListView() const +{ + return (KompareListView*)listView(); +} + +void KompareListViewItem::paintFocus( QPainter* /* p */, const QColorGroup& /* cg */, const QRect& /* r */ ) +{ + // Don't paint anything +} + +KompareListViewDiffItem::KompareListViewDiffItem( KompareListView* parent, Difference* difference ) + : KompareListViewItem( parent ), + m_difference( difference ), + m_sourceItem( 0L ), + m_destItem( 0L ) +{ + init(); +} + +KompareListViewDiffItem::KompareListViewDiffItem( KompareListView* parent, KompareListViewItem* after, Difference* difference ) + : KompareListViewItem( parent, after ), + m_difference( difference ), + m_sourceItem( 0L ), + m_destItem( 0L ) +{ + init(); +} + +void KompareListViewDiffItem::init() +{ + setExpandable( true ); + setOpen( true ); + m_destItem = new KompareListViewLineContainerItem( this, false ); + m_sourceItem = new KompareListViewLineContainerItem( this, true ); + setVisibility(); +} + +void KompareListViewDiffItem::setup() +{ + KompareListViewItem::setup(); + setHeight( 0 ); +} + +void KompareListViewDiffItem::setVisibility() +{ + m_sourceItem->setVisible( kompareListView()->isSource() || m_difference->applied() ); + m_destItem->setVisible( !m_sourceItem->isVisible() ); +} + +void KompareListViewDiffItem::applyDifference( bool apply ) +{ + kdDebug(8104) << "KompareListViewDiffItem::applyDifference( " << apply << " )" << endl; + setVisibility(); + setup(); + repaint(); +} + +int KompareListViewDiffItem::maxHeight() +{ + int lines = QMAX( m_difference->sourceLineCount(), m_difference->destinationLineCount() ); + if( lines == 0 ) + return BLANK_LINE_HEIGHT; + else + return lines * listView()->fontMetrics().lineSpacing(); +} + +void KompareListViewDiffItem::setSelected( bool b ) +{ + kdDebug(8104) << "KompareListViewDiffItem::setSelected( " << b << " )" << endl; + KompareListViewItem::setSelected( b ); + QListViewItem* item = m_sourceItem->isVisible() ? + m_sourceItem->firstChild() : + m_destItem->firstChild(); + while( item && item->isVisible() ) { + item->repaint(); + item = item->nextSibling(); + } +} + +KompareListViewLineContainerItem::KompareListViewLineContainerItem( KompareListViewDiffItem* parent, bool isSource ) + : KompareListViewItem( parent ), + m_isSource( isSource ) +{ +// kdDebug(8104) << "isSource ? " << (isSource ? " Yes!" : " No!") << endl; + setExpandable( true ); + setOpen( true ); + + int lines = lineCount(); + int line = lineNumber() + lines - 1; +// kdDebug(8104) << "LineNumber : " << lineNumber() << endl; + if( lines == 0 ) { + new KompareListViewBlankLineItem( this ); + return; + } + + for( int i = lines - 1; i >= 0; i--, line-- ) { + new KompareListViewLineItem( this, line, lineAt( i ) ); + } +} + +void KompareListViewLineContainerItem::setup() +{ + KompareListViewItem::setup(); + setHeight( 0 ); +} + +KompareListViewDiffItem* KompareListViewLineContainerItem::diffItemParent() const +{ + return (KompareListViewDiffItem*)parent(); +} + +int KompareListViewLineContainerItem::lineCount() const +{ + return m_isSource ? diffItemParent()->difference()->sourceLineCount() : + diffItemParent()->difference()->destinationLineCount(); +} + +int KompareListViewLineContainerItem::lineNumber() const +{ + return m_isSource ? diffItemParent()->difference()->sourceLineNumber() : + diffItemParent()->difference()->destinationLineNumber(); +} + +DifferenceString* KompareListViewLineContainerItem::lineAt( int i ) const +{ + return m_isSource ? diffItemParent()->difference()->sourceLineAt( i ) : + diffItemParent()->difference()->destinationLineAt( i ); +} + +KompareListViewLineItem::KompareListViewLineItem( KompareListViewLineContainerItem* parent, int line, DifferenceString* text ) + : KompareListViewItem( parent ) +{ + setText( COL_LINE_NO, QString::number( line ) ); + setText( COL_MAIN, text->string() ); + m_text = text; +} + +void KompareListViewLineItem::setup() +{ + KompareListViewItem::setup(); + setHeight( listView()->fontMetrics().lineSpacing() ); +} + +void KompareListViewLineItem::paintCell( QPainter * p, const QColorGroup & cg, int column, int width, int align ) +{ + QColor bg = cg.base(); + p->fillRect( 0, 0, width, height(), bg ); + if ( diffItemParent()->difference()->type() == Difference::Unchanged ) + { + if ( column == COL_LINE_NO ) + { + bg = cg.background(); + p->fillRect( 0, 0, width, height(), bg ); + } + } + else + { + bg = kompareListView()->settings()->colorForDifferenceType( + diffItemParent()->difference()->type(), + diffItemParent()->isSelected(), + diffItemParent()->difference()->applied() ); + if ( column != COL_MAIN ) + p->fillRect( 0, 0, width, height(), bg ); + } + + p->setPen( cg.foreground() ); + + paintText( p, bg, column, width, align ); + + if ( diffItemParent()->isSelected() ) + { + p->setPen( bg.dark(135) ); + if ( this == parent()->firstChild() ) + p->drawLine( 0, 0, width, 0 ); + if ( nextSibling() == 0 ) + p->drawLine( 0, height() - 1, width, height() - 1 ); + } +} + +void KompareListViewLineItem::paintText( QPainter* p, const QColor& bg, int column, int width, int align ) +{ + if ( column == COL_MAIN ) + { + QString textChunk; + int offset = listView()->itemMargin(); + unsigned int prevValue = 0; + int chunkWidth; + QBrush changeBrush( bg, Dense3Pattern ); + QBrush normalBrush( bg, SolidPattern ); + QBrush brush; + + if ( m_text->string().isEmpty() ) + { + p->fillRect( 0, 0, width, height(), normalBrush ); + return; + } + + p->fillRect( 0, 0, offset, height(), normalBrush ); + + if ( !m_text->markerList().isEmpty() ) + { + MarkerListConstIterator markerIt = m_text->markerList().begin(); + MarkerListConstIterator mEnd = m_text->markerList().end(); + Marker* m = *markerIt; + + for ( ; markerIt != mEnd; ++markerIt ) + { + m = *markerIt; + textChunk = m_text->string().mid( prevValue, m->offset() - prevValue ); +// kdDebug(8104) << "TextChunk = \"" << textChunk << "\"" << endl; +// kdDebug(8104) << "c->offset() = " << c->offset() << endl; +// kdDebug(8104) << "prevValue = " << prevValue << endl; + textChunk.replace( QChar('\t'), kompareListView()->spaces() ); + prevValue = m->offset(); + if ( m->type() == Marker::End ) + { + QFont font( p->font() ); + font.setBold( true ); + p->setFont( font ); +// p->setPen( Qt::blue ); + brush = changeBrush; + } + else + { + QFont font( p->font() ); + font.setBold( false ); + p->setFont( font ); +// p->setPen( Qt::black ); + brush = normalBrush; + } + chunkWidth = p->fontMetrics().width( textChunk ); + p->fillRect( offset, 0, chunkWidth, height(), brush ); + p->drawText( offset, 0, + chunkWidth, height(), + align, textChunk ); + offset += chunkWidth; + } + } + if ( prevValue < m_text->string().length() ) + { + // Still have to draw some string without changes + textChunk = m_text->string().mid( prevValue, kMax( ( unsigned int )1, m_text->string().length() - prevValue ) ); + textChunk.replace( QChar('\t'), kompareListView()->spaces() ); +// kdDebug(8104) << "TextChunk = \"" << textChunk << "\"" << endl; + QFont font( p->font() ); + font.setBold( false ); + p->setFont( font ); + chunkWidth = p->fontMetrics().width( textChunk ); + p->fillRect( offset, 0, chunkWidth, height(), normalBrush ); + p->drawText( offset, 0, + chunkWidth, height(), + align, textChunk ); + offset += chunkWidth; + } + p->fillRect( offset, 0, width - offset, height(), normalBrush ); + } + else + { + p->fillRect( 0, 0, width, height(), bg ); + p->drawText( listView()->itemMargin(), 0, + width - listView()->itemMargin(), height(), + align, text( column ) ); + } +} + +KompareListViewDiffItem* KompareListViewLineItem::diffItemParent() const +{ + KompareListViewLineContainerItem* p = (KompareListViewLineContainerItem*)parent(); + return p->diffItemParent(); +} + +KompareListViewBlankLineItem::KompareListViewBlankLineItem( KompareListViewLineContainerItem* parent ) + : KompareListViewLineItem( parent, 0, new DifferenceString() ) +{ +} + +void KompareListViewBlankLineItem::setup() +{ + KompareListViewLineItem::setup(); + setHeight( BLANK_LINE_HEIGHT ); +} + +void KompareListViewBlankLineItem::paintText( QPainter* p, const QColor& bg, int column, int width, int ) +{ + if ( column == COL_MAIN ) + { + QBrush normalBrush( bg, SolidPattern ); + p->fillRect( 0, 0, width, height(), normalBrush ); + } +} + +KompareListViewHunkItem::KompareListViewHunkItem( KompareListView* parent, DiffHunk* hunk, bool zeroHeight ) + : KompareListViewItem( parent ), + m_zeroHeight( zeroHeight ), + m_hunk( hunk ) +{ + setSelectable( false ); +} + +KompareListViewHunkItem::KompareListViewHunkItem( KompareListView* parent, KompareListViewItem* after, DiffHunk* hunk, bool zeroHeight ) + : KompareListViewItem( parent, after ), + m_zeroHeight( zeroHeight ), + m_hunk( hunk ) +{ + setSelectable( false ); +} + +int KompareListViewHunkItem::maxHeight() +{ + if( m_zeroHeight ) { + return 0; + } else if( m_hunk->function().isEmpty() ) { + return HUNK_LINE_HEIGHT; + } else { + return listView()->fontMetrics().lineSpacing(); + } +} + +void KompareListViewHunkItem::setup() +{ + KompareListViewItem::setup(); + + setHeight( maxHeight() ); +} + +void KompareListViewHunkItem::paintCell( QPainter * p, const QColorGroup & cg, int column, int width, int align ) +{ + p->fillRect( 0, 0, width, height(), cg.mid() ); + if( column == COL_MAIN ) { + p->drawText( listView()->itemMargin(), 0, width - listView()->itemMargin(), height(), + align, m_hunk->function() ); + } +} + +#include "komparelistview.moc" diff --git a/kompare/komparepart/komparelistview.h b/kompare/komparepart/komparelistview.h new file mode 100644 index 00000000..26a6dacb --- /dev/null +++ b/kompare/komparepart/komparelistview.h @@ -0,0 +1,225 @@ +/*************************************************************************** + komparelistview.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + (C) 2004 Jeff Snyder + email : otto.bruggeman@home.nl + jfirebaugh@kde.org + jeff@caffeinated.me.uk +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef KOMPARELISTVIEW_H +#define KOMPARELISTVIEW_H + +#include +#include +#include +#include + +#include + +namespace Diff2 { +class DiffModel; +class DiffHunk; +class Difference; +class DifferenceString; +class KompareModelList; +} +class ViewSettings; +class KompareSplitter; +class KompareListView; +class KompareListViewItem; +class KompareListViewDiffItem; +class KompareListViewLineContainerItem; + +class KompareListView : public KListView +{ + Q_OBJECT + +public: + KompareListView( bool isSource, ViewSettings* settings, QWidget* parent, const char* name = 0 ); + virtual ~KompareListView(); + + KompareListViewItem* itemAtIndex( int i ); + int firstVisibleDifference(); + int lastVisibleDifference(); + QRect itemRect( int i ); + int minScrollId(); + int maxScrollId(); + int contentsWidth(); + + bool isSource() const { return m_isSource; }; + ViewSettings* settings() const { return m_settings; }; + + void setSelectedDifference( const Diff2::Difference* diff, bool scroll ); + + const QString& spaces() const { return m_spaces; }; + void setSpaces( int spaces ); + +public slots: + void slotSetSelection( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void slotSetSelection( const Diff2::Difference* diff ); + void setXOffset( int x ); + void scrollToId( int id ); + int scrollId(); + void slotApplyDifference( bool apply ); + void slotApplyAllDifferences( bool apply ); + void slotApplyDifference( const Diff2::Difference* diff, bool apply ); + +signals: + void differenceClicked( const Diff2::Difference* diff ); + void applyDifference( bool apply ); + void resized(); + +protected: + void wheelEvent( QWheelEvent* e ); + void resizeEvent( QResizeEvent* e ); + void contentsMousePressEvent ( QMouseEvent * e ); + void contentsMouseDoubleClickEvent ( QMouseEvent* ); + void contentsMouseReleaseEvent ( QMouseEvent * ) {}; + void contentsMouseMoveEvent ( QMouseEvent * ) {}; + +private: + QValueList m_items; + QPtrDict m_itemDict; + bool m_isSource; + ViewSettings* m_settings; + int m_scrollId; + int m_maxMainWidth; + const Diff2::DiffModel* m_selectedModel; + const Diff2::Difference* m_selectedDifference; + QString m_spaces; +}; + +class KompareListViewFrame : public QFrame +{ + Q_OBJECT + +public: + KompareListViewFrame( bool isSource, ViewSettings* settings, KompareSplitter* parent, const char* name = 0 ); + virtual ~KompareListViewFrame() {}; + KompareListView* view() { return &m_view; }; + +public slots: + void slotSetModel( const Diff2::DiffModel* model ); + +private: + KompareListView m_view; + QLabel m_label; + QVBoxLayout m_layout; +}; + +class KompareListViewItem : public QListViewItem +{ +public: + KompareListViewItem( KompareListView* parent ); + KompareListViewItem( KompareListView* parent, KompareListViewItem* after ); + KompareListViewItem( KompareListViewItem* parent ); + KompareListViewItem( KompareListViewItem* parent, KompareListViewItem* after ); + + void paintFocus( QPainter* p, const QColorGroup& cg, const QRect& r ); + int scrollId() { return m_scrollId; }; + + virtual int maxHeight() = 0; + + KompareListView* kompareListView() const; + +private: + int m_scrollId; +}; + +class KompareListViewDiffItem : public KompareListViewItem +{ +public: + KompareListViewDiffItem( KompareListView* parent, Diff2::Difference* difference ); + KompareListViewDiffItem( KompareListView* parent, KompareListViewItem* after, Diff2::Difference* difference ); + + void setup(); + void setSelected( bool b ); + void applyDifference( bool apply ); + + Diff2::Difference* difference() { return m_difference; }; + + int maxHeight(); + +private: + void init(); + void setVisibility(); + + Diff2::Difference* m_difference; + KompareListViewLineContainerItem* m_sourceItem; + KompareListViewLineContainerItem* m_destItem; +}; + +class KompareListViewLineContainerItem : public KompareListViewItem +{ +public: + KompareListViewLineContainerItem( KompareListViewDiffItem* parent, bool isSource ); + + void setup(); + int maxHeight() { return 0; } + KompareListViewDiffItem* diffItemParent() const; + +private: + int lineCount() const; + int lineNumber() const; + Diff2::DifferenceString* lineAt( int i ) const; + + bool m_isSource; +}; + +class KompareListViewLineItem : public KompareListViewItem +{ +public: + KompareListViewLineItem( KompareListViewLineContainerItem* parent, int line, Diff2::DifferenceString* text ); + + virtual void setup(); + int maxHeight() { return 0; } + + virtual void paintCell( QPainter* p, const QColorGroup& cg, int column, int width, int align ); + virtual void paintText( QPainter* p, const QColor& bg, int column, int width, int align ); + + KompareListViewDiffItem* diffItemParent() const; + +private: + Diff2::DifferenceString* m_text; +}; + +class KompareListViewBlankLineItem : public KompareListViewLineItem +{ +public: + KompareListViewBlankLineItem( KompareListViewLineContainerItem* parent ); + + void setup(); + + void paintText( QPainter* p, const QColor& bg, int column, int width, int align ); +}; + +class KompareListViewHunkItem : public KompareListViewItem +{ +public: + KompareListViewHunkItem( KompareListView* parent, Diff2::DiffHunk* hunk, bool zeroHeight = false ); + KompareListViewHunkItem( KompareListView* parent, KompareListViewItem* after, Diff2::DiffHunk* hunk, bool zeroHeight= false ); + + void setup(); + void paintCell( QPainter* p, const QColorGroup& cg, int column, int width, int align ); + + int maxHeight(); + +private: + bool m_zeroHeight; + Diff2::DiffHunk* m_hunk; +}; + +#endif diff --git a/kompare/komparepart/komparepart.desktop b/kompare/komparepart/komparepart.desktop new file mode 100644 index 00000000..3d075d62 --- /dev/null +++ b/kompare/komparepart/komparepart.desktop @@ -0,0 +1,24 @@ +[Desktop Entry] +GenericComment=A part to graphically show the difference between 2 files +Name=KomparePart +Name[af]=K-vergelyk-deel +Name[de]=Einbettungsfähige Komponente von Kompare +Name[eo]=Komparo-parto +Name[he]=רכיב Kompare +Name[hi]=काम्पेयर-पार्ट +Name[lv]=SalīdzinātDaļu +Name[pl]=Moduł Kompare +Name[pt_BR]=Parte do Kompare +Name[ro]=Componentă Kompare +Name[ru]=Компонент утилиты сравнения файлов +Name[sv]=Kompare-del +Name[ta]=கோம்பெர் உறுப்பு +Name[tg]=Қисмати утилитҳои баробаркунии файлҳо +Name[th]=ส่วนเปรียบเทียบ +Name[zu]=Ingxenye yeKompare +MimeType=text/x-diff +ServiceTypes=Kompare/ViewPart,KParts/ReadWritePart,KParts/ReadOnlyPart +X-KDE-Library=libkomparepart +Type=Service +Icon=kompare +InitialPreference=10 diff --git a/kompare/komparepart/komparepartui.rc b/kompare/komparepart/komparepartui.rc new file mode 100644 index 00000000..c7140325 --- /dev/null +++ b/kompare/komparepart/komparepartui.rc @@ -0,0 +1,44 @@ + + + + &File + + + + + + + + + &Difference + + + + + + + + + + + + &Settings + + + + + + + + + + + + + + + + + + + diff --git a/kompare/komparepart/kompareprefdlg.cpp b/kompare/komparepart/kompareprefdlg.cpp new file mode 100644 index 00000000..2d731af3 --- /dev/null +++ b/kompare/komparepart/kompareprefdlg.cpp @@ -0,0 +1,106 @@ +/*************************************************************************** + kompareprefdlg.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include + +#include +#include +#include + +#include "diffpage.h" +#include "viewpage.h" + +#include "kompareprefdlg.h" + +// implementation + +KomparePrefDlg::KomparePrefDlg( ViewSettings* viewSets, DiffSettings* diffSets ) : KDialogBase( IconList, i18n( "Preferences" ), Help|Default|Ok|Apply|Cancel, Ok, 0, 0, true, true ) +{ + // ok i need some stuff in that pref dlg... + setIconListAllVisible(true); + + QVBox* frame; + frame = addVBoxPage( i18n( "View" ), i18n( "View Settings" ), UserIcon( "diff_view" ) ); + m_viewPage = new ViewPage( frame ); + m_viewPage->setSettings( viewSets ); + + frame = addVBoxPage( i18n( "Diff" ), i18n( "Diff Settings" ), UserIcon( "diff_specific" ) ); + m_diffPage = new DiffPage( frame ); + m_diffPage->setSettings( diffSets ); + +// frame = addVBoxPage( i18n( "" ), i18n( "" ), UserIcon( "" ) ); + + adjustSize(); +} + +KomparePrefDlg::~KomparePrefDlg() +{ + +} + +/** No descriptions */ +void KomparePrefDlg::slotDefault() +{ + kdDebug(8103) << "SlotDefault called -> Settings should be restored to defaults..." << endl; + // restore all defaults in the options... + m_viewPage->setDefaults(); + m_diffPage->setDefaults(); +} + +/** No descriptions */ +void KomparePrefDlg::slotHelp() +{ + // show some help... + // figure out the current active page + // and give help for that page +} + +/** No descriptions */ +void KomparePrefDlg::slotApply() +{ + kdDebug(8103) << "SlotApply called -> Settings should be applied..." << endl; + // well apply the settings that are currently selected + m_viewPage->apply(); + m_diffPage->apply(); + + emit applyClicked(); +} + +/** No descriptions */ +void KomparePrefDlg::slotOk() +{ + kdDebug(8103) << "SlotOk called -> Settings should be applied..." << endl; + // Apply the settings that are currently selected + m_viewPage->apply(); + m_diffPage->apply(); + + KDialogBase::slotOk(); +} + +/** No descriptions */ +void KomparePrefDlg::slotCancel() +{ + // discard the current settings and use the present ones + m_viewPage->restore(); + m_diffPage->restore(); + + KDialogBase::slotCancel(); +} + +#include "kompareprefdlg.moc" diff --git a/kompare/komparepart/kompareprefdlg.h b/kompare/komparepart/kompareprefdlg.h new file mode 100644 index 00000000..e81da90c --- /dev/null +++ b/kompare/komparepart/kompareprefdlg.h @@ -0,0 +1,54 @@ +/*************************************************************************** + kompareprefdlg.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef KOMPAREPREFDLG_H +#define KOMPAREPREFDLG_H + +#include + +class DiffPage; +class DiffSettings; +class ViewPage; +class ViewSettings; + +class KomparePrefDlg : public KDialogBase +{ +Q_OBJECT +public: + KomparePrefDlg( ViewSettings*, DiffSettings* ); + ~KomparePrefDlg(); + +protected slots: + /** No descriptions */ + virtual void slotOk(); + /** No descriptions */ + virtual void slotApply(); + /** No descriptions */ + virtual void slotHelp(); + /** No descriptions */ + virtual void slotDefault(); + /** No descriptions */ + virtual void slotCancel(); + +private: + ViewPage* m_viewPage; + DiffPage* m_diffPage; +}; + +#endif diff --git a/kompare/komparepart/komparesaveoptionsbase.ui b/kompare/komparepart/komparesaveoptionsbase.ui new file mode 100644 index 00000000..4c49b018 --- /dev/null +++ b/kompare/komparepart/komparesaveoptionsbase.ui @@ -0,0 +1,336 @@ + +KompareSaveOptionsBase + + + KompareSaveOptionsBase + + + + 0 + 0 + 558 + 399 + + + + + unnamed + + + 0 + + + 6 + + + + GroupBox2 + + + + 1 + 1 + 0 + 0 + + + + Panel + + + Run Diff In + + + + unnamed + + + 11 + + + 6 + + + + m_directoryRequester + + + + 7 + 5 + 0 + 0 + + + + + + + + m_CommandLineGB + + + + 3 + 3 + 0 + 0 + + + + Panel + + + Command Line + + + AlignVCenter|AlignLeft + + + + + + + + unnamed + + + 11 + + + 6 + + + + m_CommandLineLabel + + + cd dir && diff -udHprNa -- source destination + + + + + + + m_OptionsGB + + + Options + + + + unnamed + + + 11 + + + 6 + + + + m_SmallerChangesCB + + + Look for smaller changes + + + true + + + + + m_LargeFilesCB + + + Optimize for large files + + + true + + + + + m_IgnoreCaseCB + + + Ignore changes in case + + + + + m_ExpandTabsCB + + + Expand tabs to spaces + + + + + m_IgnoreEmptyLinesCB + + + Ignore added or removed empty lines + + + + + m_IgnoreWhiteSpaceCB + + + Ignore changes in whitespace + + + + + m_FunctionNamesCB + + + Show function names + + + true + + + + + m_RecursiveCB + + + Compare folders recursively + + + true + + + + + m_NewFilesCB + + + Treat new files as empty + + + true + + + false + + + + + + + m_FormatBG + + + Format + + + + unnamed + + + 11 + + + 6 + + + + m_ContextRB + + + Context + + + + + m_EdRB + + + Ed + + + + + m_NormalRB + + + Normal + + + + + m_RCSRB + + + RCS + + + + + m_UnifiedRB + + + Unified + + + true + + + + + m_SideBySideRB + + + Side-by-side + + + false + + + + + m_ContextLinesLayout + + + + unnamed + + + 0 + + + 6 + + + + m_ContextLinesLabel + + + Number of context lines: + + + + + m_ContextLinesSB + + + true + + + 65535 + + + 3 + + + + + + + + + + diff --git a/kompare/komparepart/komparesaveoptionswidget.cpp b/kompare/komparepart/komparesaveoptionswidget.cpp new file mode 100644 index 00000000..866ecc0d --- /dev/null +++ b/kompare/komparepart/komparesaveoptionswidget.cpp @@ -0,0 +1,215 @@ +/*************************************************************************** + komparesaveoptionswidget.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "diffsettings.h" +#include "komparesaveoptionswidget.h" + +KompareSaveOptionsWidget::KompareSaveOptionsWidget( QString source, QString destination, + DiffSettings * settings, QWidget * parent ) + : KompareSaveOptionsBase( parent, "save options" ) + , m_source( source ) + , m_destination( destination ) +{ + m_settings = settings; + + m_directoryRequester->setMode( static_cast( + KFile::ExistingOnly | KFile::Directory | KFile::LocalOnly ) ); + + KURL sourceURL; + KURL destinationURL; + sourceURL.setPath( source ); + destinationURL.setPath( destination ); + + // Find a common root. + KURL root( sourceURL ); + while( root.isValid() && !root.isParentOf( destinationURL ) ) { + root = root.upURL(); + } + + // If we found a common root, change to that directory and + // strip the common part from source and destination. + if( root.isValid() ) { + m_directoryRequester->setURL( root.url() ); + } + + connect( m_SmallerChangesCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_LargeFilesCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_IgnoreCaseCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_ExpandTabsCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_IgnoreEmptyLinesCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_IgnoreWhiteSpaceCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_FunctionNamesCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_RecursiveCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_NewFilesCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_ContextRB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_EdRB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_NormalRB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_RCSRB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_UnifiedRB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_SideBySideRB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_ContextLinesSB, SIGNAL(valueChanged(int)), SLOT(updateCommandLine()) ); + connect( m_directoryRequester, SIGNAL(textChanged(const QString&)), SLOT(updateCommandLine()) ); + + loadOptions(); +} + +KompareSaveOptionsWidget::~KompareSaveOptionsWidget() +{ + +} + +QString KompareSaveOptionsWidget::directory() const +{ + return KURL( m_directoryRequester->url() ).path(); +} + +void KompareSaveOptionsWidget::updateCommandLine() +{ + QString cmdLine = "diff"; + + QString options = ""; + + switch( static_cast( m_FormatBG->id( m_FormatBG->selected() ) ) ) { + case Kompare::Unified : + cmdLine += " -U " + QString::number( m_ContextLinesSB->value() ); + break; + case Kompare::Context : + cmdLine += " -C " + QString::number( m_ContextLinesSB->value() ); + break; + case Kompare::RCS : + options += "n"; + break; + case Kompare::Ed : + options += "e"; + break; + case Kompare::SideBySide: + options += "y"; + break; + case Kompare::Normal : + case Kompare::UnknownFormat : + default: + break; + } + + if ( m_SmallerChangesCB->isChecked() ) { + options += "d"; + } + + if ( m_LargeFilesCB->isChecked() ) { + options += "H"; + } + + if ( m_IgnoreCaseCB->isChecked() ){ + options += "i"; + } + + if ( m_ExpandTabsCB->isChecked() ) { + options += "t"; + } + + if ( m_IgnoreEmptyLinesCB->isChecked() ) { + options += "B"; + } + + if ( m_IgnoreWhiteSpaceCB->isChecked() ) { + options += "b"; + } + + if ( m_FunctionNamesCB->isChecked() ) { + options += "p"; + } + +// if ( ) { +// cmdLine += " -w"; +// } + + if ( m_RecursiveCB->isChecked() ) { + options += "r"; + } + + if( m_NewFilesCB->isChecked() ) { + options += "N"; + } + +// if( m_AllTextCB->isChecked() ) { +// options += "a"; +// } + + if( options.length() > 0 ) { + cmdLine += " -" + options; + } + + cmdLine += " -- "; + cmdLine += constructRelativePath( m_directoryRequester->url(), m_source ); + cmdLine += " "; + cmdLine += constructRelativePath( m_directoryRequester->url(), m_destination ); + + m_CommandLineLabel->setText( cmdLine ); +} + +void KompareSaveOptionsWidget::loadOptions() +{ + m_SmallerChangesCB->setChecked ( m_settings->m_createSmallerDiff ); + m_LargeFilesCB->setChecked ( m_settings->m_largeFiles ); + m_IgnoreCaseCB->setChecked ( m_settings->m_ignoreChangesInCase ); + m_ExpandTabsCB->setChecked ( m_settings->m_convertTabsToSpaces ); + m_IgnoreEmptyLinesCB->setChecked( m_settings->m_ignoreEmptyLines ); + m_IgnoreWhiteSpaceCB->setChecked( m_settings->m_ignoreWhiteSpace ); + m_FunctionNamesCB->setChecked ( m_settings->m_showCFunctionChange ); + m_RecursiveCB->setChecked ( m_settings->m_recursive ); + m_NewFilesCB->setChecked ( m_settings->m_newFiles ); +// m_AllTextCB->setChecked ( m_settings->m_allText ); + + m_ContextLinesSB->setValue ( m_settings->m_linesOfContext ); + + m_FormatBG->setButton ( m_settings->m_format ); + + updateCommandLine(); +} + +void KompareSaveOptionsWidget::saveOptions() +{ + m_settings->m_createSmallerDiff = m_SmallerChangesCB->isChecked(); + m_settings->m_largeFiles = m_LargeFilesCB->isChecked(); + m_settings->m_ignoreChangesInCase = m_IgnoreCaseCB->isChecked(); + m_settings->m_convertTabsToSpaces = m_ExpandTabsCB->isChecked(); + m_settings->m_ignoreEmptyLines = m_IgnoreEmptyLinesCB->isChecked(); + m_settings->m_ignoreWhiteSpace = m_IgnoreWhiteSpaceCB->isChecked(); + m_settings->m_showCFunctionChange = m_FunctionNamesCB->isChecked(); + m_settings->m_recursive = m_RecursiveCB->isChecked(); + m_settings->m_newFiles = m_NewFilesCB->isChecked(); +// m_settings->m_allText = m_AllTextCB->isChecked(); + + m_settings->m_linesOfContext = m_ContextLinesSB->value(); + + m_settings->m_format = static_cast( m_FormatBG->id( m_FormatBG->selected() ) ); + +} + +#include "komparesaveoptionswidget.moc" diff --git a/kompare/komparepart/komparesaveoptionswidget.h b/kompare/komparepart/komparesaveoptionswidget.h new file mode 100644 index 00000000..6361ca77 --- /dev/null +++ b/kompare/komparepart/komparesaveoptionswidget.h @@ -0,0 +1,51 @@ +/*************************************************************************** + komparesaveoptionswidget.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef KOMPARESAVEOPTIONSWIDGET_H +#define KOMPARESAVEOPTIONSWIDGET_H + +#include + +#include "komparesaveoptionsbase.h" +#include "kompare.h" + +class DiffSettings; + +class KompareSaveOptionsWidget : public KompareSaveOptionsBase, public KompareFunctions +{ +Q_OBJECT +public: + KompareSaveOptionsWidget( QString source, QString destination, DiffSettings* settings, QWidget* parent ); + ~KompareSaveOptionsWidget(); + + void saveOptions(); + QString directory() const; + +protected slots: + void updateCommandLine(); + +private: + void loadOptions(); + + DiffSettings* m_settings; + QString m_source; + QString m_destination; +}; + +#endif diff --git a/kompare/komparepart/komparesplitter.cpp b/kompare/komparepart/komparesplitter.cpp new file mode 100644 index 00000000..fcf014cb --- /dev/null +++ b/kompare/komparepart/komparesplitter.cpp @@ -0,0 +1,712 @@ +/*************************************************************************** + komparesplitter.cpp - description + ------------------- + begin : Wed Jan 14 2004 + copyright : (C) 2004 by Jeff Snyder + email : jeff@caffeinated.me.uk +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include "komparesplitter.h" + +#include +#include +#include + +#include +#include +#include + +#include "diffmodel.h" +#include "difference.h" +#include "viewsettings.h" +#include "kompare_part.h" +#include "komparelistview.h" +#include "kompareconnectwidget.h" + +using namespace Diff2; + +KompareSplitter::KompareSplitter( ViewSettings *settings, QWidget * parent, + const char *name) : + QSplitter( Horizontal, parent, name ), + m_settings( settings ) +{ + QFrame *scrollFrame = new QFrame( parent, "scrollFrame" ); + reparent( scrollFrame, *(new QPoint()), false ); + + // Set up the scrollFrame + scrollFrame->setFrameStyle( QFrame::NoFrame | QFrame::Plain ); + scrollFrame->setLineWidth(scrollFrame->style().pixelMetric(QStyle::PM_DefaultFrameWidth)); + QGridLayout *pairlayout = new QGridLayout(scrollFrame, 2, 2, 0); + m_vScroll = new QScrollBar( QScrollBar::Vertical, scrollFrame ); + pairlayout->addWidget( m_vScroll, 0, 1 ); + m_hScroll = new QScrollBar( QScrollBar::Horizontal, scrollFrame ); + pairlayout->addWidget( m_hScroll, 1, 0 ); + + new KompareListViewFrame(true, m_settings, this, "source"); + new KompareListViewFrame(false, m_settings, this, "destination"); + pairlayout->addWidget( this, 0, 0 ); + + // set up our looks + setFrameStyle( QFrame::StyledPanel | QFrame::Sunken ); + setLineWidth( style().pixelMetric( QStyle::PM_DefaultFrameWidth ) ); + + setHandleWidth(50); + setChildrenCollapsible( false ); + setFrameStyle( QFrame::NoFrame ); + setSizePolicy( QSizePolicy (QSizePolicy::Ignored, QSizePolicy::Ignored )); + setOpaqueResize( true ); + setFocusPolicy( QWidget::WheelFocus ); + + connect( this, SIGNAL(configChanged()), SLOT(slotConfigChanged()) ); + connect( this, SIGNAL(configChanged()), SLOT(slotDelayedRepaintHandles()) ); + connect( this, SIGNAL(configChanged()), SLOT(slotDelayedUpdateScrollBars()) ); + + // scrolling + connect( m_vScroll, SIGNAL(valueChanged(int)), SLOT(scrollToId(int)) ); + connect( m_vScroll, SIGNAL(sliderMoved(int)), SLOT(scrollToId(int)) ); + connect( m_hScroll, SIGNAL(valueChanged(int)), SIGNAL(setXOffset(int)) ); + connect( m_hScroll, SIGNAL(sliderMoved(int)), SIGNAL(setXOffset(int)) ); + + m_scrollTimer=new QTimer(); + restartTimer = false; + connect (m_scrollTimer, SIGNAL(timeout()), SLOT(timerTimeout()) ); + + // we need to receive childEvents now so that d->list is ready for when + // slotSetSelection(...) arrives + kapp->sendPostedEvents(this, QEvent::ChildInserted); + + // init stuff + slotUpdateScrollBars(); +} + +KompareSplitter::~KompareSplitter(){} + +/* + Inserts the widget \a w at the end (or at the beginning if \a + prepend is TRUE) of the splitter's list of widgets. + + It is the responsibility of the caller to make sure that \a w is + not already in the splitter and to call recalcId() if needed. (If + \a prepend is TRUE, then recalcId() is very probably needed.) +*/ + +QSplitterLayoutStruct *KompareSplitter::addWidget( KompareListViewFrame *w, bool prepend ) +{ + /* This function is *not* a good place to make connections to and from + * the KompareListView. Make them in the KompareListViewFrame constructor + * instad - that way the connections get made upon creation, not upon the + * next processing of the event queue. */ + + QSplitterLayoutStruct *s; + KompareConnectWidgetFrame *newHandle = 0; + if ( d->list.count() > 0 ) { + s = new QSplitterLayoutStruct; + s->resizeMode = KeepSize; + QString tmp = "qt_splithandle_"; + tmp += w->name(); + KompareListView *lw = + ((KompareListViewFrame*)(prepend?w:d->list.last()->wid))->view(); + KompareListView *rw = + ((KompareListViewFrame*)(prepend?d->list.first()->wid:w))->view(); + newHandle = new KompareConnectWidgetFrame(lw, rw, m_settings, this, tmp.latin1()); + s->wid = newHandle; + newHandle->setId( d->list.count() ); + s->isHandle = TRUE; + s->sizer = pick( newHandle->sizeHint() ); + if ( prepend ) + d->list.prepend( s ); + else + d->list.append( s ); + } + s = new QSplitterLayoutStruct; + s->resizeMode = DefaultResizeMode; + s->wid = w; + s->isHandle = FALSE; + if ( prepend ) d->list.prepend( s ); + else d->list.append( s ); + if ( newHandle && isVisible() ) + newHandle->show(); // will trigger sending of post events + return s; +} + + +/*! + Tells the splitter that the child widget described by \a c has + been inserted or removed. +*/ + +void KompareSplitter::childEvent( QChildEvent *c ) +{ + if ( c->type() == QEvent::ChildInserted ) { + if ( !c->child()->isWidgetType() ) + return; + + if ( ((QWidget*)c->child())->testWFlags( WType_TopLevel ) ) + return; + + QSplitterLayoutStruct *s = d->list.first(); + while ( s ) { + if ( s->wid == c->child() ) + return; + s = d->list.next(); + } + addWidget( (KompareListViewFrame*)c->child() ); + recalc( isVisible() ); + } else if ( c->type() == QEvent::ChildRemoved ) { + QSplitterLayoutStruct *prev = 0; + if ( d->list.count() > 1 ) + prev = d->list.at( 1 ); // yes, this is correct + QSplitterLayoutStruct *curr = d->list.first(); + while ( curr ) { + if ( curr->wid == c->child() ) { + d->list.removeRef( curr ); + if ( prev && prev->isHandle ) { + QWidget *w = prev->wid; + d->list.removeRef( prev ); + delete w; // will call childEvent() + } + recalcId(); + doResize(); + return; + } + prev = curr; + curr = d->list.next(); + } + } +} + +// This is from a private qt header (kernel/qlayoutengine_p.h). sorry. +QSize qSmartMinSize( QWidget *w ); + +static QPoint toggle( QWidget *w, QPoint pos ) +{ + QSize minS = qSmartMinSize( w ); + return -pos - QPoint( minS.width(), minS.height() ); +} + +static bool isCollapsed( QWidget *w ) +{ + return w->x() < 0 || w->y() < 0; +} + +static QPoint topLeft( QWidget *w ) +{ + if ( isCollapsed(w) ) { + return toggle( w, w->pos() ); + } else { + return w->pos(); + } +} + +static QPoint bottomRight( QWidget *w ) +{ + if ( isCollapsed(w) ) { + return toggle( w, w->pos() ) - QPoint( 1, 1 ); + } else { + return w->geometry().bottomRight(); + } +} + +void KompareSplitter::moveSplitter( QCOORD p, int id ) +{ + QSplitterLayoutStruct *s = d->list.at( id ); + int farMin; + int min; + int max; + int farMax; + p = adjustPos( p, id, &farMin, &min, &max, &farMax ); + int oldP = pick( s->wid->pos() ); + int* poss = new int[d->list.count()]; + int* ws = new int[d->list.count()]; + QWidget *w = 0; + bool upLeft; + if ( QApplication::reverseLayout() && orient == Horizontal ) { + int q = p + s->wid->width(); + doMove( FALSE, q, id - 1, -1, (p > max), poss, ws ); + doMove( TRUE, q, id, -1, (p < min), poss, ws ); + upLeft = (q > oldP); + } else { + doMove( FALSE, p, id, +1, (p > max), poss, ws ); + doMove( TRUE, p, id - 1, +1, (p < min), poss, ws ); + upLeft = (p < oldP); + } + if ( upLeft ) { + int count = d->list.count(); + for ( int id = 0; id < count; ++id ) { + w = d->list.at( id )->wid; + if( !w->isHidden() ) + setGeo( w, poss[id], ws[id], TRUE ); + } + } else { + for ( int id = d->list.count() - 1; id >= 0; --id ) { + w = d->list.at( id )->wid; + if( !w->isHidden() ) + setGeo( w, poss[id], ws[id], TRUE ); + } + } + storeSizes(); +} + +void KompareSplitter::doMove( bool backwards, int pos, int id, int delta, + bool mayCollapse, int* positions, int* widths ) +{ + QSplitterLayoutStruct *s; + QWidget *w; + for ( ; id >= 0 && id < (int)d->list.count(); + id = backwards ? id - delta : id + delta ) { + s = d->list.at( id ); + w = s->wid; + if ( w->isHidden() ) { + mayCollapse = TRUE; + } else { + if ( s->isHandle ) { + int dd = s->getSizer( orient ); + int nextPos = backwards ? pos - dd : pos + dd; + positions[id] = pos; + widths[id] = dd; + pos = nextPos; + } else { + int dd = backwards ? pos - pick( topLeft(w) ) + : pick( bottomRight(w) ) - pos + 1; + if ( dd > 0 || (!isCollapsed(w) && !mayCollapse) ) { + dd = QMAX( pick(qSmartMinSize(w)), + QMIN(dd, pick(w->maximumSize())) ); + } else { + dd = 0; + } + positions[id] = backwards ? pos - dd : pos; + widths[id] = dd; + pos = backwards ? pos - dd : pos + dd; + mayCollapse = TRUE; + } + } + } +} + +void KompareSplitter::slotSetSelection( const DiffModel* model, const Difference* diff ) +{ + QSplitterLayoutStruct *curr; + for ( curr = d->list.first(); curr; curr = d->list.next() ) + { + if ( curr->isHandle ) + ((KompareConnectWidgetFrame*) + curr->wid)->wid()->slotSetSelection( model, diff ); + else + { + ((KompareListViewFrame*) + curr->wid)->view()->slotSetSelection( model, diff ); + ((KompareListViewFrame*) + curr->wid)->slotSetModel( model ); + } + } + slotDelayedRepaintHandles(); + slotDelayedUpdateScrollBars(); +} + +void KompareSplitter::slotSetSelection( const Difference* diff ) +{ + QSplitterLayoutStruct *curr; + for ( curr = d->list.first(); curr; curr = d->list.next() ) + if ( curr->isHandle ) + ((KompareConnectWidgetFrame*) + curr->wid)->wid()->slotSetSelection( diff ); + else + ((KompareListViewFrame*) + curr->wid)->view()->slotSetSelection( diff ); + slotDelayedRepaintHandles(); + slotDelayedUpdateScrollBars(); +} + +void KompareSplitter::slotApplyDifference( bool apply ) +{ + QSplitterLayoutStruct *curr; + for ( curr = d->list.first(); curr; curr = d->list.next() ) + if ( !curr->isHandle ) + ((KompareListViewFrame*) + curr->wid)->view()->slotApplyDifference( apply ); + slotDelayedRepaintHandles(); +} + +void KompareSplitter::slotApplyAllDifferences( bool apply ) +{ + QSplitterLayoutStruct *curr; + for ( curr = d->list.first(); curr; curr = d->list.next() ) + if ( !curr->isHandle ) + ((KompareListViewFrame*) + curr->wid)->view()->slotApplyAllDifferences( apply ); + slotDelayedRepaintHandles(); + scrollToId( scrollTo ); // FIXME! +} + +void KompareSplitter::slotApplyDifference( const Difference* diff, bool apply ) +{ + QSplitterLayoutStruct *curr; + for ( curr = d->list.first(); curr; curr = d->list.next() ) + if ( !curr->isHandle ) + ((KompareListViewFrame*) + curr->wid)->view()->slotApplyDifference( diff, apply ); + slotDelayedRepaintHandles(); +} + +void KompareSplitter::slotDifferenceClicked( const Difference* diff ) +{ + QSplitterLayoutStruct *curr; + for ( curr = d->list.first(); curr; curr = d->list.next() ) + if ( !curr->isHandle ) + ((KompareListViewFrame*) + curr->wid)->view()->setSelectedDifference( diff, false ); + emit selectionChanged( diff ); +} + +void KompareSplitter::slotConfigChanged() +{ + QSplitterLayoutStruct *curr; + for ( curr = d->list.first(); curr; curr = d->list.next() ) { + if ( !curr->isHandle ) + { + ((KompareListViewFrame*) + curr->wid)->view()->setSpaces( m_settings->m_tabToNumberOfSpaces ); + ((KompareListViewFrame*) + curr->wid)->view()->setFont( m_settings->m_font ); + ((KompareListViewFrame*) + curr->wid)->view()->update(); + } + } +} + +void KompareSplitter::slotDelayedRepaintHandles() +{ + QSplitterLayoutStruct *curr; + for ( curr = d->list.first(); curr; curr = d->list.next() ) + if ( curr->isHandle ) + ((KompareConnectWidgetFrame*)curr->wid)->wid()->slotDelayedRepaint(); +} + +void KompareSplitter::repaintHandles() +{ + QSplitterLayoutStruct *curr; + for ( curr = d->list.first(); curr; curr = d->list.next() ) + if ( curr->isHandle ) + ((KompareConnectWidgetFrame*)curr->wid)->wid()->repaint(); +} + +// Scrolling stuff +/* + * limit updating to once every 50 msec with a qtimer + * FIXME: i'm a nasty hack + */ + +void KompareSplitter::wheelEvent( QWheelEvent* e ) +{ + // scroll lines... + if ( e->orientation() == Qt::Vertical ) + { + if ( e->state() & Qt::ControlButton ) + if ( e->delta() < 0 ) // scroll down one page + m_vScroll->addPage(); + else // scroll up one page + m_vScroll->subtractPage(); + else + if ( e->delta() < 0 ) // scroll down + m_vScroll->addLine(); + else // scroll up + m_vScroll->subtractLine(); + } + else + { + if ( e->state() & Qt::ControlButton ) + if ( e->delta() < 0 ) // scroll right one page + m_hScroll->addPage(); + else // scroll left one page + m_hScroll->subtractPage(); + else + if ( e->delta() < 0 ) // scroll to the right + m_hScroll->addLine(); + else // scroll to the left + m_hScroll->subtractLine(); + } + e->accept(); + repaintHandles(); +} + +void KompareSplitter::keyPressEvent( QKeyEvent* e ) +{ + //keyboard scrolling + switch ( e->key() ) { + case Key_Right: + case Key_L: + m_hScroll->addLine(); + break; + case Key_Left: + case Key_H: + m_hScroll->subtractLine(); + break; + case Key_Up: + case Key_K: + m_vScroll->subtractLine(); + break; + case Key_Down: + case Key_J: + m_vScroll->addLine(); + break; + case Key_PageDown: + m_vScroll->addPage(); + break; + case Key_PageUp: + m_vScroll->subtractPage(); + break; + } + e->accept(); + repaintHandles(); +} + +void KompareSplitter::timerTimeout() +{ + if ( restartTimer ) + restartTimer = false; + else + m_scrollTimer->stop(); + + slotDelayedRepaintHandles(); + + emit scrollViewsToId( scrollTo ); +} + +void KompareSplitter::scrollToId( int id ) +{ + scrollTo = id; + + if( restartTimer ) + return; + + if( m_scrollTimer->isActive() ) + { + restartTimer = true; + } + else + { + emit scrollViewsToId( id ); + slotDelayedRepaintHandles(); + m_scrollTimer->start(30, false); + } +} + +void KompareSplitter::slotDelayedUpdateScrollBars() +{ + QTimer::singleShot( 0, this, SLOT( slotUpdateScrollBars() ) ); +} + +void KompareSplitter::slotUpdateScrollBars() +{ + int m_scrollDistance = m_settings->m_scrollNoOfLines * lineSpacing(); + int m_pageSize = pageSize(); + + if( needVScrollBar() ) + { + m_vScroll->show(); + + m_vScroll->blockSignals( true ); + m_vScroll->setRange( minVScrollId(), + maxVScrollId() ); + m_vScroll->setValue( scrollId() ); + m_vScroll->setSteps( m_scrollDistance, m_pageSize ); + m_vScroll->blockSignals( false ); + } + else + { + m_vScroll->hide(); + } + + if( needHScrollBar() ) + { + m_hScroll->show(); + m_hScroll->blockSignals( true ); + m_hScroll->setRange( 0, maxHScrollId() ); + m_hScroll->setValue( maxContentsX() ); + m_hScroll->setSteps( 10, minVisibleWidth() - 10 ); + m_hScroll->blockSignals( false ); + } + else + { + m_hScroll->hide(); + } +} + +void KompareSplitter::slotDelayedUpdateVScrollValue() +{ + QTimer::singleShot( 0, this, SLOT( slotUpdateVScrollValue() ) ); +} + +void KompareSplitter::slotUpdateVScrollValue() +{ + m_vScroll->setValue( scrollId() ); +} + +/* FIXME: this should return the scrollId() from the listview containing the + * /base/ of the diff. but there's bigger issues with that atm. + */ + +int KompareSplitter::scrollId() +{ + QSplitterLayoutStruct *curr = d->list.first(); + for ( curr = d->list.first(); curr; curr = d->list.next() ) + if ( !curr->isHandle ) + return ((KompareListViewFrame*) curr->wid)->view()->scrollId(); + return minVScrollId(); +} + +int KompareSplitter::lineSpacing() +{ + QSplitterLayoutStruct *curr = d->list.first(); + for ( curr = d->list.first(); curr; curr = d->list.next() ) + if ( !curr->isHandle ) + return ((KompareListViewFrame*) + curr->wid)->view()->fontMetrics().lineSpacing(); + return 1; +} + +int KompareSplitter::pageSize() +{ + QSplitterLayoutStruct *curr; + for ( curr = d->list.first(); curr; curr = d->list.next() ) + { + if ( !curr->isHandle ) + { + KompareListView *view = ((KompareListViewFrame*) curr->wid)->view(); + return view->visibleHeight() - QStyle::PM_ScrollBarExtent; + } + } + return 1; +} + +bool KompareSplitter::needVScrollBar() +{ + QSplitterLayoutStruct *curr; + int pagesize = pageSize(); + for ( curr = d->list.first(); curr; curr = d->list.next() ) + { + if( !curr->isHandle ) + { + KompareListView *view = ((KompareListViewFrame*) curr->wid)->view(); + if( view ->contentsHeight() > pagesize) + return true; + } + } + return false; +} + +int KompareSplitter::minVScrollId() +{ + + QSplitterLayoutStruct *curr; + int min = -1; + int mSId; + for ( curr = d->list.first(); curr; curr = d->list.next() ) + { + if(!curr->isHandle) { + KompareListView* view = ((KompareListViewFrame*)curr->wid)->view(); + mSId = view->minScrollId(); + if (mSId < min || min == -1) min = mSId; + } + } + return ( min == -1 ) ? 0 : min; +} + +int KompareSplitter::maxVScrollId() +{ + QSplitterLayoutStruct *curr; + int max = 0; + int mSId; + for ( curr = d->list.first(); curr; curr = d->list.next() ) + { + if ( !curr->isHandle ) + { + mSId = ((KompareListViewFrame*)curr->wid)->view()->maxScrollId(); + if ( mSId > max ) + max = mSId; + } + } + return max; +} + +bool KompareSplitter::needHScrollBar() +{ + QSplitterLayoutStruct *curr; + for ( curr = d->list.first(); curr; curr = d->list.next() ) + { + if( !curr->isHandle ) + { + KompareListView *view = ((KompareListViewFrame*) curr->wid)->view(); + if ( view->contentsWidth() > view->visibleWidth() ) + return true; + } + } + return false; +} + +int KompareSplitter::maxHScrollId() +{ + QSplitterLayoutStruct *curr; + int max = 0; + int mHSId; + for ( curr = d->list.first(); curr; curr = d->list.next() ) + { + if( !curr->isHandle ) + { + KompareListView *view = ((KompareListViewFrame*) curr->wid)->view(); + mHSId = view->contentsWidth() - view->visibleWidth(); + if ( mHSId > max ) + max = mHSId; + } + } + return max; +} + +int KompareSplitter::maxContentsX() +{ + QSplitterLayoutStruct *curr; + int max = 0; + int mCX; + for ( curr = d->list.first(); curr; curr = d->list.next() ) + { + if ( !curr->isHandle ) + { + mCX = ((KompareListViewFrame*) curr->wid)->view()->contentsX(); + if ( mCX > max ) + max = mCX; + } + } + return max; +} + +int KompareSplitter::minVisibleWidth() +{ + // Why the hell do we want to know this? + // ah yes, its because we use it to set the "page size" for horiz. scrolling. + // despite the fact that *noone* has a pgright and pgleft key :P + // But we do have mousewheels with horizontal scrolling functionality, + // pressing shift and scrolling then goes left and right one page at the time + QSplitterLayoutStruct *curr; + int min = -1; + int vW; + for( curr = d->list.first(); curr; curr = d->list.next() ) + { + if ( !curr->isHandle ) { + vW = ((KompareListViewFrame*)curr->wid)->view()->visibleWidth(); + if ( vW < min || min == -1 ) + min = vW; + } + } + return ( min == -1 ) ? 0 : min; +} + +#include "komparesplitter.moc" diff --git a/kompare/komparepart/komparesplitter.h b/kompare/komparepart/komparesplitter.h new file mode 100644 index 00000000..47eee6bf --- /dev/null +++ b/kompare/komparepart/komparesplitter.h @@ -0,0 +1,115 @@ +/*************************************************************************** + kompareview.cpp - description + ------------------- + begin : Wed Jan 14 2004 + copyright : (C) 2004 by Jeff Snyder + email : jeff@caffeinated.me.uk +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef _KOMPARESPLITTER_H_ +#define _KOMPARESPLITTER_H_ + +#include "kompare_qsplitter.h" + +#include + +#include "komparelistview.h" +#include "komparemodellist.h" + +class QSplitterLayoutStruct; +class QTextStream; +class QSplitterHandle; + +namespace Diff2 { +class DiffModel; +class Difference; +} +class ViewSettings; + +class KompareSplitter : public QSplitter +{ + Q_OBJECT + +public: + KompareSplitter(ViewSettings *settings, QWidget *parent=0, const char *name = 0); + ~KompareSplitter(); + +signals: + void selectionChanged( const Diff2::Difference* diff ); + + void configChanged(); + + void scrollViewsToId( int id ); + void setXOffset( int x ); + +public slots: + void slotApplyDifference( bool apply ); + void slotApplyAllDifferences( bool apply ); + void slotApplyDifference( const Diff2::Difference* diff, bool apply ); + + // to update the list views + void slotSetSelection( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void slotSetSelection( const Diff2::Difference* diff ); + + void slotDifferenceClicked( const Diff2::Difference* diff ); + + void slotConfigChanged(); + + void scrollToId( int id ); + void slotDelayedUpdateScrollBars(); + void slotUpdateScrollBars(); + void slotDelayedUpdateVScrollValue(); + void slotUpdateVScrollValue(); + +protected: + void childEvent( QChildEvent * ); + void wheelEvent( QWheelEvent* e ); + void keyPressEvent( QKeyEvent* e ); + + void moveSplitter( QCOORD pos, int id ); + +private slots: + void slotDelayedRepaintHandles(); + void timerTimeout(); + +private: + QSplitterLayoutStruct *addWidget(KompareListViewFrame *w, + bool prepend = FALSE ); + + void doMove( bool backwards, int pos, int id, int delta, + bool mayCollapse, int* positions, int* widths ); + + void repaintHandles(); + + QTimer* m_scrollTimer; + bool restartTimer; + int scrollTo; + + // Scrollbars. all this just for the goddamn scrollbars. i hate them. + int scrollId(); + int lineSpacing(); + int pageSize(); + bool needVScrollBar(); + int minVScrollId(); + int maxVScrollId(); + bool needHScrollBar(); + int maxHScrollId(); + int maxContentsX(); + int minVisibleWidth(); + + ViewSettings* m_settings; + QScrollBar* m_vScroll; + QScrollBar* m_hScroll; + + friend class KompareConnectWidgetFrame; +}; +#endif //_KOMPARESPLITTER_H_ diff --git a/kompare/kompareui.rc b/kompare/kompareui.rc new file mode 100644 index 00000000..cfcf64c8 --- /dev/null +++ b/kompare/kompareui.rc @@ -0,0 +1,33 @@ + + + + &File + + + + + + + + + + &Settings + + + + + + + + + + + + +Main Toolbar + + + + + + diff --git a/kompare/kompareurldialog.cpp b/kompare/kompareurldialog.cpp new file mode 100644 index 00000000..4630cd18 --- /dev/null +++ b/kompare/kompareurldialog.cpp @@ -0,0 +1,143 @@ +/*************************************************************************** + comparedialog.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include + +#include +#include +#include + +#include "diffpage.h" +#include "diffsettings.h" +#include "filespage.h" +#include "filessettings.h" +#include "viewpage.h" +#include "viewsettings.h" + +#include "kompareurldialog.h" + +KompareURLDialog::KompareURLDialog( QWidget *parent, const char *name ) + : KDialogBase( IconList, "", Ok|Cancel, Ok, parent, name ) +{ + setIconListAllVisible(true); + + KConfig* cfg = kapp->config(); + QVBox* filesBox = addVBoxPage( i18n( "Files" ), i18n( "Here you can enter the files you want to compare." ) ); + m_filesPage = new FilesPage( filesBox ); + m_filesSettings = new FilesSettings( this ); + m_filesSettings->loadSettings( cfg ); + m_filesPage->setSettings( m_filesSettings ); + + QVBox* diffBox = addVBoxPage( i18n( "Diff" ), i18n( "Here you can change the options for comparing the files." ) ); + m_diffPage = new DiffPage( diffBox ); + m_diffSettings = new DiffSettings( this ); + m_diffSettings->loadSettings( cfg ); + m_diffPage->setSettings( m_diffSettings ); + + QVBox* viewBox = addVBoxPage( i18n( "Appearance" ), i18n( "Here you can change the options for the view." ) ); + m_viewPage = new ViewPage( viewBox ); + m_viewSettings = new ViewSettings( this ); + m_viewSettings->loadSettings( cfg ); + m_viewPage->setSettings( m_viewSettings ); + + adjustSize(); + + enableButtonSeparator( true ); + + connect( m_filesPage->firstURLRequester(), SIGNAL( textChanged( const QString& ) ), + this, SLOT( slotEnableOk() ) ); + connect( m_filesPage->secondURLRequester(), SIGNAL( textChanged( const QString& ) ), + this, SLOT( slotEnableOk() ) ); + + slotEnableOk(); +} + +KompareURLDialog::~KompareURLDialog() +{ +} + +void KompareURLDialog::slotOk() +{ + m_filesPage->setURLsInComboBoxes(); + + KConfig* cfg = kapp->config(); + + m_filesPage->apply(); + m_diffPage->apply(); + m_viewPage->apply(); + + m_filesSettings->saveSettings( cfg ); + m_diffSettings->saveSettings( cfg ); + m_viewSettings->saveSettings( cfg ); + + cfg->sync(); + + KDialogBase::slotOk(); +} + +void KompareURLDialog::slotEnableOk() +{ + enableButtonOK( !m_filesPage->firstURLRequester()->url().isEmpty() && + !m_filesPage->secondURLRequester()->url().isEmpty() ); +} + +KURL KompareURLDialog::getFirstURL() const +{ + return KURL( m_filesPage->firstURLRequester()->url() ); +} + +KURL KompareURLDialog::getSecondURL() const +{ + return KURL( m_filesPage->secondURLRequester()->url() ); +} + +QString KompareURLDialog::encoding() const +{ + return m_filesPage->encoding(); +} + +void KompareURLDialog::setFirstGroupBoxTitle( const QString& title ) +{ + m_filesPage->setFirstGroupBoxTitle( title ); +} + +void KompareURLDialog::setSecondGroupBoxTitle( const QString& title ) +{ + m_filesPage->setSecondGroupBoxTitle( title ); +} + +void KompareURLDialog::setGroup( const QString& groupName ) +{ + m_filesSettings->setGroup( groupName ); + m_filesSettings->loadSettings( kapp->config() ); + m_filesPage->setSettings( m_filesSettings ); +} + +void KompareURLDialog::setFirstURLRequesterMode( unsigned int mode ) +{ + m_filesPage->setFirstURLRequesterMode( mode ); +} + +void KompareURLDialog::setSecondURLRequesterMode( unsigned int mode ) +{ + m_filesPage->setSecondURLRequesterMode( mode ); +} + +#include "kompareurldialog.moc" + diff --git a/kompare/kompareurldialog.h b/kompare/kompareurldialog.h new file mode 100644 index 00000000..61db9974 --- /dev/null +++ b/kompare/kompareurldialog.h @@ -0,0 +1,76 @@ +/*************************************************************************** + kcompareurldialog.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef KOMPAREURLDIALOG_H +#define KOMPAREURLDIALOG_H + +#include +#include + +class QGroupBox; + +class KComboBox; +class KConfig; +class KFileDialog; +class KURLComboBox; +class KURLRequester; + +class FilesPage; +class FilesSettings; +class DiffPage; +class DiffSettings; +class ViewPage; +class ViewSettings; + +class KompareURLDialog : public KDialogBase +{ + Q_OBJECT + +public: + KompareURLDialog( QWidget* parent = 0, const char* name = 0 ); + ~KompareURLDialog(); + + KURL getFirstURL() const; + KURL getSecondURL() const; + QString encoding() const; + + void setFirstGroupBoxTitle ( const QString& title ); + void setSecondGroupBoxTitle( const QString& title ); + + void setGroup( const QString& groupName ); + + void setFirstURLRequesterMode ( unsigned int mode ); + void setSecondURLRequesterMode( unsigned int mode ); + +protected slots: + virtual void slotOk(); + +private slots: + void slotEnableOk(); + +private: + FilesPage* m_filesPage; + FilesSettings* m_filesSettings; + DiffPage* m_diffPage; + DiffSettings* m_diffSettings; + ViewPage* m_viewPage; + ViewSettings* m_viewSettings; +}; + +#endif diff --git a/kompare/kompareviewpart.desktop b/kompare/kompareviewpart.desktop new file mode 100644 index 00000000..5af4da28 --- /dev/null +++ b/kompare/kompareviewpart.desktop @@ -0,0 +1,4 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=Kompare/ViewPart +X-KDE-Derived=KParts/ReadOnlyPart diff --git a/kompare/libdialogpages/Makefile.am b/kompare/libdialogpages/Makefile.am new file mode 100644 index 00000000..e13405eb --- /dev/null +++ b/kompare/libdialogpages/Makefile.am @@ -0,0 +1,32 @@ +INCLUDES = \ + -I$(top_srcdir)/kompare/libdiff2 \ + $(all_includes) + +noinst_HEADERS = \ + settingsbase.h \ + diffsettings.h \ + filessettings.h \ + viewsettings.h \ + pagebase.h \ + diffpage.h \ + filespage.h \ + viewpage.h + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +noinst_LTLIBRARIES = libdialogpages.la + +# the library's source, library search path, and link libraries +libdialogpages_la_SOURCES = \ + settingsbase.cpp \ + diffsettings.cpp \ + filessettings.cpp \ + viewsettings.cpp \ + pagebase.cpp \ + diffpage.cpp \ + filespage.cpp \ + viewpage.cpp + +libdialogpages_la_LDFLAGS = $(all_libraries) +libdialogpages_la_LIBADD = $(LIB_KFILE) diff --git a/kompare/libdialogpages/diffpage.cpp b/kompare/libdialogpages/diffpage.cpp new file mode 100644 index 00000000..7f805c5b --- /dev/null +++ b/kompare/libdialogpages/diffpage.cpp @@ -0,0 +1,355 @@ +/*************************************************************************** + diffprefs.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2004 Otto Bruggeman + email : otto.bruggeman@home.nl +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "diffsettings.h" + +#include "diffpage.h" + +DiffPage::DiffPage( QWidget* parent ) : PageBase( parent ), + m_ignoreRegExpDialog( 0 ) +{ + addDiffTab(); + + addFormatTab(); + + addOptionsTab(); + + addExcludeTab(); +} + +DiffPage::~DiffPage() +{ + m_settings = 0; +} + +void DiffPage::setSettings( DiffSettings* setts ) +{ + m_settings = setts; + + m_diffURLRequester->setURL( m_settings->m_diffProgram ); + + m_smallerCheckBox->setChecked ( m_settings->m_createSmallerDiff ); + m_largerCheckBox->setChecked ( m_settings->m_largeFiles ); + m_tabsCheckBox->setChecked ( m_settings->m_convertTabsToSpaces ); + m_caseCheckBox->setChecked ( m_settings->m_ignoreChangesInCase ); + m_linesCheckBox->setChecked ( m_settings->m_ignoreEmptyLines ); + m_whitespaceCheckBox->setChecked ( m_settings->m_ignoreWhiteSpace ); + m_allWhitespaceCheckBox->setChecked ( m_settings->m_ignoreAllWhiteSpace ); + m_ignoreTabExpansionCheckBox->setChecked( m_settings->m_ignoreChangesDueToTabExpansion ); + + m_ignoreRegExpCheckBox->setChecked ( m_settings->m_ignoreRegExp ); + m_ignoreRegExpEdit->setCompletedItems( m_settings->m_ignoreRegExpTextHistory ); + m_ignoreRegExpEdit->setText ( m_settings->m_ignoreRegExpText ); + + m_locSpinBox->setValue( m_settings->m_linesOfContext ); + + m_modeButtonGroup->setButton( m_settings->m_format ); + + m_excludeFilePatternCheckBox->setChecked ( m_settings->m_excludeFilePattern ); + slotExcludeFilePatternToggled ( m_settings->m_excludeFilePattern ); + m_excludeFilePatternEditListBox->insertStringList( m_settings->m_excludeFilePatternList ); + + m_excludeFileCheckBox->setChecked( m_settings->m_excludeFilesFile ); + slotExcludeFileToggled ( m_settings->m_excludeFilesFile ); + m_excludeFileURLComboBox->setURLs( m_settings->m_excludeFilesFileHistoryList ); + m_excludeFileURLComboBox->setURL ( KURL( m_settings->m_excludeFilesFileURL ) ); +} + +DiffSettings* DiffPage::settings( void ) +{ + return m_settings; +} + +void DiffPage::restore() +{ + // this shouldn't do a thing... +} + +void DiffPage::apply() +{ + m_settings->m_diffProgram = m_diffURLRequester->url(); + + m_settings->m_largeFiles = m_largerCheckBox->isChecked(); + m_settings->m_createSmallerDiff = m_smallerCheckBox->isChecked(); + m_settings->m_convertTabsToSpaces = m_tabsCheckBox->isChecked(); + m_settings->m_ignoreChangesInCase = m_caseCheckBox->isChecked(); + m_settings->m_ignoreEmptyLines = m_linesCheckBox->isChecked(); + m_settings->m_ignoreWhiteSpace = m_whitespaceCheckBox->isChecked(); + m_settings->m_ignoreAllWhiteSpace = m_allWhitespaceCheckBox->isChecked(); + m_settings->m_ignoreChangesDueToTabExpansion = m_ignoreTabExpansionCheckBox->isChecked(); + + m_settings->m_ignoreRegExp = m_ignoreRegExpCheckBox->isChecked(); + m_settings->m_ignoreRegExpText = m_ignoreRegExpEdit->text(); + m_settings->m_ignoreRegExpTextHistory = m_ignoreRegExpEdit->completionObject()->items(); + + m_settings->m_linesOfContext = m_locSpinBox->value(); + + m_settings->m_format = static_cast( m_modeButtonGroup->selectedId() ); + + m_settings->m_excludeFilePattern = m_excludeFilePatternCheckBox->isChecked(); + m_settings->m_excludeFilePatternList = m_excludeFilePatternEditListBox->items(); + + m_settings->m_excludeFilesFile = m_excludeFileCheckBox->isChecked(); + m_settings->m_excludeFilesFileURL = m_excludeFileURLComboBox->currentText(); + m_settings->m_excludeFilesFileHistoryList = m_excludeFileURLComboBox->urls(); + + m_settings->saveSettings( kapp->config() ); +} + +void DiffPage::setDefaults() +{ + m_diffURLRequester->setURL( "diff" ); + m_smallerCheckBox->setChecked( true ); + m_largerCheckBox->setChecked( true ); + m_tabsCheckBox->setChecked( false ); + m_caseCheckBox->setChecked( false ); + m_linesCheckBox->setChecked( false ); + m_whitespaceCheckBox->setChecked( false ); + m_allWhitespaceCheckBox->setChecked( false ); + m_ignoreTabExpansionCheckBox->setChecked( false ); + m_ignoreRegExpCheckBox->setChecked( false ); + + m_ignoreRegExpEdit->setText( QString::null ); + + m_locSpinBox->setValue( 3 ); + + m_modeButtonGroup->setButton( Kompare::Unified ); + + m_excludeFilePatternCheckBox->setChecked( false ); + + m_excludeFileCheckBox->setChecked( false ); +} + +void DiffPage::slotShowRegExpEditor() +{ + if ( ! m_ignoreRegExpDialog ) + m_ignoreRegExpDialog = KParts::ComponentFactory::createInstanceFromQuery( "KRegExpEditor/KRegExpEditor", QString::null, this ); + + KRegExpEditorInterface *iface = static_cast( m_ignoreRegExpDialog->qt_cast( "KRegExpEditorInterface" ) ); + + if ( !iface ) + return; + + iface->setRegExp( m_ignoreRegExpEdit->text() ); + bool ok = m_ignoreRegExpDialog->exec(); + + if ( ok ) + m_ignoreRegExpEdit->setText( iface->regExp() ); +} + +void DiffPage::slotExcludeFilePatternToggled( bool on ) +{ + if ( !on ) + { + m_excludeFilePatternEditListBox->setEnabled( false ); + } + else + { + m_excludeFilePatternEditListBox->setEnabled( true ); + } +} + +void DiffPage::slotExcludeFileToggled( bool on ) +{ + if ( !on ) + { + m_excludeFileURLComboBox->setEnabled( false ); + m_excludeFileURLRequester->setEnabled( false ); + } + else + { + m_excludeFileURLComboBox->setEnabled( true ); + m_excludeFileURLRequester->setEnabled( true ); + } +} + +void DiffPage::addDiffTab() +{ + QWidget* page = new QWidget( this ); + QVBoxLayout* layout = new QVBoxLayout( page ); + layout->setSpacing( KDialog::spacingHint() ); + layout->setMargin( KDialog::marginHint() ); + + // add diff program selector + m_diffProgramGroup = new QVButtonGroup( i18n( "Diff Program" ), page ); + layout->addWidget( m_diffProgramGroup ); + m_diffProgramGroup->setMargin( KDialog::marginHint() ); + + m_diffURLRequester = new KURLRequester( m_diffProgramGroup, "diffURLRequester" ); + QWhatsThis::add( m_diffURLRequester, i18n( "You can select a different diff program here. On Solaris the standard diff program does not support all the options that the GNU version does. This way you can select that version." ) ); + + layout->addStretch( 1 ); + page->setMinimumSize( sizeHintForWidget( page ) ); + + addTab( page, i18n( "&Diff" ) ); +} + +void DiffPage::addFormatTab() +{ + QWidget* page = new QWidget( this ); + QVBoxLayout* layout = new QVBoxLayout( page ); + layout->setSpacing( KDialog::spacingHint() ); + layout->setMargin( KDialog::marginHint() ); + + // add diff modes + m_modeButtonGroup = new QVButtonGroup( i18n( "Output Format" ), page ); + QWhatsThis::add( m_modeButtonGroup, i18n( "Select the format of the output generated by diff. Unified is the one that is used most frequently because it is very readable. The KDE developers like this format the best so use it for sending patches." ) ); + layout->addWidget( m_modeButtonGroup ); + m_modeButtonGroup->setMargin( KDialog::marginHint() ); + + QRadioButton* radioButton; + radioButton = new QRadioButton( i18n( "Context" ), m_modeButtonGroup ); + radioButton = new QRadioButton( i18n( "Ed" ), m_modeButtonGroup ); + radioButton->setEnabled( false ); + radioButton = new QRadioButton( i18n( "Normal" ), m_modeButtonGroup ); + radioButton = new QRadioButton( i18n( "RCS" ), m_modeButtonGroup ); + radioButton->setEnabled( false ); + radioButton = new QRadioButton( i18n( "Unified" ), m_modeButtonGroup ); + + // #lines of context (loc) + QHGroupBox* groupBox = new QHGroupBox( i18n( "Lines of Context" ), page ); + layout->addWidget( groupBox ); + groupBox->setMargin( KDialog::marginHint() ); + + QLabel* label = new QLabel( i18n( "Number of context lines:" ), groupBox ); + m_locSpinBox = new QSpinBox( 0, 100, 1, groupBox ); + QWhatsThis::add( m_locSpinBox, i18n( "The number of context lines is normally 2 or 3. This makes the diff readable and applicable in most cases. More than 3 lines will only bloat the diff unnecessarily." ) ); + label->setBuddy( m_locSpinBox ); + + layout->addStretch( 1 ); + page->setMinimumSize( sizeHintForWidget( page ) ); + + addTab( page, i18n( "&Format" ) ); +} + +void DiffPage::addOptionsTab() +{ + QWidget* page = new QWidget( this ); + QVBoxLayout* layout = new QVBoxLayout( page ); + layout->setSpacing( KDialog::spacingHint() ); + layout->setMargin( KDialog::marginHint() ); + + // add diff options + QVButtonGroup* optionButtonGroup = new QVButtonGroup( i18n( "General" ), page ); + layout->addWidget( optionButtonGroup ); + optionButtonGroup->setMargin( KDialog::marginHint() ); + + m_smallerCheckBox = new QCheckBox( i18n( "&Look for smaller changes" ), optionButtonGroup ); + QToolTip::add( m_smallerCheckBox, i18n( "This corresponds to the -d diff option." ) ); + m_largerCheckBox = new QCheckBox( i18n( "O&ptimize for large files" ), optionButtonGroup ); + QToolTip::add( m_largerCheckBox, i18n( "This corresponds to the -H diff option." ) ); + m_caseCheckBox = new QCheckBox( i18n( "&Ignore changes in case" ), optionButtonGroup ); + QToolTip::add( m_caseCheckBox, i18n( "This corresponds to the -i diff option." ) ); + + QHBoxLayout* groupLayout = new QHBoxLayout( layout, -1, "regexp_horizontal_layout" ); + groupLayout->setMargin( KDialog::marginHint() ); + + m_ignoreRegExpCheckBox = new QCheckBox( i18n( "Ignore regexp:" ), page ); + QToolTip::add( m_ignoreRegExpCheckBox, i18n( "This option corresponds to the -I diff option." ) ); + groupLayout->addWidget( m_ignoreRegExpCheckBox ); + m_ignoreRegExpEdit = new KLineEdit( QString::null, page, "regexplineedit" ); + QToolTip::add( m_ignoreRegExpEdit, i18n( "Add the regular expression here that you want to use\nto ignore lines that match it." ) ); + groupLayout->addWidget( m_ignoreRegExpEdit ); + + if ( !KTrader::self()->query("KRegExpEditor/KRegExpEditor").isEmpty() ) + { + // Ok editor is available, use it + QButton* ignoreRegExpEditButton = new QPushButton( i18n( "&Edit..." ), page, "regexp_editor_button" ); + QToolTip::add( ignoreRegExpEditButton, i18n( "Clicking this will open a regular expression dialog where\nyou can graphically create regular expressions." ) ); + groupLayout->addWidget( ignoreRegExpEditButton ); + connect( ignoreRegExpEditButton, SIGNAL( clicked() ), this, SLOT( slotShowRegExpEditor() ) ); + } + + QVButtonGroup* moreOptionButtonGroup = new QVButtonGroup( i18n( "Whitespace" ), page ); + layout->addWidget( moreOptionButtonGroup ); + moreOptionButtonGroup->setMargin( KDialog::marginHint() ); + + m_tabsCheckBox = new QCheckBox( i18n( "E&xpand tabs to spaces in output" ), moreOptionButtonGroup ); + QToolTip::add( m_tabsCheckBox, i18n( "This option corresponds to the -t diff option." ) ); + m_linesCheckBox = new QCheckBox( i18n( "I&gnore added or removed empty lines" ), moreOptionButtonGroup ); + QToolTip::add( m_linesCheckBox, i18n( "This option corresponds to the -B diff option." ) ); + m_whitespaceCheckBox = new QCheckBox( i18n( "Ig&nore changes in the amount of whitespace" ), moreOptionButtonGroup ); + QToolTip::add( m_whitespaceCheckBox, i18n( "This option corresponds to the -b diff option." ) ); + m_allWhitespaceCheckBox = new QCheckBox( i18n( "Ign&ore all whitespace" ), moreOptionButtonGroup ); + QToolTip::add( m_allWhitespaceCheckBox, i18n( "This option corresponds to the -w diff option." ) ); + m_ignoreTabExpansionCheckBox = new QCheckBox( i18n( "Igno&re changes due to tab expansion" ), moreOptionButtonGroup ); + QToolTip::add( m_ignoreTabExpansionCheckBox, i18n( "This option corresponds to the -E diff option." ) ); + + layout->addStretch( 1 ); + page->setMinimumSize( sizeHintForWidget( page ) ); + + addTab( page, i18n( "O&ptions" ) ); +} + +void DiffPage::addExcludeTab() +{ + QWidget* page = new QWidget( this ); + QVBoxLayout* layout = new QVBoxLayout( page ); + layout->setSpacing( KDialog::spacingHint() ); + layout->setMargin( KDialog::marginHint() ); + + QHGroupBox* excludeFilePatternGroupBox = new QHGroupBox( i18n( "File Pattern to Exclude" ), page ); + m_excludeFilePatternCheckBox = new QCheckBox( "", excludeFilePatternGroupBox ); + QToolTip::add( m_excludeFilePatternCheckBox, i18n( "If this is checked you can enter a shell pattern in the text box on the right or select entries from the list." ) ); + m_excludeFilePatternEditListBox = new KEditListBox( excludeFilePatternGroupBox, "exclude_file_pattern_editlistbox", false, KEditListBox::Add|KEditListBox::Remove ); + QToolTip::add( m_excludeFilePatternEditListBox, i18n( "Here you can enter or remove a shell pattern or select one or more entries from the list." ) ); + layout->addWidget( excludeFilePatternGroupBox ); + + + connect( m_excludeFilePatternCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotExcludeFilePatternToggled(bool))); + + QHGroupBox* excludeFileNameGroupBox = new QHGroupBox( i18n( "File with Filenames to Exclude" ), page ); + m_excludeFileCheckBox = new QCheckBox( "", excludeFileNameGroupBox ); + QToolTip::add( m_excludeFileCheckBox, i18n( "If this is checked you can enter a filename in the combo box on the right." ) ); + m_excludeFileURLComboBox = new KURLComboBox( KURLComboBox::Files, true, excludeFileNameGroupBox, "exclude_file_urlcombo" ); + QToolTip::add( m_excludeFileURLComboBox, i18n( "Here you can enter the URL of a file with shell patterns to ignore during the comparison of the folders." ) ); + m_excludeFileURLRequester = new KURLRequester( m_excludeFileURLComboBox, excludeFileNameGroupBox, "exclude_file_name_urlrequester" ); + QToolTip::add( m_excludeFileURLRequester, i18n( "Any file you select in the dialog that pops up when you click it will be put in the dialog to the left of this button." ) ); + layout->addWidget( excludeFileNameGroupBox ); + + connect( m_excludeFileCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotExcludeFileToggled(bool))); + + layout->addStretch( 1 ); + page->setMinimumSize( sizeHintForWidget( page ) ); + + addTab( page, i18n( "&Exclude" ) ); +} + +#include "diffpage.moc" diff --git a/kompare/libdialogpages/diffpage.h b/kompare/libdialogpages/diffpage.h new file mode 100644 index 00000000..dee23989 --- /dev/null +++ b/kompare/libdialogpages/diffpage.h @@ -0,0 +1,100 @@ +/*************************************************************************** + diffprefs.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef DIFFPAGE_H +#define DIFFPAGE_H + +#include "pagebase.h" + +class QCheckBox; +class QDialog; +class QSpinBox; +class QStringList; +class QVButtonGroup; +class QWidget; + +class KLineEdit; +class KComboBox; +class KEditListBox; +class KURLComboBox; +class KURLRequester; + +class DiffSettings; + +class DiffPage : public PageBase +{ +Q_OBJECT +public: + DiffPage( QWidget* ); + ~DiffPage(); + +public: + void setSettings( DiffSettings* ); + DiffSettings* settings( void ); + +public: + virtual void restore(); + virtual void apply(); + virtual void setDefaults(); + +protected slots: + void slotShowRegExpEditor(); + void slotExcludeFilePatternToggled( bool ); + void slotExcludeFileToggled( bool ); + +private: + void addDiffTab(); + void addFormatTab(); + void addOptionsTab(); + void addExcludeTab(); + +public: + DiffSettings* m_settings; + + KURLRequester* m_diffURLRequester; + + QCheckBox* m_smallerCheckBox; + QCheckBox* m_largerCheckBox; + QCheckBox* m_tabsCheckBox; + QCheckBox* m_caseCheckBox; + QCheckBox* m_linesCheckBox; + QCheckBox* m_whitespaceCheckBox; + QCheckBox* m_allWhitespaceCheckBox; + QCheckBox* m_ignoreTabExpansionCheckBox; + + QCheckBox* m_ignoreRegExpCheckBox; + KLineEdit* m_ignoreRegExpEdit; + QStringList* m_ignoreRegExpEditHistory; + QDialog* m_ignoreRegExpDialog; + + QCheckBox* m_excludeFilePatternCheckBox; + KEditListBox* m_excludeFilePatternEditListBox; + + QCheckBox* m_excludeFileCheckBox; + KURLComboBox* m_excludeFileURLComboBox; + KURLRequester* m_excludeFileURLRequester; + + // loc == lines of context + QSpinBox* m_locSpinBox; + + QVButtonGroup* m_modeButtonGroup; + QVButtonGroup* m_diffProgramGroup; +}; + +#endif diff --git a/kompare/libdialogpages/diffsettings.cpp b/kompare/libdialogpages/diffsettings.cpp new file mode 100644 index 00000000..20504ffe --- /dev/null +++ b/kompare/libdialogpages/diffsettings.cpp @@ -0,0 +1,108 @@ +/*************************************************************************** + diffsettings.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2004 Otto Bruggeman + email : otto.bruggeman@home.nl +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +****************************************************************************/ + +#include + +#include "diffsettings.h" + +DiffSettings::DiffSettings( QWidget* parent ) + : SettingsBase( parent ), + m_linesOfContext( 0 ), + m_format( Kompare::Unified ), + m_largeFiles( false ), + m_ignoreWhiteSpace( false ), + m_ignoreAllWhiteSpace( false ), + m_ignoreEmptyLines( false ), + m_ignoreChangesDueToTabExpansion( false ), + m_createSmallerDiff( false ), + m_ignoreChangesInCase( false ), + m_showCFunctionChange( false ), + m_convertTabsToSpaces( false ), + m_ignoreRegExp( false ), + m_recursive( false ), + m_newFiles( false ), + m_excludeFilePattern( false ), + m_excludeFilesFile( false ) +{ +} + +DiffSettings::~DiffSettings() +{ +} + +void DiffSettings::loadSettings( KConfig* config ) +{ + KConfigGroup group( config, "Diff Options" ); + m_diffProgram = group.readEntry ( "DiffProgram", "" ); + m_linesOfContext = group.readNumEntry ( "LinesOfContext", 3 ); + m_largeFiles = group.readBoolEntry( "LargeFiles", true ); + m_ignoreWhiteSpace = group.readBoolEntry( "IgnoreWhiteSpace", false ); + m_ignoreAllWhiteSpace = group.readBoolEntry( "IgnoreAllWhiteSpace", false ); + m_ignoreEmptyLines = group.readBoolEntry( "IgnoreEmptyLines", false ); + m_ignoreChangesDueToTabExpansion = group.readBoolEntry( "IgnoreChangesDueToTabExpansion", false ); + m_ignoreChangesInCase = group.readBoolEntry( "IgnoreChangesInCase", false ); + m_ignoreRegExp = group.readBoolEntry( "IgnoreRegExp", false ); + m_ignoreRegExpText = group.readEntry ( "IgnoreRegExpText", "" ); + m_ignoreRegExpTextHistory = group.readListEntry( "IgnoreRegExpTextHistory" ); + m_createSmallerDiff = group.readBoolEntry( "CreateSmallerDiff", true ); + m_convertTabsToSpaces = group.readBoolEntry( "ConvertTabsToSpaces", false ); + m_showCFunctionChange = group.readBoolEntry( "ShowCFunctionChange", false ); + m_recursive = group.readBoolEntry( "CompareRecursively", true ); + m_newFiles = group.readBoolEntry( "NewFiles", true ); + + m_format = static_cast( group.readNumEntry( "Format", Kompare::Unified ) ); + + KConfigGroup group2 ( config, "Exclude File Options" ); + m_excludeFilePattern = group2.readBoolEntry( "Pattern", false ); + m_excludeFilePatternList = group2.readListEntry( "PatternList" ); + m_excludeFilesFile = group2.readBoolEntry( "File", false ); + m_excludeFilesFileURL = group2.readEntry ( "FileURL", "" ); + m_excludeFilesFileHistoryList = group2.readListEntry( "FileHistoryList" ); +} + +void DiffSettings::saveSettings( KConfig* config ) +{ + KConfigGroup group( config, "Diff Options" ); + group.writeEntry( "DiffProgram", m_diffProgram ); + group.writeEntry( "LinesOfContext", m_linesOfContext ); + group.writeEntry( "Format", m_format ); + group.writeEntry( "LargeFiles", m_largeFiles ); + group.writeEntry( "IgnoreWhiteSpace", m_ignoreWhiteSpace ); + group.writeEntry( "IgnoreAllWhiteSpace", m_ignoreAllWhiteSpace ); + group.writeEntry( "IgnoreEmptyLines", m_ignoreEmptyLines ); + group.writeEntry( "IgnoreChangesInCase", m_ignoreChangesInCase ); + group.writeEntry( "IgnoreChangesDueToTabExpansion", m_ignoreChangesDueToTabExpansion ); + group.writeEntry( "IgnoreRegExp", m_ignoreRegExp ); + group.writeEntry( "IgnoreRegExpText", m_ignoreRegExpText ); + group.writeEntry( "IgnoreRegExpTextHistory", m_ignoreRegExpTextHistory ); + group.writeEntry( "CreateSmallerDiff", m_createSmallerDiff ); + group.writeEntry( "ConvertTabsToSpaces", m_convertTabsToSpaces ); + group.writeEntry( "ShowCFunctionChange", m_showCFunctionChange ); + group.writeEntry( "CompareRecursively", m_recursive ); + group.writeEntry( "NewFiles", m_newFiles ); + group.setDirty( true ); + + KConfigGroup group2( config, "Exclude File Options" ); + group2.writeEntry( "Pattern", m_excludeFilePattern ); + group2.writeEntry( "PatternList", m_excludeFilePatternList ); + group2.writeEntry( "File", m_excludeFilesFile ); + group2.writeEntry( "FileURL", m_excludeFilesFileURL ); + group2.writeEntry( "FileHistoryList", m_excludeFilesFileHistoryList ); + group2.setDirty( true ); +} + +#include "diffsettings.moc" diff --git a/kompare/libdialogpages/diffsettings.h b/kompare/libdialogpages/diffsettings.h new file mode 100644 index 00000000..46962cb8 --- /dev/null +++ b/kompare/libdialogpages/diffsettings.h @@ -0,0 +1,66 @@ +/*************************************************************************** + diffsettings.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef DIFFSETTINGS_H +#define DIFFSETTINGS_H + +#include +#include + +#include "kompare.h" +#include "settingsbase.h" + +class DiffSettings : public SettingsBase +{ +Q_OBJECT +public: + DiffSettings( QWidget* parent ); + virtual ~DiffSettings(); +public: + // some virtual functions that will be overloaded from the base class + virtual void loadSettings( KConfig* config ); + virtual void saveSettings( KConfig* config ); + +public: + QString m_diffProgram; + int m_linesOfContext; + Kompare::Format m_format; + bool m_largeFiles; // -H + bool m_ignoreWhiteSpace; // -b + bool m_ignoreAllWhiteSpace; // -w + bool m_ignoreEmptyLines; // -B + bool m_ignoreChangesDueToTabExpansion; // -E + bool m_createSmallerDiff; // -d + bool m_ignoreChangesInCase; // -i + bool m_showCFunctionChange; // -p + bool m_convertTabsToSpaces; // -t + bool m_ignoreRegExp; // -I + QString m_ignoreRegExpText; // the RE for -I + QStringList m_ignoreRegExpTextHistory; + bool m_recursive; // -r + bool m_newFiles; // -N +// bool m_allText; // -a + bool m_excludeFilePattern; // -x + QStringList m_excludeFilePatternList; // The list of patterns for -x + bool m_excludeFilesFile; // -X + QString m_excludeFilesFileURL; // The filename to -X + QStringList m_excludeFilesFileHistoryList; // The history list of filenames +}; + +#endif diff --git a/kompare/libdialogpages/filespage.cpp b/kompare/libdialogpages/filespage.cpp new file mode 100644 index 00000000..9e15a129 --- /dev/null +++ b/kompare/libdialogpages/filespage.cpp @@ -0,0 +1,170 @@ +/*************************************************************************** + filespage.cpp + ------------------- + begin : Sun Apr 18 2004 + copyright : (C) 2004 Otto Bruggeman + email : otto.bruggeman@home.nl + +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "filessettings.h" +#include "filespage.h" + +FilesPage::FilesPage( QWidget* parent ) : PageBase( parent ), m_URLChanged( false ) +{ + QWidget* page = new QWidget( this ); + QVBoxLayout* layout = new QVBoxLayout( page ); + layout->setSpacing( KDialog::spacingHint() ); + layout->setMargin( KDialog::marginHint() ); + + m_firstGB = new QGroupBox( 1, Qt::Vertical, "You have to set this moron :)", page ); + m_firstURLComboBox = new KURLComboBox( KURLComboBox::Both, true, m_firstGB, "SourceURLComboBox" ); + m_firstURLRequester = new KURLRequester( m_firstURLComboBox, m_firstGB ); + m_firstURLRequester->setFocus(); + + m_secondGB = new QGroupBox( 1, Qt::Vertical, "This too moron !", page ); + m_secondURLComboBox = new KURLComboBox( KURLComboBox::Both, true, m_secondGB, "DestURLComboBox" ); + m_secondURLRequester = new KURLRequester( m_secondURLComboBox, m_secondGB ); + + connect( m_firstURLRequester, SIGNAL( urlSelected( const QString & ) ), SLOT( setSecondURL( const QString & ) ) ); + connect( m_secondURLRequester, SIGNAL( urlSelected( const QString & ) ), SLOT( setFirstURL( const QString & ) ) ); + + m_thirdGB = new QGroupBox( 1, Qt::Vertical, i18n( "Encoding" ), page ); + m_encodingComboBox = new QComboBox( false, m_thirdGB, "encoding_combobox" ); + m_encodingComboBox->insertStringList( KGlobal::charsets()->availableEncodingNames() ); + + layout->addWidget( m_firstGB ); + layout->addWidget( m_secondGB ); + layout->addWidget( m_thirdGB ); + + layout->addStretch( 1 ); + page->setMinimumSize( sizeHintForWidget( page ) ); + + addTab( page, i18n( "&Files" ) ); +} + +FilesPage::~FilesPage() +{ + m_settings = 0; +} + +KURLRequester* FilesPage::firstURLRequester() const +{ + return m_firstURLRequester; +} + +KURLRequester* FilesPage::secondURLRequester() const +{ + return m_secondURLRequester; +} + +QString FilesPage::encoding() const +{ + return m_encodingComboBox->currentText(); +} + +void FilesPage::setFirstGroupBoxTitle( const QString& title ) +{ + m_firstGB->setTitle( title ); +} + +void FilesPage::setSecondGroupBoxTitle( const QString& title ) +{ + m_secondGB->setTitle( title ); +} + +void FilesPage::setURLsInComboBoxes() +{ +// kdDebug() << "first : " << m_firstURLComboBox->currentText() << endl; +// kdDebug() << "second: " << m_secondURLComboBox->currentText() << endl; + m_firstURLComboBox->setURL( KURL( m_firstURLComboBox->currentText() ) ); + m_secondURLComboBox->setURL( KURL( m_secondURLComboBox->currentText() ) ); +} + + +void FilesPage::setFirstURLRequesterMode( unsigned int mode ) +{ + m_firstURLRequester->setMode( mode ); +} + +void FilesPage::setSecondURLRequesterMode( unsigned int mode ) +{ + m_secondURLRequester->setMode( mode ); +} + +void FilesPage::setSettings( FilesSettings* settings ) +{ + m_settings = settings; + + m_firstURLComboBox->setURLs( m_settings->m_recentSources ); + m_firstURLComboBox->setURL( KURL( m_settings->m_lastChosenSourceURL ) ); + m_secondURLComboBox->setURLs( m_settings->m_recentDestinations ); + m_secondURLComboBox->setURL( KURL( m_settings->m_lastChosenDestinationURL ) ); + m_encodingComboBox->setCurrentText( m_settings->m_encoding ); +} + +void FilesPage::restore() +{ + // this shouldn't do a thing... +} + +void FilesPage::apply() +{ + m_settings->m_recentSources = m_firstURLComboBox->urls(); + m_settings->m_lastChosenSourceURL = m_firstURLComboBox->currentText(); + m_settings->m_recentDestinations = m_secondURLComboBox->urls(); + m_settings->m_lastChosenDestinationURL = m_secondURLComboBox->currentText(); + m_settings->m_encoding = m_encodingComboBox->currentText(); +} + +void FilesPage::setDefaults() +{ + m_firstURLComboBox->setURLs( "" ); + m_firstURLComboBox->setURL( KURL( "" ) ); + m_secondURLComboBox->setURLs( "" ); + m_secondURLComboBox->setURL( KURL( "" ) ); + m_encodingComboBox->setCurrentText( "Default" ); +} + +void FilesPage::setFirstURL( const QString &url ) +{ + QString _url = url; + if ( !m_URLChanged ) + { + m_firstURLRequester->setURL( _url.remove( url.section( '/', -1 ) ) ); + m_URLChanged = true; + } +} + +void FilesPage::setSecondURL( const QString &url ) +{ + QString _url = url; + if ( !m_URLChanged ) + { + m_secondURLRequester->setURL( _url.remove( url.section( '/', -1 ) ) ); + m_URLChanged = true; + } +} + +#include "filespage.moc" diff --git a/kompare/libdialogpages/filespage.h b/kompare/libdialogpages/filespage.h new file mode 100644 index 00000000..145c4614 --- /dev/null +++ b/kompare/libdialogpages/filespage.h @@ -0,0 +1,83 @@ +/*************************************************************************** + kcompareurldialog.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef FILESPAGE_H +#define FILESPAGE_H + +#include "pagebase.h" + +class QGroupBox; + +class QComboBox; +class KComboBox; +class KConfig; +class KFileDialog; +class KURLComboBox; +class KURLRequester; + +class FilesSettings; + +class FilesPage : PageBase +{ +Q_OBJECT +public: + FilesPage( QWidget* parent ); + virtual ~FilesPage(); + +public: + KURLRequester* firstURLRequester() const; + KURLRequester* secondURLRequester() const; + + QString encoding() const; + + void setFirstGroupBoxTitle ( const QString& title ); + void setSecondGroupBoxTitle( const QString& title ); + + void setURLsInComboBoxes(); + + void setFirstURLRequesterMode( unsigned int mode ); + void setSecondURLRequesterMode( unsigned int mode ); + +public: + virtual void setSettings( FilesSettings* settings ); + virtual void restore(); + virtual void apply(); + virtual void setDefaults(); + +protected slots: + void setFirstURL( const QString & ); + void setSecondURL( const QString & ); + +private: + QGroupBox* m_firstGB; + QGroupBox* m_secondGB; + QGroupBox* m_thirdGB; + KURLComboBox* m_firstURLComboBox; + KURLComboBox* m_secondURLComboBox; + KURLRequester* m_firstURLRequester; + KURLRequester* m_secondURLRequester; + // Use this bool to lock the connection between both KURLRequesters. + // This prevents annoying behaviour + bool m_URLChanged; + QComboBox* m_encodingComboBox; + + FilesSettings* m_settings; +}; + +#endif diff --git a/kompare/libdialogpages/filessettings.cpp b/kompare/libdialogpages/filessettings.cpp new file mode 100644 index 00000000..d5a94a66 --- /dev/null +++ b/kompare/libdialogpages/filessettings.cpp @@ -0,0 +1,60 @@ +/*************************************************************************** + filessettings.cpp - description + ------------------- + begin : Sun Apr 18 2004 + copyright : (C) 2004 Otto Bruggeman + email : otto.bruggeman@home.nl + +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +****************************************************************************/ + +#include +#include + +#include "filessettings.h" + +FilesSettings::FilesSettings( QWidget* parent ) + : SettingsBase( parent ) +{ +} + +FilesSettings::~FilesSettings() +{ +} + +void FilesSettings::loadSettings( KConfig* config ) +{ + config->setGroup( m_configGroupName ); + + m_recentSources = config->readListEntry( "Recent Sources" ); + m_lastChosenSourceURL = config->readEntry ( "LastChosenSourceListEntry", "" ); + m_recentDestinations = config->readListEntry( "Recent Destinations" ); + m_lastChosenDestinationURL = config->readEntry ( "LastChosenDestinationListEntry", "" ); + m_encoding = config->readEntry ( "Encoding", "default" ); +} + +void FilesSettings::saveSettings( KConfig* config ) +{ + config->setGroup( m_configGroupName ); + config->writeEntry( "Recent Sources", m_recentSources ); + config->writeEntry( "Recent Destinations", m_recentDestinations ); + config->writeEntry( "LastChosenSourceListEntry", m_lastChosenSourceURL ); + config->writeEntry( "LastChosenDestinationListEntry", m_lastChosenDestinationURL ); + config->writeEntry( "Encoding", m_encoding ); + config->sync(); +} + +void FilesSettings::setGroup( const QString& groupName ) +{ + m_configGroupName = groupName; +} + +#include "filessettings.moc" diff --git a/kompare/libdialogpages/filessettings.h b/kompare/libdialogpages/filessettings.h new file mode 100644 index 00000000..3c394dbb --- /dev/null +++ b/kompare/libdialogpages/filessettings.h @@ -0,0 +1,53 @@ +/*************************************************************************** + filessettings.h - description + ------------------- + begin : Sun Apr 18 2004 + copyright : (C) 2004 Otto Bruggeman + email : otto.bruggeman@home.nl + +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +****************************************************************************/ + +#ifndef FILESSETTINGS_H +#define FILESSETTINGS_H + +#include + +#include "settingsbase.h" + +class KConfig; + +class FilesSettings : public SettingsBase +{ +Q_OBJECT +public: + FilesSettings( QWidget* parent ); + virtual ~FilesSettings(); + +public: + // some virtual functions that will be overloaded from the base class + virtual void loadSettings( KConfig* config ); + virtual void saveSettings( KConfig* config ); + + void setGroup( const QString& groupName ); + +public: + QString m_configGroupName; + + QStringList m_recentSources; + QString m_lastChosenSourceURL; + QStringList m_recentDestinations; + QString m_lastChosenDestinationURL; + QString m_encoding; +}; + +#endif // FILESSETTINGS_H + diff --git a/kompare/libdialogpages/pagebase.cpp b/kompare/libdialogpages/pagebase.cpp new file mode 100644 index 00000000..de062634 --- /dev/null +++ b/kompare/libdialogpages/pagebase.cpp @@ -0,0 +1,104 @@ +/*************************************************************************** + prefsbase.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include +#include + +#include "pagebase.h" + +PageBase::PageBase( QWidget* parent ) : KTabCtl( parent ) +{ + +} + +PageBase::~PageBase() +{ + +} + +/** No descriptions */ +QSize PageBase::sizeHintForWidget( QWidget* widget ) +{ + // + // The size is computed by adding the sizeHint().height() of all + // widget children and taking the width of the widest child and adding + // layout()->margin() and layout()->spacing() + // + + // this code in this method has been ripped out of a file in kbabel + // so copyright goes to the kbabel authors. + + QSize size; + + int numChild = 0; + QObjectList *l = (QObjectList*)(widget->children()); + + for( uint i=0; i < l->count(); i++ ) + { + QObject *o = l->at(i); + if( o->isWidgetType() ) + { + numChild += 1; + QWidget *w=((QWidget*)o); + + QSize s = w->sizeHint(); + if( s.isEmpty() == true ) + { + s = QSize( 50, 100 ); // Default size + } + size.setHeight( size.height() + s.height() ); + if( s.width() > size.width() ) + { + size.setWidth( s.width() ); + } + } + } + + if( numChild > 0 ) + { + size.setHeight( size.height() + widget->layout()->spacing()*(numChild-1) ); + size += QSize( widget->layout()->margin()*2, widget->layout()->margin()*2 + 1 ); + } + else + { + size = QSize( 1, 1 ); + } + + return( size ); +} + +/** No descriptions */ +void PageBase::apply() +{ + +} + +/** No descriptions */ +void PageBase::restore() +{ + +} + +/** No descriptions */ +void PageBase::setDefaults() +{ + +} + +#include "pagebase.moc" diff --git a/kompare/libdialogpages/pagebase.h b/kompare/libdialogpages/pagebase.h new file mode 100644 index 00000000..7aab7b81 --- /dev/null +++ b/kompare/libdialogpages/pagebase.h @@ -0,0 +1,49 @@ +/*************************************************************************** + prefsbase.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef PAGEBASE_H +#define PAGEBASE_H + +#include +#include + +#include +#include + +#include "kompare.h" + +class PageBase : public KTabCtl +{ +Q_OBJECT +public: + PageBase( QWidget* ); + ~PageBase(); + +public: + /** No descriptions */ + QSize sizeHintForWidget( QWidget* widget ); + /** No descriptions */ + virtual void restore(); + /** No descriptions */ + virtual void apply(); + /** No descriptions */ + virtual void setDefaults(); +}; + +#endif diff --git a/kompare/libdialogpages/settingsbase.cpp b/kompare/libdialogpages/settingsbase.cpp new file mode 100644 index 00000000..dc7b64d6 --- /dev/null +++ b/kompare/libdialogpages/settingsbase.cpp @@ -0,0 +1,42 @@ +/*************************************************************************** + settingsbase.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include + +#include "settingsbase.h" + +SettingsBase::SettingsBase( QWidget* parent ) : QObject( parent ) +{ + +} + +SettingsBase::~SettingsBase() +{ + +} + +void SettingsBase::loadSettings( KConfig* /* config */ ) +{ +} + +void SettingsBase::saveSettings( KConfig* /* config */ ) +{ +} + +#include "settingsbase.moc" diff --git a/kompare/libdialogpages/settingsbase.h b/kompare/libdialogpages/settingsbase.h new file mode 100644 index 00000000..5ec035ba --- /dev/null +++ b/kompare/libdialogpages/settingsbase.h @@ -0,0 +1,42 @@ +/*************************************************************************** + settingsbase.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef SETTINGSBASE_H +#define SETTINGSBASE_H + +#include + +#include "kompare.h" + +class QWidget; +class KConfig; + +class SettingsBase : public QObject +{ +Q_OBJECT +public: + SettingsBase( QWidget* parent ); + ~SettingsBase(); + +public: + virtual void loadSettings( KConfig* config ); + virtual void saveSettings( KConfig* config ); +}; + +#endif diff --git a/kompare/libdialogpages/viewpage.cpp b/kompare/libdialogpages/viewpage.cpp new file mode 100644 index 00000000..c4e61e8d --- /dev/null +++ b/kompare/libdialogpages/viewpage.cpp @@ -0,0 +1,179 @@ +/*************************************************************************** + viewprefs.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2002 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "viewpage.h" +#include "viewsettings.h" + +ViewPage::ViewPage( QWidget* parent ) : PageBase( parent ) +{ + QWidget* page; + QVBoxLayout* layout; + QGroupBox* colorGroupBox; + QHGroupBox* snolGroupBox; + QHGroupBox* tabGroupBox; + QLabel* label; + + page = new QWidget( this ); + layout = new QVBoxLayout( page ); + layout->setSpacing( KDialog::spacingHint() ); + layout->setMargin( KDialog::marginHint() ); + + // add a groupbox + colorGroupBox = new QGroupBox( 2, Qt::Horizontal, i18n( "Colors" ), page ); + layout->addWidget( colorGroupBox ); + colorGroupBox->setMargin( KDialog::marginHint() ); + + // add the removeColor + label = new QLabel( i18n( "Removed color:" ), colorGroupBox ); + m_removedColorButton = new KColorButton( colorGroupBox ); + label->setBuddy( m_removedColorButton ); + + // add the changeColor + label = new QLabel( i18n( "Changed color:" ), colorGroupBox ); + m_changedColorButton = new KColorButton( colorGroupBox ); + label->setBuddy( m_changedColorButton ); + + // add the addColor + label = new QLabel( i18n( "Added color:" ), colorGroupBox ); + m_addedColorButton = new KColorButton( colorGroupBox ); + label->setBuddy( m_addedColorButton ); + + // add the appliedColor + label = new QLabel( i18n( "Applied color:" ), colorGroupBox ); + m_appliedColorButton = new KColorButton( colorGroupBox ); + label->setBuddy( m_appliedColorButton ); + + // scroll number of lines (snol) + snolGroupBox = new QHGroupBox( i18n( "Mouse Wheel" ), page ); + layout->addWidget( snolGroupBox ); + snolGroupBox->setMargin( KDialog::marginHint() ); + + label = new QLabel( i18n( "Number of lines:" ), snolGroupBox ); + m_snolSpinBox = new QSpinBox( 0, 50, 1, snolGroupBox ); + label->setBuddy( m_snolSpinBox ); + + // Temporarily here for testing... + // number of spaces for a tab character stuff + tabGroupBox = new QHGroupBox( i18n( "Tabs to Spaces" ), page ); + layout->addWidget( tabGroupBox ); + tabGroupBox->setMargin( KDialog::marginHint() ); + + label = new QLabel( i18n( "Number of spaces to convert a tab character to:" ), tabGroupBox ); + m_tabSpinBox = new QSpinBox( 1, 16, 1, tabGroupBox ); + label->setBuddy( m_tabSpinBox ); + + layout->addStretch( 1 ); + page->setMinimumSize( sizeHintForWidget( page ) ); + + addTab( page, i18n( "A&ppearance" ) ); + + page = new QWidget( this ); + layout = new QVBoxLayout( page ); + layout->setSpacing( KDialog::spacingHint() ); + layout->setMargin( KDialog::marginHint() ); + + QHGroupBox* gb = new QHGroupBox( i18n( "Text Font" ), page ); + layout->addWidget( gb ); + gb->setMargin( KDialog::marginHint() ); + + label = new QLabel( i18n( "Font:" ), gb ); + m_fontCombo = new KFontCombo( gb, "fontcombo" ); + label->setBuddy( m_fontCombo ); + + label = new QLabel( i18n( "Size:" ), gb ); + m_fontSizeSpinBox = new QSpinBox( 6, 24, 1, gb, "fontsize" ); + label->setBuddy( m_fontSizeSpinBox ); + + layout->addStretch( 1 ); + page->setMinimumSize( sizeHintForWidget( page ) ); + + addTab( page, i18n( "&Fonts" ) ); +} + +ViewPage::~ViewPage() +{ + +} + +void ViewPage::setSettings( ViewSettings* setts ) +{ + m_settings = setts; + + m_addedColorButton->setColor ( m_settings->m_addColor ); + m_changedColorButton->setColor( m_settings->m_changeColor ); + m_removedColorButton->setColor( m_settings->m_removeColor ); + m_appliedColorButton->setColor( m_settings->m_appliedColor ); + m_snolSpinBox->setValue ( m_settings->m_scrollNoOfLines ); + m_tabSpinBox->setValue ( m_settings->m_tabToNumberOfSpaces ); + + m_fontCombo->setCurrentFont ( m_settings->m_font.family() ); + m_fontSizeSpinBox->setValue ( m_settings->m_font.pointSize() ); +} + +ViewSettings* ViewPage::settings( void ) +{ + return m_settings; +} + +void ViewPage::restore() +{ +} + +void ViewPage::apply() +{ + m_settings->m_addColor = m_addedColorButton->color(); + m_settings->m_changeColor = m_changedColorButton->color(); + m_settings->m_removeColor = m_removedColorButton->color(); + m_settings->m_appliedColor = m_appliedColorButton->color(); + m_settings->m_scrollNoOfLines = m_snolSpinBox->value(); + m_settings->m_tabToNumberOfSpaces = m_tabSpinBox->value(); + + m_settings->m_font = QFont( m_fontCombo->currentFont() ); + m_settings->m_font.setPointSize( m_fontSizeSpinBox->value() ); + + m_settings->saveSettings( kapp->config() ); +} + +void ViewPage::setDefaults() +{ + m_addedColorButton->setColor ( ViewSettings::default_addColor ); + m_changedColorButton->setColor( ViewSettings::default_changeColor ); + m_removedColorButton->setColor( ViewSettings::default_removeColor ); + m_appliedColorButton->setColor( ViewSettings::default_appliedColor ); + m_snolSpinBox->setValue ( 3 ); + m_tabSpinBox->setValue ( 4 ); + + m_fontCombo->setCurrentFont ( KGlobalSettings::fixedFont().family() ); + m_fontSizeSpinBox->setValue ( 10 ); +} + +#include "viewpage.moc" diff --git a/kompare/libdialogpages/viewpage.h b/kompare/libdialogpages/viewpage.h new file mode 100644 index 00000000..30591f4b --- /dev/null +++ b/kompare/libdialogpages/viewpage.h @@ -0,0 +1,64 @@ +/*************************************************************************** + generalprefs.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef VIEWPAGE_H +#define VIEWPAGE_H + +#include "pagebase.h" + +class QCheckBox; +class QSpinBox; + +class KColorButton; +class KFontCombo; + +class ViewSettings; + +class ViewPage : public PageBase +{ +Q_OBJECT +public: + ViewPage( QWidget* ); + ~ViewPage(); + +public: + void setSettings( ViewSettings* ); + ViewSettings* settings( void ); + +public: + ViewSettings* m_settings; + +public: + virtual void restore(); + virtual void apply(); + virtual void setDefaults(); + +public: + KColorButton* m_removedColorButton; + KColorButton* m_changedColorButton; + KColorButton* m_addedColorButton; + KColorButton* m_appliedColorButton; + // snol == scroll number of lines + QSpinBox* m_snolSpinBox; + QSpinBox* m_tabSpinBox; + KFontCombo* m_fontCombo; + QSpinBox* m_fontSizeSpinBox; +}; + +#endif diff --git a/kompare/libdialogpages/viewsettings.cpp b/kompare/libdialogpages/viewsettings.cpp new file mode 100644 index 00000000..c55dd40e --- /dev/null +++ b/kompare/libdialogpages/viewsettings.cpp @@ -0,0 +1,101 @@ +/*************************************************************************** + generalsettings.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include + +#include +#include + +#include "viewsettings.h" + +using namespace Diff2; + +const QColor ViewSettings::default_removeColor (190, 237, 190); +const QColor ViewSettings::default_changeColor (237, 190, 190); +const QColor ViewSettings::default_addColor (190, 190, 237); +const QColor ViewSettings::default_appliedColor(237, 237, 190); + +ViewSettings::ViewSettings( QWidget* parent ) + : SettingsBase( parent ), + m_removeColor( 0, 0, 0 ), + m_changeColor( 0, 0, 0), + m_addColor( 0, 0, 0), + m_appliedColor( 0, 0, 0), + m_scrollNoOfLines( 0 ), + m_tabToNumberOfSpaces( 0 ) +{ +} + +ViewSettings::~ViewSettings() +{ +} + +void ViewSettings::loadSettings( KConfig* config ) +{ + KConfigGroup cfg( config, "View Options" ); + m_removeColor = cfg.readColorEntry( "RemoveColor", &default_removeColor ); + m_changeColor = cfg.readColorEntry( "ChangeColor", &default_changeColor ); + m_addColor = cfg.readColorEntry( "AddColor", &default_addColor ); + m_appliedColor = cfg.readColorEntry( "AppliedColor", &default_appliedColor ); + m_scrollNoOfLines = cfg.readNumEntry ( "ScrollNoOfLines", 3 ); + m_tabToNumberOfSpaces = cfg.readNumEntry ( "TabToNumberOfSpaces", 4 ); + + QFont stdFixed = KGlobalSettings::fixedFont(); + stdFixed.setPointSize( 10 ); + m_font = cfg.readFontEntry ( "TextFont", &stdFixed ); +} + +void ViewSettings::saveSettings( KConfig* config ) +{ + KConfigGroup cfg( config, "View Options" ); + cfg.writeEntry( "RemoveColor", m_removeColor ); + cfg.writeEntry( "ChangeColor", m_changeColor ); + cfg.writeEntry( "AddColor", m_addColor ); + cfg.writeEntry( "AppliedColor", m_appliedColor ); + cfg.writeEntry( "ScrollNoOfLines", m_scrollNoOfLines ); + cfg.writeEntry( "TabToNumberOfSpaces", m_tabToNumberOfSpaces ); + + cfg.writeEntry( "TextFont", m_font ); +} + +QColor ViewSettings::colorForDifferenceType( int type, bool selected, bool applied ) +{ + // FIXME: does not belong here + QColor color; + if( applied ) + color = m_appliedColor; + else + { + type = type & 0xFFFFFFEF; // remove the AppliedByBlend + switch( type ) { + case Difference::Unchanged: color = white; break; + case Difference::Change: color = m_changeColor; break; + case Difference::Insert: color = m_addColor; break; + case Difference::Delete: color = m_removeColor; break; + default: break; + } + } + + if( selected ) + color = color.light( 110 ); + + return color; +} + +#include "viewsettings.moc" diff --git a/kompare/libdialogpages/viewsettings.h b/kompare/libdialogpages/viewsettings.h new file mode 100644 index 00000000..ab0f4d5f --- /dev/null +++ b/kompare/libdialogpages/viewsettings.h @@ -0,0 +1,61 @@ +/*************************************************************************** + generalsettings.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef VIEWSETTINGS_H +#define VIEWSETTINGS_H + +#include +#include + +#include "difference.h" +#include "settingsbase.h" + +class ViewSettings : public SettingsBase +{ +Q_OBJECT +public: + static const QColor default_removeColor; + static const QColor default_changeColor; + static const QColor default_addColor; + static const QColor default_appliedColor; + + ViewSettings( QWidget* parent ); + ~ViewSettings(); +public: + // some virtual functions that will be overloaded from the base class + virtual void loadSettings( KConfig* config ); + virtual void saveSettings( KConfig* config ); + QColor colorForDifferenceType( int type, bool selected = false, bool applied = false ); + +public: + QColor m_removeColor; + QColor m_changeColor; + QColor m_addColor; + QColor m_appliedColor; + QColor m_selectedRemoveColor; + QColor m_selectedChangeColor; + QColor m_selectedAddColor; + QColor m_selectedAppliedColor; + int m_scrollNoOfLines; + int m_tabToNumberOfSpaces; + + QFont m_font; +}; + +#endif // VIEWSETTINGS_H diff --git a/kompare/libdiff2/Makefile.am b/kompare/libdiff2/Makefile.am new file mode 100644 index 00000000..6f9048d8 --- /dev/null +++ b/kompare/libdiff2/Makefile.am @@ -0,0 +1,37 @@ +INCLUDES = \ + -I$(top_srcdir)/kompare/libdialogpages \ + -I$(top_srcdir)/kompare/komparepart \ + -I$(top_srcdir)/kompare/interfaces $(all_includes) + +noinst_HEADERS = \ + levenshteintable.h \ + kompare.h \ + kompareprocess.h \ + komparemodellist.h \ + diffmodel.h \ + difference.h \ + diffhunk.h + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +noinst_LTLIBRARIES = libdiff2.la + +# the Part's source, library search path, and link libraries +libdiff2_la_SOURCES = \ + kompareprocess.cpp \ + komparemodellist.cpp \ + diffmodellist.cpp \ + diffmodel.cpp \ + difference.cpp \ + diffhunk.cpp \ + levenshteintable.cpp \ + parser.cpp \ + parserbase.cpp \ + cvsdiffparser.cpp \ + diffparser.cpp \ + perforceparser.cpp + +libdiff2_la_LDFLAGS = $(all_libraries) +libdiff2_la_LIBADD = $(LIB_KFILE) + diff --git a/kompare/libdiff2/cvsdiffparser.cpp b/kompare/libdiff2/cvsdiffparser.cpp new file mode 100644 index 00000000..d210eb66 --- /dev/null +++ b/kompare/libdiff2/cvsdiffparser.cpp @@ -0,0 +1,160 @@ +/************************************************************************** +** cvsdiffparser.cpp +** ----------------- +** begin : Sun Aug 4 15:05:35 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#include + +#include + +#include "cvsdiffparser.h" +#include "komparemodellist.h" + + +using namespace Diff2; + +CVSDiffParser::CVSDiffParser( const KompareModelList* list, const QStringList& diff ) : ParserBase( list, diff ) +{ + // The regexps needed for context cvs diff parsing, the rest is the same as in parserbase.cpp + // third capture in header1 is non optional for cvs diff, it is the revision + m_contextDiffHeader1.setPattern( "\\*\\*\\* ([^\\t]+)\\t([^\\t]+)\\t(.*)\\n" ); + m_contextDiffHeader2.setPattern( "--- ([^\\t]+)\\t([^\\t]+)(|\\t(.*))\\n" ); + + m_normalDiffHeader.setPattern( "Index: (.*)\\n" ); +} + +CVSDiffParser::~CVSDiffParser() +{ +} + +enum Kompare::Format CVSDiffParser::determineFormat() +{ +// kdDebug(8101) << "Determining the format of the CVSDiff" << endl; + + QRegExp normalRE ( "[0-9]+[0-9,]*[acd][0-9]+[0-9,]*" ); + QRegExp unifiedRE( "^--- [^\\t]+\\t" ); + QRegExp contextRE( "^\\*\\*\\* [^\\t]+\\t" ); + QRegExp rcsRE ( "^[acd][0-9]+ [0-9]+" ); + QRegExp edRE ( "^[0-9]+[0-9,]*[acd]" ); + + QStringList::ConstIterator it = m_diffLines.begin(); + + while( it != m_diffLines.end() ) + { + if( (*it).find( normalRE, 0 ) == 0 ) + { +// kdDebug(8101) << "Difflines are from a Normal diff..." << endl; + return Kompare::Normal; + } + else if( (*it).find( unifiedRE, 0 ) == 0 ) + { +// kdDebug(8101) << "Difflines are from a Unified diff..." << endl; + return Kompare::Unified; + } + else if( (*it).find( contextRE, 0 ) == 0 ) + { +// kdDebug(8101) << "Difflines are from a Context diff..." << endl; + return Kompare::Context; + } + else if( (*it).find( rcsRE, 0 ) == 0 ) + { +// kdDebug(8101) << "Difflines are from a RCS diff..." << endl; + return Kompare::RCS; + } + else if( (*it).find( edRE, 0 ) == 0 ) + { +// kdDebug(8101) << "Difflines are from an ED diff..." << endl; + return Kompare::Ed; + } + ++it; + } +// kdDebug(8101) << "Difflines are from an unknown diff..." << endl; + return Kompare::UnknownFormat; +} + +bool CVSDiffParser::parseNormalDiffHeader() +{ + kdDebug(8101) << "CVSDiffParser::parseNormalDiffHeader()" << endl; + bool result = false; + + QStringList::ConstIterator diffEnd = m_diffLines.end(); + + while ( m_diffIterator != diffEnd ) + { + if ( m_normalDiffHeader.exactMatch( *m_diffIterator ) ) + { + kdDebug(8101) << "Matched length Header = " << m_normalDiffHeader.matchedLength() << endl; + kdDebug(8101) << "Matched string Header = " << m_normalDiffHeader.cap( 0 ) << endl; + + m_currentModel = new DiffModel(); + QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) ); + m_currentModel->setSourceFile ( m_normalDiffHeader.cap( 1 ) ); + m_currentModel->setDestinationFile ( m_normalDiffHeader.cap( 1 ) ); + + result = true; + + ++m_diffIterator; + break; + } + else + { + kdDebug(8101) << "No match for: " << ( *m_diffIterator ) << endl; + } + ++m_diffIterator; + } + + if ( result == false ) + { + // Set this to the first line again and hope it is a single file diff + m_diffIterator = m_diffLines.begin(); + m_currentModel = new DiffModel(); + QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) ); + m_singleFileDiff = true; + } + + return result; +} + + +bool CVSDiffParser::parseEdDiffHeader() +{ + return false; +} + +bool CVSDiffParser::parseRCSDiffHeader() +{ + return false; +} + +bool CVSDiffParser::parseEdHunkHeader() +{ + return false; +} + +bool CVSDiffParser::parseRCSHunkHeader() +{ + return false; +} + +bool CVSDiffParser::parseEdHunkBody() +{ + return false; +} + +bool CVSDiffParser::parseRCSHunkBody() +{ + return false; +} + diff --git a/kompare/libdiff2/cvsdiffparser.h b/kompare/libdiff2/cvsdiffparser.h new file mode 100644 index 00000000..88fef485 --- /dev/null +++ b/kompare/libdiff2/cvsdiffparser.h @@ -0,0 +1,61 @@ +/************************************************************************** +** cvsdiffparser.h +** ----------------- +** begin : Sun Aug 4 15:05:35 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#ifndef _CVSDIFF_PARSER_H +#define _CVSDIFF_PARSER_H + +#include + +#include "parserbase.h" + +namespace Diff2 +{ + +class KompareModelList; + +class CVSDiffParser : public ParserBase +{ +public: + CVSDiffParser( const KompareModelList* list, const QStringList& diff ); + virtual ~CVSDiffParser(); + +protected: + virtual enum Kompare::Format determineFormat(); + +protected: +// virtual bool parseContextDiffHeader(); + virtual bool parseEdDiffHeader(); + virtual bool parseNormalDiffHeader(); + virtual bool parseRCSDiffHeader(); +// virtual bool parseUnifiedDiffHeader(); + +// virtual bool parseContextHunkHeader(); + virtual bool parseEdHunkHeader(); +// virtual bool parseNormalHunkHeader(); + virtual bool parseRCSHunkHeader(); +// virtual bool parseUnifiedHunkHeader(); + +// virtual bool parseContextHunkBody(); + virtual bool parseEdHunkBody(); +// virtual bool parseNormalHunkBody(); + virtual bool parseRCSHunkBody(); +// virtual bool parseUnifiedHunkBody(); +}; + +} // End of namespace Diff2 + +#endif diff --git a/kompare/libdiff2/difference.cpp b/kompare/libdiff2/difference.cpp new file mode 100644 index 00000000..8cbb4093 --- /dev/null +++ b/kompare/libdiff2/difference.cpp @@ -0,0 +1,137 @@ +/*************************************************************************** + difference.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include "difference.h" +#include "levenshteintable.h" + +using namespace Diff2; + +Difference::Difference( int sourceLineNo, int destinationLineNo, int type ) : + m_type( type ), + m_sourceLineNo( sourceLineNo ), + m_destinationLineNo( destinationLineNo ), + m_applied( false ) +{ +} + +Difference::~Difference() +{ +} + +void Difference::addSourceLine( QString line ) +{ + m_sourceLines.append( new DifferenceString( line ) ); +} + +void Difference::addDestinationLine( QString line ) +{ + m_destinationLines.append( new DifferenceString( line ) ); +} + +int Difference::sourceLineCount() const +{ + return m_sourceLines.count(); +} + +int Difference::destinationLineCount() const +{ + return m_destinationLines.count(); +} + +void Difference::apply( bool apply ) +{ + m_applied = apply; +} + +void Difference::determineInlineDifferences() +{ + LevenshteinTable table; + if ( m_type != Difference::Change ) + return; + + // Do nothing for now when the slc != dlc + // One could try to find the closest matching destination string for any + // of the source strings but this is compute intensive + if ( sourceLineCount() != destinationLineCount() ) + return; + + int slc = sourceLineCount(); + + for ( int i = 0; i < slc; ++i ) + { + DifferenceString* sl = sourceLineAt( i ); + DifferenceString* dl = destinationLineAt( i ); + + // FIXME: If the table cant be created dont do the rest + table.createTable( sl, dl ); + + table.createListsOfMarkers(); + } +} + +QString Difference::recreateDifference() const +{ + QString difference; + + // source + DifferenceStringListConstIterator stringIt = m_sourceLines.begin(); + DifferenceStringListConstIterator sEnd = m_sourceLines.end(); + + for ( ; stringIt != sEnd; ++stringIt ) + { + switch ( m_type ) + { + case Change: + case Delete: + difference += "-"; + break; + default: + // Insert but this is not possible in source + // Unchanged will be handled in destination + // since they are the same +// kdDebug( 8101 ) << "Go away, nothing to do for you in source..." << endl; + continue; + } + difference += (*stringIt)->string(); + } + + //destination + stringIt = m_destinationLines.begin(); + sEnd = m_destinationLines.end(); + + for ( ; stringIt != sEnd; ++stringIt ) + { + switch ( m_type ) + { + case Change: + case Insert: + difference += "+"; + break; + case Unchanged: + difference += " "; + break; + default: // Delete but this is not possible in destination +// kdDebug( 8101 ) << "Go away, nothing to do for you in destination..." << endl; + continue; + } + difference += (*stringIt)->string(); + } + + return difference; +} diff --git a/kompare/libdiff2/difference.h b/kompare/libdiff2/difference.h new file mode 100644 index 00000000..91065891 --- /dev/null +++ b/kompare/libdiff2/difference.h @@ -0,0 +1,223 @@ +/*************************************************************************** + difference.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef DIFFERENCE_H +#define DIFFERENCE_H + +#include +#include + +#include + +class QString; + +namespace Diff2 +{ + +class LevenshteinTable; + +class Marker +{ +public: + enum Type { Start = 0, End = 1 }; + +public: + Marker() + { + m_type = Marker::Start; + m_offset = 0; + } + Marker( enum Marker::Type type, unsigned int offset ) + { + m_type = type; + m_offset = offset; + } + ~Marker() {} + +public: + enum Marker::Type type() const { return m_type; } + unsigned int offset() const { return m_offset; } + + void setType ( enum Marker::Type type ) { m_type = type; } + void setOffset( unsigned int offset ) { m_offset = offset; } + +private: + enum Marker::Type m_type; + unsigned int m_offset; +}; + +typedef QValueList MarkerList; +typedef QValueList::iterator MarkerListIterator; +typedef QValueList::const_iterator MarkerListConstIterator; + +class DifferenceString +{ +public: + DifferenceString() + { +// kdDebug(8101) << "DifferenceString::DifferenceString()" << endl; + } + DifferenceString( const QString& string, const MarkerList& markerList = MarkerList() ) : + m_string( string ), + m_markerList( markerList ) + { +// kdDebug(8101) << "DifferenceString::DifferenceString( " << string << ", " << markerList << " )" << endl; + calculateHash(); + } + DifferenceString( const DifferenceString& ds ) : + m_string( ds.m_string ), + m_conflict( ds.m_conflict ), + m_hash( ds.m_hash ), + m_markerList( ds.m_markerList ) + { +// kdDebug(8101) << "DifferenceString::DifferenceString( const DifferenceString& " << ds << " )" << endl; + } + ~DifferenceString() {} + +public: + const QString& string() const + { + return m_string; + } + const QString& conflictString() const + { + return m_conflict; + } + const MarkerList& markerList() + { + return m_markerList; + } + void setString( const QString& string ) + { + m_string = string; + calculateHash(); + } + void setConflictString( const QString& conflict ) + { + m_conflict = conflict; + } + void setMarkerList( const MarkerList& markerList ) + { + m_markerList = markerList; + } + void prepend( Marker* marker ) + { + m_markerList.prepend( marker ); + } + bool operator==( const DifferenceString& ks ) + { + if ( m_hash != ks.m_hash ) + return false; + return m_string == ks.m_string; + } + +protected: + void calculateHash() + { + unsigned short const* str = reinterpret_cast( m_string.unicode() ); + const unsigned int len = m_string.length(); + + m_hash = 1315423911; + + for ( unsigned int i = 0; i < len; i++ ) + { + m_hash ^= ( m_hash << 5 ) + str[i] + ( m_hash >> 2 ); + } + } + +private: + QString m_string; + QString m_conflict; + unsigned int m_hash; + MarkerList m_markerList; +}; + +typedef QValueVector DifferenceStringList; +typedef QValueVector::iterator DifferenceStringListIterator; +typedef QValueVector::const_iterator DifferenceStringListConstIterator; + +class Difference +{ +public: + enum Type { Change, Insert, Delete, Unchanged }; + +public: + Difference( int sourceLineNo, int destinationLineNo, int type = Difference::Unchanged ); + ~Difference(); + +public: + int type() const { return m_type; }; + + int sourceLineNumber() const { return m_sourceLineNo; } + int destinationLineNumber() const { return m_destinationLineNo; } + + int sourceLineCount() const; + int destinationLineCount() const; + + DifferenceString* sourceLineAt( int i ) { return m_sourceLines[ i ]; } + DifferenceString* destinationLineAt( int i ) { return m_destinationLines[ i ]; } + + const DifferenceStringList sourceLines() const { return m_sourceLines; } + const DifferenceStringList destinationLines() const { return m_destinationLines; } + + bool hasConflict() const + { + return m_conflicts; + } + void setConflict( bool conflicts ) + { + m_conflicts = conflicts; + } + + void apply( bool apply ); + bool applied() const { return m_applied; } + + void setType( int type ) { m_type = type; } + + void addSourceLine( QString line ); + void addDestinationLine( QString line ); + + /** This method will calculate the differences between the individual strings and store them as Markers */ + void determineInlineDifferences(); + + QString recreateDifference() const; + +private: + int m_type; + + int m_sourceLineNo; + int m_destinationLineNo; + + DifferenceStringList m_sourceLines; + DifferenceStringList m_destinationLines; + + bool m_applied; + bool m_conflicts; + + LevenshteinTable* m_tableXXX; // now unused +}; + +typedef QValueList DifferenceList; +typedef QValueList::iterator DifferenceListIterator; +typedef QValueList::const_iterator DifferenceListConstIterator; + +} // End of namespace Diff2 + +#endif + diff --git a/kompare/libdiff2/diffhunk.cpp b/kompare/libdiff2/diffhunk.cpp new file mode 100644 index 00000000..f980dd93 --- /dev/null +++ b/kompare/libdiff2/diffhunk.cpp @@ -0,0 +1,115 @@ +/*************************************************************************** + diffhunk.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + + +#include "difference.h" +#include "diffhunk.h" + +using namespace Diff2; + +DiffHunk::DiffHunk( int sourceLine, int destinationLine, QString function, Type type ) : + m_sourceLine( sourceLine ), + m_destinationLine( destinationLine ), + m_function( function ), + m_type( type ) +{ +} + +DiffHunk::~DiffHunk() +{ +} + +void DiffHunk::add( Difference* diff ) +{ + m_differences.append( diff ); +} + +int DiffHunk::sourceLineCount() const +{ + DifferenceListConstIterator diffIt = m_differences.begin(); + DifferenceListConstIterator dEnd = m_differences.end(); + + int lineCount = 0; + + for ( ; diffIt != dEnd; ++diffIt ) + lineCount += (*diffIt)->sourceLineCount(); + + return lineCount; +} + +int DiffHunk::destinationLineCount() const +{ + DifferenceListConstIterator diffIt = m_differences.begin(); + DifferenceListConstIterator dEnd = m_differences.end(); + + int lineCount = 0; + + for ( ; diffIt != dEnd; ++diffIt ) + lineCount += (*diffIt)->destinationLineCount(); + + return lineCount; +} + +QString DiffHunk::recreateHunk() const +{ + QString hunk; + QString differences; + + // recreate body + DifferenceListConstIterator diffIt = m_differences.begin(); + DifferenceListConstIterator dEnd = m_differences.end(); + + int slc = 0; // source line count + int dlc = 0; // destination line count + for ( ; diffIt != dEnd; ++diffIt ) + { + switch ( (*diffIt)->type() ) + { + case Difference::Unchanged: + case Difference::Change: + slc += (*diffIt)->sourceLineCount(); + dlc += (*diffIt)->destinationLineCount(); + break; + case Difference::Insert: + dlc += (*diffIt)->destinationLineCount(); + break; + case Difference::Delete: + slc += (*diffIt)->sourceLineCount(); + break; + } + differences += (*diffIt)->recreateDifference(); + } + + // recreate header + hunk += QString::fromLatin1( "@@ -%1,%3 +%2,%4 @@" ) + .arg( m_sourceLine ) + .arg( m_destinationLine ) + .arg( slc ) + .arg( dlc ); + + if ( !m_function.isEmpty() ) + hunk += " " + m_function; + + hunk += QString::fromLatin1( "\n" ); + + hunk += differences; + + kdDebug( 8101 ) << hunk << endl; + return hunk; +} diff --git a/kompare/libdiff2/diffhunk.h b/kompare/libdiff2/diffhunk.h new file mode 100644 index 00000000..8a76babb --- /dev/null +++ b/kompare/libdiff2/diffhunk.h @@ -0,0 +1,69 @@ +/*************************************************************************** + diffhunk.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef DIFFHUNK_H +#define DIFFHUNK_H + +#include "difference.h" + +namespace Diff2 +{ + +class Difference; + +class DiffHunk +{ +public: + enum Type { Normal, AddedByBlend }; + +public: + DiffHunk( int sourceLine, int destinationLine, QString function = QString::null, Type type = Normal ); + ~DiffHunk(); + + const DifferenceList& differences() const { return m_differences; }; + const QString& function() const { return m_function; }; + + int sourceLineNumber() const { return m_sourceLine; }; + int destinationLineNumber() const { return m_destinationLine; }; + + int sourceLineCount() const; + int destinationLineCount() const; + + const Type type() const { return m_type; } + void setType( Type type ) { m_type = type; } + + void add( Difference* diff ); + + QString recreateHunk() const; + +private: + int m_sourceLine; + int m_destinationLine; + DifferenceList m_differences; + QString m_function; + Type m_type; +}; + +typedef QValueList DiffHunkList; +typedef QValueList::iterator DiffHunkListIterator; +typedef QValueList::const_iterator DiffHunkListConstIterator; + +} // End of namespace Diff2 + +#endif diff --git a/kompare/libdiff2/diffmodel.cpp b/kompare/libdiff2/diffmodel.cpp new file mode 100644 index 00000000..54c33457 --- /dev/null +++ b/kompare/libdiff2/diffmodel.cpp @@ -0,0 +1,409 @@ +/*************************************************************************** + diffmodel.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include +#include + +#include +#include + +#include "difference.h" +#include "diffhunk.h" +#include "diffmodel.h" + +using namespace Diff2; + +/** */ +DiffModel::DiffModel( const QString& source, const QString& destination ) : + m_source( source ), + m_destination( destination ), + m_sourcePath( "" ), + m_destinationPath( "" ), + m_sourceFile( "" ), + m_destinationFile( "" ), + m_sourceTimestamp( "" ), + m_destinationTimestamp( "" ), + m_sourceRevision( "" ), + m_destinationRevision( "" ), + m_appliedCount( 0 ), + m_modified( false ), + m_diffIndex( 0 ), + m_selectedDifference( 0 ), + m_blended( false ) +{ + splitSourceInPathAndFileName(); + splitDestinationInPathAndFileName(); +} + +DiffModel::DiffModel() : + m_source( "" ), + m_destination( "" ), + m_sourcePath( "" ), + m_destinationPath( "" ), + m_sourceFile( "" ), + m_destinationFile( "" ), + m_sourceTimestamp( "" ), + m_destinationTimestamp( "" ), + m_sourceRevision( "" ), + m_destinationRevision( "" ), + m_appliedCount( 0 ), + m_modified( false ), + m_diffIndex( 0 ), + m_selectedDifference( 0 ), + m_blended( false ) +{ +} + +/** */ +DiffModel::~DiffModel() +{ +} + +void DiffModel::splitSourceInPathAndFileName() +{ + int pos; + + if( ( pos = m_source.findRev( "/" ) ) >= 0 ) + m_sourcePath = m_source.mid( 0, pos+1 ); + + if( ( pos = m_source.findRev( "/" ) ) >= 0 ) + m_sourceFile = m_source.mid( pos+1, m_source.length() - pos ); + else + m_sourceFile = m_source; + + kdDebug(8101) << m_source << " was split into " << m_sourcePath << " and " << m_sourceFile << endl; +} + +void DiffModel::splitDestinationInPathAndFileName() +{ + int pos; + + if( ( pos = m_destination.findRev( "/" ) )>= 0 ) + m_destinationPath = m_destination.mid( 0, pos+1 ); + + if( ( pos = m_destination.findRev( "/" ) ) >= 0 ) + m_destinationFile = m_destination.mid( pos+1, m_destination.length() - pos ); + else + m_destinationFile = m_source; + + kdDebug(8101) << m_destination << " was split into " << m_destinationPath << " and " << m_destinationFile << endl; +} + +DiffModel& DiffModel::operator=( const DiffModel& model ) +{ + if ( &model != this ) // Guard from self-assignment + { + m_source = model.m_source; + m_destination = model.m_destination; + m_sourcePath = model.m_sourcePath; + m_sourceFile = model.m_sourceFile; + m_sourceTimestamp = model.m_sourceTimestamp; + m_sourceRevision = model.m_sourceRevision; + m_destinationPath = model.m_destinationPath; + m_destinationFile = model.m_destinationFile; + m_destinationTimestamp = model.m_destinationTimestamp; + m_destinationRevision = model.m_destinationRevision; + m_appliedCount = model.m_appliedCount; + m_modified = model.m_modified; + + m_diffIndex = model.m_diffIndex; + m_selectedDifference = model.m_selectedDifference; + } + + return *this; +} + +bool DiffModel::operator<( const DiffModel& model ) +{ + if ( localeAwareCompareSource( model ) < 0 ) + return true; + return false; +} + +int DiffModel::localeAwareCompareSource( const DiffModel& model ) +{ + int result = m_sourcePath.localeAwareCompare( model.m_sourcePath ); + + if ( result == 0 ) + return m_sourceFile.localeAwareCompare( model.m_sourceFile ); + + return result; +} + +QString DiffModel::recreateDiff() const +{ + // For now we'll always return a diff in the diff format + QString diff; + + // recreate header + QString tab = QString::fromLatin1( "\t" ); + QString nl = QString::fromLatin1( "\n" ); + diff += QString::fromLatin1( "--- %1\t%2" ).arg( m_source ).arg( m_sourceTimestamp ); + if ( !m_sourceRevision.isEmpty() ) + diff += tab + m_sourceRevision; + diff += nl; + diff += QString::fromLatin1( "+++ %1\t%2" ).arg( m_destination ).arg( m_destinationTimestamp ); + if ( !m_destinationRevision.isEmpty() ) + diff += tab + m_destinationRevision; + diff += nl; + + // recreate body by iterating over the hunks + DiffHunkListConstIterator hunkIt = m_hunks.begin(); + DiffHunkListConstIterator hEnd = m_hunks.end(); + + for ( ; hunkIt != hEnd; ++hunkIt ) + { + if ((*hunkIt)->type() != DiffHunk::AddedByBlend) + diff += (*hunkIt)->recreateHunk(); + } + + return diff; +} + +DifferenceList* DiffModel::allDifferences() +{ + if ( m_hunks.count() != 0 ) + { + DiffHunkListConstIterator hunkIt = m_hunks.begin(); + DiffHunkListConstIterator hEnd = m_hunks.end(); + + for ( ; hunkIt != hEnd; ++hunkIt ) + { + DiffHunk* hunk = *hunkIt; + + DifferenceListConstIterator diffIt = hunk->differences().begin(); + DifferenceListConstIterator dEnd = hunk->differences().end(); + + for ( ; diffIt != dEnd; ++diffIt ) + { + m_allDifferences.append( *diffIt ); + } + } + return &m_allDifferences; + } + else + { + DifferenceList *diffList = new DifferenceList; + return diffList; + } +} + +Difference* DiffModel::firstDifference() +{ + kdDebug( 8101 ) << "DiffModel::firstDifference()" << endl; + m_diffIndex = 0; + kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl; + + m_selectedDifference = m_differences[ m_diffIndex ]; + + return m_selectedDifference; +} + +Difference* DiffModel::lastDifference() +{ + kdDebug( 8101 ) << "DiffModel::lastDifference()" << endl; + m_diffIndex = m_differences.count() - 1; + kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl; + + m_selectedDifference = m_differences[ m_diffIndex ]; + + return m_selectedDifference; +} + +Difference* DiffModel::prevDifference() +{ + kdDebug( 8101 ) << "DiffModel::prevDifference()" << endl; + if ( --m_diffIndex < m_differences.count() ) + { + kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl; + m_selectedDifference = m_differences[ m_diffIndex ]; + } + else + { + m_selectedDifference = 0; + m_diffIndex = 0; + kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl; + } + + return m_selectedDifference; +} + +Difference* DiffModel::nextDifference() +{ + kdDebug( 8101 ) << "DiffModel::nextDifference()" << endl; + if ( ++m_diffIndex < m_differences.count() ) + { + kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl; + m_selectedDifference = m_differences[ m_diffIndex ]; + } + else + { + m_selectedDifference = 0; + m_diffIndex = 0; // just for safety... + kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl; + } + + return m_selectedDifference; +} + +const QString DiffModel::sourceFile() const +{ + return m_sourceFile; +} + +const QString DiffModel::destinationFile() const +{ + return m_destinationFile; +} + +const QString DiffModel::sourcePath() const +{ + return m_sourcePath; +} + +const QString DiffModel::destinationPath() const +{ + return m_destinationPath; +} + +void DiffModel::setSourceFile( QString path ) +{ + m_source = path; + splitSourceInPathAndFileName(); +} + +void DiffModel::setDestinationFile( QString path ) +{ + m_destination = path; + splitDestinationInPathAndFileName(); +} + +void DiffModel::setSourceTimestamp( QString timestamp ) +{ + m_sourceTimestamp = timestamp; +} + +void DiffModel::setDestinationTimestamp( QString timestamp ) +{ + m_destinationTimestamp = timestamp; +} + +void DiffModel::setSourceRevision( QString revision ) +{ + m_destinationRevision = revision; +} + +void DiffModel::setDestinationRevision( QString revision ) +{ + m_destinationRevision = revision; +} + +void DiffModel::addHunk( DiffHunk* hunk ) +{ + m_hunks.append( hunk ); +} + +void DiffModel::addDiff( Difference* diff ) +{ + m_differences.append( diff ); +} + +void DiffModel::applyDifference( bool apply ) +{ + if ( apply && !m_selectedDifference->applied() ) + m_appliedCount++; + else if ( !apply && m_selectedDifference->applied() ) + m_appliedCount--; + + bool modified; + + // Not setting the m_modified yet so i can still query the current + // modified status from the slot that is connected to the signal + if ( m_appliedCount == 0 ) + modified = false; + else + modified = true; + + emit setModified( modified ); + + m_modified = modified; + + m_selectedDifference->apply( apply ); +} + +void DiffModel::applyAllDifferences( bool apply ) +{ + bool modified; + + // Not setting the m_modified yet so i can still query the current + // modified status from the slot that is connected to the signal + if ( apply ) + { + m_appliedCount = m_differences.count(); + modified = true; + } + else + { + m_appliedCount = 0; + modified = false; + } + + emit setModified( modified ); + + m_modified = modified; + + DifferenceListIterator diffIt = m_differences.begin(); + DifferenceListIterator dEnd = m_differences.end(); + + for ( ; diffIt != dEnd; ++diffIt ) + { + (*diffIt)->apply( apply ); + } +} + +void DiffModel::slotSetModified( bool modified ) +{ + // Not setting the m_modified yet so i can still query the current + // modified status from the slot that is connected to the signal + emit setModified( modified ); + + m_modified = modified; +} + +bool DiffModel::setSelectedDifference( Difference* diff ) +{ + kdDebug(8101) << "diff = " << diff << endl; + kdDebug(8101) << "m_selectedDifference = " << m_selectedDifference << endl; + + if ( diff != m_selectedDifference ) + { + if ( ( m_differences.findIndex( diff ) ) == -1 ) + return false; + // Dont set m_diffIndex if it cant be found + m_diffIndex = m_differences.findIndex( diff ); + kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl; + m_selectedDifference = diff; + } + + return true; +} + +#include "diffmodel.moc" + +/* vim: set ts=4 sw=4 noet: */ diff --git a/kompare/libdiff2/diffmodel.h b/kompare/libdiff2/diffmodel.h new file mode 100644 index 00000000..11c424b5 --- /dev/null +++ b/kompare/libdiff2/diffmodel.h @@ -0,0 +1,150 @@ +/*************************************************************************** + diffmodel.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef DIFFMODEL_H +#define DIFFMODEL_H + +#include +#include + +#include "diffhunk.h" +#include "kompare.h" + +namespace Diff2 +{ + +class DiffHunk; +class Difference; + +class DiffModel : public QObject +{ +Q_OBJECT +public: + + DiffModel( const QString& srcBaseURL, const QString& destBaseURL ); + DiffModel(); + DiffModel( const DiffModel& ) : QObject() {}; + ~DiffModel(); + + int parseDiff( enum Kompare::Format format, const QStringList& list ); + + QString recreateDiff() const; + + int hunkCount() const { return m_hunks.count(); } + int differenceCount() const { return m_differences.count(); } + int appliedCount() const { return m_appliedCount; } + + DiffHunk* hunkAt( int i ) { return *( m_hunks.at( i ) ); } + const Difference* differenceAt( int i ) { return *( m_differences.at( i ) ); } + + DiffHunkList* hunks() { return &m_hunks; } + const DiffHunkList* hunks() const { return &m_hunks; } + DifferenceList* differences() { return &m_differences; } + const DifferenceList* differences() const { return &m_differences; } + + DifferenceList* allDifferences(); + + int findDifference( Difference* diff ) const { return m_differences.findIndex( diff ); } + + Difference* firstDifference(); + Difference* lastDifference(); + Difference* prevDifference(); + Difference* nextDifference(); + + const QString source() const { return m_source; } + const QString destination() const { return m_destination; } + const QString sourceFile() const; + const QString destinationFile() const; + const QString sourcePath() const; + const QString destinationPath() const; + const QString sourceTimestamp() const { return m_sourceTimestamp; } + const QString destinationTimestamp() const { return m_destinationTimestamp; } + const QString sourceRevision() const { return m_sourceRevision; } + const QString destinationRevision() const { return m_destinationRevision; } + + void setSourceFile( QString path ); + void setDestinationFile( QString path ); + void setSourceTimestamp( QString timestamp ); + void setDestinationTimestamp( QString timestamp ); + void setSourceRevision( QString revision ); + void setDestinationRevision( QString revision ); + + void addHunk( DiffHunk* hunk ); + void addDiff( Difference* diff ); + bool isModified() const { return m_modified; } + + const int diffIndex( void ) const { return m_diffIndex; } + void setDiffIndex( int diffIndex ) { m_diffIndex = diffIndex; } + + void applyDifference( bool apply ); + void applyAllDifferences( bool apply ); + + bool setSelectedDifference( Difference* diff ); + + DiffModel& operator=( const DiffModel& model ); + bool operator<( const DiffModel& model ); + + int localeAwareCompareSource( const DiffModel& model ); + + bool isBlended() const { return m_blended; } + void setBlended( bool blended ) { m_blended = blended; } + +signals: + void setModified( bool modified ); + +public slots: + void slotSetModified( bool modified ); + +private: + void splitSourceInPathAndFileName(); + void splitDestinationInPathAndFileName(); + +private: + QString m_source; + QString m_destination; + + QString m_sourcePath; + QString m_destinationPath; + + QString m_sourceFile; + QString m_destinationFile; + + QString m_sourceTimestamp; + QString m_destinationTimestamp; + + QString m_sourceRevision; + QString m_destinationRevision; + + DiffHunkList m_hunks; + DifferenceList m_differences; + DifferenceList m_allDifferences; + + int m_appliedCount; + bool m_modified; + + unsigned int m_diffIndex; + Difference* m_selectedDifference; + + bool m_blended; +}; + +} // End of namespace Diff2 + +#endif + diff --git a/kompare/libdiff2/diffmodellist.cpp b/kompare/libdiff2/diffmodellist.cpp new file mode 100644 index 00000000..200e8108 --- /dev/null +++ b/kompare/libdiff2/diffmodellist.cpp @@ -0,0 +1,29 @@ +/******************************************************************************* +** +** Filename : diffmodellist.cpp +** Created on : 26 march, 2004 +** Copyright : (c) 2004 Otto Bruggeman +** Email : bruggie@home.nl +** +*******************************************************************************/ + +/******************************************************************************* +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +*******************************************************************************/ + +#include + +#include "diffmodellist.h" + +using namespace Diff2; + +void DiffModelList::sort() +{ + qHeapSort(*this); +} + diff --git a/kompare/libdiff2/diffmodellist.h b/kompare/libdiff2/diffmodellist.h new file mode 100644 index 00000000..9c4f9807 --- /dev/null +++ b/kompare/libdiff2/diffmodellist.h @@ -0,0 +1,49 @@ +/******************************************************************************* +** +** Filename : diffmodellist.h +** Created on : 24 januari, 2004 +** Copyright : (c) 2004 Otto Bruggeman +** Email : bruggie@home.nl +** +*******************************************************************************/ + +/******************************************************************************* +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +*******************************************************************************/ + +#ifndef DIFFMODELLIST_H +#define DIFFMODELLIST_H + +#include // include for the base class + +#include "diffmodel.h" + +namespace Diff2 +{ + +typedef QValueListIterator DiffModelListIterator; +typedef QValueListConstIterator DiffModelListConstIterator; + +class DiffModelList : public QValueList +{ +public: + DiffModelList() {} + DiffModelList( const DiffModelList &list ) : QValueList( list ) {} + virtual ~DiffModelList() + { + clear(); + } + +public: + virtual void sort(); + +}; // End of class DiffModelList + +} // End of Namespace Diff2 + +#endif // DIFFMODELLIST_H diff --git a/kompare/libdiff2/diffparser.cpp b/kompare/libdiff2/diffparser.cpp new file mode 100644 index 00000000..f98fbde5 --- /dev/null +++ b/kompare/libdiff2/diffparser.cpp @@ -0,0 +1,81 @@ +/************************************************************************** +** diffparser.cpp +** -------------- +** begin : Sun Aug 4 15:05:35 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#include + +#include + +#include "diffparser.h" + +using namespace Diff2; + +DiffParser::DiffParser( const KompareModelList* list, const QStringList& diff ) : ParserBase( list, diff ) +{ + // The regexps needed for context diff parsing, the rest is the same as in parserbase.cpp + m_contextDiffHeader1.setPattern( "\\*\\*\\* ([^\\t]+)\\t([^\\t]+)\\n" ); + m_contextDiffHeader2.setPattern( "--- ([^\\t]+)\\t([^\\t]+)\\n" ); +} + +DiffParser::~DiffParser() +{ +} + +enum Kompare::Format DiffParser::determineFormat() +{ + kdDebug(8101) << "Determining the format of the diff Diff" << endl; + + QRegExp normalRE ( "[0-9]+[0-9,]*[acd][0-9]+[0-9,]*" ); + QRegExp unifiedRE( "^--- " ); + QRegExp contextRE( "^\\*\\*\\* " ); + QRegExp rcsRE ( "^[acd][0-9]+ [0-9]+" ); + QRegExp edRE ( "^[0-9]+[0-9,]*[acd]" ); + + QStringList::ConstIterator it = m_diffLines.begin(); + + while( it != m_diffLines.end() ) + { + kdDebug(8101) << (*it) << endl; + if( (*it).find( normalRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from a Normal diff..." << endl; + return Kompare::Normal; + } + else if( (*it).find( unifiedRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from a Unified diff..." << endl; + return Kompare::Unified; + } + else if( (*it).find( contextRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from a Context diff..." << endl; + return Kompare::Context; + } + else if( (*it).find( rcsRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from an RCS diff..." << endl; + return Kompare::RCS; + } + else if( (*it).find( edRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from an ED diff..." << endl; + return Kompare::Ed; + } + ++it; + } + kdDebug(8101) << "Difflines are from an unknown diff..." << endl; + return Kompare::UnknownFormat; +} diff --git a/kompare/libdiff2/diffparser.h b/kompare/libdiff2/diffparser.h new file mode 100644 index 00000000..72905e3f --- /dev/null +++ b/kompare/libdiff2/diffparser.h @@ -0,0 +1,38 @@ +/************************************************************************** +** diffparser.h +** ----------------- +** begin : Sun Aug 4 15:05:35 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#ifndef _DIFF_PARSER_H +#define _DIFF_PARSER_H + +#include "parserbase.h" + +namespace Diff2 +{ + +class DiffParser : public ParserBase +{ +public: + DiffParser( const KompareModelList* list, const QStringList& diff ); + virtual ~DiffParser(); + +protected: + virtual enum Kompare::Format determineFormat(); +}; + +} // End of namespace Diff2 + +#endif diff --git a/kompare/libdiff2/kompare.h b/kompare/libdiff2/kompare.h new file mode 100644 index 00000000..1ed5c4c7 --- /dev/null +++ b/kompare/libdiff2/kompare.h @@ -0,0 +1,144 @@ +/*************************************************************************** + kompare.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef KOMPARE_H +#define KOMPARE_H + +#include + +namespace Kompare +{ + enum Format { + Context = 0, + Ed = 1, + Normal = 2, + RCS = 3, + Unified = 4, + SideBySide = 5, + UnknownFormat = -1 + }; + + enum Generator { + CVSDiff = 0, + Diff = 1, + Perforce = 2, + SubVersion = 3, + Reserved2 = 4, + Reserved3 = 5, + Reserved4 = 6, + Reserved5 = 7, + Reserved6 = 8, + Reserved7 = 9, + UnknownGenerator = -1 + }; + + enum Mode { + ComparingFiles, // compareFiles + ComparingDirs, // compareDirs + ShowingDiff, // openDiff + BlendingDir, // openDirAnfDiff + BlendingFile, // openFileAndDiff + UnknownMode // Used to initialize the Infoi struct + }; + + enum DiffMode { + Default, + Custom, + UnknownDiffMode // Use to initialize the Info struct + }; + + enum Status { + RunningDiff, + Parsing, + FinishedParsing, + FinishedWritingDiff, + ReRunningDiff // When a change has been detected after diff has run + }; + + enum Target { + Source, + Destination + }; + + struct Info { + Info ( + enum Mode _mode = UnknownMode, + enum DiffMode _diffMode = UnknownDiffMode, + enum Format _format = UnknownFormat, + enum Generator _generator = UnknownGenerator, + KURL _source = KURL(), + KURL _destination = KURL(), + QString _localSource = "", + QString _localDestination = "" + ) + { + mode = _mode; + diffMode = _diffMode; + format = _format; + generator = _generator; + source = _source; + destination = _destination; + localSource = _localSource; + localDestination = _localDestination; + } + enum Mode mode; + enum DiffMode diffMode; + enum Format format; + enum Generator generator; + KURL source; + KURL destination; + QString localSource; + QString localDestination; + }; +} // End of namespace Kompare + +/* +** This should be removed and put somewhere else +*/ +class KompareFunctions +{ +public: + static QString constructRelativePath( const QString& from, const QString& to ) + { + KURL fromURL( from ); + KURL toURL( to ); + KURL root; + int upLevels = 0; + + // Find a common root. + root = from; + while( root.isValid() && !root.isParentOf( toURL ) ) { + root = root.upURL(); + upLevels++; + } + + if( !root.isValid() ) return to; + + QString relative; + for( ; upLevels > 0; upLevels-- ) { + relative += "../"; + } + + relative += QString( to ).replace( 0, root.path(1).length(), "" ); + + return relative; + } +}; + +#endif diff --git a/kompare/libdiff2/komparemodellist.cpp b/kompare/libdiff2/komparemodellist.cpp new file mode 100644 index 00000000..ac3c725a --- /dev/null +++ b/kompare/libdiff2/komparemodellist.cpp @@ -0,0 +1,1423 @@ +/*************************************************************************** + komparemodellist.cpp - description + ------------------- + begin : Tue Jun 26 2001 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + (C) 2007 Kevin Kofler + email : jfirebaugh@kde.org + otto.bruggeman@home.nl + kevin.kofler@chello.at +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "difference.h" +#include "diffhunk.h" +#include "diffmodel.h" +#include "diffmodellist.h" +#include "kompareprocess.h" +#include "komparemodellist.h" +#include "parser.h" + +#include "kompare_part.h" + +using namespace Diff2; + +KompareModelList::KompareModelList( DiffSettings* diffSettings, struct Kompare::Info& info, QObject* parent, const char* name ) + : QObject( parent, name ), + m_diffProcess( 0 ), + m_diffSettings( diffSettings ), + m_models( 0 ), + m_selectedModel( 0 ), + m_selectedDifference( 0 ), + m_noOfModified( 0 ), + m_modelIndex( 0 ), + m_info( info ), + m_textCodec( 0 ) +{ + m_applyDifference = new KAction( i18n("&Apply Difference"), "1rightarrow", Qt::Key_Space, + this, SLOT(slotActionApplyDifference()), + (( KomparePart* )parent)->actionCollection(), "difference_apply" ); + m_unApplyDifference = new KAction( i18n("Un&apply Difference"), "1leftarrow", Qt::Key_BackSpace, + this, SLOT(slotActionUnApplyDifference()), + (( KomparePart* )parent)->actionCollection(), "difference_unapply" ); + m_applyAll = new KAction( i18n("App&ly All"), "2rightarrow", Qt::CTRL + Qt::Key_A, + this, SLOT(slotActionApplyAllDifferences()), + (( KomparePart* )parent)->actionCollection(), "difference_applyall" ); + m_unapplyAll = new KAction( i18n("&Unapply All"), "2leftarrow", Qt::CTRL + Qt::Key_U, + this, SLOT(slotActionUnapplyAllDifferences()), + (( KomparePart* )parent)->actionCollection(), "difference_unapplyall" ); + m_previousFile = new KAction( i18n("P&revious File"), "2uparrow", Qt::CTRL + Qt::Key_PageUp, + this, SLOT(slotPreviousModel()), + (( KomparePart* )parent)->actionCollection(), "difference_previousfile" ); + m_nextFile = new KAction( i18n("N&ext File"), "2downarrow", Qt::CTRL + Qt::Key_PageDown, + this, SLOT(slotNextModel()), + (( KomparePart* )parent)->actionCollection(), "difference_nextfile" ); + m_previousDifference = new KAction( i18n("&Previous Difference"), "1uparrow", Qt::CTRL + Qt::Key_Up, + this, SLOT(slotPreviousDifference()), + (( KomparePart* )parent)->actionCollection(), "difference_previous" ); + m_nextDifference = new KAction( i18n("&Next Difference"), "1downarrow", Qt::CTRL + Qt::Key_Down, + this, SLOT(slotNextDifference()), + (( KomparePart* )parent)->actionCollection(), "difference_next" ); + m_previousDifference->setEnabled( false ); + m_nextDifference->setEnabled( false ); + + m_save = KStdAction::save( this, SLOT(slotSaveDestination()), ((KomparePart*)parent)->actionCollection() ); + m_save->setEnabled( false ); + + updateModelListActions(); +} + +KompareModelList::~KompareModelList() +{ +} + +bool KompareModelList::isDirectory( const QString& url ) const +{ + QFileInfo fi( url ); + if ( fi.isDir() ) + return true; + else + return false; +} + +bool KompareModelList::isDiff( const QString& mimeType ) const +{ + if ( mimeType == "text/x-diff" ) + return true; + else + return false; +} + +bool KompareModelList::compare( const QString& source, const QString& destination ) +{ + bool result = false; + + bool sourceIsDirectory = isDirectory( source ); + bool destinationIsDirectory = isDirectory( source ); + + if ( sourceIsDirectory && destinationIsDirectory ) + { + m_info.mode = Kompare::ComparingDirs; + result = compareDirs( source, destination ); + } + else if ( !sourceIsDirectory && !destinationIsDirectory ) + { + QFile sourceFile( source ); + sourceFile.open( IO_ReadOnly ); + QString sourceMimeType = ( KMimeType::findByContent( sourceFile.readAll() ) )->name(); + sourceFile.close(); + kdDebug(8101) << "Mimetype source : " << sourceMimeType << endl; + + QFile destinationFile( destination ); + destinationFile.open( IO_ReadOnly ); + QString destinationMimeType = ( KMimeType::findByContent( destinationFile.readAll() ) )->name(); + destinationFile.close(); + kdDebug(8101) << "Mimetype destination: " << destinationMimeType << endl; + + // Not checking if it is a text file/something diff can even compare, we'll let diff handle that + if ( !isDiff( sourceMimeType ) && isDiff( destinationMimeType ) ) + { + kdDebug(8101) << "Blending destination into source..." << endl; + m_info.mode = Kompare::BlendingFile; + result = openFileAndDiff( source, destination ); + } + else if ( isDiff( sourceMimeType ) && !isDiff( destinationMimeType ) ) + { + kdDebug(8101) << "Blending source into destination..." << endl; + m_info.mode = Kompare::BlendingFile; + result = openFileAndDiff( destination, source ); + } + else + { + kdDebug(8101) << "Comparing source with destination" << endl; + m_info.mode = Kompare::ComparingFiles; + result = compareFiles( source, destination ); + } + } + else if ( sourceIsDirectory && !destinationIsDirectory ) + { + m_info.mode = Kompare::BlendingDir; + result = openDirAndDiff( source, destination ); + } + else + { + m_info.mode = Kompare::BlendingDir; + result = openDirAndDiff( destination, source ); + } + + return result; +} + +bool KompareModelList::compareFiles( const QString& source, const QString& destination ) +{ + m_source = source; + m_destination = destination; + + clear(); // Destroy the old models... + +// m_fileWatch = new KDirWatch( this, "filewatch" ); +// m_fileWatch->addFile( m_source ); +// m_fileWatch->addFile( m_destination ); + +// connect( m_fileWatch, SIGNAL( dirty( const QString& ) ), this, SLOT( slotFileChanged( const QString& ) ) ); +// connect( m_fileWatch, SIGNAL( created( const QString& ) ), this, SLOT( slotFileChanged( const QString& ) ) ); +// connect( m_fileWatch, SIGNAL( deleted( const QString& ) ), this, SLOT( slotFileChanged( const QString& ) ) ); + +// m_fileWatch->startScan(); + m_diffProcess = new KompareProcess( m_diffSettings, Kompare::Custom, m_source, m_destination ); + m_diffProcess->setEncoding( m_encoding ); + + connect( m_diffProcess, SIGNAL(diffHasFinished( bool )), + this, SLOT(slotDiffProcessFinished( bool )) ); + + emit status( Kompare::RunningDiff ); + m_diffProcess->start(); + + return true; +} + +bool KompareModelList::compareDirs( const QString& source, const QString& destination ) +{ + m_source = source; + m_destination = destination; + + clear(); // Destroy the old models... + +// m_dirWatch = new KDirWatch( this, "dirwatch" ); + // Watch files in the dirs and watch the dirs recursively +// m_dirWatch->addDir( m_source, true, true ); +// m_dirWatch->addDir( m_destination, true, true ); + +// connect( m_dirWatch, SIGNAL( dirty ( const QString& ) ), this, SLOT( slotDirectoryChanged( const QString& ) ) ); +// connect( m_dirWatch, SIGNAL( created( const QString& ) ), this, SLOT( slotDirectoryChanged( const QString& ) ) ); +// connect( m_dirWatch, SIGNAL( deleted( const QString& ) ), this, SLOT( slotDirectoryChanged( const QString& ) ) ); + +// m_dirWatch->startScan(); + m_diffProcess = new KompareProcess( m_diffSettings, Kompare::Custom, m_source, m_destination ); + m_diffProcess->setEncoding( m_encoding ); + + connect( m_diffProcess, SIGNAL(diffHasFinished( bool )), + this, SLOT(slotDiffProcessFinished( bool )) ); + + emit status( Kompare::RunningDiff ); + m_diffProcess->start(); + + return true; +} + +bool KompareModelList::openFileAndDiff( const QString& file, const QString& diff ) +{ + clear(); + + if ( parseDiffOutput( readFile( diff ) ) != 0 ) + { + emit error( i18n( "No models or no differences, this file: %1, is not a valid diff file." ).arg( diff ) ); + return false; + } + + // Do our thing :) + if ( !blendOriginalIntoModelList( file ) ) + { + kdDebug(8101) << "Oops cant blend original file into modellist : " << file << endl; + emit( i18n( "There were problems applying the diff %1 to the file %2." ).arg( diff ).arg( file ) ); + return false; + } + + updateModelListActions(); + show(); + + return true; +} + +bool KompareModelList::openDirAndDiff( const QString& dir, const QString& diff ) +{ + clear(); + + if ( parseDiffOutput( readFile( diff ) ) != 0 ) + { + emit error( i18n( "No models or no differences, this file: %1, is not a valid diff file." ).arg( diff ) ); + return false; + } + + // Do our thing :) + if ( !blendOriginalIntoModelList( dir ) ) + { + // Trouble blending the original into the model + kdDebug(8101) << "Oops cant blend original dir into modellist : " << dir << endl; + emit error( i18n( "There were problems applying the diff %1 to the folder %2." ).arg( diff ).arg( dir ) ); + return false; + } + + updateModelListActions(); + show(); + + return true; +} + +void KompareModelList::slotSaveDestination() +{ + if ( m_selectedModel ) + { + saveDestination( m_selectedModel ); + } +} + +bool KompareModelList::saveDestination( DiffModel* model ) +{ + kdDebug() << "KompareModelList::saveDestination: " << endl; + + if( !model->isModified() ) + return true; + + KTempFile* temp = new KTempFile(); + + if( temp->status() != 0 ) { + emit error( i18n( "Could not open a temporary file." ) ); + temp->unlink(); + delete temp; + return false; + } + + QTextStream* stream = temp->textStream(); + QStringList list; + + DiffHunkListConstIterator hunkIt = model->hunks()->begin(); + DiffHunkListConstIterator hEnd = model->hunks()->end(); + + for( ; hunkIt != hEnd; ++hunkIt ) + { + DiffHunk* hunk = *hunkIt; + + DifferenceListConstIterator diffIt = hunk->differences().begin(); + DifferenceListConstIterator dEnd = hunk->differences().end(); + + Difference* diff; + for( ; diffIt != dEnd; ++diffIt ) + { + diff = *diffIt; + if( !diff->applied() ) + { + DifferenceStringListConstIterator stringIt = diff->destinationLines().begin(); + DifferenceStringListConstIterator sEnd = diff->destinationLines().end(); + for ( ; stringIt != sEnd; ++stringIt ) + { + list.append( ( *stringIt )->string() ); + } + } + else + { + DifferenceStringListConstIterator stringIt = diff->sourceLines().begin(); + DifferenceStringListConstIterator sEnd = diff->sourceLines().end(); + for ( ; stringIt != sEnd; ++stringIt ) + { + list.append( ( *stringIt )->string() ); + } + } + } + } + + // kdDebug( 8101 ) << "Everything: " << endl << list.join( "\n" ) << endl; + + if( list.count() > 0 ) + *stream << list.join( "" ); + + temp->close(); + if( temp->status() != 0 ) { + emit error( i18n( "Could not write to the temporary file %1, deleting it." ).arg( temp->name() ) ); + temp->unlink(); + delete temp; + return false; + } + + bool result = false; + + if ( m_info.mode == Kompare::ComparingDirs ) + { + QString destination = model->destinationPath() + model->destinationFile(); + kdDebug(8101) << "Tempfilename : " << temp->name() << endl; + kdDebug(8101) << "DestinationURL : " << destination << endl; + KIO::UDSEntry entry; + if ( !KIO::NetAccess::stat( KURL( destination ).path(), entry, (QWidget*)parent() ) ) + { + if ( !KIO::NetAccess::mkdir( KURL( destination ).path(), (QWidget*)parent() ) ) + { + emit error( i18n( "Could not create destination directory %1.\nThe file has not been saved." ) ); + return false; + } + } + result = KIO::NetAccess::upload( temp->name(), KURL( destination ), (QWidget*)parent() ); + } + else + { + kdDebug(8101) << "Tempfilename : " << temp->name() << endl; + kdDebug(8101) << "DestinationURL : " << m_destination << endl; + result = KIO::NetAccess::upload( temp->name(), KURL( m_destination ), (QWidget*)parent() ); + } + + if ( !result ) + { + emit error( i18n( "Could not upload the temporary file to the destination location %1. The temporary file is still available under: %2. You can manually copy it to the right place." ).arg( m_destination ).arg( temp->name() ) ); + } + else + { + //model->slotSetModified( false ); + temp->unlink(); + delete temp; + } + + return true; +} + +bool KompareModelList::saveAll() +{ + if ( !m_models ) + return false; + + DiffModelListIterator it = m_models->begin(); + DiffModelListIterator end = m_models->end(); + for ( ; it != end; ++it ) + { + if( !saveDestination( *it ) ) + return false; + } + return true; +} + +void KompareModelList::setEncoding( const QString& encoding ) +{ + m_encoding = encoding; + if ( encoding.lower() == "default" ) + { + m_textCodec = QTextCodec::codecForLocale(); + } + else + { + kdDebug() << "Encoding : " << encoding << endl; + m_textCodec = KGlobal::charsets()->codecForName( encoding.latin1() ); + kdDebug() << "TextCodec: " << m_textCodec << endl; + if ( !m_textCodec ) + m_textCodec = QTextCodec::codecForLocale(); + } + kdDebug() << "TextCodec: " << m_textCodec << endl; +} + +void KompareModelList::slotDiffProcessFinished( bool success ) +{ + if ( success ) + { + emit status( Kompare::Parsing ); + if ( parseDiffOutput( m_diffProcess->diffOutput() ) != 0 ) + { + emit error( i18n( "Could not parse diff output." ) ); + } + else + { + if ( m_info.mode != Kompare::ShowingDiff ) + { + kdDebug() << "Blend this crap please and dont gimme any conflicts..." << endl; + blendOriginalIntoModelList( m_info.localSource ); + } + updateModelListActions(); + show(); + } + emit status( Kompare::FinishedParsing ); + } + else if ( m_diffProcess->exitStatus() == 0 ) + { + emit error( i18n( "The files are identical." ) ); + } + else + { + emit error( m_diffProcess->stdErr() ); + } + + delete m_diffProcess; + m_diffProcess = 0; +} + +void KompareModelList::slotDirectoryChanged( const QString& /*dir*/ ) +{ + // some debug output to see if watching works properly + kdDebug(8101) << "Yippie directories are being watched !!! :)" << endl; + if ( m_diffProcess ) + { + emit status( Kompare::ReRunningDiff ); + m_diffProcess->start(); + } +} + +void KompareModelList::slotFileChanged( const QString& /*file*/ ) +{ + // some debug output to see if watching works properly + kdDebug(8101) << "Yippie files are being watched !!! :)" << endl; + if ( m_diffProcess ) + { + emit status( Kompare::ReRunningDiff ); + m_diffProcess->start(); + } +} + +QStringList KompareModelList::split( const QString& fileContents ) +{ + QString contents = fileContents; + QStringList list; + + int pos = 0; + unsigned int oldpos = 0; + // split that does not strip the split char +#ifdef QT_OS_MAC + const char split = '\r'; +#else + const char split = '\n'; +#endif + while ( ( pos = contents.find( split, oldpos ) ) >= 0 ) + { + list.append( contents.mid( oldpos, pos - oldpos + 1 ) ); + oldpos = pos + 1; + } + + if ( contents.length() > oldpos ) + { + list.append( contents.right( contents.length() - oldpos ) ); + } + + return list; +} + +QString KompareModelList::readFile( const QString& fileName ) +{ + QStringList list; + + QFile file( fileName ); + file.open( IO_ReadOnly ); + + QTextStream stream( &file ); + kdDebug() << "Codec = " << m_textCodec << endl; + + if ( !m_textCodec ) + m_textCodec = QTextCodec::codecForLocale(); + + stream.setCodec( m_textCodec ); + + QString contents = stream.read(); + + file.close(); + + return contents; +} + +bool KompareModelList::openDiff( const QString& diffFile ) +{ + kdDebug(8101) << "Stupid :) Url = " << diffFile << endl; + + if ( diffFile.isEmpty() ) + return false; + + QString diff = readFile( diffFile ); + + clear(); // Clear the current models + + emit status( Kompare::Parsing ); + + if ( parseDiffOutput( diff ) != 0 ) + { + emit error( i18n( "Could not parse diff output." ) ); + return false; + } + + updateModelListActions(); + show(); + + emit status( Kompare::FinishedParsing ); + + return true; +} + +QString KompareModelList::recreateDiff() const +{ + QString diff; + + DiffModelListConstIterator modelIt = m_models->begin(); + DiffModelListConstIterator mEnd = m_models->end(); + + for ( ; modelIt != mEnd; ++modelIt ) + { + diff += (*modelIt)->recreateDiff(); + } + return diff; +} + +bool KompareModelList::saveDiff( const QString& url, QString directory, DiffSettings* diffSettings ) +{ + kdDebug() << "KompareModelList::saveDiff: " << endl; + + m_diffTemp = new KTempFile(); + m_diffURL = url; + + if( m_diffTemp->status() != 0 ) { + emit error( i18n( "Could not open a temporary file." ) ); + m_diffTemp->unlink(); + delete m_diffTemp; + m_diffTemp = 0; + return false; + } + + m_diffProcess = new KompareProcess( diffSettings, Kompare::Custom, m_source, m_destination, directory ); + m_diffProcess->setEncoding( m_encoding ); + + connect( m_diffProcess, SIGNAL(diffHasFinished( bool )), + this, SLOT(slotWriteDiffOutput( bool )) ); + + emit status( Kompare::RunningDiff ); + return m_diffProcess->start(); +} + +void KompareModelList::slotWriteDiffOutput( bool success ) +{ + kdDebug(8101) << "Success = " << success << endl; + + if( success ) + { + QTextStream* stream = m_diffTemp->textStream(); + + *stream << m_diffProcess->diffOutput(); + + m_diffTemp->close(); + + if( m_diffTemp->status() != 0 ) + { + emit error( i18n( "Could not write to the temporary file." ) ); + } + + KIO::NetAccess::upload( m_diffTemp->name(), KURL( m_diffURL ), (QWidget*)parent() ); + + emit status( Kompare::FinishedWritingDiff ); + } + + m_diffURL.truncate( 0 ); + m_diffTemp->unlink(); + + delete m_diffTemp; + m_diffTemp = 0; + + delete m_diffProcess; + m_diffProcess = 0; +} + +void KompareModelList::slotSelectionChanged( const Diff2::DiffModel* model, const Diff2::Difference* diff ) +{ +// This method will signal all the other objects about a change in selection, +// it will emit setSelection( const DiffModel*, const Difference* ) to all who are connected + kdDebug(8101) << "KompareModelList::slotSelectionChanged( " << model << ", " << diff << " )" << endl; + kdDebug(8101) << "Sender is : " << sender()->className() << endl; +// kdDebug(8101) << kdBacktrace() << endl; + + m_selectedModel = const_cast(model); + m_modelIndex = m_models->findIndex( m_selectedModel ); + kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl; + m_selectedDifference = const_cast(diff); + + m_selectedModel->setSelectedDifference( m_selectedDifference ); + + // setSelected* search for the argument in the lists and return false if not found + // if found they return true and set the m_selected* + if ( !setSelectedModel( m_selectedModel ) ) + { + // Backup plan + m_selectedModel = firstModel(); + m_selectedDifference = m_selectedModel->firstDifference(); + } + else if ( !m_selectedModel->setSelectedDifference( m_selectedDifference ) ) + { + // Another backup plan + m_selectedDifference = m_selectedModel->firstDifference(); + } + + emit setSelection( model, diff ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + + updateModelListActions(); +} + +void KompareModelList::slotSelectionChanged( const Diff2::Difference* diff ) +{ +// This method will emit setSelection( const Difference* ) to whomever is listening +// when for instance in kompareview the selection has changed + kdDebug(8101) << "KompareModelList::slotSelectionChanged( " << diff << " )" << endl; + kdDebug(8101) << "Sender is : " << sender()->className() << endl; + + m_selectedDifference = const_cast(diff); + + if ( !m_selectedModel->setSelectedDifference( m_selectedDifference ) ) + { + // Backup plan + m_selectedDifference = m_selectedModel->firstDifference(); + } + + emit setSelection( diff ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + + updateModelListActions(); +} + +void KompareModelList::slotPreviousModel() +{ + if ( ( m_selectedModel = prevModel() ) != 0 ) + { + m_selectedDifference = m_selectedModel->firstDifference(); + } + else + { + m_selectedModel = firstModel(); + m_selectedDifference = m_selectedModel->firstDifference(); + } + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); +} + +void KompareModelList::slotNextModel() +{ + if ( ( m_selectedModel = nextModel() ) != 0 ) + { + m_selectedDifference = m_selectedModel->firstDifference(); + } + else + { + m_selectedModel = lastModel(); + m_selectedDifference = m_selectedModel->firstDifference(); + } + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); +} + +DiffModel* KompareModelList::firstModel() +{ + kdDebug( 8101 ) << "KompareModelList::firstModel()" << endl; + m_modelIndex = 0; + kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl; + + m_selectedModel = m_models->first(); + + return m_selectedModel; +} + +DiffModel* KompareModelList::lastModel() +{ + kdDebug( 8101 ) << "KompareModelList::lastModel()" << endl; + m_modelIndex = m_models->count() - 1; + kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl; + + m_selectedModel = m_models->last(); + + return m_selectedModel; +} + +DiffModel* KompareModelList::prevModel() +{ + kdDebug( 8101 ) << "KompareModelList::prevModel()" << endl; + if ( --m_modelIndex < m_models->count() ) + { + kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl; + m_selectedModel = (*m_models)[ m_modelIndex ]; + } + else + { + m_selectedModel = 0; + m_modelIndex = 0; + kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl; + } + + return m_selectedModel; +} + +DiffModel* KompareModelList::nextModel() +{ + kdDebug( 8101 ) << "KompareModelList::nextModel()" << endl; + if ( ++m_modelIndex < m_models->count() ) + { + kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl; + m_selectedModel = (*m_models)[ m_modelIndex ]; + } + else + { + m_selectedModel = 0; + m_modelIndex = 0; + kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl; + } + + return m_selectedModel; +} + +void KompareModelList::slotPreviousDifference() +{ + kdDebug(8101) << "slotPreviousDifference called" << endl; + if ( ( m_selectedDifference = m_selectedModel->prevDifference() ) != 0 ) + { + emit setSelection( m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); + return; + } + + kdDebug(8101) << "**** no previous difference... ok lets find the previous model..." << endl; + + if ( ( m_selectedModel = prevModel() ) != 0 ) + { + m_selectedDifference = m_selectedModel->lastDifference(); + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); + return; + } + + + kdDebug(8101) << "**** !!! No previous model, ok backup plan activated..." << endl; + + // Backup plan + m_selectedModel = firstModel(); + m_selectedDifference = m_selectedModel->firstDifference(); + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); +} + +void KompareModelList::slotNextDifference() +{ + kdDebug(8101) << "slotNextDifference called" << endl; + if ( ( m_selectedDifference = m_selectedModel->nextDifference() ) != 0 ) + { + emit setSelection( m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); + return; + } + + kdDebug(8101) << "**** no next difference... ok lets find the next model..." << endl; + + if ( ( m_selectedModel = nextModel() ) != 0 ) + { + m_selectedDifference = m_selectedModel->firstDifference(); + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); + return; + } + + kdDebug(8101) << "**** !!! No next model, ok backup plan activated..." << endl; + + // Backup plan + m_selectedModel = lastModel(); + m_selectedDifference = m_selectedModel->lastDifference(); + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); +} + +void KompareModelList::slotApplyDifference( bool apply ) +{ + m_selectedModel->applyDifference( apply ); + emit applyDifference( apply ); +} + +void KompareModelList::slotApplyAllDifferences( bool apply ) +{ + m_selectedModel->applyAllDifferences( apply ); + emit applyAllDifferences( apply ); +} + +int KompareModelList::parseDiffOutput( const QString& diff ) +{ + kdDebug(8101) << "KompareModelList::parseDiffOutput" << endl; + + QStringList diffLines = split( diff ); + + Parser* parser = new Parser( this ); + m_models = parser->parse( diffLines ); + + m_info.generator = parser->generator(); + m_info.format = parser->format(); + + delete parser; + + if ( m_models ) + { + m_selectedModel = firstModel(); + kdDebug(8101) << "Ok there are differences..." << endl; + m_selectedDifference = m_selectedModel->firstDifference(); + emit setStatusBarModelInfo( 0, 0, modelCount(), differenceCount(), 0); + } + else + { + // Wow trouble, no models, so no differences... + kdDebug(8101) << "Now i'll be damned, there should be models here !!!" << endl; + return -1; + } + + return 0; +} + +bool KompareModelList::blendOriginalIntoModelList( const QString& localURL ) +{ + kdDebug() << "Hurrah we are blending..." << endl; + QFileInfo fi( localURL ); + + bool result = false; + DiffModel* model; + + QString fileContents; + + if ( fi.isDir() ) + { // is a dir + kdDebug() << "Blend Dir" << endl; +// QDir dir( localURL, QString::null, QDir::Name|QDir::DirsFirst, QDir::All ); + DiffModelListIterator modelIt = m_models->begin(); + DiffModelListIterator mEnd = m_models->end(); + for ( ; modelIt != mEnd; ++modelIt ) + { + model = *modelIt; + kdDebug(8101) << "Model : " << model << endl; + QString filename = model->sourcePath() + model->sourceFile(); + if ( !filename.startsWith( localURL ) ) + filename.prepend( localURL ); + QFileInfo fi2( filename ); + if ( fi2.exists() ) + { + kdDebug(8101) << "Reading from: " << filename << endl; + fileContents = readFile( filename ); + result = blendFile( model, fileContents ); + } + else + { + kdDebug(8101) << "File " << filename << " does not exist !" << endl; + kdDebug(8101) << "Assume empty file !" << endl; + fileContents.truncate( 0 ); + result = blendFile( model, fileContents ); + } + } + kdDebug() << "End of Blend Dir" << endl; + } + else if ( fi.isFile() ) + { // is a file + kdDebug() << "Blend File" << endl; + kdDebug(8101) << "Reading from: " << localURL << endl; + fileContents = readFile( localURL ); + + result = blendFile( (*m_models)[ 0 ], fileContents ); + kdDebug() << "End of Blend File" << endl; + } + + return result; +} + +bool KompareModelList::blendFile( DiffModel* model, const QString& fileContents ) +{ + if ( !model ) + { + kdDebug() << "**** model is null :(" << endl; + return false; + } + + model->setBlended( true ); + + int srcLineNo = 1, destLineNo = 1; + + QStringList lines = split( fileContents ); + + QStringList::ConstIterator linesIt = lines.begin(); + QStringList::ConstIterator lEnd = lines.end(); + + DiffHunkList* hunks = model->hunks(); + kdDebug(8101) << "Hunks in hunklist: " << hunks->count() << endl; + DiffHunkListIterator hunkIt = hunks->begin(); + + DiffHunk* newHunk = 0; + Difference* newDiff = 0; + + // FIXME: this approach is not very good, we should first check if the hunk applies cleanly + // and without offset and if not use that new linenumber with offset to compare against + // This will only work for files we just diffed with kompare but not for blending where + // file(s) to patch has/have potentially changed + + for ( ; hunkIt != hunks->end(); ++hunkIt ) + { + // Do we need to insert a new hunk before this one ? + DiffHunk* hunk = *hunkIt; + if ( srcLineNo < hunk->sourceLineNumber() ) + { + newHunk = new DiffHunk( srcLineNo, destLineNo, "", DiffHunk::AddedByBlend ); + + hunks->insert( hunkIt, newHunk ); + + newDiff = new Difference( srcLineNo, destLineNo, + Difference::Unchanged ); + + newHunk->add( newDiff ); + + while ( srcLineNo < hunk->sourceLineNumber() && linesIt != lEnd ) + { + newDiff->addSourceLine( *linesIt ); + newDiff->addDestinationLine( *linesIt ); + srcLineNo++; + destLineNo++; + ++linesIt; + } + } + + // Now we add the linecount difference for the hunk that follows + int size = hunk->sourceLineCount(); + + for ( int i = 0; i < size; ++i ) + { + ++linesIt; + } + + srcLineNo += size; + destLineNo += (*hunkIt)->destinationLineCount(); + } + + if ( linesIt != lEnd ) + { + newHunk = new DiffHunk( srcLineNo, destLineNo, "", DiffHunk::AddedByBlend ); + + model->addHunk( newHunk ); + + newDiff = new Difference( srcLineNo, destLineNo, Difference::Unchanged ); + + newHunk->add( newDiff ); + + while ( linesIt != lEnd ) + { + newDiff->addSourceLine( *linesIt ); + newDiff->addDestinationLine( *linesIt ); + ++linesIt; + } + } +#if 0 + DifferenceList hunkDiffList = (*hunkIt)->differences(); + DifferenceListIterator diffIt = hunkDiffList.begin(); + DifferenceListIterator dEnd = hunkDiffList.end(); + kdDebug() << "Number of differences in hunkDiffList = " << diffList.count() << endl; + + DifferenceListIterator tempIt; + Difference* diff; + + for ( ; diffIt != dEnd; ++diffIt ) + { + diff = *diffIt; + kdDebug() << "*(Diff it) = " << diff << endl; + // Check if there are lines in the original file before the difference + // that are not yet in the diff. If so create new Unchanged diff + if ( srcLineNo < diff->sourceLineNumber() ) + { + newDiff = new Difference( srcLineNo, destLineNo, + Difference::Unchanged | Difference::AddedByBlend ); + newHunk->add( newDiff ); + while ( srcLineNo < diff->sourceLineNumber() && linesIt != lEnd ) + { +// kdDebug(8101) << "SourceLine = " << srcLineNo << ": " << *linesIt << endl; + newDiff->addSourceLine( *linesIt ); + newDiff->addDestinationLine( *linesIt ); + srcLineNo++; + destLineNo++; + ++linesIt; + } + } + // Now i've got to add that diff + switch ( diff->type() ) + { + case Difference::Unchanged: + kdDebug(8101) << "Unchanged" << endl; + for ( int i = 0; i < diff->sourceLineCount(); i++ ) + { + if ( linesIt != lEnd && *linesIt != diff->sourceLineAt( i )->string() ) + { + kdDebug(8101) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt << endl; + kdDebug(8101) << "Conflict: DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string() << endl; + + // Do conflict resolution (well sort of) + diff->sourceLineAt( i )->setConflictString( *linesIt ); + diff->setConflict( true ); + } +// kdDebug(8101) << "SourceLine = " << srcLineNo << ": " << *linesIt << endl; +// kdDebug(8101) << "DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string() << endl; + srcLineNo++; + destLineNo++; + ++linesIt; + } + + tempIt = diffIt; + --diffIt; + diffList.remove( tempIt ); + newHunk->add( diff ); + + break; + case Difference::Change: + kdDebug(8101) << "Change" << endl; + + //QStringListConstIterator saveIt = linesIt; + + for ( int i = 0; i < diff->sourceLineCount(); i++ ) + { + if ( linesIt != lEnd && *linesIt != diff->sourceLineAt( i )->string() ) + { + kdDebug(8101) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt << endl; + kdDebug(8101) << "Conflict: DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string() << endl; + + // Do conflict resolution (well sort of) + diff->sourceLineAt( i )->setConflictString( *linesIt ); + diff->setConflict( true ); + } + srcLineNo++; + destLineNo++; + ++linesIt; + } + + destLineNo += diff->destinationLineCount(); + + tempIt = diffIt; + --diffIt; + diffList.remove( tempIt ); + newHunk->add( diff ); + newModel->addDiff( diff ); + + break; + case Difference::Insert: + kdDebug(8101) << "Insert" << endl; + destLineNo += diff->destinationLineCount(); + tempIt = diffIt; + --diffIt; + diffList.remove( tempIt ); + newHunk->add( diff ); + newModel->addDiff( diff ); + break; + case Difference::Delete: + kdDebug(8101) << "Delete" << endl; + kdDebug(8101) << "Number of lines in Delete: " << diff->sourceLineCount() << endl; + for ( int i = 0; i < diff->sourceLineCount(); i++ ) + { + if ( linesIt != lEnd && *linesIt != diff->sourceLineAt( i )->string() ) + { + kdDebug(8101) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt << endl; + kdDebug(8101) << "Conflict: DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string() << endl; + + // Do conflict resolution (well sort of) + diff->sourceLineAt( i )->setConflictString( *linesIt ); + diff->setConflict( true ); + } + +// kdDebug(8101) << "SourceLine = " << srcLineNo << ": " << *it << endl; +// kdDebug(8101) << "DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string() << endl; + srcLineNo++; + ++linesIt; + } + + tempIt = diffIt; + --diffIt; + diffList.remove( tempIt ); + newHunk->add( diff ); + newModel->addDiff( diff ); + break; + default: + kdDebug(8101) << "****, some diff type we dont know about ???" << endl; + } + } + } +#endif + +/* + diffList = newModel->differences(); + + diff = diffList.first(); + kdDebug(8101) << "Count = " << diffList.count() << endl; + for ( diff = diffList.first(); diff; diff = diffList.next() ) + { + kdDebug(8101) << "sourcelinenumber = " << diff->sourceLineNumber() << endl; + } +*/ + + m_selectedModel = firstModel(); + + m_selectedDifference = m_selectedModel->firstDifference(); + + return true; +} + +void KompareModelList::show() +{ + kdDebug() << "KompareModelList::Show Number of models = " << m_models->count() << endl; + emit modelsChanged( m_models ); + emit setSelection( m_selectedModel, m_selectedDifference ); +} + +void KompareModelList::clear() +{ + if ( m_models ) + m_models->clear(); + + emit modelsChanged( m_models ); +} + +void KompareModelList::swap() +{ + QString source = m_source; + QString destination = m_destination; + if ( m_info.mode == Kompare::ComparingFiles ) + compareFiles( destination, source ); + else if ( m_info.mode == Kompare::ComparingDirs ) + compareDirs( destination, source ); +} + +bool KompareModelList::isModified() const +{ + if ( m_noOfModified > 0 ) + return true; + return false; +} + +int KompareModelList::modelCount() const +{ + return m_models ? m_models->count() : 0; +} + +int KompareModelList::differenceCount() const +{ + return m_selectedModel ? m_selectedModel->differenceCount() : -1; +} + +int KompareModelList::appliedCount() const +{ + return m_selectedModel ? m_selectedModel->appliedCount() : -1; +} + +void KompareModelList::slotSetModified( bool modified ) +{ + kdDebug(8101) << "KompareModelList::slotSetModified( " << modified << " );" << endl; + kdDebug(8101) << "Before: m_noOfModified = " << m_noOfModified << endl; + + // If selectedModel emits its signal setModified it does not set the model + // internal m_modified bool yet, it only does that after the emit. + if ( modified && !m_selectedModel->isModified() ) + m_noOfModified++; + else if ( !modified && m_selectedModel->isModified() ) + m_noOfModified--; + + kdDebug(8101) << "After : m_noOfModified = " << m_noOfModified << endl; + + if ( m_noOfModified < 0 ) + { + kdDebug(8101) << "Wow something is ****ed up..." << endl; + } + else if ( m_noOfModified == 0 ) + { + emit setModified( false ); + } + else // > 0 :-) + { + emit setModified( true ); + } +} + +bool KompareModelList::setSelectedModel( DiffModel* model ) +{ + kdDebug(8101) << "KompareModelList::setSelectedModel( " << model << " )" << endl; + + if ( model != m_selectedModel ) + { + if ( m_models->findIndex( model ) == -1 ) + return false; + kdDebug(8101) << "m_selectedModel (was) = " << m_selectedModel << endl; + m_modelIndex = m_models->findIndex( model ); + kdDebug(8101) << "m_selectedModel (is) = " << m_selectedModel << endl; + m_selectedModel = model; + } + + updateModelListActions(); + + return true; +} + +void KompareModelList::updateModelListActions() +{ + if ( m_models && m_selectedModel && m_selectedDifference ) + { + if ( ( ( KomparePart* )parent() )->isReadWrite() ) + { + if ( m_selectedModel->appliedCount() != m_selectedModel->differenceCount() ) + m_applyAll->setEnabled( true ); + else + m_applyAll->setEnabled( false ); + + if ( m_selectedModel->appliedCount() != 0 ) + m_unapplyAll->setEnabled( true ); + else + m_unapplyAll->setEnabled( false ); + + m_applyDifference->setEnabled( true ); + m_unApplyDifference->setEnabled( true ); + m_save->setEnabled( m_selectedModel->isModified() ); + } + else + { + m_applyDifference->setEnabled ( false ); + m_unApplyDifference->setEnabled( false ); + m_applyAll->setEnabled ( false ); + m_unapplyAll->setEnabled ( false ); + m_save->setEnabled ( false ); + } + + m_previousFile->setEnabled ( hasPrevModel() ); + m_nextFile->setEnabled ( hasNextModel() ); + m_previousDifference->setEnabled( hasPrevDiff() ); + m_nextDifference->setEnabled ( hasNextDiff() ); + } + else + { + m_applyDifference->setEnabled ( false ); + m_unApplyDifference->setEnabled ( false ); + m_applyAll->setEnabled ( false ); + m_unapplyAll->setEnabled ( false ); + + m_previousFile->setEnabled ( false ); + m_nextFile->setEnabled ( false ); + m_previousDifference->setEnabled( false ); + m_nextDifference->setEnabled ( false ); + m_save->setEnabled ( false ); + } +} + +bool KompareModelList::hasPrevModel() const +{ + kdDebug(8101) << "KompareModelList::hasPrevModel()" << endl; + + if ( m_modelIndex > 0 ) + { +// kdDebug(8101) << "has prev model" << endl; + return true; + } + +// kdDebug(8101) << "doesn't have a prev model, this is the first one..." << endl; + + return false; +} + +bool KompareModelList::hasNextModel() const +{ + kdDebug(8101) << "KompareModelList::hasNextModel()" << endl; + + if ( ( unsigned int )m_modelIndex < ( m_models->count() - 1 ) ) + { +// kdDebug(8101) << "has next model" << endl; + return true; + } + +// kdDebug(8101) << "doesn't have a next model, this is the last one..." << endl; + return false; +} + +bool KompareModelList::hasPrevDiff() const +{ +// kdDebug(8101) << "KompareModelList::hasPrevDiff()" << endl; + int index = m_selectedModel->diffIndex(); + + if ( index > 0 ) + { +// kdDebug(8101) << "has prev difference in same model" << endl; + return true; + } + + if ( hasPrevModel() ) + { +// kdDebug(8101) << "has prev difference but in prev model" << endl; + return true; + } + +// kdDebug(8101) << "doesn't have a prev difference, not even in the previous model because there is no previous model" << endl; + + return false; +} + +bool KompareModelList::hasNextDiff() const +{ +// kdDebug(8101) << "KompareModelList::hasNextDiff()" << endl; + int index = m_selectedModel->diffIndex(); + + if ( index < ( m_selectedModel->differenceCount() - 1 ) ) + { +// kdDebug(8101) << "has next difference in same model" << endl; + return true; + } + + if ( hasNextModel() ) + { +// kdDebug(8101) << "has next difference but in next model" << endl; + return true; + } + +// kdDebug(8101) << "doesn't have a next difference, not even in next model because there is no next model" << endl; + + return false; +} + +void KompareModelList::slotActionApplyDifference() +{ + if ( !m_selectedDifference->applied() ) + slotApplyDifference( true ); + slotNextDifference(); + updateModelListActions(); +} + +void KompareModelList::slotActionUnApplyDifference() +{ + if ( m_selectedDifference->applied() ) + slotApplyDifference( false ); + slotPreviousDifference(); + updateModelListActions(); +} + +void KompareModelList::slotActionApplyAllDifferences() +{ + slotApplyAllDifferences( true ); + updateModelListActions(); +} + +void KompareModelList::slotActionUnapplyAllDifferences() +{ + slotApplyAllDifferences( false ); + updateModelListActions(); +} + +#include "komparemodellist.moc" + +/* vim: set ts=4 sw=4 noet: */ diff --git a/kompare/libdiff2/komparemodellist.h b/kompare/libdiff2/komparemodellist.h new file mode 100644 index 00000000..8ba264fb --- /dev/null +++ b/kompare/libdiff2/komparemodellist.h @@ -0,0 +1,213 @@ +/*************************************************************************** + komparemodellist.h - description + ------------------- + begin : Tue Jun 26 2001 + copyright : (C) 2001-2003 by John Firebaugh + and Otto Bruggeman + email : jfirebaugh@kde.org + otto.bruggeman@home.nl + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KOMPAREMODELLIST_H +#define KOMPAREMODELLIST_H + +#include + +#include "diffmodel.h" +#include "diffmodellist.h" +#include "kompare.h" + +class QFile; + +class KAction; +class KDirWatch; +class KTempFile; + +class DiffSettings; +class KompareProcess; + +namespace Diff2 +{ + +class KompareModelList : public QObject +{ + Q_OBJECT +public: + KompareModelList( DiffSettings* diffSettings, struct Kompare::Info& info, QObject* parent = 0, const char* name = 0 ); + ~KompareModelList(); + +public: + // Swap source with destination and show differences + void swap(); + + /* Comparing methods */ + bool compare( const QString& source, const QString& destination ); + + bool compareFiles( const QString& source, const QString& destination ); + bool compareDirs( const QString& source, const QString& destination ); + + bool openDiff( const QString& diff ); + + bool openFileAndDiff( const QString& file, const QString& diff ); + bool openDirAndDiff( const QString& dir, const QString& diff ); + + bool saveDiff( const QString& url, QString directory, DiffSettings* diffSettings ); + bool saveAll(); + + bool saveDestination( DiffModel* model ); + + void setEncoding( const QString& encoding ); + + QString recreateDiff() const; + + // This parses the difflines and creates new models + int parseDiffOutput( const QString& diff ); + + // Call this to emit the signals to the rest of the "world" to show the diff + void show(); + + // This will blend the original URL (dir or file) into the diffmodel, + // this is like patching but with a twist + bool blendOriginalIntoModelList( const QString& localURL ); + + enum Kompare::Mode mode() const { return m_info.mode; }; + const DiffModelList* models() const { return m_models; }; + + int modelCount() const; + int differenceCount() const; + int appliedCount() const; + + const DiffModel* modelAt( int i ) const { return *( m_models->at( i ) ); }; + int findModel( DiffModel* model ) const { return m_models->findIndex( model ); }; + + bool isModified() const; + + int currentModel() const { return m_models->findIndex( m_selectedModel ); }; + int currentDifference() const { return m_selectedModel ? m_selectedModel->findDifference( m_selectedDifference ) : -1; }; + + const DiffModel* selectedModel() const { return m_selectedModel; }; + const Difference* selectedDifference() const { return m_selectedDifference; }; + + void clear(); + +private: + Diff2::DiffModel* firstModel(); + Diff2::DiffModel* lastModel(); + Diff2::DiffModel* prevModel(); + Diff2::DiffModel* nextModel(); + + bool setSelectedModel( Diff2::DiffModel* model ); + + void updateModelListActions(); + +protected: + bool blendFile( DiffModel* model, const QString& lines ); + +signals: + void status( Kompare::Status status ); + void setStatusBarModelInfo( int modelIndex, int differenceIndex, int modelCount, int differenceCount, int appliedCount ); + void error( QString error ); + void modelsChanged( const Diff2::DiffModelList* models ); + void setSelection( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void setSelection( const Diff2::Difference* diff ); + void applyDifference( bool apply ); + void applyAllDifferences( bool apply ); + void applyDifference( const Diff2::Difference* diff, bool apply ); + + // Emits true when m_noOfModified > 0, false when m_noOfModified == 0 + void setModified( bool modified ); + +public slots: + void slotSelectionChanged( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void slotSelectionChanged( const Diff2::Difference* diff ); + + void slotApplyDifference( bool apply ); + void slotApplyAllDifferences( bool apply ); + void slotPreviousModel(); + void slotNextModel(); + void slotPreviousDifference(); + void slotNextDifference(); + + // This slot is called by the diffmodels whenever their status changes to modified or unmodified + void slotSetModified( bool modified ); + +protected slots: + void slotDiffProcessFinished( bool success ); + void slotWriteDiffOutput( bool success ); + + void slotActionApplyDifference(); + void slotActionUnApplyDifference(); + void slotActionApplyAllDifferences(); + void slotActionUnapplyAllDifferences(); + + /** Save the currently selected destination in a multi-file diff, + or the single destination if a single file diff. */ + void slotSaveDestination(); + +private slots: + void slotDirectoryChanged( const QString& ); + void slotFileChanged( const QString& ); + +private: // Helper methods + bool isDirectory( const QString& url ) const; + bool isDiff( const QString& mimetype ) const; + QString readFile( const QString& fileName ); + + bool hasPrevModel() const; + bool hasNextModel() const; + bool hasPrevDiff() const; + bool hasNextDiff() const; + + QStringList split( const QString& diff ); + +private: + KTempFile* m_diffTemp; + QString m_diffURL; + + KompareProcess* m_diffProcess; + + DiffSettings* m_diffSettings; + + DiffModelList* m_models; + + QString m_source; + QString m_destination; + + DiffModel* m_selectedModel; + Difference* m_selectedDifference; + + KDirWatch* m_dirWatch; + KDirWatch* m_fileWatch; + + int m_noOfModified; + unsigned int m_modelIndex; + + struct Kompare::Info& m_info; + + KAction* m_applyDifference; + KAction* m_unApplyDifference; + KAction* m_applyAll; + KAction* m_unapplyAll; + KAction* m_previousFile; + KAction* m_nextFile; + KAction* m_previousDifference; + KAction* m_nextDifference; + + KAction* m_save; + + QString m_encoding; + QTextCodec* m_textCodec; +}; + +} // End of namespace Diff2 + +#endif diff --git a/kompare/libdiff2/kompareprocess.cpp b/kompare/libdiff2/kompareprocess.cpp new file mode 100644 index 00000000..2d5eac00 --- /dev/null +++ b/kompare/libdiff2/kompareprocess.cpp @@ -0,0 +1,269 @@ +/*************************************************************************** + kompareprocess.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + (C) 2007 Kevin Kofler + email : otto.bruggeman@home.nl + jfirebaugh@kde.org + kevin.kofler@chello.at +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#include +#include +#include + +#include +#include +#include + +#include "diffsettings.h" +#include "kompareprocess.h" + +KompareProcess::KompareProcess( DiffSettings* diffSettings, enum Kompare::DiffMode mode, QString source, QString destination, QString dir ) + : KProcess(), + m_diffSettings( diffSettings ), + m_mode( mode ), + m_textDecoder( 0 ) +{ + setUseShell( true ); + + // connect the stdout and stderr signals + connect( this, SIGNAL( receivedStdout( KProcess*, char*, int ) ), + SLOT ( slotReceivedStdout( KProcess*, char*, int ) ) ); + connect( this, SIGNAL( receivedStderr( KProcess*, char*, int ) ), + SLOT ( slotReceivedStderr( KProcess*, char*, int ) ) ); + + // connect the signal that indicates that the proces has exited + connect( this, SIGNAL( processExited( KProcess* ) ), + SLOT ( slotProcessExited( KProcess* ) ) ); + + *this << "LANG=C"; + + // Write command and options + if( m_mode == Kompare::Default ) + { + writeDefaultCommandLine(); + } + else + { + writeCommandLine(); + } + + if( !dir.isEmpty() ) { + QDir::setCurrent( dir ); + } + + // Write file names + *this << "--"; + *this << KProcess::quote( constructRelativePath( dir, source ) ); + *this << KProcess::quote( constructRelativePath( dir, destination ) ); +} + +void KompareProcess::writeDefaultCommandLine() +{ + if ( !m_diffSettings || m_diffSettings->m_diffProgram.isEmpty() ) + { + *this << "diff" << "-dr"; + } + else + { + *this << m_diffSettings->m_diffProgram << "-dr"; + } + + *this << "-U" << QString::number( m_diffSettings->m_linesOfContext ); +} + +void KompareProcess::writeCommandLine() +{ + // load the executable into the KProcess + if ( m_diffSettings->m_diffProgram.isEmpty() ) + { + kdDebug(8101) << "Using the first diff in the path..." << endl; + *this << "diff"; + } + else + { + kdDebug(8101) << "Using a user specified diff, namely: " << m_diffSettings->m_diffProgram << endl; + *this << m_diffSettings->m_diffProgram; + } + + switch( m_diffSettings->m_format ) { + case Kompare::Unified : + *this << "-U" << QString::number( m_diffSettings->m_linesOfContext ); + break; + case Kompare::Context : + *this << "-C" << QString::number( m_diffSettings->m_linesOfContext ); + break; + case Kompare::RCS : + *this << "-n"; + break; + case Kompare::Ed : + *this << "-e"; + break; + case Kompare::SideBySide: + *this << "-y"; + break; + case Kompare::Normal : + case Kompare::UnknownFormat : + default: + break; + } + + if ( m_diffSettings->m_largeFiles ) + { + *this << "-H"; + } + + if ( m_diffSettings->m_ignoreWhiteSpace ) + { + *this << "-b"; + } + + if ( m_diffSettings->m_ignoreAllWhiteSpace ) + { + *this << "-w"; + } + + if ( m_diffSettings->m_ignoreEmptyLines ) + { + *this << "-B"; + } + + if ( m_diffSettings->m_ignoreChangesDueToTabExpansion ) + { + *this << "-E"; + } + + if ( m_diffSettings->m_createSmallerDiff ) + { + *this << "-d"; + } + + if ( m_diffSettings->m_ignoreChangesInCase ) + { + *this << "-i"; + } + + if ( m_diffSettings->m_ignoreRegExp && !m_diffSettings->m_ignoreRegExpText.isEmpty() ) + { + *this << "-I " << KProcess::quote( m_diffSettings->m_ignoreRegExpText ); + } + + if ( m_diffSettings->m_showCFunctionChange ) + { + *this << "-p"; + } + + if ( m_diffSettings->m_convertTabsToSpaces ) + { + *this << "-t"; + } + + if ( m_diffSettings->m_recursive ) + { + *this << "-r"; + } + + if ( m_diffSettings->m_newFiles ) + { + *this << "-N"; + } + +// This option is more trouble than it is worth... please do not ever enable it unless you want really weird crashes +// if ( m_diffSettings->m_allText ) +// { +// *this << "-a"; +// } + + if ( m_diffSettings->m_excludeFilePattern ) + { + QStringList::ConstIterator it = m_diffSettings->m_excludeFilePatternList.begin(); + QStringList::ConstIterator end = m_diffSettings->m_excludeFilePatternList.end(); + for ( ; it != end; ++it ) + { + *this << "-x" << KProcess::quote( *it ); + } + } + + if ( m_diffSettings->m_excludeFilesFile && !m_diffSettings->m_excludeFilesFileURL.isEmpty() ) + { + *this << "-X" << KProcess::quote( m_diffSettings->m_excludeFilesFileURL ); + } +} + +KompareProcess::~KompareProcess() +{ +} + +void KompareProcess::setEncoding( const QString& encoding ) +{ + if ( encoding.lower() == "default" ) + { + m_textDecoder = QTextCodec::codecForLocale()->makeDecoder(); + } + else + { + QTextCodec* textCodec = KGlobal::charsets()->codecForName( encoding.latin1() ); + if ( textCodec ) + m_textDecoder = textCodec->makeDecoder(); + else + { + kdDebug(8101) << "Using locale codec as backup..." << endl; + textCodec = QTextCodec::codecForLocale(); + m_textDecoder = textCodec->makeDecoder(); + } + } +} + +void KompareProcess::slotReceivedStdout( KProcess* /* process */, char* buffer, int length ) +{ + // add all output to m_stdout + if ( m_textDecoder ) + m_stdout += m_textDecoder->toUnicode( buffer, length ); + else + kdDebug(8101) << "KompareProcess::slotReceivedStdout : No decoder !!!" << endl; +} + +void KompareProcess::slotReceivedStderr( KProcess* /* process */, char* buffer, int length ) +{ + // add all output to m_stderr + if ( m_textDecoder ) + m_stderr += m_textDecoder->toUnicode( buffer, length ); + else + kdDebug(8101) << "KompareProcess::slotReceivedStderr : No decoder !!!" << endl; +} + +bool KompareProcess::start() +{ +#ifndef NDEBUG + QString cmdLine; + QValueList::ConstIterator it = arguments.begin(); + for (; it != arguments.end(); ++it ) + cmdLine += "\"" + (*it) + "\" "; + kdDebug(8101) << cmdLine << endl; +#endif + return( KProcess::start( KProcess::NotifyOnExit, KProcess::AllOutput ) ); +} + +void KompareProcess::slotProcessExited( KProcess* /* proc */ ) +{ + // exit status of 0: no differences + // 1: some differences + // 2: error but there may be differences ! + kdDebug(8101) << "Exited with exit status : " << exitStatus() << endl; + emit diffHasFinished( normalExit() && exitStatus() != 0 ); +} + +#include "kompareprocess.moc" + diff --git a/kompare/libdiff2/kompareprocess.h b/kompare/libdiff2/kompareprocess.h new file mode 100644 index 00000000..06a4a5ce --- /dev/null +++ b/kompare/libdiff2/kompareprocess.h @@ -0,0 +1,67 @@ +/*************************************************************************** + kompareprocess.h - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2003 by Otto Bruggeman + and John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +#ifndef KOMPAREPROCESS_H +#define KOMPAREPROCESS_H + +#include + +#include "kompare.h" + +class QTextCodec; + +class DiffSettings; + +class KompareProcess : public KProcess, public KompareFunctions +{ + Q_OBJECT + +public: + KompareProcess( DiffSettings* diffSettings, enum Kompare::DiffMode mode, QString source, QString destination, QString directory = QString::null ); + ~KompareProcess(); + + bool start(); + + QString diffOutput() { return m_stdout; } + QString stdOut() { return m_stdout; } + QString stdErr() { return m_stderr; } + + void setEncoding( const QString& encoding ); + +signals: + void diffHasFinished( bool finishedNormally ); + +protected: + void writeDefaultCommandLine(); + void writeCommandLine(); + +protected slots: + void slotReceivedStdout( KProcess*, char*, int ); + void slotReceivedStderr( KProcess*, char*, int ); + void slotProcessExited( KProcess* proc ); + +private: + DiffSettings* m_diffSettings; + enum Kompare::DiffMode m_mode; + QString m_stdout; + QString m_stderr; + QTextDecoder* m_textDecoder; +}; + +#endif diff --git a/kompare/libdiff2/levenshteintable.cpp b/kompare/libdiff2/levenshteintable.cpp new file mode 100644 index 00000000..54525aef --- /dev/null +++ b/kompare/libdiff2/levenshteintable.cpp @@ -0,0 +1,332 @@ +/******************************************************************************* +** +** Filename : levenshteintable.cpp +** Created on : 08 november, 2003 +** Copyright : (c) 2003 Otto Bruggeman +** Email : bruggie@home.nl +** +*******************************************************************************/ + +/******************************************************************************* +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +*******************************************************************************/ + +#include + +#include + +#include +#include + +#include "levenshteintable.h" + +#include "difference.h" + +using namespace Diff2; + +LevenshteinTable::LevenshteinTable() + : m_width( 256 ), + m_height( 256 ), + m_size( m_height * m_width ), + m_table( new unsigned int[ m_size ] ), + m_source( 0 ), + m_destination( 0 ) +{ +} + +LevenshteinTable::LevenshteinTable( unsigned int width, unsigned int height ) + : m_width( width ), + m_height( height ), + m_size( m_width * m_height ), + m_table( new unsigned int[ m_size ] ), + m_source( 0 ), + m_destination( 0 ) +{ +} + +LevenshteinTable::~LevenshteinTable() +{ + delete[] m_table; + m_source = 0; + m_destination = 0; +} + +int LevenshteinTable::getContent( unsigned int posX, unsigned int posY ) const +{ +// kdDebug(8101) << "Width = " << m_width << ", height = " << m_height << ", posX = " << posX << ", posY = " << posY << endl; + return m_table[ posY * m_width + posX ]; +} + +int LevenshteinTable::setContent( unsigned int posX, unsigned int posY, int value ) +{ + m_table[ posY * m_width + posX ] = value; + + return 0; +} + +bool LevenshteinTable::setSize( unsigned int width, unsigned int height ) +{ + // Set a limit of 16.7 million entries, will be about 64 MB of ram, that should be plenty + if ( ( ( width ) * ( height ) ) > ( 256 * 256 * 256 ) ) + return false; + + if ( ( ( width ) * ( height ) ) > m_size ) + { + delete[] m_table; + + m_size = width * height; + m_table = new unsigned int[ m_size ]; + } + + m_width = width; + m_height = height; + + return true; +} + +void LevenshteinTable::dumpLevenshteinTable() +{ + for ( unsigned int i = 0; i < m_height; ++i ) + { + for ( unsigned int j = 0; j < m_width; ++j ) + { + std::cout.width( 3 ); + std::cout << getContent( j, i ); + } + std::cout << std::endl; + } +} + +unsigned int LevenshteinTable::createTable( DifferenceString* source, DifferenceString* destination ) +{ + m_source = source; + m_destination = destination; + + QString s = ' ' + source->string(); // Optimization, so i dont have to subtract 1 from the indexes every + QString d = ' ' + destination->string(); // single time and add 1 to the width and height of the table + + unsigned int m = s.length(); + unsigned int n = d.length(); + + const QChar* sq = s.unicode(); + const QChar* dq = d.unicode(); + + if ( m == 1 ) + return --n; + + if ( n == 1 ) + return --m; + + if ( !setSize( m, n ) ) + return 0; + + unsigned int i; + unsigned int j; + + // initialize first row + for ( i = 0; i < m; ++i ) + setContent( i, 0, i ); + // initialize first column + for ( j = 0; j < n; ++j ) + setContent( 0, j, j ); + + int cost = 0, north = 0, west = 0, northwest = 0; + + ushort si, dj; + // Optimization, calculate row wise instead of column wise, wont trash the cache so much with large strings + for ( j = 1; j < n; ++j ) + { + dj = dq[ j ]; + + for ( i = 1; i < m; ++i ) + { + si = sq[ i ]; + if ( si == dj ) + cost = 0; + else + cost = 1; + + north = getContent( i, j-1 ) + 1; + west = getContent( i-1, j ) + 1; + northwest = getContent( i-1, j-1 ) + cost; + + setContent( i, j, kMin( north, kMin( west, northwest ) ) ); + } + } + + return getContent( m-1, n-1 ); +} + +int LevenshteinTable::chooseRoute( int c1, int c2, int c3 ) +{ +// kdDebug(8101) << "c1 = " << c1 << ", c2 = " << c2 << ", c3 = " << c3 << endl; + // preference order: c2, c3, c1, hopefully this will work out for me + if ( c2 <= c1 && c2 <= c3 ) + return 1; + + if ( c3 <= c2 && c3 <= c1 ) + return 2; + + return 0; +} + +void LevenshteinTable::createListsOfMarkers() +{ +// std::cout << source.latin1() << std::endl; +// std::cout << destination.latin1() << std::endl; +// dumpLevenshteinTable(); + + unsigned int x = m_width-1; + unsigned int y = m_height-1; + + Marker* c = 0; + + int n, nw, w, direction, currentValue; + while ( x > 0 && y > 0 ) + { + currentValue = getContent( x, y ); + + nw = getContent( x - 1, y - 1 ); + n = getContent( x, y - 1 ); + w = getContent( x - 1, y ); + + direction = chooseRoute( n, nw, w ); + + switch ( direction ) + { + case 0: // north +// kdDebug(8101) << "Picking north" << endl; +// kdDebug(8101) << "Source[" << ( x - 1 ) << "] = " << QString( source[ x-1 ] ) << ", destination[" << ( y - 1 ) << "] = " << QString( destination[ y-1 ] ) << endl; + + if ( !m_destination->markerList().isEmpty() ) + c = m_destination->markerList().first(); + else + c = 0; + + if ( c && c->type() == Marker::End ) + { +// kdDebug(8101) << "CurrentValue: " << currentValue << endl; + if ( n == currentValue ) + m_destination->prepend( new Marker( Marker::Start, y ) ); + // else: the change continues, dont do anything + } + else + { +// kdDebug(8101) << "CurrentValue: " << currentValue << endl; + if ( n < currentValue ) + m_destination->prepend( new Marker( Marker::End, y ) ); + } + + --y; + break; + case 1: // northwest +// kdDebug(8101) << "Picking northwest" << endl; +// kdDebug(8101) << "Source[" << ( x - 1 ) << "] = " << QString( source[ x-1 ] ) << ", destination[" << ( y - 1 ) << "] = " << QString( destination[ y-1 ] ) << endl; + + if ( !m_destination->markerList().isEmpty() ) + c = m_destination->markerList().first(); + else + c = 0; + + if ( c && c->type() == Marker::End ) + { +// kdDebug(8101) << "End found: CurrentValue: " << currentValue << endl; + if ( nw == currentValue ) + m_destination->prepend( new Marker( Marker::Start, y ) ); + // else: the change continues, dont do anything + } + else + { +// kdDebug(8101) << "CurrentValue: " << currentValue << endl; + if ( nw < currentValue ) + m_destination->prepend( new Marker( Marker::End, y ) ); + } + + if ( !m_source->markerList().isEmpty() ) + c = m_source->markerList().first(); + else + c = 0; + + if ( c && c->type() == Marker::End ) + { +// kdDebug(8101) << "End found: CurrentValue: " << currentValue << endl; + if ( nw == currentValue ) + m_source->prepend( new Marker( Marker::Start, x ) ); + // else: the change continues, dont do anything + } + else + { +// kdDebug(8101) << "CurrentValue: " << currentValue << endl; + if ( nw < currentValue ) + m_source->prepend( new Marker( Marker::End, x ) ); + } + + --y; + --x; + break; + case 2: // west +// kdDebug(8101) << "Picking west" << endl; +// kdDebug(8101) << "Source[" << ( x - 1 ) << "] = " << QString( source[ x-1 ] ) << ", destination[" << ( y - 1 ) << "] = " << QString( destination[ y-1 ] ) << endl; + + if ( !m_source->markerList().isEmpty() ) + c = m_source->markerList().first(); + else + c = 0; + + if ( c && c->type() == Marker::End ) + { +// kdDebug(8101) << "End found: CurrentValue: " << currentValue << endl; + if ( w == currentValue ) + m_source->prepend( new Marker( Marker::Start, x ) ); + // else: the change continues, dont do anything + } + else + { +// kdDebug(8101) << "CurrentValue: " << currentValue << endl; + if ( w < currentValue ) + m_source->prepend( new Marker( Marker::End, x ) ); + } + + --x; + break; + } + } + +// kdDebug(8101) << "Source string: " << m_source->string() << endl; +// c = m_source->markerList()->first(); +// QStringList list; +// unsigned int prevValue = 0; +// for ( ; c; c = m_source->markerList()->next() ) +// { +// kdDebug(8101) << "Source Marker Entry : Type: " << c->type() << ", Offset: " << c->offset() << endl; +// list.append( m_source->string().mid( prevValue, c->offset() - prevValue ) ); +// prevValue = c->offset(); +// } +// if ( prevValue < m_source->string().length() - 1 ) +// { +// list.append( m_source->string().mid( prevValue, m_source->string().length() - prevValue ) ); +// } +// kdDebug(8101) << "Source Resulting stringlist : " << list.join("\n") << endl; + +// list.clear(); +// prevValue = 0; + +// kdDebug(8101) << "Destination string: " << m_destination->string() << endl; +// for ( ; c; c = m_destination->markerList()->next() ) +// { +// kdDebug(8101) << "Destination Marker Entry : Type: " << c->type() << ", Offset: " << c->offset() << endl; +// list.append( m_destination->string().mid( prevValue, c->offset() - prevValue ) ); +// prevValue = c->offset(); +// } +// if ( prevValue < m_destination->string().length() - 1 ) +// { +// list.append( m_destination->string().mid( prevValue, m_destination->string().length() - prevValue ) ); +// } +// kdDebug(8101) << "Destination Resulting string : " << list.join("\n") << endl; +} + diff --git a/kompare/libdiff2/levenshteintable.h b/kompare/libdiff2/levenshteintable.h new file mode 100644 index 00000000..201d1c10 --- /dev/null +++ b/kompare/libdiff2/levenshteintable.h @@ -0,0 +1,69 @@ +/******************************************************************************* +** +** Filename : levenshteintable.h +** Created on : 08 november, 2003 +** Copyright : (c) 2003 Otto Bruggeman +** Email : bruggie@home.nl +** +*******************************************************************************/ + +/******************************************************************************* +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +*******************************************************************************/ + +#ifndef _LEVENSHTEIN_H +#define _LEVENSHTEIN_H + +#include "difference.h" + +class QString; + +namespace Diff2 { + +class Marker; + +class LevenshteinTable +{ +public: + LevenshteinTable(); + LevenshteinTable( unsigned int width, unsigned int height ); + ~LevenshteinTable(); + +public: + int getContent( unsigned int posX, unsigned int posY ) const; + int setContent( unsigned int posX, unsigned int posY, int value ); + bool setSize ( unsigned int width, unsigned int height ); + + unsigned int width() const { return m_width; }; + unsigned int height() const { return m_height; }; + + /** Debug method to check if the table is properly filled */ + void dumpLevenshteinTable( void ); + + /** This will calculate the levenshtein distance of 2 strings */ + unsigned int createTable( DifferenceString* s, DifferenceString* d ); + + void createListsOfMarkers( void ); + int chooseRoute( int c1, int c2, int c3 ); + +protected: + LevenshteinTable( const LevenshteinTable& table ); + const LevenshteinTable& operator = ( const LevenshteinTable& table ); + +private: + unsigned int m_width; + unsigned int m_height; + unsigned int m_size; + unsigned int* m_table; + DifferenceString* m_source; + DifferenceString* m_destination; +}; + +} // namespace Diff2 + +#endif // _LEVENSHTEIN_H diff --git a/kompare/libdiff2/parser.cpp b/kompare/libdiff2/parser.cpp new file mode 100644 index 00000000..04ff7a4a --- /dev/null +++ b/kompare/libdiff2/parser.cpp @@ -0,0 +1,139 @@ +/************************************************************************** +** parser.cpp +** ------------------- +** begin : Sun Aug 4 15:05:35 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#include + +#include "parser.h" +#include "cvsdiffparser.h" +#include "diffparser.h" +#include "perforceparser.h" +#include "diffmodel.h" + +using namespace Diff2; + +Parser::Parser( const KompareModelList* list ) : + m_list( list ) +{ +} + +Parser::~Parser() +{ +} + +int Parser::cleanUpCrap( QStringList& diffLines ) +{ + QStringList::Iterator it = diffLines.begin(); + + int nol = 0; + + QString noNewLine( "\\ No newline" ); + + for ( ; it != diffLines.end(); ++it ) + { + if ( (*it).startsWith( noNewLine ) ) + { + it = diffLines.remove( it ); + // correcting the advance of the iterator because of the remove + --it; + QString temp( *it ); + temp.truncate( temp.find( '\n' ) ); + *it = temp; + ++nol; + } + } + + return nol; +} + +DiffModelList* Parser::parse( QStringList& diffLines ) +{ + /* Basically determine the generator then call the parse method */ + ParserBase* parser; + + m_generator = determineGenerator( diffLines ); + + int nol = cleanUpCrap( diffLines ); + kdDebug(8101) << "Cleaned up " << nol << " line(s) of crap from the diff..." << endl; + + switch( m_generator ) + { + case Kompare::CVSDiff : + kdDebug(8101) << "It is a CVS generated diff..." << endl; + parser = new CVSDiffParser( m_list, diffLines ); + break; + case Kompare::Diff : + kdDebug(8101) << "It is a diff generated diff..." << endl; + parser = new DiffParser( m_list, diffLines ); + break; + case Kompare::Perforce : + kdDebug(8101) << "It is a Perforce generated diff..." << endl; + parser = new PerforceParser( m_list, diffLines ); + break; + default: + // Nothing to delete, just leave... + return 0L; + } + + m_format = parser->format(); + DiffModelList* modelList = parser->parse(); + if ( modelList ) + { + kdDebug(8101) << "Modelcount: " << modelList->count() << endl; + DiffModelListIterator modelIt = modelList->begin(); + DiffModelListIterator mEnd = modelList->end(); + for ( ; modelIt != mEnd; ++modelIt ) + { + kdDebug(8101) << "Hunkcount: " << (*modelIt)->hunkCount() << endl; + kdDebug(8101) << "Diffcount: " << (*modelIt)->differenceCount() << endl; + } + } + + delete parser; + + return modelList; +} + +enum Kompare::Generator Parser::determineGenerator( const QStringList& diffLines ) +{ + // Shit have to duplicate some code with this method and the ParserBase derived classes + QString cvsDiff ( "Index: " ); + QString perforceDiff( "==== " ); + + QStringList::ConstIterator it = diffLines.begin(); + QStringList::ConstIterator linesEnd = diffLines.end(); + + while ( it != linesEnd ) + { + if ( ( *it ).startsWith( cvsDiff ) ) + { + kdDebug(8101) << "Diff is a CVSDiff" << endl; + return Kompare::CVSDiff; + } + else if ( ( *it ).startsWith( perforceDiff ) ) + { + kdDebug(8101) << "Diff is a Perforce Diff" << endl; + return Kompare::Perforce; + } + ++it; + } + + kdDebug(8101) << "We'll assume it is a diff Diff" << endl; + + // For now we'll assume it is a diff file diff, later we might + // try to really determine if it is a diff file diff. + return Kompare::Diff; +} diff --git a/kompare/libdiff2/parser.h b/kompare/libdiff2/parser.h new file mode 100644 index 00000000..0ffae23a --- /dev/null +++ b/kompare/libdiff2/parser.h @@ -0,0 +1,58 @@ +/************************************************************************** +** parser.h +** -------- +** begin : Tue Jul 30 23:53:52 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#ifndef _DIFF2_PARSER_H +#define _DIFF2_PARSER_H + +#include "diffmodellist.h" +#include "kompare.h" + +namespace Diff2 +{ + +class DiffModel; +class KompareModelList; + +class Parser +{ +public: + Parser( const KompareModelList* list ); + ~Parser(); + +public: + DiffModelList* parse( QStringList& diffLines ); + + enum Kompare::Generator generator() const { return m_generator; }; + enum Kompare::Format format() const { return m_format; }; + +private: + /** Which program was used to generate the output */ + enum Kompare::Generator determineGenerator( const QStringList& diffLines ); + + int cleanUpCrap( QStringList& diffLines ); + +private: + enum Kompare::Generator m_generator; + enum Kompare::Format m_format; + + const KompareModelList* m_list; +}; + +} // End of namespace Diff2 + +#endif + diff --git a/kompare/libdiff2/parserbase.cpp b/kompare/libdiff2/parserbase.cpp new file mode 100644 index 00000000..303f7b22 --- /dev/null +++ b/kompare/libdiff2/parserbase.cpp @@ -0,0 +1,739 @@ +/************************************************************************** +** parserbase.cpp +** ------------------- +** begin : Sun Aug 4 15:05:35 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#include + +#include + +#include "diffmodel.h" +#include "diffhunk.h" +#include "difference.h" +#include "komparemodellist.h" + +#include "parserbase.h" + +using namespace Diff2; + +ParserBase::ParserBase( const KompareModelList* list, const QStringList& diff ) : + m_diffLines( diff ), + m_currentModel( 0 ), + m_models( 0 ), + m_diffIterator( m_diffLines.begin() ), + m_singleFileDiff( false ), + m_list( list ) +{ +// kdDebug(8101) << diff << endl; +// kdDebug(8101) << m_diffLines << endl; + m_models = new DiffModelList(); + + // used in contexthunkheader + m_contextHunkHeader1.setPattern( "\\*{15} ?(.*)\\n" ); // capture is for function name + m_contextHunkHeader2.setPattern( "\\*\\*\\* ([0-9]+),([0-9]+) \\*\\*\\*\\*\\n" ); + // used in contexthunkbody + m_contextHunkHeader3.setPattern( "--- ([0-9]+),([0-9]+) ----\\n" ); + + m_contextHunkBodyRemoved.setPattern( "- (.*)" ); + m_contextHunkBodyAdded.setPattern ( "\\+ (.*)" ); + m_contextHunkBodyChanged.setPattern( "! (.*)" ); + m_contextHunkBodyContext.setPattern( " (.*)" ); + m_contextHunkBodyLine.setPattern ( "[-\\+! ] (.*)" ); + + // This regexp sucks... i'll see what happens + m_normalDiffHeader.setPattern( "diff (?:(?:-|--)[a-zA-Z0-9=\\\"]+ )*(?:|-- +)(.*) +(.*)\\n" ); + + m_normalHunkHeaderAdded.setPattern ( "([0-9]+)a([0-9]+)(|,[0-9]+)(.*)\\n" ); + m_normalHunkHeaderRemoved.setPattern( "([0-9]+)(|,[0-9]+)d([0-9]+)(.*)\\n" ); + m_normalHunkHeaderChanged.setPattern( "([0-9]+)(|,[0-9]+)c([0-9]+)(|,[0-9]+)(.*)\\n" ); + + m_normalHunkBodyRemoved.setPattern ( "< (.*)" ); + m_normalHunkBodyAdded.setPattern ( "> (.*)" ); + m_normalHunkBodyDivider.setPattern ( "---" ); + + m_unifiedDiffHeader1.setPattern ( "--- ([^\\t]+)(?:\\t([^\\t]+)(?:\\t?)(.*))?\\n" ); + m_unifiedDiffHeader2.setPattern ( "\\+\\+\\+ ([^\\t]+)(?:\\t([^\\t]+)(?:\\t?)(.*))?\\n" ); + m_unifiedHunkHeader.setPattern ( "@@ -([0-9]+)(|,([0-9]+)) \\+([0-9]+)(|,([0-9]+)) @@(?: ?)(.*)\\n" ); + m_unifiedHunkBodyAdded.setPattern ( "\\+(.*)" ); + m_unifiedHunkBodyRemoved.setPattern( "-(.*)" ); + m_unifiedHunkBodyContext.setPattern( " (.*)" ); + m_unifiedHunkBodyLine.setPattern ( "([-+ ])(.*)" ); +} + +ParserBase::~ParserBase() +{ + if ( m_models ) + m_models = 0; // dont delete this, i pass it around... +} + +enum Kompare::Format ParserBase::determineFormat() +{ + // Write your own format detection routine damn it :) + return Kompare::UnknownFormat; +} + +DiffModelList* ParserBase::parse() +{ + switch( determineFormat() ) + { + case Kompare::Context : + return parseContext(); + case Kompare::Ed : + return parseEd(); + case Kompare::Normal : + return parseNormal(); + case Kompare::RCS : + return parseRCS(); + case Kompare::Unified : + return parseUnified(); + default: // Unknown and SideBySide for now + return 0L; + } +} + +bool ParserBase::parseContextDiffHeader() +{ +// kdDebug(8101) << "ParserBase::parseContextDiffHeader()" << endl; + bool result = false; + + while ( m_diffIterator != m_diffLines.end() ) + { + if ( !m_contextDiffHeader1.exactMatch( *(m_diffIterator)++ ) ) + { + continue; + } +// kdDebug(8101) << "Matched length Header1 = " << m_contextDiffHeader1.matchedLength() << endl; +// kdDebug(8101) << "Matched string Header1 = " << m_contextDiffHeader1.cap( 0 ) << endl; + if ( m_diffIterator != m_diffLines.end() && m_contextDiffHeader2.exactMatch( *m_diffIterator ) ) + { +// kdDebug(8101) << "Matched length Header2 = " << m_contextDiffHeader2.matchedLength() << endl; +// kdDebug(8101) << "Matched string Header2 = " << m_contextDiffHeader2.cap( 0 ) << endl; + + m_currentModel = new DiffModel( m_contextDiffHeader1.cap( 1 ), m_contextDiffHeader2.cap( 1 ) ); + QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) ); + m_currentModel->setSourceTimestamp ( m_contextDiffHeader1.cap( 2 ) ); + m_currentModel->setSourceRevision ( m_contextDiffHeader1.cap( 4 ) ); + m_currentModel->setDestinationTimestamp( m_contextDiffHeader2.cap( 2 ) ); + m_currentModel->setDestinationRevision ( m_contextDiffHeader2.cap( 4 ) ); + + ++m_diffIterator; + result = true; + + break; + } + else + { + // We're screwed, second line does not match or is not there... + break; + } + // Dont inc the Iterator because the second line might be the first line of + // the context header and the first hit was a fluke (impossible imo) + // maybe we should return false here because the diff is broken ? + } + + return result; +} + +bool ParserBase::parseEdDiffHeader() +{ + return false; +} + +bool ParserBase::parseNormalDiffHeader() +{ +// kdDebug(8101) << "ParserBase::parseNormalDiffHeader()" << endl; + bool result = false; + + while ( m_diffIterator != m_diffLines.end() ) + { + if ( m_normalDiffHeader.exactMatch( *m_diffIterator ) ) + { +// kdDebug(8101) << "Matched length Header = " << m_normalDiffHeader.matchedLength() << endl; +// kdDebug(8101) << "Matched string Header = " << m_normalDiffHeader.cap( 0 ) << endl; + + m_currentModel = new DiffModel(); + QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) ); + m_currentModel->setSourceFile ( m_normalDiffHeader.cap( 1 ) ); + m_currentModel->setDestinationFile ( m_normalDiffHeader.cap( 2 ) ); + + result = true; + + ++m_diffIterator; + break; + } + else + { + kdDebug(8101) << "No match for: " << ( *m_diffIterator ) << endl; + } + ++m_diffIterator; + } + + if ( result == false ) + { + // Set this to the first line again and hope it is a single file diff + m_diffIterator = m_diffLines.begin(); + m_currentModel = new DiffModel(); + QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) ); + m_singleFileDiff = true; + } + + return result; +} + +bool ParserBase::parseRCSDiffHeader() +{ + return false; +} + +bool ParserBase::parseUnifiedDiffHeader() +{ +// kdDebug(8101) << "ParserBase::parseUnifiedDiffHeader()" << endl; + bool result = false; + + while ( m_diffIterator != m_diffLines.end() ) // dont assume we start with the diffheader1 line + { + if ( !m_unifiedDiffHeader1.exactMatch( *m_diffIterator ) ) + { + ++m_diffIterator; + continue; + } +// kdDebug(8101) << "Matched length Header1 = " << m_unifiedDiffHeader1.matchedLength() << endl; +// kdDebug(8101) << "Matched string Header1 = " << m_unifiedDiffHeader1.cap( 0 ) << endl; + ++m_diffIterator; + if ( m_diffIterator != m_diffLines.end() && m_unifiedDiffHeader2.exactMatch( *m_diffIterator ) ) + { + m_currentModel = new DiffModel( m_unifiedDiffHeader1.cap( 1 ), m_unifiedDiffHeader2.cap( 1 ) ); + QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) ); + m_currentModel->setSourceTimestamp( m_unifiedDiffHeader1.cap( 2 ) ); + m_currentModel->setSourceRevision( m_unifiedDiffHeader1.cap( 4 ) ); + m_currentModel->setDestinationTimestamp( m_unifiedDiffHeader2.cap( 2 ) ); + m_currentModel->setDestinationRevision( m_unifiedDiffHeader2.cap( 4 ) ); + + ++m_diffIterator; + result = true; + + break; + } + else + { + // We're screwed, second line does not match or is not there... + break; + } + } + + return result; +} + +bool ParserBase::parseContextHunkHeader() +{ +// kdDebug(8101) << "ParserBase::parseContextHunkHeader()" << endl; + + if ( m_diffIterator == m_diffLines.end() ) + return false; + + if ( !m_contextHunkHeader1.exactMatch( *(m_diffIterator) ) ) + return false; // big fat trouble, aborting... + + ++m_diffIterator; + + if ( m_diffIterator == m_diffLines.end() ) + return false; + + if ( !m_contextHunkHeader2.exactMatch( *(m_diffIterator) ) ) + return false; // big fat trouble, aborting... + + ++m_diffIterator; + + return true; +} + +bool ParserBase::parseEdHunkHeader() +{ + return false; +} + +bool ParserBase::parseNormalHunkHeader() +{ +// kdDebug(8101) << "ParserBase::parseNormalHunkHeader()" << endl; + if ( m_diffIterator != m_diffLines.end() ) + { +// kdDebug(8101) << "Header = " << *m_diffIterator << endl; + if ( m_normalHunkHeaderAdded.exactMatch( *m_diffIterator ) ) + { + m_normalDiffType = Difference::Insert; + } + else if ( m_normalHunkHeaderRemoved.exactMatch( *m_diffIterator ) ) + { + m_normalDiffType = Difference::Delete; + } + else if ( m_normalHunkHeaderChanged.exactMatch( *m_diffIterator ) ) + { + m_normalDiffType = Difference::Change; + } + else + return false; + + ++m_diffIterator; + return true; + } + + return false; +} + +bool ParserBase::parseRCSHunkHeader() +{ + return false; +} + +bool ParserBase::parseUnifiedHunkHeader() +{ +// kdDebug(8101) << "ParserBase::parseUnifiedHunkHeader()" << endl; + + if ( m_unifiedHunkHeader.exactMatch( *m_diffIterator ) ) + { + ++m_diffIterator; + return true; + } + else + { +// kdDebug(8101) << "This is not a unified hunk header : " << (*m_diffIterator) << endl; + return false; + } + +} + +bool ParserBase::parseContextHunkBody() +{ +// kdDebug(8101) << "ParserBase::parseContextHunkBody()" << endl; + + // Storing the src part of the hunk for later use + QStringList oldLines; + for( ; m_diffIterator != m_diffLines.end() && m_contextHunkBodyLine.exactMatch( *m_diffIterator ); ++m_diffIterator ) { +// kdDebug(8101) << "Added old line: " << *m_diffIterator << endl; + oldLines.append( *m_diffIterator ); + } + + if( !m_contextHunkHeader3.exactMatch( *m_diffIterator ) ) + return false; + + ++m_diffIterator; + + // Storing the dest part of the hunk for later use + QStringList newLines; + for( ; m_diffIterator != m_diffLines.end() && m_contextHunkBodyLine.exactMatch( *m_diffIterator ); ++m_diffIterator ) { +// kdDebug(8101) << "Added new line: " << *m_diffIterator << endl; + newLines.append( *m_diffIterator ); + } + + QString function = m_contextHunkHeader1.cap( 1 ); +// kdDebug(8101) << "Captured function: " << function << endl; + int linenoA = m_contextHunkHeader2.cap( 1 ).toInt(); +// kdDebug(8101) << "Source line number: " << linenoA << endl; + int linenoB = m_contextHunkHeader3.cap( 1 ).toInt(); +// kdDebug(8101) << "Dest line number: " << linenoB << endl; + + DiffHunk* hunk = new DiffHunk( linenoA, linenoB, function ); + + m_currentModel->addHunk( hunk ); + + QStringList::Iterator oldIt = oldLines.begin(); + QStringList::Iterator newIt = newLines.begin(); + + Difference* diff; + while( oldIt != oldLines.end() || newIt != newLines.end() ) + { + if( oldIt != oldLines.end() && m_contextHunkBodyRemoved.exactMatch( *oldIt ) ) + { +// kdDebug(8101) << "Delete: " << endl; + diff = new Difference( linenoA, linenoB ); + diff->setType( Difference::Delete ); + m_currentModel->addDiff( diff ); +// kdDebug(8101) << "Difference added" << endl; + hunk->add( diff ); + for( ; oldIt != oldLines.end() && m_contextHunkBodyRemoved.exactMatch( *oldIt ); ++oldIt ) + { +// kdDebug(8101) << " " << m_contextHunkBodyRemoved.cap( 1 ) << endl; + diff->addSourceLine( m_contextHunkBodyRemoved.cap( 1 ) ); + linenoA++; + } + } + else if( newIt != newLines.end() && m_contextHunkBodyAdded.exactMatch( *newIt ) ) + { +// kdDebug(8101) << "Insert: " << endl; + diff = new Difference( linenoA, linenoB ); + diff->setType( Difference::Insert ); + m_currentModel->addDiff( diff ); +// kdDebug(8101) << "Difference added" << endl; + hunk->add( diff ); + for( ; newIt != newLines.end() && m_contextHunkBodyAdded.exactMatch( *newIt ); ++newIt ) + { +// kdDebug(8101) << " " << m_contextHunkBodyAdded.cap( 1 ) << endl; + diff->addDestinationLine( m_contextHunkBodyAdded.cap( 1 ) ); + linenoB++; + } + } + else if( ( oldIt == oldLines.end() || m_contextHunkBodyContext.exactMatch( *oldIt ) ) && + ( newIt == newLines.end() || m_contextHunkBodyContext.exactMatch( *newIt ) ) ) + { +// kdDebug(8101) << "Unchanged: " << endl; + diff = new Difference( linenoA, linenoB ); + // Dont add this diff with addDiff to the model... no unchanged differences allowed in there... + diff->setType( Difference::Unchanged ); + hunk->add( diff ); + while( ( oldIt == oldLines.end() || m_contextHunkBodyContext.exactMatch( *oldIt ) ) && + ( newIt == newLines.end() || m_contextHunkBodyContext.exactMatch( *newIt ) ) && + ( oldIt != oldLines.end() || newIt != newLines.end() ) ) + { + QString l; + if( oldIt != oldLines.end() ) + { + l = m_contextHunkBodyContext.cap( 1 ); +// kdDebug(8101) << "old: " << l << endl; + ++oldIt; + } + if( newIt != newLines.end() ) + { + l = m_contextHunkBodyContext.cap( 1 ); +// kdDebug(8101) << "new: " << l << endl; + ++newIt; + } + diff->addSourceLine( l ); + diff->addDestinationLine( l ); + linenoA++; + linenoB++; + } + } + else if( ( oldIt != oldLines.end() && m_contextHunkBodyChanged.exactMatch( *oldIt ) ) || + ( newIt != newLines.end() && m_contextHunkBodyChanged.exactMatch( *newIt ) ) ) + { +// kdDebug(8101) << "Changed: " << endl; + diff = new Difference( linenoA, linenoB ); + diff->setType( Difference::Change ); + m_currentModel->addDiff( diff ); +// kdDebug(8101) << "Difference added" << endl; + hunk->add( diff ); + while( oldIt != oldLines.end() && m_contextHunkBodyChanged.exactMatch( *oldIt ) ) + { +// kdDebug(8101) << " " << m_contextHunkBodyChanged.cap( 1 ) << endl; + diff->addSourceLine( m_contextHunkBodyChanged.cap( 1 ) ); + linenoA++; + ++oldIt; + } + while( newIt != newLines.end() && m_contextHunkBodyChanged.exactMatch( *newIt ) ) + { +// kdDebug(8101) << " " << m_contextHunkBodyChanged.cap( 1 ) << endl; + diff->addDestinationLine( m_contextHunkBodyChanged.cap( 1 ) ); + linenoB++; + ++newIt; + } + } + else + return false; + diff->determineInlineDifferences(); + } + + return true; +} + +bool ParserBase::parseEdHunkBody() +{ + return false; +} + +bool ParserBase::parseNormalHunkBody() +{ +// kdDebug(8101) << "ParserBase::parseNormalHunkBody" << endl; + + QString type = QString::null; + + int linenoA = 0, linenoB = 0; + + if ( m_normalDiffType == Difference::Insert ) + { + linenoA = m_normalHunkHeaderAdded.cap( 1 ).toInt(); + linenoB = m_normalHunkHeaderAdded.cap( 2 ).toInt(); + } + else if ( m_normalDiffType == Difference::Delete ) + { + linenoA = m_normalHunkHeaderRemoved.cap( 1 ).toInt(); + linenoB = m_normalHunkHeaderRemoved.cap( 3 ).toInt(); + } + else if ( m_normalDiffType == Difference::Change ) + { + linenoA = m_normalHunkHeaderChanged.cap( 1 ).toInt(); + linenoB = m_normalHunkHeaderChanged.cap( 3 ).toInt(); + } + + DiffHunk* hunk = new DiffHunk( linenoA, linenoB ); + m_currentModel->addHunk( hunk ); + Difference* diff = new Difference( linenoA, linenoB ); + hunk->add( diff ); + m_currentModel->addDiff( diff ); + + diff->setType( m_normalDiffType ); + + if ( m_normalDiffType == Difference::Change || m_normalDiffType == Difference::Delete ) + for( ; m_diffIterator != m_diffLines.end() && m_normalHunkBodyRemoved.exactMatch( *m_diffIterator ); ++m_diffIterator ) + { +// kdDebug(8101) << "Line = " << *m_diffIterator << endl; + diff->addSourceLine( m_normalHunkBodyRemoved.cap( 1 ) ); + } + if ( m_normalDiffType == Difference::Change ) + if( m_diffIterator != m_diffLines.end() && m_normalHunkBodyDivider.exactMatch( *m_diffIterator ) ) + { +// kdDebug(8101) << "Line = " << *m_diffIterator << endl; + ++m_diffIterator; + } + else + return false; + if ( m_normalDiffType == Difference::Insert || m_normalDiffType == Difference::Change ) + for( ; m_diffIterator != m_diffLines.end() && m_normalHunkBodyAdded.exactMatch( *m_diffIterator ); ++m_diffIterator ) + { +// kdDebug(8101) << "Line = " << *m_diffIterator << endl; + diff->addDestinationLine( m_normalHunkBodyAdded.cap( 1 ) ); + } + + return true; +} + +bool ParserBase::parseRCSHunkBody() +{ + return false; +} + +bool ParserBase::matchesUnifiedHunkLine( QString line ) const +{ + static const QChar context( ' ' ); + static const QChar added ( '+' ); + static const QChar removed( '-' ); + + QChar first = line[0]; + + return ( first == context || first == added || first == removed ); +} + +bool ParserBase::parseUnifiedHunkBody() +{ +// kdDebug(8101) << "ParserBase::parseUnifiedHunkBody" << endl; + + int linenoA = 0, linenoB = 0; + bool wasNum; + + // Fetching the stuff we need from the hunkheader regexp that was parsed in parseUnifiedHunkHeader(); + linenoA = m_unifiedHunkHeader.cap( 1 ).toInt(); + if( !m_unifiedHunkHeader.cap( 3 ).isEmpty() && m_unifiedHunkHeader.cap( 3 ).toInt(&wasNum) == 0 ) { + // If a hunk is an insertion or deletion with no context, the line number given + // is the one before the hunk. this isn't what we want, so increment it to fix this. + if( wasNum == false ) + return false; + linenoA++; + } + linenoB = m_unifiedHunkHeader.cap( 4 ).toInt(); + if( !m_unifiedHunkHeader.cap( 6 ).isEmpty() && m_unifiedHunkHeader.cap( 6 ).toInt(&wasNum) == 0 ) { + // see above + if( wasNum == false ) + return false; + linenoB++; + } + QString function = m_unifiedHunkHeader.cap( 7 ); + for ( int i = 0; i < 9; i++ ) + { +// kdDebug(8101) << "Capture " << i << ": " << m_unifiedHunkHeader.cap( i ) << endl; + } + + DiffHunk* hunk = new DiffHunk( linenoA, linenoB, function ); + m_currentModel->addHunk( hunk ); + + const QStringList::ConstIterator m_diffLinesEnd = m_diffLines.end(); + + const QString context = QString( " " ); + const QString added = QString( "+" ); + const QString removed = QString( "-" ); + + while( m_diffIterator != m_diffLinesEnd && matchesUnifiedHunkLine( *m_diffIterator ) ) + { + Difference* diff = new Difference( linenoA, linenoB ); + hunk->add( diff ); + + if( (*m_diffIterator).startsWith( context ) ) + { // context + for( ; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith( context ); ++m_diffIterator ) + { + diff->addSourceLine( QString( *m_diffIterator ).remove( 0, 1 ) ); + diff->addDestinationLine( QString( *m_diffIterator ).remove( 0, 1 ) ); + linenoA++; + linenoB++; + } + } + else + { // This is a real difference, not context + for( ; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith( removed ); ++m_diffIterator ) + { + diff->addSourceLine( QString( *m_diffIterator ).remove( 0, 1 ) ); + linenoA++; + } + for( ; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith( added ); ++m_diffIterator ) + { + diff->addDestinationLine( QString( *m_diffIterator ).remove( 0, 1 ) ); + linenoB++; + } + if ( diff->sourceLineCount() == 0 ) + { + diff->setType( Difference::Insert ); +// kdDebug(8101) << "Insert difference" << endl; + } + else if ( diff->destinationLineCount() == 0 ) + { + diff->setType( Difference::Delete ); +// kdDebug(8101) << "Delete difference" << endl; + } + else + { + diff->setType( Difference::Change ); +// kdDebug(8101) << "Change difference" << endl; + } + diff->determineInlineDifferences(); + m_currentModel->addDiff( diff ); + } + } + + return true; +} + +DiffModelList* ParserBase::parseContext() +{ + while ( parseContextDiffHeader() ) + { + while ( parseContextHunkHeader() ) + parseContextHunkBody(); + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + } + + m_models->sort(); + + if ( m_models->count() > 0 ) + { + return m_models; + } + else + { + delete m_models; + return 0L; + } +} + +DiffModelList* ParserBase::parseEd() +{ + while ( parseEdDiffHeader() ) + { + while ( parseEdHunkHeader() ) + parseEdHunkBody(); + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + } + + m_models->sort(); + + if ( m_models->count() > 0 ) + { + return m_models; + } + else + { + delete m_models; + return 0L; + } +} + +DiffModelList* ParserBase::parseNormal() +{ + while ( parseNormalDiffHeader() ) + { + while ( parseNormalHunkHeader() ) + parseNormalHunkBody(); + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + } + + if ( m_singleFileDiff ) + { + while ( parseNormalHunkHeader() ) + parseNormalHunkBody(); + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + } + + m_models->sort(); + + if ( m_models->count() > 0 ) + { + return m_models; + } + else + { + delete m_models; + return 0L; + } +} + +DiffModelList* ParserBase::parseRCS() +{ + while ( parseRCSDiffHeader() ) + { + while ( parseRCSHunkHeader() ) + parseRCSHunkBody(); + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + } + + m_models->sort(); + + if ( m_models->count() > 0 ) + { + return m_models; + } + else + { + delete m_models; + return 0L; + } +} + +DiffModelList* ParserBase::parseUnified() +{ + while ( parseUnifiedDiffHeader() ) + { + while ( parseUnifiedHunkHeader() ) + parseUnifiedHunkBody(); +// kdDebug(8101) << "New model ready to be analyzed..." << endl; +// kdDebug(8101) << " differenceCount() == " << m_currentModel->differenceCount() << endl; + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + } + + m_models->sort(); + + if ( m_models->count() > 0 ) + { + return m_models; + } + else + { + delete m_models; + return 0L; + } +} + diff --git a/kompare/libdiff2/parserbase.h b/kompare/libdiff2/parserbase.h new file mode 100644 index 00000000..5e08803e --- /dev/null +++ b/kompare/libdiff2/parserbase.h @@ -0,0 +1,133 @@ +/************************************************************************** +** parserbase.h +** ------------------- +** begin : Tue Jul 30 23:53:52 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#ifndef _DIFF2_PARSERBASE_H +#define _DIFF2_PARSERBASE_H + +#include + +#include "kompare.h" +#include "difference.h" +#include "diffmodellist.h" + +class QStringList; +class QString; + +namespace Diff2 +{ + +class KompareModelList; + +class ParserBase +{ +public: + ParserBase( const KompareModelList* list, const QStringList& diff ); + virtual ~ParserBase(); + +public: + enum Kompare::Format format() { return determineFormat(); }; + DiffModelList* parse(); + +protected: + virtual bool parseContextDiffHeader(); + virtual bool parseEdDiffHeader(); + virtual bool parseNormalDiffHeader(); + virtual bool parseRCSDiffHeader(); + virtual bool parseUnifiedDiffHeader(); + + virtual bool parseContextHunkHeader(); + virtual bool parseEdHunkHeader(); + virtual bool parseNormalHunkHeader(); + virtual bool parseRCSHunkHeader(); + virtual bool parseUnifiedHunkHeader(); + + virtual bool parseContextHunkBody(); + virtual bool parseEdHunkBody(); + virtual bool parseNormalHunkBody(); + virtual bool parseRCSHunkBody(); + virtual bool parseUnifiedHunkBody(); + + virtual DiffModelList* parseContext(); + virtual DiffModelList* parseEd(); + virtual DiffModelList* parseNormal(); + virtual DiffModelList* parseRCS(); + virtual DiffModelList* parseUnified(); + +protected: // Helper methods to speed things up + bool matchesUnifiedHunkLine( QString line ) const; + +protected: + /** What is format of the diff */ + virtual enum Kompare::Format determineFormat(); + +protected: + // Regexps for context parsing + QRegExp m_contextDiffHeader1; + QRegExp m_contextDiffHeader2; + + QRegExp m_contextHunkHeader1; + QRegExp m_contextHunkHeader2; + QRegExp m_contextHunkHeader3; + + QRegExp m_contextHunkBodyRemoved; + QRegExp m_contextHunkBodyAdded; + QRegExp m_contextHunkBodyChanged; + QRegExp m_contextHunkBodyContext; + QRegExp m_contextHunkBodyLine; // Added for convenience + + // Regexps for normal parsing + QRegExp m_normalDiffHeader; + + QRegExp m_normalHunkHeaderAdded; + QRegExp m_normalHunkHeaderRemoved; + QRegExp m_normalHunkHeaderChanged; + + QRegExp m_normalHunkBodyRemoved; + QRegExp m_normalHunkBodyAdded; + QRegExp m_normalHunkBodyDivider; + + enum Difference::Type m_normalDiffType; + + // RegExps for rcs parsing + QRegExp m_rcsDiffHeader; + + // Regexps for unified parsing + QRegExp m_unifiedDiffHeader1; + QRegExp m_unifiedDiffHeader2; + + QRegExp m_unifiedHunkHeader; + + QRegExp m_unifiedHunkBodyAdded; + QRegExp m_unifiedHunkBodyRemoved; + QRegExp m_unifiedHunkBodyContext; + QRegExp m_unifiedHunkBodyLine; // Added for convenience + +protected: + const QStringList& m_diffLines; + DiffModel* m_currentModel; + DiffModelList* m_models; + QStringList::ConstIterator m_diffIterator; + + bool m_singleFileDiff; + +protected: + const KompareModelList* m_list; +}; + +} // End of namespace Diff2 + +#endif diff --git a/kompare/libdiff2/perforceparser.cpp b/kompare/libdiff2/perforceparser.cpp new file mode 100644 index 00000000..907d88ff --- /dev/null +++ b/kompare/libdiff2/perforceparser.cpp @@ -0,0 +1,223 @@ +/************************************************************************** +** perforceparser.cpp +** ------------------ +** begin : Sun Aug 4 15:05:35 2002 +** copyright : (C) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#include + +#include + +#include "perforceparser.h" + +using namespace Diff2; + +PerforceParser::PerforceParser( const KompareModelList* list, const QStringList& diff ) : ParserBase( list, diff ) +{ + m_contextDiffHeader1.setPattern( "==== (.*) - (.*) ====\\n" ); + m_contextDiffHeader1.setMinimal( true ); + m_normalDiffHeader.setPattern ( "==== (.*) - (.*) ====\\n" ); + m_normalDiffHeader.setMinimal ( true ); + m_rcsDiffHeader.setPattern ( "==== (.*) - (.*) ====\\n" ); + m_rcsDiffHeader.setMinimal ( true ); + m_unifiedDiffHeader1.setPattern( "==== (.*) - (.*) ====\\n" ); + m_unifiedDiffHeader1.setMinimal( true ); +} + +PerforceParser::~PerforceParser() +{ +} + +enum Kompare::Format PerforceParser::determineFormat() +{ + kdDebug(8101) << "Determining the format of the Perforce Diff" << endl; + + QRegExp unifiedRE( "^@@" ); + QRegExp contextRE( "^\\*{15}" ); + QRegExp normalRE ( "^\\d+(|,\\d+)[acd]\\d+(|,\\d+)" ); + QRegExp rcsRE ( "^[acd]\\d+ \\d+" ); + // Summary is not supported since it gives no useful parsable info + + QStringList::ConstIterator it = m_diffLines.begin(); + + while( it != m_diffLines.end() ) + { + if( (*it).find( unifiedRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from a Unified diff..." << endl; + return Kompare::Unified; + } + else if( (*it).find( contextRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from a Context diff..." << endl; + return Kompare::Context; + } + else if( (*it).find( normalRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from a Normal diff..." << endl; + return Kompare::Normal; + } + else if( (*it).find( rcsRE, 0 ) == 0 ) + { + kdDebug(8101) << "Difflines are from a RCS diff..." << endl; + return Kompare::RCS; + } + ++it; + } + kdDebug(8101) << "Difflines are from an unknown diff..." << endl; + return Kompare::UnknownFormat; +} + +bool PerforceParser::parseContextDiffHeader() +{ +// kdDebug(8101) << "ParserBase::parseContextDiffHeader()" << endl; + bool result = false; + + QStringList::ConstIterator itEnd = m_diffLines.end(); + + QRegExp sourceFileRE ( "([^\\#]+)#(\\d+)" ); + QRegExp destinationFileRE( "([^\\#]+)#(|\\d+)" ); + + while ( m_diffIterator != itEnd ) + { + if ( m_contextDiffHeader1.exactMatch( *(m_diffIterator)++ ) ) + { +// kdDebug(8101) << "Matched length Header1 = " << m_contextDiffHeader1.matchedLength() << endl; +// kdDebug(8101) << "Matched string Header1 = " << m_contextDiffHeader1.cap( 0 ) << endl; +// kdDebug(8101) << "First capture Header1 = " << m_contextDiffHeader1.cap( 1 ) << endl; +// kdDebug(8101) << "Second capture Header1 = " << m_contextDiffHeader1.cap( 2 ) << endl; + + m_currentModel = new DiffModel(); + sourceFileRE.exactMatch( m_contextDiffHeader1.cap( 1 ) ); + destinationFileRE.exactMatch( m_contextDiffHeader1.cap( 2 ) ); + kdDebug(8101) << "Matched length = " << sourceFileRE.matchedLength() << endl; + kdDebug(8101) << "Matched length = " << destinationFileRE.matchedLength() << endl; + kdDebug(8101) << "Captured texts = " << sourceFileRE.capturedTexts() << endl; + kdDebug(8101) << "Captured texts = " << destinationFileRE.capturedTexts() << endl; + kdDebug(8101) << "Source File : " << sourceFileRE.cap( 1 ) << endl; + kdDebug(8101) << "Destination File : " << destinationFileRE.cap( 1 ) << endl; + m_currentModel->setSourceFile ( sourceFileRE.cap( 1 ) ); + m_currentModel->setDestinationFile( destinationFileRE.cap( 1 ) ); + + result = true; + + break; + } + else + { + kdDebug(8101) << "Matched length = " << m_contextDiffHeader1.matchedLength() << endl; + kdDebug(8101) << "Captured texts = " << m_contextDiffHeader1.capturedTexts() << endl; + } + } + + return result; +} + +bool PerforceParser::parseNormalDiffHeader() +{ + bool result = false; + + QStringList::ConstIterator itEnd = m_diffLines.end(); + + QRegExp sourceFileRE ( "([^\\#]+)#(\\d+)" ); + QRegExp destinationFileRE( "([^\\#]+)#(|\\d+)" ); + + while ( m_diffIterator != itEnd ) + { + kdDebug(8101) << "Line = " << *m_diffIterator << endl; + kdDebug(8101) << "String length = " << (*m_diffIterator).length() << endl; + if ( m_normalDiffHeader.exactMatch( *(m_diffIterator)++ ) ) + { + kdDebug(8101) << "Matched length Header1 = " << m_normalDiffHeader.matchedLength() << endl; + kdDebug(8101) << "Matched string Header1 = " << m_normalDiffHeader.cap( 0 ) << endl; + kdDebug(8101) << "First capture Header1 = \"" << m_normalDiffHeader.cap( 1 ) << "\"" << endl; + kdDebug(8101) << "Second capture Header1 = \"" << m_normalDiffHeader.cap( 2 ) << "\"" << endl; + + m_currentModel = new DiffModel(); + sourceFileRE.exactMatch( m_normalDiffHeader.cap( 1 ) ); + destinationFileRE.exactMatch( m_normalDiffHeader.cap( 2 ) ); + kdDebug(8101) << "Matched length = " << sourceFileRE.matchedLength() << endl; + kdDebug(8101) << "Matched length = " << destinationFileRE.matchedLength() << endl; + kdDebug(8101) << "Captured texts = " << sourceFileRE.capturedTexts() << endl; + kdDebug(8101) << "Captured texts = " << destinationFileRE.capturedTexts() << endl; + kdDebug(8101) << "Source File : " << sourceFileRE.cap( 1 ) << endl; + kdDebug(8101) << "Destination File : " << destinationFileRE.cap( 1 ) << endl; + m_currentModel->setSourceFile ( sourceFileRE.cap( 1 ) ); + m_currentModel->setDestinationFile( destinationFileRE.cap( 1 ) ); + + result = true; + + break; + } + else + { + kdDebug(8101) << "Matched length = " << m_normalDiffHeader.matchedLength() << endl; + kdDebug(8101) << "Captured texts = " << m_normalDiffHeader.capturedTexts() << endl; + } + } + + return result; +} + +bool PerforceParser::parseRCSDiffHeader() +{ + return false; +} + +bool PerforceParser::parseUnifiedDiffHeader() +{ + bool result = false; + + QStringList::ConstIterator itEnd = m_diffLines.end(); + + QRegExp sourceFileRE ( "([^\\#]+)#(\\d+)" ); + QRegExp destinationFileRE( "([^\\#]+)#(|\\d+)" ); + + while ( m_diffIterator != itEnd ) + { +// kdDebug(8101) << "Line = " << *m_diffIterator << endl; +// kdDebug(8101) << "String length = " << (*m_diffIterator).length() << endl; + if ( m_unifiedDiffHeader1.exactMatch( *(m_diffIterator)++ ) ) + { +// kdDebug(8101) << "Matched length Header1 = " << m_unifiedDiffHeader1.matchedLength() << endl; +// kdDebug(8101) << "Matched string Header1 = " << m_unifiedDiffHeader1.cap( 0 ) << endl; +// kdDebug(8101) << "First capture Header1 = \"" << m_unifiedDiffHeader1.cap( 1 ) << "\"" << endl; +// kdDebug(8101) << "Second capture Header1 = \"" << m_unifiedDiffHeader1.cap( 2 ) << "\"" << endl; + + m_currentModel = new DiffModel(); + sourceFileRE.exactMatch( m_unifiedDiffHeader1.cap( 1 ) ); + destinationFileRE.exactMatch( m_unifiedDiffHeader1.cap( 2 ) ); +// kdDebug(8101) << "Matched length = " << sourceFileRE.matchedLength() << endl; +// kdDebug(8101) << "Matched length = " << destinationFileRE.matchedLength() << endl; +// kdDebug(8101) << "Captured texts = " << sourceFileRE.capturedTexts() << endl; +// kdDebug(8101) << "Captured texts = " << destinationFileRE.capturedTexts() << endl; +// kdDebug(8101) << "Source File : " << sourceFileRE.cap( 1 ) << endl; +// kdDebug(8101) << "Destination File : " << destinationFileRE.cap( 1 ) << endl; + m_currentModel->setSourceFile ( sourceFileRE.cap( 1 ) ); + m_currentModel->setDestinationFile( destinationFileRE.cap( 1 ) ); + + result = true; + + break; + } + else + { +// kdDebug(8101) << "Matched length = " << m_unifiedDiffHeader1.matchedLength() << endl; +// kdDebug(8101) << "Captured texts = " << m_unifiedDiffHeader1.capturedTexts() << endl; + } + } + + return result; +} + diff --git a/kompare/libdiff2/perforceparser.h b/kompare/libdiff2/perforceparser.h new file mode 100644 index 00000000..99973167 --- /dev/null +++ b/kompare/libdiff2/perforceparser.h @@ -0,0 +1,44 @@ +/************************************************************************** +** perforceparser.h +** ------------------- +** begin : Sun Sep 8 20:58:59 2002 +** copyright : (c) 2002-2004 Otto Bruggeman +** email : otto.bruggeman@home.nl +** +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** ( at your option ) any later version. +** +***************************************************************************/ + +#ifndef _PERFORCE_PARSER_H +#define _PERFORCE_PARSER_H + +#include "parserbase.h" + +namespace Diff2 +{ + +class PerforceParser : public ParserBase +{ +public: + PerforceParser( const KompareModelList* list, const QStringList& diff ); + virtual ~PerforceParser(); + +protected: + virtual bool parseContextDiffHeader(); + virtual bool parseNormalDiffHeader(); + virtual bool parseRCSDiffHeader(); + virtual bool parseUnifiedDiffHeader(); + +protected: + virtual enum Kompare::Format determineFormat(); +}; + +} // End of namespace Diff2 + +#endif diff --git a/kompare/main.cpp b/kompare/main.cpp new file mode 100644 index 00000000..00827c4d --- /dev/null +++ b/kompare/main.cpp @@ -0,0 +1,217 @@ +/*************************************************************************** + main.cpp - description + ------------------- + begin : Sun Mar 4 2001 + copyright : (C) 2001-2004 Otto Bruggeman + (C) 2001-2003 John Firebaugh + email : otto.bruggeman@home.nl + jfirebaugh@kde.org +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + + +#include +#include +#include +#include +#include +#include + +#include "kompare_part.h" +#include "kompare_shell.h" +#include "kompareurldialog.h" + +static const char description[] = + I18N_NOOP("A program to view the differences between files and optionally generate a diff" ); + +static const char version[] = "3.4"; + +static KCmdLineOptions options[] = +{ + { "c", I18N_NOOP( "This will compare URL1 with URL2" ), 0 }, + { "o", I18N_NOOP( "This will open URL1 and expect it to be diff output. URL1 can also be a '-' and then it will read from standard input. Can be used for instance for cvs diff | kompare -o -. Kompare will do a check to see if it can find the original file(s) and then blend the original file(s) into the diffoutput and show that in the viewer. -n disables the check." ), 0 }, + { "b", I18N_NOOP( "This will blend URL2 into URL1, URL2 is expected to be diff output and URL1 the file or folder that the diffoutput needs to be blended into. " ), 0 }, + { "n", I18N_NOOP( "Disables the check for automatically finding the original file(s) when using '-' as URL with the -o option." ), 0 }, + { "e ", I18N_NOOP( "Use this to specify the encoding when calling it from the command line. It will default to the local encoding if not specified." ), 0 }, + { "+[URL1 [URL2]]",0 , 0 }, + { "+-", 0, 0 }, +// { "", I18N_NOOP( "" ), 0 }, + KCmdLineLastOption +}; + +int main(int argc, char *argv[]) +{ + KAboutData aboutData( "kompare", I18N_NOOP("Kompare"), version, description, + KAboutData::License_GPL, + I18N_NOOP("(c) 2001-2004, John Firebaugh and Otto Bruggeman"), 0, "http://bruggie.dnsalias.org/kompare/" ); + aboutData.addAuthor( "John Firebaugh", I18N_NOOP("Author"), "jfirebaugh@kde.org" ); + aboutData.addAuthor( "Otto Bruggeman", I18N_NOOP("Author"), "otto.bruggeman@home.nl" ); + aboutData.addCredit( "Chris Luetchford", I18N_NOOP("Kompare icon artist"), "chris@os11.com" ); + aboutData.addCredit( "Malte Starostik", I18N_NOOP("A lot of good advice"), "malte@kde.org" ); + aboutData.addCredit( "Bernd Gehrmann", I18N_NOOP("Cervisia diff viewer"), "bernd@physik.hu-berlin.de" ); + + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineArgs::addCmdLineOptions( options ); + KApplication app; + bool difault = false; + + // see if we are starting with session management + if (app.isRestored()) + { + RESTORE(KompareShell) + } + else + { + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + KompareShell* ks; + + kdDebug( 8100 ) << "Arg Count = " << args->count() << endl; + for ( int i=0; i < args->count(); i++ ) + { + kdDebug( 8100 ) << "Argument " << (i+1) << ": " << args->arg( i ) << endl; + } + + if ( args->isSet( "e" ) ) + { + // Encoding given... + // FIXME: Need to implement this... + } + + if ( args->isSet( "o" ) ) + { + kdDebug( 8100 ) << "Option -o is set" << endl; + if ( args->count() != 1 ) + { + difault = true; + } + else + { + ks = new KompareShell(); + ks->show(); + kdDebug( 8100 ) << "OpenDiff..." << endl; + if ( ( strlen( args->arg(0) ) == 1 ) && ( *(args->arg(0)) == '-' ) ) + ks->openStdin(); + else + ks->openDiff( args->url( 0 ) ); + difault = false; + } + } + else if ( args->isSet( "c" ) ) + { + kdDebug( 8100 ) << "Option -c is set" << endl; + if ( args->count() != 2 ) + { + KCmdLineArgs::usage( "kompare" ); + difault = true; + } + else + { + ks = new KompareShell(); + ks->show(); + KURL url0 = args->url( 0 ); + kdDebug( 8100 ) << "URL0 = " << url0.url() << endl; + KURL url1 = args->url( 1 ); + kdDebug( 8100 ) << "URL1 = " << url1.url() << endl; + ks->compare( url0, url1 ); + difault = false; + } + } + else if ( args->isSet( "b" ) ) + { + kdDebug( 8100 ) << "Option -b is set" << endl; + if ( args->count() != 2 ) + { + KCmdLineArgs::usage( "kompare" ); + difault = true; + } + else + { + ks = new KompareShell(); + ks->show(); + kdDebug( 8100 ) << "blend..." << endl; + KURL url0 = args->url( 0 ); + kdDebug( 8100 ) << "URL0 = " << url0.url() << endl; + KURL url1 = args->url( 1 ); + kdDebug( 8100 ) << "URL1 = " << url1.url() << endl; + ks->blend( url0, url1 ); + difault = false; + } + } + else if ( args->count() == 1 ) + { + ks = new KompareShell(); + ks->show(); + + kdDebug( 8100 ) << "Single file. so openDiff/openStdin is only possible..." << endl; + if ( ( strlen( args->arg(0) ) == 1 && *(args->arg(0)) == '-' ) ) + ks->openStdin(); + else + ks->openDiff( args->url( 0 ) ); + + difault = false; + } + else if ( args->count() == 2 ) + { + // In this case we are assuming you want to compare files/dirs + // and not blending because that is almost impossible to detect + ks = new KompareShell(); + ks->show(); + kdDebug( 8100 ) << "Dunno, we'll have to figure it out later, trying compare for now..." << endl; + KURL url0 = args->url( 0 ); + kdDebug( 8100 ) << "URL0 = " << url0.url() << endl; + KURL url1 = args->url( 1 ); + kdDebug( 8100 ) << "URL1 = " << url1.url() << endl; + ks->compare( url0, url1 ); + difault = false; + } + else if ( args->count() == 0 ) // no options and no args + { + difault = true; + } + + if ( difault ) + { + KompareURLDialog* dialog = new KompareURLDialog(); + + dialog->setCaption( i18n("Compare Files or Folders") ); + dialog->setFirstGroupBoxTitle( i18n( "Source" ) ); + dialog->setSecondGroupBoxTitle( i18n( "Destination" ) ); + + KGuiItem compareGuiItem( i18n( "Compare" ), QString::null, i18n( "Compare these files or folder" ), i18n( "If you have entered 2 filenames or 2 folders in the fields in this dialog then this button will be enabled and pressing it will start a comparison of the entered files or folders. " ) ); + dialog->setButtonOK( compareGuiItem ); + + dialog->setGroup( "Recent Compare Files" ); + + dialog->setFirstURLRequesterMode( KFile::File|KFile::Directory|KFile::ExistingOnly ); + dialog->setSecondURLRequesterMode( KFile::File|KFile::Directory|KFile::ExistingOnly ); + + if( dialog->exec() == QDialog::Accepted ) + { + ks = new KompareShell(); + ks->show(); + ks->viewPart()->setEncoding( dialog->encoding() ); + ks->compare( dialog->getFirstURL(), dialog->getSecondURL() ); + } + else + return -1; + + delete dialog; + } + + args->clear(); + } + + return kapp->exec(); +} + +/* vim: set ts=4 sw=4 noet: */ + diff --git a/kompare/pics/Makefile.am b/kompare/pics/Makefile.am new file mode 100644 index 00000000..f8961b29 --- /dev/null +++ b/kompare/pics/Makefile.am @@ -0,0 +1,3 @@ +KDE_ICON = AUTO + + diff --git a/kompare/pics/hi128-app-kompare.png b/kompare/pics/hi128-app-kompare.png new file mode 100644 index 00000000..27d8e576 Binary files /dev/null and b/kompare/pics/hi128-app-kompare.png differ diff --git a/kompare/pics/hi16-app-kompare.png b/kompare/pics/hi16-app-kompare.png new file mode 100644 index 00000000..6dbccf11 Binary files /dev/null and b/kompare/pics/hi16-app-kompare.png differ diff --git a/kompare/pics/hi22-app-kompare.png b/kompare/pics/hi22-app-kompare.png new file mode 100644 index 00000000..55c53514 Binary files /dev/null and b/kompare/pics/hi22-app-kompare.png differ diff --git a/kompare/pics/hi32-app-kompare.png b/kompare/pics/hi32-app-kompare.png new file mode 100644 index 00000000..68011ccf Binary files /dev/null and b/kompare/pics/hi32-app-kompare.png differ diff --git a/kompare/pics/hi48-app-kompare.png b/kompare/pics/hi48-app-kompare.png new file mode 100644 index 00000000..fb04abca Binary files /dev/null and b/kompare/pics/hi48-app-kompare.png differ diff --git a/kompare/pics/hisc-app-kompare.svgz b/kompare/pics/hisc-app-kompare.svgz new file mode 100644 index 00000000..35b0fb6c Binary files /dev/null and b/kompare/pics/hisc-app-kompare.svgz differ diff --git a/kompare/tests/cvsdiff/context.diff b/kompare/tests/cvsdiff/context.diff new file mode 100644 index 00000000..c9a7f855 --- /dev/null +++ b/kompare/tests/cvsdiff/context.diff @@ -0,0 +1,83 @@ +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -c -r1.2 dcopfind.cpp +*** client/dcopfind.cpp 2001/10/31 01:17:39 1.2 +--- client/dcopfind.cpp 2002/01/16 18:07:13 +*************** +*** 36,42 **** + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +! bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +--- 36,42 ---- + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +! bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +*************** +*** 118,124 **** + f = fc; + } + +! if ( (int) types.count() != argc ) { + qWarning( "arguments do not match" ); + exit(1); + } +--- 118,124 ---- + f = fc; + } + +! if ( types.count() != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +*************** +*** 128,136 **** + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +! marshall(arg, argc, args, i, *it); + } +! if ( (int) i != argc ) { + qWarning( "arguments do not match" ); + exit(1); + } +--- 128,136 ---- + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +! marshall(arg, args, i, *it); + } +! if ( (uint) i != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +*************** +*** 221,227 **** + argc = 0; + } + +! findObject( app, objid, function, argc, args ); + + return 0; + } +--- 221,231 ---- + argc = 0; + } + +! QCStringList params; +! for( int i = 0; i < argc; i++ ) +! params.append( args[ i ] ); +! +! findObject( app, objid, function, params ); + + return 0; + } diff --git a/kompare/tests/cvsdiff/contextm.diff b/kompare/tests/cvsdiff/contextm.diff new file mode 100644 index 00000000..ef20ec4c --- /dev/null +++ b/kompare/tests/cvsdiff/contextm.diff @@ -0,0 +1,1046 @@ +Index: client/dcop.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcop.cpp,v +retrieving revision 1.26 +diff -c -r1.26 dcop.cpp +*** client/dcop.cpp 2001/10/31 01:17:39 1.26 +--- client/dcop.cpp 2002/01/16 18:06:24 +*************** +*** 20,38 **** + + ******************************************************************/ + +! #include + #include +! #include "../kdatastream.h" + #include "../dcopclient.h" + #include "../dcopref.h" +! #include +! #include +! #include + + #include "marshall.cpp" + + static DCOPClient* dcop = 0; + + bool startsWith(const QCString &id, const char *str, int n) + { + return !n || (strncmp(id.data(), str, n) == 0); +--- 20,66 ---- + + ******************************************************************/ + +! #include +! #include +! #include +! + #include +! #include +! #include +! #include +! #include +! #include +! #include +! #include +! +! // putenv() is not available on all platforms, so make sure the emulation +! // wrapper is available in those cases by loading config.h! +! #include +! + #include "../dcopclient.h" + #include "../dcopref.h" +! #include "../kdatastream.h" + + #include "marshall.cpp" + ++ typedef QMap UserList; ++ + static DCOPClient* dcop = 0; + ++ static QTextStream cout( stdout, IO_WriteOnly ); ++ static QTextStream cerr( stderr, IO_WriteOnly ); ++ ++ /** ++ * Session to send call to ++ * DefaultSession - current session. Current KDE session when called without ++ * --user or --all-users option. Otherwise this value ignores ++ * all users with more than one active session. ++ * AllSessions - Send to all sessions found. requires --user or --all-users. ++ * QuerySessions - Don't call DCOP, return a list of available sessions. ++ * CustomSession - Use the specified session ++ */ ++ enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; ++ + bool startsWith(const QCString &id, const char *str, int n) + { + return !n || (strncmp(id.data(), str, n) == 0); +*************** +*** 118,126 **** + } + } + +! void callFunction( const char* app, const char* obj, const char* func, int argc, char** args ) + { +- + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); + int right = f.find( ')' ); +--- 146,153 ---- + } + } + +! void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); + int right = f.find( ')' ); +*************** +*** 136,142 **** + bool ok = false; + QCStringList funcs = dcop->remoteFunctions( app, obj, &ok ); + QCString realfunc; +! if ( !ok && argc == 0 ) + goto doit; + if ( !ok ) + { +--- 163,169 ---- + bool ok = false; + QCStringList funcs = dcop->remoteFunctions( app, obj, &ok ); + QCString realfunc; +! if ( !ok && args.isEmpty() ) + goto doit; + if ( !ok ) + { +*************** +*** 153,167 **** + + if ( l > 0 && (*it).mid( s, l - s ) == func ) { + realfunc = (*it).mid( s ); +! int a = (*it).contains(','); +! if ( ( a == 0 && argc == 0) || ( a > 0 && a + 1 == argc ) ) + break; + } + } + if ( realfunc.isEmpty() ) + { + qWarning("no such function"); +! exit(1); + } + f = realfunc; + left = f.find( '(' ); +--- 180,195 ---- + + if ( l > 0 && (*it).mid( s, l - s ) == func ) { + realfunc = (*it).mid( s ); +! uint a = (*it).contains(','); +! if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) + break; + } + } + if ( realfunc.isEmpty() ) + { + qWarning("no such function"); +! // exit(1); +! return; + } + f = realfunc; + left = f.find( '(' ); +*************** +*** 243,253 **** + QCString replyType; + QDataStream arg(data, IO_WriteOnly); + +! int i = 0; +! for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +! marshall(arg, argc, args, i, *it); +! } +! if ( i != argc ) { + qWarning( "arguments do not match" ); + exit(1); + } +--- 271,282 ---- + QCString replyType; + QDataStream arg(data, IO_WriteOnly); + +! uint i = 0; +! for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) +! marshall( arg, args, i, *it ); +! +! if ( i != args.count() ) +! { + qWarning( "arguments do not match" ); + exit(1); + } +*************** +*** 265,343 **** + } + } + } +- + + +! int main( int argc, char** argv ) + { + +! if ( argc > 1 && argv[1][0] == '-' ) { +! fprintf( stderr, "Usage: dcop [ application [object [function [arg1] [arg2] [arg3] ... ] ] ] \n" ); +! exit(0); + } + +! DCOPClient client; +! client.attach(); +! dcop = &client; + + QCString app; + QCString objid; + QCString function; +! char **args = 0; +! if ((argc > 1) && (strncmp(argv[1], "DCOPRef(", 8)) == 0) + { +! char *delim = strchr(argv[1], ','); +! if (!delim) +! { +! fprintf(stderr, "Error: '%s' is not a valid DCOP reference.\n", argv[1]); +! return 1; +! } +! *delim = 0; +! app = argv[1] + 8; +! delim++; +! delim[strlen(delim)-1] = 0; +! objid = delim; +! if (argc > 2) +! function = argv[2]; +! if (argc > 3) +! args = &argv[3]; +! argc++; + } + else + { +! if (argc > 1) +! app = argv[1]; +! if (argc > 2) +! objid = argv[2]; +! if (argc > 3) +! function = argv[3]; +! if (argc > 4) +! args = &argv[4]; +! } +! +! switch ( argc ) { +! case 0: +! case 1: +! queryApplications(""); +! break; +! case 2: +! if (endsWith(app, '*')) +! queryApplications(app); +! else +! queryObjects( app, "" ); +! break; +! case 3: +! if (endsWith(objid, '*')) +! queryObjects(app, objid); +! else +! queryFunctions( app, objid ); +! break; +! case 4: +! default: +! callFunction( app, objid, function, argc - 4, args ); +! break; + + } + + return 0; + } +--- 294,773 ---- + } + } + } + ++ /** ++ * Show command-line help and exit ++ */ ++ void showHelp( int exitCode = 0 ) ++ { ++ cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl ++ << "" << endl ++ << "Console DCOP client" << endl ++ << "" << endl ++ << "Generic options:" << endl ++ << " --help Show help about options" << endl ++ << "" << endl ++ << "Options:" << endl ++ << " --pipe Call DCOP for each line read from stdin" << endl ++ << " --user Connect to the given user's DCOP server. This option will" << endl ++ << " ignore the values of the environment vars $DCOPSERVER and" << endl ++ << " $ICEAUTHORITY, even if they are set." << endl ++ << " If the user has more than one open session, you must also" << endl ++ << " use one of the --list-sessions, --session or --als-sessions" << endl ++ << " command-line options." << endl ++ << " --all-users Send the same DCOP call to all users with a running DCOP" << endl ++ << " server. Only failed calls to existing DCOP servers will" ++ << " generate an error message. If no DCOP server is available" << endl ++ << " at all, no error will be generated." << endl; ++ ++ exit( exitCode ); ++ } + +! /** +! * Return a list of all users and their home directories. +! * Returns an empty list if /etc/passwd cannot be read for some reason. +! */ +! static UserList userList() + { ++ UserList result; ++ ++ QFile f( "/etc/passwd" ); ++ ++ if( !f.open( IO_ReadOnly ) ) ++ { ++ cerr << "Can't open /etc/passwd for reading!" << endl; ++ return result; ++ } + +! QStringList l( QStringList::split( '\n', f.readAll() ) ); +! +! for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) +! { +! QStringList userInfo( QStringList::split( ':', *it, true ) ); +! result[ userInfo[ 0 ] ] = userInfo[ 5 ]; + } + +! return result; +! } +! +! /** +! * Return a list of available DCOP sessions for the specified user +! * An empty list means no sessions are available, or an error occurred. +! */ +! QStringList dcopSessionList( const QString &user, const QString &home ) +! { +! if( home.isEmpty() ) +! { +! cerr << "WARNING: Cannot determine home directory for user " +! << user << "!" << endl +! << "Please check permissions or set the $DCOPSERVER variable manually before" << endl +! << "calling dcop." << endl; +! return QStringList(); +! } +! +! QStringList result; +! QFileInfo dirInfo( home ); +! if( !dirInfo.exists() || !dirInfo.isReadable() ) +! return result; +! +! QDir d( home ); +! d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); +! d.setNameFilter( ".DCOPserver*" ); +! +! const QFileInfoList *list = d.entryInfoList(); +! if( !list ) +! return result; +! +! QFileInfoListIterator it( *list ); +! QFileInfo *fi; +! +! while ( ( fi = it.current() ) != 0 ) +! { +! if( fi->isReadable() ) +! result.append( fi->fileName() ); +! ++it; +! } +! return result; +! } + ++ /** ++ * Do the actual DCOP call ++ */ ++ void runDCOP( QCStringList args, UserList users, Session session, ++ const QString sessionName, bool readStdin ) ++ { + QCString app; + QCString objid; + QCString function; +! QCStringList params; +! DCOPClient *client = 0L; +! if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) + { +! // WARNING: This part (until the closing '}') could very +! // well be broken now. As I don't know how to trigger and test +! // dcoprefs this code is *not* tested. It compiles and it looks +! // ok to me, but that's all I can say - Martijn (2001/12/24) +! int delimPos = args[ 0 ].findRev( ',' ); +! if( delimPos == -1 ) +! { +! cerr << "Error: '" << args[ 0 ] +! << "' is not a valid DCOP reference." << endl; +! exit( -1 ); +! } +! args[ 0 ][ delimPos ] = 0; +! app = args[ 0 ].mid( 8 ); +! delimPos++; +! args[ 0 ][ args[ 0 ].length() - 1 ] = 0; +! objid = args[ 0 ].mid( delimPos ); +! if( args.count() > 1 ) +! function = args[ 1 ]; +! if( args.count() > 2 ) +! { +! params = args; +! params.remove( params.begin() ); +! params.remove( params.begin() ); +! } + } + else + { +! if( !args.isEmpty() ) +! app = args[ 0 ]; +! if( args.count() > 1 ) +! objid = args[ 1 ]; +! if( args.count() > 2 ) +! function = args[ 2 ]; +! if( args.count() > 3) +! { +! params = args; +! params.remove( params.begin() ); +! params.remove( params.begin() ); +! params.remove( params.begin() ); +! } +! } +! +! bool firstRun = true; +! UserList::Iterator it; +! QStringList sessions; +! bool presetDCOPServer = false; +! // char *dcopStr = 0L; +! QString dcopServer; +! +! for( it = users.begin(); it != users.end() || firstRun; it++ ) +! { +! firstRun = false; +! +! //cout << "Iterating '" << it.key() << "'" << endl; +! +! if( session == QuerySessions ) +! { +! QStringList sessions = dcopSessionList( it.key(), it.data() ); +! if( sessions.isEmpty() ) +! { +! cout << "No active sessions"; +! if( !( *it ).isEmpty() ) +! cout << " for user " << *it; +! cout << endl; +! } +! else +! { +! cout << "Active sessions "; +! if( !( *it ).isEmpty() ) +! cout << "for user " << *it << " "; +! cout << ":" << endl; +! +! QStringList::Iterator sIt; +! for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) +! cout << " " << *sIt << endl; +! +! cout << endl; +! } +! continue; +! } +! +! if( getenv( "DCOPSERVER" ) ) +! { +! sessions.append( getenv( "DCOPSERVER" ) ); +! presetDCOPServer = true; +! } +! +! if( users.count() > 1 || ( users.count() == 1 && +! ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) +! { +! sessions = dcopSessionList( it.key(), it.data() ); +! if( sessions.isEmpty() ) +! { +! if( users.count() > 1 ) +! continue; +! else +! { +! cerr << "ERROR: No active KDE sessions!" << endl +! << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl +! << "before calling dcop." << endl; +! exit( -1 ); +! } +! } +! else if( sessions.count() > 1 && session != AllSessions ) +! { +! cerr << "ERROR: Multiple available KDE sessions!" << endl +! << "Please specify the correct session to use with --session or use the" << endl +! << "--all-sessions option to broadcast to all sessions." << endl; +! exit( -1 ); +! } +! } + ++ if( users.count() > 1 || ( users.count() == 1 && ++ ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) ++ { ++ // Check for ICE authority file and if the file can be read by us ++ QString home = it.data(); ++ QString iceFile = it.data() + "/.ICEauthority"; ++ QFileInfo fi( iceFile ); ++ if( iceFile.isEmpty() ) ++ { ++ cerr << "WARNING: Cannot determine home directory for user " ++ << it.key() << "!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ else if( fi.exists() ) ++ { ++ if( fi.isReadable() ) ++ { ++ char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); ++ putenv( envStr ); ++ //cerr << "ice: " << envStr << endl; ++ } ++ else ++ { ++ cerr << "WARNING: ICE authority file " << iceFile ++ << "is not readable by you!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ } ++ else ++ { ++ if( users.count() > 1 ) ++ continue; ++ else ++ { ++ cerr << "WARNING: Cannot find ICE authority file " ++ << iceFile << "!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY" ++ << " variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ } ++ } ++ ++ // Main loop ++ // If users is an empty list we're calling for the currently logged ++ // in user. In this case we don't have a session, but still want ++ // to iterate the loop once. ++ QStringList::Iterator sIt = sessions.begin(); ++ for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) ++ { ++ if( !presetDCOPServer && !users.isEmpty() ) ++ { ++ QString dcopFile = it.data() + "/" + *sIt; ++ QFile f( dcopFile ); ++ if( !f.open( IO_ReadOnly ) ) ++ { ++ cerr << "Can't open " << dcopFile << " for reading!" << endl; ++ exit( -1 ); ++ } ++ ++ QStringList l( QStringList::split( '\n', f.readAll() ) ); ++ dcopServer = l.first(); ++ ++ if( dcopServer.isEmpty() ) ++ { ++ cerr << "WARNING: Unable to determine DCOP server for session " ++ << *sIt << "!" << endl ++ << "Please check permissions or set the $DCOPSERVER variable manually before" << endl ++ << "calling dcop." << endl; ++ exit( -1 ); ++ } ++ } ++ ++ delete client; ++ client = new DCOPClient; ++ if( !dcopServer.isEmpty() ) ++ client->setServerAddress( dcopServer.ascii() ); ++ bool success = client->attach(); ++ if( !success ) ++ { ++ cerr << "ERROR: Couldn't attach to DCOP server!" << endl; ++ continue; ++ } ++ dcop = client; ++ ++ switch ( args.count() ) ++ { ++ case 0: ++ queryApplications(""); ++ break; ++ case 1: ++ if (endsWith(app, '*')) ++ queryApplications(app); ++ else ++ queryObjects( app, "" ); ++ break; ++ case 2: ++ if (endsWith(objid, '*')) ++ queryObjects(app, objid); ++ else ++ queryFunctions( app, objid ); ++ break; ++ case 3: ++ default: ++ if( readStdin ) ++ { ++ QCStringList::Iterator replaceArg = args.end(); ++ ++ QCStringList::Iterator it; ++ for( it = args.begin(); it != args.end(); it++ ) ++ if( *it == "%1" ) ++ replaceArg = it; ++ ++ // Read from stdin until EOF and call function for each line read ++ char *buf = new char[ 1000 ]; ++ while ( !feof( stdin ) ) ++ { ++ fgets( buf, 1000, stdin ); ++ ++ if( replaceArg != args.end() ) ++ *replaceArg = buf; ++ ++ callFunction( app, objid, function, params ); ++ } ++ } ++ else ++ { ++ // Just call function ++ // cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; ++ callFunction( app, objid, function, params ); ++ } ++ break; ++ } ++ // Another sIt++ would make the loop infinite... ++ if( users.isEmpty() ) ++ break; ++ } ++ ++ // Another it++ would make the loop infinite... ++ if( it == users.end() ) ++ break; + } ++ } ++ + ++ int main( int argc, char** argv ) ++ { ++ bool readStdin = false; ++ int numOptions = 0; ++ QString user; ++ Session session = DefaultSession; ++ QString sessionName; ++ ++ // Scan for command-line options first ++ for( int pos = 1 ; pos <= argc - 1 ; pos++ ) ++ { ++ if( strcmp( argv[ pos ], "--help" ) == 0 ) ++ showHelp( 0 ); ++ else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) ++ { ++ readStdin = true; ++ numOptions++; ++ } ++ else if( strcmp( argv[ pos ], "--user" ) == 0 ) ++ { ++ if( pos <= argc - 2 ) ++ { ++ user = QString::fromLocal8Bit( argv[ pos + 1] ); ++ numOptions +=2; ++ pos++; ++ } ++ else ++ { ++ cerr << "Missing username for '--user' option!" << endl << endl; ++ showHelp( -1 ); ++ } ++ } ++ else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) ++ { ++ user = "*"; ++ numOptions ++; ++ } ++ else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) ++ { ++ session = QuerySessions; ++ numOptions ++; ++ } ++ else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) ++ { ++ session = AllSessions; ++ numOptions ++; ++ } ++ else if( argv[ pos ][ 0 ] == '-' ) ++ { ++ cerr << "Unknown command-line option '" << argv[ pos ] ++ << "'." << endl << endl; ++ showHelp( -1 ); ++ } ++ else ++ break; // End of options ++ } ++ ++ argc -= numOptions; ++ ++ QCStringList args; ++ for( int i = numOptions; i < argc + numOptions - 1; i++ ) ++ args.append( argv[ i + 1 ] ); ++ ++ if( readStdin && args.count() < 3 ) ++ { ++ cerr << "--pipe option only supported for function calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( user == "*" && args.count() < 3 && session != QuerySessions ) ++ { ++ cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session == QuerySessions && !args.isEmpty() ) ++ { ++ cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session == QuerySessions && user.isEmpty() ) ++ { ++ cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl ++ << "--all-users options!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session != DefaultSession && session != QuerySessions && ++ args.count() < 3 ) ++ { ++ cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl ++ << "calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ UserList users; ++ if( user == "*" ) ++ users = userList(); ++ else if( !user.isEmpty() ) ++ users[ user ] = userList()[ user ]; ++ ++ runDCOP( args, users, session, sessionName, readStdin ); ++ + return 0; + } ++ ++ // vim: set ts=8 sts=4 sw=4 noet: ++ +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -c -r1.2 dcopfind.cpp +*** client/dcopfind.cpp 2001/10/31 01:17:39 1.2 +--- client/dcopfind.cpp 2002/01/16 18:06:24 +*************** +*** 36,42 **** + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +! bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +--- 36,42 ---- + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +! bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +*************** +*** 118,124 **** + f = fc; + } + +! if ( (int) types.count() != argc ) { + qWarning( "arguments do not match" ); + exit(1); + } +--- 118,124 ---- + f = fc; + } + +! if ( types.count() != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +*************** +*** 128,136 **** + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +! marshall(arg, argc, args, i, *it); + } +! if ( (int) i != argc ) { + qWarning( "arguments do not match" ); + exit(1); + } +--- 128,136 ---- + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +! marshall(arg, args, i, *it); + } +! if ( (uint) i != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +*************** +*** 221,227 **** + argc = 0; + } + +! findObject( app, objid, function, argc, args ); + + return 0; + } +--- 221,231 ---- + argc = 0; + } + +! QCStringList params; +! for( int i = 0; i < argc; i++ ) +! params.append( args[ i ] ); +! +! findObject( app, objid, function, params ); + + return 0; + } +Index: client/marshall.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/marshall.cpp,v +retrieving revision 1.3 +diff -c -r1.3 marshall.cpp +*** client/marshall.cpp 2001/10/31 01:17:39 1.3 +--- client/marshall.cpp 2002/01/16 18:06:24 +*************** +*** 242,349 **** + + } + +! void marshall(QDataStream &arg, int argc, char **argv, int &i, QString type) + { +! if (type == "QStringList") +! type = "QValueList"; +! if (type == "QCStringList") +! type = "QValueList"; +! if (i >= argc) +! { +! qWarning("Not enough arguments."); +! exit(1); +! } +! QString s = QString::fromLocal8Bit(argv[i]); +! +! if ( type == "int" ) +! arg << s.toInt(); +! else if ( type == "uint" ) +! arg << s.toUInt(); +! else if ( type == "unsigned" ) +! arg << s.toUInt(); +! else if ( type == "unsigned int" ) +! arg << s.toUInt(); +! else if ( type == "long" ) +! arg << s.toLong(); +! else if ( type == "long int" ) +! arg << s.toLong(); +! else if ( type == "unsigned long" ) +! arg << s.toULong(); +! else if ( type == "unsigned long int" ) +! arg << s.toULong(); +! else if ( type == "float" ) +! arg << s.toFloat(); +! else if ( type == "double" ) +! arg << s.toDouble(); +! else if ( type == "bool" ) +! arg << mkBool( s ); +! else if ( type == "QString" ) +! arg << s; +! else if ( type == "QCString" ) +! arg << QCString( argv[i] ); +! else if ( type == "QColor" ) +! arg << mkColor( s ); +! else if ( type == "QPoint" ) +! arg << mkPoint( s ); +! else if ( type == "QSize" ) +! arg << mkSize( s ); +! else if ( type == "QRect" ) +! arg << mkRect( s ); +! else if ( type == "QVariant" ) { +! if ( s == "true" || s == "false" ) +! arg << QVariant( mkBool( s ), 42 ); +! else if ( s.left( 4 ) == "int(" ) +! arg << QVariant( s.mid(4, s.length()-5).toInt() ); +! else if ( s.left( 7 ) == "QPoint(" ) +! arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +! else if ( s.left( 6 ) == "QSize(" ) +! arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +! else if ( s.left( 6 ) == "QRect(" ) +! arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +! else if ( s.left( 7 ) == "QColor(" ) +! arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +! else +! arg << QVariant( s ); +! } else if ( type.startsWith("QValueList<")) { +! type = type.mid(11, type.length() - 12); +! QStringList list; +! QString delim = s; +! if (delim == "[") +! delim = "]"; +! if (delim == "(") +! delim = ")"; +! i++; +! QByteArray dummy_data; +! QDataStream dummy_arg(dummy_data, IO_WriteOnly); + +! int j = i; +! int count = 0; +! // Parse list to get the count +! while (true) { +! if (j >= argc) +! { +! qWarning("List end-delimiter '%s' not found.", delim.latin1()); +! exit(1); +! } +! if (argv[j] == delim) break; +! marshall(dummy_arg, argc, argv, j, type); +! count++; +! } +! arg << (Q_UINT32) count; +! // Parse the list for real +! while (true) { +! if (i >= argc) +! { +! qWarning("List end-delimiter '%s' not found.", delim.latin1()); +! exit(1); +! } +! if (argv[i] == delim) break; +! marshall(arg, argc, argv, i, type); +! } +! } else { +! qWarning( "cannot handle datatype '%s'", type.latin1() ); +! exit(1); +! } + i++; + } + +--- 242,351 ---- + + } + +! void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) + { +! if (type == "QStringList") +! type = "QValueList"; +! if (type == "QCStringList") +! type = "QValueList"; +! if( i > args.count() ) +! { +! qWarning("Not enough arguments."); +! exit(1); +! } +! QString s = QString::fromLocal8Bit( args[ i ] ); + +! if ( type == "int" ) +! arg << s.toInt(); +! else if ( type == "uint" ) +! arg << s.toUInt(); +! else if ( type == "unsigned" ) +! arg << s.toUInt(); +! else if ( type == "unsigned int" ) +! arg << s.toUInt(); +! else if ( type == "long" ) +! arg << s.toLong(); +! else if ( type == "long int" ) +! arg << s.toLong(); +! else if ( type == "unsigned long" ) +! arg << s.toULong(); +! else if ( type == "unsigned long int" ) +! arg << s.toULong(); +! else if ( type == "float" ) +! arg << s.toFloat(); +! else if ( type == "double" ) +! arg << s.toDouble(); +! else if ( type == "bool" ) +! arg << mkBool( s ); +! else if ( type == "QString" ) +! arg << s; +! else if ( type == "QCString" ) +! arg << QCString( args[ i ] ); +! else if ( type == "QColor" ) +! arg << mkColor( s ); +! else if ( type == "QPoint" ) +! arg << mkPoint( s ); +! else if ( type == "QSize" ) +! arg << mkSize( s ); +! else if ( type == "QRect" ) +! arg << mkRect( s ); +! else if ( type == "QVariant" ) { +! if ( s == "true" || s == "false" ) +! arg << QVariant( mkBool( s ), 42 ); +! else if ( s.left( 4 ) == "int(" ) +! arg << QVariant( s.mid(4, s.length()-5).toInt() ); +! else if ( s.left( 7 ) == "QPoint(" ) +! arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +! else if ( s.left( 6 ) == "QSize(" ) +! arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +! else if ( s.left( 6 ) == "QRect(" ) +! arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +! else if ( s.left( 7 ) == "QColor(" ) +! arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +! else +! arg << QVariant( s ); +! } else if ( type.startsWith("QValueList<")) { +! type = type.mid(11, type.length() - 12); +! QStringList list; +! QString delim = s; +! if (delim == "[") +! delim = "]"; +! if (delim == "(") +! delim = ")"; + i++; ++ QByteArray dummy_data; ++ QDataStream dummy_arg(dummy_data, IO_WriteOnly); ++ ++ uint j = i; ++ uint count = 0; ++ // Parse list to get the count ++ while (true) { ++ if( j > args.count() ) ++ { ++ qWarning("List end-delimiter '%s' not found.", delim.latin1()); ++ exit(1); ++ } ++ if( QString::fromLocal8Bit( args[ j ] ) == delim ) ++ break; ++ marshall( dummy_arg, args, j, type ); ++ count++; ++ } ++ arg << (Q_UINT32) count; ++ // Parse the list for real ++ while (true) { ++ if( i > args.count() ) ++ { ++ qWarning("List end-delimiter '%s' not found.", delim.latin1()); ++ exit(1); ++ } ++ if( QString::fromLocal8Bit( args[ i ] ) == delim ) ++ break; ++ marshall( arg, args, i, type ); ++ } ++ } else { ++ qWarning( "cannot handle datatype '%s'", type.latin1() ); ++ exit(1); ++ } ++ i++; + } + diff --git a/kompare/tests/cvsdiff/ed.diff b/kompare/tests/cvsdiff/ed.diff new file mode 100644 index 00000000..2c859e61 --- /dev/null +++ b/kompare/tests/cvsdiff/ed.diff @@ -0,0 +1,24 @@ +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -e -r1.2 dcopfind.cpp +224c + QCStringList params; + for( int i = 0; i < argc; i++ ) + params.append( args[ i ] ); + + findObject( app, objid, function, params ); +. +133c + if ( (uint) i != args.count() ) { +. +131c + marshall(arg, args, i, *it); +. +121c + if ( types.count() != args.count() ) { +. +39c +bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +. diff --git a/kompare/tests/cvsdiff/edm.diff b/kompare/tests/cvsdiff/edm.diff new file mode 100644 index 00000000..0fb04575 --- /dev/null +++ b/kompare/tests/cvsdiff/edm.diff @@ -0,0 +1,692 @@ +Index: client/dcop.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcop.cpp,v +retrieving revision 1.26 +diff -e -r1.26 dcop.cpp +343a + +// vim: set ts=8 sts=4 sw=4 noet: + +. +340a +} + + +int main( int argc, char** argv ) +{ + bool readStdin = false; + int numOptions = 0; + QString user; + Session session = DefaultSession; + QString sessionName; + + // Scan for command-line options first + for( int pos = 1 ; pos <= argc - 1 ; pos++ ) + { + if( strcmp( argv[ pos ], "--help" ) == 0 ) + showHelp( 0 ); + else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) + { + readStdin = true; + numOptions++; + } + else if( strcmp( argv[ pos ], "--user" ) == 0 ) + { + if( pos <= argc - 2 ) + { + user = QString::fromLocal8Bit( argv[ pos + 1] ); + numOptions +=2; + pos++; + } + else + { + cerr << "Missing username for '--user' option!" << endl << endl; + showHelp( -1 ); + } + } + else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) + { + user = "*"; + numOptions ++; + } + else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) + { + session = QuerySessions; + numOptions ++; + } + else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) + { + session = AllSessions; + numOptions ++; + } + else if( argv[ pos ][ 0 ] == '-' ) + { + cerr << "Unknown command-line option '" << argv[ pos ] + << "'." << endl << endl; + showHelp( -1 ); + } + else + break; // End of options + } + + argc -= numOptions; + + QCStringList args; + for( int i = numOptions; i < argc + numOptions - 1; i++ ) + args.append( argv[ i + 1 ] ); + + if( readStdin && args.count() < 3 ) + { + cerr << "--pipe option only supported for function calls!" << endl << endl; + showHelp( -1 ); + } + + if( user == "*" && args.count() < 3 && session != QuerySessions ) + { + cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; + showHelp( -1 ); + } + + if( session == QuerySessions && !args.isEmpty() ) + { + cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; + showHelp( -1 ); + } + + if( session == QuerySessions && user.isEmpty() ) + { + cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl + << "--all-users options!" << endl << endl; + showHelp( -1 ); + } + + if( session != DefaultSession && session != QuerySessions && + args.count() < 3 ) + { + cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl + << "calls!" << endl << endl; + showHelp( -1 ); + } + + UserList users; + if( user == "*" ) + users = userList(); + else if( !user.isEmpty() ) + users[ user ] = userList()[ user ]; + + runDCOP( args, users, session, sessionName, readStdin ); +. +339a + if( users.count() > 1 || ( users.count() == 1 && + ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) + { + // Check for ICE authority file and if the file can be read by us + QString home = it.data(); + QString iceFile = it.data() + "/.ICEauthority"; + QFileInfo fi( iceFile ); + if( iceFile.isEmpty() ) + { + cerr << "WARNING: Cannot determine home directory for user " + << it.key() << "!" << endl + << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl + << "calling dcop." << endl; + } + else if( fi.exists() ) + { + if( fi.isReadable() ) + { + char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); + putenv( envStr ); + //cerr << "ice: " << envStr << endl; + } + else + { + cerr << "WARNING: ICE authority file " << iceFile + << "is not readable by you!" << endl + << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl + << "calling dcop." << endl; + } + } + else + { + if( users.count() > 1 ) + continue; + else + { + cerr << "WARNING: Cannot find ICE authority file " + << iceFile << "!" << endl + << "Please check permissions or set the $ICEAUTHORITY" + << " variable manually before" << endl + << "calling dcop." << endl; + } + } + } + + // Main loop + // If users is an empty list we're calling for the currently logged + // in user. In this case we don't have a session, but still want + // to iterate the loop once. + QStringList::Iterator sIt = sessions.begin(); + for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) + { + if( !presetDCOPServer && !users.isEmpty() ) + { + QString dcopFile = it.data() + "/" + *sIt; + QFile f( dcopFile ); + if( !f.open( IO_ReadOnly ) ) + { + cerr << "Can't open " << dcopFile << " for reading!" << endl; + exit( -1 ); + } + + QStringList l( QStringList::split( '\n', f.readAll() ) ); + dcopServer = l.first(); + + if( dcopServer.isEmpty() ) + { + cerr << "WARNING: Unable to determine DCOP server for session " + << *sIt << "!" << endl + << "Please check permissions or set the $DCOPSERVER variable manually before" << endl + << "calling dcop." << endl; + exit( -1 ); + } + } + + delete client; + client = new DCOPClient; + if( !dcopServer.isEmpty() ) + client->setServerAddress( dcopServer.ascii() ); + bool success = client->attach(); + if( !success ) + { + cerr << "ERROR: Couldn't attach to DCOP server!" << endl; + continue; + } + dcop = client; + + switch ( args.count() ) + { + case 0: + queryApplications(""); + break; + case 1: + if (endsWith(app, '*')) + queryApplications(app); + else + queryObjects( app, "" ); + break; + case 2: + if (endsWith(objid, '*')) + queryObjects(app, objid); + else + queryFunctions( app, objid ); + break; + case 3: + default: + if( readStdin ) + { + QCStringList::Iterator replaceArg = args.end(); + + QCStringList::Iterator it; + for( it = args.begin(); it != args.end(); it++ ) + if( *it == "%1" ) + replaceArg = it; + + // Read from stdin until EOF and call function for each line read + char *buf = new char[ 1000 ]; + while ( !feof( stdin ) ) + { + fgets( buf, 1000, stdin ); + + if( replaceArg != args.end() ) + *replaceArg = buf; + + callFunction( app, objid, function, params ); + } + } + else + { + // Just call function +// cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; + callFunction( app, objid, function, params ); + } + break; + } + // Another sIt++ would make the loop infinite... + if( users.isEmpty() ) + break; + } + + // Another it++ would make the loop infinite... + if( it == users.end() ) + break; +. +308,338c + if( !args.isEmpty() ) + app = args[ 0 ]; + if( args.count() > 1 ) + objid = args[ 1 ]; + if( args.count() > 2 ) + function = args[ 2 ]; + if( args.count() > 3) + { + params = args; + params.remove( params.begin() ); + params.remove( params.begin() ); + params.remove( params.begin() ); + } + } + + bool firstRun = true; + UserList::Iterator it; + QStringList sessions; + bool presetDCOPServer = false; +// char *dcopStr = 0L; + QString dcopServer; + + for( it = users.begin(); it != users.end() || firstRun; it++ ) + { + firstRun = false; + + //cout << "Iterating '" << it.key() << "'" << endl; + + if( session == QuerySessions ) + { + QStringList sessions = dcopSessionList( it.key(), it.data() ); + if( sessions.isEmpty() ) + { + cout << "No active sessions"; + if( !( *it ).isEmpty() ) + cout << " for user " << *it; + cout << endl; + } + else + { + cout << "Active sessions "; + if( !( *it ).isEmpty() ) + cout << "for user " << *it << " "; + cout << ":" << endl; + + QStringList::Iterator sIt; + for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) + cout << " " << *sIt << endl; + + cout << endl; + } + continue; + } + + if( getenv( "DCOPSERVER" ) ) + { + sessions.append( getenv( "DCOPSERVER" ) ); + presetDCOPServer = true; + } + + if( users.count() > 1 || ( users.count() == 1 && + ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) + { + sessions = dcopSessionList( it.key(), it.data() ); + if( sessions.isEmpty() ) + { + if( users.count() > 1 ) + continue; + else + { + cerr << "ERROR: No active KDE sessions!" << endl + << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl + << "before calling dcop." << endl; + exit( -1 ); + } + } + else if( sessions.count() > 1 && session != AllSessions ) + { + cerr << "ERROR: Multiple available KDE sessions!" << endl + << "Please specify the correct session to use with --session or use the" << endl + << "--all-sessions option to broadcast to all sessions." << endl; + exit( -1 ); + } + } +. +289,304c + // WARNING: This part (until the closing '}') could very + // well be broken now. As I don't know how to trigger and test + // dcoprefs this code is *not* tested. It compiles and it looks + // ok to me, but that's all I can say - Martijn (2001/12/24) + int delimPos = args[ 0 ].findRev( ',' ); + if( delimPos == -1 ) + { + cerr << "Error: '" << args[ 0 ] + << "' is not a valid DCOP reference." << endl; + exit( -1 ); + } + args[ 0 ][ delimPos ] = 0; + app = args[ 0 ].mid( 8 ); + delimPos++; + args[ 0 ][ args[ 0 ].length() - 1 ] = 0; + objid = args[ 0 ].mid( delimPos ); + if( args.count() > 1 ) + function = args[ 1 ]; + if( args.count() > 2 ) + { + params = args; + params.remove( params.begin() ); + params.remove( params.begin() ); + } +. +286,287c + QCStringList params; + DCOPClient *client = 0L; + if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) +. +282a +/** + * Do the actual DCOP call + */ +void runDCOP( QCStringList args, UserList users, Session session, + const QString sessionName, bool readStdin ) +{ +. +279,281c + return result; +} + +/** + * Return a list of available DCOP sessions for the specified user + * An empty list means no sessions are available, or an error occurred. + */ +QStringList dcopSessionList( const QString &user, const QString &home ) +{ + if( home.isEmpty() ) + { + cerr << "WARNING: Cannot determine home directory for user " + << user << "!" << endl + << "Please check permissions or set the $DCOPSERVER variable manually before" << endl + << "calling dcop." << endl; + return QStringList(); + } + + QStringList result; + QFileInfo dirInfo( home ); + if( !dirInfo.exists() || !dirInfo.isReadable() ) + return result; + + QDir d( home ); + d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); + d.setNameFilter( ".DCOPserver*" ); + + const QFileInfoList *list = d.entryInfoList(); + if( !list ) + return result; + + QFileInfoListIterator it( *list ); + QFileInfo *fi; + + while ( ( fi = it.current() ) != 0 ) + { + if( fi->isReadable() ) + result.append( fi->fileName() ); + ++it; + } + return result; +} +. +274,276c + QStringList l( QStringList::split( '\n', f.readAll() ) ); + + for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) + { + QStringList userInfo( QStringList::split( ':', *it, true ) ); + result[ userInfo[ 0 ] ] = userInfo[ 5 ]; +. +272a + UserList result; + + QFile f( "/etc/passwd" ); + + if( !f.open( IO_ReadOnly ) ) + { + cerr << "Can't open /etc/passwd for reading!" << endl; + return result; + } +. +270,271c +/** + * Return a list of all users and their home directories. + * Returns an empty list if /etc/passwd cannot be read for some reason. + */ +static UserList userList() +. +268a +/** + * Show command-line help and exit + */ +void showHelp( int exitCode = 0 ) +{ + cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl + << "" << endl + << "Console DCOP client" << endl + << "" << endl + << "Generic options:" << endl + << " --help Show help about options" << endl + << "" << endl + << "Options:" << endl + << " --pipe Call DCOP for each line read from stdin" << endl + << " --user Connect to the given user's DCOP server. This option will" << endl + << " ignore the values of the environment vars $DCOPSERVER and" << endl + << " $ICEAUTHORITY, even if they are set." << endl + << " If the user has more than one open session, you must also" << endl + << " use one of the --list-sessions, --session or --als-sessions" << endl + << " command-line options." << endl + << " --all-users Send the same DCOP call to all users with a running DCOP" << endl + << " server. Only failed calls to existing DCOP servers will" + << " generate an error message. If no DCOP server is available" << endl + << " at all, no error will be generated." << endl; + + exit( exitCode ); +} +. +246,250c + uint i = 0; + for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) + marshall( arg, args, i, *it ); + + if ( i != args.count() ) + { +. +164c +// exit(1); + return; +. +156,157c + uint a = (*it).contains(','); + if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) +. +139c + if ( !ok && args.isEmpty() ) +. +123d +121c +void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) +. +35a +static QTextStream cout( stdout, IO_WriteOnly ); +static QTextStream cerr( stderr, IO_WriteOnly ); + +/** + * Session to send call to + * DefaultSession - current session. Current KDE session when called without + * --user or --all-users option. Otherwise this value ignores + * all users with more than one active session. + * AllSessions - Send to all sessions found. requires --user or --all-users. + * QuerySessions - Don't call DCOP, return a list of available sessions. + * CustomSession - Use the specified session + */ +enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; + +. +33a +typedef QMap UserList; + +. +28,30c +#include "../kdatastream.h" +. +25c +#include +#include +#include +#include +#include +#include +#include + +// putenv() is not available on all platforms, so make sure the emulation +// wrapper is available in those cases by loading config.h! +#include + +. +23c +#include +#include +#include + +. +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -e -r1.2 dcopfind.cpp +224c + QCStringList params; + for( int i = 0; i < argc; i++ ) + params.append( args[ i ] ); + + findObject( app, objid, function, params ); +. +133c + if ( (uint) i != args.count() ) { +. +131c + marshall(arg, args, i, *it); +. +121c + if ( types.count() != args.count() ) { +. +39c +bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +. +Index: client/marshall.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/marshall.cpp,v +retrieving revision 1.3 +diff -e -r1.3 marshall.cpp +347a + QByteArray dummy_data; + QDataStream dummy_arg(dummy_data, IO_WriteOnly); + + uint j = i; + uint count = 0; + // Parse list to get the count + while (true) { + if( j > args.count() ) + { + qWarning("List end-delimiter '%s' not found.", delim.latin1()); + exit(1); + } + if( QString::fromLocal8Bit( args[ j ] ) == delim ) + break; + marshall( dummy_arg, args, j, type ); + count++; + } + arg << (Q_UINT32) count; + // Parse the list for real + while (true) { + if( i > args.count() ) + { + qWarning("List end-delimiter '%s' not found.", delim.latin1()); + exit(1); + } + if( QString::fromLocal8Bit( args[ i ] ) == delim ) + break; + marshall( arg, args, i, type ); + } + } else { + qWarning( "cannot handle datatype '%s'", type.latin1() ); + exit(1); + } + i++; +. +319,346c + if ( type == "int" ) + arg << s.toInt(); + else if ( type == "uint" ) + arg << s.toUInt(); + else if ( type == "unsigned" ) + arg << s.toUInt(); + else if ( type == "unsigned int" ) + arg << s.toUInt(); + else if ( type == "long" ) + arg << s.toLong(); + else if ( type == "long int" ) + arg << s.toLong(); + else if ( type == "unsigned long" ) + arg << s.toULong(); + else if ( type == "unsigned long int" ) + arg << s.toULong(); + else if ( type == "float" ) + arg << s.toFloat(); + else if ( type == "double" ) + arg << s.toDouble(); + else if ( type == "bool" ) + arg << mkBool( s ); + else if ( type == "QString" ) + arg << s; + else if ( type == "QCString" ) + arg << QCString( args[ i ] ); + else if ( type == "QColor" ) + arg << mkColor( s ); + else if ( type == "QPoint" ) + arg << mkPoint( s ); + else if ( type == "QSize" ) + arg << mkSize( s ); + else if ( type == "QRect" ) + arg << mkRect( s ); + else if ( type == "QVariant" ) { + if ( s == "true" || s == "false" ) + arg << QVariant( mkBool( s ), 42 ); + else if ( s.left( 4 ) == "int(" ) + arg << QVariant( s.mid(4, s.length()-5).toInt() ); + else if ( s.left( 7 ) == "QPoint(" ) + arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); + else if ( s.left( 6 ) == "QSize(" ) + arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); + else if ( s.left( 6 ) == "QRect(" ) + arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); + else if ( s.left( 7 ) == "QColor(" ) + arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); + else + arg << QVariant( s ); + } else if ( type.startsWith("QValueList<")) { + type = type.mid(11, type.length() - 12); + QStringList list; + QString delim = s; + if (delim == "[") + delim = "]"; + if (delim == "(") + delim = ")"; +. +247,317c + if (type == "QStringList") + type = "QValueList"; + if (type == "QCStringList") + type = "QValueList"; + if( i > args.count() ) + { + qWarning("Not enough arguments."); + exit(1); + } + QString s = QString::fromLocal8Bit( args[ i ] ); +. +245c +void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) +. diff --git a/kompare/tests/cvsdiff/normal.diff b/kompare/tests/cvsdiff/normal.diff new file mode 100644 index 00000000..3becb815 --- /dev/null +++ b/kompare/tests/cvsdiff/normal.diff @@ -0,0 +1,29 @@ +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -r1.2 dcopfind.cpp +39c39 +< bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) +--- +> bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +121c121 +< if ( (int) types.count() != argc ) { +--- +> if ( types.count() != args.count() ) { +131c131 +< marshall(arg, argc, args, i, *it); +--- +> marshall(arg, args, i, *it); +133c133 +< if ( (int) i != argc ) { +--- +> if ( (uint) i != args.count() ) { +224c224,228 +< findObject( app, objid, function, argc, args ); +--- +> QCStringList params; +> for( int i = 0; i < argc; i++ ) +> params.append( args[ i ] ); +> +> findObject( app, objid, function, params ); diff --git a/kompare/tests/cvsdiff/normalm.diff b/kompare/tests/cvsdiff/normalm.diff new file mode 100644 index 00000000..935763a0 --- /dev/null +++ b/kompare/tests/cvsdiff/normalm.diff @@ -0,0 +1,861 @@ +Index: client/dcop.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcop.cpp,v +retrieving revision 1.26 +diff -r1.26 dcop.cpp +23c23,26 +< #include +--- +> #include +> #include +> #include +> +25c28,39 +< #include "../kdatastream.h" +--- +> #include +> #include +> #include +> #include +> #include +> #include +> #include +> +> // putenv() is not available on all platforms, so make sure the emulation +> // wrapper is available in those cases by loading config.h! +> #include +> +28,30c42 +< #include +< #include +< #include +--- +> #include "../kdatastream.h" +33a46,47 +> typedef QMap UserList; +> +35a50,63 +> static QTextStream cout( stdout, IO_WriteOnly ); +> static QTextStream cerr( stderr, IO_WriteOnly ); +> +> /** +> * Session to send call to +> * DefaultSession - current session. Current KDE session when called without +> * --user or --all-users option. Otherwise this value ignores +> * all users with more than one active session. +> * AllSessions - Send to all sessions found. requires --user or --all-users. +> * QuerySessions - Don't call DCOP, return a list of available sessions. +> * CustomSession - Use the specified session +> */ +> enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; +> +121c149 +< void callFunction( const char* app, const char* obj, const char* func, int argc, char** args ) +--- +> void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) +123d150 +< +139c166 +< if ( !ok && argc == 0 ) +--- +> if ( !ok && args.isEmpty() ) +156,157c183,184 +< int a = (*it).contains(','); +< if ( ( a == 0 && argc == 0) || ( a > 0 && a + 1 == argc ) ) +--- +> uint a = (*it).contains(','); +> if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) +164c191,192 +< exit(1); +--- +> // exit(1); +> return; +246,250c274,279 +< int i = 0; +< for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +< marshall(arg, argc, args, i, *it); +< } +< if ( i != argc ) { +--- +> uint i = 0; +> for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) +> marshall( arg, args, i, *it ); +> +> if ( i != args.count() ) +> { +268a298,324 +> /** +> * Show command-line help and exit +> */ +> void showHelp( int exitCode = 0 ) +> { +> cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl +> << "" << endl +> << "Console DCOP client" << endl +> << "" << endl +> << "Generic options:" << endl +> << " --help Show help about options" << endl +> << "" << endl +> << "Options:" << endl +> << " --pipe Call DCOP for each line read from stdin" << endl +> << " --user Connect to the given user's DCOP server. This option will" << endl +> << " ignore the values of the environment vars $DCOPSERVER and" << endl +> << " $ICEAUTHORITY, even if they are set." << endl +> << " If the user has more than one open session, you must also" << endl +> << " use one of the --list-sessions, --session or --als-sessions" << endl +> << " command-line options." << endl +> << " --all-users Send the same DCOP call to all users with a running DCOP" << endl +> << " server. Only failed calls to existing DCOP servers will" +> << " generate an error message. If no DCOP server is available" << endl +> << " at all, no error will be generated." << endl; +> +> exit( exitCode ); +> } +270,271c326,330 +< +< int main( int argc, char** argv ) +--- +> /** +> * Return a list of all users and their home directories. +> * Returns an empty list if /etc/passwd cannot be read for some reason. +> */ +> static UserList userList() +272a332,340 +> UserList result; +> +> QFile f( "/etc/passwd" ); +> +> if( !f.open( IO_ReadOnly ) ) +> { +> cerr << "Can't open /etc/passwd for reading!" << endl; +> return result; +> } +274,276c342,347 +< if ( argc > 1 && argv[1][0] == '-' ) { +< fprintf( stderr, "Usage: dcop [ application [object [function [arg1] [arg2] [arg3] ... ] ] ] \n" ); +< exit(0); +--- +> QStringList l( QStringList::split( '\n', f.readAll() ) ); +> +> for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) +> { +> QStringList userInfo( QStringList::split( ':', *it, true ) ); +> result[ userInfo[ 0 ] ] = userInfo[ 5 ]; +279,281c350,391 +< DCOPClient client; +< client.attach(); +< dcop = &client; +--- +> return result; +> } +> +> /** +> * Return a list of available DCOP sessions for the specified user +> * An empty list means no sessions are available, or an error occurred. +> */ +> QStringList dcopSessionList( const QString &user, const QString &home ) +> { +> if( home.isEmpty() ) +> { +> cerr << "WARNING: Cannot determine home directory for user " +> << user << "!" << endl +> << "Please check permissions or set the $DCOPSERVER variable manually before" << endl +> << "calling dcop." << endl; +> return QStringList(); +> } +> +> QStringList result; +> QFileInfo dirInfo( home ); +> if( !dirInfo.exists() || !dirInfo.isReadable() ) +> return result; +> +> QDir d( home ); +> d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); +> d.setNameFilter( ".DCOPserver*" ); +> +> const QFileInfoList *list = d.entryInfoList(); +> if( !list ) +> return result; +> +> QFileInfoListIterator it( *list ); +> QFileInfo *fi; +> +> while ( ( fi = it.current() ) != 0 ) +> { +> if( fi->isReadable() ) +> result.append( fi->fileName() ); +> ++it; +> } +> return result; +> } +282a393,398 +> /** +> * Do the actual DCOP call +> */ +> void runDCOP( QCStringList args, UserList users, Session session, +> const QString sessionName, bool readStdin ) +> { +286,287c402,404 +< char **args = 0; +< if ((argc > 1) && (strncmp(argv[1], "DCOPRef(", 8)) == 0) +--- +> QCStringList params; +> DCOPClient *client = 0L; +> if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) +289,304c406,429 +< char *delim = strchr(argv[1], ','); +< if (!delim) +< { +< fprintf(stderr, "Error: '%s' is not a valid DCOP reference.\n", argv[1]); +< return 1; +< } +< *delim = 0; +< app = argv[1] + 8; +< delim++; +< delim[strlen(delim)-1] = 0; +< objid = delim; +< if (argc > 2) +< function = argv[2]; +< if (argc > 3) +< args = &argv[3]; +< argc++; +--- +> // WARNING: This part (until the closing '}') could very +> // well be broken now. As I don't know how to trigger and test +> // dcoprefs this code is *not* tested. It compiles and it looks +> // ok to me, but that's all I can say - Martijn (2001/12/24) +> int delimPos = args[ 0 ].findRev( ',' ); +> if( delimPos == -1 ) +> { +> cerr << "Error: '" << args[ 0 ] +> << "' is not a valid DCOP reference." << endl; +> exit( -1 ); +> } +> args[ 0 ][ delimPos ] = 0; +> app = args[ 0 ].mid( 8 ); +> delimPos++; +> args[ 0 ][ args[ 0 ].length() - 1 ] = 0; +> objid = args[ 0 ].mid( delimPos ); +> if( args.count() > 1 ) +> function = args[ 1 ]; +> if( args.count() > 2 ) +> { +> params = args; +> params.remove( params.begin() ); +> params.remove( params.begin() ); +> } +308,338c433,516 +< if (argc > 1) +< app = argv[1]; +< if (argc > 2) +< objid = argv[2]; +< if (argc > 3) +< function = argv[3]; +< if (argc > 4) +< args = &argv[4]; +< } +< +< switch ( argc ) { +< case 0: +< case 1: +< queryApplications(""); +< break; +< case 2: +< if (endsWith(app, '*')) +< queryApplications(app); +< else +< queryObjects( app, "" ); +< break; +< case 3: +< if (endsWith(objid, '*')) +< queryObjects(app, objid); +< else +< queryFunctions( app, objid ); +< break; +< case 4: +< default: +< callFunction( app, objid, function, argc - 4, args ); +< break; +--- +> if( !args.isEmpty() ) +> app = args[ 0 ]; +> if( args.count() > 1 ) +> objid = args[ 1 ]; +> if( args.count() > 2 ) +> function = args[ 2 ]; +> if( args.count() > 3) +> { +> params = args; +> params.remove( params.begin() ); +> params.remove( params.begin() ); +> params.remove( params.begin() ); +> } +> } +> +> bool firstRun = true; +> UserList::Iterator it; +> QStringList sessions; +> bool presetDCOPServer = false; +> // char *dcopStr = 0L; +> QString dcopServer; +> +> for( it = users.begin(); it != users.end() || firstRun; it++ ) +> { +> firstRun = false; +> +> //cout << "Iterating '" << it.key() << "'" << endl; +> +> if( session == QuerySessions ) +> { +> QStringList sessions = dcopSessionList( it.key(), it.data() ); +> if( sessions.isEmpty() ) +> { +> cout << "No active sessions"; +> if( !( *it ).isEmpty() ) +> cout << " for user " << *it; +> cout << endl; +> } +> else +> { +> cout << "Active sessions "; +> if( !( *it ).isEmpty() ) +> cout << "for user " << *it << " "; +> cout << ":" << endl; +> +> QStringList::Iterator sIt; +> for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) +> cout << " " << *sIt << endl; +> +> cout << endl; +> } +> continue; +> } +> +> if( getenv( "DCOPSERVER" ) ) +> { +> sessions.append( getenv( "DCOPSERVER" ) ); +> presetDCOPServer = true; +> } +> +> if( users.count() > 1 || ( users.count() == 1 && +> ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) +> { +> sessions = dcopSessionList( it.key(), it.data() ); +> if( sessions.isEmpty() ) +> { +> if( users.count() > 1 ) +> continue; +> else +> { +> cerr << "ERROR: No active KDE sessions!" << endl +> << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl +> << "before calling dcop." << endl; +> exit( -1 ); +> } +> } +> else if( sessions.count() > 1 && session != AllSessions ) +> { +> cerr << "ERROR: Multiple available KDE sessions!" << endl +> << "Please specify the correct session to use with --session or use the" << endl +> << "--all-sessions option to broadcast to all sessions." << endl; +> exit( -1 ); +> } +> } +339a518,660 +> if( users.count() > 1 || ( users.count() == 1 && +> ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) +> { +> // Check for ICE authority file and if the file can be read by us +> QString home = it.data(); +> QString iceFile = it.data() + "/.ICEauthority"; +> QFileInfo fi( iceFile ); +> if( iceFile.isEmpty() ) +> { +> cerr << "WARNING: Cannot determine home directory for user " +> << it.key() << "!" << endl +> << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl +> << "calling dcop." << endl; +> } +> else if( fi.exists() ) +> { +> if( fi.isReadable() ) +> { +> char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); +> putenv( envStr ); +> //cerr << "ice: " << envStr << endl; +> } +> else +> { +> cerr << "WARNING: ICE authority file " << iceFile +> << "is not readable by you!" << endl +> << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl +> << "calling dcop." << endl; +> } +> } +> else +> { +> if( users.count() > 1 ) +> continue; +> else +> { +> cerr << "WARNING: Cannot find ICE authority file " +> << iceFile << "!" << endl +> << "Please check permissions or set the $ICEAUTHORITY" +> << " variable manually before" << endl +> << "calling dcop." << endl; +> } +> } +> } +> +> // Main loop +> // If users is an empty list we're calling for the currently logged +> // in user. In this case we don't have a session, but still want +> // to iterate the loop once. +> QStringList::Iterator sIt = sessions.begin(); +> for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) +> { +> if( !presetDCOPServer && !users.isEmpty() ) +> { +> QString dcopFile = it.data() + "/" + *sIt; +> QFile f( dcopFile ); +> if( !f.open( IO_ReadOnly ) ) +> { +> cerr << "Can't open " << dcopFile << " for reading!" << endl; +> exit( -1 ); +> } +> +> QStringList l( QStringList::split( '\n', f.readAll() ) ); +> dcopServer = l.first(); +> +> if( dcopServer.isEmpty() ) +> { +> cerr << "WARNING: Unable to determine DCOP server for session " +> << *sIt << "!" << endl +> << "Please check permissions or set the $DCOPSERVER variable manually before" << endl +> << "calling dcop." << endl; +> exit( -1 ); +> } +> } +> +> delete client; +> client = new DCOPClient; +> if( !dcopServer.isEmpty() ) +> client->setServerAddress( dcopServer.ascii() ); +> bool success = client->attach(); +> if( !success ) +> { +> cerr << "ERROR: Couldn't attach to DCOP server!" << endl; +> continue; +> } +> dcop = client; +> +> switch ( args.count() ) +> { +> case 0: +> queryApplications(""); +> break; +> case 1: +> if (endsWith(app, '*')) +> queryApplications(app); +> else +> queryObjects( app, "" ); +> break; +> case 2: +> if (endsWith(objid, '*')) +> queryObjects(app, objid); +> else +> queryFunctions( app, objid ); +> break; +> case 3: +> default: +> if( readStdin ) +> { +> QCStringList::Iterator replaceArg = args.end(); +> +> QCStringList::Iterator it; +> for( it = args.begin(); it != args.end(); it++ ) +> if( *it == "%1" ) +> replaceArg = it; +> +> // Read from stdin until EOF and call function for each line read +> char *buf = new char[ 1000 ]; +> while ( !feof( stdin ) ) +> { +> fgets( buf, 1000, stdin ); +> +> if( replaceArg != args.end() ) +> *replaceArg = buf; +> +> callFunction( app, objid, function, params ); +> } +> } +> else +> { +> // Just call function +> // cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; +> callFunction( app, objid, function, params ); +> } +> break; +> } +> // Another sIt++ would make the loop infinite... +> if( users.isEmpty() ) +> break; +> } +> +> // Another it++ would make the loop infinite... +> if( it == users.end() ) +> break; +340a662,767 +> } +> +> +> int main( int argc, char** argv ) +> { +> bool readStdin = false; +> int numOptions = 0; +> QString user; +> Session session = DefaultSession; +> QString sessionName; +> +> // Scan for command-line options first +> for( int pos = 1 ; pos <= argc - 1 ; pos++ ) +> { +> if( strcmp( argv[ pos ], "--help" ) == 0 ) +> showHelp( 0 ); +> else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) +> { +> readStdin = true; +> numOptions++; +> } +> else if( strcmp( argv[ pos ], "--user" ) == 0 ) +> { +> if( pos <= argc - 2 ) +> { +> user = QString::fromLocal8Bit( argv[ pos + 1] ); +> numOptions +=2; +> pos++; +> } +> else +> { +> cerr << "Missing username for '--user' option!" << endl << endl; +> showHelp( -1 ); +> } +> } +> else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) +> { +> user = "*"; +> numOptions ++; +> } +> else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) +> { +> session = QuerySessions; +> numOptions ++; +> } +> else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) +> { +> session = AllSessions; +> numOptions ++; +> } +> else if( argv[ pos ][ 0 ] == '-' ) +> { +> cerr << "Unknown command-line option '" << argv[ pos ] +> << "'." << endl << endl; +> showHelp( -1 ); +> } +> else +> break; // End of options +> } +> +> argc -= numOptions; +> +> QCStringList args; +> for( int i = numOptions; i < argc + numOptions - 1; i++ ) +> args.append( argv[ i + 1 ] ); +> +> if( readStdin && args.count() < 3 ) +> { +> cerr << "--pipe option only supported for function calls!" << endl << endl; +> showHelp( -1 ); +> } +> +> if( user == "*" && args.count() < 3 && session != QuerySessions ) +> { +> cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; +> showHelp( -1 ); +> } +> +> if( session == QuerySessions && !args.isEmpty() ) +> { +> cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; +> showHelp( -1 ); +> } +> +> if( session == QuerySessions && user.isEmpty() ) +> { +> cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl +> << "--all-users options!" << endl << endl; +> showHelp( -1 ); +> } +> +> if( session != DefaultSession && session != QuerySessions && +> args.count() < 3 ) +> { +> cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl +> << "calls!" << endl << endl; +> showHelp( -1 ); +> } +> +> UserList users; +> if( user == "*" ) +> users = userList(); +> else if( !user.isEmpty() ) +> users[ user ] = userList()[ user ]; +> +> runDCOP( args, users, session, sessionName, readStdin ); +343a771,773 +> +> // vim: set ts=8 sts=4 sw=4 noet: +> +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -r1.2 dcopfind.cpp +39c39 +< bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) +--- +> bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +121c121 +< if ( (int) types.count() != argc ) { +--- +> if ( types.count() != args.count() ) { +131c131 +< marshall(arg, argc, args, i, *it); +--- +> marshall(arg, args, i, *it); +133c133 +< if ( (int) i != argc ) { +--- +> if ( (uint) i != args.count() ) { +224c224,228 +< findObject( app, objid, function, argc, args ); +--- +> QCStringList params; +> for( int i = 0; i < argc; i++ ) +> params.append( args[ i ] ); +> +> findObject( app, objid, function, params ); +Index: client/marshall.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/marshall.cpp,v +retrieving revision 1.3 +diff -r1.3 marshall.cpp +245c245 +< void marshall(QDataStream &arg, int argc, char **argv, int &i, QString type) +--- +> void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) +247,317c247,256 +< if (type == "QStringList") +< type = "QValueList"; +< if (type == "QCStringList") +< type = "QValueList"; +< if (i >= argc) +< { +< qWarning("Not enough arguments."); +< exit(1); +< } +< QString s = QString::fromLocal8Bit(argv[i]); +< +< if ( type == "int" ) +< arg << s.toInt(); +< else if ( type == "uint" ) +< arg << s.toUInt(); +< else if ( type == "unsigned" ) +< arg << s.toUInt(); +< else if ( type == "unsigned int" ) +< arg << s.toUInt(); +< else if ( type == "long" ) +< arg << s.toLong(); +< else if ( type == "long int" ) +< arg << s.toLong(); +< else if ( type == "unsigned long" ) +< arg << s.toULong(); +< else if ( type == "unsigned long int" ) +< arg << s.toULong(); +< else if ( type == "float" ) +< arg << s.toFloat(); +< else if ( type == "double" ) +< arg << s.toDouble(); +< else if ( type == "bool" ) +< arg << mkBool( s ); +< else if ( type == "QString" ) +< arg << s; +< else if ( type == "QCString" ) +< arg << QCString( argv[i] ); +< else if ( type == "QColor" ) +< arg << mkColor( s ); +< else if ( type == "QPoint" ) +< arg << mkPoint( s ); +< else if ( type == "QSize" ) +< arg << mkSize( s ); +< else if ( type == "QRect" ) +< arg << mkRect( s ); +< else if ( type == "QVariant" ) { +< if ( s == "true" || s == "false" ) +< arg << QVariant( mkBool( s ), 42 ); +< else if ( s.left( 4 ) == "int(" ) +< arg << QVariant( s.mid(4, s.length()-5).toInt() ); +< else if ( s.left( 7 ) == "QPoint(" ) +< arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +< else if ( s.left( 6 ) == "QSize(" ) +< arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +< else if ( s.left( 6 ) == "QRect(" ) +< arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +< else if ( s.left( 7 ) == "QColor(" ) +< arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +< else +< arg << QVariant( s ); +< } else if ( type.startsWith("QValueList<")) { +< type = type.mid(11, type.length() - 12); +< QStringList list; +< QString delim = s; +< if (delim == "[") +< delim = "]"; +< if (delim == "(") +< delim = ")"; +< i++; +< QByteArray dummy_data; +< QDataStream dummy_arg(dummy_data, IO_WriteOnly); +--- +> if (type == "QStringList") +> type = "QValueList"; +> if (type == "QCStringList") +> type = "QValueList"; +> if( i > args.count() ) +> { +> qWarning("Not enough arguments."); +> exit(1); +> } +> QString s = QString::fromLocal8Bit( args[ i ] ); +319,346c258,314 +< int j = i; +< int count = 0; +< // Parse list to get the count +< while (true) { +< if (j >= argc) +< { +< qWarning("List end-delimiter '%s' not found.", delim.latin1()); +< exit(1); +< } +< if (argv[j] == delim) break; +< marshall(dummy_arg, argc, argv, j, type); +< count++; +< } +< arg << (Q_UINT32) count; +< // Parse the list for real +< while (true) { +< if (i >= argc) +< { +< qWarning("List end-delimiter '%s' not found.", delim.latin1()); +< exit(1); +< } +< if (argv[i] == delim) break; +< marshall(arg, argc, argv, i, type); +< } +< } else { +< qWarning( "cannot handle datatype '%s'", type.latin1() ); +< exit(1); +< } +--- +> if ( type == "int" ) +> arg << s.toInt(); +> else if ( type == "uint" ) +> arg << s.toUInt(); +> else if ( type == "unsigned" ) +> arg << s.toUInt(); +> else if ( type == "unsigned int" ) +> arg << s.toUInt(); +> else if ( type == "long" ) +> arg << s.toLong(); +> else if ( type == "long int" ) +> arg << s.toLong(); +> else if ( type == "unsigned long" ) +> arg << s.toULong(); +> else if ( type == "unsigned long int" ) +> arg << s.toULong(); +> else if ( type == "float" ) +> arg << s.toFloat(); +> else if ( type == "double" ) +> arg << s.toDouble(); +> else if ( type == "bool" ) +> arg << mkBool( s ); +> else if ( type == "QString" ) +> arg << s; +> else if ( type == "QCString" ) +> arg << QCString( args[ i ] ); +> else if ( type == "QColor" ) +> arg << mkColor( s ); +> else if ( type == "QPoint" ) +> arg << mkPoint( s ); +> else if ( type == "QSize" ) +> arg << mkSize( s ); +> else if ( type == "QRect" ) +> arg << mkRect( s ); +> else if ( type == "QVariant" ) { +> if ( s == "true" || s == "false" ) +> arg << QVariant( mkBool( s ), 42 ); +> else if ( s.left( 4 ) == "int(" ) +> arg << QVariant( s.mid(4, s.length()-5).toInt() ); +> else if ( s.left( 7 ) == "QPoint(" ) +> arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +> else if ( s.left( 6 ) == "QSize(" ) +> arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +> else if ( s.left( 6 ) == "QRect(" ) +> arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +> else if ( s.left( 7 ) == "QColor(" ) +> arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +> else +> arg << QVariant( s ); +> } else if ( type.startsWith("QValueList<")) { +> type = type.mid(11, type.length() - 12); +> QStringList list; +> QString delim = s; +> if (delim == "[") +> delim = "]"; +> if (delim == "(") +> delim = ")"; +347a316,349 +> QByteArray dummy_data; +> QDataStream dummy_arg(dummy_data, IO_WriteOnly); +> +> uint j = i; +> uint count = 0; +> // Parse list to get the count +> while (true) { +> if( j > args.count() ) +> { +> qWarning("List end-delimiter '%s' not found.", delim.latin1()); +> exit(1); +> } +> if( QString::fromLocal8Bit( args[ j ] ) == delim ) +> break; +> marshall( dummy_arg, args, j, type ); +> count++; +> } +> arg << (Q_UINT32) count; +> // Parse the list for real +> while (true) { +> if( i > args.count() ) +> { +> qWarning("List end-delimiter '%s' not found.", delim.latin1()); +> exit(1); +> } +> if( QString::fromLocal8Bit( args[ i ] ) == delim ) +> break; +> marshall( arg, args, i, type ); +> } +> } else { +> qWarning( "cannot handle datatype '%s'", type.latin1() ); +> exit(1); +> } +> i++; diff --git a/kompare/tests/cvsdiff/rcs.diff b/kompare/tests/cvsdiff/rcs.diff new file mode 100644 index 00000000..da42d91a --- /dev/null +++ b/kompare/tests/cvsdiff/rcs.diff @@ -0,0 +1,24 @@ +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -n -r1.2 dcopfind.cpp +d39 1 +a39 1 +bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +d121 1 +a121 1 + if ( types.count() != args.count() ) { +d131 1 +a131 1 + marshall(arg, args, i, *it); +d133 1 +a133 1 + if ( (uint) i != args.count() ) { +d224 1 +a224 5 + QCStringList params; + for( int i = 0; i < argc; i++ ) + params.append( args[ i ] ); + + findObject( app, objid, function, params ); diff --git a/kompare/tests/cvsdiff/rcsm.diff b/kompare/tests/cvsdiff/rcsm.diff new file mode 100644 index 00000000..c690b2a7 --- /dev/null +++ b/kompare/tests/cvsdiff/rcsm.diff @@ -0,0 +1,683 @@ +Index: client/dcop.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcop.cpp,v +retrieving revision 1.26 +diff -n -r1.26 dcop.cpp +d23 1 +a23 4 +#include +#include +#include + +d25 1 +a25 12 +#include +#include +#include +#include +#include +#include +#include + +// putenv() is not available on all platforms, so make sure the emulation +// wrapper is available in those cases by loading config.h! +#include + +d28 3 +a30 1 +#include "../kdatastream.h" +a33 2 +typedef QMap UserList; + +a35 14 +static QTextStream cout( stdout, IO_WriteOnly ); +static QTextStream cerr( stderr, IO_WriteOnly ); + +/** + * Session to send call to + * DefaultSession - current session. Current KDE session when called without + * --user or --all-users option. Otherwise this value ignores + * all users with more than one active session. + * AllSessions - Send to all sessions found. requires --user or --all-users. + * QuerySessions - Don't call DCOP, return a list of available sessions. + * CustomSession - Use the specified session + */ +enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; + +d121 1 +a121 1 +void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) +d123 1 +d139 1 +a139 1 + if ( !ok && args.isEmpty() ) +d156 2 +a157 2 + uint a = (*it).contains(','); + if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) +d164 1 +a164 2 +// exit(1); + return; +d246 5 +a250 6 + uint i = 0; + for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) + marshall( arg, args, i, *it ); + + if ( i != args.count() ) + { +a268 27 +/** + * Show command-line help and exit + */ +void showHelp( int exitCode = 0 ) +{ + cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl + << "" << endl + << "Console DCOP client" << endl + << "" << endl + << "Generic options:" << endl + << " --help Show help about options" << endl + << "" << endl + << "Options:" << endl + << " --pipe Call DCOP for each line read from stdin" << endl + << " --user Connect to the given user's DCOP server. This option will" << endl + << " ignore the values of the environment vars $DCOPSERVER and" << endl + << " $ICEAUTHORITY, even if they are set." << endl + << " If the user has more than one open session, you must also" << endl + << " use one of the --list-sessions, --session or --als-sessions" << endl + << " command-line options." << endl + << " --all-users Send the same DCOP call to all users with a running DCOP" << endl + << " server. Only failed calls to existing DCOP servers will" + << " generate an error message. If no DCOP server is available" << endl + << " at all, no error will be generated." << endl; + + exit( exitCode ); +} +d270 2 +a271 5 +/** + * Return a list of all users and their home directories. + * Returns an empty list if /etc/passwd cannot be read for some reason. + */ +static UserList userList() +a272 9 + UserList result; + + QFile f( "/etc/passwd" ); + + if( !f.open( IO_ReadOnly ) ) + { + cerr << "Can't open /etc/passwd for reading!" << endl; + return result; + } +d274 3 +a276 6 + QStringList l( QStringList::split( '\n', f.readAll() ) ); + + for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) + { + QStringList userInfo( QStringList::split( ':', *it, true ) ); + result[ userInfo[ 0 ] ] = userInfo[ 5 ]; +d279 3 +a281 42 + return result; +} + +/** + * Return a list of available DCOP sessions for the specified user + * An empty list means no sessions are available, or an error occurred. + */ +QStringList dcopSessionList( const QString &user, const QString &home ) +{ + if( home.isEmpty() ) + { + cerr << "WARNING: Cannot determine home directory for user " + << user << "!" << endl + << "Please check permissions or set the $DCOPSERVER variable manually before" << endl + << "calling dcop." << endl; + return QStringList(); + } + + QStringList result; + QFileInfo dirInfo( home ); + if( !dirInfo.exists() || !dirInfo.isReadable() ) + return result; + + QDir d( home ); + d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); + d.setNameFilter( ".DCOPserver*" ); + + const QFileInfoList *list = d.entryInfoList(); + if( !list ) + return result; + + QFileInfoListIterator it( *list ); + QFileInfo *fi; + + while ( ( fi = it.current() ) != 0 ) + { + if( fi->isReadable() ) + result.append( fi->fileName() ); + ++it; + } + return result; +} +a282 6 +/** + * Do the actual DCOP call + */ +void runDCOP( QCStringList args, UserList users, Session session, + const QString sessionName, bool readStdin ) +{ +d286 2 +a287 3 + QCStringList params; + DCOPClient *client = 0L; + if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) +d289 16 +a304 24 + // WARNING: This part (until the closing '}') could very + // well be broken now. As I don't know how to trigger and test + // dcoprefs this code is *not* tested. It compiles and it looks + // ok to me, but that's all I can say - Martijn (2001/12/24) + int delimPos = args[ 0 ].findRev( ',' ); + if( delimPos == -1 ) + { + cerr << "Error: '" << args[ 0 ] + << "' is not a valid DCOP reference." << endl; + exit( -1 ); + } + args[ 0 ][ delimPos ] = 0; + app = args[ 0 ].mid( 8 ); + delimPos++; + args[ 0 ][ args[ 0 ].length() - 1 ] = 0; + objid = args[ 0 ].mid( delimPos ); + if( args.count() > 1 ) + function = args[ 1 ]; + if( args.count() > 2 ) + { + params = args; + params.remove( params.begin() ); + params.remove( params.begin() ); + } +d308 31 +a338 84 + if( !args.isEmpty() ) + app = args[ 0 ]; + if( args.count() > 1 ) + objid = args[ 1 ]; + if( args.count() > 2 ) + function = args[ 2 ]; + if( args.count() > 3) + { + params = args; + params.remove( params.begin() ); + params.remove( params.begin() ); + params.remove( params.begin() ); + } + } + + bool firstRun = true; + UserList::Iterator it; + QStringList sessions; + bool presetDCOPServer = false; +// char *dcopStr = 0L; + QString dcopServer; + + for( it = users.begin(); it != users.end() || firstRun; it++ ) + { + firstRun = false; + + //cout << "Iterating '" << it.key() << "'" << endl; + + if( session == QuerySessions ) + { + QStringList sessions = dcopSessionList( it.key(), it.data() ); + if( sessions.isEmpty() ) + { + cout << "No active sessions"; + if( !( *it ).isEmpty() ) + cout << " for user " << *it; + cout << endl; + } + else + { + cout << "Active sessions "; + if( !( *it ).isEmpty() ) + cout << "for user " << *it << " "; + cout << ":" << endl; + + QStringList::Iterator sIt; + for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) + cout << " " << *sIt << endl; + + cout << endl; + } + continue; + } + + if( getenv( "DCOPSERVER" ) ) + { + sessions.append( getenv( "DCOPSERVER" ) ); + presetDCOPServer = true; + } + + if( users.count() > 1 || ( users.count() == 1 && + ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) + { + sessions = dcopSessionList( it.key(), it.data() ); + if( sessions.isEmpty() ) + { + if( users.count() > 1 ) + continue; + else + { + cerr << "ERROR: No active KDE sessions!" << endl + << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl + << "before calling dcop." << endl; + exit( -1 ); + } + } + else if( sessions.count() > 1 && session != AllSessions ) + { + cerr << "ERROR: Multiple available KDE sessions!" << endl + << "Please specify the correct session to use with --session or use the" << endl + << "--all-sessions option to broadcast to all sessions." << endl; + exit( -1 ); + } + } +a339 143 + if( users.count() > 1 || ( users.count() == 1 && + ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) + { + // Check for ICE authority file and if the file can be read by us + QString home = it.data(); + QString iceFile = it.data() + "/.ICEauthority"; + QFileInfo fi( iceFile ); + if( iceFile.isEmpty() ) + { + cerr << "WARNING: Cannot determine home directory for user " + << it.key() << "!" << endl + << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl + << "calling dcop." << endl; + } + else if( fi.exists() ) + { + if( fi.isReadable() ) + { + char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); + putenv( envStr ); + //cerr << "ice: " << envStr << endl; + } + else + { + cerr << "WARNING: ICE authority file " << iceFile + << "is not readable by you!" << endl + << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl + << "calling dcop." << endl; + } + } + else + { + if( users.count() > 1 ) + continue; + else + { + cerr << "WARNING: Cannot find ICE authority file " + << iceFile << "!" << endl + << "Please check permissions or set the $ICEAUTHORITY" + << " variable manually before" << endl + << "calling dcop." << endl; + } + } + } + + // Main loop + // If users is an empty list we're calling for the currently logged + // in user. In this case we don't have a session, but still want + // to iterate the loop once. + QStringList::Iterator sIt = sessions.begin(); + for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) + { + if( !presetDCOPServer && !users.isEmpty() ) + { + QString dcopFile = it.data() + "/" + *sIt; + QFile f( dcopFile ); + if( !f.open( IO_ReadOnly ) ) + { + cerr << "Can't open " << dcopFile << " for reading!" << endl; + exit( -1 ); + } + + QStringList l( QStringList::split( '\n', f.readAll() ) ); + dcopServer = l.first(); + + if( dcopServer.isEmpty() ) + { + cerr << "WARNING: Unable to determine DCOP server for session " + << *sIt << "!" << endl + << "Please check permissions or set the $DCOPSERVER variable manually before" << endl + << "calling dcop." << endl; + exit( -1 ); + } + } + + delete client; + client = new DCOPClient; + if( !dcopServer.isEmpty() ) + client->setServerAddress( dcopServer.ascii() ); + bool success = client->attach(); + if( !success ) + { + cerr << "ERROR: Couldn't attach to DCOP server!" << endl; + continue; + } + dcop = client; + + switch ( args.count() ) + { + case 0: + queryApplications(""); + break; + case 1: + if (endsWith(app, '*')) + queryApplications(app); + else + queryObjects( app, "" ); + break; + case 2: + if (endsWith(objid, '*')) + queryObjects(app, objid); + else + queryFunctions( app, objid ); + break; + case 3: + default: + if( readStdin ) + { + QCStringList::Iterator replaceArg = args.end(); + + QCStringList::Iterator it; + for( it = args.begin(); it != args.end(); it++ ) + if( *it == "%1" ) + replaceArg = it; + + // Read from stdin until EOF and call function for each line read + char *buf = new char[ 1000 ]; + while ( !feof( stdin ) ) + { + fgets( buf, 1000, stdin ); + + if( replaceArg != args.end() ) + *replaceArg = buf; + + callFunction( app, objid, function, params ); + } + } + else + { + // Just call function +// cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; + callFunction( app, objid, function, params ); + } + break; + } + // Another sIt++ would make the loop infinite... + if( users.isEmpty() ) + break; + } + + // Another it++ would make the loop infinite... + if( it == users.end() ) + break; +a340 106 +} + + +int main( int argc, char** argv ) +{ + bool readStdin = false; + int numOptions = 0; + QString user; + Session session = DefaultSession; + QString sessionName; + + // Scan for command-line options first + for( int pos = 1 ; pos <= argc - 1 ; pos++ ) + { + if( strcmp( argv[ pos ], "--help" ) == 0 ) + showHelp( 0 ); + else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) + { + readStdin = true; + numOptions++; + } + else if( strcmp( argv[ pos ], "--user" ) == 0 ) + { + if( pos <= argc - 2 ) + { + user = QString::fromLocal8Bit( argv[ pos + 1] ); + numOptions +=2; + pos++; + } + else + { + cerr << "Missing username for '--user' option!" << endl << endl; + showHelp( -1 ); + } + } + else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) + { + user = "*"; + numOptions ++; + } + else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) + { + session = QuerySessions; + numOptions ++; + } + else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) + { + session = AllSessions; + numOptions ++; + } + else if( argv[ pos ][ 0 ] == '-' ) + { + cerr << "Unknown command-line option '" << argv[ pos ] + << "'." << endl << endl; + showHelp( -1 ); + } + else + break; // End of options + } + + argc -= numOptions; + + QCStringList args; + for( int i = numOptions; i < argc + numOptions - 1; i++ ) + args.append( argv[ i + 1 ] ); + + if( readStdin && args.count() < 3 ) + { + cerr << "--pipe option only supported for function calls!" << endl << endl; + showHelp( -1 ); + } + + if( user == "*" && args.count() < 3 && session != QuerySessions ) + { + cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; + showHelp( -1 ); + } + + if( session == QuerySessions && !args.isEmpty() ) + { + cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; + showHelp( -1 ); + } + + if( session == QuerySessions && user.isEmpty() ) + { + cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl + << "--all-users options!" << endl << endl; + showHelp( -1 ); + } + + if( session != DefaultSession && session != QuerySessions && + args.count() < 3 ) + { + cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl + << "calls!" << endl << endl; + showHelp( -1 ); + } + + UserList users; + if( user == "*" ) + users = userList(); + else if( !user.isEmpty() ) + users[ user ] = userList()[ user ]; + + runDCOP( args, users, session, sessionName, readStdin ); +a343 3 + +// vim: set ts=8 sts=4 sw=4 noet: + +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -n -r1.2 dcopfind.cpp +d39 1 +a39 1 +bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +d121 1 +a121 1 + if ( types.count() != args.count() ) { +d131 1 +a131 1 + marshall(arg, args, i, *it); +d133 1 +a133 1 + if ( (uint) i != args.count() ) { +d224 1 +a224 5 + QCStringList params; + for( int i = 0; i < argc; i++ ) + params.append( args[ i ] ); + + findObject( app, objid, function, params ); +Index: client/marshall.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/marshall.cpp,v +retrieving revision 1.3 +diff -n -r1.3 marshall.cpp +d245 1 +a245 1 +void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) +d247 71 +a317 10 + if (type == "QStringList") + type = "QValueList"; + if (type == "QCStringList") + type = "QValueList"; + if( i > args.count() ) + { + qWarning("Not enough arguments."); + exit(1); + } + QString s = QString::fromLocal8Bit( args[ i ] ); +d319 28 +a346 57 + if ( type == "int" ) + arg << s.toInt(); + else if ( type == "uint" ) + arg << s.toUInt(); + else if ( type == "unsigned" ) + arg << s.toUInt(); + else if ( type == "unsigned int" ) + arg << s.toUInt(); + else if ( type == "long" ) + arg << s.toLong(); + else if ( type == "long int" ) + arg << s.toLong(); + else if ( type == "unsigned long" ) + arg << s.toULong(); + else if ( type == "unsigned long int" ) + arg << s.toULong(); + else if ( type == "float" ) + arg << s.toFloat(); + else if ( type == "double" ) + arg << s.toDouble(); + else if ( type == "bool" ) + arg << mkBool( s ); + else if ( type == "QString" ) + arg << s; + else if ( type == "QCString" ) + arg << QCString( args[ i ] ); + else if ( type == "QColor" ) + arg << mkColor( s ); + else if ( type == "QPoint" ) + arg << mkPoint( s ); + else if ( type == "QSize" ) + arg << mkSize( s ); + else if ( type == "QRect" ) + arg << mkRect( s ); + else if ( type == "QVariant" ) { + if ( s == "true" || s == "false" ) + arg << QVariant( mkBool( s ), 42 ); + else if ( s.left( 4 ) == "int(" ) + arg << QVariant( s.mid(4, s.length()-5).toInt() ); + else if ( s.left( 7 ) == "QPoint(" ) + arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); + else if ( s.left( 6 ) == "QSize(" ) + arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); + else if ( s.left( 6 ) == "QRect(" ) + arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); + else if ( s.left( 7 ) == "QColor(" ) + arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); + else + arg << QVariant( s ); + } else if ( type.startsWith("QValueList<")) { + type = type.mid(11, type.length() - 12); + QStringList list; + QString delim = s; + if (delim == "[") + delim = "]"; + if (delim == "(") + delim = ")"; +a347 34 + QByteArray dummy_data; + QDataStream dummy_arg(dummy_data, IO_WriteOnly); + + uint j = i; + uint count = 0; + // Parse list to get the count + while (true) { + if( j > args.count() ) + { + qWarning("List end-delimiter '%s' not found.", delim.latin1()); + exit(1); + } + if( QString::fromLocal8Bit( args[ j ] ) == delim ) + break; + marshall( dummy_arg, args, j, type ); + count++; + } + arg << (Q_UINT32) count; + // Parse the list for real + while (true) { + if( i > args.count() ) + { + qWarning("List end-delimiter '%s' not found.", delim.latin1()); + exit(1); + } + if( QString::fromLocal8Bit( args[ i ] ) == delim ) + break; + marshall( arg, args, i, type ); + } + } else { + qWarning( "cannot handle datatype '%s'", type.latin1() ); + exit(1); + } + i++; diff --git a/kompare/tests/cvsdiff/unified.diff b/kompare/tests/cvsdiff/unified.diff new file mode 100644 index 00000000..562dee43 --- /dev/null +++ b/kompare/tests/cvsdiff/unified.diff @@ -0,0 +1,50 @@ +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -u -r1.2 dcopfind.cpp +--- client/dcopfind.cpp 2001/10/31 01:17:39 1.2 ++++ client/dcopfind.cpp 2002/01/16 18:07:51 +@@ -36,7 +36,7 @@ + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +-bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) ++bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +@@ -118,7 +118,7 @@ + f = fc; + } + +- if ( (int) types.count() != argc ) { ++ if ( types.count() != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +@@ -128,9 +128,9 @@ + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +- marshall(arg, argc, args, i, *it); ++ marshall(arg, args, i, *it); + } +- if ( (int) i != argc ) { ++ if ( (uint) i != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +@@ -221,7 +221,11 @@ + argc = 0; + } + +- findObject( app, objid, function, argc, args ); ++ QCStringList params; ++ for( int i = 0; i < argc; i++ ) ++ params.append( args[ i ] ); ++ ++ findObject( app, objid, function, params ); + + return 0; + } diff --git a/kompare/tests/cvsdiff/unifiedm.diff b/kompare/tests/cvsdiff/unifiedm.diff new file mode 100644 index 00000000..1de79f8f --- /dev/null +++ b/kompare/tests/cvsdiff/unifiedm.diff @@ -0,0 +1,924 @@ +Index: client/dcop.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcop.cpp,v +retrieving revision 1.26 +diff -u -r1.26 dcop.cpp +--- client/dcop.cpp 2001/10/31 01:17:39 1.26 ++++ client/dcop.cpp 2002/01/16 18:06:14 +@@ -20,19 +20,47 @@ + + ******************************************************************/ + +-#include ++#include ++#include ++#include ++ + #include +-#include "../kdatastream.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++// putenv() is not available on all platforms, so make sure the emulation ++// wrapper is available in those cases by loading config.h! ++#include ++ + #include "../dcopclient.h" + #include "../dcopref.h" +-#include +-#include +-#include ++#include "../kdatastream.h" + + #include "marshall.cpp" + ++typedef QMap UserList; ++ + static DCOPClient* dcop = 0; + ++static QTextStream cout( stdout, IO_WriteOnly ); ++static QTextStream cerr( stderr, IO_WriteOnly ); ++ ++/** ++ * Session to send call to ++ * DefaultSession - current session. Current KDE session when called without ++ * --user or --all-users option. Otherwise this value ignores ++ * all users with more than one active session. ++ * AllSessions - Send to all sessions found. requires --user or --all-users. ++ * QuerySessions - Don't call DCOP, return a list of available sessions. ++ * CustomSession - Use the specified session ++ */ ++enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; ++ + bool startsWith(const QCString &id, const char *str, int n) + { + return !n || (strncmp(id.data(), str, n) == 0); +@@ -118,9 +146,8 @@ + } + } + +-void callFunction( const char* app, const char* obj, const char* func, int argc, char** args ) ++void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) + { +- + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); + int right = f.find( ')' ); +@@ -136,7 +163,7 @@ + bool ok = false; + QCStringList funcs = dcop->remoteFunctions( app, obj, &ok ); + QCString realfunc; +- if ( !ok && argc == 0 ) ++ if ( !ok && args.isEmpty() ) + goto doit; + if ( !ok ) + { +@@ -153,15 +180,16 @@ + + if ( l > 0 && (*it).mid( s, l - s ) == func ) { + realfunc = (*it).mid( s ); +- int a = (*it).contains(','); +- if ( ( a == 0 && argc == 0) || ( a > 0 && a + 1 == argc ) ) ++ uint a = (*it).contains(','); ++ if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) + break; + } + } + if ( realfunc.isEmpty() ) + { + qWarning("no such function"); +- exit(1); ++// exit(1); ++ return; + } + f = realfunc; + left = f.find( '(' ); +@@ -243,11 +271,12 @@ + QCString replyType; + QDataStream arg(data, IO_WriteOnly); + +- int i = 0; +- for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +- marshall(arg, argc, args, i, *it); +- } +- if ( i != argc ) { ++ uint i = 0; ++ for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) ++ marshall( arg, args, i, *it ); ++ ++ if ( i != args.count() ) ++ { + qWarning( "arguments do not match" ); + exit(1); + } +@@ -265,79 +294,480 @@ + } + } + } +- + ++/** ++ * Show command-line help and exit ++ */ ++void showHelp( int exitCode = 0 ) ++{ ++ cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl ++ << "" << endl ++ << "Console DCOP client" << endl ++ << "" << endl ++ << "Generic options:" << endl ++ << " --help Show help about options" << endl ++ << "" << endl ++ << "Options:" << endl ++ << " --pipe Call DCOP for each line read from stdin" << endl ++ << " --user Connect to the given user's DCOP server. This option will" << endl ++ << " ignore the values of the environment vars $DCOPSERVER and" << endl ++ << " $ICEAUTHORITY, even if they are set." << endl ++ << " If the user has more than one open session, you must also" << endl ++ << " use one of the --list-sessions, --session or --als-sessions" << endl ++ << " command-line options." << endl ++ << " --all-users Send the same DCOP call to all users with a running DCOP" << endl ++ << " server. Only failed calls to existing DCOP servers will" ++ << " generate an error message. If no DCOP server is available" << endl ++ << " at all, no error will be generated." << endl; ++ ++ exit( exitCode ); ++} + +-int main( int argc, char** argv ) ++/** ++ * Return a list of all users and their home directories. ++ * Returns an empty list if /etc/passwd cannot be read for some reason. ++ */ ++static UserList userList() + { ++ UserList result; ++ ++ QFile f( "/etc/passwd" ); ++ ++ if( !f.open( IO_ReadOnly ) ) ++ { ++ cerr << "Can't open /etc/passwd for reading!" << endl; ++ return result; ++ } + +- if ( argc > 1 && argv[1][0] == '-' ) { +- fprintf( stderr, "Usage: dcop [ application [object [function [arg1] [arg2] [arg3] ... ] ] ] \n" ); +- exit(0); ++ QStringList l( QStringList::split( '\n', f.readAll() ) ); ++ ++ for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) ++ { ++ QStringList userInfo( QStringList::split( ':', *it, true ) ); ++ result[ userInfo[ 0 ] ] = userInfo[ 5 ]; + } + +- DCOPClient client; +- client.attach(); +- dcop = &client; ++ return result; ++} ++ ++/** ++ * Return a list of available DCOP sessions for the specified user ++ * An empty list means no sessions are available, or an error occurred. ++ */ ++QStringList dcopSessionList( const QString &user, const QString &home ) ++{ ++ if( home.isEmpty() ) ++ { ++ cerr << "WARNING: Cannot determine home directory for user " ++ << user << "!" << endl ++ << "Please check permissions or set the $DCOPSERVER variable manually before" << endl ++ << "calling dcop." << endl; ++ return QStringList(); ++ } ++ ++ QStringList result; ++ QFileInfo dirInfo( home ); ++ if( !dirInfo.exists() || !dirInfo.isReadable() ) ++ return result; ++ ++ QDir d( home ); ++ d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); ++ d.setNameFilter( ".DCOPserver*" ); ++ ++ const QFileInfoList *list = d.entryInfoList(); ++ if( !list ) ++ return result; ++ ++ QFileInfoListIterator it( *list ); ++ QFileInfo *fi; ++ ++ while ( ( fi = it.current() ) != 0 ) ++ { ++ if( fi->isReadable() ) ++ result.append( fi->fileName() ); ++ ++it; ++ } ++ return result; ++} + ++/** ++ * Do the actual DCOP call ++ */ ++void runDCOP( QCStringList args, UserList users, Session session, ++ const QString sessionName, bool readStdin ) ++{ + QCString app; + QCString objid; + QCString function; +- char **args = 0; +- if ((argc > 1) && (strncmp(argv[1], "DCOPRef(", 8)) == 0) ++ QCStringList params; ++ DCOPClient *client = 0L; ++ if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) + { +- char *delim = strchr(argv[1], ','); +- if (!delim) +- { +- fprintf(stderr, "Error: '%s' is not a valid DCOP reference.\n", argv[1]); +- return 1; +- } +- *delim = 0; +- app = argv[1] + 8; +- delim++; +- delim[strlen(delim)-1] = 0; +- objid = delim; +- if (argc > 2) +- function = argv[2]; +- if (argc > 3) +- args = &argv[3]; +- argc++; ++ // WARNING: This part (until the closing '}') could very ++ // well be broken now. As I don't know how to trigger and test ++ // dcoprefs this code is *not* tested. It compiles and it looks ++ // ok to me, but that's all I can say - Martijn (2001/12/24) ++ int delimPos = args[ 0 ].findRev( ',' ); ++ if( delimPos == -1 ) ++ { ++ cerr << "Error: '" << args[ 0 ] ++ << "' is not a valid DCOP reference." << endl; ++ exit( -1 ); ++ } ++ args[ 0 ][ delimPos ] = 0; ++ app = args[ 0 ].mid( 8 ); ++ delimPos++; ++ args[ 0 ][ args[ 0 ].length() - 1 ] = 0; ++ objid = args[ 0 ].mid( delimPos ); ++ if( args.count() > 1 ) ++ function = args[ 1 ]; ++ if( args.count() > 2 ) ++ { ++ params = args; ++ params.remove( params.begin() ); ++ params.remove( params.begin() ); ++ } + } + else + { +- if (argc > 1) +- app = argv[1]; +- if (argc > 2) +- objid = argv[2]; +- if (argc > 3) +- function = argv[3]; +- if (argc > 4) +- args = &argv[4]; +- } +- +- switch ( argc ) { +- case 0: +- case 1: +- queryApplications(""); +- break; +- case 2: +- if (endsWith(app, '*')) +- queryApplications(app); +- else +- queryObjects( app, "" ); +- break; +- case 3: +- if (endsWith(objid, '*')) +- queryObjects(app, objid); +- else +- queryFunctions( app, objid ); +- break; +- case 4: +- default: +- callFunction( app, objid, function, argc - 4, args ); +- break; ++ if( !args.isEmpty() ) ++ app = args[ 0 ]; ++ if( args.count() > 1 ) ++ objid = args[ 1 ]; ++ if( args.count() > 2 ) ++ function = args[ 2 ]; ++ if( args.count() > 3) ++ { ++ params = args; ++ params.remove( params.begin() ); ++ params.remove( params.begin() ); ++ params.remove( params.begin() ); ++ } ++ } ++ ++ bool firstRun = true; ++ UserList::Iterator it; ++ QStringList sessions; ++ bool presetDCOPServer = false; ++// char *dcopStr = 0L; ++ QString dcopServer; ++ ++ for( it = users.begin(); it != users.end() || firstRun; it++ ) ++ { ++ firstRun = false; ++ ++ //cout << "Iterating '" << it.key() << "'" << endl; ++ ++ if( session == QuerySessions ) ++ { ++ QStringList sessions = dcopSessionList( it.key(), it.data() ); ++ if( sessions.isEmpty() ) ++ { ++ cout << "No active sessions"; ++ if( !( *it ).isEmpty() ) ++ cout << " for user " << *it; ++ cout << endl; ++ } ++ else ++ { ++ cout << "Active sessions "; ++ if( !( *it ).isEmpty() ) ++ cout << "for user " << *it << " "; ++ cout << ":" << endl; ++ ++ QStringList::Iterator sIt; ++ for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) ++ cout << " " << *sIt << endl; ++ ++ cout << endl; ++ } ++ continue; ++ } ++ ++ if( getenv( "DCOPSERVER" ) ) ++ { ++ sessions.append( getenv( "DCOPSERVER" ) ); ++ presetDCOPServer = true; ++ } ++ ++ if( users.count() > 1 || ( users.count() == 1 && ++ ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) ++ { ++ sessions = dcopSessionList( it.key(), it.data() ); ++ if( sessions.isEmpty() ) ++ { ++ if( users.count() > 1 ) ++ continue; ++ else ++ { ++ cerr << "ERROR: No active KDE sessions!" << endl ++ << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl ++ << "before calling dcop." << endl; ++ exit( -1 ); ++ } ++ } ++ else if( sessions.count() > 1 && session != AllSessions ) ++ { ++ cerr << "ERROR: Multiple available KDE sessions!" << endl ++ << "Please specify the correct session to use with --session or use the" << endl ++ << "--all-sessions option to broadcast to all sessions." << endl; ++ exit( -1 ); ++ } ++ } + ++ if( users.count() > 1 || ( users.count() == 1 && ++ ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) ++ { ++ // Check for ICE authority file and if the file can be read by us ++ QString home = it.data(); ++ QString iceFile = it.data() + "/.ICEauthority"; ++ QFileInfo fi( iceFile ); ++ if( iceFile.isEmpty() ) ++ { ++ cerr << "WARNING: Cannot determine home directory for user " ++ << it.key() << "!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ else if( fi.exists() ) ++ { ++ if( fi.isReadable() ) ++ { ++ char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); ++ putenv( envStr ); ++ //cerr << "ice: " << envStr << endl; ++ } ++ else ++ { ++ cerr << "WARNING: ICE authority file " << iceFile ++ << "is not readable by you!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ } ++ else ++ { ++ if( users.count() > 1 ) ++ continue; ++ else ++ { ++ cerr << "WARNING: Cannot find ICE authority file " ++ << iceFile << "!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY" ++ << " variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ } ++ } ++ ++ // Main loop ++ // If users is an empty list we're calling for the currently logged ++ // in user. In this case we don't have a session, but still want ++ // to iterate the loop once. ++ QStringList::Iterator sIt = sessions.begin(); ++ for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) ++ { ++ if( !presetDCOPServer && !users.isEmpty() ) ++ { ++ QString dcopFile = it.data() + "/" + *sIt; ++ QFile f( dcopFile ); ++ if( !f.open( IO_ReadOnly ) ) ++ { ++ cerr << "Can't open " << dcopFile << " for reading!" << endl; ++ exit( -1 ); ++ } ++ ++ QStringList l( QStringList::split( '\n', f.readAll() ) ); ++ dcopServer = l.first(); ++ ++ if( dcopServer.isEmpty() ) ++ { ++ cerr << "WARNING: Unable to determine DCOP server for session " ++ << *sIt << "!" << endl ++ << "Please check permissions or set the $DCOPSERVER variable manually before" << endl ++ << "calling dcop." << endl; ++ exit( -1 ); ++ } ++ } ++ ++ delete client; ++ client = new DCOPClient; ++ if( !dcopServer.isEmpty() ) ++ client->setServerAddress( dcopServer.ascii() ); ++ bool success = client->attach(); ++ if( !success ) ++ { ++ cerr << "ERROR: Couldn't attach to DCOP server!" << endl; ++ continue; ++ } ++ dcop = client; ++ ++ switch ( args.count() ) ++ { ++ case 0: ++ queryApplications(""); ++ break; ++ case 1: ++ if (endsWith(app, '*')) ++ queryApplications(app); ++ else ++ queryObjects( app, "" ); ++ break; ++ case 2: ++ if (endsWith(objid, '*')) ++ queryObjects(app, objid); ++ else ++ queryFunctions( app, objid ); ++ break; ++ case 3: ++ default: ++ if( readStdin ) ++ { ++ QCStringList::Iterator replaceArg = args.end(); ++ ++ QCStringList::Iterator it; ++ for( it = args.begin(); it != args.end(); it++ ) ++ if( *it == "%1" ) ++ replaceArg = it; ++ ++ // Read from stdin until EOF and call function for each line read ++ char *buf = new char[ 1000 ]; ++ while ( !feof( stdin ) ) ++ { ++ fgets( buf, 1000, stdin ); ++ ++ if( replaceArg != args.end() ) ++ *replaceArg = buf; ++ ++ callFunction( app, objid, function, params ); ++ } ++ } ++ else ++ { ++ // Just call function ++// cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; ++ callFunction( app, objid, function, params ); ++ } ++ break; ++ } ++ // Another sIt++ would make the loop infinite... ++ if( users.isEmpty() ) ++ break; ++ } ++ ++ // Another it++ would make the loop infinite... ++ if( it == users.end() ) ++ break; + } ++} ++ + ++int main( int argc, char** argv ) ++{ ++ bool readStdin = false; ++ int numOptions = 0; ++ QString user; ++ Session session = DefaultSession; ++ QString sessionName; ++ ++ // Scan for command-line options first ++ for( int pos = 1 ; pos <= argc - 1 ; pos++ ) ++ { ++ if( strcmp( argv[ pos ], "--help" ) == 0 ) ++ showHelp( 0 ); ++ else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) ++ { ++ readStdin = true; ++ numOptions++; ++ } ++ else if( strcmp( argv[ pos ], "--user" ) == 0 ) ++ { ++ if( pos <= argc - 2 ) ++ { ++ user = QString::fromLocal8Bit( argv[ pos + 1] ); ++ numOptions +=2; ++ pos++; ++ } ++ else ++ { ++ cerr << "Missing username for '--user' option!" << endl << endl; ++ showHelp( -1 ); ++ } ++ } ++ else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) ++ { ++ user = "*"; ++ numOptions ++; ++ } ++ else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) ++ { ++ session = QuerySessions; ++ numOptions ++; ++ } ++ else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) ++ { ++ session = AllSessions; ++ numOptions ++; ++ } ++ else if( argv[ pos ][ 0 ] == '-' ) ++ { ++ cerr << "Unknown command-line option '" << argv[ pos ] ++ << "'." << endl << endl; ++ showHelp( -1 ); ++ } ++ else ++ break; // End of options ++ } ++ ++ argc -= numOptions; ++ ++ QCStringList args; ++ for( int i = numOptions; i < argc + numOptions - 1; i++ ) ++ args.append( argv[ i + 1 ] ); ++ ++ if( readStdin && args.count() < 3 ) ++ { ++ cerr << "--pipe option only supported for function calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( user == "*" && args.count() < 3 && session != QuerySessions ) ++ { ++ cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session == QuerySessions && !args.isEmpty() ) ++ { ++ cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session == QuerySessions && user.isEmpty() ) ++ { ++ cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl ++ << "--all-users options!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session != DefaultSession && session != QuerySessions && ++ args.count() < 3 ) ++ { ++ cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl ++ << "calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ UserList users; ++ if( user == "*" ) ++ users = userList(); ++ else if( !user.isEmpty() ) ++ users[ user ] = userList()[ user ]; ++ ++ runDCOP( args, users, session, sessionName, readStdin ); ++ + return 0; + } ++ ++// vim: set ts=8 sts=4 sw=4 noet: ++ +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -u -r1.2 dcopfind.cpp +--- client/dcopfind.cpp 2001/10/31 01:17:39 1.2 ++++ client/dcopfind.cpp 2002/01/16 18:06:14 +@@ -36,7 +36,7 @@ + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +-bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) ++bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +@@ -118,7 +118,7 @@ + f = fc; + } + +- if ( (int) types.count() != argc ) { ++ if ( types.count() != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +@@ -128,9 +128,9 @@ + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +- marshall(arg, argc, args, i, *it); ++ marshall(arg, args, i, *it); + } +- if ( (int) i != argc ) { ++ if ( (uint) i != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +@@ -221,7 +221,11 @@ + argc = 0; + } + +- findObject( app, objid, function, argc, args ); ++ QCStringList params; ++ for( int i = 0; i < argc; i++ ) ++ params.append( args[ i ] ); ++ ++ findObject( app, objid, function, params ); + + return 0; + } +Index: client/marshall.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/marshall.cpp,v +retrieving revision 1.3 +diff -u -r1.3 marshall.cpp +--- client/marshall.cpp 2001/10/31 01:17:39 1.3 ++++ client/marshall.cpp 2002/01/16 18:06:14 +@@ -242,108 +242,110 @@ + + } + +-void marshall(QDataStream &arg, int argc, char **argv, int &i, QString type) ++void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) + { +- if (type == "QStringList") +- type = "QValueList"; +- if (type == "QCStringList") +- type = "QValueList"; +- if (i >= argc) +- { +- qWarning("Not enough arguments."); +- exit(1); +- } +- QString s = QString::fromLocal8Bit(argv[i]); +- +- if ( type == "int" ) +- arg << s.toInt(); +- else if ( type == "uint" ) +- arg << s.toUInt(); +- else if ( type == "unsigned" ) +- arg << s.toUInt(); +- else if ( type == "unsigned int" ) +- arg << s.toUInt(); +- else if ( type == "long" ) +- arg << s.toLong(); +- else if ( type == "long int" ) +- arg << s.toLong(); +- else if ( type == "unsigned long" ) +- arg << s.toULong(); +- else if ( type == "unsigned long int" ) +- arg << s.toULong(); +- else if ( type == "float" ) +- arg << s.toFloat(); +- else if ( type == "double" ) +- arg << s.toDouble(); +- else if ( type == "bool" ) +- arg << mkBool( s ); +- else if ( type == "QString" ) +- arg << s; +- else if ( type == "QCString" ) +- arg << QCString( argv[i] ); +- else if ( type == "QColor" ) +- arg << mkColor( s ); +- else if ( type == "QPoint" ) +- arg << mkPoint( s ); +- else if ( type == "QSize" ) +- arg << mkSize( s ); +- else if ( type == "QRect" ) +- arg << mkRect( s ); +- else if ( type == "QVariant" ) { +- if ( s == "true" || s == "false" ) +- arg << QVariant( mkBool( s ), 42 ); +- else if ( s.left( 4 ) == "int(" ) +- arg << QVariant( s.mid(4, s.length()-5).toInt() ); +- else if ( s.left( 7 ) == "QPoint(" ) +- arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +- else if ( s.left( 6 ) == "QSize(" ) +- arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +- else if ( s.left( 6 ) == "QRect(" ) +- arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +- else if ( s.left( 7 ) == "QColor(" ) +- arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +- else +- arg << QVariant( s ); +- } else if ( type.startsWith("QValueList<")) { +- type = type.mid(11, type.length() - 12); +- QStringList list; +- QString delim = s; +- if (delim == "[") +- delim = "]"; +- if (delim == "(") +- delim = ")"; +- i++; +- QByteArray dummy_data; +- QDataStream dummy_arg(dummy_data, IO_WriteOnly); ++ if (type == "QStringList") ++ type = "QValueList"; ++ if (type == "QCStringList") ++ type = "QValueList"; ++ if( i > args.count() ) ++ { ++ qWarning("Not enough arguments."); ++ exit(1); ++ } ++ QString s = QString::fromLocal8Bit( args[ i ] ); + +- int j = i; +- int count = 0; +- // Parse list to get the count +- while (true) { +- if (j >= argc) +- { +- qWarning("List end-delimiter '%s' not found.", delim.latin1()); +- exit(1); +- } +- if (argv[j] == delim) break; +- marshall(dummy_arg, argc, argv, j, type); +- count++; +- } +- arg << (Q_UINT32) count; +- // Parse the list for real +- while (true) { +- if (i >= argc) +- { +- qWarning("List end-delimiter '%s' not found.", delim.latin1()); +- exit(1); +- } +- if (argv[i] == delim) break; +- marshall(arg, argc, argv, i, type); +- } +- } else { +- qWarning( "cannot handle datatype '%s'", type.latin1() ); +- exit(1); +- } ++ if ( type == "int" ) ++ arg << s.toInt(); ++ else if ( type == "uint" ) ++ arg << s.toUInt(); ++ else if ( type == "unsigned" ) ++ arg << s.toUInt(); ++ else if ( type == "unsigned int" ) ++ arg << s.toUInt(); ++ else if ( type == "long" ) ++ arg << s.toLong(); ++ else if ( type == "long int" ) ++ arg << s.toLong(); ++ else if ( type == "unsigned long" ) ++ arg << s.toULong(); ++ else if ( type == "unsigned long int" ) ++ arg << s.toULong(); ++ else if ( type == "float" ) ++ arg << s.toFloat(); ++ else if ( type == "double" ) ++ arg << s.toDouble(); ++ else if ( type == "bool" ) ++ arg << mkBool( s ); ++ else if ( type == "QString" ) ++ arg << s; ++ else if ( type == "QCString" ) ++ arg << QCString( args[ i ] ); ++ else if ( type == "QColor" ) ++ arg << mkColor( s ); ++ else if ( type == "QPoint" ) ++ arg << mkPoint( s ); ++ else if ( type == "QSize" ) ++ arg << mkSize( s ); ++ else if ( type == "QRect" ) ++ arg << mkRect( s ); ++ else if ( type == "QVariant" ) { ++ if ( s == "true" || s == "false" ) ++ arg << QVariant( mkBool( s ), 42 ); ++ else if ( s.left( 4 ) == "int(" ) ++ arg << QVariant( s.mid(4, s.length()-5).toInt() ); ++ else if ( s.left( 7 ) == "QPoint(" ) ++ arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); ++ else if ( s.left( 6 ) == "QSize(" ) ++ arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); ++ else if ( s.left( 6 ) == "QRect(" ) ++ arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); ++ else if ( s.left( 7 ) == "QColor(" ) ++ arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); ++ else ++ arg << QVariant( s ); ++ } else if ( type.startsWith("QValueList<")) { ++ type = type.mid(11, type.length() - 12); ++ QStringList list; ++ QString delim = s; ++ if (delim == "[") ++ delim = "]"; ++ if (delim == "(") ++ delim = ")"; + i++; ++ QByteArray dummy_data; ++ QDataStream dummy_arg(dummy_data, IO_WriteOnly); ++ ++ uint j = i; ++ uint count = 0; ++ // Parse list to get the count ++ while (true) { ++ if( j > args.count() ) ++ { ++ qWarning("List end-delimiter '%s' not found.", delim.latin1()); ++ exit(1); ++ } ++ if( QString::fromLocal8Bit( args[ j ] ) == delim ) ++ break; ++ marshall( dummy_arg, args, j, type ); ++ count++; ++ } ++ arg << (Q_UINT32) count; ++ // Parse the list for real ++ while (true) { ++ if( i > args.count() ) ++ { ++ qWarning("List end-delimiter '%s' not found.", delim.latin1()); ++ exit(1); ++ } ++ if( QString::fromLocal8Bit( args[ i ] ) == delim ) ++ break; ++ marshall( arg, args, i, type ); ++ } ++ } else { ++ qWarning( "cannot handle datatype '%s'", type.latin1() ); ++ exit(1); ++ } ++ i++; + } + diff --git a/kompare/tests/diff/context.diff b/kompare/tests/diff/context.diff new file mode 100644 index 00000000..e17df000 --- /dev/null +++ b/kompare/tests/diff/context.diff @@ -0,0 +1,27 @@ +*** /home/John/lao Thu Apr 12 11:09:30 2001 +--- /home/John/tzu Sat Jul 28 13:23:25 2001 +*************** +*** 1,7 **** +- The Way that can be told of is not the eternal Way; +- The name that can be named is not the eternal name. + The Nameless is the origin of Heaven and Earth; +! The Named is the mother of all things. + Therefore let there always be non-being, + so we may see their subtlety, + And let there always be being, +--- 1,6 ---- + The Nameless is the origin of Heaven and Earth; +! The named is the mother of all things. +! + Therefore let there always be non-being, + so we may see their subtlety, + And let there always be being, +*************** +*** 9,11 **** +--- 8,13 ---- + The two are the same, + But after they are produced, + they have different names. ++ They both may be called deep and profound. ++ Deeper and more profound, ++ The door of all subtleties! diff --git a/kompare/tests/diff/contextm.diff b/kompare/tests/diff/contextm.diff new file mode 100644 index 00000000..0b1bba5a --- /dev/null +++ b/kompare/tests/diff/contextm.diff @@ -0,0 +1,1032 @@ +diff -cr dcop/client/dcop.cpp dcop2/client/dcop.cpp +*** dcop/client/dcop.cpp Wed Jan 30 22:38:07 2002 +--- dcop2/client/dcop.cpp Wed Jan 30 22:37:04 2002 +*************** +*** 20,38 **** + + ******************************************************************/ + +! #include + #include +! #include "../kdatastream.h" + #include "../dcopclient.h" + #include "../dcopref.h" +! #include +! #include +! #include + + #include "marshall.cpp" + + static DCOPClient* dcop = 0; + + bool startsWith(const QCString &id, const char *str, int n) + { + return !n || (strncmp(id.data(), str, n) == 0); +--- 20,66 ---- + + ******************************************************************/ + +! #include +! #include +! #include +! + #include +! #include +! #include +! #include +! #include +! #include +! #include +! #include +! +! // putenv() is not available on all platforms, so make sure the emulation +! // wrapper is available in those cases by loading config.h! +! #include +! + #include "../dcopclient.h" + #include "../dcopref.h" +! #include "../kdatastream.h" + + #include "marshall.cpp" + ++ typedef QMap UserList; ++ + static DCOPClient* dcop = 0; + ++ static QTextStream cout( stdout, IO_WriteOnly ); ++ static QTextStream cerr( stderr, IO_WriteOnly ); ++ ++ /** ++ * Session to send call to ++ * DefaultSession - current session. Current KDE session when called without ++ * --user or --all-users option. Otherwise this value ignores ++ * all users with more than one active session. ++ * AllSessions - Send to all sessions found. requires --user or --all-users. ++ * QuerySessions - Don't call DCOP, return a list of available sessions. ++ * CustomSession - Use the specified session ++ */ ++ enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; ++ + bool startsWith(const QCString &id, const char *str, int n) + { + return !n || (strncmp(id.data(), str, n) == 0); +*************** +*** 118,126 **** + } + } + +! void callFunction( const char* app, const char* obj, const char* func, int argc, char** args ) + { +- + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); + int right = f.find( ')' ); +--- 146,153 ---- + } + } + +! void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); + int right = f.find( ')' ); +*************** +*** 136,142 **** + bool ok = false; + QCStringList funcs = dcop->remoteFunctions( app, obj, &ok ); + QCString realfunc; +! if ( !ok && argc == 0 ) + goto doit; + if ( !ok ) + { +--- 163,169 ---- + bool ok = false; + QCStringList funcs = dcop->remoteFunctions( app, obj, &ok ); + QCString realfunc; +! if ( !ok && args.isEmpty() ) + goto doit; + if ( !ok ) + { +*************** +*** 153,167 **** + + if ( l > 0 && (*it).mid( s, l - s ) == func ) { + realfunc = (*it).mid( s ); +! int a = (*it).contains(','); +! if ( ( a == 0 && argc == 0) || ( a > 0 && a + 1 == argc ) ) + break; + } + } + if ( realfunc.isEmpty() ) + { + qWarning("no such function"); +! exit(1); + } + f = realfunc; + left = f.find( '(' ); +--- 180,195 ---- + + if ( l > 0 && (*it).mid( s, l - s ) == func ) { + realfunc = (*it).mid( s ); +! uint a = (*it).contains(','); +! if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) + break; + } + } + if ( realfunc.isEmpty() ) + { + qWarning("no such function"); +! // exit(1); +! return; + } + f = realfunc; + left = f.find( '(' ); +*************** +*** 243,253 **** + QCString replyType; + QDataStream arg(data, IO_WriteOnly); + +! int i = 0; +! for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +! marshall(arg, argc, args, i, *it); +! } +! if ( i != argc ) { + qWarning( "arguments do not match" ); + exit(1); + } +--- 271,282 ---- + QCString replyType; + QDataStream arg(data, IO_WriteOnly); + +! uint i = 0; +! for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) +! marshall( arg, args, i, *it ); +! +! if ( i != args.count() ) +! { + qWarning( "arguments do not match" ); + exit(1); + } +*************** +*** 266,343 **** + } + } + + +! +! int main( int argc, char** argv ) + { + +! if ( argc > 1 && argv[1][0] == '-' ) { +! fprintf( stderr, "Usage: dcop [ application [object [function [arg1] [arg2] [arg3] ... ] ] ] \n" ); +! exit(0); + } + +! DCOPClient client; +! client.attach(); +! dcop = &client; + + QCString app; + QCString objid; + QCString function; +! char **args = 0; +! if ((argc > 1) && (strncmp(argv[1], "DCOPRef(", 8)) == 0) + { +! char *delim = strchr(argv[1], ','); +! if (!delim) +! { +! fprintf(stderr, "Error: '%s' is not a valid DCOP reference.\n", argv[1]); +! return 1; +! } +! *delim = 0; +! app = argv[1] + 8; +! delim++; +! delim[strlen(delim)-1] = 0; +! objid = delim; +! if (argc > 2) +! function = argv[2]; +! if (argc > 3) +! args = &argv[3]; +! argc++; + } + else + { +! if (argc > 1) +! app = argv[1]; +! if (argc > 2) +! objid = argv[2]; +! if (argc > 3) +! function = argv[3]; +! if (argc > 4) +! args = &argv[4]; +! } +! +! switch ( argc ) { +! case 0: +! case 1: +! queryApplications(""); +! break; +! case 2: +! if (endsWith(app, '*')) +! queryApplications(app); +! else +! queryObjects( app, "" ); +! break; +! case 3: +! if (endsWith(objid, '*')) +! queryObjects(app, objid); +! else +! queryFunctions( app, objid ); +! break; +! case 4: +! default: +! callFunction( app, objid, function, argc - 4, args ); +! break; + + } + + return 0; + } +--- 295,773 ---- + } + } + ++ /** ++ * Show command-line help and exit ++ */ ++ void showHelp( int exitCode = 0 ) ++ { ++ cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl ++ << "" << endl ++ << "Console DCOP client" << endl ++ << "" << endl ++ << "Generic options:" << endl ++ << " --help Show help about options" << endl ++ << "" << endl ++ << "Options:" << endl ++ << " --pipe Call DCOP for each line read from stdin" << endl ++ << " --user Connect to the given user's DCOP server. This option will" << endl ++ << " ignore the values of the environment vars $DCOPSERVER and" << endl ++ << " $ICEAUTHORITY, even if they are set." << endl ++ << " If the user has more than one open session, you must also" << endl ++ << " use one of the --list-sessions, --session or --als-sessions" << endl ++ << " command-line options." << endl ++ << " --all-users Send the same DCOP call to all users with a running DCOP" << endl ++ << " server. Only failed calls to existing DCOP servers will" ++ << " generate an error message. If no DCOP server is available" << endl ++ << " at all, no error will be generated." << endl; ++ ++ exit( exitCode ); ++ } + +! /** +! * Return a list of all users and their home directories. +! * Returns an empty list if /etc/passwd cannot be read for some reason. +! */ +! static UserList userList() + { ++ UserList result; ++ ++ QFile f( "/etc/passwd" ); ++ ++ if( !f.open( IO_ReadOnly ) ) ++ { ++ cerr << "Can't open /etc/passwd for reading!" << endl; ++ return result; ++ } + +! QStringList l( QStringList::split( '\n', f.readAll() ) ); +! +! for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) +! { +! QStringList userInfo( QStringList::split( ':', *it, true ) ); +! result[ userInfo[ 0 ] ] = userInfo[ 5 ]; + } + +! return result; +! } +! +! /** +! * Return a list of available DCOP sessions for the specified user +! * An empty list means no sessions are available, or an error occurred. +! */ +! QStringList dcopSessionList( const QString &user, const QString &home ) +! { +! if( home.isEmpty() ) +! { +! cerr << "WARNING: Cannot determine home directory for user " +! << user << "!" << endl +! << "Please check permissions or set the $DCOPSERVER variable manually before" << endl +! << "calling dcop." << endl; +! return QStringList(); +! } +! +! QStringList result; +! QFileInfo dirInfo( home ); +! if( !dirInfo.exists() || !dirInfo.isReadable() ) +! return result; +! +! QDir d( home ); +! d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); +! d.setNameFilter( ".DCOPserver*" ); +! +! const QFileInfoList *list = d.entryInfoList(); +! if( !list ) +! return result; +! +! QFileInfoListIterator it( *list ); +! QFileInfo *fi; +! +! while ( ( fi = it.current() ) != 0 ) +! { +! if( fi->isReadable() ) +! result.append( fi->fileName() ); +! ++it; +! } +! return result; +! } + ++ /** ++ * Do the actual DCOP call ++ */ ++ void runDCOP( QCStringList args, UserList users, Session session, ++ const QString sessionName, bool readStdin ) ++ { + QCString app; + QCString objid; + QCString function; +! QCStringList params; +! DCOPClient *client = 0L; +! if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) + { +! // WARNING: This part (until the closing '}') could very +! // well be broken now. As I don't know how to trigger and test +! // dcoprefs this code is *not* tested. It compiles and it looks +! // ok to me, but that's all I can say - Martijn (2001/12/24) +! int delimPos = args[ 0 ].findRev( ',' ); +! if( delimPos == -1 ) +! { +! cerr << "Error: '" << args[ 0 ] +! << "' is not a valid DCOP reference." << endl; +! exit( -1 ); +! } +! args[ 0 ][ delimPos ] = 0; +! app = args[ 0 ].mid( 8 ); +! delimPos++; +! args[ 0 ][ args[ 0 ].length() - 1 ] = 0; +! objid = args[ 0 ].mid( delimPos ); +! if( args.count() > 1 ) +! function = args[ 1 ]; +! if( args.count() > 2 ) +! { +! params = args; +! params.remove( params.begin() ); +! params.remove( params.begin() ); +! } + } + else + { +! if( !args.isEmpty() ) +! app = args[ 0 ]; +! if( args.count() > 1 ) +! objid = args[ 1 ]; +! if( args.count() > 2 ) +! function = args[ 2 ]; +! if( args.count() > 3) +! { +! params = args; +! params.remove( params.begin() ); +! params.remove( params.begin() ); +! params.remove( params.begin() ); +! } +! } +! +! bool firstRun = true; +! UserList::Iterator it; +! QStringList sessions; +! bool presetDCOPServer = false; +! // char *dcopStr = 0L; +! QString dcopServer; +! +! for( it = users.begin(); it != users.end() || firstRun; it++ ) +! { +! firstRun = false; +! +! //cout << "Iterating '" << it.key() << "'" << endl; +! +! if( session == QuerySessions ) +! { +! QStringList sessions = dcopSessionList( it.key(), it.data() ); +! if( sessions.isEmpty() ) +! { +! cout << "No active sessions"; +! if( !( *it ).isEmpty() ) +! cout << " for user " << *it; +! cout << endl; +! } +! else +! { +! cout << "Active sessions "; +! if( !( *it ).isEmpty() ) +! cout << "for user " << *it << " "; +! cout << ":" << endl; +! +! QStringList::Iterator sIt; +! for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) +! cout << " " << *sIt << endl; +! +! cout << endl; +! } +! continue; +! } +! +! if( getenv( "DCOPSERVER" ) ) +! { +! sessions.append( getenv( "DCOPSERVER" ) ); +! presetDCOPServer = true; +! } +! +! if( users.count() > 1 || ( users.count() == 1 && +! ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) +! { +! sessions = dcopSessionList( it.key(), it.data() ); +! if( sessions.isEmpty() ) +! { +! if( users.count() > 1 ) +! continue; +! else +! { +! cerr << "ERROR: No active KDE sessions!" << endl +! << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl +! << "before calling dcop." << endl; +! exit( -1 ); +! } +! } +! else if( sessions.count() > 1 && session != AllSessions ) +! { +! cerr << "ERROR: Multiple available KDE sessions!" << endl +! << "Please specify the correct session to use with --session or use the" << endl +! << "--all-sessions option to broadcast to all sessions." << endl; +! exit( -1 ); +! } +! } + ++ if( users.count() > 1 || ( users.count() == 1 && ++ ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) ++ { ++ // Check for ICE authority file and if the file can be read by us ++ QString home = it.data(); ++ QString iceFile = it.data() + "/.ICEauthority"; ++ QFileInfo fi( iceFile ); ++ if( iceFile.isEmpty() ) ++ { ++ cerr << "WARNING: Cannot determine home directory for user " ++ << it.key() << "!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ else if( fi.exists() ) ++ { ++ if( fi.isReadable() ) ++ { ++ char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); ++ putenv( envStr ); ++ //cerr << "ice: " << envStr << endl; ++ } ++ else ++ { ++ cerr << "WARNING: ICE authority file " << iceFile ++ << "is not readable by you!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ } ++ else ++ { ++ if( users.count() > 1 ) ++ continue; ++ else ++ { ++ cerr << "WARNING: Cannot find ICE authority file " ++ << iceFile << "!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY" ++ << " variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ } ++ } ++ ++ // Main loop ++ // If users is an empty list we're calling for the currently logged ++ // in user. In this case we don't have a session, but still want ++ // to iterate the loop once. ++ QStringList::Iterator sIt = sessions.begin(); ++ for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) ++ { ++ if( !presetDCOPServer && !users.isEmpty() ) ++ { ++ QString dcopFile = it.data() + "/" + *sIt; ++ QFile f( dcopFile ); ++ if( !f.open( IO_ReadOnly ) ) ++ { ++ cerr << "Can't open " << dcopFile << " for reading!" << endl; ++ exit( -1 ); ++ } ++ ++ QStringList l( QStringList::split( '\n', f.readAll() ) ); ++ dcopServer = l.first(); ++ ++ if( dcopServer.isEmpty() ) ++ { ++ cerr << "WARNING: Unable to determine DCOP server for session " ++ << *sIt << "!" << endl ++ << "Please check permissions or set the $DCOPSERVER variable manually before" << endl ++ << "calling dcop." << endl; ++ exit( -1 ); ++ } ++ } ++ ++ delete client; ++ client = new DCOPClient; ++ if( !dcopServer.isEmpty() ) ++ client->setServerAddress( dcopServer.ascii() ); ++ bool success = client->attach(); ++ if( !success ) ++ { ++ cerr << "ERROR: Couldn't attach to DCOP server!" << endl; ++ continue; ++ } ++ dcop = client; ++ ++ switch ( args.count() ) ++ { ++ case 0: ++ queryApplications(""); ++ break; ++ case 1: ++ if (endsWith(app, '*')) ++ queryApplications(app); ++ else ++ queryObjects( app, "" ); ++ break; ++ case 2: ++ if (endsWith(objid, '*')) ++ queryObjects(app, objid); ++ else ++ queryFunctions( app, objid ); ++ break; ++ case 3: ++ default: ++ if( readStdin ) ++ { ++ QCStringList::Iterator replaceArg = args.end(); ++ ++ QCStringList::Iterator it; ++ for( it = args.begin(); it != args.end(); it++ ) ++ if( *it == "%1" ) ++ replaceArg = it; ++ ++ // Read from stdin until EOF and call function for each line read ++ char *buf = new char[ 1000 ]; ++ while ( !feof( stdin ) ) ++ { ++ fgets( buf, 1000, stdin ); ++ ++ if( replaceArg != args.end() ) ++ *replaceArg = buf; ++ ++ callFunction( app, objid, function, params ); ++ } ++ } ++ else ++ { ++ // Just call function ++ // cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; ++ callFunction( app, objid, function, params ); ++ } ++ break; ++ } ++ // Another sIt++ would make the loop infinite... ++ if( users.isEmpty() ) ++ break; ++ } ++ ++ // Another it++ would make the loop infinite... ++ if( it == users.end() ) ++ break; + } ++ } ++ ++ ++ int main( int argc, char** argv ) ++ { ++ bool readStdin = false; ++ int numOptions = 0; ++ QString user; ++ Session session = DefaultSession; ++ QString sessionName; ++ ++ // Scan for command-line options first ++ for( int pos = 1 ; pos <= argc - 1 ; pos++ ) ++ { ++ if( strcmp( argv[ pos ], "--help" ) == 0 ) ++ showHelp( 0 ); ++ else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) ++ { ++ readStdin = true; ++ numOptions++; ++ } ++ else if( strcmp( argv[ pos ], "--user" ) == 0 ) ++ { ++ if( pos <= argc - 2 ) ++ { ++ user = QString::fromLocal8Bit( argv[ pos + 1] ); ++ numOptions +=2; ++ pos++; ++ } ++ else ++ { ++ cerr << "Missing username for '--user' option!" << endl << endl; ++ showHelp( -1 ); ++ } ++ } ++ else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) ++ { ++ user = "*"; ++ numOptions ++; ++ } ++ else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) ++ { ++ session = QuerySessions; ++ numOptions ++; ++ } ++ else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) ++ { ++ session = AllSessions; ++ numOptions ++; ++ } ++ else if( argv[ pos ][ 0 ] == '-' ) ++ { ++ cerr << "Unknown command-line option '" << argv[ pos ] ++ << "'." << endl << endl; ++ showHelp( -1 ); ++ } ++ else ++ break; // End of options ++ } ++ ++ argc -= numOptions; ++ ++ QCStringList args; ++ for( int i = numOptions; i < argc + numOptions - 1; i++ ) ++ args.append( argv[ i + 1 ] ); ++ ++ if( readStdin && args.count() < 3 ) ++ { ++ cerr << "--pipe option only supported for function calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( user == "*" && args.count() < 3 && session != QuerySessions ) ++ { ++ cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session == QuerySessions && !args.isEmpty() ) ++ { ++ cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session == QuerySessions && user.isEmpty() ) ++ { ++ cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl ++ << "--all-users options!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session != DefaultSession && session != QuerySessions && ++ args.count() < 3 ) ++ { ++ cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl ++ << "calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ UserList users; ++ if( user == "*" ) ++ users = userList(); ++ else if( !user.isEmpty() ) ++ users[ user ] = userList()[ user ]; ++ ++ runDCOP( args, users, session, sessionName, readStdin ); + + return 0; + } ++ ++ // vim: set ts=8 sts=4 sw=4 noet: ++ +diff -cr dcop/client/dcopfind.cpp dcop2/client/dcopfind.cpp +*** dcop/client/dcopfind.cpp Wed Jan 30 22:38:07 2002 +--- dcop2/client/dcopfind.cpp Wed Jan 30 22:37:04 2002 +*************** +*** 36,42 **** + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +! bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +--- 36,42 ---- + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +! bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +*************** +*** 118,124 **** + f = fc; + } + +! if ( (int) types.count() != argc ) { + qWarning( "arguments do not match" ); + exit(1); + } +--- 118,124 ---- + f = fc; + } + +! if ( types.count() != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +*************** +*** 128,136 **** + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +! marshall(arg, argc, args, i, *it); + } +! if ( (int) i != argc ) { + qWarning( "arguments do not match" ); + exit(1); + } +--- 128,136 ---- + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +! marshall(arg, args, i, *it); + } +! if ( (uint) i != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +*************** +*** 221,227 **** + argc = 0; + } + +! findObject( app, objid, function, argc, args ); + + return 0; + } +--- 221,231 ---- + argc = 0; + } + +! QCStringList params; +! for( int i = 0; i < argc; i++ ) +! params.append( args[ i ] ); +! +! findObject( app, objid, function, params ); + + return 0; + } +diff -cr dcop/client/marshall.cpp dcop2/client/marshall.cpp +*** dcop/client/marshall.cpp Wed Jan 30 22:38:07 2002 +--- dcop2/client/marshall.cpp Wed Jan 30 22:37:04 2002 +*************** +*** 242,349 **** + + } + +! void marshall(QDataStream &arg, int argc, char **argv, int &i, QString type) + { +! if (type == "QStringList") +! type = "QValueList"; +! if (type == "QCStringList") +! type = "QValueList"; +! if (i >= argc) +! { +! qWarning("Not enough arguments."); +! exit(1); +! } +! QString s = QString::fromLocal8Bit(argv[i]); +! +! if ( type == "int" ) +! arg << s.toInt(); +! else if ( type == "uint" ) +! arg << s.toUInt(); +! else if ( type == "unsigned" ) +! arg << s.toUInt(); +! else if ( type == "unsigned int" ) +! arg << s.toUInt(); +! else if ( type == "long" ) +! arg << s.toLong(); +! else if ( type == "long int" ) +! arg << s.toLong(); +! else if ( type == "unsigned long" ) +! arg << s.toULong(); +! else if ( type == "unsigned long int" ) +! arg << s.toULong(); +! else if ( type == "float" ) +! arg << s.toFloat(); +! else if ( type == "double" ) +! arg << s.toDouble(); +! else if ( type == "bool" ) +! arg << mkBool( s ); +! else if ( type == "QString" ) +! arg << s; +! else if ( type == "QCString" ) +! arg << QCString( argv[i] ); +! else if ( type == "QColor" ) +! arg << mkColor( s ); +! else if ( type == "QPoint" ) +! arg << mkPoint( s ); +! else if ( type == "QSize" ) +! arg << mkSize( s ); +! else if ( type == "QRect" ) +! arg << mkRect( s ); +! else if ( type == "QVariant" ) { +! if ( s == "true" || s == "false" ) +! arg << QVariant( mkBool( s ), 42 ); +! else if ( s.left( 4 ) == "int(" ) +! arg << QVariant( s.mid(4, s.length()-5).toInt() ); +! else if ( s.left( 7 ) == "QPoint(" ) +! arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +! else if ( s.left( 6 ) == "QSize(" ) +! arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +! else if ( s.left( 6 ) == "QRect(" ) +! arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +! else if ( s.left( 7 ) == "QColor(" ) +! arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +! else +! arg << QVariant( s ); +! } else if ( type.startsWith("QValueList<")) { +! type = type.mid(11, type.length() - 12); +! QStringList list; +! QString delim = s; +! if (delim == "[") +! delim = "]"; +! if (delim == "(") +! delim = ")"; +! i++; +! QByteArray dummy_data; +! QDataStream dummy_arg(dummy_data, IO_WriteOnly); + +! int j = i; +! int count = 0; +! // Parse list to get the count +! while (true) { +! if (j >= argc) +! { +! qWarning("List end-delimiter '%s' not found.", delim.latin1()); +! exit(1); +! } +! if (argv[j] == delim) break; +! marshall(dummy_arg, argc, argv, j, type); +! count++; +! } +! arg << (Q_UINT32) count; +! // Parse the list for real +! while (true) { +! if (i >= argc) +! { +! qWarning("List end-delimiter '%s' not found.", delim.latin1()); +! exit(1); +! } +! if (argv[i] == delim) break; +! marshall(arg, argc, argv, i, type); +! } +! } else { +! qWarning( "cannot handle datatype '%s'", type.latin1() ); +! exit(1); +! } + i++; + } + +--- 242,351 ---- + + } + +! void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) + { +! if (type == "QStringList") +! type = "QValueList"; +! if (type == "QCStringList") +! type = "QValueList"; +! if( i > args.count() ) +! { +! qWarning("Not enough arguments."); +! exit(1); +! } +! QString s = QString::fromLocal8Bit( args[ i ] ); + +! if ( type == "int" ) +! arg << s.toInt(); +! else if ( type == "uint" ) +! arg << s.toUInt(); +! else if ( type == "unsigned" ) +! arg << s.toUInt(); +! else if ( type == "unsigned int" ) +! arg << s.toUInt(); +! else if ( type == "long" ) +! arg << s.toLong(); +! else if ( type == "long int" ) +! arg << s.toLong(); +! else if ( type == "unsigned long" ) +! arg << s.toULong(); +! else if ( type == "unsigned long int" ) +! arg << s.toULong(); +! else if ( type == "float" ) +! arg << s.toFloat(); +! else if ( type == "double" ) +! arg << s.toDouble(); +! else if ( type == "bool" ) +! arg << mkBool( s ); +! else if ( type == "QString" ) +! arg << s; +! else if ( type == "QCString" ) +! arg << QCString( args[ i ] ); +! else if ( type == "QColor" ) +! arg << mkColor( s ); +! else if ( type == "QPoint" ) +! arg << mkPoint( s ); +! else if ( type == "QSize" ) +! arg << mkSize( s ); +! else if ( type == "QRect" ) +! arg << mkRect( s ); +! else if ( type == "QVariant" ) { +! if ( s == "true" || s == "false" ) +! arg << QVariant( mkBool( s ), 42 ); +! else if ( s.left( 4 ) == "int(" ) +! arg << QVariant( s.mid(4, s.length()-5).toInt() ); +! else if ( s.left( 7 ) == "QPoint(" ) +! arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +! else if ( s.left( 6 ) == "QSize(" ) +! arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +! else if ( s.left( 6 ) == "QRect(" ) +! arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +! else if ( s.left( 7 ) == "QColor(" ) +! arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +! else +! arg << QVariant( s ); +! } else if ( type.startsWith("QValueList<")) { +! type = type.mid(11, type.length() - 12); +! QStringList list; +! QString delim = s; +! if (delim == "[") +! delim = "]"; +! if (delim == "(") +! delim = ")"; + i++; ++ QByteArray dummy_data; ++ QDataStream dummy_arg(dummy_data, IO_WriteOnly); ++ ++ uint j = i; ++ uint count = 0; ++ // Parse list to get the count ++ while (true) { ++ if( j > args.count() ) ++ { ++ qWarning("List end-delimiter '%s' not found.", delim.latin1()); ++ exit(1); ++ } ++ if( QString::fromLocal8Bit( args[ j ] ) == delim ) ++ break; ++ marshall( dummy_arg, args, j, type ); ++ count++; ++ } ++ arg << (Q_UINT32) count; ++ // Parse the list for real ++ while (true) { ++ if( i > args.count() ) ++ { ++ qWarning("List end-delimiter '%s' not found.", delim.latin1()); ++ exit(1); ++ } ++ if( QString::fromLocal8Bit( args[ i ] ) == delim ) ++ break; ++ marshall( arg, args, i, type ); ++ } ++ } else { ++ qWarning( "cannot handle datatype '%s'", type.latin1() ); ++ exit(1); ++ } ++ i++; + } + diff --git a/kompare/tests/diff/contextp.diff b/kompare/tests/diff/contextp.diff new file mode 100644 index 00000000..ed9319bc --- /dev/null +++ b/kompare/tests/diff/contextp.diff @@ -0,0 +1,27 @@ +*** /home/John/lao Thu Apr 12 11:09:30 2001 +--- /home/John/tzu Sat Jul 28 13:23:25 2001 +*************** +*** 1,7 **** +- The Way that can be told of is not the eternal Way; +- The name that can be named is not the eternal name. + The Nameless is the origin of Heaven and Earth; +! The Named is the mother of all things. + Therefore let there always be non-being, + so we may see their subtlety, + And let there always be being, +--- 1,6 ---- + The Nameless is the origin of Heaven and Earth; +! The named is the mother of all things. +! + Therefore let there always be non-being, + so we may see their subtlety, + And let there always be being, +*************** And let there always be being, +*** 9,11 **** +--- 8,13 ---- + The two are the same, + But after they are produced, + they have different names. ++ They both may be called deep and profound. ++ Deeper and more profound, ++ The door of all subtleties! diff --git a/kompare/tests/diff/ed.diff b/kompare/tests/diff/ed.diff new file mode 100644 index 00000000..43c2b2f1 --- /dev/null +++ b/kompare/tests/diff/ed.diff @@ -0,0 +1,10 @@ +11a +They both may be called deep and profound. +Deeper and more profound, +The door of all subtleties! +. +4c +The named is the mother of all things. + +. +1,2d diff --git a/kompare/tests/diff/edm.diff b/kompare/tests/diff/edm.diff new file mode 100644 index 00000000..0df2abc1 --- /dev/null +++ b/kompare/tests/diff/edm.diff @@ -0,0 +1,680 @@ +diff -er dcop/client/dcop.cpp dcop2/client/dcop.cpp +343a + +// vim: set ts=8 sts=4 sw=4 noet: + +. +340a +} + + +int main( int argc, char** argv ) +{ + bool readStdin = false; + int numOptions = 0; + QString user; + Session session = DefaultSession; + QString sessionName; + + // Scan for command-line options first + for( int pos = 1 ; pos <= argc - 1 ; pos++ ) + { + if( strcmp( argv[ pos ], "--help" ) == 0 ) + showHelp( 0 ); + else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) + { + readStdin = true; + numOptions++; + } + else if( strcmp( argv[ pos ], "--user" ) == 0 ) + { + if( pos <= argc - 2 ) + { + user = QString::fromLocal8Bit( argv[ pos + 1] ); + numOptions +=2; + pos++; + } + else + { + cerr << "Missing username for '--user' option!" << endl << endl; + showHelp( -1 ); + } + } + else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) + { + user = "*"; + numOptions ++; + } + else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) + { + session = QuerySessions; + numOptions ++; + } + else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) + { + session = AllSessions; + numOptions ++; + } + else if( argv[ pos ][ 0 ] == '-' ) + { + cerr << "Unknown command-line option '" << argv[ pos ] + << "'." << endl << endl; + showHelp( -1 ); + } + else + break; // End of options + } + + argc -= numOptions; + + QCStringList args; + for( int i = numOptions; i < argc + numOptions - 1; i++ ) + args.append( argv[ i + 1 ] ); + + if( readStdin && args.count() < 3 ) + { + cerr << "--pipe option only supported for function calls!" << endl << endl; + showHelp( -1 ); + } + + if( user == "*" && args.count() < 3 && session != QuerySessions ) + { + cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; + showHelp( -1 ); + } + + if( session == QuerySessions && !args.isEmpty() ) + { + cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; + showHelp( -1 ); + } + + if( session == QuerySessions && user.isEmpty() ) + { + cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl + << "--all-users options!" << endl << endl; + showHelp( -1 ); + } + + if( session != DefaultSession && session != QuerySessions && + args.count() < 3 ) + { + cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl + << "calls!" << endl << endl; + showHelp( -1 ); + } + + UserList users; + if( user == "*" ) + users = userList(); + else if( !user.isEmpty() ) + users[ user ] = userList()[ user ]; + + runDCOP( args, users, session, sessionName, readStdin ); +. +339a + if( users.count() > 1 || ( users.count() == 1 && + ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) + { + // Check for ICE authority file and if the file can be read by us + QString home = it.data(); + QString iceFile = it.data() + "/.ICEauthority"; + QFileInfo fi( iceFile ); + if( iceFile.isEmpty() ) + { + cerr << "WARNING: Cannot determine home directory for user " + << it.key() << "!" << endl + << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl + << "calling dcop." << endl; + } + else if( fi.exists() ) + { + if( fi.isReadable() ) + { + char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); + putenv( envStr ); + //cerr << "ice: " << envStr << endl; + } + else + { + cerr << "WARNING: ICE authority file " << iceFile + << "is not readable by you!" << endl + << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl + << "calling dcop." << endl; + } + } + else + { + if( users.count() > 1 ) + continue; + else + { + cerr << "WARNING: Cannot find ICE authority file " + << iceFile << "!" << endl + << "Please check permissions or set the $ICEAUTHORITY" + << " variable manually before" << endl + << "calling dcop." << endl; + } + } + } + + // Main loop + // If users is an empty list we're calling for the currently logged + // in user. In this case we don't have a session, but still want + // to iterate the loop once. + QStringList::Iterator sIt = sessions.begin(); + for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) + { + if( !presetDCOPServer && !users.isEmpty() ) + { + QString dcopFile = it.data() + "/" + *sIt; + QFile f( dcopFile ); + if( !f.open( IO_ReadOnly ) ) + { + cerr << "Can't open " << dcopFile << " for reading!" << endl; + exit( -1 ); + } + + QStringList l( QStringList::split( '\n', f.readAll() ) ); + dcopServer = l.first(); + + if( dcopServer.isEmpty() ) + { + cerr << "WARNING: Unable to determine DCOP server for session " + << *sIt << "!" << endl + << "Please check permissions or set the $DCOPSERVER variable manually before" << endl + << "calling dcop." << endl; + exit( -1 ); + } + } + + delete client; + client = new DCOPClient; + if( !dcopServer.isEmpty() ) + client->setServerAddress( dcopServer.ascii() ); + bool success = client->attach(); + if( !success ) + { + cerr << "ERROR: Couldn't attach to DCOP server!" << endl; + continue; + } + dcop = client; + + switch ( args.count() ) + { + case 0: + queryApplications(""); + break; + case 1: + if (endsWith(app, '*')) + queryApplications(app); + else + queryObjects( app, "" ); + break; + case 2: + if (endsWith(objid, '*')) + queryObjects(app, objid); + else + queryFunctions( app, objid ); + break; + case 3: + default: + if( readStdin ) + { + QCStringList::Iterator replaceArg = args.end(); + + QCStringList::Iterator it; + for( it = args.begin(); it != args.end(); it++ ) + if( *it == "%1" ) + replaceArg = it; + + // Read from stdin until EOF and call function for each line read + char *buf = new char[ 1000 ]; + while ( !feof( stdin ) ) + { + fgets( buf, 1000, stdin ); + + if( replaceArg != args.end() ) + *replaceArg = buf; + + callFunction( app, objid, function, params ); + } + } + else + { + // Just call function +// cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; + callFunction( app, objid, function, params ); + } + break; + } + // Another sIt++ would make the loop infinite... + if( users.isEmpty() ) + break; + } + + // Another it++ would make the loop infinite... + if( it == users.end() ) + break; +. +308,338c + if( !args.isEmpty() ) + app = args[ 0 ]; + if( args.count() > 1 ) + objid = args[ 1 ]; + if( args.count() > 2 ) + function = args[ 2 ]; + if( args.count() > 3) + { + params = args; + params.remove( params.begin() ); + params.remove( params.begin() ); + params.remove( params.begin() ); + } + } + + bool firstRun = true; + UserList::Iterator it; + QStringList sessions; + bool presetDCOPServer = false; +// char *dcopStr = 0L; + QString dcopServer; + + for( it = users.begin(); it != users.end() || firstRun; it++ ) + { + firstRun = false; + + //cout << "Iterating '" << it.key() << "'" << endl; + + if( session == QuerySessions ) + { + QStringList sessions = dcopSessionList( it.key(), it.data() ); + if( sessions.isEmpty() ) + { + cout << "No active sessions"; + if( !( *it ).isEmpty() ) + cout << " for user " << *it; + cout << endl; + } + else + { + cout << "Active sessions "; + if( !( *it ).isEmpty() ) + cout << "for user " << *it << " "; + cout << ":" << endl; + + QStringList::Iterator sIt; + for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) + cout << " " << *sIt << endl; + + cout << endl; + } + continue; + } + + if( getenv( "DCOPSERVER" ) ) + { + sessions.append( getenv( "DCOPSERVER" ) ); + presetDCOPServer = true; + } + + if( users.count() > 1 || ( users.count() == 1 && + ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) + { + sessions = dcopSessionList( it.key(), it.data() ); + if( sessions.isEmpty() ) + { + if( users.count() > 1 ) + continue; + else + { + cerr << "ERROR: No active KDE sessions!" << endl + << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl + << "before calling dcop." << endl; + exit( -1 ); + } + } + else if( sessions.count() > 1 && session != AllSessions ) + { + cerr << "ERROR: Multiple available KDE sessions!" << endl + << "Please specify the correct session to use with --session or use the" << endl + << "--all-sessions option to broadcast to all sessions." << endl; + exit( -1 ); + } + } +. +289,304c + // WARNING: This part (until the closing '}') could very + // well be broken now. As I don't know how to trigger and test + // dcoprefs this code is *not* tested. It compiles and it looks + // ok to me, but that's all I can say - Martijn (2001/12/24) + int delimPos = args[ 0 ].findRev( ',' ); + if( delimPos == -1 ) + { + cerr << "Error: '" << args[ 0 ] + << "' is not a valid DCOP reference." << endl; + exit( -1 ); + } + args[ 0 ][ delimPos ] = 0; + app = args[ 0 ].mid( 8 ); + delimPos++; + args[ 0 ][ args[ 0 ].length() - 1 ] = 0; + objid = args[ 0 ].mid( delimPos ); + if( args.count() > 1 ) + function = args[ 1 ]; + if( args.count() > 2 ) + { + params = args; + params.remove( params.begin() ); + params.remove( params.begin() ); + } +. +286,287c + QCStringList params; + DCOPClient *client = 0L; + if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) +. +282a +/** + * Do the actual DCOP call + */ +void runDCOP( QCStringList args, UserList users, Session session, + const QString sessionName, bool readStdin ) +{ +. +279,281c + return result; +} + +/** + * Return a list of available DCOP sessions for the specified user + * An empty list means no sessions are available, or an error occurred. + */ +QStringList dcopSessionList( const QString &user, const QString &home ) +{ + if( home.isEmpty() ) + { + cerr << "WARNING: Cannot determine home directory for user " + << user << "!" << endl + << "Please check permissions or set the $DCOPSERVER variable manually before" << endl + << "calling dcop." << endl; + return QStringList(); + } + + QStringList result; + QFileInfo dirInfo( home ); + if( !dirInfo.exists() || !dirInfo.isReadable() ) + return result; + + QDir d( home ); + d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); + d.setNameFilter( ".DCOPserver*" ); + + const QFileInfoList *list = d.entryInfoList(); + if( !list ) + return result; + + QFileInfoListIterator it( *list ); + QFileInfo *fi; + + while ( ( fi = it.current() ) != 0 ) + { + if( fi->isReadable() ) + result.append( fi->fileName() ); + ++it; + } + return result; +} +. +274,276c + QStringList l( QStringList::split( '\n', f.readAll() ) ); + + for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) + { + QStringList userInfo( QStringList::split( ':', *it, true ) ); + result[ userInfo[ 0 ] ] = userInfo[ 5 ]; +. +272a + UserList result; + + QFile f( "/etc/passwd" ); + + if( !f.open( IO_ReadOnly ) ) + { + cerr << "Can't open /etc/passwd for reading!" << endl; + return result; + } +. +270,271c +/** + * Return a list of all users and their home directories. + * Returns an empty list if /etc/passwd cannot be read for some reason. + */ +static UserList userList() +. +268a +/** + * Show command-line help and exit + */ +void showHelp( int exitCode = 0 ) +{ + cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl + << "" << endl + << "Console DCOP client" << endl + << "" << endl + << "Generic options:" << endl + << " --help Show help about options" << endl + << "" << endl + << "Options:" << endl + << " --pipe Call DCOP for each line read from stdin" << endl + << " --user Connect to the given user's DCOP server. This option will" << endl + << " ignore the values of the environment vars $DCOPSERVER and" << endl + << " $ICEAUTHORITY, even if they are set." << endl + << " If the user has more than one open session, you must also" << endl + << " use one of the --list-sessions, --session or --als-sessions" << endl + << " command-line options." << endl + << " --all-users Send the same DCOP call to all users with a running DCOP" << endl + << " server. Only failed calls to existing DCOP servers will" + << " generate an error message. If no DCOP server is available" << endl + << " at all, no error will be generated." << endl; + + exit( exitCode ); +} +. +246,250c + uint i = 0; + for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) + marshall( arg, args, i, *it ); + + if ( i != args.count() ) + { +. +164c +// exit(1); + return; +. +156,157c + uint a = (*it).contains(','); + if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) +. +139c + if ( !ok && args.isEmpty() ) +. +123d +121c +void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) +. +35a +static QTextStream cout( stdout, IO_WriteOnly ); +static QTextStream cerr( stderr, IO_WriteOnly ); + +/** + * Session to send call to + * DefaultSession - current session. Current KDE session when called without + * --user or --all-users option. Otherwise this value ignores + * all users with more than one active session. + * AllSessions - Send to all sessions found. requires --user or --all-users. + * QuerySessions - Don't call DCOP, return a list of available sessions. + * CustomSession - Use the specified session + */ +enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; + +. +33a +typedef QMap UserList; + +. +28,30c +#include "../kdatastream.h" +. +25c +#include +#include +#include +#include +#include +#include +#include + +// putenv() is not available on all platforms, so make sure the emulation +// wrapper is available in those cases by loading config.h! +#include + +. +23c +#include +#include +#include + +. +diff -er dcop/client/dcopfind.cpp dcop2/client/dcopfind.cpp +224c + QCStringList params; + for( int i = 0; i < argc; i++ ) + params.append( args[ i ] ); + + findObject( app, objid, function, params ); +. +133c + if ( (uint) i != args.count() ) { +. +131c + marshall(arg, args, i, *it); +. +121c + if ( types.count() != args.count() ) { +. +39c +bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +. +diff -er dcop/client/marshall.cpp dcop2/client/marshall.cpp +347a + QByteArray dummy_data; + QDataStream dummy_arg(dummy_data, IO_WriteOnly); + + uint j = i; + uint count = 0; + // Parse list to get the count + while (true) { + if( j > args.count() ) + { + qWarning("List end-delimiter '%s' not found.", delim.latin1()); + exit(1); + } + if( QString::fromLocal8Bit( args[ j ] ) == delim ) + break; + marshall( dummy_arg, args, j, type ); + count++; + } + arg << (Q_UINT32) count; + // Parse the list for real + while (true) { + if( i > args.count() ) + { + qWarning("List end-delimiter '%s' not found.", delim.latin1()); + exit(1); + } + if( QString::fromLocal8Bit( args[ i ] ) == delim ) + break; + marshall( arg, args, i, type ); + } + } else { + qWarning( "cannot handle datatype '%s'", type.latin1() ); + exit(1); + } + i++; +. +319,346c + if ( type == "int" ) + arg << s.toInt(); + else if ( type == "uint" ) + arg << s.toUInt(); + else if ( type == "unsigned" ) + arg << s.toUInt(); + else if ( type == "unsigned int" ) + arg << s.toUInt(); + else if ( type == "long" ) + arg << s.toLong(); + else if ( type == "long int" ) + arg << s.toLong(); + else if ( type == "unsigned long" ) + arg << s.toULong(); + else if ( type == "unsigned long int" ) + arg << s.toULong(); + else if ( type == "float" ) + arg << s.toFloat(); + else if ( type == "double" ) + arg << s.toDouble(); + else if ( type == "bool" ) + arg << mkBool( s ); + else if ( type == "QString" ) + arg << s; + else if ( type == "QCString" ) + arg << QCString( args[ i ] ); + else if ( type == "QColor" ) + arg << mkColor( s ); + else if ( type == "QPoint" ) + arg << mkPoint( s ); + else if ( type == "QSize" ) + arg << mkSize( s ); + else if ( type == "QRect" ) + arg << mkRect( s ); + else if ( type == "QVariant" ) { + if ( s == "true" || s == "false" ) + arg << QVariant( mkBool( s ), 42 ); + else if ( s.left( 4 ) == "int(" ) + arg << QVariant( s.mid(4, s.length()-5).toInt() ); + else if ( s.left( 7 ) == "QPoint(" ) + arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); + else if ( s.left( 6 ) == "QSize(" ) + arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); + else if ( s.left( 6 ) == "QRect(" ) + arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); + else if ( s.left( 7 ) == "QColor(" ) + arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); + else + arg << QVariant( s ); + } else if ( type.startsWith("QValueList<")) { + type = type.mid(11, type.length() - 12); + QStringList list; + QString delim = s; + if (delim == "[") + delim = "]"; + if (delim == "(") + delim = ")"; +. +247,317c + if (type == "QStringList") + type = "QValueList"; + if (type == "QCStringList") + type = "QValueList"; + if( i > args.count() ) + { + qWarning("Not enough arguments."); + exit(1); + } + QString s = QString::fromLocal8Bit( args[ i ] ); +. +245c +void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) +. diff --git a/kompare/tests/diff/normal.diff b/kompare/tests/diff/normal.diff new file mode 100644 index 00000000..6f82a1c4 --- /dev/null +++ b/kompare/tests/diff/normal.diff @@ -0,0 +1,12 @@ +1,2d0 +< The Way that can be told of is not the eternal Way; +< The name that can be named is not the eternal name. +4c2,3 +< The Named is the mother of all things. +--- +> The named is the mother of all things. +> +11a11,13 +> They both may be called deep and profound. +> Deeper and more profound, +> The door of all subtleties! diff --git a/kompare/tests/diff/normalm.diff b/kompare/tests/diff/normalm.diff new file mode 100644 index 00000000..3db667ba --- /dev/null +++ b/kompare/tests/diff/normalm.diff @@ -0,0 +1,849 @@ +diff -r dcop/client/dcop.cpp dcop2/client/dcop.cpp +23c23,26 +< #include +--- +> #include +> #include +> #include +> +25c28,39 +< #include "../kdatastream.h" +--- +> #include +> #include +> #include +> #include +> #include +> #include +> #include +> +> // putenv() is not available on all platforms, so make sure the emulation +> // wrapper is available in those cases by loading config.h! +> #include +> +28,30c42 +< #include +< #include +< #include +--- +> #include "../kdatastream.h" +33a46,47 +> typedef QMap UserList; +> +35a50,63 +> static QTextStream cout( stdout, IO_WriteOnly ); +> static QTextStream cerr( stderr, IO_WriteOnly ); +> +> /** +> * Session to send call to +> * DefaultSession - current session. Current KDE session when called without +> * --user or --all-users option. Otherwise this value ignores +> * all users with more than one active session. +> * AllSessions - Send to all sessions found. requires --user or --all-users. +> * QuerySessions - Don't call DCOP, return a list of available sessions. +> * CustomSession - Use the specified session +> */ +> enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; +> +121c149 +< void callFunction( const char* app, const char* obj, const char* func, int argc, char** args ) +--- +> void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) +123d150 +< +139c166 +< if ( !ok && argc == 0 ) +--- +> if ( !ok && args.isEmpty() ) +156,157c183,184 +< int a = (*it).contains(','); +< if ( ( a == 0 && argc == 0) || ( a > 0 && a + 1 == argc ) ) +--- +> uint a = (*it).contains(','); +> if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) +164c191,192 +< exit(1); +--- +> // exit(1); +> return; +246,250c274,279 +< int i = 0; +< for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +< marshall(arg, argc, args, i, *it); +< } +< if ( i != argc ) { +--- +> uint i = 0; +> for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) +> marshall( arg, args, i, *it ); +> +> if ( i != args.count() ) +> { +268a298,324 +> /** +> * Show command-line help and exit +> */ +> void showHelp( int exitCode = 0 ) +> { +> cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl +> << "" << endl +> << "Console DCOP client" << endl +> << "" << endl +> << "Generic options:" << endl +> << " --help Show help about options" << endl +> << "" << endl +> << "Options:" << endl +> << " --pipe Call DCOP for each line read from stdin" << endl +> << " --user Connect to the given user's DCOP server. This option will" << endl +> << " ignore the values of the environment vars $DCOPSERVER and" << endl +> << " $ICEAUTHORITY, even if they are set." << endl +> << " If the user has more than one open session, you must also" << endl +> << " use one of the --list-sessions, --session or --als-sessions" << endl +> << " command-line options." << endl +> << " --all-users Send the same DCOP call to all users with a running DCOP" << endl +> << " server. Only failed calls to existing DCOP servers will" +> << " generate an error message. If no DCOP server is available" << endl +> << " at all, no error will be generated." << endl; +> +> exit( exitCode ); +> } +270,271c326,330 +< +< int main( int argc, char** argv ) +--- +> /** +> * Return a list of all users and their home directories. +> * Returns an empty list if /etc/passwd cannot be read for some reason. +> */ +> static UserList userList() +272a332,340 +> UserList result; +> +> QFile f( "/etc/passwd" ); +> +> if( !f.open( IO_ReadOnly ) ) +> { +> cerr << "Can't open /etc/passwd for reading!" << endl; +> return result; +> } +274,276c342,347 +< if ( argc > 1 && argv[1][0] == '-' ) { +< fprintf( stderr, "Usage: dcop [ application [object [function [arg1] [arg2] [arg3] ... ] ] ] \n" ); +< exit(0); +--- +> QStringList l( QStringList::split( '\n', f.readAll() ) ); +> +> for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) +> { +> QStringList userInfo( QStringList::split( ':', *it, true ) ); +> result[ userInfo[ 0 ] ] = userInfo[ 5 ]; +279,281c350,391 +< DCOPClient client; +< client.attach(); +< dcop = &client; +--- +> return result; +> } +> +> /** +> * Return a list of available DCOP sessions for the specified user +> * An empty list means no sessions are available, or an error occurred. +> */ +> QStringList dcopSessionList( const QString &user, const QString &home ) +> { +> if( home.isEmpty() ) +> { +> cerr << "WARNING: Cannot determine home directory for user " +> << user << "!" << endl +> << "Please check permissions or set the $DCOPSERVER variable manually before" << endl +> << "calling dcop." << endl; +> return QStringList(); +> } +> +> QStringList result; +> QFileInfo dirInfo( home ); +> if( !dirInfo.exists() || !dirInfo.isReadable() ) +> return result; +> +> QDir d( home ); +> d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); +> d.setNameFilter( ".DCOPserver*" ); +> +> const QFileInfoList *list = d.entryInfoList(); +> if( !list ) +> return result; +> +> QFileInfoListIterator it( *list ); +> QFileInfo *fi; +> +> while ( ( fi = it.current() ) != 0 ) +> { +> if( fi->isReadable() ) +> result.append( fi->fileName() ); +> ++it; +> } +> return result; +> } +282a393,398 +> /** +> * Do the actual DCOP call +> */ +> void runDCOP( QCStringList args, UserList users, Session session, +> const QString sessionName, bool readStdin ) +> { +286,287c402,404 +< char **args = 0; +< if ((argc > 1) && (strncmp(argv[1], "DCOPRef(", 8)) == 0) +--- +> QCStringList params; +> DCOPClient *client = 0L; +> if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) +289,304c406,429 +< char *delim = strchr(argv[1], ','); +< if (!delim) +< { +< fprintf(stderr, "Error: '%s' is not a valid DCOP reference.\n", argv[1]); +< return 1; +< } +< *delim = 0; +< app = argv[1] + 8; +< delim++; +< delim[strlen(delim)-1] = 0; +< objid = delim; +< if (argc > 2) +< function = argv[2]; +< if (argc > 3) +< args = &argv[3]; +< argc++; +--- +> // WARNING: This part (until the closing '}') could very +> // well be broken now. As I don't know how to trigger and test +> // dcoprefs this code is *not* tested. It compiles and it looks +> // ok to me, but that's all I can say - Martijn (2001/12/24) +> int delimPos = args[ 0 ].findRev( ',' ); +> if( delimPos == -1 ) +> { +> cerr << "Error: '" << args[ 0 ] +> << "' is not a valid DCOP reference." << endl; +> exit( -1 ); +> } +> args[ 0 ][ delimPos ] = 0; +> app = args[ 0 ].mid( 8 ); +> delimPos++; +> args[ 0 ][ args[ 0 ].length() - 1 ] = 0; +> objid = args[ 0 ].mid( delimPos ); +> if( args.count() > 1 ) +> function = args[ 1 ]; +> if( args.count() > 2 ) +> { +> params = args; +> params.remove( params.begin() ); +> params.remove( params.begin() ); +> } +308,338c433,516 +< if (argc > 1) +< app = argv[1]; +< if (argc > 2) +< objid = argv[2]; +< if (argc > 3) +< function = argv[3]; +< if (argc > 4) +< args = &argv[4]; +< } +< +< switch ( argc ) { +< case 0: +< case 1: +< queryApplications(""); +< break; +< case 2: +< if (endsWith(app, '*')) +< queryApplications(app); +< else +< queryObjects( app, "" ); +< break; +< case 3: +< if (endsWith(objid, '*')) +< queryObjects(app, objid); +< else +< queryFunctions( app, objid ); +< break; +< case 4: +< default: +< callFunction( app, objid, function, argc - 4, args ); +< break; +--- +> if( !args.isEmpty() ) +> app = args[ 0 ]; +> if( args.count() > 1 ) +> objid = args[ 1 ]; +> if( args.count() > 2 ) +> function = args[ 2 ]; +> if( args.count() > 3) +> { +> params = args; +> params.remove( params.begin() ); +> params.remove( params.begin() ); +> params.remove( params.begin() ); +> } +> } +> +> bool firstRun = true; +> UserList::Iterator it; +> QStringList sessions; +> bool presetDCOPServer = false; +> // char *dcopStr = 0L; +> QString dcopServer; +> +> for( it = users.begin(); it != users.end() || firstRun; it++ ) +> { +> firstRun = false; +> +> //cout << "Iterating '" << it.key() << "'" << endl; +> +> if( session == QuerySessions ) +> { +> QStringList sessions = dcopSessionList( it.key(), it.data() ); +> if( sessions.isEmpty() ) +> { +> cout << "No active sessions"; +> if( !( *it ).isEmpty() ) +> cout << " for user " << *it; +> cout << endl; +> } +> else +> { +> cout << "Active sessions "; +> if( !( *it ).isEmpty() ) +> cout << "for user " << *it << " "; +> cout << ":" << endl; +> +> QStringList::Iterator sIt; +> for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) +> cout << " " << *sIt << endl; +> +> cout << endl; +> } +> continue; +> } +> +> if( getenv( "DCOPSERVER" ) ) +> { +> sessions.append( getenv( "DCOPSERVER" ) ); +> presetDCOPServer = true; +> } +> +> if( users.count() > 1 || ( users.count() == 1 && +> ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) +> { +> sessions = dcopSessionList( it.key(), it.data() ); +> if( sessions.isEmpty() ) +> { +> if( users.count() > 1 ) +> continue; +> else +> { +> cerr << "ERROR: No active KDE sessions!" << endl +> << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl +> << "before calling dcop." << endl; +> exit( -1 ); +> } +> } +> else if( sessions.count() > 1 && session != AllSessions ) +> { +> cerr << "ERROR: Multiple available KDE sessions!" << endl +> << "Please specify the correct session to use with --session or use the" << endl +> << "--all-sessions option to broadcast to all sessions." << endl; +> exit( -1 ); +> } +> } +339a518,660 +> if( users.count() > 1 || ( users.count() == 1 && +> ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) +> { +> // Check for ICE authority file and if the file can be read by us +> QString home = it.data(); +> QString iceFile = it.data() + "/.ICEauthority"; +> QFileInfo fi( iceFile ); +> if( iceFile.isEmpty() ) +> { +> cerr << "WARNING: Cannot determine home directory for user " +> << it.key() << "!" << endl +> << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl +> << "calling dcop." << endl; +> } +> else if( fi.exists() ) +> { +> if( fi.isReadable() ) +> { +> char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); +> putenv( envStr ); +> //cerr << "ice: " << envStr << endl; +> } +> else +> { +> cerr << "WARNING: ICE authority file " << iceFile +> << "is not readable by you!" << endl +> << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl +> << "calling dcop." << endl; +> } +> } +> else +> { +> if( users.count() > 1 ) +> continue; +> else +> { +> cerr << "WARNING: Cannot find ICE authority file " +> << iceFile << "!" << endl +> << "Please check permissions or set the $ICEAUTHORITY" +> << " variable manually before" << endl +> << "calling dcop." << endl; +> } +> } +> } +> +> // Main loop +> // If users is an empty list we're calling for the currently logged +> // in user. In this case we don't have a session, but still want +> // to iterate the loop once. +> QStringList::Iterator sIt = sessions.begin(); +> for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) +> { +> if( !presetDCOPServer && !users.isEmpty() ) +> { +> QString dcopFile = it.data() + "/" + *sIt; +> QFile f( dcopFile ); +> if( !f.open( IO_ReadOnly ) ) +> { +> cerr << "Can't open " << dcopFile << " for reading!" << endl; +> exit( -1 ); +> } +> +> QStringList l( QStringList::split( '\n', f.readAll() ) ); +> dcopServer = l.first(); +> +> if( dcopServer.isEmpty() ) +> { +> cerr << "WARNING: Unable to determine DCOP server for session " +> << *sIt << "!" << endl +> << "Please check permissions or set the $DCOPSERVER variable manually before" << endl +> << "calling dcop." << endl; +> exit( -1 ); +> } +> } +> +> delete client; +> client = new DCOPClient; +> if( !dcopServer.isEmpty() ) +> client->setServerAddress( dcopServer.ascii() ); +> bool success = client->attach(); +> if( !success ) +> { +> cerr << "ERROR: Couldn't attach to DCOP server!" << endl; +> continue; +> } +> dcop = client; +> +> switch ( args.count() ) +> { +> case 0: +> queryApplications(""); +> break; +> case 1: +> if (endsWith(app, '*')) +> queryApplications(app); +> else +> queryObjects( app, "" ); +> break; +> case 2: +> if (endsWith(objid, '*')) +> queryObjects(app, objid); +> else +> queryFunctions( app, objid ); +> break; +> case 3: +> default: +> if( readStdin ) +> { +> QCStringList::Iterator replaceArg = args.end(); +> +> QCStringList::Iterator it; +> for( it = args.begin(); it != args.end(); it++ ) +> if( *it == "%1" ) +> replaceArg = it; +> +> // Read from stdin until EOF and call function for each line read +> char *buf = new char[ 1000 ]; +> while ( !feof( stdin ) ) +> { +> fgets( buf, 1000, stdin ); +> +> if( replaceArg != args.end() ) +> *replaceArg = buf; +> +> callFunction( app, objid, function, params ); +> } +> } +> else +> { +> // Just call function +> // cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; +> callFunction( app, objid, function, params ); +> } +> break; +> } +> // Another sIt++ would make the loop infinite... +> if( users.isEmpty() ) +> break; +> } +> +> // Another it++ would make the loop infinite... +> if( it == users.end() ) +> break; +340a662,767 +> } +> +> +> int main( int argc, char** argv ) +> { +> bool readStdin = false; +> int numOptions = 0; +> QString user; +> Session session = DefaultSession; +> QString sessionName; +> +> // Scan for command-line options first +> for( int pos = 1 ; pos <= argc - 1 ; pos++ ) +> { +> if( strcmp( argv[ pos ], "--help" ) == 0 ) +> showHelp( 0 ); +> else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) +> { +> readStdin = true; +> numOptions++; +> } +> else if( strcmp( argv[ pos ], "--user" ) == 0 ) +> { +> if( pos <= argc - 2 ) +> { +> user = QString::fromLocal8Bit( argv[ pos + 1] ); +> numOptions +=2; +> pos++; +> } +> else +> { +> cerr << "Missing username for '--user' option!" << endl << endl; +> showHelp( -1 ); +> } +> } +> else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) +> { +> user = "*"; +> numOptions ++; +> } +> else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) +> { +> session = QuerySessions; +> numOptions ++; +> } +> else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) +> { +> session = AllSessions; +> numOptions ++; +> } +> else if( argv[ pos ][ 0 ] == '-' ) +> { +> cerr << "Unknown command-line option '" << argv[ pos ] +> << "'." << endl << endl; +> showHelp( -1 ); +> } +> else +> break; // End of options +> } +> +> argc -= numOptions; +> +> QCStringList args; +> for( int i = numOptions; i < argc + numOptions - 1; i++ ) +> args.append( argv[ i + 1 ] ); +> +> if( readStdin && args.count() < 3 ) +> { +> cerr << "--pipe option only supported for function calls!" << endl << endl; +> showHelp( -1 ); +> } +> +> if( user == "*" && args.count() < 3 && session != QuerySessions ) +> { +> cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; +> showHelp( -1 ); +> } +> +> if( session == QuerySessions && !args.isEmpty() ) +> { +> cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; +> showHelp( -1 ); +> } +> +> if( session == QuerySessions && user.isEmpty() ) +> { +> cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl +> << "--all-users options!" << endl << endl; +> showHelp( -1 ); +> } +> +> if( session != DefaultSession && session != QuerySessions && +> args.count() < 3 ) +> { +> cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl +> << "calls!" << endl << endl; +> showHelp( -1 ); +> } +> +> UserList users; +> if( user == "*" ) +> users = userList(); +> else if( !user.isEmpty() ) +> users[ user ] = userList()[ user ]; +> +> runDCOP( args, users, session, sessionName, readStdin ); +343a771,773 +> +> // vim: set ts=8 sts=4 sw=4 noet: +> +diff -r dcop/client/dcopfind.cpp dcop2/client/dcopfind.cpp +39c39 +< bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) +--- +> bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +121c121 +< if ( (int) types.count() != argc ) { +--- +> if ( types.count() != args.count() ) { +131c131 +< marshall(arg, argc, args, i, *it); +--- +> marshall(arg, args, i, *it); +133c133 +< if ( (int) i != argc ) { +--- +> if ( (uint) i != args.count() ) { +224c224,228 +< findObject( app, objid, function, argc, args ); +--- +> QCStringList params; +> for( int i = 0; i < argc; i++ ) +> params.append( args[ i ] ); +> +> findObject( app, objid, function, params ); +diff -r dcop/client/marshall.cpp dcop2/client/marshall.cpp +245c245 +< void marshall(QDataStream &arg, int argc, char **argv, int &i, QString type) +--- +> void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) +247,317c247,256 +< if (type == "QStringList") +< type = "QValueList"; +< if (type == "QCStringList") +< type = "QValueList"; +< if (i >= argc) +< { +< qWarning("Not enough arguments."); +< exit(1); +< } +< QString s = QString::fromLocal8Bit(argv[i]); +< +< if ( type == "int" ) +< arg << s.toInt(); +< else if ( type == "uint" ) +< arg << s.toUInt(); +< else if ( type == "unsigned" ) +< arg << s.toUInt(); +< else if ( type == "unsigned int" ) +< arg << s.toUInt(); +< else if ( type == "long" ) +< arg << s.toLong(); +< else if ( type == "long int" ) +< arg << s.toLong(); +< else if ( type == "unsigned long" ) +< arg << s.toULong(); +< else if ( type == "unsigned long int" ) +< arg << s.toULong(); +< else if ( type == "float" ) +< arg << s.toFloat(); +< else if ( type == "double" ) +< arg << s.toDouble(); +< else if ( type == "bool" ) +< arg << mkBool( s ); +< else if ( type == "QString" ) +< arg << s; +< else if ( type == "QCString" ) +< arg << QCString( argv[i] ); +< else if ( type == "QColor" ) +< arg << mkColor( s ); +< else if ( type == "QPoint" ) +< arg << mkPoint( s ); +< else if ( type == "QSize" ) +< arg << mkSize( s ); +< else if ( type == "QRect" ) +< arg << mkRect( s ); +< else if ( type == "QVariant" ) { +< if ( s == "true" || s == "false" ) +< arg << QVariant( mkBool( s ), 42 ); +< else if ( s.left( 4 ) == "int(" ) +< arg << QVariant( s.mid(4, s.length()-5).toInt() ); +< else if ( s.left( 7 ) == "QPoint(" ) +< arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +< else if ( s.left( 6 ) == "QSize(" ) +< arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +< else if ( s.left( 6 ) == "QRect(" ) +< arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +< else if ( s.left( 7 ) == "QColor(" ) +< arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +< else +< arg << QVariant( s ); +< } else if ( type.startsWith("QValueList<")) { +< type = type.mid(11, type.length() - 12); +< QStringList list; +< QString delim = s; +< if (delim == "[") +< delim = "]"; +< if (delim == "(") +< delim = ")"; +< i++; +< QByteArray dummy_data; +< QDataStream dummy_arg(dummy_data, IO_WriteOnly); +--- +> if (type == "QStringList") +> type = "QValueList"; +> if (type == "QCStringList") +> type = "QValueList"; +> if( i > args.count() ) +> { +> qWarning("Not enough arguments."); +> exit(1); +> } +> QString s = QString::fromLocal8Bit( args[ i ] ); +319,346c258,314 +< int j = i; +< int count = 0; +< // Parse list to get the count +< while (true) { +< if (j >= argc) +< { +< qWarning("List end-delimiter '%s' not found.", delim.latin1()); +< exit(1); +< } +< if (argv[j] == delim) break; +< marshall(dummy_arg, argc, argv, j, type); +< count++; +< } +< arg << (Q_UINT32) count; +< // Parse the list for real +< while (true) { +< if (i >= argc) +< { +< qWarning("List end-delimiter '%s' not found.", delim.latin1()); +< exit(1); +< } +< if (argv[i] == delim) break; +< marshall(arg, argc, argv, i, type); +< } +< } else { +< qWarning( "cannot handle datatype '%s'", type.latin1() ); +< exit(1); +< } +--- +> if ( type == "int" ) +> arg << s.toInt(); +> else if ( type == "uint" ) +> arg << s.toUInt(); +> else if ( type == "unsigned" ) +> arg << s.toUInt(); +> else if ( type == "unsigned int" ) +> arg << s.toUInt(); +> else if ( type == "long" ) +> arg << s.toLong(); +> else if ( type == "long int" ) +> arg << s.toLong(); +> else if ( type == "unsigned long" ) +> arg << s.toULong(); +> else if ( type == "unsigned long int" ) +> arg << s.toULong(); +> else if ( type == "float" ) +> arg << s.toFloat(); +> else if ( type == "double" ) +> arg << s.toDouble(); +> else if ( type == "bool" ) +> arg << mkBool( s ); +> else if ( type == "QString" ) +> arg << s; +> else if ( type == "QCString" ) +> arg << QCString( args[ i ] ); +> else if ( type == "QColor" ) +> arg << mkColor( s ); +> else if ( type == "QPoint" ) +> arg << mkPoint( s ); +> else if ( type == "QSize" ) +> arg << mkSize( s ); +> else if ( type == "QRect" ) +> arg << mkRect( s ); +> else if ( type == "QVariant" ) { +> if ( s == "true" || s == "false" ) +> arg << QVariant( mkBool( s ), 42 ); +> else if ( s.left( 4 ) == "int(" ) +> arg << QVariant( s.mid(4, s.length()-5).toInt() ); +> else if ( s.left( 7 ) == "QPoint(" ) +> arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +> else if ( s.left( 6 ) == "QSize(" ) +> arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +> else if ( s.left( 6 ) == "QRect(" ) +> arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +> else if ( s.left( 7 ) == "QColor(" ) +> arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +> else +> arg << QVariant( s ); +> } else if ( type.startsWith("QValueList<")) { +> type = type.mid(11, type.length() - 12); +> QStringList list; +> QString delim = s; +> if (delim == "[") +> delim = "]"; +> if (delim == "(") +> delim = ")"; +347a316,349 +> QByteArray dummy_data; +> QDataStream dummy_arg(dummy_data, IO_WriteOnly); +> +> uint j = i; +> uint count = 0; +> // Parse list to get the count +> while (true) { +> if( j > args.count() ) +> { +> qWarning("List end-delimiter '%s' not found.", delim.latin1()); +> exit(1); +> } +> if( QString::fromLocal8Bit( args[ j ] ) == delim ) +> break; +> marshall( dummy_arg, args, j, type ); +> count++; +> } +> arg << (Q_UINT32) count; +> // Parse the list for real +> while (true) { +> if( i > args.count() ) +> { +> qWarning("List end-delimiter '%s' not found.", delim.latin1()); +> exit(1); +> } +> if( QString::fromLocal8Bit( args[ i ] ) == delim ) +> break; +> marshall( arg, args, i, type ); +> } +> } else { +> qWarning( "cannot handle datatype '%s'", type.latin1() ); +> exit(1); +> } +> i++; diff --git a/kompare/tests/diff/rcs.diff b/kompare/tests/diff/rcs.diff new file mode 100644 index 00000000..08069790 --- /dev/null +++ b/kompare/tests/diff/rcs.diff @@ -0,0 +1,9 @@ +d1 2 +d4 1 +a4 2 +The named is the mother of all things. + +a11 3 +They both may be called deep and profound. +Deeper and more profound, +The door of all subtleties! diff --git a/kompare/tests/diff/rcsm.diff b/kompare/tests/diff/rcsm.diff new file mode 100644 index 00000000..0c4222f9 --- /dev/null +++ b/kompare/tests/diff/rcsm.diff @@ -0,0 +1,671 @@ +diff -nr dcop/client/dcop.cpp dcop2/client/dcop.cpp +d23 1 +a23 4 +#include +#include +#include + +d25 1 +a25 12 +#include +#include +#include +#include +#include +#include +#include + +// putenv() is not available on all platforms, so make sure the emulation +// wrapper is available in those cases by loading config.h! +#include + +d28 3 +a30 1 +#include "../kdatastream.h" +a33 2 +typedef QMap UserList; + +a35 14 +static QTextStream cout( stdout, IO_WriteOnly ); +static QTextStream cerr( stderr, IO_WriteOnly ); + +/** + * Session to send call to + * DefaultSession - current session. Current KDE session when called without + * --user or --all-users option. Otherwise this value ignores + * all users with more than one active session. + * AllSessions - Send to all sessions found. requires --user or --all-users. + * QuerySessions - Don't call DCOP, return a list of available sessions. + * CustomSession - Use the specified session + */ +enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; + +d121 1 +a121 1 +void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) +d123 1 +d139 1 +a139 1 + if ( !ok && args.isEmpty() ) +d156 2 +a157 2 + uint a = (*it).contains(','); + if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) +d164 1 +a164 2 +// exit(1); + return; +d246 5 +a250 6 + uint i = 0; + for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) + marshall( arg, args, i, *it ); + + if ( i != args.count() ) + { +a268 27 +/** + * Show command-line help and exit + */ +void showHelp( int exitCode = 0 ) +{ + cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl + << "" << endl + << "Console DCOP client" << endl + << "" << endl + << "Generic options:" << endl + << " --help Show help about options" << endl + << "" << endl + << "Options:" << endl + << " --pipe Call DCOP for each line read from stdin" << endl + << " --user Connect to the given user's DCOP server. This option will" << endl + << " ignore the values of the environment vars $DCOPSERVER and" << endl + << " $ICEAUTHORITY, even if they are set." << endl + << " If the user has more than one open session, you must also" << endl + << " use one of the --list-sessions, --session or --als-sessions" << endl + << " command-line options." << endl + << " --all-users Send the same DCOP call to all users with a running DCOP" << endl + << " server. Only failed calls to existing DCOP servers will" + << " generate an error message. If no DCOP server is available" << endl + << " at all, no error will be generated." << endl; + + exit( exitCode ); +} +d270 2 +a271 5 +/** + * Return a list of all users and their home directories. + * Returns an empty list if /etc/passwd cannot be read for some reason. + */ +static UserList userList() +a272 9 + UserList result; + + QFile f( "/etc/passwd" ); + + if( !f.open( IO_ReadOnly ) ) + { + cerr << "Can't open /etc/passwd for reading!" << endl; + return result; + } +d274 3 +a276 6 + QStringList l( QStringList::split( '\n', f.readAll() ) ); + + for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) + { + QStringList userInfo( QStringList::split( ':', *it, true ) ); + result[ userInfo[ 0 ] ] = userInfo[ 5 ]; +d279 3 +a281 42 + return result; +} + +/** + * Return a list of available DCOP sessions for the specified user + * An empty list means no sessions are available, or an error occurred. + */ +QStringList dcopSessionList( const QString &user, const QString &home ) +{ + if( home.isEmpty() ) + { + cerr << "WARNING: Cannot determine home directory for user " + << user << "!" << endl + << "Please check permissions or set the $DCOPSERVER variable manually before" << endl + << "calling dcop." << endl; + return QStringList(); + } + + QStringList result; + QFileInfo dirInfo( home ); + if( !dirInfo.exists() || !dirInfo.isReadable() ) + return result; + + QDir d( home ); + d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); + d.setNameFilter( ".DCOPserver*" ); + + const QFileInfoList *list = d.entryInfoList(); + if( !list ) + return result; + + QFileInfoListIterator it( *list ); + QFileInfo *fi; + + while ( ( fi = it.current() ) != 0 ) + { + if( fi->isReadable() ) + result.append( fi->fileName() ); + ++it; + } + return result; +} +a282 6 +/** + * Do the actual DCOP call + */ +void runDCOP( QCStringList args, UserList users, Session session, + const QString sessionName, bool readStdin ) +{ +d286 2 +a287 3 + QCStringList params; + DCOPClient *client = 0L; + if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) +d289 16 +a304 24 + // WARNING: This part (until the closing '}') could very + // well be broken now. As I don't know how to trigger and test + // dcoprefs this code is *not* tested. It compiles and it looks + // ok to me, but that's all I can say - Martijn (2001/12/24) + int delimPos = args[ 0 ].findRev( ',' ); + if( delimPos == -1 ) + { + cerr << "Error: '" << args[ 0 ] + << "' is not a valid DCOP reference." << endl; + exit( -1 ); + } + args[ 0 ][ delimPos ] = 0; + app = args[ 0 ].mid( 8 ); + delimPos++; + args[ 0 ][ args[ 0 ].length() - 1 ] = 0; + objid = args[ 0 ].mid( delimPos ); + if( args.count() > 1 ) + function = args[ 1 ]; + if( args.count() > 2 ) + { + params = args; + params.remove( params.begin() ); + params.remove( params.begin() ); + } +d308 31 +a338 84 + if( !args.isEmpty() ) + app = args[ 0 ]; + if( args.count() > 1 ) + objid = args[ 1 ]; + if( args.count() > 2 ) + function = args[ 2 ]; + if( args.count() > 3) + { + params = args; + params.remove( params.begin() ); + params.remove( params.begin() ); + params.remove( params.begin() ); + } + } + + bool firstRun = true; + UserList::Iterator it; + QStringList sessions; + bool presetDCOPServer = false; +// char *dcopStr = 0L; + QString dcopServer; + + for( it = users.begin(); it != users.end() || firstRun; it++ ) + { + firstRun = false; + + //cout << "Iterating '" << it.key() << "'" << endl; + + if( session == QuerySessions ) + { + QStringList sessions = dcopSessionList( it.key(), it.data() ); + if( sessions.isEmpty() ) + { + cout << "No active sessions"; + if( !( *it ).isEmpty() ) + cout << " for user " << *it; + cout << endl; + } + else + { + cout << "Active sessions "; + if( !( *it ).isEmpty() ) + cout << "for user " << *it << " "; + cout << ":" << endl; + + QStringList::Iterator sIt; + for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) + cout << " " << *sIt << endl; + + cout << endl; + } + continue; + } + + if( getenv( "DCOPSERVER" ) ) + { + sessions.append( getenv( "DCOPSERVER" ) ); + presetDCOPServer = true; + } + + if( users.count() > 1 || ( users.count() == 1 && + ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) + { + sessions = dcopSessionList( it.key(), it.data() ); + if( sessions.isEmpty() ) + { + if( users.count() > 1 ) + continue; + else + { + cerr << "ERROR: No active KDE sessions!" << endl + << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl + << "before calling dcop." << endl; + exit( -1 ); + } + } + else if( sessions.count() > 1 && session != AllSessions ) + { + cerr << "ERROR: Multiple available KDE sessions!" << endl + << "Please specify the correct session to use with --session or use the" << endl + << "--all-sessions option to broadcast to all sessions." << endl; + exit( -1 ); + } + } +a339 143 + if( users.count() > 1 || ( users.count() == 1 && + ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) + { + // Check for ICE authority file and if the file can be read by us + QString home = it.data(); + QString iceFile = it.data() + "/.ICEauthority"; + QFileInfo fi( iceFile ); + if( iceFile.isEmpty() ) + { + cerr << "WARNING: Cannot determine home directory for user " + << it.key() << "!" << endl + << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl + << "calling dcop." << endl; + } + else if( fi.exists() ) + { + if( fi.isReadable() ) + { + char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); + putenv( envStr ); + //cerr << "ice: " << envStr << endl; + } + else + { + cerr << "WARNING: ICE authority file " << iceFile + << "is not readable by you!" << endl + << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl + << "calling dcop." << endl; + } + } + else + { + if( users.count() > 1 ) + continue; + else + { + cerr << "WARNING: Cannot find ICE authority file " + << iceFile << "!" << endl + << "Please check permissions or set the $ICEAUTHORITY" + << " variable manually before" << endl + << "calling dcop." << endl; + } + } + } + + // Main loop + // If users is an empty list we're calling for the currently logged + // in user. In this case we don't have a session, but still want + // to iterate the loop once. + QStringList::Iterator sIt = sessions.begin(); + for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) + { + if( !presetDCOPServer && !users.isEmpty() ) + { + QString dcopFile = it.data() + "/" + *sIt; + QFile f( dcopFile ); + if( !f.open( IO_ReadOnly ) ) + { + cerr << "Can't open " << dcopFile << " for reading!" << endl; + exit( -1 ); + } + + QStringList l( QStringList::split( '\n', f.readAll() ) ); + dcopServer = l.first(); + + if( dcopServer.isEmpty() ) + { + cerr << "WARNING: Unable to determine DCOP server for session " + << *sIt << "!" << endl + << "Please check permissions or set the $DCOPSERVER variable manually before" << endl + << "calling dcop." << endl; + exit( -1 ); + } + } + + delete client; + client = new DCOPClient; + if( !dcopServer.isEmpty() ) + client->setServerAddress( dcopServer.ascii() ); + bool success = client->attach(); + if( !success ) + { + cerr << "ERROR: Couldn't attach to DCOP server!" << endl; + continue; + } + dcop = client; + + switch ( args.count() ) + { + case 0: + queryApplications(""); + break; + case 1: + if (endsWith(app, '*')) + queryApplications(app); + else + queryObjects( app, "" ); + break; + case 2: + if (endsWith(objid, '*')) + queryObjects(app, objid); + else + queryFunctions( app, objid ); + break; + case 3: + default: + if( readStdin ) + { + QCStringList::Iterator replaceArg = args.end(); + + QCStringList::Iterator it; + for( it = args.begin(); it != args.end(); it++ ) + if( *it == "%1" ) + replaceArg = it; + + // Read from stdin until EOF and call function for each line read + char *buf = new char[ 1000 ]; + while ( !feof( stdin ) ) + { + fgets( buf, 1000, stdin ); + + if( replaceArg != args.end() ) + *replaceArg = buf; + + callFunction( app, objid, function, params ); + } + } + else + { + // Just call function +// cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; + callFunction( app, objid, function, params ); + } + break; + } + // Another sIt++ would make the loop infinite... + if( users.isEmpty() ) + break; + } + + // Another it++ would make the loop infinite... + if( it == users.end() ) + break; +a340 106 +} + + +int main( int argc, char** argv ) +{ + bool readStdin = false; + int numOptions = 0; + QString user; + Session session = DefaultSession; + QString sessionName; + + // Scan for command-line options first + for( int pos = 1 ; pos <= argc - 1 ; pos++ ) + { + if( strcmp( argv[ pos ], "--help" ) == 0 ) + showHelp( 0 ); + else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) + { + readStdin = true; + numOptions++; + } + else if( strcmp( argv[ pos ], "--user" ) == 0 ) + { + if( pos <= argc - 2 ) + { + user = QString::fromLocal8Bit( argv[ pos + 1] ); + numOptions +=2; + pos++; + } + else + { + cerr << "Missing username for '--user' option!" << endl << endl; + showHelp( -1 ); + } + } + else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) + { + user = "*"; + numOptions ++; + } + else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) + { + session = QuerySessions; + numOptions ++; + } + else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) + { + session = AllSessions; + numOptions ++; + } + else if( argv[ pos ][ 0 ] == '-' ) + { + cerr << "Unknown command-line option '" << argv[ pos ] + << "'." << endl << endl; + showHelp( -1 ); + } + else + break; // End of options + } + + argc -= numOptions; + + QCStringList args; + for( int i = numOptions; i < argc + numOptions - 1; i++ ) + args.append( argv[ i + 1 ] ); + + if( readStdin && args.count() < 3 ) + { + cerr << "--pipe option only supported for function calls!" << endl << endl; + showHelp( -1 ); + } + + if( user == "*" && args.count() < 3 && session != QuerySessions ) + { + cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; + showHelp( -1 ); + } + + if( session == QuerySessions && !args.isEmpty() ) + { + cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; + showHelp( -1 ); + } + + if( session == QuerySessions && user.isEmpty() ) + { + cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl + << "--all-users options!" << endl << endl; + showHelp( -1 ); + } + + if( session != DefaultSession && session != QuerySessions && + args.count() < 3 ) + { + cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl + << "calls!" << endl << endl; + showHelp( -1 ); + } + + UserList users; + if( user == "*" ) + users = userList(); + else if( !user.isEmpty() ) + users[ user ] = userList()[ user ]; + + runDCOP( args, users, session, sessionName, readStdin ); +a343 3 + +// vim: set ts=8 sts=4 sw=4 noet: + +diff -nr dcop/client/dcopfind.cpp dcop2/client/dcopfind.cpp +d39 1 +a39 1 +bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +d121 1 +a121 1 + if ( types.count() != args.count() ) { +d131 1 +a131 1 + marshall(arg, args, i, *it); +d133 1 +a133 1 + if ( (uint) i != args.count() ) { +d224 1 +a224 5 + QCStringList params; + for( int i = 0; i < argc; i++ ) + params.append( args[ i ] ); + + findObject( app, objid, function, params ); +diff -nr dcop/client/marshall.cpp dcop2/client/marshall.cpp +d245 1 +a245 1 +void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) +d247 71 +a317 10 + if (type == "QStringList") + type = "QValueList"; + if (type == "QCStringList") + type = "QValueList"; + if( i > args.count() ) + { + qWarning("Not enough arguments."); + exit(1); + } + QString s = QString::fromLocal8Bit( args[ i ] ); +d319 28 +a346 57 + if ( type == "int" ) + arg << s.toInt(); + else if ( type == "uint" ) + arg << s.toUInt(); + else if ( type == "unsigned" ) + arg << s.toUInt(); + else if ( type == "unsigned int" ) + arg << s.toUInt(); + else if ( type == "long" ) + arg << s.toLong(); + else if ( type == "long int" ) + arg << s.toLong(); + else if ( type == "unsigned long" ) + arg << s.toULong(); + else if ( type == "unsigned long int" ) + arg << s.toULong(); + else if ( type == "float" ) + arg << s.toFloat(); + else if ( type == "double" ) + arg << s.toDouble(); + else if ( type == "bool" ) + arg << mkBool( s ); + else if ( type == "QString" ) + arg << s; + else if ( type == "QCString" ) + arg << QCString( args[ i ] ); + else if ( type == "QColor" ) + arg << mkColor( s ); + else if ( type == "QPoint" ) + arg << mkPoint( s ); + else if ( type == "QSize" ) + arg << mkSize( s ); + else if ( type == "QRect" ) + arg << mkRect( s ); + else if ( type == "QVariant" ) { + if ( s == "true" || s == "false" ) + arg << QVariant( mkBool( s ), 42 ); + else if ( s.left( 4 ) == "int(" ) + arg << QVariant( s.mid(4, s.length()-5).toInt() ); + else if ( s.left( 7 ) == "QPoint(" ) + arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); + else if ( s.left( 6 ) == "QSize(" ) + arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); + else if ( s.left( 6 ) == "QRect(" ) + arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); + else if ( s.left( 7 ) == "QColor(" ) + arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); + else + arg << QVariant( s ); + } else if ( type.startsWith("QValueList<")) { + type = type.mid(11, type.length() - 12); + QStringList list; + QString delim = s; + if (delim == "[") + delim = "]"; + if (delim == "(") + delim = ")"; +a347 34 + QByteArray dummy_data; + QDataStream dummy_arg(dummy_data, IO_WriteOnly); + + uint j = i; + uint count = 0; + // Parse list to get the count + while (true) { + if( j > args.count() ) + { + qWarning("List end-delimiter '%s' not found.", delim.latin1()); + exit(1); + } + if( QString::fromLocal8Bit( args[ j ] ) == delim ) + break; + marshall( dummy_arg, args, j, type ); + count++; + } + arg << (Q_UINT32) count; + // Parse the list for real + while (true) { + if( i > args.count() ) + { + qWarning("List end-delimiter '%s' not found.", delim.latin1()); + exit(1); + } + if( QString::fromLocal8Bit( args[ i ] ) == delim ) + break; + marshall( arg, args, i, type ); + } + } else { + qWarning( "cannot handle datatype '%s'", type.latin1() ); + exit(1); + } + i++; diff --git a/kompare/tests/diff/unified.diff b/kompare/tests/diff/unified.diff new file mode 100644 index 00000000..952e648c --- /dev/null +++ b/kompare/tests/diff/unified.diff @@ -0,0 +1,19 @@ +--- /home/John/lao Thu Apr 12 11:09:30 2001 ++++ /home/John/tzu Sat Jul 28 13:23:25 2001 +@@ -1,7 +1,6 @@ +-The Way that can be told of is not the eternal Way; +-The name that can be named is not the eternal name. + The Nameless is the origin of Heaven and Earth; +-The Named is the mother of all things. ++The named is the mother of all things. ++ + Therefore let there always be non-being, + so we may see their subtlety, + And let there always be being, +@@ -9,3 +8,6 @@ + The two are the same, + But after they are produced, + they have different names. ++They both may be called deep and profound. ++Deeper and more profound, ++The door of all subtleties! diff --git a/kompare/tests/diff/unifiedm.diff b/kompare/tests/diff/unifiedm.diff new file mode 100644 index 00000000..4a30c6b4 --- /dev/null +++ b/kompare/tests/diff/unifiedm.diff @@ -0,0 +1,911 @@ +diff -aur dcop/client/dcop.cpp dcop2/client/dcop.cpp +--- dcop/client/dcop.cpp Wed Jan 30 22:38:07 2002 ++++ dcop2/client/dcop.cpp Wed Jan 30 22:37:04 2002 +@@ -20,19 +20,47 @@ + + ******************************************************************/ + +-#include ++#include ++#include ++#include ++ + #include +-#include "../kdatastream.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++// putenv() is not available on all platforms, so make sure the emulation ++// wrapper is available in those cases by loading config.h! ++#include ++ + #include "../dcopclient.h" + #include "../dcopref.h" +-#include +-#include +-#include ++#include "../kdatastream.h" + + #include "marshall.cpp" + ++typedef QMap UserList; ++ + static DCOPClient* dcop = 0; + ++static QTextStream cout( stdout, IO_WriteOnly ); ++static QTextStream cerr( stderr, IO_WriteOnly ); ++ ++/** ++ * Session to send call to ++ * DefaultSession - current session. Current KDE session when called without ++ * --user or --all-users option. Otherwise this value ignores ++ * all users with more than one active session. ++ * AllSessions - Send to all sessions found. requires --user or --all-users. ++ * QuerySessions - Don't call DCOP, return a list of available sessions. ++ * CustomSession - Use the specified session ++ */ ++enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; ++ + bool startsWith(const QCString &id, const char *str, int n) + { + return !n || (strncmp(id.data(), str, n) == 0); +@@ -118,9 +146,8 @@ + } + } + +-void callFunction( const char* app, const char* obj, const char* func, int argc, char** args ) ++void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) + { +- + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); + int right = f.find( ')' ); +@@ -136,7 +163,7 @@ + bool ok = false; + QCStringList funcs = dcop->remoteFunctions( app, obj, &ok ); + QCString realfunc; +- if ( !ok && argc == 0 ) ++ if ( !ok && args.isEmpty() ) + goto doit; + if ( !ok ) + { +@@ -153,15 +180,16 @@ + + if ( l > 0 && (*it).mid( s, l - s ) == func ) { + realfunc = (*it).mid( s ); +- int a = (*it).contains(','); +- if ( ( a == 0 && argc == 0) || ( a > 0 && a + 1 == argc ) ) ++ uint a = (*it).contains(','); ++ if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) + break; + } + } + if ( realfunc.isEmpty() ) + { + qWarning("no such function"); +- exit(1); ++// exit(1); ++ return; + } + f = realfunc; + left = f.find( '(' ); +@@ -243,11 +271,12 @@ + QCString replyType; + QDataStream arg(data, IO_WriteOnly); + +- int i = 0; +- for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +- marshall(arg, argc, args, i, *it); +- } +- if ( i != argc ) { ++ uint i = 0; ++ for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) ++ marshall( arg, args, i, *it ); ++ ++ if ( i != args.count() ) ++ { + qWarning( "arguments do not match" ); + exit(1); + } +@@ -266,78 +295,479 @@ + } + } + ++/** ++ * Show command-line help and exit ++ */ ++void showHelp( int exitCode = 0 ) ++{ ++ cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl ++ << "" << endl ++ << "Console DCOP client" << endl ++ << "" << endl ++ << "Generic options:" << endl ++ << " --help Show help about options" << endl ++ << "" << endl ++ << "Options:" << endl ++ << " --pipe Call DCOP for each line read from stdin" << endl ++ << " --user Connect to the given user's DCOP server. This option will" << endl ++ << " ignore the values of the environment vars $DCOPSERVER and" << endl ++ << " $ICEAUTHORITY, even if they are set." << endl ++ << " If the user has more than one open session, you must also" << endl ++ << " use one of the --list-sessions, --session or --als-sessions" << endl ++ << " command-line options." << endl ++ << " --all-users Send the same DCOP call to all users with a running DCOP" << endl ++ << " server. Only failed calls to existing DCOP servers will" ++ << " generate an error message. If no DCOP server is available" << endl ++ << " at all, no error will be generated." << endl; ++ ++ exit( exitCode ); ++} + +- +-int main( int argc, char** argv ) ++/** ++ * Return a list of all users and their home directories. ++ * Returns an empty list if /etc/passwd cannot be read for some reason. ++ */ ++static UserList userList() + { ++ UserList result; ++ ++ QFile f( "/etc/passwd" ); ++ ++ if( !f.open( IO_ReadOnly ) ) ++ { ++ cerr << "Can't open /etc/passwd for reading!" << endl; ++ return result; ++ } + +- if ( argc > 1 && argv[1][0] == '-' ) { +- fprintf( stderr, "Usage: dcop [ application [object [function [arg1] [arg2] [arg3] ... ] ] ] \n" ); +- exit(0); ++ QStringList l( QStringList::split( '\n', f.readAll() ) ); ++ ++ for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) ++ { ++ QStringList userInfo( QStringList::split( ':', *it, true ) ); ++ result[ userInfo[ 0 ] ] = userInfo[ 5 ]; + } + +- DCOPClient client; +- client.attach(); +- dcop = &client; ++ return result; ++} ++ ++/** ++ * Return a list of available DCOP sessions for the specified user ++ * An empty list means no sessions are available, or an error occurred. ++ */ ++QStringList dcopSessionList( const QString &user, const QString &home ) ++{ ++ if( home.isEmpty() ) ++ { ++ cerr << "WARNING: Cannot determine home directory for user " ++ << user << "!" << endl ++ << "Please check permissions or set the $DCOPSERVER variable manually before" << endl ++ << "calling dcop." << endl; ++ return QStringList(); ++ } ++ ++ QStringList result; ++ QFileInfo dirInfo( home ); ++ if( !dirInfo.exists() || !dirInfo.isReadable() ) ++ return result; ++ ++ QDir d( home ); ++ d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); ++ d.setNameFilter( ".DCOPserver*" ); ++ ++ const QFileInfoList *list = d.entryInfoList(); ++ if( !list ) ++ return result; ++ ++ QFileInfoListIterator it( *list ); ++ QFileInfo *fi; ++ ++ while ( ( fi = it.current() ) != 0 ) ++ { ++ if( fi->isReadable() ) ++ result.append( fi->fileName() ); ++ ++it; ++ } ++ return result; ++} + ++/** ++ * Do the actual DCOP call ++ */ ++void runDCOP( QCStringList args, UserList users, Session session, ++ const QString sessionName, bool readStdin ) ++{ + QCString app; + QCString objid; + QCString function; +- char **args = 0; +- if ((argc > 1) && (strncmp(argv[1], "DCOPRef(", 8)) == 0) ++ QCStringList params; ++ DCOPClient *client = 0L; ++ if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) + { +- char *delim = strchr(argv[1], ','); +- if (!delim) +- { +- fprintf(stderr, "Error: '%s' is not a valid DCOP reference.\n", argv[1]); +- return 1; +- } +- *delim = 0; +- app = argv[1] + 8; +- delim++; +- delim[strlen(delim)-1] = 0; +- objid = delim; +- if (argc > 2) +- function = argv[2]; +- if (argc > 3) +- args = &argv[3]; +- argc++; ++ // WARNING: This part (until the closing '}') could very ++ // well be broken now. As I don't know how to trigger and test ++ // dcoprefs this code is *not* tested. It compiles and it looks ++ // ok to me, but that's all I can say - Martijn (2001/12/24) ++ int delimPos = args[ 0 ].findRev( ',' ); ++ if( delimPos == -1 ) ++ { ++ cerr << "Error: '" << args[ 0 ] ++ << "' is not a valid DCOP reference." << endl; ++ exit( -1 ); ++ } ++ args[ 0 ][ delimPos ] = 0; ++ app = args[ 0 ].mid( 8 ); ++ delimPos++; ++ args[ 0 ][ args[ 0 ].length() - 1 ] = 0; ++ objid = args[ 0 ].mid( delimPos ); ++ if( args.count() > 1 ) ++ function = args[ 1 ]; ++ if( args.count() > 2 ) ++ { ++ params = args; ++ params.remove( params.begin() ); ++ params.remove( params.begin() ); ++ } + } + else + { +- if (argc > 1) +- app = argv[1]; +- if (argc > 2) +- objid = argv[2]; +- if (argc > 3) +- function = argv[3]; +- if (argc > 4) +- args = &argv[4]; +- } +- +- switch ( argc ) { +- case 0: +- case 1: +- queryApplications(""); +- break; +- case 2: +- if (endsWith(app, '*')) +- queryApplications(app); +- else +- queryObjects( app, "" ); +- break; +- case 3: +- if (endsWith(objid, '*')) +- queryObjects(app, objid); +- else +- queryFunctions( app, objid ); +- break; +- case 4: +- default: +- callFunction( app, objid, function, argc - 4, args ); +- break; ++ if( !args.isEmpty() ) ++ app = args[ 0 ]; ++ if( args.count() > 1 ) ++ objid = args[ 1 ]; ++ if( args.count() > 2 ) ++ function = args[ 2 ]; ++ if( args.count() > 3) ++ { ++ params = args; ++ params.remove( params.begin() ); ++ params.remove( params.begin() ); ++ params.remove( params.begin() ); ++ } ++ } ++ ++ bool firstRun = true; ++ UserList::Iterator it; ++ QStringList sessions; ++ bool presetDCOPServer = false; ++// char *dcopStr = 0L; ++ QString dcopServer; ++ ++ for( it = users.begin(); it != users.end() || firstRun; it++ ) ++ { ++ firstRun = false; ++ ++ //cout << "Iterating '" << it.key() << "'" << endl; ++ ++ if( session == QuerySessions ) ++ { ++ QStringList sessions = dcopSessionList( it.key(), it.data() ); ++ if( sessions.isEmpty() ) ++ { ++ cout << "No active sessions"; ++ if( !( *it ).isEmpty() ) ++ cout << " for user " << *it; ++ cout << endl; ++ } ++ else ++ { ++ cout << "Active sessions "; ++ if( !( *it ).isEmpty() ) ++ cout << "for user " << *it << " "; ++ cout << ":" << endl; ++ ++ QStringList::Iterator sIt; ++ for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) ++ cout << " " << *sIt << endl; ++ ++ cout << endl; ++ } ++ continue; ++ } ++ ++ if( getenv( "DCOPSERVER" ) ) ++ { ++ sessions.append( getenv( "DCOPSERVER" ) ); ++ presetDCOPServer = true; ++ } ++ ++ if( users.count() > 1 || ( users.count() == 1 && ++ ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) ++ { ++ sessions = dcopSessionList( it.key(), it.data() ); ++ if( sessions.isEmpty() ) ++ { ++ if( users.count() > 1 ) ++ continue; ++ else ++ { ++ cerr << "ERROR: No active KDE sessions!" << endl ++ << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl ++ << "before calling dcop." << endl; ++ exit( -1 ); ++ } ++ } ++ else if( sessions.count() > 1 && session != AllSessions ) ++ { ++ cerr << "ERROR: Multiple available KDE sessions!" << endl ++ << "Please specify the correct session to use with --session or use the" << endl ++ << "--all-sessions option to broadcast to all sessions." << endl; ++ exit( -1 ); ++ } ++ } + ++ if( users.count() > 1 || ( users.count() == 1 && ++ ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) ++ { ++ // Check for ICE authority file and if the file can be read by us ++ QString home = it.data(); ++ QString iceFile = it.data() + "/.ICEauthority"; ++ QFileInfo fi( iceFile ); ++ if( iceFile.isEmpty() ) ++ { ++ cerr << "WARNING: Cannot determine home directory for user " ++ << it.key() << "!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ else if( fi.exists() ) ++ { ++ if( fi.isReadable() ) ++ { ++ char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); ++ putenv( envStr ); ++ //cerr << "ice: " << envStr << endl; ++ } ++ else ++ { ++ cerr << "WARNING: ICE authority file " << iceFile ++ << "is not readable by you!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ } ++ else ++ { ++ if( users.count() > 1 ) ++ continue; ++ else ++ { ++ cerr << "WARNING: Cannot find ICE authority file " ++ << iceFile << "!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY" ++ << " variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ } ++ } ++ ++ // Main loop ++ // If users is an empty list we're calling for the currently logged ++ // in user. In this case we don't have a session, but still want ++ // to iterate the loop once. ++ QStringList::Iterator sIt = sessions.begin(); ++ for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) ++ { ++ if( !presetDCOPServer && !users.isEmpty() ) ++ { ++ QString dcopFile = it.data() + "/" + *sIt; ++ QFile f( dcopFile ); ++ if( !f.open( IO_ReadOnly ) ) ++ { ++ cerr << "Can't open " << dcopFile << " for reading!" << endl; ++ exit( -1 ); ++ } ++ ++ QStringList l( QStringList::split( '\n', f.readAll() ) ); ++ dcopServer = l.first(); ++ ++ if( dcopServer.isEmpty() ) ++ { ++ cerr << "WARNING: Unable to determine DCOP server for session " ++ << *sIt << "!" << endl ++ << "Please check permissions or set the $DCOPSERVER variable manually before" << endl ++ << "calling dcop." << endl; ++ exit( -1 ); ++ } ++ } ++ ++ delete client; ++ client = new DCOPClient; ++ if( !dcopServer.isEmpty() ) ++ client->setServerAddress( dcopServer.ascii() ); ++ bool success = client->attach(); ++ if( !success ) ++ { ++ cerr << "ERROR: Couldn't attach to DCOP server!" << endl; ++ continue; ++ } ++ dcop = client; ++ ++ switch ( args.count() ) ++ { ++ case 0: ++ queryApplications(""); ++ break; ++ case 1: ++ if (endsWith(app, '*')) ++ queryApplications(app); ++ else ++ queryObjects( app, "" ); ++ break; ++ case 2: ++ if (endsWith(objid, '*')) ++ queryObjects(app, objid); ++ else ++ queryFunctions( app, objid ); ++ break; ++ case 3: ++ default: ++ if( readStdin ) ++ { ++ QCStringList::Iterator replaceArg = args.end(); ++ ++ QCStringList::Iterator it; ++ for( it = args.begin(); it != args.end(); it++ ) ++ if( *it == "%1" ) ++ replaceArg = it; ++ ++ // Read from stdin until EOF and call function for each line read ++ char *buf = new char[ 1000 ]; ++ while ( !feof( stdin ) ) ++ { ++ fgets( buf, 1000, stdin ); ++ ++ if( replaceArg != args.end() ) ++ *replaceArg = buf; ++ ++ callFunction( app, objid, function, params ); ++ } ++ } ++ else ++ { ++ // Just call function ++// cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; ++ callFunction( app, objid, function, params ); ++ } ++ break; ++ } ++ // Another sIt++ would make the loop infinite... ++ if( users.isEmpty() ) ++ break; ++ } ++ ++ // Another it++ would make the loop infinite... ++ if( it == users.end() ) ++ break; + } ++} ++ ++ ++int main( int argc, char** argv ) ++{ ++ bool readStdin = false; ++ int numOptions = 0; ++ QString user; ++ Session session = DefaultSession; ++ QString sessionName; ++ ++ // Scan for command-line options first ++ for( int pos = 1 ; pos <= argc - 1 ; pos++ ) ++ { ++ if( strcmp( argv[ pos ], "--help" ) == 0 ) ++ showHelp( 0 ); ++ else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) ++ { ++ readStdin = true; ++ numOptions++; ++ } ++ else if( strcmp( argv[ pos ], "--user" ) == 0 ) ++ { ++ if( pos <= argc - 2 ) ++ { ++ user = QString::fromLocal8Bit( argv[ pos + 1] ); ++ numOptions +=2; ++ pos++; ++ } ++ else ++ { ++ cerr << "Missing username for '--user' option!" << endl << endl; ++ showHelp( -1 ); ++ } ++ } ++ else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) ++ { ++ user = "*"; ++ numOptions ++; ++ } ++ else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) ++ { ++ session = QuerySessions; ++ numOptions ++; ++ } ++ else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) ++ { ++ session = AllSessions; ++ numOptions ++; ++ } ++ else if( argv[ pos ][ 0 ] == '-' ) ++ { ++ cerr << "Unknown command-line option '" << argv[ pos ] ++ << "'." << endl << endl; ++ showHelp( -1 ); ++ } ++ else ++ break; // End of options ++ } ++ ++ argc -= numOptions; ++ ++ QCStringList args; ++ for( int i = numOptions; i < argc + numOptions - 1; i++ ) ++ args.append( argv[ i + 1 ] ); ++ ++ if( readStdin && args.count() < 3 ) ++ { ++ cerr << "--pipe option only supported for function calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( user == "*" && args.count() < 3 && session != QuerySessions ) ++ { ++ cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session == QuerySessions && !args.isEmpty() ) ++ { ++ cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session == QuerySessions && user.isEmpty() ) ++ { ++ cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl ++ << "--all-users options!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session != DefaultSession && session != QuerySessions && ++ args.count() < 3 ) ++ { ++ cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl ++ << "calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ UserList users; ++ if( user == "*" ) ++ users = userList(); ++ else if( !user.isEmpty() ) ++ users[ user ] = userList()[ user ]; ++ ++ runDCOP( args, users, session, sessionName, readStdin ); + + return 0; + } ++ ++// vim: set ts=8 sts=4 sw=4 noet: ++ +diff -aur dcop/client/dcopfind.cpp dcop2/client/dcopfind.cpp +--- dcop/client/dcopfind.cpp Wed Jan 30 22:38:07 2002 ++++ dcop2/client/dcopfind.cpp Wed Jan 30 22:37:04 2002 +@@ -36,7 +36,7 @@ + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +-bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) ++bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +@@ -118,7 +118,7 @@ + f = fc; + } + +- if ( (int) types.count() != argc ) { ++ if ( types.count() != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +@@ -128,9 +128,9 @@ + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +- marshall(arg, argc, args, i, *it); ++ marshall(arg, args, i, *it); + } +- if ( (int) i != argc ) { ++ if ( (uint) i != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +@@ -221,7 +221,11 @@ + argc = 0; + } + +- findObject( app, objid, function, argc, args ); ++ QCStringList params; ++ for( int i = 0; i < argc; i++ ) ++ params.append( args[ i ] ); ++ ++ findObject( app, objid, function, params ); + + return 0; + } +diff -aur dcop/client/marshall.cpp dcop2/client/marshall.cpp +--- dcop/client/marshall.cpp Wed Jan 30 22:38:07 2002 ++++ dcop2/client/marshall.cpp Wed Jan 30 22:37:04 2002 +@@ -242,108 +242,110 @@ + + } + +-void marshall(QDataStream &arg, int argc, char **argv, int &i, QString type) ++void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) + { +- if (type == "QStringList") +- type = "QValueList"; +- if (type == "QCStringList") +- type = "QValueList"; +- if (i >= argc) +- { +- qWarning("Not enough arguments."); +- exit(1); +- } +- QString s = QString::fromLocal8Bit(argv[i]); +- +- if ( type == "int" ) +- arg << s.toInt(); +- else if ( type == "uint" ) +- arg << s.toUInt(); +- else if ( type == "unsigned" ) +- arg << s.toUInt(); +- else if ( type == "unsigned int" ) +- arg << s.toUInt(); +- else if ( type == "long" ) +- arg << s.toLong(); +- else if ( type == "long int" ) +- arg << s.toLong(); +- else if ( type == "unsigned long" ) +- arg << s.toULong(); +- else if ( type == "unsigned long int" ) +- arg << s.toULong(); +- else if ( type == "float" ) +- arg << s.toFloat(); +- else if ( type == "double" ) +- arg << s.toDouble(); +- else if ( type == "bool" ) +- arg << mkBool( s ); +- else if ( type == "QString" ) +- arg << s; +- else if ( type == "QCString" ) +- arg << QCString( argv[i] ); +- else if ( type == "QColor" ) +- arg << mkColor( s ); +- else if ( type == "QPoint" ) +- arg << mkPoint( s ); +- else if ( type == "QSize" ) +- arg << mkSize( s ); +- else if ( type == "QRect" ) +- arg << mkRect( s ); +- else if ( type == "QVariant" ) { +- if ( s == "true" || s == "false" ) +- arg << QVariant( mkBool( s ), 42 ); +- else if ( s.left( 4 ) == "int(" ) +- arg << QVariant( s.mid(4, s.length()-5).toInt() ); +- else if ( s.left( 7 ) == "QPoint(" ) +- arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +- else if ( s.left( 6 ) == "QSize(" ) +- arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +- else if ( s.left( 6 ) == "QRect(" ) +- arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +- else if ( s.left( 7 ) == "QColor(" ) +- arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +- else +- arg << QVariant( s ); +- } else if ( type.startsWith("QValueList<")) { +- type = type.mid(11, type.length() - 12); +- QStringList list; +- QString delim = s; +- if (delim == "[") +- delim = "]"; +- if (delim == "(") +- delim = ")"; +- i++; +- QByteArray dummy_data; +- QDataStream dummy_arg(dummy_data, IO_WriteOnly); ++ if (type == "QStringList") ++ type = "QValueList"; ++ if (type == "QCStringList") ++ type = "QValueList"; ++ if( i > args.count() ) ++ { ++ qWarning("Not enough arguments."); ++ exit(1); ++ } ++ QString s = QString::fromLocal8Bit( args[ i ] ); + +- int j = i; +- int count = 0; +- // Parse list to get the count +- while (true) { +- if (j >= argc) +- { +- qWarning("List end-delimiter '%s' not found.", delim.latin1()); +- exit(1); +- } +- if (argv[j] == delim) break; +- marshall(dummy_arg, argc, argv, j, type); +- count++; +- } +- arg << (Q_UINT32) count; +- // Parse the list for real +- while (true) { +- if (i >= argc) +- { +- qWarning("List end-delimiter '%s' not found.", delim.latin1()); +- exit(1); +- } +- if (argv[i] == delim) break; +- marshall(arg, argc, argv, i, type); +- } +- } else { +- qWarning( "cannot handle datatype '%s'", type.latin1() ); +- exit(1); +- } ++ if ( type == "int" ) ++ arg << s.toInt(); ++ else if ( type == "uint" ) ++ arg << s.toUInt(); ++ else if ( type == "unsigned" ) ++ arg << s.toUInt(); ++ else if ( type == "unsigned int" ) ++ arg << s.toUInt(); ++ else if ( type == "long" ) ++ arg << s.toLong(); ++ else if ( type == "long int" ) ++ arg << s.toLong(); ++ else if ( type == "unsigned long" ) ++ arg << s.toULong(); ++ else if ( type == "unsigned long int" ) ++ arg << s.toULong(); ++ else if ( type == "float" ) ++ arg << s.toFloat(); ++ else if ( type == "double" ) ++ arg << s.toDouble(); ++ else if ( type == "bool" ) ++ arg << mkBool( s ); ++ else if ( type == "QString" ) ++ arg << s; ++ else if ( type == "QCString" ) ++ arg << QCString( args[ i ] ); ++ else if ( type == "QColor" ) ++ arg << mkColor( s ); ++ else if ( type == "QPoint" ) ++ arg << mkPoint( s ); ++ else if ( type == "QSize" ) ++ arg << mkSize( s ); ++ else if ( type == "QRect" ) ++ arg << mkRect( s ); ++ else if ( type == "QVariant" ) { ++ if ( s == "true" || s == "false" ) ++ arg << QVariant( mkBool( s ), 42 ); ++ else if ( s.left( 4 ) == "int(" ) ++ arg << QVariant( s.mid(4, s.length()-5).toInt() ); ++ else if ( s.left( 7 ) == "QPoint(" ) ++ arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); ++ else if ( s.left( 6 ) == "QSize(" ) ++ arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); ++ else if ( s.left( 6 ) == "QRect(" ) ++ arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); ++ else if ( s.left( 7 ) == "QColor(" ) ++ arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); ++ else ++ arg << QVariant( s ); ++ } else if ( type.startsWith("QValueList<")) { ++ type = type.mid(11, type.length() - 12); ++ QStringList list; ++ QString delim = s; ++ if (delim == "[") ++ delim = "]"; ++ if (delim == "(") ++ delim = ")"; + i++; ++ QByteArray dummy_data; ++ QDataStream dummy_arg(dummy_data, IO_WriteOnly); ++ ++ uint j = i; ++ uint count = 0; ++ // Parse list to get the count ++ while (true) { ++ if( j > args.count() ) ++ { ++ qWarning("List end-delimiter '%s' not found.", delim.latin1()); ++ exit(1); ++ } ++ if( QString::fromLocal8Bit( args[ j ] ) == delim ) ++ break; ++ marshall( dummy_arg, args, j, type ); ++ count++; ++ } ++ arg << (Q_UINT32) count; ++ // Parse the list for real ++ while (true) { ++ if( i > args.count() ) ++ { ++ qWarning("List end-delimiter '%s' not found.", delim.latin1()); ++ exit(1); ++ } ++ if( QString::fromLocal8Bit( args[ i ] ) == delim ) ++ break; ++ marshall( arg, args, i, type ); ++ } ++ } else { ++ qWarning( "cannot handle datatype '%s'", type.latin1() ); ++ exit(1); ++ } ++ i++; + } + diff --git a/kompare/tests/diff/unifiedp.diff b/kompare/tests/diff/unifiedp.diff new file mode 100644 index 00000000..891b8b8d --- /dev/null +++ b/kompare/tests/diff/unifiedp.diff @@ -0,0 +1,19 @@ +--- /home/John/lao Thu Apr 12 11:09:30 2001 ++++ /home/John/tzu Sat Jul 28 13:23:25 2001 +@@ -1,7 +1,6 @@ +-The Way that can be told of is not the eternal Way; +-The name that can be named is not the eternal name. + The Nameless is the origin of Heaven and Earth; +-The Named is the mother of all things. ++The named is the mother of all things. ++ + Therefore let there always be non-being, + so we may see their subtlety, + And let there always be being, +@@ -9,3 +8,6 @@ And let there always be being, + The two are the same, + But after they are produced, + they have different names. ++They both may be called deep and profound. ++Deeper and more profound, ++The door of all subtleties! diff --git a/kompare/tests/perforce/context.diff b/kompare/tests/perforce/context.diff new file mode 100644 index 00000000..a25c2461 --- /dev/null +++ b/kompare/tests/perforce/context.diff @@ -0,0 +1,8 @@ +==== //depot/testje#2 - /home/bruggie/perforce-repo/testje ==== +*************** +*** 3,5 **** +--- 3,6 ---- + + but i + 'll see what this is all about later on ++ More lines for #3 to see what happens in a multifile diff diff --git a/kompare/tests/perforce/contextm.diff b/kompare/tests/perforce/contextm.diff new file mode 100644 index 00000000..07380b31 --- /dev/null +++ b/kompare/tests/perforce/contextm.diff @@ -0,0 +1,23 @@ +==== //depot/craphola#1 - /home/bruggie/perforce-repo/craphola ==== +*************** +*** 2,4 **** +--- 2,11 ---- + I'm just adding this file to see what it does with multiple file that have differences + + Now i'll add some text more later in the #2 revision but that has to wait a bit ++ ++ This will be a copy and paste of the previous lines just to have some changes for revision 2 of this file ++ ++ This is another t4extfile used to test the perforce diff stuff ++ I'm just adding this file to see what it does with multiple file that have differences ++ ++ Now i'll add some text more later in the #2 revision but that has to wait a bit +==== //depot/testje#2 - /home/bruggie/perforce-repo/testje ==== +*************** +*** 3,5 **** +--- 3,7 ---- + + but i + 'll see what this is all about later on ++ ++ More lines for #3 to see what happens in a multifile diff diff --git a/kompare/tests/perforce/rcs.diff b/kompare/tests/perforce/rcs.diff new file mode 100644 index 00000000..ce99087d --- /dev/null +++ b/kompare/tests/perforce/rcs.diff @@ -0,0 +1,3 @@ +==== //depot/testje#2 - /home/bruggie/perforce-repo/testje ==== +a5 1 +More lines for #3 to see what happens in a multifile diff diff --git a/kompare/tests/perforce/rcsm.diff b/kompare/tests/perforce/rcsm.diff new file mode 100644 index 00000000..bee028a0 --- /dev/null +++ b/kompare/tests/perforce/rcsm.diff @@ -0,0 +1,12 @@ +==== //depot/craphola#1 - /home/bruggie/perforce-repo/craphola ==== +a4 7 + +This will be a copy and paste of the previous lines just to have some changes for revision 2 of this file + +This is another t4extfile used to test the perforce diff stuff +I'm just adding this file to see what it does with multiple file that have differences + +Now i'll add some text more later in the #2 revision but that has to wait a bit +==== //depot/testje#2 - /home/bruggie/perforce-repo/testje ==== +a5 2 +More lines for #3 to see what happens in a multifile diff diff --git a/kompare/tests/perforce/unified.diff b/kompare/tests/perforce/unified.diff new file mode 100644 index 00000000..f9235ba9 --- /dev/null +++ b/kompare/tests/perforce/unified.diff @@ -0,0 +1,6 @@ +==== //depot/testje#2 - /home/bruggie/perforce-repo/testje ==== +@@ -3,3 +3,4 @@ + + but i + 'll see what this is all about later on ++More lines for #3 to see what happens in a multifile diff diff --git a/kompare/tests/perforce/unifiedm.diff b/kompare/tests/perforce/unifiedm.diff new file mode 100644 index 00000000..6aa832e7 --- /dev/null +++ b/kompare/tests/perforce/unifiedm.diff @@ -0,0 +1,19 @@ +==== //depot/craphola#1 - /home/bruggie/perforce-repo/craphola ==== +@@ -2,3 +2,10 @@ + I'm just adding this file to see what it does with multiple file that have differences + + Now i'll add some text more later in the #2 revision but that has to wait a bit ++ ++This will be a copy and paste of the previous lines just to have some changes for revision 2 of this file ++ ++This is another t4extfile used to test the perforce diff stuff ++I'm just adding this file to see what it does with multiple file that have differences ++ ++Now i'll add some text more later in the #2 revision but that has to wait a bit +==== //depot/testje#2 - /home/bruggie/perforce-repo/testje ==== +@@ -3,3 +3,5 @@ + + but i + 'll see what this is all about later on ++ ++More lines for #3 to see what happens in a multifile diff diff --git a/kompare/tests/subversion/context.diff b/kompare/tests/subversion/context.diff new file mode 100644 index 00000000..b8380037 --- /dev/null +++ b/kompare/tests/subversion/context.diff @@ -0,0 +1,9 @@ +Index: NEWS +=================================================================== +*** NEWS +--- NEWS Sun Sep 22 14:34:37 2002 +*************** +*** 1 **** +! +--- 1 ---- +! just a fake modif for kompare tests diff --git a/kompare/tests/subversion/contextm.diff b/kompare/tests/subversion/contextm.diff new file mode 100644 index 00000000..fbb61263 --- /dev/null +++ b/kompare/tests/subversion/contextm.diff @@ -0,0 +1,180 @@ +Index: NEWS +=================================================================== +*** NEWS +--- NEWS Sun Sep 22 14:34:37 2002 +*************** +*** 1 **** +! +--- 1 ---- +! just a fake modif for kompare tests +Index: README +=================================================================== +*** README +--- README Fri Sep 13 23:05:48 2002 +*************** +*** 1,117 **** +! Vim KPart +! +! +! by Philippe Fremy +! +! +! Okay, I made it : a Vim KPart! +! +! This means that you can have Vim embedded inside Konqueror, and everywhere a +! text ReadWrite or ReadOnly KPart is requested. Actually, there is almost no +! place right now where this is the case in KDE. KMail uses its own editor, +! KDEvelop uses its own editor, Kate uses some more powerful Kate component. +! +! But this only the beginning. Enabling a part in those programs shouldn't be +! much hassle and you can probably help me do it. My hope is really to get +! KDevelop use Vim. +! +! +! ======= OBSOLETE =========== +! Requirements: +! ------------- +! To make this KPart work, you need a graphicial Vim version 6 with the client-server stuff feature activated and with the vim60-vimpart-patch.diff applied. The patch is in this dir. I hope to get it into the main Vim tree. KVim has already the patch but is slightly less stable that the original GVim. A big advantage of KVim is that you get the native KDE dialogs when vim asks a question. +! +! +! ======= OBSOLETE =========== +! Installation: +! ------------- +! To make your vimpart work, you'll have to go into the vimpart directory and run "testVim your_patched_vim". If the test does work, a file goodVim will be created. You will be able to install and use the component. Else, the test will report why it fails (features missing in vim). +! +! +! +! ======= OBSOLETE =========== +! Testing: +! -------- +! If you want to see your component without installing it, you can do the +! following: +! +! 1. configure, build. Then go into the Vimpart subdirectory. +! +! 2. Include the current Vimpart directory in your KDEDIRS: +! export KDEDIRS=`pwd`:$KDEDIR +! +! 3. Symlink .libs to lib +! ln -s .libs lib +! +! 4. Create pseudo share/services dir: +! mkdir share; mkdir share/services; +! +! 5. Symlink to Vimpart.desktop: +! ln -s Vimpart.desktop share/services/Vimpart.desktop +! +! 6. Create a pseudo share/config dir +! mkdir share/config; +! +! 7. Symlink to vimwidgetrc +! ln -s vimwidgetrc share/config/vimwidgetrc +! +! 8. Update the desktop mimetype database: +! kbuildsycoca +! +! To test it, run VimPartShell. Or run konqueror from this dir and click on a +! text file. +! +! +! ======= OBSOLETE =========== +! Remarks: +! -------- +! The initial preference of the Vim KPart is 10. Kate uses 8, so if you install the part, it will override Kate for all the mimetypes. You can always change that by manually editing the initial preference in the desktop file or by simply selecting which editor you prefer for which mimetype in the control center. +! +! If you find some mimetype not handled by the Vim KPart although they should be, send me a patch! +! +! +! +! How it works: +! ------------- +! At the beginning, we started to write KVim, a port of GVim to KDE to make +! it possible to embed Vim inside KDE. But with the latest version of Vim, it +! turns out that it is not necessary to have a native Vim. +! +! +! I use QXembed, a widget which can embed any X application if it knows its X Window Id, using some X feature. The patch I provide will make vim displays its window id on stdout when the window is mapped. GVim 6.0 then provides a way to send commands to a Vim window from another process. If you look at the VimWidget source, you will see that 70% of the code is there to handle the communication process. The rest uses the communication channel to send the vim commands needed by kpart and ktexteditor. +! +! As far as I can tell, the part is race-condition free. If you issue many sendNormalCmd and many evalExpr, they are guaranted to be executed sequentially. This has caused me enough problems when it wasn't the case! +! +! +! Qt, KDE2 and KDE3: +! ------------------ +! The VimWidget itself depends very litle on KDE. It is quite easy to port remove the KDE specific stuff, to use it in a Qt only program. In fact, at the beginning, it was only Qt-based. +! +! +! +! ======= OBSOLETE =========== +! Features & TODO: +! ---------------- +! I think most basic features required by an editor widget or part are supported. There are some possible improvement but I would like more feedback to know what really needs to be done. So don't hesitate to write me about your feelings using this. +! +! My TODO list is: +! - restore the editing mode after sendCmd +! - implement KTextEditor interface +! - add some useful actions to the part (like search, ...) +! +! +! +! Feedback: +! --------- +! For the Vim KPart : pfremy@kde.com +! For KVim: pfremy@kde.org, mikmak@freenux.org, orzel@kde.org +! +! +! +! +! +! +! + + + +--- 1,47 ---- +! Yes, that's really a Vim Komponent :) +! Yes, you can have Vim inside KDE apps, you guessed it :) + ++ So, it's designed for KDE 3.x (if someone wants to port it to KDE 2 that ++ should be easy), it uses GVim or KVim (even Motif Vim works) 6.x. ++ It can be used in different apps : ++ - KDevelop (version 3) ++ - Konqueror (as a file viewer) ++ - KWrite ++ - KMail (coming in KDE 3.2) ++ - Kompare, KBabel ........ ;) ++ ++ CONFIGURATION ++ ============= ++ once you compiled and installed it as any other app, ++ start your KDE Control Center, go to the file manager section ++ and open the Vim Component configuration module. ++ Here, you have to select a Vim executable which may be found on ++ your computer (generally /usr/bin/vim) will do it fine for most ++ linux distributions. All you need is that it's a GUI-enabled Vim 6.0 or ++ better. ++ Push the test button, if that's okay then that's should be enough to start ++ using it :) ++ ++ FUNCTIONMENT ++ ============ ++ Philippe Fremy (pfremy@kde.com) wrote the initial version of this kpart. ++ The concept is to start a normal GUI Vim (like gvim), then "embeds" the Vim ++ window into a KDE Widget. ++ It used to be based on the ClientServer feature of Vim (type :help ++ clientserver in Vim for more info) using external processus to control the ++ embedded Vim. That was running quite fine, but was slow :/ ++ We changed that :) ++ Now we communicate directly from the kpart to the embedded Vim thanks to X11 ++ without using externals processus. That's much faster and reliable ;) ++ KVim has also another remote control system using KDE's DCOP communication ++ backend. ++ Currently I would advice people to use DCOP when running KVim and using X11 ++ communication with GVim (DCOP won't work with GVim anyway). ++ There may be some differences in speed, though I have not noticed it here. ++ The most important difference is that DCOP provides a signal system and that can ++ make a difference to improve the interaction between KVim and the hosting ++ application (KDevelop for example). But it's not yet used. + ++ Hope you'll enjoy Vim inside KDE :) ++ Mickael "Mikmak" Marchand (marchand@kde.org) + diff --git a/kompare/tests/subversion/ed.diff b/kompare/tests/subversion/ed.diff new file mode 100644 index 00000000..512b3880 --- /dev/null +++ b/kompare/tests/subversion/ed.diff @@ -0,0 +1,5 @@ +Index: NEWS +=================================================================== +1c +just a fake modif for kompare tests +. diff --git a/kompare/tests/subversion/edm.diff b/kompare/tests/subversion/edm.diff new file mode 100644 index 00000000..dc51b21f --- /dev/null +++ b/kompare/tests/subversion/edm.diff @@ -0,0 +1,57 @@ +Index: NEWS +=================================================================== +1c +just a fake modif for kompare tests +. +Index: README +=================================================================== +116a +Hope you'll enjoy Vim inside KDE :) +Mickael "Mikmak" Marchand (marchand@kde.org) +. +115a +So, it's designed for KDE 3.x (if someone wants to port it to KDE 2 that +should be easy), it uses GVim or KVim (even Motif Vim works) 6.x. +It can be used in different apps : + - KDevelop (version 3) + - Konqueror (as a file viewer) + - KWrite + - KMail (coming in KDE 3.2) + - Kompare, KBabel ........ ;) + +CONFIGURATION +============= +once you compiled and installed it as any other app, +start your KDE Control Center, go to the file manager section +and open the Vim Component configuration module. +Here, you have to select a Vim executable which may be found on +your computer (generally /usr/bin/vim) will do it fine for most +linux distributions. All you need is that it's a GUI-enabled Vim 6.0 or +better. +Push the test button, if that's okay then that's should be enough to start +using it :) + +FUNCTIONMENT +============ +Philippe Fremy (pfremy@kde.com) wrote the initial version of this kpart. +The concept is to start a normal GUI Vim (like gvim), then "embeds" the Vim +window into a KDE Widget. +It used to be based on the ClientServer feature of Vim (type :help +clientserver in Vim for more info) using external processus to control the +embedded Vim. That was running quite fine, but was slow :/ +We changed that :) +Now we communicate directly from the kpart to the embedded Vim thanks to X11 +without using externals processus. That's much faster and reliable ;) +KVim has also another remote control system using KDE's DCOP communication +backend. +Currently I would advice people to use DCOP when running KVim and using X11 +communication with GVim (DCOP won't work with GVim anyway). +There may be some differences in speed, though I have not noticed it here. +The most important difference is that DCOP provides a signal system and that can +make a difference to improve the interaction between KVim and the hosting +application (KDevelop for example). But it's not yet used. +. +1,114c +Yes, that's really a Vim Komponent :) +Yes, you can have Vim inside KDE apps, you guessed it :) +. diff --git a/kompare/tests/subversion/normal.diff b/kompare/tests/subversion/normal.diff new file mode 100644 index 00000000..853cc219 --- /dev/null +++ b/kompare/tests/subversion/normal.diff @@ -0,0 +1,6 @@ +Index: NEWS +=================================================================== +1c1 +< +--- +> just a fake modif for kompare tests diff --git a/kompare/tests/subversion/normalm.diff b/kompare/tests/subversion/normalm.diff new file mode 100644 index 00000000..f526a3b0 --- /dev/null +++ b/kompare/tests/subversion/normalm.diff @@ -0,0 +1,170 @@ +Index: NEWS +=================================================================== +1c1 +< +--- +> just a fake modif for kompare tests +Index: README +=================================================================== +1,114c1,2 +< Vim KPart +< +< +< by Philippe Fremy +< +< +< Okay, I made it : a Vim KPart! +< +< This means that you can have Vim embedded inside Konqueror, and everywhere a +< text ReadWrite or ReadOnly KPart is requested. Actually, there is almost no +< place right now where this is the case in KDE. KMail uses its own editor, +< KDEvelop uses its own editor, Kate uses some more powerful Kate component. +< +< But this only the beginning. Enabling a part in those programs shouldn't be +< much hassle and you can probably help me do it. My hope is really to get +< KDevelop use Vim. +< +< +< ======= OBSOLETE =========== +< Requirements: +< ------------- +< To make this KPart work, you need a graphicial Vim version 6 with the client-server stuff feature activated and with the vim60-vimpart-patch.diff applied. The patch is in this dir. I hope to get it into the main Vim tree. KVim has already the patch but is slightly less stable that the original GVim. A big advantage of KVim is that you get the native KDE dialogs when vim asks a question. +< +< +< ======= OBSOLETE =========== +< Installation: +< ------------- +< To make your vimpart work, you'll have to go into the vimpart directory and run "testVim your_patched_vim". If the test does work, a file goodVim will be created. You will be able to install and use the component. Else, the test will report why it fails (features missing in vim). +< +< +< +< ======= OBSOLETE =========== +< Testing: +< -------- +< If you want to see your component without installing it, you can do the +< following: +< +< 1. configure, build. Then go into the Vimpart subdirectory. +< +< 2. Include the current Vimpart directory in your KDEDIRS: +< export KDEDIRS=`pwd`:$KDEDIR +< +< 3. Symlink .libs to lib +< ln -s .libs lib +< +< 4. Create pseudo share/services dir: +< mkdir share; mkdir share/services; +< +< 5. Symlink to Vimpart.desktop: +< ln -s Vimpart.desktop share/services/Vimpart.desktop +< +< 6. Create a pseudo share/config dir +< mkdir share/config; +< +< 7. Symlink to vimwidgetrc +< ln -s vimwidgetrc share/config/vimwidgetrc +< +< 8. Update the desktop mimetype database: +< kbuildsycoca +< +< To test it, run VimPartShell. Or run konqueror from this dir and click on a +< text file. +< +< +< ======= OBSOLETE =========== +< Remarks: +< -------- +< The initial preference of the Vim KPart is 10. Kate uses 8, so if you install the part, it will override Kate for all the mimetypes. You can always change that by manually editing the initial preference in the desktop file or by simply selecting which editor you prefer for which mimetype in the control center. +< +< If you find some mimetype not handled by the Vim KPart although they should be, send me a patch! +< +< +< +< How it works: +< ------------- +< At the beginning, we started to write KVim, a port of GVim to KDE to make +< it possible to embed Vim inside KDE. But with the latest version of Vim, it +< turns out that it is not necessary to have a native Vim. +< +< +< I use QXembed, a widget which can embed any X application if it knows its X Window Id, using some X feature. The patch I provide will make vim displays its window id on stdout when the window is mapped. GVim 6.0 then provides a way to send commands to a Vim window from another process. If you look at the VimWidget source, you will see that 70% of the code is there to handle the communication process. The rest uses the communication channel to send the vim commands needed by kpart and ktexteditor. +< +< As far as I can tell, the part is race-condition free. If you issue many sendNormalCmd and many evalExpr, they are guaranted to be executed sequentially. This has caused me enough problems when it wasn't the case! +< +< +< Qt, KDE2 and KDE3: +< ------------------ +< The VimWidget itself depends very litle on KDE. It is quite easy to port remove the KDE specific stuff, to use it in a Qt only program. In fact, at the beginning, it was only Qt-based. +< +< +< +< ======= OBSOLETE =========== +< Features & TODO: +< ---------------- +< I think most basic features required by an editor widget or part are supported. There are some possible improvement but I would like more feedback to know what really needs to be done. So don't hesitate to write me about your feelings using this. +< +< My TODO list is: +< - restore the editing mode after sendCmd +< - implement KTextEditor interface +< - add some useful actions to the part (like search, ...) +< +< +< +< Feedback: +< --------- +< For the Vim KPart : pfremy@kde.com +< For KVim: pfremy@kde.org, mikmak@freenux.org, orzel@kde.org +< +< +< +< +< +< +< +--- +> Yes, that's really a Vim Komponent :) +> Yes, you can have Vim inside KDE apps, you guessed it :) +115a4,43 +> So, it's designed for KDE 3.x (if someone wants to port it to KDE 2 that +> should be easy), it uses GVim or KVim (even Motif Vim works) 6.x. +> It can be used in different apps : +> - KDevelop (version 3) +> - Konqueror (as a file viewer) +> - KWrite +> - KMail (coming in KDE 3.2) +> - Kompare, KBabel ........ ;) +> +> CONFIGURATION +> ============= +> once you compiled and installed it as any other app, +> start your KDE Control Center, go to the file manager section +> and open the Vim Component configuration module. +> Here, you have to select a Vim executable which may be found on +> your computer (generally /usr/bin/vim) will do it fine for most +> linux distributions. All you need is that it's a GUI-enabled Vim 6.0 or +> better. +> Push the test button, if that's okay then that's should be enough to start +> using it :) +> +> FUNCTIONMENT +> ============ +> Philippe Fremy (pfremy@kde.com) wrote the initial version of this kpart. +> The concept is to start a normal GUI Vim (like gvim), then "embeds" the Vim +> window into a KDE Widget. +> It used to be based on the ClientServer feature of Vim (type :help +> clientserver in Vim for more info) using external processus to control the +> embedded Vim. That was running quite fine, but was slow :/ +> We changed that :) +> Now we communicate directly from the kpart to the embedded Vim thanks to X11 +> without using externals processus. That's much faster and reliable ;) +> KVim has also another remote control system using KDE's DCOP communication +> backend. +> Currently I would advice people to use DCOP when running KVim and using X11 +> communication with GVim (DCOP won't work with GVim anyway). +> There may be some differences in speed, though I have not noticed it here. +> The most important difference is that DCOP provides a signal system and that can +> make a difference to improve the interaction between KVim and the hosting +> application (KDevelop for example). But it's not yet used. +116a45,46 +> Hope you'll enjoy Vim inside KDE :) +> Mickael "Mikmak" Marchand (marchand@kde.org) diff --git a/kompare/tests/subversion/rcs.diff b/kompare/tests/subversion/rcs.diff new file mode 100644 index 00000000..1633c3a3 --- /dev/null +++ b/kompare/tests/subversion/rcs.diff @@ -0,0 +1,5 @@ +Index: NEWS +=================================================================== +d1 1 +a1 1 +just a fake modif for kompare tests diff --git a/kompare/tests/subversion/rcsm.diff b/kompare/tests/subversion/rcsm.diff new file mode 100644 index 00000000..a409cd54 --- /dev/null +++ b/kompare/tests/subversion/rcsm.diff @@ -0,0 +1,55 @@ +Index: NEWS +=================================================================== +d1 1 +a1 1 +just a fake modif for kompare tests +Index: README +=================================================================== +d1 114 +a114 2 +Yes, that's really a Vim Komponent :) +Yes, you can have Vim inside KDE apps, you guessed it :) +a115 40 +So, it's designed for KDE 3.x (if someone wants to port it to KDE 2 that +should be easy), it uses GVim or KVim (even Motif Vim works) 6.x. +It can be used in different apps : + - KDevelop (version 3) + - Konqueror (as a file viewer) + - KWrite + - KMail (coming in KDE 3.2) + - Kompare, KBabel ........ ;) + +CONFIGURATION +============= +once you compiled and installed it as any other app, +start your KDE Control Center, go to the file manager section +and open the Vim Component configuration module. +Here, you have to select a Vim executable which may be found on +your computer (generally /usr/bin/vim) will do it fine for most +linux distributions. All you need is that it's a GUI-enabled Vim 6.0 or +better. +Push the test button, if that's okay then that's should be enough to start +using it :) + +FUNCTIONMENT +============ +Philippe Fremy (pfremy@kde.com) wrote the initial version of this kpart. +The concept is to start a normal GUI Vim (like gvim), then "embeds" the Vim +window into a KDE Widget. +It used to be based on the ClientServer feature of Vim (type :help +clientserver in Vim for more info) using external processus to control the +embedded Vim. That was running quite fine, but was slow :/ +We changed that :) +Now we communicate directly from the kpart to the embedded Vim thanks to X11 +without using externals processus. That's much faster and reliable ;) +KVim has also another remote control system using KDE's DCOP communication +backend. +Currently I would advice people to use DCOP when running KVim and using X11 +communication with GVim (DCOP won't work with GVim anyway). +There may be some differences in speed, though I have not noticed it here. +The most important difference is that DCOP provides a signal system and that can +make a difference to improve the interaction between KVim and the hosting +application (KDevelop for example). But it's not yet used. +a116 2 +Hope you'll enjoy Vim inside KDE :) +Mickael "Mikmak" Marchand (marchand@kde.org) diff --git a/kompare/tests/subversion/unified.diff b/kompare/tests/subversion/unified.diff new file mode 100644 index 00000000..fca49ace --- /dev/null +++ b/kompare/tests/subversion/unified.diff @@ -0,0 +1,7 @@ +Index: NEWS +=================================================================== +--- NEWS ++++ NEWS 2002-09-22 14:34:37.000000000 +0200 +@@ -1 +1 @@ +- ++just a fake modif for kompare tests diff --git a/kompare/tests/subversion/unifiedm.diff b/kompare/tests/subversion/unifiedm.diff new file mode 100644 index 00000000..29a07705 --- /dev/null +++ b/kompare/tests/subversion/unifiedm.diff @@ -0,0 +1,173 @@ +Index: NEWS +=================================================================== +--- NEWS ++++ NEWS 2002-09-22 14:34:37.000000000 +0200 +@@ -1 +1 @@ +- ++just a fake modif for kompare tests +Index: README +=================================================================== +--- README ++++ README 2002-09-13 23:05:48.000000000 +0200 +@@ -1,117 +1,47 @@ +- Vim KPart +- +- +- by Philippe Fremy +- +- +-Okay, I made it : a Vim KPart! +- +-This means that you can have Vim embedded inside Konqueror, and everywhere a +-text ReadWrite or ReadOnly KPart is requested. Actually, there is almost no +-place right now where this is the case in KDE. KMail uses its own editor, +-KDEvelop uses its own editor, Kate uses some more powerful Kate component. +- +-But this only the beginning. Enabling a part in those programs shouldn't be +-much hassle and you can probably help me do it. My hope is really to get +-KDevelop use Vim. +- +- +-======= OBSOLETE =========== +-Requirements: +-------------- +-To make this KPart work, you need a graphicial Vim version 6 with the client-server stuff feature activated and with the vim60-vimpart-patch.diff applied. The patch is in this dir. I hope to get it into the main Vim tree. KVim has already the patch but is slightly less stable that the original GVim. A big advantage of KVim is that you get the native KDE dialogs when vim asks a question. +- +- +-======= OBSOLETE =========== +-Installation: +-------------- +-To make your vimpart work, you'll have to go into the vimpart directory and run "testVim your_patched_vim". If the test does work, a file goodVim will be created. You will be able to install and use the component. Else, the test will report why it fails (features missing in vim). +- +- +- +-======= OBSOLETE =========== +-Testing: +--------- +-If you want to see your component without installing it, you can do the +-following: +- +-1. configure, build. Then go into the Vimpart subdirectory. +- +-2. Include the current Vimpart directory in your KDEDIRS: +-export KDEDIRS=`pwd`:$KDEDIR +- +-3. Symlink .libs to lib +-ln -s .libs lib +- +-4. Create pseudo share/services dir: +-mkdir share; mkdir share/services; +- +-5. Symlink to Vimpart.desktop: +-ln -s Vimpart.desktop share/services/Vimpart.desktop +- +-6. Create a pseudo share/config dir +-mkdir share/config; +- +-7. Symlink to vimwidgetrc +-ln -s vimwidgetrc share/config/vimwidgetrc +- +-8. Update the desktop mimetype database: +-kbuildsycoca +- +-To test it, run VimPartShell. Or run konqueror from this dir and click on a +-text file. +- +- +-======= OBSOLETE =========== +-Remarks: +--------- +-The initial preference of the Vim KPart is 10. Kate uses 8, so if you install the part, it will override Kate for all the mimetypes. You can always change that by manually editing the initial preference in the desktop file or by simply selecting which editor you prefer for which mimetype in the control center. +- +-If you find some mimetype not handled by the Vim KPart although they should be, send me a patch! +- +- +- +-How it works: +-------------- +-At the beginning, we started to write KVim, a port of GVim to KDE to make +-it possible to embed Vim inside KDE. But with the latest version of Vim, it +-turns out that it is not necessary to have a native Vim. +- +- +-I use QXembed, a widget which can embed any X application if it knows its X Window Id, using some X feature. The patch I provide will make vim displays its window id on stdout when the window is mapped. GVim 6.0 then provides a way to send commands to a Vim window from another process. If you look at the VimWidget source, you will see that 70% of the code is there to handle the communication process. The rest uses the communication channel to send the vim commands needed by kpart and ktexteditor. +- +-As far as I can tell, the part is race-condition free. If you issue many sendNormalCmd and many evalExpr, they are guaranted to be executed sequentially. This has caused me enough problems when it wasn't the case! +- +- +-Qt, KDE2 and KDE3: +------------------- +-The VimWidget itself depends very litle on KDE. It is quite easy to port remove the KDE specific stuff, to use it in a Qt only program. In fact, at the beginning, it was only Qt-based. +- +- +- +-======= OBSOLETE =========== +-Features & TODO: +----------------- +-I think most basic features required by an editor widget or part are supported. There are some possible improvement but I would like more feedback to know what really needs to be done. So don't hesitate to write me about your feelings using this. +- +-My TODO list is: +-- restore the editing mode after sendCmd +-- implement KTextEditor interface +-- add some useful actions to the part (like search, ...) +- +- +- +-Feedback: +---------- +-For the Vim KPart : pfremy@kde.com +-For KVim: pfremy@kde.org, mikmak@freenux.org, orzel@kde.org +- +- +- +- +- +- +- ++Yes, that's really a Vim Komponent :) ++Yes, you can have Vim inside KDE apps, you guessed it :) + ++So, it's designed for KDE 3.x (if someone wants to port it to KDE 2 that ++should be easy), it uses GVim or KVim (even Motif Vim works) 6.x. ++It can be used in different apps : ++ - KDevelop (version 3) ++ - Konqueror (as a file viewer) ++ - KWrite ++ - KMail (coming in KDE 3.2) ++ - Kompare, KBabel ........ ;) ++ ++CONFIGURATION ++============= ++once you compiled and installed it as any other app, ++start your KDE Control Center, go to the file manager section ++and open the Vim Component configuration module. ++Here, you have to select a Vim executable which may be found on ++your computer (generally /usr/bin/vim) will do it fine for most ++linux distributions. All you need is that it's a GUI-enabled Vim 6.0 or ++better. ++Push the test button, if that's okay then that's should be enough to start ++using it :) ++ ++FUNCTIONMENT ++============ ++Philippe Fremy (pfremy@kde.com) wrote the initial version of this kpart. ++The concept is to start a normal GUI Vim (like gvim), then "embeds" the Vim ++window into a KDE Widget. ++It used to be based on the ClientServer feature of Vim (type :help ++clientserver in Vim for more info) using external processus to control the ++embedded Vim. That was running quite fine, but was slow :/ ++We changed that :) ++Now we communicate directly from the kpart to the embedded Vim thanks to X11 ++without using externals processus. That's much faster and reliable ;) ++KVim has also another remote control system using KDE's DCOP communication ++backend. ++Currently I would advice people to use DCOP when running KVim and using X11 ++communication with GVim (DCOP won't work with GVim anyway). ++There may be some differences in speed, though I have not noticed it here. ++The most important difference is that DCOP provides a signal system and that can ++make a difference to improve the interaction between KVim and the hosting ++application (KDevelop for example). But it's not yet used. + ++Hope you'll enjoy Vim inside KDE :) ++Mickael "Mikmak" Marchand (marchand@kde.org) + diff --git a/kprofilemethod/Makefile.am b/kprofilemethod/Makefile.am new file mode 100644 index 00000000..0d2aa3f6 --- /dev/null +++ b/kprofilemethod/Makefile.am @@ -0,0 +1,2 @@ +include_HEADERS = kprofilemethod.h + diff --git a/kprofilemethod/README b/kprofilemethod/README new file mode 100644 index 00000000..543e841c --- /dev/null +++ b/kprofilemethod/README @@ -0,0 +1,21 @@ +As the docu in kprofilemethod.h says: + + Those macros help profiling using QTime. + They allow to sum up the time taken by a given bit of code + in a method called several times. + This way one can find out which low-level method used by a high-level + method is taking most of its time. + +WARNING: + + Please do not commit code that uses kprofilemethod.h + Since not everyone has kdesdk installed, it won't build for everyone. + This is a tool to be used for a one-time profiling, and then you need + to remove all its traces before committing. + +TODO: + + KDevelop, XEmacs and vi shortcuts to insert begin/end macros around a block + of code (e.g. after selecting it) - this macro would also insert the + int variable, and another shortcut for inserting the print macro. + diff --git a/kprofilemethod/kprofilemethod.h b/kprofilemethod/kprofilemethod.h new file mode 100644 index 00000000..fc5ecbe1 --- /dev/null +++ b/kprofilemethod/kprofilemethod.h @@ -0,0 +1,51 @@ +/* This file is part of the KDE project + Copyright (C) 2002 David Faure + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef KPROFILE_METHOD_H +#define KPROFILE_METHOD_H + +#include +#include + +/** + * Those macros help profiling using QTime. + * They allow to sum up the time taken by a given bit of code + * in a method called several times. + * This way one can find out which low-level method used by a high-level + * method is taking most of its time + * + * + * Declare the var somewhere, out of any method: + * int pr_theMethod = 0; + * (the name pr_* helps finding and removing all of this before committing) + * + * Then in the method, around the code to be timed: + * PROFILE_METHOD_BEGIN( pr_theMethod ); + * ... + * PROFILE_METHOD_END( pr_theMethod ); + * + * And finally, to see the result, put this call in a method + * called after all that (destructor, program exit...) + * PROFILE_METHOD_PRINT( pr_theMethod, "theMethod" ); + * + */ +#define PROFILE_METHOD_BEGIN(sym) extern int sym; QTime profile_dt##sym; profile_dt##sym.start(); +#define PROFILE_METHOD_END(sym) extern int sym; sym += profile_dt##sym.elapsed(); +#define PROFILE_METHOD_PRINT(sym, name) extern int sym; kdDebug() << name << " took " << sym << " milliseconds" << endl; + +#endif // KPROFILE_METHOD_H diff --git a/kspy/Makefile.am b/kspy/Makefile.am new file mode 100644 index 00000000..37af3c20 --- /dev/null +++ b/kspy/Makefile.am @@ -0,0 +1,30 @@ + +lib_LTLIBRARIES = libkspy.la +libkspy_la_SOURCES = navviewitem.cpp propsview.cpp navview.cpp spy.cpp sigslotview.cpp \ + receiversview.cpp classinfoview.cpp + +include_HEADERS = kspy.h + +noinst_HEADERS = spy.h navview.h propsview.h navviewitem.h receiversview.h classinfoview.h + +libkspy_la_LIBADD = $(LIB_QT) $(LIB_KDECORE) $(LIB_KDEUI) $(LIBSOCKET) + +# set the include path for X, qt and KDE +INCLUDES= $(all_includes) + +libkspy_la_LDFLAGS = $(all_libraries) -version-info 3:0:2 -no-undefined $(USER_LDFLAGS) + +EXTRA_DIST = main.cpp spy.cpp spy.h navview.cpp navview.h propsview.cpp \ + propsview.h navviewitem.cpp navviewitem.h sigslotview.h receiversview.h classinfoview.h + +METASOURCES = AUTO + +messages: + LIST=`find . -name \*.h -o -name \*.hh -o -name \*.H -o -name \*.hxx -o -name \*.hpp -o -name \*.cpp -o -name \*.cc -o -name \*.cxx -o -name \*.ecpp -o -name \*.C`; \ + if test -n "$$LIST"; then \ + $(XGETTEXT) $$LIST -o $(podir)/spy.pot; \ + fi + +check_PROGRAMS = testkspy +testkspy_SOURCES = main.cpp +testkspy_LDADD = libkspy.la diff --git a/kspy/README b/kspy/README new file mode 100644 index 00000000..ad810112 --- /dev/null +++ b/kspy/README @@ -0,0 +1,12 @@ +KSpy +==== + +KSpy is a utility intended to help developers examine the internal +state of a Qt/KDE application. KSpy graphically displays all the +QObjects in use, and allows you to browse their properties. Using KSpy +is very simple, include kspy.h and call KSpy::invoke() when you want +to looks inside your app. The KSpy function is inline and the main +part of KSpy is dynamically loaded, so you may even want to leave this +in the release build of an application. + +Richard Moore, diff --git a/kspy/classinfoview.cpp b/kspy/classinfoview.cpp new file mode 100644 index 00000000..1b976cfa --- /dev/null +++ b/kspy/classinfoview.cpp @@ -0,0 +1,58 @@ +/*************************************************************************** + classinfoview.cpp - description + ------------------- + begin : Tue Jan 11 2005 + copyright : (C) 2005 by Richard Dale + email : Richard_Dale@tipitina.demon.co.uk + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include + +#include + +#include "classinfoview.h" + +ClassInfoView::ClassInfoView(QWidget *parent, const char *name ) : KListView(parent,name) +{ + addColumn( i18n( "Name" ) ); + addColumn( i18n( "Value" ) ); + + setRootIsDecorated( true ); + setAllColumnsShowFocus( true ); + setFullWidth( true ); +} + +ClassInfoView::~ClassInfoView() +{ +} + +void ClassInfoView::buildList( QObject *o ) +{ + QMetaObject *mo = o->metaObject(); + + for (int index = 0; index < mo->numClassInfo(true); index++) { + const QClassInfo * classInfo = mo->classInfo(index, true); + new KListViewItem( this, classInfo->name, classInfo->value ); + } +} + +void ClassInfoView::setTarget( QObject *o ) +{ + clear(); + buildList( o ); +} + +#include "classinfoview.moc" diff --git a/kspy/classinfoview.h b/kspy/classinfoview.h new file mode 100644 index 00000000..e8b4440b --- /dev/null +++ b/kspy/classinfoview.h @@ -0,0 +1,40 @@ +/*************************************************************************** + classinfoview.h - description + ------------------- + begin : Tue Jan 11 2005 + copyright : (C) 2005 by Richard Dale + email : Richard_Dale@tipitina.demon.co.uk + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef CLASSINFOVIEW_H +#define CLASSINFOVIEW_H + +#include +#include + +/** + *@author Richard Dale + */ + +class ClassInfoView : public KListView { + Q_OBJECT +public: + ClassInfoView(QWidget *parent=0, const char *name=0); + ~ClassInfoView(); + + void buildList( QObject *o ); + +public slots: + void setTarget( QObject * ); +}; + +#endif diff --git a/kspy/kspy.h b/kspy/kspy.h new file mode 100644 index 00000000..e2878125 --- /dev/null +++ b/kspy/kspy.h @@ -0,0 +1,44 @@ +// -*- c++ -*- + +#ifndef KSPY_H +#define KSPY_H + +#include + +/** + * Loader for the QObject debugging tool. The usage is simple, just call + * KSpy::invoke(), then use the spy window to examine the state of your + * QObjects. + * + * @author Richard Moore, rich@kde.org + * @version $Id$ + */ +class KSpy +{ +public: + /** + * Loads and invokes the KSpy utility. + */ + static void invoke() { + KLibLoader *loader = KLibLoader::self(); + KLibrary *lib = loader->library( "libkspy" ); + + if ( !lib ) { + qWarning( "Unable to load KSpy library\n" ); + return; + } + + lib->factory(); // Ensure the factory is loaded + + // We don't need to do any more, KSpy is fired up by the loader hook + // in the shared library. + } + +private: + // Prevent instantiation. + KSpy() {} + ~KSpy() {} +}; + +#endif // KSPY_H + diff --git a/kspy/main.cpp b/kspy/main.cpp new file mode 100644 index 00000000..ab8a3594 --- /dev/null +++ b/kspy/main.cpp @@ -0,0 +1,52 @@ +/*************************************************************************** + main.cpp - description + ------------------- + begin : Tue May 1 02:59:33 BST 2001 + copyright : (C) 2001 by Richard Moore + email : rich@kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include + +#include "spy.h" + +static const char description[] = + I18N_NOOP("Spy"); +// INSERT A DESCRIPTION FOR YOUR APPLICATION HERE + + +static KCmdLineOptions options[] = +{ + KCmdLineLastOption + // INSERT YOUR COMMANDLINE OPTIONS HERE +}; + +int main(int argc, char *argv[]) +{ + + KAboutData aboutData( "spy", I18N_NOOP("Spy"), + VERSION, description, KAboutData::License_GPL, + "(c) 2001, Richard Moore", 0, 0, "rich@kde.org"); + aboutData.addAuthor("Richard Moore",0, "rich@kde.org"); + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions( options ); // Add our own options. + + KApplication a; + Spy *spy = new Spy(); + a.setMainWidget(spy); + + spy->show(); + + return a.exec(); +} diff --git a/kspy/navview.cpp b/kspy/navview.cpp new file mode 100644 index 00000000..8f6afc8e --- /dev/null +++ b/kspy/navview.cpp @@ -0,0 +1,88 @@ +/*************************************************************************** + navview.cpp - description + ------------------- + begin : Tue May 1 2001 + copyright : (C) 2001 by Richard Moore + email : rich@kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include + +#include + +#include "navview.h" +#include "navviewitem.h" + +NavView::NavView(QWidget *parent, const char *name ) : KListView(parent,name) +{ + addColumn( i18n( "Name" ) ); + addColumn( i18n( "Type" ) ); + + setRootIsDecorated( true ); + setAllColumnsShowFocus( true ); + setFullWidth( true ); + + connect( this, SIGNAL( selectionChanged( QListViewItem * ) ), + this, SLOT( selectItem( QListViewItem * ) ) ); +} + +NavView::~NavView(){ +} + +void NavView::buildTree() +{ + const QObjectList *roots = QObject::objectTrees(); + QObjectListIt it( *roots ); + + QObject * obj; + while ( (obj = it.current()) != 0 ) { + ++it; + NavViewItem *item = new NavViewItem( this, obj ); + createSubTree( item ); + } +} + +void NavView::expandVisibleTree() +{ + QListViewItemIterator it( this, QListViewItemIterator::Visible | + QListViewItemIterator::Expandable ); + + while ( it.current() ) { + setOpen( *it, true ); + ++it; + } +} + +void NavView::selectItem( QListViewItem *item ) +{ + NavViewItem *navItem = static_cast( item ); + + emit selected( navItem->data ); +} + +void NavView::createSubTree( NavViewItem *parent ) +{ + const QObjectList *kids = parent->data->children(); + if ( !kids ) + return; + + QObject * obj; + QObjectListIt it( *kids ); + while ( (obj=it.current()) != 0 ) { + ++it; + NavViewItem *item = new NavViewItem( parent, obj ); + createSubTree( item ); + } +} + +#include "navview.moc" diff --git a/kspy/navview.h b/kspy/navview.h new file mode 100644 index 00000000..326f7790 --- /dev/null +++ b/kspy/navview.h @@ -0,0 +1,57 @@ +/*************************************************************************** + navview.h - description + ------------------- + begin : Tue May 1 2001 + copyright : (C) 2001 by Richard Moore + email : rich@kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef NAVVIEW_H +#define NAVVIEW_H + +#include +#include + +class NavViewItem; + +/** + * @author Richard Moore + */ +class NavView : public KListView +{ + Q_OBJECT + + public: + NavView( QWidget *parent = 0, const char *name = 0 ); + ~NavView(); + + /** + Builds the complete tree. + */ + void buildTree(); + + /** + Expands all currently visible items. + */ + void expandVisibleTree(); + + signals: + void selected( QObject *object ); + + protected slots: + void selectItem( QListViewItem *item ); + + private: + void createSubTree( NavViewItem* ); +}; + +#endif diff --git a/kspy/navviewitem.cpp b/kspy/navviewitem.cpp new file mode 100644 index 00000000..de372b4f --- /dev/null +++ b/kspy/navviewitem.cpp @@ -0,0 +1,40 @@ +/*************************************************************************** + navviewitem.cpp - description + ------------------- + begin : Tue May 1 2001 + copyright : (C) 2001 by Richard Moore + email : rich@kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include + +#include "navview.h" +#include "navviewitem.h" + +NavViewItem::NavViewItem(NavView *parent, QObject *obj ) + : KListViewItem(parent, obj->name(), obj->className() ) +{ + data = obj; + setExpandable( true ); +} + +NavViewItem::NavViewItem(NavViewItem *parent, QObject *obj ) + : KListViewItem(parent, obj->name(), obj->className() ) +{ + data = obj; + setExpandable( true ); +} + +NavViewItem::~NavViewItem() +{ +} + diff --git a/kspy/navviewitem.h b/kspy/navviewitem.h new file mode 100644 index 00000000..35a66c07 --- /dev/null +++ b/kspy/navviewitem.h @@ -0,0 +1,38 @@ +/*************************************************************************** + navviewitem.h - description + ------------------- + begin : Tue May 1 2001 + copyright : (C) 2001 by Richard Moore + email : rich@kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef NAVVIEWITEM_H +#define NAVVIEWITEM_H + +#include + +class NavView; + +/** + *@author Richard Moore + */ + +class NavViewItem : public KListViewItem { +public: + NavViewItem(NavView *parent, QObject *item ); + NavViewItem(NavViewItem *parent, QObject *item ); + ~NavViewItem(); + + QObject *data; +}; + +#endif diff --git a/kspy/propsview.cpp b/kspy/propsview.cpp new file mode 100644 index 00000000..12b855c0 --- /dev/null +++ b/kspy/propsview.cpp @@ -0,0 +1,196 @@ +/*************************************************************************** + propsview.cpp - description + ------------------- + begin : Tue May 1 2001 + copyright : (C) 2001 by Richard Moore + email : rich@kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "propsview.h" + +class KSpyItem : KListViewItem +{ +public: + KSpyItem( QListView * parent, QString label1, QString label2 = QString::null, QString label3 = QString::null, QString label4 = QString::null, QString label5 = QString::null, QString label6 = QString::null ) + : KListViewItem(parent, label1, label2, label3, label4, label5, label6) + { + } +protected: + void paintCell( QPainter * p, const QColorGroup & cg, + int column, int width, int alignment ) + { + if (column == 1 && text(2) == "QColor") { + QColorGroup color_cg( cg.foreground(), cg.background(), + cg.light(), cg.dark(), cg.mid(), + QColor(text(1)), QColor(text(1)) ); + QListViewItem::paintCell(p, color_cg, column, width, alignment); + } else { + KListViewItem::paintCell(p, cg, column, width, alignment); + } + } +}; + +PropsView::PropsView(QWidget *parent, const char *name ) : KListView(parent,name) +{ + addColumn( i18n( "Name" ) ); + addColumn( i18n( "Value" ) ); + addColumn( i18n( "Type" ) ); + addColumn( i18n( "Access" ) ); + addColumn( i18n( "Designable" ) ); + addColumn( i18n( "Type Flags" ) ); + + setAllColumnsShowFocus( true ); + setColumnAlignment( 3, AlignCenter ); + setColumnAlignment( 4, AlignCenter ); + setFullWidth( true ); +} + +PropsView::~PropsView() +{ +} + +void PropsView::buildList( QObject *o ) +{ + QMetaObject *mo = o->metaObject(); + QStrList names = mo->propertyNames( true ); + + for ( uint i = 0; i < names.count(); i++ ) { + char *prop = names.at( i ); + QVariant v = o->property( prop ); + const QMetaProperty *mp = mo->property( mo->findProperty(prop, true), true ); + + QString val( "????" ); + switch( v.type() ) { + case QVariant::String: + case QVariant::CString: + val = v.toString(); + break; + case QVariant::Bool: + val = ( v.toBool() ? "True" : "False" ); + break; + case QVariant::Color: + { + QColor c = v.toColor(); + val = c.name(); + break; + } + case QVariant::Cursor: + { + QCursor c = v.toCursor(); + val = QString("shape=%1").arg(c.shape()); + break; + } + case QVariant::Font: + { + QFont f = v.toFont(); + val = QString("family=%1, pointSize=%2, weight=%3, italic=%4, bold=%5, underline=%6, strikeOut=%7") + .arg(f.family()) + .arg(f.pointSize()) + .arg(f.weight()) + .arg(f.italic()) + .arg(f.bold()) + .arg(f.underline()) + .arg(f.strikeOut()); + break; + } + case QVariant::Int: + val.setNum( v.toInt() ); + if (mp->isEnumType()) { + QMetaObject * metaObject = *(mp->meta); + val = QString("%1::%2").arg(metaObject->className()).arg(mp->valueToKey(val.toInt())); + } + break; + case QVariant::Point: + { + QPoint p = v.toPoint(); + val = QString("x=%1, y=%2").arg(p.x()).arg(p.y()); + break; + } + case QVariant::Rect: + { + QRect r = v.toRect(); + val = QString("left=%1, right=%2, top=%3, bottom=%4") + .arg(r.left()) + .arg(r.right()) + .arg(r.top()) + .arg(r.bottom()); + break; + } + case QVariant::Size: + { + QSize s = v.toSize(); + val = QString("width=%1, height=%2").arg(s.width()).arg(s.height()); + break; + } + case QVariant::SizePolicy: + { + QSizePolicy s = v.toSizePolicy(); + val = QString("horData=%1, verData=%2").arg(s.horData()).arg(s.verData()); + break; + } + case QVariant::Double: + val.setNum( v.toDouble() ); + break; + default: + break; + } + + QString ro("R/O"); + QString rw("R/W"); + QString st("Set"); + QString et("Enum"); + QString yes("Yes"); + QString no("No"); + + QString writable = ( mp->writable() ? rw : ro ); + QString setType = ( mp->isSetType() ? st : QString::null ); + QString enumType = ( mp->isEnumType() ? et : QString::null ); + QString designable = ( mp->designable(o) ? yes : no ); + + QString flags; + bool first = true; + if ( !setType.isNull() ) { + if ( first ) + first = false; + else + flags += " | "; + + flags += setType; + } + if ( !enumType.isNull() ) { + if ( first ) + first = false; + else + flags += " | "; + + flags += enumType; + } + + new KSpyItem( this, prop, val, v.typeName(), writable, designable, flags ); + } +} + +void PropsView::setTarget( QObject *o ) +{ + clear(); + buildList( o ); +} +#include "propsview.moc" diff --git a/kspy/propsview.h b/kspy/propsview.h new file mode 100644 index 00000000..62eb5e8d --- /dev/null +++ b/kspy/propsview.h @@ -0,0 +1,40 @@ +/*************************************************************************** + propsview.h - description + ------------------- + begin : Tue May 1 2001 + copyright : (C) 2001 by Richard Moore + email : rich@kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef PROPSVIEW_H +#define PROPSVIEW_H + +#include +#include + +/** + *@author Richard Moore + */ + +class PropsView : public KListView { + Q_OBJECT +public: + PropsView(QWidget *parent=0, const char *name=0); + ~PropsView(); + + void buildList( QObject *o ); + +public slots: + void setTarget( QObject * ); +}; + +#endif diff --git a/kspy/receiversview.cpp b/kspy/receiversview.cpp new file mode 100644 index 00000000..e1da1127 --- /dev/null +++ b/kspy/receiversview.cpp @@ -0,0 +1,80 @@ +/*************************************************************************** + receiversview.cpp - description + ------------------- + begin : Tue Jan 11 2005 + copyright : (C) 2005 by Richard Dale + email : Richard_Dale@tipitina.demon.co.uk + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "receiversview.h" + +class UnencapsulatedQObject : public QObject { +public: + QConnectionList *public_receivers(int signal) const { return receivers(signal); } +}; + +ReceiversView::ReceiversView(QWidget *parent, const char *name ) : KListView(parent,name) +{ + addColumn( i18n( "Object" ) ); + addColumn( i18n( "Type" ) ); + addColumn( i18n( "Member Name" ) ); + + setRootIsDecorated( true ); + setAllColumnsShowFocus( true ); + setFullWidth( true ); +} + +ReceiversView::~ReceiversView() +{ +} + +void ReceiversView::buildList( QObject *o ) +{ + QMetaObject *mo = o->metaObject(); + + UnencapsulatedQObject * qobject = (UnencapsulatedQObject *) o; + QStrList signalNames = mo->signalNames(true); + + for (int sig = 0; sig < mo->numSignals(true); sig++) { + QConnectionList * clist = qobject->public_receivers(sig); + if (clist != 0) { + KListViewItem *conn = new KListViewItem( this, signalNames.at(sig) ); + + for ( QConnection * connection = clist->first(); + connection != 0; + connection = clist->next() ) + { + new KListViewItem( conn, + connection->object()->name(), + connection->memberType() == 1 ? "SLOT" : "SIGNAL", + connection->memberName() ); + } + } + } +} + +void ReceiversView::setTarget( QObject *o ) +{ + clear(); + buildList( o ); +} + +#include "receiversview.moc" diff --git a/kspy/receiversview.h b/kspy/receiversview.h new file mode 100644 index 00000000..31768883 --- /dev/null +++ b/kspy/receiversview.h @@ -0,0 +1,40 @@ +/*************************************************************************** + receiversview.h - description + ------------------- + begin : Tue Jan 11 2005 + copyright : (C) 2005 by Richard Dale + email : Richard_Dale@tipitina.demon.co.uk + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef RECEIVERSVIEW_H +#define RECEIVERSVIEW_H + +#include +#include + +/** + *@author Richard Dale + */ + +class ReceiversView : public KListView { + Q_OBJECT +public: + ReceiversView(QWidget *parent=0, const char *name=0); + ~ReceiversView(); + + void buildList( QObject *o ); + +public slots: + void setTarget( QObject * ); +}; + +#endif diff --git a/kspy/sigslotview.cpp b/kspy/sigslotview.cpp new file mode 100644 index 00000000..2e04cfa7 --- /dev/null +++ b/kspy/sigslotview.cpp @@ -0,0 +1,73 @@ +/*************************************************************************** + sigslotview.cpp - description + ------------------- + begin : Tue May 1 2001 + copyright : (C) 2001 by Richard Moore + email : rich@kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include + +#include + +#include "sigslotview.h" + +SigSlotView::SigSlotView(QWidget *parent, const char *name ) : KListView(parent,name) +{ + addColumn( i18n( "Signals/Slots" ) ); + + setRootIsDecorated( true ); + setAllColumnsShowFocus( true ); + setFullWidth( true ); +} + +SigSlotView::~SigSlotView() +{ +} + +void SigSlotView::buildList( QObject *o ) +{ + QMetaObject *mo = o->metaObject(); + + KListViewItem *sigs = new KListViewItem( this, "Signals" ); + QStrList sigList = mo->signalNames( true ); + QStrListIterator sigIt( sigList ); + char *si; + while ( (si=sigIt.current()) != 0 ) { + ++sigIt; + new KListViewItem( sigs, si /*, "someSignal()"*/ ); + } + + KListViewItem *slts = new KListViewItem( this, "Slots" ); + QStrList sltList = mo->slotNames( true ); + QStrListIterator sltIt( sltList ); + char *sl; + while ( (sl=sltIt.current()) != 0 ) { + ++sltIt; + new KListViewItem( slts, sl/*, "someSlot()"*/ ); + } + + setOpen( sigs, false ); + setOpen( slts, false ); +} + +void SigSlotView::setTarget( QObject *o ) +{ + clear(); + buildList( o ); +} + +#include "sigslotview.moc" diff --git a/kspy/sigslotview.h b/kspy/sigslotview.h new file mode 100644 index 00000000..86138fd1 --- /dev/null +++ b/kspy/sigslotview.h @@ -0,0 +1,40 @@ +/*************************************************************************** + sigslotview.h - description + ------------------- + begin : Tue May 1 2001 + copyright : (C) 2001 by Richard Moore + email : rich@kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef SIGSLOTVIEW_H +#define SIGSLOTVIEW_H + +#include +#include + +/** + *@author Richard Moore + */ + +class SigSlotView : public KListView { + Q_OBJECT +public: + SigSlotView(QWidget *parent=0, const char *name=0); + ~SigSlotView(); + + void buildList( QObject *o ); + +public slots: + void setTarget( QObject * ); +}; + +#endif diff --git a/kspy/spy.cpp b/kspy/spy.cpp new file mode 100644 index 00000000..341f5b62 --- /dev/null +++ b/kspy/spy.cpp @@ -0,0 +1,115 @@ +/*************************************************************************** + spy.cpp - description + ------------------- + begin : Tue May 1 02:59:33 BST 2001 + copyright : (C) 2001 by Richard Moore + email : rich@kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "navview.h" +#include "propsview.h" +#include "sigslotview.h" +#include "receiversview.h" +#include "classinfoview.h" +#include "spy.h" + +extern "C" +{ + KDE_EXPORT void* init_libkspy() + { + qWarning( "KSpy: Initialising...\n" ); + Spy *s = new Spy(); + s->show(); + + return 0; + } +} + +Spy::Spy( QWidget *parent, const char *name ) + : QWidget( parent, name ) +{ + QVBoxLayout *layout = new QVBoxLayout( this, 11, 6 ); + + QSplitter *div = new QSplitter( this ); + layout->addWidget( div ); + + QVBox *leftPane = new QVBox( div ); + + KListViewSearchLine *searchLine = new KListViewSearchLine( leftPane, "search line" ); + mNavView = new NavView( leftPane, "navigation view" ); + searchLine->setListView( mNavView ); + + KTabWidget *tabs = new KTabWidget( div ); + + mPropsView = new PropsView( tabs, "properties view" ); + tabs->addTab( mPropsView, i18n( "Properties" ) ); + + mSigSlotView = new SigSlotView( tabs, "signals and slots view" ); + tabs->addTab( mSigSlotView, i18n( "Signals && Slots" ) ); + + mReceiversView = new ReceiversView( tabs, "receivers view" ); + tabs->addTab( mReceiversView, i18n( "Receivers" ) ); + + mClassInfoView = new ClassInfoView( tabs, "class info view" ); + tabs->addTab( mClassInfoView, i18n( "Class Info" ) ); + + mNavView->buildTree(); + + connect( mNavView, SIGNAL( selected( QObject * ) ), + mPropsView, SLOT( setTarget( QObject * ) ) ); + connect( mNavView, SIGNAL( selected( QObject * ) ), + mSigSlotView, SLOT( setTarget( QObject * ) ) ); + connect( mNavView, SIGNAL( selected( QObject * ) ), + mReceiversView, SLOT( setTarget( QObject * ) ) ); + connect( mNavView, SIGNAL( selected( QObject * ) ), + mClassInfoView, SLOT( setTarget( QObject * ) ) ); +} + +Spy::~Spy() +{ +} + + +void Spy::setTarget( QWidget *target ) +{ + mTarget = target; + mPropsView->buildList( mTarget ); + mSigSlotView->buildList( mTarget ); + mReceiversView->buildList( mTarget ); + mClassInfoView->buildList( mTarget ); +} + +void Spy::keyPressEvent( QKeyEvent *event ) +{ + if ( event->key() == Qt::Key_Up ) { + event->accept(); + QApplication::postEvent( mNavView, new QKeyEvent( QEvent::KeyPress, Qt::Key_Up, 0, 0 ) ); + } else if ( event->key() == Qt::Key_Down ) { + event->accept(); + QApplication::postEvent( mNavView, new QKeyEvent( QEvent::KeyPress, Qt::Key_Down, 0, 0 ) ); + } else if ( event->key() == Qt::Key_Return ) { + event->accept(); + mNavView->expandVisibleTree(); + } +} + +#include "spy.moc" diff --git a/kspy/spy.h b/kspy/spy.h new file mode 100644 index 00000000..61d80162 --- /dev/null +++ b/kspy/spy.h @@ -0,0 +1,59 @@ +/*************************************************************************** + spy.h - description + ------------------- + begin : Tue May 1 02:59:33 BST 2001 + copyright : (C) 2001 by Richard Moore + email : rich@kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef SPY_H +#define SPY_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +class NavView; +class PropsView; +class SigSlotView; +class ReceiversView; +class ClassInfoView; + +/** + Spy is the main window class of the project. + */ +class Spy : public QWidget +{ + Q_OBJECT + + public: + Spy( QWidget *parent = 0, const char *name = 0 ); + ~Spy(); + + void setTarget( QWidget *target ); + + protected: + virtual void keyPressEvent( QKeyEvent* ); + + private: + QWidget *mTarget; + PropsView *mPropsView; + SigSlotView *mSigSlotView; + ReceiversView *mReceiversView; + ClassInfoView *mClassInfoView; + NavView *mNavView; +}; + +#endif diff --git a/kstartperf/Makefile.am b/kstartperf/Makefile.am new file mode 100644 index 00000000..9214d389 --- /dev/null +++ b/kstartperf/Makefile.am @@ -0,0 +1,14 @@ +INCLUDES = $(all_includes) + +lib_LTLIBRARIES = libkstartperf.la +libkstartperf_la_LDFLAGS = $(all_libraries) -version-info 1:0 -no-undefined +# libkstartperf_la_LIBADD = ../libltdl/libltdlc.la +libkstartperf_la_SOURCES = libkstartperf.c + +bin_PROGRAMS = kstartperf +kstartperf_LDFLAGS = $(all_libraries) +kstartperf_LDADD = $(LIB_KDECORE) +kstartperf_SOURCES = kstartperf.cpp + +messages: + $(XGETTEXT) $(kstartperf_SOURCES) -o $(podir)/kstartperf.pot diff --git a/kstartperf/README b/kstartperf/README new file mode 100644 index 00000000..ff986444 --- /dev/null +++ b/kstartperf/README @@ -0,0 +1,31 @@ + +** kstartperf: startup time measurement for KDE apps ** + +** Usage: + +kstartperf measures startup time for KDE applications. Usage is simple: + + $ kstartperf konsole + +will show you the startup time of konsole in milliseconds. + + +** How does it work? + +1. Kstartperf stores the current time with microsecond resolution in an + environment variable ($KSTARTPERF). +2. Kstartperf executes the requested application, but with a LD_PRELOAD + library that overrides the X11 XMapWindow() function. +3. As soon as the app calls XMapWindow (this is the point where we assume + that the app has "started up"), our function is called instead of the + original XMapWindow(). This function calculates the time difference + between the current time and the time stored in the environment variable + and prints this information to standard error. +4. Our function disables itself and calls the original XMapWindow(). + +** Notes + +The appliation that is being profiled, needs to be linked against kdecore. + +Geert Jansen , +20 July 2000 diff --git a/kstartperf/kstartperf.cpp b/kstartperf/kstartperf.cpp new file mode 100644 index 00000000..3f029b62 --- /dev/null +++ b/kstartperf/kstartperf.cpp @@ -0,0 +1,124 @@ +/* vi: ts=8 sts=4 sw=4 + * + * $Id$ + * + * This file is part of the KDE project, module kstartperf. + * Copyright (C) 2000 Geert Jansen + * + * You can freely redistribute this program under the "Artistic License". + * See the file "LICENSE.readme" for the exact terms. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + + +static KCmdLineOptions options[] = +{ + { "+command", I18N_NOOP("Specifies the command to run"), 0 }, + KCmdLineLastOption +}; + + +QString libkstartperf() +{ + QString lib = QString::null; + QString la_file = locate("lib", "libkstartperf.la"); + + if (la_file.isEmpty()) + return lib; + + // Find the name of the .so file by reading the .la file + QFile la(la_file); + if (la.open(IO_ReadOnly)) + { + QTextStream is(&la); + QString line; + + while (!is.atEnd()) + { + line = is.readLine(); + if (line.left(15) == "library_names='") + { + lib = line.mid(15); + int pos = lib.find(" "); + if (pos > 0) + lib = lib.left(pos); + } + } + + la.close(); + } + + // Look up the actual .so file. + lib = locate("lib", lib); + return lib; +} + + +int main(int argc, char **argv) +{ + KAboutData aboutData("kstartperf", I18N_NOOP("KStartPerf"), + "1.0", I18N_NOOP("Measures start up time of a KDE application"), + KAboutData::License_Artistic, + "Copyright (c) 2000 Geert Jansen and libkmapnotify authors"); + aboutData.addAuthor("Geert Jansen", I18N_NOOP("Maintainer"), + "jansen@kde.org", "http://www.stack.nl/~geertj/"); + + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineArgs::addCmdLineOptions(options); + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + KApplication *app = new KApplication(false, false); + + // Check arguments + + if (args->count() == 0) + { + fprintf(stderr, "No command specified!\n"); + fprintf(stderr, "usage: kstartperf command [arguments]\n"); + exit(1); + } + + // Build command + + char cmd[1024]; + sprintf(cmd, "LD_PRELOAD=%s %s", libkstartperf().latin1(), args->arg(0)); + for (int i=1; icount(); i++) + { + strcat(cmd, " "); + strcat(cmd, args->arg(i)); + } + + // Put the current time in the environment variable `KSTARTPERF' + + struct timeval tv; + if (gettimeofday(&tv, 0L) != 0) + { + perror("gettimeofday()"); + exit(1); + } + char env[100]; + sprintf(env, "KSTARTPERF=%ld:%ld", tv.tv_sec, tv.tv_usec); + putenv(env); + + // And exec() the command + + execl("/bin/sh", "sh", "-c", cmd, (void *)0); + + perror("execl()"); + exit(1); +} diff --git a/kstartperf/libkstartperf.c b/kstartperf/libkstartperf.c new file mode 100644 index 00000000..3c8deae0 --- /dev/null +++ b/kstartperf/libkstartperf.c @@ -0,0 +1,122 @@ +/* vi: ts=8 sts=4 sw=4 + * + * $Id$ + * + * libkstartperf.c: LD_PRELOAD library for startup time measurements. + * + * Copyright (C) 2000 Geert Jansen + * + * Based heavily on kmapnotify.c: + * + * (C) 2000 Rik Hemsley + * (C) 2000 Simon Hausmann + * (C) 2000 Bill Soudan + */ + +#include + +#include +#include +#include +#include + +#include +#include + +#include + + +/* Prototypes */ + +int XMapWindow(Display *, Window); +int XMapRaised(Display *, Window); +void KDE_InterceptXMapRequest(Display *, Window); +void KDE_ShowPerformance(); + +/* Globals */ + +typedef Window (*KDE_XMapRequestSignature)(Display *, Window); +KDE_XMapRequestSignature KDE_RealXMapWindow = 0L; +KDE_XMapRequestSignature KDE_RealXMapRaised = 0L; + + +/* Functions */ + +int XMapWindow(Display * d, Window w) +{ + if (KDE_RealXMapWindow == 0L) + { + KDE_InterceptXMapRequest(d, w); + KDE_ShowPerformance(); + } + return KDE_RealXMapWindow(d, w); +} + +int XMapRaised(Display * d, Window w) +{ + if (KDE_RealXMapRaised == 0L) + { + KDE_InterceptXMapRequest(d, w); + KDE_ShowPerformance(); + } + return KDE_RealXMapRaised(d, w); +} + +void KDE_InterceptXMapRequest(Display * d, Window w) +{ + lt_dlhandle handle; + + handle = lt_dlopen("libX11.so"); + if (handle == 0L) + handle = lt_dlopen("libX11.so.6"); + + if (handle == 0L) + { + fprintf(stderr, "kstartperf: Could not dlopen libX11\n"); + exit(1); + } + + KDE_RealXMapWindow = (KDE_XMapRequestSignature)lt_dlsym(handle, "XMapWindow"); + if (KDE_RealXMapWindow == 0L) + { + fprintf(stderr, "kstartperf: Could not find symbol XMapWindow in libX11\n"); + exit(1); + } + + KDE_RealXMapRaised = (KDE_XMapRequestSignature)lt_dlsym(handle, "XMapRaised"); + if (KDE_RealXMapRaised == 0L) + { + fprintf(stderr, "kstartperf: Could not find symbol XMapRaised in libX11\n"); + exit(1); + } +} + +void KDE_ShowPerformance() +{ + char *env; + long l1, l2; + float dt; + struct timeval tv; + + env = getenv("KSTARTPERF"); + if (env == 0L) + { + fprintf(stderr, "kstartperf: $KSTARTPERF not set!\n"); + exit(1); + } + if (sscanf(env, "%ld:%ld", &l1, &l2) != 2) + { + fprintf(stderr, "kstartperf: $KSTARTPERF illegal format\n"); + exit(1); + } + + if (gettimeofday(&tv, 0L) != 0) + { + fprintf(stderr, "kstartperf: gettimeofday() failed.\n"); + exit(1); + } + + dt = 1e3*(tv.tv_sec - l1) + 1e-3*(tv.tv_usec - l2); + fprintf(stderr, "\nkstartperf: measured startup time at %7.2f ms\n\n", dt); +} + diff --git a/kuiviewer/Makefile.am b/kuiviewer/Makefile.am new file mode 100644 index 00000000..e5713689 --- /dev/null +++ b/kuiviewer/Makefile.am @@ -0,0 +1,70 @@ +# this has all of the subdirectories that make will recurse into. if +# there are none, comment this out + +# set the include path for X, qt and KDE +INCLUDES = $(all_includes) + +# these are the headers for your project +noinst_HEADERS = kuiviewer.h kuiviewer_part.h + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kuiviewer.pot + +KDE_ICON = kuiviewer + +# this Makefile creates both a KPart application and a KPart +######################################################################### +# APPLICATION SECTION +######################################################################### +# this is the program that gets installed. it's name is used for all +# of the other Makefile.am variables +bin_PROGRAMS = kuiviewer + +# the application source, library search path, and link libraries +kuiviewer_SOURCES = main.cpp kuiviewer.cpp +kuiviewer_LDFLAGS = $(KDE_RPATH) $(all_libraries) +kuiviewer_LDADD = $(LIB_KPARTS) + +xdg_apps_DATA =kuiviewer.desktop + +# this is where the shell's XML-GUI resource file goes +shellrcdir = $(kde_datadir)/kuiviewer +shellrc_DATA = kuiviewerui.rc + +######################################################################### +# KPART SECTION +######################################################################### +kde_module_LTLIBRARIES = libkuiviewerpart.la quithumbnail.la + +# the Part's source, library search path, and link libraries +libkuiviewerpart_la_SOURCES = kuiviewer_part.cpp +libkuiviewerpart_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +libkuiviewerpart_la_LIBADD = $(LIB_KPARTS) $(LIB_KFILE) -lqui + +# this is where the desktop file will go +partdesktopdir = $(kde_servicesdir) +partdesktop_DATA = kuiviewer_part.desktop + +# this is where the part's XML-GUI resource file goes +partrcdir = $(kde_datadir)/kuiviewerpart +partrc_DATA = kuiviewer_part.rc + + +######################################################################### +# THUMBNAIL SECTION +######################################################################### +quithumbnail_la_SOURCES = quicreator.cpp +quithumbnail_la_LIBADD = $(LIB_KDECORE) -lqui +quithumbnail_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) +services_DATA = designerthumbnail.desktop +servicesdir = $(kde_servicesdir) + +######################################################################### +# UTILS SECTION +######################################################################### +.PHONY: changes +changes: + cvs2cl.pl --window 3600 -w --hide-filenames -I '.desktop' diff --git a/kuiviewer/designerthumbnail.desktop b/kuiviewer/designerthumbnail.desktop new file mode 100644 index 00000000..ec98ccfd --- /dev/null +++ b/kuiviewer/designerthumbnail.desktop @@ -0,0 +1,53 @@ +[Desktop Entry] +Type=Service +Name=Qt Designer Files +Name[bg]=Файлове на дизайнера Qt +Name[br]=Restroù Ergrafer Qt +Name[bs]=Qt Designer datoteke +Name[ca]=Fitxers de Qt Designer +Name[cs]=Soubory Qt designeru +Name[cy]=Ffeiliau Qt Designer +Name[da]=Qt Designer-filer +Name[de]=Qt-Designer Dateien +Name[el]=Αρχεία Qt Designer +Name[es]=Archivos de Qt Designer +Name[et]=Qt Designeri failid +Name[eu]=Qt Designer fitxategiak +Name[fa]=پرونده‌های طراح Qt +Name[fi]=Qt Designer -tiedostot +Name[fr]=Fichiers Qt Designer +Name[ga]=Comhaid Qt Designer +Name[gl]=Ficheiros de Qt Designer +Name[hi]=क्यू-टी डिजाइनर फ़ाइलें +Name[hu]=Qt Designer fájlok +Name[is]=Qt Designer skrár +Name[it]=File di Qt Designer +Name[ja]=Qt デザイナーファイル +Name[ka]=Qt დიზაინერის ფაილები +Name[kk]=Qt Designer файлы +Name[lt]=Qt Designer bylos +Name[nb]=Qt Designer-filer +Name[nds]=Qt-Designer-Dateien +Name[ne]=Qt डिजाइनर फाइल +Name[nl]=Qt Designer-bestanden +Name[nn]=Qt Designer-filer +Name[pa]=Qt Designer ਫਾਇਲ +Name[pl]=Pliki Qt Designer +Name[pt]=Ficheiros do Qt Designer +Name[pt_BR]=Arquivos do Qt Designer +Name[ru]=Файлы Qt Designer +Name[sk]=Súbory Qt Designer +Name[sl]=Datoteke za Qt Designer +Name[sr]=Фајлови Qt Designer-а +Name[sr@Latn]=Fajlovi Qt Designer-a +Name[sv]=Qt-designer-filer +Name[ta]=Qt வடிவமை கோப்புகள் +Name[tg]=Файлҳои Qt Designer +Name[tr]=Qt Designer Dosyaları +Name[uk]=Файли Qt Designer +Name[zh_CN]=Qt 设计师文件 +Name[zh_TW]=Qt Designer 檔案 +ServiceTypes=ThumbCreator +MimeTypes=application/x-designer +X-KDE-Library=quithumbnail +CacheThumbnail=true diff --git a/kuiviewer/hi16-app-kuiviewer.png b/kuiviewer/hi16-app-kuiviewer.png new file mode 100644 index 00000000..43eab761 Binary files /dev/null and b/kuiviewer/hi16-app-kuiviewer.png differ diff --git a/kuiviewer/hi32-app-kuiviewer.png b/kuiviewer/hi32-app-kuiviewer.png new file mode 100644 index 00000000..ce9df987 Binary files /dev/null and b/kuiviewer/hi32-app-kuiviewer.png differ diff --git a/kuiviewer/hi48-app-kuiviewer.png b/kuiviewer/hi48-app-kuiviewer.png new file mode 100644 index 00000000..6464fb39 Binary files /dev/null and b/kuiviewer/hi48-app-kuiviewer.png differ diff --git a/kuiviewer/kuiviewer.cpp b/kuiviewer/kuiviewer.cpp new file mode 100644 index 00000000..f56b0985 --- /dev/null +++ b/kuiviewer/kuiviewer.cpp @@ -0,0 +1,165 @@ +/* + * + * This file is part of the kuiviewer package + * Copyright (c) 2003 Richard Moore + * Copyright (c) 2003 Ian Reinhart Geiser + * Copyright (c) 2004 Benjamin C. Meyer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + **/ + +#include "kuiviewer.h" +#include "kuiviewer.moc" +#include "kuiviewer_part.h" + +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +KUIViewer::KUIViewer() + : KParts::MainWindow( 0L, "KUIViewer" ) +{ + // setup our actions + setupActions(); + + setMinimumSize(300, 200); + + // Bring up the gui + setupGUI(); + + // this routine will find and load our Part. it finds the Part by + // name which is a bad idea usually.. but it's alright in this + // case since our Part is made for this Shell + KLibFactory *factory = KLibLoader::self()->factory("libkuiviewerpart"); + if (factory) + { + // now that the Part is loaded, we cast it to a Part to get + // our hands on it + m_part = static_cast(factory->create(this, + "kuiviewer_part", "KParts::ReadOnlyPart" )); + + if (m_part) + { + // tell the KParts::MainWindow that this is indeed the main widget + setCentralWidget(m_part->widget()); + + // and integrate the part's GUI with the shell's + createGUI(m_part); + } + } + else + { + // if we couldn't find our Part, we exit since the Shell by + // itself can't do anything useful + //FIXME improve message, which Part is this referring to? + KMessageBox::error(this, i18n("Unable to locate Kuiviewer kpart.")); + kapp->quit(); + // we return here, cause kapp->quit() only means "exit the + // next time we enter the event loop... + return; + } +} + +KUIViewer::~KUIViewer() +{ +} + +void KUIViewer::load(const KURL& url) +{ + m_part->openURL( url ); + adjustSize(); +} + +void KUIViewer::setupActions() +{ + KStdAction::open(this, SLOT(fileOpen()), actionCollection()); + KStdAction::quit(kapp, SLOT(quit()), actionCollection()); +} + +void KUIViewer::saveProperties(KConfig* /*config*/) +{ + // the 'config' object points to the session managed + // config file. anything you write here will be available + // later when this app is restored +} + +void KUIViewer::readProperties(KConfig* /*config*/) +{ + // the 'config' object points to the session managed + // config file. this function is automatically called whenever + // the app is being restored. read in here whatever you wrote + // in 'saveProperties' +} + +void KUIViewer::fileOpen() +{ + // this slot is called whenever the File->Open menu is selected, + // the Open shortcut is pressed (usually CTRL+O) or the Open toolbar + // button is clicked + KURL file_name = + KFileDialog::getOpenURL( QString::null, i18n("*.ui *.UI|User Interface Files"), this ); + + if (file_name.isEmpty() == false) + { + // About this function, the style guide ( + // http://developer.kde.org/documentation/standards/kde/style/basics/index.html ) + // says that it should open a new window if the document is _not_ + // in its initial state. This is what we do here.. + if ( m_part->url().isEmpty() ) + { + // we open the file in this window... + load( file_name ); + } + else + { + // we open the file in a new window... + KUIViewer* newWin = new KUIViewer; + newWin->load( file_name ); + newWin->show(); + } + } +} + +void KUIViewer::takeScreenshot(const QCString &filename, int w, int h){ + if(!m_part) + return; + showMinimized(); + if(w!=-1 && h!=-1){ + // resize widget to the desired size + m_part->widget()->setMinimumSize(w, h); + m_part->widget()->setMaximumSize(w, h); + m_part->widget()->repaint(); + // resize app to be as large as desired size + adjustSize(); + // Disable the saving of the size + setAutoSaveSettings(QString::fromLatin1("MainWindow"), false); + } + QPixmap pixmap = QPixmap::grabWidget( m_part->widget() ); + pixmap.save( filename, "PNG" ); +} + diff --git a/kuiviewer/kuiviewer.desktop b/kuiviewer/kuiviewer.desktop new file mode 100644 index 00000000..e1e62263 --- /dev/null +++ b/kuiviewer/kuiviewer.desktop @@ -0,0 +1,58 @@ +[Desktop Entry] +Name=KUIViewer +Name[cy]=KUIGwelydd +Name[hi]=के-यूआई-व्यूअर +Name[ja]=KUI ビューア +Name[sv]=KUIviewer +Name[ta]=KUIவியூவர் +Exec=kuiviewer %i %m -caption "%c" +Icon=kuiviewer +Type=Application +GenericName=Qt Designer UI File Viewer +GenericName[br]=Ur gweler restr UI Ergrafer Qt +GenericName[bs]=Preglednik Qt Designer UI datoteka +GenericName[ca]=Visor de fitxer UI de Qt Designer +GenericName[cs]=Prohlížeč UI souborů z Qt Designeru +GenericName[cy]=Gwelydd Ffeiliau UI Qt Designer +GenericName[da]=Qt Designer UI filviser +GenericName[de]=Betrachter für UI-Dateien des Qt-Designer +GenericName[el]=Προβολέας αρχείων Qt Designer UI +GenericName[es]=Visor de archivos UI de Qt Designer +GenericName[et]=Qt Designeri UI-failide näitaja +GenericName[eu]=Qt Designer UI fitxartegi ikusgailua +GenericName[fa]=مشاهده‌گر پروندۀ واسط نگاره‌ای طراح Qt +GenericName[fi]=Qt Designerin UI-tiedostojen katselin +GenericName[fr]=Afficheur de fichiers UI de QT Designer +GenericName[gl]=Visor de ficheiros de Qt Designer +GenericName[hu]=Qt Designer UI-fájlnézegető +GenericName[is]=Qt Designer UI skráaskoðari +GenericName[it]=Visualizzatore per file UI di Qt Designer +GenericName[ja]=Qt デザイナー UI ファイルビューア +GenericName[ka]=Qt დიზაინერის UI ფაილთა მხილველი +GenericName[kk]=Qt Designer пайдаланушы интерфейсінің файлдарын қарау құралы +GenericName[lt]=Qt Designer naudotojo sąsajos bylų žiūryklė +GenericName[nb]=Qt Designer UI-filviser +GenericName[nds]=Kieker för Qt-Designer sien UI-Dateien +GenericName[ne]=Qt डिजाइनर यूआई फाइल दर्शक +GenericName[nl]=Weergave van QT Designer UI-bestanden +GenericName[nn]=Qt Designer UI-filvisar +GenericName[pl]=Przeglądarka plików interfejsu użytkownika Qt Designera +GenericName[pt]=Visualizador de Ficheiros UI do Qt Designer +GenericName[pt_BR]=Visualizador de Arquivos e Interface do Qt Designer +GenericName[ru]=Просмотрщик UI-файлов Qt Designer +GenericName[sk]=Prehliadač súborov UI pre Qt Designer +GenericName[sl]=Pregledovalnik datotek UI za Qt Designer +GenericName[sr]=Приказивач UI фајлова Qt Designer-а +GenericName[sr@Latn]=Prikazivač UI fajlova Qt Designer-a +GenericName[sv]=Qt-designer UI-filvisning +GenericName[ta]=Qt வடிவமைப்பவர் UI கோப்புகள் காட்சியகம் +GenericName[tg]=Намоишгари UI-файлҳо Qt Designer +GenericName[tr]=Qt Designer UI Dosya Görüntüleyici +GenericName[uk]=Переглядач UI-файлів Qt Designer +GenericName[zh_CN]=Qt 设计师 UI 文件查看器 +GenericName[zh_TW]=Qt Designer UI 檔案檢視器 +MimeType=application/x-designer; +Terminal=false +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Development; +InitialPreference=10 diff --git a/kuiviewer/kuiviewer.h b/kuiviewer/kuiviewer.h new file mode 100644 index 00000000..c6194b4e --- /dev/null +++ b/kuiviewer/kuiviewer.h @@ -0,0 +1,103 @@ +/* + * + * This file is part of the kuiviewer package + * Copyright (c) 2003 Richard Moore + * Copyright (c) 2003 Ian Reinhart Geiser + * Copyright (c) 2004 Benjamin C. Meyer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + **/ + +#ifndef KUIVIEWER_H +#define KUIVIEWER_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +class KToggleAction; +class KListView; + +namespace KParts { +class ReadOnlyPart; +} + +/** + * This is the application "Shell". It has a menubar, toolbar, and + * statusbar but relies on the "Part" to do all the real work. + * + * @short KUI Viewer Shell + * @author Richard Moore + * @author Ian Reinhart Geiser + * @version 1.0 + */ +class KUIViewer : public KParts::MainWindow +{ + Q_OBJECT +public: + /** + * Default Constructor + */ + KUIViewer(); + + /** + * Default Destructor + */ + virtual ~KUIViewer(); + + /** + * Use this method to load whatever file/URL you have + */ + void load(const KURL& url); + + /** + * Take screenshot of current ui file + * @param filename to save image in + * @param h height of image + * @param w width of image + */ + void takeScreenshot(const QCString &filename, int h=-1, int w=-1); + +protected: + /** + * This method is called when it is time for the app to save its + * properties for session management purposes. + */ + void saveProperties(KConfig *); + + /** + * This method is called when this app is restored. The KConfig + * object points to the session management config file that was saved + * with @ref saveProperties + */ + void readProperties(KConfig *); + +private slots: + void fileOpen(); + +private: + void setupActions(); + +private: + KParts::ReadOnlyPart *m_part; + KToggleAction *m_toolbarAction; + KToggleAction *m_statusbarAction; +}; + +#endif // KUIVIEWER_H + diff --git a/kuiviewer/kuiviewer_part.cpp b/kuiviewer/kuiviewer_part.cpp new file mode 100644 index 00000000..25ef1b2d --- /dev/null +++ b/kuiviewer/kuiviewer_part.cpp @@ -0,0 +1,211 @@ +/** + * + * This file is part of the kuiviewer package + * Copyright (c) 2003 Richard Moore + * Copyright (c) 2003 Ian Reinhart Geiser + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + **/ + +#include "kuiviewer_part.h" +#include "kuiviewer_part.moc" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef KParts::GenericFactory KUIViewerPartFactory; +K_EXPORT_COMPONENT_FACTORY( libkuiviewerpart, KUIViewerPartFactory ) + +KUIViewerPart::KUIViewerPart( QWidget *parentWidget, const char *widgetName, + QObject *parent, const char *name, + const QStringList & /*args*/ ) + : KParts::ReadOnlyPart(parent, name) +{ + // we need an instance + setInstance( KUIViewerPartFactory::instance() ); + + KGlobal::locale()->insertCatalogue("kuiviewer"); + + // this should be your custom internal widget + m_widget = new QVBox( parentWidget, widgetName ); + + // notify the part that this is our internal widget + setWidget(m_widget); + + // set our XML-UI resource file + setXMLFile("kuiviewer_part.rc"); + + m_style = new KListAction( i18n("Style"), + CTRL + Key_S, + this, + SLOT(slotStyle(int)), + actionCollection(), + "change_style"); + m_style->setEditable(false); + + kapp->config()->setGroup("General"); + const QString currentStyle = kapp->config()->readEntry("currentWidgetStyle", KStyle::defaultStyle()); + + const QStringList styles = QStyleFactory::keys(); + m_style->setItems(styles); + m_style->setCurrentItem(0); + + QStringList::ConstIterator it = styles.begin(); + QStringList::ConstIterator end = styles.end(); + int idx = 0; + for (; it != end; ++it, ++idx) { + if ((*it).lower() == currentStyle.lower()) { + m_style->setCurrentItem(idx); + break; + } + } + m_style->setToolTip(i18n("Set the current style to view.")); + m_style->setMenuAccelsEnabled(true); + + m_copy = KStdAction::copy(this, SLOT(slotGrab()), actionCollection()); + + updateActions(); + +// Commented out to fix warning (rich) +// slot should probably be called saveAs() for consistency with +// KParts::ReadWritePart BTW. +// KStdAction::saveAs(this, SLOT(slotSave()), actionCollection()); +} + +KUIViewerPart::~KUIViewerPart() +{ +} + +KAboutData *KUIViewerPart::createAboutData() +{ + // the non-i18n name here must be the same as the directory in + // which the part's rc file is installed ('partrcdir' in the + // Makefile) + KAboutData *aboutData = new KAboutData("kuiviewerpart", I18N_NOOP("KUIViewerPart"), "0.1", + I18N_NOOP("Displays Designer's UI files"), + KAboutData::License_LGPL ); + aboutData->addAuthor("Richard Moore", 0, "rich@kde.org"); + aboutData->addAuthor("Ian Reinhart Geiser", 0, "geiseri@kde.org"); + return aboutData; +} + +bool KUIViewerPart::openFile() +{ + // m_file is always local so we can use QFile on it + QFile file( m_file ); + if ( !file.open(IO_ReadOnly) ) + return false; + + delete m_view; + m_view = QWidgetFactory::create( &file, 0, m_widget ); + + file.close(); + updateActions(); + + if ( !m_view ) + return false; + + m_view->show(); + slotStyle(0); + return true; +} + +bool KUIViewerPart::openURL( const KURL& url) +{ + // just for fun, set the status bar + emit setStatusBarText( url.prettyURL() ); + emit setWindowCaption( url.prettyURL() ); + + m_url = url; + m_file = QString::null; + if (KIO::NetAccess::download(url, m_file)) + return openFile(); + else + return false; +} + +void KUIViewerPart::updateActions() +{ + if ( !m_view.isNull() ) { + m_style->setEnabled( true ); + m_copy->setEnabled( true ); + } + else { + m_style->setEnabled( false ); + m_copy->setEnabled( false ); + } +} + +void KUIViewerPart::slotStyle(int) +{ + if ( m_view.isNull() ) { + updateActions(); + return; + } + + QString styleName = m_style->currentText(); + QStyle* style = QStyleFactory::create(styleName); + kdDebug() << "Change style..." << endl; + m_widget->hide(); + QApplication::setOverrideCursor( WaitCursor ); + m_widget->setStyle( style); + + QObjectList *l = m_widget->queryList( "QWidget" ); + for ( QObject *o = l->first(); o; o = l->next() ) + ( static_cast(o) )->setStyle( style ); + delete l; + + m_widget->show(); + QApplication::restoreOverrideCursor(); + + kapp->config()->setGroup("General"); + kapp->config()->writeEntry("currentWidgetStyle", m_style->currentText()); + kapp->config()->sync(); +} + +void KUIViewerPart::slotGrab() +{ + if ( m_view.isNull() ) { + updateActions(); + return; + } + + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setPixmap(QPixmap::grabWidget(m_widget)); +} + diff --git a/kuiviewer/kuiviewer_part.desktop b/kuiviewer/kuiviewer_part.desktop new file mode 100644 index 00000000..704eaf5d --- /dev/null +++ b/kuiviewer/kuiviewer_part.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=KUIViewerPart +Name[hi]=के-यूआई-व्यूअर-पार्ट +Name[sv]=KUIviewer-del +Name[ta]=KUIவியூவர் உறுப்பு +MimeType=application/x-designer; +ServiceTypes=KParts/ReadOnlyPart +X-KDE-Library=libkuiviewerpart +Type=Service diff --git a/kuiviewer/kuiviewer_part.h b/kuiviewer/kuiviewer_part.h new file mode 100644 index 00000000..e34f1f35 --- /dev/null +++ b/kuiviewer/kuiviewer_part.h @@ -0,0 +1,80 @@ +/** + * + * This file is part of the kuiviewer package + * Copyright (c) 2003 Richard Moore + * Copyright (c) 2003 Ian Reinhart Geiser + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + **/ + +#ifndef KUIVIEWERPART_H +#define KUIVIEWERPART_H + +#include +#include + +class QWidget; +class KURL; +class QVBox; +class KAboutData; +class KListAction; +class KListView; + +/** + * This is a "Part". It that does all the real work in a KPart + * application. + * + * @short Main Part + * @author Richard Moore + * @version 0.1 + */ +class KUIViewerPart : public KParts::ReadOnlyPart +{ + Q_OBJECT +public: + /** + * Default constructor + */ + KUIViewerPart(QWidget *parentWidget, const char *widgetName, + QObject *parent, const char *name, const QStringList &args); + + /** + * Destructor + */ + virtual ~KUIViewerPart(); + + static KAboutData *createAboutData(); + +public slots: + bool openURL( const KURL& ); + void slotStyle(int); + void slotGrab(); + void updateActions(); + +protected: + /** + * This must be implemented by each part + */ + virtual bool openFile(); + +private: + QVBox *m_widget; + QGuardedPtr m_view; + KListAction *m_style; + KAction *m_copy; +}; + +#endif // KUIVIEWERPART_H + diff --git a/kuiviewer/kuiviewer_part.rc b/kuiviewer/kuiviewer_part.rc new file mode 100644 index 00000000..26fead68 --- /dev/null +++ b/kuiviewer/kuiviewer_part.rc @@ -0,0 +1,17 @@ + + + + &File + + + &Edit + + + &View + + + +Style + + + diff --git a/kuiviewer/kuiviewerui.rc b/kuiviewer/kuiviewerui.rc new file mode 100644 index 00000000..296367af --- /dev/null +++ b/kuiviewer/kuiviewerui.rc @@ -0,0 +1,8 @@ + + + + &File + &Edit + &View + + diff --git a/kuiviewer/lo16-app-kuiviewer.png b/kuiviewer/lo16-app-kuiviewer.png new file mode 100644 index 00000000..6983b6ab Binary files /dev/null and b/kuiviewer/lo16-app-kuiviewer.png differ diff --git a/kuiviewer/lo32-app-kuiviewer.png b/kuiviewer/lo32-app-kuiviewer.png new file mode 100644 index 00000000..8a55a122 Binary files /dev/null and b/kuiviewer/lo32-app-kuiviewer.png differ diff --git a/kuiviewer/main.cpp b/kuiviewer/main.cpp new file mode 100644 index 00000000..0ab53d0f --- /dev/null +++ b/kuiviewer/main.cpp @@ -0,0 +1,88 @@ +/** + * + * This file is part of the kuiviewer package + * Copyright (c) 2003 Richard Moore + * Copyright (c) 2003 Ian Reinhart Geiser + * Copyright (c) 2004 Benjamin C. Meyer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + **/ + +#include "kuiviewer.h" +#include +#include +#include +#include + +static KCmdLineOptions options[] = +{ + { "+[URL]", I18N_NOOP( "Document to open" ), 0 }, + { "s",0,0 }, + { "takescreenshot ", I18N_NOOP( "Save screenshot to file and exit" ), 0 }, + { "w",0,0 }, + { "screenshotwidth ", I18N_NOOP( "Screenshot width" ), "-1" }, + { "h",0,0 }, + { "screenshotheight ", I18N_NOOP( "Screenshot height" ), "-1" }, + KCmdLineLastOption +}; + +int main(int argc, char **argv) +{ + KAboutData about("kuiviewer", I18N_NOOP("KUIViewer"), "0.1", + I18N_NOOP("Displays Designer's UI files"), + KAboutData::License_LGPL ); + about.addAuthor("Richard Moore", 0, "rich@kde.org"); + about.addAuthor("Ian Reinhart Geiser", 0, "geiseri@kde.org"); + // Screenshot capability + about.addAuthor("Benjamin C. Meyer", 0, "ben+kuiviewer@meyerhome.net"); + + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addCmdLineOptions( options ); + KApplication app; + + // see if we are starting with session management + if (app.isRestored()) + RESTORE(KUIViewer) + else + { + // no session.. just start up normally + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + if ( args->count() == 0 ) + { + KUIViewer *widget = new KUIViewer; + widget->show(); + } + else + { + int i = 0; + for (; i < args->count(); i++ ) { + KUIViewer *widget = new KUIViewer; + widget->load( args->url(i) ); + + if (args->isSet("takescreenshot")){ + widget->takeScreenshot(args->getOption("takescreenshot"), + QString(args->getOption("screenshotwidth")).toInt(), + QString(args->getOption("screenshotheight")).toInt()); + return 0; + } + widget->show(); + } + } + args->clear(); + } + + return app.exec(); +} diff --git a/kuiviewer/quicreator.cpp b/kuiviewer/quicreator.cpp new file mode 100644 index 00000000..0bf47e71 --- /dev/null +++ b/kuiviewer/quicreator.cpp @@ -0,0 +1,61 @@ +/** + * + * This file is part of the kuiviewer package + * Copyright (c) 2003 Richard Moore + * Copyright (c) 2003 Ian Reinhart Geiser + * Copyright (c) 2004 Benjamin C. Meyer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + **/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "quicreator.h" + +#include + +extern "C" +{ + KDE_EXPORT ThumbCreator *new_creator() + { + return new QUICreator; + } +} + +bool QUICreator::create(const QString &path, int width, int height, QImage & +img) +{ + QWidget *w = QWidgetFactory::create(path, 0, 0); + if ( w ) + { + QPixmap p = QPixmap::grabWidget(w); + img = p.convertToImage().smoothScale(width,height,QImage::ScaleMin); + return true; + } + else + return false; +} + +ThumbCreator::Flags QUICreator::flags() const +{ + return static_cast(DrawFrame); +} + diff --git a/kuiviewer/quicreator.h b/kuiviewer/quicreator.h new file mode 100644 index 00000000..7127ecc7 --- /dev/null +++ b/kuiviewer/quicreator.h @@ -0,0 +1,36 @@ +/* + * + * This file is part of the kuiviewer package + * Copyright (c) 2003 Richard Moore + * Copyright (c) 2003 Ian Reinhart Geiser + * Copyright (c) 2004 Benjamin C. Meyer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + **/ +#ifndef UICREATOR_H +#define UICREATOR_H + +#include + +class QUICreator : public ThumbCreator +{ +public: + QUICreator() {}; + virtual bool create(const QString &path, int, int, QImage &img); + virtual Flags flags() const; +}; + +#endif // UICREATOR_H + diff --git a/kunittest/Makefile.am b/kunittest/Makefile.am new file mode 100644 index 00000000..2e5d3d0b --- /dev/null +++ b/kunittest/Makefile.am @@ -0,0 +1,21 @@ +SUBDIRS = example +INCLUDES = $(all_includes) +METASOURCES = AUTO + +lib_LTLIBRARIES = libkunittestgui.la +libkunittestgui_la_SOURCES = testerwidget.ui runnergui.cpp dcopinterface.skel +libkunittestgui_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) +libkunittestgui_la_LIBADD = -lkunittest $(LIB_KDECORE) + +runnergui.lo : testerwidget.h + +bin_PROGRAMS = kunittestguimodrunner +kunittestguimodrunner_SOURCES = guimodrunner.cpp +kunittestguimodrunner_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kunittestguimodrunner_LDADD = libkunittestgui.la $(LIB_KDECORE) $(LIB_KIO) + +noinst_HEADERS = dcopinterface.h +libkunittestinclude_HEADERS = runnergui.h +libkunittestincludedir = $(includedir)/kunittest + +bin_SCRIPTS = kunittest kunittestmod kunittest_debughelper diff --git a/kunittest/dcopinterface.h b/kunittest/dcopinterface.h new file mode 100644 index 00000000..046753d2 --- /dev/null +++ b/kunittest/dcopinterface.h @@ -0,0 +1,38 @@ +/*************************************************************************** + * Copyright (C) 2005 by Jeroen Wijnhout * + * Jeroen.Wijnhout@kdemail.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef KUNITTEST_DCOPINTERFACE_H +#define KUNITTEST_DCOPINTERFACE_H + +#include +#include + +namespace KUnitTest +{ + class DCOPInterface : public DCOPObject + { + K_DCOP + + k_dcop: + virtual bool addDebugInfo(const QString &name, const QString &info) = 0; + virtual bool addSlotDebugInfo(const QString &name, const QString &slt, const QString &info) = 0; + }; +} + +#endif diff --git a/kunittest/example/Makefile.am b/kunittest/example/Makefile.am new file mode 100644 index 00000000..d008ca22 --- /dev/null +++ b/kunittest/example/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = simple module diff --git a/kunittest/example/module/Makefile.am b/kunittest/example/module/Makefile.am new file mode 100644 index 00000000..17404d06 --- /dev/null +++ b/kunittest/example/module/Makefile.am @@ -0,0 +1,20 @@ +INCLUDES = -I$(top_srcdir)/include $(all_includes) +METASOURCES = AUTO + +noinst_HEADERS = samplemodule.h sampleextra.h sampletests.h + +check_LTLIBRARIES = kunittest_samplemodule.la kunittest_samplemodule2.la + +kunittest_samplemodule_la_SOURCES = samplemodule.cpp sampletests.cpp sampleextra.cpp +kunittest_samplemodule_la_LIBADD = -lkunittest +kunittest_samplemodule_la_LDFLAGS = -module $(KDE_CHECK_PLUGIN) $(all_libraries) + +kunittest_samplemodule2_la_SOURCES = samplemodule2.cpp +kunittest_samplemodule2_la_LIBADD = -lkunittest +kunittest_samplemodule2_la_LDFLAGS = -module $(KDE_CHECK_PLUGIN) $(all_libraries) + +check-local: + kunittestmodrunner + +guicheck: + $(srcdir)/../../kunittestmod $(PWD) diff --git a/kunittest/example/module/sampleextra.cpp b/kunittest/example/module/sampleextra.cpp new file mode 100644 index 00000000..38c49448 --- /dev/null +++ b/kunittest/example/module/sampleextra.cpp @@ -0,0 +1,37 @@ +/** + * Copyright (C) 2005 Jeroen Wijnhout + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "sampleextra.h" + + +void SomeExtraTester::allTests() +{ + kdDebug() << "Debug output belonging to SomeExtraTester." << endl; + + CHECK( QString("Extra") , QString("Extra") ); +} diff --git a/kunittest/example/module/sampleextra.h b/kunittest/example/module/sampleextra.h new file mode 100644 index 00000000..f27a6bd9 --- /dev/null +++ b/kunittest/example/module/sampleextra.h @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2005 Jeroen Wijnhout + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SAMPLEEXTRA_H_ +#define _SAMPLEEXTRA_H_ + +#include + +class SomeExtraTester : public KUnitTest::Tester +{ + void allTests(); +}; + +#endif diff --git a/kunittest/example/module/samplemodule.cpp b/kunittest/example/module/samplemodule.cpp new file mode 100644 index 00000000..3b6665fd --- /dev/null +++ b/kunittest/example/module/samplemodule.cpp @@ -0,0 +1,37 @@ +/** + * Copyright (C) 2005 Jeroen Wijnhout + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "sampletests.h" +#include "sampleextra.h" + +using namespace KUnitTest; + +KUNITTEST_MODULE( kunittest_samplemodule, "Suite1" ) +KUNITTEST_MODULE_REGISTER_TESTER( SlotSampleTester ) +KUNITTEST_MODULE_REGISTER_TESTER( SomeSampleTester ) +KUNITTEST_MODULE_REGISTER_TESTER( SomeExtraTester ) diff --git a/kunittest/example/module/samplemodule.h b/kunittest/example/module/samplemodule.h new file mode 100644 index 00000000..b09002c6 --- /dev/null +++ b/kunittest/example/module/samplemodule.h @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2005 Jeroen Wijnhout + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SAMPLETESTMODULE_H +#define SAMPLETESTMODULE_H + +#include + +class SimpleSampleTester : public KUnitTest::Tester +{ +public: + void allTests(); +}; + +class SomeSampleTester : public KUnitTest::Tester +{ +public: + void allTests(); +}; + +#endif diff --git a/kunittest/example/module/samplemodule2.cpp b/kunittest/example/module/samplemodule2.cpp new file mode 100644 index 00000000..87efde28 --- /dev/null +++ b/kunittest/example/module/samplemodule2.cpp @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2005 Jeroen Wijnhout + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include "samplemodule.h" + +using namespace KUnitTest; + +KUNITTEST_MODULE( kunittest_samplemodule2, "Suite2::Sub" ) +KUNITTEST_MODULE_REGISTER_TESTER( SimpleSampleTester ) +KUNITTEST_MODULE_REGISTER_TESTER( SomeSampleTester ) + +void SimpleSampleTester::allTests() +{ + kdDebug() << "Debug output belonging to SimpleSampleTester." << endl; + CHECK( QString("SimpleSample") , QString("SimpleSample") ); + + // operator == is used, so this can't work... + //XFAIL( "SimpleSample" , "SampleSimple" ); + + kdDebug() << "Do some math." << endl; + //XFAIL( 2*2 , 4 ); // to test unexpected passes + SKIP("Just curious how this 'skipping' works."); +} + +void SomeSampleTester::allTests() +{ + kdDebug() << "Checking operator precedences." << endl; + CHECK( 2.0 * 3.0 / 2.0 * 4.0 / 2.0 , 6.0 ); + + QStringList testList; + testList << "one" << "two"; + CHECK( testList.count() , (QStringList::size_type) 2 ); +} diff --git a/kunittest/example/module/samplemodule2.h b/kunittest/example/module/samplemodule2.h new file mode 100644 index 00000000..b09002c6 --- /dev/null +++ b/kunittest/example/module/samplemodule2.h @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2005 Jeroen Wijnhout + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SAMPLETESTMODULE_H +#define SAMPLETESTMODULE_H + +#include + +class SimpleSampleTester : public KUnitTest::Tester +{ +public: + void allTests(); +}; + +class SomeSampleTester : public KUnitTest::Tester +{ +public: + void allTests(); +}; + +#endif diff --git a/kunittest/example/module/sampletests.cpp b/kunittest/example/module/sampletests.cpp new file mode 100644 index 00000000..3b253e55 --- /dev/null +++ b/kunittest/example/module/sampletests.cpp @@ -0,0 +1,82 @@ +/** + * Copyright (C) 2005 Jeroen Wijnhout + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include "sampletests.h" +#include "sampleextra.h" + +void SlotSampleTester::setUp() +{ + kdDebug() << "setUp" << endl; + m_str = new QString("setUp str"); +} + +void SlotSampleTester::tearDown() +{ + kdDebug() << "tearDown" << endl; + delete m_str; +} + +bool SlotSampleTester::test() +{ + kdDebug() << "SlotSampleTester::test()" << endl; + return true; +} + +void SlotSampleTester::testSlot() +{ + kdDebug() << "Debug output belonging to SlotSampleTester slot 1." << endl; + CHECK( test() , true); + CHECK( "test" , "test"); + kdDebug() << "Checking if m_str is initialized correctly." << endl; + CHECK( *m_str , QString("setUp str") ); +} + +void SlotSampleTester::testSlot2() +{ + kdDebug() << "Debug output belonging to SlotSampleTester slot 2." << endl; + CHECK("testSlot2","testSlot2"); + CHECK(1,1); + CHECK(2,2); +} + +void SomeSampleTester::allTests() +{ + kdDebug() << "Checking operator precedences." << endl; + CHECK( 2.0 * 3.0 / 2.0 * 4.0 / 2.0 , 6.0 ); + + QStringList testList; + testList << "one" << "two"; + CHECK( testList.count() , (QStringList::size_type) 2 ); + CHECK( testList.count()*2 , (QStringList::size_type) 4 ); +} + +#include "sampletests.moc" + diff --git a/kunittest/example/module/sampletests.h b/kunittest/example/module/sampletests.h new file mode 100644 index 00000000..63ff6e44 --- /dev/null +++ b/kunittest/example/module/sampletests.h @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2005 Jeroen Wijnhout + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SAMPLETESTMODULE_H +#define SAMPLETESTMODULE_H + +#include + +class SlotSampleTester : public KUnitTest::SlotTester +{ + Q_OBJECT + +public slots: + void setUp(); + void tearDown(); + void testSlot(); + void testSlot2(); + +private: + bool test(); + + QString *m_str; +}; + +class SomeSampleTester : public KUnitTest::Tester +{ +public: + void allTests(); +}; + +#endif diff --git a/kunittest/example/simple/Makefile.am b/kunittest/example/simple/Makefile.am new file mode 100644 index 00000000..f35b8228 --- /dev/null +++ b/kunittest/example/simple/Makefile.am @@ -0,0 +1,22 @@ +INCLUDES = -I$(top_srcdir) $(all_includes) +METASOURCES = AUTO + +check_PROGRAMS = sampletests sampletestsgui + +sampletests_SOURCES = main.cpp sampletest.cpp +sampletests_LDFLAGS = $(KDE_RPATH) $(all_libraries) +sampletests_LDADD = -lkunittest + +sampletestsgui_SOURCES = maingui.cpp sampletest.cpp +sampletestsgui_LDFLAGS = $(KDE_RPATH) $(all_libraries) +# Normally you would write -lkunittestgui here, but since the examples +# are bundled with the library source code itself we don't want to +# have you install the libraries before you can compile the examples. +sampletestsgui_LDADD = ../../libkunittestgui.la + +noinst_HEADERS = sampletest.h + +TESTS = sampletests + +guicheck: sampletestsgui + kunittest ./sampletestsgui SampleTests diff --git a/kunittest/example/simple/main.cpp b/kunittest/example/simple/main.cpp new file mode 100644 index 00000000..a750587c --- /dev/null +++ b/kunittest/example/simple/main.cpp @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2005 Jeroen Wijnhout + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "kunittest/runner.h" + +int main( int /*argc*/, char** /*argv*/ ) +{ + KUnitTest::Runner::self()->runTests(); + return KUnitTest::Runner::self()->numberOfFailedTests(); +} diff --git a/kunittest/example/simple/maingui.cpp b/kunittest/example/simple/maingui.cpp new file mode 100644 index 00000000..63e8cfd9 --- /dev/null +++ b/kunittest/example/simple/maingui.cpp @@ -0,0 +1,60 @@ +/** + * Copyright (C) 2005 Jeroen Wijnhout + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "kunittest/runnergui.h" + +static const char description[] = + I18N_NOOP("A simple sample."); + +static const char version[] = "0.1"; + +static KCmdLineOptions options[] = +{ +// { "+[URL]", I18N_NOOP( "Document to open" ), 0 }, + KCmdLineLastOption +}; + +int main( int argc, char** argv ) +{ + KAboutData about("SampleTests", I18N_NOOP("SampleTests"), version, description, + KAboutData::License_BSD, "(C) 2005 Jeroen Wijnhout", 0, 0, + "Jeroen.Wijnhout@kdemail.net"); + + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addCmdLineOptions( options ); + + KApplication app; + + KUnitTest::RunnerGUI runner(0); + runner.show(); + app.setMainWidget(&runner); + + return app.exec(); +} diff --git a/kunittest/example/simple/sampletest.cpp b/kunittest/example/simple/sampletest.cpp new file mode 100644 index 00000000..32d97703 --- /dev/null +++ b/kunittest/example/simple/sampletest.cpp @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2005 Jeroen Wijnhout + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "sampletest.h" + +using namespace KUnitTest; + +KUNITTEST_SUITE("SampleSuite"); +KUNITTEST_REGISTER_TESTER(SimpleSampleTester); +KUNITTEST_REGISTER_TESTER(SomeSampleTester); + +void SimpleSampleTester::allTests() +{ + kdDebug() << "Debug output belonging to SimpleSampleTester." << endl; + CHECK( QString("SimpleSample") , QString("SimpleSample") ); + + SKIP("Just curious how this 'skipping' works."); +} + +void SomeSampleTester::allTests() +{ + kdDebug() << "Checking operator precedences." << endl; + CHECK( 2.0 * 3.0 / 2.0 * 4.0 / 2.0 , 6.0 ); + + QStringList testList; + testList << "one" << "two"; + CHECK( testList.count() , (QStringList::size_type) 2 ); +} diff --git a/kunittest/example/simple/sampletest.h b/kunittest/example/simple/sampletest.h new file mode 100644 index 00000000..a4d85266 --- /dev/null +++ b/kunittest/example/simple/sampletest.h @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2005 Jeroen Wijnhout + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SAMPLETEST_H +#define SAMPLETEST_H + +#include + +class SimpleSampleTester : public KUnitTest::Tester +{ +public: + void allTests(); +}; + +class SomeSampleTester : public KUnitTest::Tester +{ +public: + void allTests(); +}; +#endif diff --git a/kunittest/guimodrunner.cpp b/kunittest/guimodrunner.cpp new file mode 100644 index 00000000..08f47734 --- /dev/null +++ b/kunittest/guimodrunner.cpp @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2005 Jeroen Wijnhout + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "runnergui.h" + +static const char description[] = + I18N_NOOP("A command-line application that can be used to run KUnitTest modules."); + +static const char version[] = "0.1"; + +static KCmdLineOptions options[] = +{ + {"query [regexp]", I18N_NOOP("Only run modules which filename match the regexp."), "^kunittest_.*\\.la$"}, + {"folder [folder]", I18N_NOOP("Only run tests modules which are found in the folder. Use the query option to select modules."), "."}, + { "enable-dbgcap", I18N_NOOP("Enables debug capturing. You typically use this option when you use the GUI."), 0}, + KCmdLineLastOption +}; + + +int main( int argc, char **argv ) +{ + KInstance instance("modrunner"); + + KAboutData about("KUnitTestModRunner", I18N_NOOP("KUnitTestModRunner"), version, description, + KAboutData::License_BSD, "(C) 2005 Jeroen Wijnhout", 0, 0, + "Jeroen.Wijnhout@kdemail.net"); + + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addCmdLineOptions( options ); + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + KUnitTest::Runner::loadModules(args->getOption("folder"), args->getOption("query")); + KUnitTest::Runner::setDebugCapturingEnabled(args->isSet("enable-dbgcap")); + + KApplication app; + + KUnitTest::RunnerGUI runner(0); + runner.show(); + app.setMainWidget(&runner); + + return app.exec(); +} diff --git a/kunittest/kunittest b/kunittest/kunittest new file mode 100755 index 00000000..fbb8424f --- /dev/null +++ b/kunittest/kunittest @@ -0,0 +1,19 @@ +#!/bin/bash + +export APP=$1 +export DCOPNAME=$2 + +if [ ! -x $APP ] +then + kdialog --error "Sorry, $APP is not a valid executable file." + exit 1; +fi + +DEBUGHELPER=`which kunittest_debughelper` +if [ -z $DEBUGHELPER ] +then + kdialog --error "Sorry, couldn't find the kunittest_debughelper script." + exit 3 +fi + +$APP 2>&1 | perl $DEBUGHELPER "$DCOPNAME-*" diff --git a/kunittest/kunittest_debughelper b/kunittest/kunittest_debughelper new file mode 100755 index 00000000..66dfacf2 --- /dev/null +++ b/kunittest/kunittest_debughelper @@ -0,0 +1,107 @@ +#!/usr/bin/perl +# Copyright (C) 2005 Jeroen Wijnhout +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use DCOP; + +my $app = $ARGV[0]; shift @ARGV; +my $dcopid = ""; +my $testername = ""; +my $reading_debug = 0; +my $debug=""; +my $runnergui; + +my $client = new DCOP; +$client->attach(); + +while (<>) +{ + $runnergui = getDCOPObject(); + my $line = $_; + + print $_; + + if ( $line =~ /KUnitTest_Debug_End\[.*\]/ ) + { + $ret = $runnergui->addDebugInfo($testername, $debug); + + $debug = ""; + + # stop reading + $reading_debug = 0; + } + elsif ( $line =~ /KUnitTest_Debug_EndSlot\[.*\]/ ) + { + $ret = $runnergui->addSlotDebugInfo($testername, $slotname, $debug); + + $line = ""; + $debug = ""; + $slotname = ""; + } + elsif ( $line =~ /KUnitTest_Debug_BeginSlot\[(.*)\]/ ) + { + $slotname = $1; + $line = ""; + } + + if ( $reading_debug ) + { + if ( $line =~ /^check:(.*\[[0-9]+\])/ ) + { + $line = $1.":\n"; + } + + $debug = $debug.$line; + } + + if ( $line =~ /KUnitTest_Debug_Start\[(.*)\]/ ) + { + $testername = $1; + $reading_debug = 1; + $debug=""; + } +} + +sub getDCOPObject +{ + if ( $dcopid eq "" ) + { + $allapps = $client->registeredApplications(); + my $i = 0; + while ( ! ($allapps->[$i] eq "") ) + { + if ( $allapps->[$i] =~ /$app/ ) + { + print "found: ".$allapps->[$i]."\n"; + $dcopid = $allapps->[$i]; + break; + } + + $i = $i + 1; + } + + $object = $client->createObject($dcopid, "Runner"); + } + + return $object; +} \ No newline at end of file diff --git a/kunittest/kunittestmod b/kunittest/kunittestmod new file mode 100755 index 00000000..ba039844 --- /dev/null +++ b/kunittest/kunittestmod @@ -0,0 +1,37 @@ +#!/bin/bash + +FOLDER="--folder $PWD" +QUERY="" + +while [ "$#" -gt 0 ] +do + case $1 in + -f|--folder) + FOLDER="--folder $2" + shift + ;; + -q|--query) + QUERY="--query $2" + shift + ;; + esac + # to process the next parameter + shift +done + +APP=`which kunittestguimodrunner` +if [ ! -x $APP ] +then + kdialog --error "Sorry, $APP is not a valid executable file." + exit 1; +fi + +DEBUGHELPER=`which kunittest_debughelper` +if [ -z $DEBUGHELPER ] +then + kdialog --error "Sorry, couldn't find the kunittest_debughelper script." + exit 3 +fi + +DCOPNAME="KUnitTestModRunner" +$APP --enable-dbgcap $FOLDER $QUERY 2>&1 | perl $DEBUGHELPER "$DCOPNAME-*" diff --git a/kunittest/runnergui.cpp b/kunittest/runnergui.cpp new file mode 100644 index 00000000..ef5afe98 --- /dev/null +++ b/kunittest/runnergui.cpp @@ -0,0 +1,433 @@ +/** + * Copyright (C) 2005 Jeroen Wijnhout + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "dcopinterface.h" +#include "runnergui.h" +#include "testerwidget.h" + +namespace KUnitTest +{ + const int g_nameColumn = 0; + const int g_finishedColumn = 1; + const int g_skippedColumn = 2; + const int g_failedColumn = 3; + const int g_xfailedColumn = 4; + const int g_passedColumn = 5; + const int g_xpassedColumn = 6; + + /*! The DCOP implementation for the RunnerGUI. + */ + class RunnerGUIDCOPImpl : virtual public DCOPInterface + { + public: + RunnerGUIDCOPImpl(RunnerGUI *rg) : m_rg(rg) + { + // set the DCOP object id + setObjId("Runner"); + } + + /*! This DCOP method adds debug info to a given test case. + * @param name The name of the test. + * @param info The debug info. + */ + bool addDebugInfo(const QString &name, const QString &info) + { + Tester *tester = Runner::self()->registry().find(name.local8Bit()); + if ( tester == 0L ) return false; + + tester->results()->addDebugInfo(info); + + return true; + } + + bool addSlotDebugInfo(const QString &name, const QString &slt, const QString &info) + { + Tester *tester = Runner::self()->registry().find(name.local8Bit()); + + if ( tester == 0L ) return false; + if ( ! tester->inherits("KUnitTest::SlotTester") ) return false; + + SlotTester *sltester = static_cast(tester); + sltester->results(slt.local8Bit())->addDebugInfo(info); + + return true; + } + + private: + RunnerGUI *m_rg; + }; + + RunnerGUI::RunnerGUI(QWidget *parent) : QHBox(parent) + { + m_dcop = new RunnerGUIDCOPImpl(this); + + m_testerWidget = new TesterWidget(this); + setGeometry(0, 0, 700, 500); + + // file the combo box + m_testerWidget->selectCombo()->insertItem("All suites/modules . . ."); + m_testerWidget->selectCombo()->insertItem("Selected tests . . ."); + + RegistryIteratorType it(Runner::self()->registry()); + QStringList suites; + for ( ; it.current(); ++it ) + { + addTester(it.currentKey(), it.current()); + + QString test = it.currentKey(); + int index = test.find("::"); + if ( index != -1 ) test = test.left(index); + + if ( suites.contains(test) == 0 ) + suites.append(test); + } + + for ( uint i = 0; i < suites.count(); ++i ) + m_testerWidget->selectCombo()->insertItem(suites[i]); + + // configure the resultslist + m_testerWidget->resultList()->setAllColumnsShowFocus(true); + m_testerWidget->resultList()->setSelectionMode(QListView::Extended); + m_testerWidget->resultList()->setRootIsDecorated(true); + m_testerWidget->resultList()->setColumnAlignment(g_finishedColumn, Qt::AlignHCenter); + m_testerWidget->resultList()->setColumnAlignment(g_skippedColumn, Qt::AlignHCenter); + m_testerWidget->resultList()->setColumnAlignment(g_failedColumn, Qt::AlignHCenter); + m_testerWidget->resultList()->setColumnAlignment(g_xfailedColumn, Qt::AlignHCenter); + m_testerWidget->resultList()->setColumnAlignment(g_passedColumn, Qt::AlignHCenter); + m_testerWidget->resultList()->setColumnAlignment(g_xpassedColumn, Qt::AlignHCenter); + + // set the text in the results label + fillResultsLabel(); + + // init the progress bar + configureProgressBar(Runner::self()->numberOfTestCases(), 0); + + connect(Runner::self(), SIGNAL(finished(const char *, Tester *)), this, SLOT(addTestResult(const char *, Tester *))); + connect(m_testerWidget->resultList(), SIGNAL(clicked(QListViewItem *)), this, SLOT(showDetails(QListViewItem *))); + connect(m_testerWidget, SIGNAL(run()), this, SLOT(runSuite())); + connect(m_testerWidget->details(), SIGNAL(doubleClicked(int, int)), this, SLOT(doubleClickedOnDetails(int, int))); + } + + RunnerGUI::~RunnerGUI() + { + delete m_dcop; + } + + void RunnerGUI::configureProgressBar(int steps, int progress) + { + m_testerWidget->progressBar()->setTotalSteps(steps); + m_testerWidget->progressBar()->setProgress(progress); + } + + void RunnerGUI::fillResultsLabel() + { + if ( Runner::self()->numberOfTests() > 0 ) + m_testerWidget->resultsLabel()->setText( + QString("Test cases: %1 | Tests performed: %5, Skipped: %4 | Passed: %2, Failed: %3") + .arg(Runner::self()->numberOfTestCases()) + .arg(Runner::self()->numberOfPassedTests()) + .arg(Runner::self()->numberOfFailedTests()) + .arg(Runner::self()->numberOfSkippedTests()) + .arg(Runner::self()->numberOfTests()) ); + else + m_testerWidget->resultsLabel()->setText(QString("Test cases: %1").arg(Runner::self()->numberOfTestCases())); + } + + void RunnerGUI::addTestResult(const char *name, Tester *test) + { + QStringList scopes = QStringList::split("::", name); + QString suite = scopes[0]; + + // find the suite item + QListViewItem *item = 0L; + for ( uint i = 0; i < scopes.count(); ++i ) + item = getItem(scopes[i], item); + + if ( test->inherits("KUnitTest::SlotTester") ) + { + SlotTester *sltest = static_cast(test); + TestResultsListIteratorType it(sltest->resultsList()); + QListViewItem *slotItem = 0L; + for ( ; it.current(); ++it) + { + slotItem = getItem(it.currentKey(), item); + setSummary(slotItem, it.current()); + } + } + else + setSummary(item, test->results()); + + fillResultsLabel(); + m_testerWidget->progressBar()->setProgress(m_testerWidget->progressBar()->progress() + 1); + } + + void RunnerGUI::addTester(const char *name, Tester *test) + { + QStringList scopes = QStringList::split("::", name); + QString suite = scopes[0]; + + // find the suite item + QListViewItem *item = 0L; + for ( uint i = 0; i < scopes.count(); ++i ) + item = getItem(scopes[i], item); + + if ( test->inherits("KUnitTest::SlotTester") ) + { + QStrList allSlots = test->metaObject()->slotNames(); + for ( char *sl = allSlots.first(); sl; sl = allSlots.next() ) + { + if ( QString(sl).startsWith("test") ) + getItem(sl, item); + } + } + } + + QListViewItem *RunnerGUI::getItem(const QString &name, QListViewItem *item /*= 0L*/) + { + QListViewItem *parent = item; + + if ( item == 0L ) item = m_testerWidget->resultList()->firstChild(); + else item = item->firstChild(); + + while ( item && (item->text(g_nameColumn) != name) ) + item = item->nextSibling(); + + // item not found, create it + if ( item == 0L ) + { + if ( parent == 0L ) + item = new QListViewItem(m_testerWidget->resultList()); + else + item = new QListViewItem(parent); + + item->setText(g_nameColumn, name); + } + + return item; + } + + void RunnerGUI::reset() + { + QListViewItemIterator it( m_testerWidget->resultList() ); + while ( it.current() ) + { + QListViewItem *item = it.current(); + item->setText(g_finishedColumn, "0"); + item->setText(g_skippedColumn, "0"); + item->setText(g_failedColumn, "0"); + item->setText(g_xfailedColumn, "0"); + item->setText(g_passedColumn, "0"); + item->setText(g_xpassedColumn, "0"); + item->setPixmap(g_nameColumn, QPixmap()); + ++it; + } + } + + void RunnerGUI::setSummary(QListViewItem *item, TestResults *res) + { + if ( item == 0L ) return; + + bool ok; + + int val = item->text(g_finishedColumn).toInt(&ok); if (!ok) val = 0; + item->setText(g_finishedColumn, QString::number(val + res->testsFinished())); + + val = item->text(g_skippedColumn).toInt(&ok); if (!ok) val = 0; + item->setText(g_skippedColumn, QString::number(val + res->skipped())); + + val = item->text(g_passedColumn).toInt(&ok); if (!ok) val = 0; + item->setText(g_passedColumn, QString::number(val + res->passed())); + + val = item->text(g_failedColumn).toInt(&ok); if (!ok) val = 0; + item->setText(g_failedColumn, QString::number(val + res->errors())); + + val = item->text(g_xfailedColumn).toInt(&ok); if (!ok) val = 0; + item->setText(g_xfailedColumn, QString::number(val + res->xfails())); + + val = item->text(g_xpassedColumn).toInt(&ok); if (!ok) val = 0; + item->setText(g_xpassedColumn, QString::number(val + res->xpasses())); + + bool passed = (item->text(g_failedColumn).toInt(&ok) + item->text(g_xfailedColumn).toInt(&ok)) == 0; + item->setPixmap(g_nameColumn, passed ? SmallIcon("button_ok") : SmallIcon("button_cancel") ); + + setSummary(item->parent(), res); + } + + QString RunnerGUI::fullName(QListViewItem *item) + { + QString name = item->text(g_nameColumn); + while ( (item = item->parent()) != 0L ) + name = item->text(g_nameColumn) + "::" + name; + + return name; + } + + void RunnerGUI::runSuite() + { + Runner::self()->reset(); + reset(); + + if ( m_testerWidget->selectCombo()->currentItem() == 0 ) + { + configureProgressBar(Runner::self()->numberOfTestCases(), 0); + Runner::self()->runTests(); + } + else if ( m_testerWidget->selectCombo()->currentItem() == 1 ) + { + QListViewItemIterator it( m_testerWidget->resultList() ); + QStringList prefixes; + while ( it.current() ) + { + QListViewItem *item = it.current(); + if ( item->isSelected() ) + { + QString prefix = fullName(item); + if ( prefix.endsWith("()") ) + { + int index = prefix.findRev("::"); + prefix = prefix.left(index); + } + prefixes << prefix; + } + + ++it; + } + + configureProgressBar(prefixes.count(), 0); + for ( uint i = 0; i < prefixes.count(); ++i ) + Runner::self()->runMatchingTests(prefixes[i]); + } + else + { + QString suite = m_testerWidget->selectCombo()->currentText(); + QStringList tests; + RegistryIteratorType it(Runner::self()->registry()); + for ( ; it.current(); ++it ) + if ( QString(it.currentKey()).startsWith(suite) ) + tests.append(it.currentKey()); + + configureProgressBar(tests.count(), 0); + + for ( uint i = 0; i < tests.count(); ++i ) + Runner::self()->runTest(tests[i].local8Bit()); + } + + showDetails(m_testerWidget->resultList()->currentItem()); + } + + void RunnerGUI::showDetails(QListViewItem *item) + { + if ( item == 0L ) return; + + QString name = fullName(item); + if ( name.endsWith("()") ) name = fullName(item->parent()); + + Tester *tester = Runner::self()->registry().find(name.local8Bit()); + + if ( tester == 0L ) return; + + TestResults *res = 0L; + if ( tester->inherits("KUnitTest::SlotTester") ) + res = static_cast(tester)->results(item->text(g_nameColumn).local8Bit()); + else + res = tester->results(); + + if ( tester == 0L ) + m_testerWidget->details()->setText("No test found with name: " + fullName(item)); + else + { + QTextEdit *te = m_testerWidget->details(); + + te->clear(); + + te->append("Errors:
    "); + appendList(te, res->errorList()); + + te->append("

    Expected to fail:
    "); + appendList(te, res->xfailList()); + + te->append("

    Unexpected Success:
    "); + appendList(te, res->xpassList()); + + te->append("

    Success:
    "); + appendList(te, res->successList()); + + te->append("

    Skipped:
    "); + appendList(te, res->skipList()); + + te->append("

    Debug:
    "); + + te->append(res->debugInfo()); + + te->scrollToAnchor("errors"); + } + } + + void RunnerGUI::appendList(QTextEdit *te, const QStringList &list) + { + for ( uint i = 0; i < list.count(); ++i ) + te->append(list[i]); + } + + void RunnerGUI::doubleClickedOnDetails(int para, int /*pos*/) + { + static QRegExp reFileAndLine("^(.*)\\[([0-9]+)\\]:"); + + QString line = m_testerWidget->details()->text(para); + m_testerWidget->details()->setSelection(para, 0, para, line.length()-1); + + if ( reFileAndLine.search(line) != -1 ) + { + DCOPClient client; + client.attach(); + QByteArray data; + QDataStream arg(data, IO_WriteOnly); + bool ok; + arg << QString(reFileAndLine.cap(1)) << (reFileAndLine.cap(2).toInt(&ok) - 1); + client.send("kdevelop-*", "KDevPartController", "editDocument(QString,int)", data); + client.send("kdevelop-*", "MainWindow", "raise()", ""); + + client.detach(); + } + } +} + +#include "runnergui.moc" diff --git a/kunittest/runnergui.h b/kunittest/runnergui.h new file mode 100644 index 00000000..dbd03666 --- /dev/null +++ b/kunittest/runnergui.h @@ -0,0 +1,78 @@ +/** + * Copyright (C) 2005 Jeroen Wijnhout + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _KUNITTEST_TESTER_H_ +#define _KUNITTEST_TESTER_H_ + +#include +#include + +#include +#include + +#include + +class TesterWidget; +class QTextEdit; + +namespace KUnitTest +{ + class RunnerGUIDCOPImpl; + + class KDE_EXPORT RunnerGUI : public QHBox + { + Q_OBJECT + + public: + RunnerGUI(QWidget *parent); + ~RunnerGUI(); + + private slots: + void addTestResult(const char *name, Tester *test); + void addTester(const char *name, Tester *test); + void showDetails(QListViewItem *item); + void runSuite(); + void doubleClickedOnDetails(int para, int pos); + + private: + void reset(); + void configureProgressBar(int steps, int progress); + void fillResultsLabel(); + void appendList(QTextEdit *te, const QStringList &list); + + QListViewItem *getItem(const QString &name, QListViewItem *item = 0L); + void setItem(QListViewItem *item, const TestResults *res); + QString fullName(QListViewItem *item); + + void setSummary(QListViewItem *item, TestResults *res); + + TesterWidget *m_testerWidget; + + friend class RunnerGUIDCOPImpl; + RunnerGUIDCOPImpl *m_dcop; + }; +} + +#endif diff --git a/kunittest/testerwidget.ui b/kunittest/testerwidget.ui new file mode 100644 index 00000000..61f74337 --- /dev/null +++ b/kunittest/testerwidget.ui @@ -0,0 +1,197 @@ + +TesterWidget + + + TesterWidget + + + + 0 + 0 + 622 + 773 + + + + + 500 + 500 + + + + KUnitTester + + + + unnamed + + + + m_pshRun + + + Run + + + + + m_pbProgress + + + + + m_cbSelect + + + + 7 + 0 + 0 + 0 + + + + + + m_lbSelect + + + Select a suite or module: + + + + + m_lbResults + + + + + + + + + Test + + + true + + + true + + + + + Finished + + + true + + + true + + + + + Skipped + + + true + + + true + + + + + Failed + + + true + + + true + + + + + xFailed + + + true + + + true + + + + + Passed + + + true + + + true + + + + + xPassed + + + true + + + true + + + + m_lvResults + + + + 7 + 1 + 0 + 0 + + + + + 0 + 200 + + + + + + m_teDetails + + + + 600 + 200 + + + + true + + + + + + testerwidget.ui.h + + + run() + + + init() + resultList() + details() + progressBar() + resultsLabel() + selectCombo() + + + diff --git a/kunittest/testerwidget.ui.h b/kunittest/testerwidget.ui.h new file mode 100644 index 00000000..b895a357 --- /dev/null +++ b/kunittest/testerwidget.ui.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + + +void TesterWidget::init() +{ + connect(m_pshRun, SIGNAL(clicked()), this, SIGNAL(run())); +} + +QListView * TesterWidget::resultList() +{ + return m_lvResults; +} + + +QTextEdit * TesterWidget::details() +{ + return m_teDetails; +} + + +QProgressBar * TesterWidget::progressBar() +{ + return m_pbProgress; +} + + +QLabel * TesterWidget::resultsLabel() +{ + return m_lbResults; +} + + +QComboBox * TesterWidget::selectCombo() +{ + return m_cbSelect; +} diff --git a/poxml/GettextLexer.cpp b/poxml/GettextLexer.cpp new file mode 100644 index 00000000..cc768114 --- /dev/null +++ b/poxml/GettextLexer.cpp @@ -0,0 +1,550 @@ +/* $ANTLR 2.7.1: "gettext.g" -> "GettextLexer.cpp"$ */ +#include "GettextLexer.hpp" +#include "antlr/CharBuffer.hpp" +#include "antlr/TokenStreamException.hpp" +#include "antlr/TokenStreamIOException.hpp" +#include "antlr/TokenStreamRecognitionException.hpp" +#include "antlr/CharStreamException.hpp" +#include "antlr/CharStreamIOException.hpp" +#include "antlr/NoViableAltForCharException.hpp" + +#line 1 "gettext.g" + +#line 14 "GettextLexer.cpp" +GettextLexer::GettextLexer(ANTLR_USE_NAMESPACE(std)istream& in) + : ANTLR_USE_NAMESPACE(antlr)CharScanner(new ANTLR_USE_NAMESPACE(antlr)CharBuffer(in)) +{ + setCaseSensitive(true); + initLiterals(); +} + +GettextLexer::GettextLexer(ANTLR_USE_NAMESPACE(antlr)InputBuffer& ib) + : ANTLR_USE_NAMESPACE(antlr)CharScanner(ib) +{ + setCaseSensitive(true); + initLiterals(); +} + +GettextLexer::GettextLexer(const ANTLR_USE_NAMESPACE(antlr)LexerSharedInputState& state) + : ANTLR_USE_NAMESPACE(antlr)CharScanner(state) +{ + setCaseSensitive(true); + initLiterals(); +} + +void GettextLexer::initLiterals() +{ +} +bool GettextLexer::getCaseSensitiveLiterals() const +{ + return true; +} + +ANTLR_USE_NAMESPACE(antlr)RefToken GettextLexer::nextToken() +{ + ANTLR_USE_NAMESPACE(antlr)RefToken theRetToken; + for (;;) { + ANTLR_USE_NAMESPACE(antlr)RefToken theRetToken; + int _ttype = ANTLR_USE_NAMESPACE(antlr)Token::INVALID_TYPE; + resetText(); + try { // for char stream error handling + try { // for lexical error handling + switch ( LA(1)) { + case static_cast('\t'): + case static_cast('\n'): + case static_cast('\r'): + case static_cast(' '): + { + mWS(true); + theRetToken=_returnToken; + break; + } + case static_cast('['): + { + mL_BRACKET(true); + theRetToken=_returnToken; + break; + } + case static_cast(']'): + { + mR_BRACKET(true); + theRetToken=_returnToken; + break; + } + case static_cast('0'): + case static_cast('1'): + case static_cast('2'): + case static_cast('3'): + case static_cast('4'): + case static_cast('5'): + case static_cast('6'): + case static_cast('7'): + case static_cast('8'): + case static_cast('9'): + { + mT_INT(true); + theRetToken=_returnToken; + break; + } + case static_cast('#'): + { + mT_COMMENT(true); + theRetToken=_returnToken; + break; + } + case static_cast('m'): + { + mMSG_TAG(true); + theRetToken=_returnToken; + break; + } + case static_cast('"'): + { + mT_STRING(true); + theRetToken=_returnToken; + break; + } + default: + { + if (LA(1)==EOF_CHAR) {uponEOF(); _returnToken = makeToken(ANTLR_USE_NAMESPACE(antlr)Token::EOF_TYPE);} + else {throw ANTLR_USE_NAMESPACE(antlr)NoViableAltForCharException(LA(1), getFilename(), getLine());} + } + } + if ( !_returnToken ) goto tryAgain; // found SKIP token + _ttype = _returnToken->getType(); + _returnToken->setType(_ttype); + return _returnToken; + } + catch (ANTLR_USE_NAMESPACE(antlr)RecognitionException& e) { + throw ANTLR_USE_NAMESPACE(antlr)TokenStreamRecognitionException(e); + } + } + catch (ANTLR_USE_NAMESPACE(antlr)CharStreamIOException& csie) { + throw ANTLR_USE_NAMESPACE(antlr)TokenStreamIOException(csie.io); + } + catch (ANTLR_USE_NAMESPACE(antlr)CharStreamException& cse) { + throw ANTLR_USE_NAMESPACE(antlr)TokenStreamException(cse.getMessage()); + } +tryAgain:; + } +} + +void GettextLexer::mWS(bool _createToken) { + int _ttype; ANTLR_USE_NAMESPACE(antlr)RefToken _token; int _begin=text.length(); + _ttype = WS; + int _saveIndex; + + { + switch ( LA(1)) { + case static_cast(' '): + { + match(static_cast(' ')); + break; + } + case static_cast('\t'): + { + match(static_cast('\t')); + break; + } + case static_cast('\n'): + case static_cast('\r'): + { + { + switch ( LA(1)) { + case static_cast('\n'): + { + match(static_cast('\n')); + break; + } + case static_cast('\r'): + { + match("\r\n"); + break; + } + default: + { + throw ANTLR_USE_NAMESPACE(antlr)NoViableAltForCharException(LA(1), getFilename(), getLine()); + } + } + } +#line 110 "gettext.g" + newline(); +#line 173 "GettextLexer.cpp" + break; + } + default: + { + throw ANTLR_USE_NAMESPACE(antlr)NoViableAltForCharException(LA(1), getFilename(), getLine()); + } + } + } +#line 111 "gettext.g" + _ttype = ANTLR_USE_NAMESPACE(antlr)Token::SKIP; +#line 184 "GettextLexer.cpp" + if ( _createToken && _token==ANTLR_USE_NAMESPACE(antlr)nullToken && _ttype!=ANTLR_USE_NAMESPACE(antlr)Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void GettextLexer::mL_BRACKET(bool _createToken) { + int _ttype; ANTLR_USE_NAMESPACE(antlr)RefToken _token; int _begin=text.length(); + _ttype = L_BRACKET; + int _saveIndex; + + match(static_cast('[')); + if ( _createToken && _token==ANTLR_USE_NAMESPACE(antlr)nullToken && _ttype!=ANTLR_USE_NAMESPACE(antlr)Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void GettextLexer::mR_BRACKET(bool _createToken) { + int _ttype; ANTLR_USE_NAMESPACE(antlr)RefToken _token; int _begin=text.length(); + _ttype = R_BRACKET; + int _saveIndex; + + match(static_cast(']')); + if ( _createToken && _token==ANTLR_USE_NAMESPACE(antlr)nullToken && _ttype!=ANTLR_USE_NAMESPACE(antlr)Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void GettextLexer::mT_INT(bool _createToken) { + int _ttype; ANTLR_USE_NAMESPACE(antlr)RefToken _token; int _begin=text.length(); + _ttype = T_INT; + int _saveIndex; + + { + int _cnt26=0; + for (;;) { + if (((LA(1) >= static_cast('0') && LA(1) <= static_cast('9')))) { + matchRange(static_cast('0'),static_cast('9')); + } + else { + if ( _cnt26>=1 ) { goto _loop26; } else {throw ANTLR_USE_NAMESPACE(antlr)NoViableAltForCharException(LA(1), getFilename(), getLine());} + } + + _cnt26++; + } + _loop26:; + } + if ( _createToken && _token==ANTLR_USE_NAMESPACE(antlr)nullToken && _ttype!=ANTLR_USE_NAMESPACE(antlr)Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void GettextLexer::mT_COMMENT(bool _createToken) { + int _ttype; ANTLR_USE_NAMESPACE(antlr)RefToken _token; int _begin=text.length(); + _ttype = T_COMMENT; + int _saveIndex; + + match(static_cast('#')); + { + for (;;) { + if ((_tokenSet_0.member(LA(1)))) { + matchNot(static_cast('\n')); + } + else { + goto _loop29; + } + + } + _loop29:; + } + if ( _createToken && _token==ANTLR_USE_NAMESPACE(antlr)nullToken && _ttype!=ANTLR_USE_NAMESPACE(antlr)Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void GettextLexer::mMSG_TAG(bool _createToken) { + int _ttype; ANTLR_USE_NAMESPACE(antlr)RefToken _token; int _begin=text.length(); + _ttype = MSG_TAG; + int _saveIndex; + + match("msg"); + { + switch ( LA(1)) { + case static_cast('i'): + { + { + match("id"); + } + { + if ((LA(1)==static_cast('_'))) { + match("_plural"); +#line 126 "gettext.g" + _ttype = T_MSGID_PLURAL; +#line 292 "GettextLexer.cpp" + } + else { + match(""); +#line 125 "gettext.g" + _ttype = T_MSGID; +#line 298 "GettextLexer.cpp" + } + + } + break; + } + case static_cast('s'): + { + match("str"); +#line 128 "gettext.g" + _ttype = T_MSGSTR; +#line 309 "GettextLexer.cpp" + break; + } + default: + { + throw ANTLR_USE_NAMESPACE(antlr)NoViableAltForCharException(LA(1), getFilename(), getLine()); + } + } + } + if ( _createToken && _token==ANTLR_USE_NAMESPACE(antlr)nullToken && _ttype!=ANTLR_USE_NAMESPACE(antlr)Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void GettextLexer::mT_STRING(bool _createToken) { + int _ttype; ANTLR_USE_NAMESPACE(antlr)RefToken _token; int _begin=text.length(); + _ttype = T_STRING; + int _saveIndex; + + { + int _cnt43=0; + for (;;) { + if ((LA(1)==static_cast('"'))) { + _saveIndex=text.length(); + match(static_cast('"')); + text.erase(_saveIndex); + { + for (;;) { + if ((LA(1)==static_cast('\\'))) { + mESC(false); + } + else if ((_tokenSet_1.member(LA(1)))) { + matchNot(static_cast('"')); + } + else { + goto _loop37; + } + + } + _loop37:; + } + { + _saveIndex=text.length(); + match(static_cast('"')); + text.erase(_saveIndex); + { + for (;;) { + switch ( LA(1)) { + case static_cast(' '): + { + match(static_cast(' ')); + break; + } + case static_cast('t'): + { + match(static_cast('t')); + break; + } + default: + { + goto _loop40; + } + } + } + _loop40:; + } + _saveIndex=text.length(); + match(static_cast('\n')); + text.erase(_saveIndex); +#line 133 "gettext.g" + newline(); +#line 383 "GettextLexer.cpp" + { + for (;;) { + switch ( LA(1)) { + case static_cast(' '): + { + _saveIndex=text.length(); + match(static_cast(' ')); + text.erase(_saveIndex); + break; + } + case static_cast('\t'): + { + _saveIndex=text.length(); + match(static_cast('\t')); + text.erase(_saveIndex); + break; + } + default: + { + goto _loop42; + } + } + } + _loop42:; + } + } + } + else { + if ( _cnt43>=1 ) { goto _loop43; } else {throw ANTLR_USE_NAMESPACE(antlr)NoViableAltForCharException(LA(1), getFilename(), getLine());} + } + + _cnt43++; + } + _loop43:; + } + if ( _createToken && _token==ANTLR_USE_NAMESPACE(antlr)nullToken && _ttype!=ANTLR_USE_NAMESPACE(antlr)Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void GettextLexer::mESC(bool _createToken) { + int _ttype; ANTLR_USE_NAMESPACE(antlr)RefToken _token; int _begin=text.length(); + _ttype = ESC; + int _saveIndex; + + match(static_cast('\\')); + { + switch ( LA(1)) { + case static_cast('n'): + { + match(static_cast('n')); + break; + } + case static_cast('r'): + { + match(static_cast('r')); + break; + } + case static_cast('t'): + { + match(static_cast('t')); + break; + } + case static_cast('b'): + { + match(static_cast('b')); + break; + } + case static_cast('f'): + { + match(static_cast('f')); + break; + } + case static_cast('"'): + { + match(static_cast('"')); + break; + } + case static_cast('\''): + { + match(static_cast('\'')); + break; + } + case static_cast('\\'): + { + match(static_cast('\\')); + break; + } + case static_cast('0'): + case static_cast('1'): + case static_cast('2'): + case static_cast('3'): + { + { + matchRange(static_cast('0'),static_cast('3')); + } + { + if (((LA(1) >= static_cast('0') && LA(1) <= static_cast('9')))) { + { + matchRange(static_cast('0'),static_cast('9')); + } + { + if (((LA(1) >= static_cast('0') && LA(1) <= static_cast('9')))) { + matchRange(static_cast('0'),static_cast('9')); + } + else if (((LA(1) >= static_cast('\0') && LA(1) <= static_cast('\377')))) { + } + else { + throw ANTLR_USE_NAMESPACE(antlr)NoViableAltForCharException(LA(1), getFilename(), getLine()); + } + + } + } + else if (((LA(1) >= static_cast('\0') && LA(1) <= static_cast('\377')))) { + } + else { + throw ANTLR_USE_NAMESPACE(antlr)NoViableAltForCharException(LA(1), getFilename(), getLine()); + } + + } + break; + } + case static_cast('4'): + case static_cast('5'): + case static_cast('6'): + case static_cast('7'): + { + { + matchRange(static_cast('4'),static_cast('7')); + } + { + if (((LA(1) >= static_cast('0') && LA(1) <= static_cast('9')))) { + { + matchRange(static_cast('0'),static_cast('9')); + } + } + else if (((LA(1) >= static_cast('\0') && LA(1) <= static_cast('\377')))) { + } + else { + throw ANTLR_USE_NAMESPACE(antlr)NoViableAltForCharException(LA(1), getFilename(), getLine()); + } + + } + break; + } + default: + { + throw ANTLR_USE_NAMESPACE(antlr)NoViableAltForCharException(LA(1), getFilename(), getLine()); + } + } + } + if ( _createToken && _token==ANTLR_USE_NAMESPACE(antlr)nullToken && _ttype!=ANTLR_USE_NAMESPACE(antlr)Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + + +const unsigned long GettextLexer::_tokenSet_0_data_[] = { 4294966271UL, 4294967295UL, 4294967295UL, 4294967295UL, 4294967295UL, 4294967295UL, 4294967295UL, 4294967295UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL }; +const ANTLR_USE_NAMESPACE(antlr)BitSet GettextLexer::_tokenSet_0(_tokenSet_0_data_,16); +const unsigned long GettextLexer::_tokenSet_1_data_[] = { 4294967295UL, 4294967291UL, 4026531839UL, 4294967295UL, 4294967295UL, 4294967295UL, 4294967295UL, 4294967295UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL }; +const ANTLR_USE_NAMESPACE(antlr)BitSet GettextLexer::_tokenSet_1(_tokenSet_1_data_,16); + diff --git a/poxml/GettextLexer.hpp b/poxml/GettextLexer.hpp new file mode 100644 index 00000000..951ad423 --- /dev/null +++ b/poxml/GettextLexer.hpp @@ -0,0 +1,47 @@ +#ifndef INC_GettextLexer_hpp_ +#define INC_GettextLexer_hpp_ + +#line 2 "gettext.g" + +#include +using namespace std; +#include "parser.h" + +#line 11 "GettextLexer.hpp" +#include "antlr/config.hpp" +/* $ANTLR 2.7.1: "gettext.g" -> "GettextLexer.hpp"$ */ +#include "antlr/CommonToken.hpp" +#include "antlr/InputBuffer.hpp" +#include "antlr/BitSet.hpp" +#include "GettextParserTokenTypes.hpp" +#include "antlr/CharScanner.hpp" +class GettextLexer : public ANTLR_USE_NAMESPACE(antlr)CharScanner, public GettextParserTokenTypes + { +#line 1 "gettext.g" +#line 22 "GettextLexer.hpp" +private: + void initLiterals(); +public: + bool getCaseSensitiveLiterals() const; +public: + GettextLexer(ANTLR_USE_NAMESPACE(std)istream& in); + GettextLexer(ANTLR_USE_NAMESPACE(antlr)InputBuffer& ib); + GettextLexer(const ANTLR_USE_NAMESPACE(antlr)LexerSharedInputState& state); + ANTLR_USE_NAMESPACE(antlr)RefToken nextToken(); + public: void mWS(bool _createToken); + public: void mL_BRACKET(bool _createToken); + public: void mR_BRACKET(bool _createToken); + public: void mT_INT(bool _createToken); + public: void mT_COMMENT(bool _createToken); + public: void mMSG_TAG(bool _createToken); + public: void mT_STRING(bool _createToken); + protected: void mESC(bool _createToken); +private: + + static const unsigned long _tokenSet_0_data_[]; + static const ANTLR_USE_NAMESPACE(antlr)BitSet _tokenSet_0; + static const unsigned long _tokenSet_1_data_[]; + static const ANTLR_USE_NAMESPACE(antlr)BitSet _tokenSet_1; +}; + +#endif /*INC_GettextLexer_hpp_*/ diff --git a/poxml/GettextParser.cpp b/poxml/GettextParser.cpp new file mode 100644 index 00000000..90651eaa --- /dev/null +++ b/poxml/GettextParser.cpp @@ -0,0 +1,414 @@ +/* $ANTLR 2.7.1: "gettext.g" -> "GettextParser.cpp"$ */ +#include "GettextParser.hpp" +#include "antlr/NoViableAltException.hpp" +#include "antlr/SemanticException.hpp" +#line 12 "gettext.g" + +#include +#include "GettextLexer.hpp" +#include "GettextParser.hpp" +#include "antlr/AST.hpp" +#include "antlr/CommonAST.hpp" + +/* +int main() +{ + ANTLR_USING_NAMESPACE(std) + ANTLR_USING_NAMESPACE(antlr) + try { + GettextLexer lexer(cin); + GettextParser parser(lexer); + parser.file(); + + } catch(exception& e) { + cerr << "exception: " << e.what() << endl; + } +} +*/ + +#line 30 "GettextParser.cpp" +GettextParser::GettextParser(ANTLR_USE_NAMESPACE(antlr)TokenBuffer& tokenBuf, int k) +: ANTLR_USE_NAMESPACE(antlr)LLkParser(tokenBuf,k) +{ + setTokenNames(_tokenNames); +} + +GettextParser::GettextParser(ANTLR_USE_NAMESPACE(antlr)TokenBuffer& tokenBuf) +: ANTLR_USE_NAMESPACE(antlr)LLkParser(tokenBuf,1) +{ + setTokenNames(_tokenNames); +} + +GettextParser::GettextParser(ANTLR_USE_NAMESPACE(antlr)TokenStream& lexer, int k) +: ANTLR_USE_NAMESPACE(antlr)LLkParser(lexer,k) +{ + setTokenNames(_tokenNames); +} + +GettextParser::GettextParser(ANTLR_USE_NAMESPACE(antlr)TokenStream& lexer) +: ANTLR_USE_NAMESPACE(antlr)LLkParser(lexer,1) +{ + setTokenNames(_tokenNames); +} + +GettextParser::GettextParser(const ANTLR_USE_NAMESPACE(antlr)ParserSharedInputState& state) +: ANTLR_USE_NAMESPACE(antlr)LLkParser(state,1) +{ + setTokenNames(_tokenNames); +} + + MsgList GettextParser::file() { +#line 43 "gettext.g" + MsgList ml ; +#line 64 "GettextParser.cpp" +#line 43 "gettext.g" + + string c, mi, ms; + MsgBlock mb; + MsgList ml2; + +#line 71 "GettextParser.cpp" + + try { // for error handling + bool synPredMatched3 = false; + if (((LA(1)==T_MSGID||LA(1)==T_COMMENT))) { + int _m3 = mark(); + synPredMatched3 = true; + inputState->guessing++; + try { + { + comment(); + match(T_MSGID); + } + } + catch (ANTLR_USE_NAMESPACE(antlr)RecognitionException& pe) { + synPredMatched3 = false; + } + rewind(_m3); + inputState->guessing--; + } + if ( synPredMatched3 ) { + { + mb=file_block(); + ml2=file(); + if ( inputState->guessing==0 ) { +#line 49 "gettext.g" + ml = ml2; ml.append(mb); +#line 98 "GettextParser.cpp" + } + } + } + else { + bool synPredMatched6 = false; + if (((LA(1)==ANTLR_USE_NAMESPACE(antlr)Token::EOF_TYPE||LA(1)==T_COMMENT))) { + int _m6 = mark(); + synPredMatched6 = true; + inputState->guessing++; + try { + { + comment(); + match(ANTLR_USE_NAMESPACE(antlr)Token::EOF_TYPE); + } + } + catch (ANTLR_USE_NAMESPACE(antlr)RecognitionException& pe) { + synPredMatched6 = false; + } + rewind(_m6); + inputState->guessing--; + } + if ( synPredMatched6 ) { + c=comment(); + if ( inputState->guessing==0 ) { +#line 50 "gettext.g" + (void)c; +#line 125 "GettextParser.cpp" + } + } + else { + throw ANTLR_USE_NAMESPACE(antlr)NoViableAltException(LT(1), getFilename()); + } + } + } + catch (ANTLR_USE_NAMESPACE(antlr)RecognitionException& ex) { + if( inputState->guessing == 0 ) { + reportError(ex); + consume(); + consumeUntil(_tokenSet_0); + } else { + throw ex; + } + } + return ml ; +} + +string GettextParser::comment() { +#line 76 "gettext.g" + string s; +#line 148 "GettextParser.cpp" + ANTLR_USE_NAMESPACE(antlr)RefToken c = ANTLR_USE_NAMESPACE(antlr)nullToken; +#line 76 "gettext.g" + + string r; + +#line 154 "GettextParser.cpp" + + try { // for error handling + if ((LA(1)==T_COMMENT)) { + { + c = LT(1); + match(T_COMMENT); + r=comment(); + if ( inputState->guessing==0 ) { +#line 80 "gettext.g" + s = c->getText() + r; +#line 165 "GettextParser.cpp" + } + } + } + else if ((LA(1)==ANTLR_USE_NAMESPACE(antlr)Token::EOF_TYPE||LA(1)==T_MSGID)) { + } + else { + throw ANTLR_USE_NAMESPACE(antlr)NoViableAltException(LT(1), getFilename()); + } + + } + catch (ANTLR_USE_NAMESPACE(antlr)RecognitionException& ex) { + if( inputState->guessing == 0 ) { + reportError(ex); + consume(); + consumeUntil(_tokenSet_1); + } else { + throw ex; + } + } + return s; +} + + MsgBlock GettextParser::file_block() { +#line 53 "gettext.g" + MsgBlock mb ; +#line 191 "GettextParser.cpp" +#line 53 "gettext.g" + + string c, mi, mip, ms; + +#line 196 "GettextParser.cpp" + + try { // for error handling + c=comment(); + mi=msgid(); + { + if ((LA(1)==T_MSGSTR)) { + { + ms=msgstr(); + if ( inputState->guessing==0 ) { +#line 59 "gettext.g" + + mb.comment = QString::fromUtf8(c.c_str()); + mb.msgid = QString::fromUtf8(mi.c_str()); + mb.msgstr = QString::fromUtf8(ms.c_str()); + +#line 212 "GettextParser.cpp" + } + } + } + else if ((LA(1)==T_MSGID_PLURAL)) { + { + mip=msgid_plural(); + ms=msgstr_plural(); + if ( inputState->guessing==0 ) { +#line 66 "gettext.g" + + mb.comment = QString::fromUtf8(c.c_str()); + mb.msgid = QString::fromUtf8(mi.c_str()); + mb.msgid_plural = QString::fromUtf8(mip.c_str()); + mb.msgstr = QString::fromUtf8(ms.c_str()); + +#line 228 "GettextParser.cpp" + } + } + } + else { + throw ANTLR_USE_NAMESPACE(antlr)NoViableAltException(LT(1), getFilename()); + } + + } + } + catch (ANTLR_USE_NAMESPACE(antlr)RecognitionException& ex) { + if( inputState->guessing == 0 ) { + reportError(ex); + consume(); + consumeUntil(_tokenSet_2); + } else { + throw ex; + } + } + return mb ; +} + +string GettextParser::msgid() { +#line 84 "gettext.g" + string s; +#line 253 "GettextParser.cpp" + ANTLR_USE_NAMESPACE(antlr)RefToken t = ANTLR_USE_NAMESPACE(antlr)nullToken; + + try { // for error handling + match(T_MSGID); + t = LT(1); + match(T_STRING); + if ( inputState->guessing==0 ) { +#line 85 "gettext.g" + s = t->getText(); +#line 263 "GettextParser.cpp" + } + } + catch (ANTLR_USE_NAMESPACE(antlr)RecognitionException& ex) { + if( inputState->guessing == 0 ) { + reportError(ex); + consume(); + consumeUntil(_tokenSet_3); + } else { + throw ex; + } + } + return s; +} + +string GettextParser::msgstr() { +#line 92 "gettext.g" + string s; +#line 281 "GettextParser.cpp" + ANTLR_USE_NAMESPACE(antlr)RefToken t = ANTLR_USE_NAMESPACE(antlr)nullToken; + + try { // for error handling + match(T_MSGSTR); + t = LT(1); + match(T_STRING); + if ( inputState->guessing==0 ) { +#line 93 "gettext.g" + s = t->getText(); +#line 291 "GettextParser.cpp" + } + } + catch (ANTLR_USE_NAMESPACE(antlr)RecognitionException& ex) { + if( inputState->guessing == 0 ) { + reportError(ex); + consume(); + consumeUntil(_tokenSet_2); + } else { + throw ex; + } + } + return s; +} + +string GettextParser::msgid_plural() { +#line 88 "gettext.g" + string s; +#line 309 "GettextParser.cpp" + ANTLR_USE_NAMESPACE(antlr)RefToken t = ANTLR_USE_NAMESPACE(antlr)nullToken; + + try { // for error handling + match(T_MSGID_PLURAL); + t = LT(1); + match(T_STRING); + if ( inputState->guessing==0 ) { +#line 89 "gettext.g" + s = t->getText(); +#line 319 "GettextParser.cpp" + } + } + catch (ANTLR_USE_NAMESPACE(antlr)RecognitionException& ex) { + if( inputState->guessing == 0 ) { + reportError(ex); + consume(); + consumeUntil(_tokenSet_4); + } else { + throw ex; + } + } + return s; +} + +string GettextParser::msgstr_plural() { +#line 96 "gettext.g" + string s; +#line 337 "GettextParser.cpp" + ANTLR_USE_NAMESPACE(antlr)RefToken n = ANTLR_USE_NAMESPACE(antlr)nullToken; + ANTLR_USE_NAMESPACE(antlr)RefToken t = ANTLR_USE_NAMESPACE(antlr)nullToken; + + try { // for error handling + { + int _cnt18=0; + for (;;) { + if ((LA(1)==T_MSGSTR)) { + match(T_MSGSTR); + match(L_BRACKET); + n = LT(1); + match(T_INT); + match(R_BRACKET); + t = LT(1); + match(T_STRING); + if ( inputState->guessing==0 ) { +#line 98 "gettext.g" + s = t->getText(); +#line 356 "GettextParser.cpp" + } + } + else { + if ( _cnt18>=1 ) { goto _loop18; } else {throw ANTLR_USE_NAMESPACE(antlr)NoViableAltException(LT(1), getFilename());} + } + + _cnt18++; + } + _loop18:; + } + } + catch (ANTLR_USE_NAMESPACE(antlr)RecognitionException& ex) { + if( inputState->guessing == 0 ) { + reportError(ex); + consume(); + consumeUntil(_tokenSet_2); + } else { + throw ex; + } + } + return s; +} + +const char* GettextParser::_tokenNames[] = { + "<0>", + "EOF", + "<2>", + "NULL_TREE_LOOKAHEAD", + "T_MSGID", + "T_COMMENT", + "T_STRING", + "T_MSGID_PLURAL", + "T_MSGSTR", + "L_BRACKET", + "T_INT", + "R_BRACKET", + "WS", + "MSG_TAG", + "ESC", + 0 +}; + +const unsigned long GettextParser::_tokenSet_0_data_[] = { 2UL, 0UL, 0UL, 0UL }; +// EOF +const ANTLR_USE_NAMESPACE(antlr)BitSet GettextParser::_tokenSet_0(_tokenSet_0_data_,4); +const unsigned long GettextParser::_tokenSet_1_data_[] = { 18UL, 0UL, 0UL, 0UL }; +// EOF T_MSGID +const ANTLR_USE_NAMESPACE(antlr)BitSet GettextParser::_tokenSet_1(_tokenSet_1_data_,4); +const unsigned long GettextParser::_tokenSet_2_data_[] = { 50UL, 0UL, 0UL, 0UL }; +// EOF T_MSGID T_COMMENT +const ANTLR_USE_NAMESPACE(antlr)BitSet GettextParser::_tokenSet_2(_tokenSet_2_data_,4); +const unsigned long GettextParser::_tokenSet_3_data_[] = { 384UL, 0UL, 0UL, 0UL }; +// T_MSGID_PLURAL T_MSGSTR +const ANTLR_USE_NAMESPACE(antlr)BitSet GettextParser::_tokenSet_3(_tokenSet_3_data_,4); +const unsigned long GettextParser::_tokenSet_4_data_[] = { 256UL, 0UL, 0UL, 0UL }; +// T_MSGSTR +const ANTLR_USE_NAMESPACE(antlr)BitSet GettextParser::_tokenSet_4(_tokenSet_4_data_,4); + + diff --git a/poxml/GettextParser.hpp b/poxml/GettextParser.hpp new file mode 100644 index 00000000..46b2b137 --- /dev/null +++ b/poxml/GettextParser.hpp @@ -0,0 +1,53 @@ +#ifndef INC_GettextParser_hpp_ +#define INC_GettextParser_hpp_ + +#line 2 "gettext.g" + +#include +using namespace std; +#include "parser.h" + +#line 11 "GettextParser.hpp" +#include "antlr/config.hpp" +/* $ANTLR 2.7.1: "gettext.g" -> "GettextParser.hpp"$ */ +#include "antlr/TokenStream.hpp" +#include "antlr/TokenBuffer.hpp" +#include "GettextParserTokenTypes.hpp" +#include "antlr/LLkParser.hpp" + +class GettextParser : public ANTLR_USE_NAMESPACE(antlr)LLkParser, public GettextParserTokenTypes + { +#line 1 "gettext.g" +#line 22 "GettextParser.hpp" +protected: + GettextParser(ANTLR_USE_NAMESPACE(antlr)TokenBuffer& tokenBuf, int k); +public: + GettextParser(ANTLR_USE_NAMESPACE(antlr)TokenBuffer& tokenBuf); +protected: + GettextParser(ANTLR_USE_NAMESPACE(antlr)TokenStream& lexer, int k); +public: + GettextParser(ANTLR_USE_NAMESPACE(antlr)TokenStream& lexer); + GettextParser(const ANTLR_USE_NAMESPACE(antlr)ParserSharedInputState& state); + public: MsgList file(); + public: string comment(); + public: MsgBlock file_block(); + public: string msgid(); + public: string msgstr(); + public: string msgid_plural(); + public: string msgstr_plural(); +private: + static const char* _tokenNames[]; + + static const unsigned long _tokenSet_0_data_[]; + static const ANTLR_USE_NAMESPACE(antlr)BitSet _tokenSet_0; + static const unsigned long _tokenSet_1_data_[]; + static const ANTLR_USE_NAMESPACE(antlr)BitSet _tokenSet_1; + static const unsigned long _tokenSet_2_data_[]; + static const ANTLR_USE_NAMESPACE(antlr)BitSet _tokenSet_2; + static const unsigned long _tokenSet_3_data_[]; + static const ANTLR_USE_NAMESPACE(antlr)BitSet _tokenSet_3; + static const unsigned long _tokenSet_4_data_[]; + static const ANTLR_USE_NAMESPACE(antlr)BitSet _tokenSet_4; +}; + +#endif /*INC_GettextParser_hpp_*/ diff --git a/poxml/GettextParserTokenTypes.hpp b/poxml/GettextParserTokenTypes.hpp new file mode 100644 index 00000000..05fd7408 --- /dev/null +++ b/poxml/GettextParserTokenTypes.hpp @@ -0,0 +1,22 @@ +#ifndef INC_GettextParserTokenTypes_hpp_ +#define INC_GettextParserTokenTypes_hpp_ + +/* $ANTLR 2.7.1: "gettext.g" -> "GettextParserTokenTypes.hpp"$ */ +struct GettextParserTokenTypes { + enum { + EOF_ = 1, + T_MSGID = 4, + T_COMMENT = 5, + T_STRING = 6, + T_MSGID_PLURAL = 7, + T_MSGSTR = 8, + L_BRACKET = 9, + T_INT = 10, + R_BRACKET = 11, + WS = 12, + MSG_TAG = 13, + ESC = 14, + NULL_TREE_LOOKAHEAD = 3 + }; +}; +#endif /*INC_GettextParserTokenTypes_hpp_*/ diff --git a/poxml/GettextParserTokenTypes.txt b/poxml/GettextParserTokenTypes.txt new file mode 100644 index 00000000..083e90e3 --- /dev/null +++ b/poxml/GettextParserTokenTypes.txt @@ -0,0 +1,13 @@ +// $ANTLR 2.7.1: gettext.g -> GettextParserTokenTypes.txt$ +GettextParser // output token vocab name +T_MSGID=4 +T_COMMENT=5 +T_STRING=6 +T_MSGID_PLURAL=7 +T_MSGSTR=8 +L_BRACKET=9 +T_INT=10 +R_BRACKET=11 +WS=12 +MSG_TAG=13 +ESC=14 diff --git a/poxml/Makefile.am b/poxml/Makefile.am new file mode 100644 index 00000000..bef54179 --- /dev/null +++ b/poxml/Makefile.am @@ -0,0 +1,48 @@ + +bin_PROGRAMS = split2po xml2pot po2xml swappo transxx + +INCLUDES = -I$(srcdir)/antlr $(all_includes) +KDE_CXXFLAGS = $(USE_EXCEPTIONS) + +SUBDIRS = antlr + +split2po_SOURCES = split.cpp parser.cpp +split2po_LDFLAGS = $(all_libraries) $(KDE_RPATH) +split2po_LDADD = $(LIB_QT) + +xml2pot_SOURCES = xml2pot.cpp parser.cpp +xml2pot_LDFLAGS = $(all_libraries) $(KDE_RPATH) +xml2pot_LDADD = $(LIB_QT) + +po2xml_SOURCES = GettextLexer.cpp GettextParser.cpp po2xml.cpp parser.cpp +po2xml_LDFLAGS = $(all_libraries) $(KDE_RPATH) +po2xml_LDADD = antlr/src/libantlr.la $(LIB_QT) + +swappo_SOURCES = GettextLexer.cpp GettextParser.cpp swappo.cpp parser.cpp +swappo_LDFLAGS = $(all_libraries) $(KDE_RPATH) +swappo_LDADD = antlr/src/libantlr.la $(LIB_QT) + +transxx_SOURCES = GettextLexer.cpp GettextParser.cpp transxx.cpp parser.cpp +transxx_LDFLAGS = $(all_libraries) $(KDE_RPATH) +transxx_LDADD = antlr/src/libantlr.la $(LIB_QT) + +parser: + cd $(srcdir) && java antlr.Tool gettext.g + +SUFFIXES = .pot .po .xml .txml + +lauri.pot: xml2pot lauri.xml + checkXML $(srcdir)/lauri.xml + ./xml2pot $(srcdir)/lauri.xml > lauri.pot + msgmerge -o lauri.pot lauri.pot lauri.pot + +$(srcdir)/lauri.po: lauri.pot + msgmerge -o $(srcdir)/lauri.po $(srcdir)/lauri.po lauri.pot + msgfmt --statistics $(srcdir)/lauri.po -o /dev/null + +lauri_de.xml: po2xml $(srcdir)/lauri.po $(srcdir)/lauri.xml + ./po2xml $(srcdir)/lauri.xml $(srcdir)/lauri.po | \ + sed -e "s, lauri_de.xml + +test: lauri_de.xml + checkXML lauri_de.xml diff --git a/poxml/antlr/AUTHORS b/poxml/antlr/AUTHORS new file mode 100644 index 00000000..7bdc4852 --- /dev/null +++ b/poxml/antlr/AUTHORS @@ -0,0 +1,2 @@ +Author: + Peter Wells diff --git a/poxml/antlr/COPYING b/poxml/antlr/COPYING new file mode 100644 index 00000000..ce9ec595 --- /dev/null +++ b/poxml/antlr/COPYING @@ -0,0 +1,32 @@ + +SOFTWARE RIGHTS + +ANTLR MageLang Institute, 1989-1999 +http://www.ANTLR.org + +We reserve no legal rights to the ANTLR--it is fully in the +public domain. An individual or company may do whatever +they wish with source code distributed with ANTLR or the +code generated by ANTLR, including the incorporation of +ANTLR, or its output, into commerical software. + +We encourage users to develop software with ANTLR. However, +we do ask that credit is given to us for developing +ANTLR. By "credit", we mean that if you use ANTLR or +incorporate any source code into one of your programs +(commercial product, research project, or otherwise) that +you acknowledge this fact somewhere in the documentation, +research report, etc... If you like ANTLR and have +developed a nice tool with the output, please mention that +you developed it using ANTLR. In addition, we ask that the +headers remain intact in our source code. As long as these +guidelines are kept, we expect to continue enhancing this +system and expect to make other tools available as they are +completed. + +The primary ANTLR guy: + +Terence Parr +MageLang Institute; http://www.MageLang.com +parrt@jguru.com +parrt@magelang.com diff --git a/poxml/antlr/ChangeLog b/poxml/antlr/ChangeLog new file mode 100644 index 00000000..735046e5 --- /dev/null +++ b/poxml/antlr/ChangeLog @@ -0,0 +1,293 @@ +Not 100% complete. Changes from develtree are not listed yet. + +Change 400 on 2000/09/27 by klaren@klaren.hawking.main + + Made little TCL script to pretty print a ChangeLog with C++ stuff. + + +Change 399 on 2000/09/27 by klaren@klaren.hawking.main + + Fixed generating too many ASTNULL checks in wrong places. + + +Change 397 on 2000/09/27 by klaren@klaren.hawking.main + + Some *UGLY* fixes for the last typecasting problems in Cpp codegen. It + now works. In 2.7.2 or later I'll fix this in a nice way. + + +Change 397 on 2000/09/27 by klaren@klaren.hawking.main + + Some *UGLY* fixes for the last typecasting problems in Cpp codegen. It + now works. In 2.7.2 or later I'll fix this in a nice way. + + +Change 394 on 2000/09/26 by klaren@klaren.hawking.main + + Prefixed Unicode optimization checks with a ASTNULL check. + + +Change 393 on 2000/09/25 by klaren@klaren.hawking.main + + Bumped up the version no from 2.7.1a4 to 2.7.1. + + +Change 380 on 2000/09/24 by parrt@parrt.foggy + + integrating ric's stuff into main + + +Change 380 on 2000/09/24 by parrt@parrt.foggy + + integrating ric's stuff into main + + +Change 380 on 2000/09/24 by parrt@parrt.foggy + + integrating ric's stuff into main + + +Change 348 on 2000/09/07 by klaren@klaren.hawking.main + + Small improvement in constructor of CommonAST. + + +Change 344 on 2000/09/06 by klaren@klaren.hawking.main + + Fixed missing namespace in generated TreeParsers as reported by Ross + Bencina. + + +Change 341 on 2000/09/06 by klaren@klaren.hawking.main + + Miniscule fix for Borland C++Builder 4.0/C++ 5.4. (extra parens) + + +Change 317 on 2000/08/22 by klaren@klaren.hawking.main + + Updated changelog for a5 (or was it 2.7.1) release.. + + +Change 316 on 2000/08/22 by klaren@klaren.hawking.main + + All kinds of small Makefile/configure tweaks. All gcc-isms should be + gone now. + + +Change 309 on 2000/08/15 by klaren@klaren.hawking.main + + Integrate bugfixes from klaren.dev to MismatchedChar/TokenException. + + +Change 297 on 2000/08/07 by klaren@klaren.kronecker.main + + Fixes for namespace/namespaceAntlr/namespaceStd/genHashLines options. + + +Change 296 on 2000/08/07 by klaren@klaren.kronecker.main + + Virtualized all functions that someone should want to override. Probably + necessary for heteroAST stuff. + + +Change 291 on 2000/08/07 by klaren@klaren.kronecker.main + + Some tweaks to configure.in and Makefile.am's. Fix for CXXFLAGS being + set incorrectly when not using gcc. + + +Change 290 on 2000/08/05 by klaren@klaren.kronecker.main + + Updated prototype of toLower to definition in cpp file. It seems I + messed them up a while back. + + +Change 289 on 2000/08/05 by klaren@klaren.kronecker.main + + Added namespace macro to out_of_range exception. + + +Change 288 on 2000/07/28 by parrt@parrt.foggy + + re-added toLower return type fix + + +Change 285 on 2000/07/19 by klaren@klaren.kronecker.main + + Fixed thinko. + + +Change 284 on 2000/07/19 by klaren@klaren.kronecker.main + + Dumped output of p4 changes -l into it... + + +Change 283 on 2000/07/19 by klaren@klaren.kronecker.main + + Fix for bug found by Michael Ebner. Bitset size was not increased in add + method. + + +Change 280 on 2000/07/19 by klaren@klaren.kronecker.main + + Made namespaceAntlr, namespaceStd and genHashlines options file-level + options. Removed nameSpace member from Tool class all is now handled in + CppCodegenerator.java. + + +Change 276 on 2000/07/18 by klaren@klaren.kronecker.main + + C++ Changes for the indented traceXXXX output as invented by Monty Zukowski + + +Change 275 on 2000/07/18 by klaren@klaren.kronecker.main + + Added missing initializer in generated code for TreeParser + + +Change 272 on 2000/07/17 by klaren@klaren.kronecker.main + + Another workspace for MSVC6 has support for dll's (for version 2.6.1). + + +Change 271 on 2000/07/17 by klaren@klaren.kronecker.main + + New autoconf/automake stuff for the C++ support library. + + +Change 270 on 2000/07/17 by klaren@klaren.kronecker.main + + Fixed error within the NO_STATIC_CONSTS #ifdef + + +Change 269 on 2000/07/17 by klaren@klaren.kronecker.main + + Move C++ files to lib/cpp/src as first step for autoconf setup + + +Change 268 on 2000/07/17 by klaren@klaren.kronecker.main + + Add contrib dir and Microsoft Visual C++ 6.0 projects supplied by John + Millaway + + +Change 260 on 2000/07/14 by klaren@klaren.kronecker.main + + Fixed crashbugs/typos in constructors of Mismatched[Token|Char]Exception + + +Change 258 on 2000/07/10 by parrt@parrt.foggy + + fixes per klaren + + +Change 258 on 2000/07/10 by parrt@parrt.foggy + + fixes per klaren + + +Change 248 on 2000/07/04 by parrt@parrt.foggy + + Ric Klaren's changes to C++ lib + + +Change 247 on 2000/07/04 by parrt@parrt.foggy + + Ric Klaren's changes for namespaces + + +Change 239 on 2000/06/03 by parrt@parrt.foggy + + adjusted so it works; header actions got converted to Token objects from + Strings; lots of cast problems and then null ptr exceptions. + +Change 235 on 2000/05/31 by pete@pete.linux + + More changes to support #line generation in C++ (from Ric Klaren) + +Change 220 on 2000/05/29 by parrt@parrt.foggy + + changed char to int for toLower + + +Change 219 on 2000/05/28 by pete@pete.linux + + Mirroring Java changes + + +Change 218 on 2000/05/28 by pete@pete.linux + + Cleaned up the #line generator a little. + + +Change 211 on 2000/05/27 by parrt@parrt.foggy + + had same bug as JavaCodeGenerator related to ~(A|B) + + +Change 205 on 2000/05/24 by pete@pete.linux + + Add support for Metrowerks Codewarrior + + +Change 203 on 2000/05/22 by pete@pete.linux + + Fix for multithreading from Jan Mikkelsen + + +Change 202 on 2000/05/21 by pete@pete.linux + + Merged in some fixes from Ric Klaren for tracing TreeParsers, cleaner + namespace code, and #line generation. + + +Change 202 on 2000/05/21 by pete@pete.linux + + Merged in some fixes from Ric Klaren for tracing TreeParsers, cleaner + namespace code, and #line generation. + +Change 201 on 2000/05/21 by pete@pete.linux + + Added destructors with empty throw specs, as suggested by Dan Field. + + +Change 200 on 2000/05/21 by pete@pete.linux + + Various performance improvements, mostly from Eric Dumas. + + +Change 183 on 2000/02/08 by pete@pete.linux + + Added support for Sun CC 5.0 (from Michael Schmitt) + + +Change 182 on 2000/02/08 by pete@pete.linux + + Fix a couple of minor problems with C++ generation (noted by Michael + Schmitt) + +Change 132 on 2000/01/18 by parrt@parrt.foggy + + setting type to ktext for everything + + +Change 132 on 2000/01/18 by parrt@parrt.foggy + + setting type to ktext for everything + + +Change 131 on 2000/01/18 by parrt@parrt.foggy + + from dev back to main + + +Change 131 on 2000/01/18 by parrt@parrt.foggy + + from dev back to main + + +Change 1 on 1999/12/13 by parrt@parrt.foggy + + adding 2.6.0 from antlr site as initial main line + + diff --git a/poxml/antlr/INSTALL b/poxml/antlr/INSTALL new file mode 100644 index 00000000..30dd4d49 --- /dev/null +++ b/poxml/antlr/INSTALL @@ -0,0 +1,183 @@ +Basic Installation +================== + + These are generic installation instructions. Check out the README for +additional info. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/poxml/antlr/Makefile.am b/poxml/antlr/Makefile.am new file mode 100644 index 00000000..be459d64 --- /dev/null +++ b/poxml/antlr/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = src antlr + diff --git a/poxml/antlr/README b/poxml/antlr/README new file mode 100644 index 00000000..6ca0913b --- /dev/null +++ b/poxml/antlr/README @@ -0,0 +1,72 @@ +C++ support libraries + +Original GNU autconf stuff contributed by Braden N. McDaniel. Slightly +hacked up by me (Ric Klaren (klaren@cs.utwente.nl)) for who it's the first +autoconf/automake/aclocal stuff ever, so suggestions additions welcome. + +HOW TO INSTALL + +In theory do: + +./configure --prefix= +make +make install + +Now libantlr.a should reside under /lib/libantlr.a and the +includes should be at /include/antlr. + +NOTE: this only installs the C++ library and header files. + +In the contrib directory a dsp/dsw project for Microsoft Visual C++ can be +found. + +In general this library needs runtime typing information (RTTI) make sure +you enable this in whatever compiler you are using. + +SUPPORTED COMPILERS + +Pasted from the FAQ entry on: http://www.jguru.com/jguru/faq/view.jsp?EID=121 + +Compiler OS Version +------------------ --------------------- ---------- +Sun Workshop 4.2 Solaris 2.6, 7 2.7.1a2 +Sun Workshop 5.0 Solaris 2.7 2.7.1a2 +Sun Workshop 6.0 Solaris 2.7 2.7.1a2 +egcs-1.1.2 Solaris 2.6,7 2.7.1a2 +egcs-1.1.2 Linux 2.2, Solaris 2.6 2.7.1a2 +gcc-2.95.2 Linux 2.2, Solaris 2.6,7 2.7.1a2 +gcc-2.96 (20000527) Solaris 2.6 2.7.1a2 +aCC A.01.21 HP-UX 10.20 2.7.0 no! +Visual C++ 6.0 PC 2.7.1a2 (warnings) +Intel C++ 4.0 NT 4.0 2.7.0 +Borland 5.0 NT 4.0 2.7.0 + +IT DOESN'T WORK!? + +Check out the faq: http://www.jguru.com/jguru/faq/view.jsp?EID=120 + +The text of that entry (by Peter Wells): + +The ANTLR code uses some relatively new features of C++ which not all +compilers support yet (such as namespaces, and new style standard headers). + +There is work currently in progress to provide a compatibility mode for +ANTLR, to enable older compilers to handle this. + +At the moment, you may be able to work around the problem with a few nasty +tricks: + +Try creating some header files like 'iostream' just containing: + +#include + +and compile with an option to define away the word 'std', such as + +CC .... -Dstd= .... + +Also in the antlr subdirectory there's a file config.hpp. Tweak this one to +enable/disable the different bells and whistles used in the rest of the code. +Don't forget to submit those changes back to us (along with compiler info) +so we can incorporate them in our next release! + +Thanks! diff --git a/poxml/antlr/TODO b/poxml/antlr/TODO new file mode 100644 index 00000000..51d104c3 --- /dev/null +++ b/poxml/antlr/TODO @@ -0,0 +1,34 @@ +* Improve configure scripts => KICK OUT automake! + +* Add allocators to the objects + +* Look more at exception handling + +* TreeParser.cpp around line 76 the MismatchedTokenException here does not + use ttype to improve it's errormessage. Would require changing a bit in + MismatchedTokenException.cpp + +* On Thu, Sep 21, 2000 at 12:33:48AM -0700, John Lambert wrote: + > 1) The literal EOF is not defined and causes the define of EOF_CHAR in + > CharScanner.hpp to fail. + + ANTLR with STL Port. Changing the EOF define to char_traits::eof() + breaks things for gcc-2.95.2. Fix this in next release portably. + http://www.egroups.com/message/antlr-interest/2520 + +* John Millaway requested some mechanism to add code to the constructor + of the parser/lexer/treewalker. This can be usefull. + http://www.egroups.com/message/antlr-interest/2501 + +* Fix heterogeneous AST stuff. It boils down to adding a method to AST + types that knows how to duplicate the sucker. Atm duptree cannot work + because of this. Knowing one factory is not enough. Also look at having + to set the astfactory by hand (this is not 100% necessary). + http://www.egroups.com/message/antlr-interest/2496 + +* Look at messageLog stuff Ross Bencina proposed. Looks good at first glance. + http://www.egroups.com/message/antlr-interest/2555 + +* Add RW_STL & CC 4.2 patch from Ulrich Teichert: + See my mailbox.. and these comments from Ross Bencina: + http://www.egroups.com/message/antlr-interest/2494 diff --git a/poxml/antlr/antlr/ANTLRException.hpp b/poxml/antlr/antlr/ANTLRException.hpp new file mode 100644 index 00000000..efbe0d7f --- /dev/null +++ b/poxml/antlr/antlr/ANTLRException.hpp @@ -0,0 +1,60 @@ +#ifndef INC_ANTLRException_hpp__ +#define INC_ANTLRException_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include +#include + +ANTLR_BEGIN_NAMESPACE(antlr) + +class ANTLRException : public ANTLR_USE_NAMESPACE(std)exception { +private: + ANTLR_USE_NAMESPACE(std)string text; + +public: + ANTLRException(); + ANTLRException(const ANTLR_USE_NAMESPACE(std)string& s); + virtual ~ANTLRException() throw(); + + virtual ANTLR_USE_NAMESPACE(std)string toString() const; + + virtual ANTLR_USE_NAMESPACE(std)string getMessage() const; + + virtual const char* what() const throw(); +}; + +ANTLR_END_NAMESPACE + +#endif //INC_ANTLRException_hpp__ diff --git a/poxml/antlr/antlr/AST.hpp b/poxml/antlr/antlr/AST.hpp new file mode 100644 index 00000000..a36ffd15 --- /dev/null +++ b/poxml/antlr/antlr/AST.hpp @@ -0,0 +1,108 @@ +#ifndef INC_AST_hpp__ +#define INC_AST_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/ASTRefCount.hpp" +#include "antlr/Token.hpp" +#include +#include + +ANTLR_BEGIN_NAMESPACE(antlr) + +struct ASTRef; + +class AST { +public: + AST() : ref(0) {} + virtual ~AST() {} + + virtual void addChild(RefAST c)=0; + + virtual bool equals(RefAST t) const=0; + virtual bool equalsList(RefAST t) const=0; + virtual bool equalsListPartial(RefAST t) const=0; + virtual bool equalsTree(RefAST t) const=0; + virtual bool equalsTreePartial(RefAST t) const=0; + + virtual ANTLR_USE_NAMESPACE(std)vector findAll(RefAST t)=0; + virtual ANTLR_USE_NAMESPACE(std)vector findAllPartial(RefAST t)=0; + + /** Get the first child of this node; null if no children */ + virtual RefAST getFirstChild() const=0; + /** Get the next sibling in line after this one */ + virtual RefAST getNextSibling() const=0; + + /** Get the token text for this node */ + virtual ANTLR_USE_NAMESPACE(std)string getText() const=0; + /** Get the token type for this node */ + virtual int getType() const=0; + + virtual void initialize(int t,const ANTLR_USE_NAMESPACE(std)string& txt)=0; + virtual void initialize(RefAST t)=0; + virtual void initialize(RefToken t)=0; + + /** Set the first child of a node. */ + virtual void setFirstChild(RefAST c)=0; + /** Set the next sibling after this one. */ + virtual void setNextSibling(RefAST n)=0; + + /** Set the token text for this node */ + virtual void setText(const ANTLR_USE_NAMESPACE(std)string& txt)=0; + /** Set the token type for this node */ + virtual void setType(int type)=0; + + virtual ANTLR_USE_NAMESPACE(std)string toString() const=0; + virtual ANTLR_USE_NAMESPACE(std)string toStringList() const=0; + virtual ANTLR_USE_NAMESPACE(std)string toStringTree() const=0; +private: + friend struct ASTRef; + ASTRef* ref; + + AST(const AST& other); + AST(RefAST other); + AST& operator=(const AST& other); + AST& operator=(RefAST other); +}; + +extern RefAST nullAST; +extern AST* const nullASTptr; + +#ifdef NEEDS_OPERATOR_LESS_THAN +inline operator<(RefAST l,RefAST r); // {return true;} +#endif + +ANTLR_END_NAMESPACE + +#endif //INC_AST_hpp__ diff --git a/poxml/antlr/antlr/ASTArray.hpp b/poxml/antlr/antlr/ASTArray.hpp new file mode 100644 index 00000000..5203acf0 --- /dev/null +++ b/poxml/antlr/antlr/ASTArray.hpp @@ -0,0 +1,63 @@ +#ifndef INC_ASTArray_hpp__ +#define INC_ASTArray_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/AST.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** ASTArray is a class that allows ANTLR to + * generate code that can create and initialize an array + * in one expression, like: + * (new ASTArray(3))->add(x)->add(y)->add(z) + */ +class ASTArray { +public: + int size; // = 0; + ANTLR_USE_NAMESPACE(std)vector array; + + ASTArray(int capacity) + : size(0) + , array(capacity) + {} + ASTArray* add(RefAST node) { + array[size++] = node; + return this; + } +}; + +ANTLR_END_NAMESPACE + +#endif //INC_ASTArray_hpp__ diff --git a/poxml/antlr/antlr/ASTFactory.hpp b/poxml/antlr/antlr/ASTFactory.hpp new file mode 100644 index 00000000..584cee6d --- /dev/null +++ b/poxml/antlr/antlr/ASTFactory.hpp @@ -0,0 +1,113 @@ +#ifndef INC_ASTFactory_hpp__ +#define INC_ASTFactory_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/AST.hpp" +#include "antlr/ASTArray.hpp" +#include "antlr/ASTPair.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** AST Support code shared by TreeParser and Parser. + * We use delegation to share code (and have only one + * bit of code to maintain) rather than subclassing + * or superclassing (forces AST support code to be + * loaded even when you don't want to do AST stuff). + * + * Typically, setASTNodeType is used to specify the + * type of node to create, but you can override + * create to make heterogeneous nodes etc... + */ +class ASTFactory { +public: + typedef RefAST (*factory_type)(); +protected: + /** Name of AST class to create during tree construction. + * Null implies that the create method should create + * a default AST type such as CommonAST. + */ + factory_type nodeFactory; + +public: + ASTFactory(); + /** Add a child to the current AST */ + void addASTChild(ASTPair& currentAST, RefAST child); + /** Create a new empty AST node; if the user did not specify + * an AST node type, then create a default one: CommonAST. + */ + virtual RefAST create(); + RefAST create(int type); + RefAST create(int type, const ANTLR_USE_NAMESPACE(std)string& txt); + /** Create a new empty AST node; if the user did not specify + * an AST node type, then create a default one: CommonAST. + */ + RefAST create(RefAST tr); + RefAST create(RefToken tok); + /** Copy a single node. clone() is not used because + * we want to return an AST not a plain object...a type + * safety issue. Further, we want to have all AST node + * creation go through the factory so creation can be + * tracked. Returns null if t is null. + */ + RefAST dup(RefAST t); + /** Duplicate tree including siblings of root. */ + RefAST dupList(RefAST t); + /**Duplicate a tree, assuming this is a root node of a tree-- + * duplicate that node and what's below; ignore siblings of root node. + */ + RefAST dupTree(RefAST t); + /** Make a tree from a list of nodes. The first element in the + * array is the root. If the root is null, then the tree is + * a simple list not a tree. Handles null children nodes correctly. + * For example, build(a, b, null, c) yields tree (a b c). build(null,a,b) + * yields tree (nil a b). + */ + RefAST make(ANTLR_USE_NAMESPACE(std)vector nodes); + /** Make a tree from a list of nodes, where the nodes are contained + * in an ASTArray object + */ + RefAST make(ASTArray* nodes); + /** Make an AST the root of current AST */ + void makeASTRoot(ASTPair& currentAST, RefAST root); + void setASTNodeFactory(factory_type factory); + virtual ~ASTFactory() {} +private: + ASTFactory( const ASTFactory& ); + ASTFactory& operator=( const ASTFactory& ); +}; + +ANTLR_END_NAMESPACE + +#endif //INC_ASTFactory_hpp__ diff --git a/poxml/antlr/antlr/ASTNULLType.hpp b/poxml/antlr/antlr/ASTNULLType.hpp new file mode 100644 index 00000000..8f3faa46 --- /dev/null +++ b/poxml/antlr/antlr/ASTNULLType.hpp @@ -0,0 +1,72 @@ +#ifndef INC_ASTNULLType_hpp__ +#define INC_ASTNULLType_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/AST.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** There is only one instance of this class **/ +class ASTNULLType : public AST { +public: + void addChild(RefAST c) {;} + bool equals(RefAST t) const {return false;} + bool equalsList(RefAST t) const {return false;} + bool equalsListPartial(RefAST t) const {return false;} + bool equalsTree(RefAST t) const {return false;} + bool equalsTreePartial(RefAST t) const {return false;} + ANTLR_USE_NAMESPACE(std)vector findAll(RefAST tree) + {return ANTLR_USE_NAMESPACE(std)vector();} + ANTLR_USE_NAMESPACE(std)vector findAllPartial(RefAST subtree) + {return ANTLR_USE_NAMESPACE(std)vector();} + RefAST getFirstChild() const { return this; } + RefAST getNextSibling() const { return this; } + ANTLR_USE_NAMESPACE(std)string getText() const { return ""; } + int getType() const { return Token::NULL_TREE_LOOKAHEAD; } + void initialize(int t, const ANTLR_USE_NAMESPACE(std)string& txt) {} + void initialize(RefAST t) {} + void initialize(RefToken t) {} + void setFirstChild(RefAST c) {;} + void setNextSibling(RefAST n) {;} + void setText(const ANTLR_USE_NAMESPACE(std)string& text) {;} + void setType(int ttype) {;} + ANTLR_USE_NAMESPACE(std)string toString() const {return getText();} + ANTLR_USE_NAMESPACE(std)string toStringList() const {return getText();} + ANTLR_USE_NAMESPACE(std)string toStringTree() const {return getText();} +}; + +ANTLR_END_NAMESPACE + +#endif //INC_ASTNULLType_hpp__ diff --git a/poxml/antlr/antlr/ASTPair.hpp b/poxml/antlr/antlr/ASTPair.hpp new file mode 100644 index 00000000..eb7629ba --- /dev/null +++ b/poxml/antlr/antlr/ASTPair.hpp @@ -0,0 +1,77 @@ +#ifndef INC_ASTPair_hpp__ +#define INC_ASTPair_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ +#include "antlr/config.hpp" +#include "antlr/AST.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** ASTPair: utility class used for manipulating a pair of ASTs + * representing the current AST root and current AST sibling. + * This exists to compensate for the lack of pointers or 'var' + * arguments in Java. + * + * OK, so we can do those things in C++, but it seems easier + * to stick with the Java way for now. + */ +class ASTPair { +public: + RefAST root; // current root of tree + RefAST child; // current child to which siblings are added + + /** Make sure that child is the last sibling */ + void advanceChildToEnd() { + if (child) { + while (child->getNextSibling()) { + child = child->getNextSibling(); + } + } + } +// /** Copy an ASTPair. Don't call it clone() because we want type-safety */ +// ASTPair copy() { +// ASTPair tmp = new ASTPair(); +// tmp.root = root; +// tmp.child = child; +// return tmp; +// } + ANTLR_USE_NAMESPACE(std)string toString() const { + ANTLR_USE_NAMESPACE(std)string r = !root ? ANTLR_USE_NAMESPACE(std)string("null") : root->getText(); + ANTLR_USE_NAMESPACE(std)string c = !child ? ANTLR_USE_NAMESPACE(std)string("null") : child->getText(); + return "["+r+","+c+"]"; + } +}; + +ANTLR_END_NAMESPACE + +#endif //INC_ASTPair_hpp__ diff --git a/poxml/antlr/antlr/ASTRefCount.hpp b/poxml/antlr/antlr/ASTRefCount.hpp new file mode 100644 index 00000000..cb44128b --- /dev/null +++ b/poxml/antlr/antlr/ASTRefCount.hpp @@ -0,0 +1,104 @@ +#ifndef INC_ASTRefCount_hpp__ +# define INC_ASTRefCount_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +# include "antlr/config.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + + class AST; + +struct ASTRef +{ + AST* const ptr; + unsigned int count; + + ASTRef(AST* p); + ~ASTRef(); + ASTRef* increment(); + bool decrement(); + + static ASTRef* getRef(const AST* p); +private: + ASTRef( const ASTRef& ); + ASTRef& operator=( const ASTRef& ); +}; + +template + class ASTRefCount +{ +private: + ASTRef* ref; + +public: + ASTRefCount(const AST* p=0) + : ref(p ? ASTRef::getRef(p) : 0) + { + } + ASTRefCount(const ASTRefCount& other) + : ref(other.ref ? other.ref->increment() : 0) + { + } + ~ASTRefCount() + { + if (ref && ref->decrement()) delete ref; + } + ASTRefCount& operator=(AST* other) + { + ASTRef* tmp=ASTRef::getRef(other); + if (ref && ref->decrement()) delete ref; + ref=tmp; + return *this; + } + ASTRefCount& operator=(const ASTRefCount& other) + { + ASTRef* tmp=other.ref ? other.ref->increment() : 0; + if (ref && ref->decrement()) delete ref; + ref=tmp; + return *this; + } + + operator T* () const + { return ref ? static_cast(ref->ptr) : 0; } + T* operator->() const + { return ref ? static_cast(ref->ptr) : 0; } + T* get() const + { return ref ? static_cast(ref->ptr) : 0; } +}; + +typedef ASTRefCount RefAST; + +ANTLR_END_NAMESPACE + +#endif //INC_ASTRefCount_hpp__ diff --git a/poxml/antlr/antlr/BaseAST.hpp b/poxml/antlr/antlr/BaseAST.hpp new file mode 100644 index 00000000..7b93c1ef --- /dev/null +++ b/poxml/antlr/antlr/BaseAST.hpp @@ -0,0 +1,106 @@ +#ifndef INC_BaseAST_hpp__ +#define INC_BaseAST_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/AST.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +class BaseAST; +typedef ASTRefCount RefBaseAST; + +class BaseAST : public AST { +public: + BaseAST() : AST() {} + virtual ~BaseAST() {} +protected: + RefBaseAST down; + RefBaseAST right; + +//private: +// static bool verboseStringConversion; +// static ANTLR_USE_NAMESPACE(std)vector tokenNames; + +public: + virtual void addChild(RefAST c); + +private: + void doWorkForFindAll(ANTLR_USE_NAMESPACE(std)vector& v, + RefAST target,bool partialMatch); + +public: + virtual bool equals(RefAST t) const; + virtual bool equalsList(RefAST t) const; + virtual bool equalsListPartial(RefAST t) const; + virtual bool equalsTree(RefAST t) const; + virtual bool equalsTreePartial(RefAST t) const; + + virtual ANTLR_USE_NAMESPACE(std)vector findAll(RefAST t); + virtual ANTLR_USE_NAMESPACE(std)vector findAllPartial(RefAST t); + + /** Get the first child of this node; null if no children */ + virtual RefAST getFirstChild() const; + /** Get the next sibling in line after this one */ + virtual RefAST getNextSibling() const; + + /** Get the token text for this node */ + virtual ANTLR_USE_NAMESPACE(std)string getText() const; + /** Get the token type for this node */ + virtual int getType() const; + + /** Remove all children */ + virtual void removeChildren(); + + /** Set the first child of a node. */ + virtual void setFirstChild(RefAST c); + /** Set the next sibling after this one. */ + void setNextSibling(RefAST n); + + /** Set the token text for this node */ + virtual void setText(const ANTLR_USE_NAMESPACE(std)string& txt); + /** Set the token type for this node */ + virtual void setType(int type); + +// static void setVerboseStringConversion(bool verbose, +// const ANTLR_USE_NAMESPACE(std)vector& names); + + virtual ANTLR_USE_NAMESPACE(std)string toString() const; + virtual ANTLR_USE_NAMESPACE(std)string toStringList() const; + virtual ANTLR_USE_NAMESPACE(std)string toStringTree() const; +}; + +ANTLR_END_NAMESPACE + +#endif //INC_BaseAST_hpp__ diff --git a/poxml/antlr/antlr/BitSet.hpp b/poxml/antlr/antlr/BitSet.hpp new file mode 100644 index 00000000..4eb400c7 --- /dev/null +++ b/poxml/antlr/antlr/BitSet.hpp @@ -0,0 +1,50 @@ +#ifndef INC_BitSet_hpp__ +#define INC_BitSet_hpp__ + +#include "antlr/config.hpp" +#include +#include + +ANTLR_BEGIN_NAMESPACE(antlr) + +/**A BitSet to replace java.util.BitSet. + * Primary differences are that most set operators return new sets + * as opposed to oring and anding "in place". Further, a number of + * operations were added. I cannot contain a BitSet because there + * is no way to access the internal bits (which I need for speed) + * and, because it is final, I cannot subclass to add functionality. + * Consider defining set degree. Without access to the bits, I must + * call a method n times to test the ith bit...ack! + * + * Also seems like or() from util is wrong when size of incoming set is bigger + * than this.length. + * + * + * This is a C++ version of the Java class described above, with only + * a handful of the methods implemented, because we don't need the + * others at runtime. It's really just a wrapper around vector, + * which should probably be changed to a wrapper around bitset, once + * bitset is more widely available. + * + * @author Terence Parr, MageLang Institute + * @author
    Pete Wells + */ +class BitSet { +private: + ANTLR_USE_NAMESPACE(std)vector storage; + +public: + BitSet(int nbits=64); + BitSet(const unsigned long* bits_,int nlongs); + ~BitSet(); + + void add(int el); + + bool member(int el) const; + + ANTLR_USE_NAMESPACE(std)vector toArray() const; +}; + +ANTLR_END_NAMESPACE + +#endif //INC_BitSet_hpp__ diff --git a/poxml/antlr/antlr/CharBuffer.hpp b/poxml/antlr/antlr/CharBuffer.hpp new file mode 100644 index 00000000..45d467bb --- /dev/null +++ b/poxml/antlr/antlr/CharBuffer.hpp @@ -0,0 +1,75 @@ +#ifndef INC_CharBuffer_hpp__ +#define INC_CharBuffer_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +/**A Stream of characters fed to the lexer from a InputStream that can + * be rewound via mark()/rewind() methods. + *

    + * A dynamic array is used to buffer up all the input characters. Normally, + * "k" characters are stored in the buffer. More characters may be stored during + * guess mode (testing syntactic predicate), or when LT(i>k) is referenced. + * Consumption of characters is deferred. In other words, reading the next + * character is not done by conume(), but deferred until needed by LA or LT. + *

    + * + * @see antlr.CharQueue + */ + +#include "antlr/config.hpp" +#include "antlr/InputBuffer.hpp" +#include + +ANTLR_BEGIN_NAMESPACE(antlr) + +class CharBuffer : public InputBuffer { +private: + // char source + ANTLR_USE_NAMESPACE(std)istream& input; + +public: + /** Create a character buffer */ + CharBuffer(ANTLR_USE_NAMESPACE(std)istream& input_); + + /** Get the next character from the stream */ + int getChar(); + +private: +// Not implemented. +// CharBuffer(const CharBuffer& other); +// CharBuffer& operator=(const CharBuffer& other); +}; + +ANTLR_END_NAMESPACE + +#endif //INC_CharBuffer_hpp__ diff --git a/poxml/antlr/antlr/CharScanner.hpp b/poxml/antlr/antlr/CharScanner.hpp new file mode 100644 index 00000000..b0ab9276 --- /dev/null +++ b/poxml/antlr/antlr/CharScanner.hpp @@ -0,0 +1,265 @@ +#ifndef INC_CharScanner_hpp__ +#define INC_CharScanner_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * $Id$ + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/TokenStream.hpp" +#include "antlr/RecognitionException.hpp" +#include "antlr/InputBuffer.hpp" +#include "antlr/BitSet.hpp" +#include "antlr/LexerSharedInputState.hpp" +#include +#include + +ANTLR_BEGIN_NAMESPACE(antlr) + +class CharScanner; + +class CharScannerLiteralsLess : public ANTLR_USE_NAMESPACE(std)binary_function { +private: + const CharScanner* scanner; +public: +#ifdef NO_TEMPLATE_PARTS + CharScannerLiteralsLess(); // not really used +#endif + CharScannerLiteralsLess(const CharScanner* theScanner); + bool operator() (const ANTLR_USE_NAMESPACE(std)string& x,const ANTLR_USE_NAMESPACE(std)string& y) const; +private: +// CharScannerLiteralsLess(const CharScannerLiteralsLess&); +// CharScannerLiteralsLess& operator=(const CharScannerLiteralsLess&); +}; + +class CharScanner : public TokenStream { +private: +#ifndef NO_STATIC_CONSTS + static const int NO_CHAR = 0; +#else + enum { + NO_CHAR = 0 + }; +#endif + +public: +#ifndef NO_STATIC_CONSTS + static const int EOF_CHAR = EOF; +#else + enum { + EOF_CHAR = EOF + }; +#endif + +protected: + ANTLR_USE_NAMESPACE(std)string text; // text of current token + + bool saveConsumedInput; // does consume() save characters? + + typedef RefToken (*factory_type)(); + factory_type tokenFactory; // what kind of tokens to create? + + bool caseSensitive; + ANTLR_USE_NAMESPACE(std)map literals; // set by subclass + + RefToken _returnToken; // used to return tokens w/o using return val + + // Input chars + LexerSharedInputState inputState; + + /** Used during filter mode to indicate that path is desired. + * A subsequent scan error will report an error as usual if acceptPath=true; + */ + bool commitToPath; + +public: + CharScanner(); + + CharScanner(InputBuffer& cb); + CharScanner(InputBuffer* cb); + + CharScanner(const LexerSharedInputState& state); + + virtual ~CharScanner(); + + virtual void append(char c); + + virtual void append(const ANTLR_USE_NAMESPACE(std)string& s); + + virtual void commit(); + + virtual void consume(); + + /** Consume chars until one matches the given char */ + virtual void consumeUntil(int c); + + /** Consume chars until one matches the given set */ + virtual void consumeUntil(const BitSet& set); + + virtual bool getCaseSensitive() const; + + virtual bool getCaseSensitiveLiterals() const=0; + + virtual int getColumn() const; + + virtual void setColumn(int c); + + virtual bool getCommitToPath() const; + + virtual const ANTLR_USE_NAMESPACE(std)string& getFilename() const; + + virtual InputBuffer& getInputBuffer(); + + virtual LexerSharedInputState getInputState(); + + virtual int getLine() const; + + /** return a copy of the current text buffer */ + virtual const ANTLR_USE_NAMESPACE(std)string& getText() const; + + virtual RefToken getTokenObject() const; + + virtual int LA(int i); + +protected: + virtual RefToken makeToken(int t); + +public: + virtual int mark(); + + virtual void match(int c); + + virtual void match(const BitSet& b); + + virtual void match(const ANTLR_USE_NAMESPACE(std)string& s); + + virtual void matchNot(int c); + + virtual void matchRange(int c1, int c2); + + virtual void newline(); + + virtual void tab(); + + void panic(); + + void panic(const ANTLR_USE_NAMESPACE(std)string& s); + + /** Report exception errors caught in nextToken() */ + virtual void reportError(const RecognitionException& e); + + /** Parser error-reporting function can be overridden in subclass */ + virtual void reportError(const ANTLR_USE_NAMESPACE(std)string& s); + + /** Parser warning-reporting function can be overridden in subclass */ + virtual void reportWarning(const ANTLR_USE_NAMESPACE(std)string& s); + + virtual void resetText(); + + virtual void rewind(int pos); + + virtual void setCaseSensitive(bool t); + + virtual void setCommitToPath(bool commit); + + virtual void setFilename(const ANTLR_USE_NAMESPACE(std)string& f); + + virtual void setInputState(LexerSharedInputState state); + + virtual void setLine(int l); + + virtual void setText(const ANTLR_USE_NAMESPACE(std)string& s); + + virtual void setTokenObjectFactory(factory_type factory); + + // Test the token text against the literals table + // Override this method to perform a different literals test + virtual int testLiteralsTable(int ttype) const; + + // Test the text passed in against the literals table + // Override this method to perform a different literals test + // This is used primarily when you want to test a portion of + // a token + virtual int testLiteralsTable(const ANTLR_USE_NAMESPACE(std)string& text,int ttype) const; + + // Override this method to get more specific case handling + virtual int toLower(int c) const; + +protected: + class Tracer { + private: + CharScanner* parser; + ANTLR_USE_NAMESPACE(std)string text; + public: + Tracer(CharScanner* p,const ANTLR_USE_NAMESPACE(std)string& t) + : parser(p), text(t) { parser->traceIn(text); } + ~Tracer() + { parser->traceOut(text); } + }; + + int traceDepth; +public: + virtual void traceIndent(); + virtual void traceIn(const ANTLR_USE_NAMESPACE(std)string& rname); + virtual void traceOut(const ANTLR_USE_NAMESPACE(std)string& rname); + + /* This method is called by YourLexer::nextToken() when the lexer has + * hit EOF condition. EOF is NOT a character. + * This method is not called if EOF is reached during + * syntactic predicate evaluation or during evaluation + * of normal lexical rules, which presumably would be + * an IOException. This traps the "normal" EOF condition. + * + * uponEOF() is called after the complete evaluation of + * the previous token and only if your parser asks + * for another token beyond that last non-EOF token. + * + * You might want to throw token or char stream exceptions + * like: "Heh, premature eof" or a retry stream exception + * ("I found the end of this file, go back to referencing file"). + */ + virtual void uponEOF(); +}; + +inline int CharScanner::LA(int i) +{ + if ( caseSensitive ) { + return inputState->getInput().LA(i); + } else { + return toLower(inputState->getInput().LA(i)); + } +} + +ANTLR_END_NAMESPACE + +#endif //INC_CharScanner_hpp__ diff --git a/poxml/antlr/antlr/CharStreamException.hpp b/poxml/antlr/antlr/CharStreamException.hpp new file mode 100644 index 00000000..33f52061 --- /dev/null +++ b/poxml/antlr/antlr/CharStreamException.hpp @@ -0,0 +1,18 @@ +#ifndef INC_CharStreamException_hpp__ +#define INC_CharStreamException_hpp__ + +#include "antlr/config.hpp" +#include "antlr/ANTLRException.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +class CharStreamException : public ANTLRException { +public: + CharStreamException(const ANTLR_USE_NAMESPACE(std)string& s) + : ANTLRException(s) {} + ~CharStreamException() throw() {} +}; + +ANTLR_END_NAMESPACE + +#endif //INC_CharStreamException_hpp__ diff --git a/poxml/antlr/antlr/CharStreamIOException.hpp b/poxml/antlr/antlr/CharStreamIOException.hpp new file mode 100644 index 00000000..1a8b1d1e --- /dev/null +++ b/poxml/antlr/antlr/CharStreamIOException.hpp @@ -0,0 +1,20 @@ +#ifndef INC_CharStreamIOException_hpp__ +#define INC_CharStreamIOException_hpp__ + +#include "antlr/config.hpp" +#include "antlr/CharStreamException.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +class CharStreamIOException : public CharStreamException { +public: + ANTLR_USE_NAMESPACE(std)exception io; + + CharStreamIOException(ANTLR_USE_NAMESPACE(std)exception& e) + : CharStreamException(e.what()), io(e) {} + ~CharStreamIOException() throw() {} +}; + +ANTLR_END_NAMESPACE + +#endif //INC_CharStreamIOException_hpp__ diff --git a/poxml/antlr/antlr/CircularQueue.hpp b/poxml/antlr/antlr/CircularQueue.hpp new file mode 100644 index 00000000..eadf8d42 --- /dev/null +++ b/poxml/antlr/antlr/CircularQueue.hpp @@ -0,0 +1,88 @@ +#ifndef INC_CircularQueue_hpp__ +#define INC_CircularQueue_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include + +ANTLR_BEGIN_NAMESPACE(antlr) + +// Resize every 5000 items +#define OFFSET_MAX_RESIZE 5000 + +template +class CircularQueue { +private: + ANTLR_USE_NAMESPACE(std)vector storage; + +public: + CircularQueue() + : storage(), m_offset(0) {} + ~CircularQueue() + {} + + T elementAt(int idx) const + { return storage[idx+m_offset]; } //Is this safe? + void removeFirst() + { + if (m_offset >= OFFSET_MAX_RESIZE) { + storage.erase( storage.begin(), storage.begin() + m_offset + 1 ); + m_offset = 0; + } else { + ++m_offset; + } + } + inline void removeItems( int nb ) + { + if (m_offset >= OFFSET_MAX_RESIZE) { + storage.erase( storage.begin(), storage.begin() + m_offset + nb ); + m_offset = 0; + } else { + m_offset+=nb; + } + } + void append(const T& t) + { storage.push_back(t); } + int entries() const + { return storage.size()-m_offset; } + +private: + int m_offset; + CircularQueue(const CircularQueue&); + const CircularQueue& operator=(const CircularQueue&); +}; + +ANTLR_END_NAMESPACE + +#endif //INC_CircularQueue_hpp__ diff --git a/poxml/antlr/antlr/CommonAST.hpp b/poxml/antlr/antlr/CommonAST.hpp new file mode 100644 index 00000000..c7ab7313 --- /dev/null +++ b/poxml/antlr/antlr/CommonAST.hpp @@ -0,0 +1,68 @@ +#ifndef INC_CommonAST_hpp__ +#define INC_CommonAST_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/BaseAST.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +class CommonAST : public BaseAST { +public: + CommonAST(); + CommonAST(RefToken t); + virtual ~CommonAST(); + + virtual ANTLR_USE_NAMESPACE(std)string getText() const; + virtual int getType() const; + + virtual void initialize(int t,const ANTLR_USE_NAMESPACE(std)string& txt); + virtual void initialize(RefAST t); + virtual void initialize(RefToken t); + + virtual void setText(const ANTLR_USE_NAMESPACE(std)string& txt); + virtual void setType(int type); + + static RefAST factory(); + +protected: + int ttype; + ANTLR_USE_NAMESPACE(std)string text; +}; + +typedef ASTRefCount RefCommonAST; + +ANTLR_END_NAMESPACE + +#endif //INC_CommonAST_hpp__ diff --git a/poxml/antlr/antlr/CommonASTWithHiddenTokens.hpp b/poxml/antlr/antlr/CommonASTWithHiddenTokens.hpp new file mode 100644 index 00000000..11e030e7 --- /dev/null +++ b/poxml/antlr/antlr/CommonASTWithHiddenTokens.hpp @@ -0,0 +1,41 @@ +#ifndef INC_CommonASTWithHiddenTokens_hpp__ +#define INC_CommonASTWithHiddenTokens_hpp__ + +/** A CommonAST whose initialization copies hidden token + * information from the Token used to create a node. + */ + +#include "antlr/config.hpp" +#include "antlr/CommonAST.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +class CommonASTWithHiddenTokens : public CommonAST { +public: + CommonASTWithHiddenTokens() : CommonAST() + { + } + virtual ~CommonASTWithHiddenTokens() + { + } +protected: + RefToken hiddenBefore,hiddenAfter; // references to hidden tokens +public: + virtual RefToken getHiddenAfter() const + { return hiddenAfter; } + virtual RefToken getHiddenBefore() const + { return hiddenBefore; } + + // Borland C++ builder seems to need the decl's of the first two... + virtual void initialize(int t,const ANTLR_USE_NAMESPACE(std)string& txt); + virtual void initialize(RefAST t); + virtual void initialize(RefToken t); + + static RefAST factory(); +}; + +typedef ASTRefCount RefCommonASTWithHiddenTokens; + +ANTLR_END_NAMESPACE + +#endif //INC_CommonASTWithHiddenTokens_hpp__ diff --git a/poxml/antlr/antlr/CommonHiddenStreamToken.hpp b/poxml/antlr/antlr/CommonHiddenStreamToken.hpp new file mode 100644 index 00000000..50ff2354 --- /dev/null +++ b/poxml/antlr/antlr/CommonHiddenStreamToken.hpp @@ -0,0 +1,30 @@ +#ifndef INC_CommonHiddenStreamToken_hpp__ +#define INC_CommonHiddenStreamToken_hpp__ + +#include "antlr/config.hpp" +#include "antlr/CommonToken.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +class CommonHiddenStreamToken : public CommonToken { +protected: + RefToken hiddenBefore; + RefToken hiddenAfter; + +public: + CommonHiddenStreamToken(); + CommonHiddenStreamToken(int t, const ANTLR_USE_NAMESPACE(std)string& txt); + CommonHiddenStreamToken(const ANTLR_USE_NAMESPACE(std)string& s); + + RefToken getHiddenAfter(); + RefToken getHiddenBefore(); + + static RefToken factory(); + + void setHiddenAfter(RefToken t); + void setHiddenBefore(RefToken t); +}; + +ANTLR_END_NAMESPACE + +#endif //INC_CommonHiddenStreamToken_hpp__ diff --git a/poxml/antlr/antlr/CommonToken.hpp b/poxml/antlr/antlr/CommonToken.hpp new file mode 100644 index 00000000..669aa535 --- /dev/null +++ b/poxml/antlr/antlr/CommonToken.hpp @@ -0,0 +1,77 @@ +#ifndef INC_CommonToken_hpp__ +#define INC_CommonToken_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/Token.hpp" +#include + +ANTLR_BEGIN_NAMESPACE(antlr) + +class CommonToken : public Token { +protected: + // most tokens will want line and text information + int line; + int col; + ANTLR_USE_NAMESPACE(std)string text; + +public: + CommonToken(); + CommonToken(int t, const ANTLR_USE_NAMESPACE(std)string& txt); + CommonToken(const ANTLR_USE_NAMESPACE(std)string& s); + + int getLine() const; + ANTLR_USE_NAMESPACE(std)string getText() const; + void setLine(int l); + void setText(const ANTLR_USE_NAMESPACE(std)string& s); + + ANTLR_USE_NAMESPACE(std)string toString() const; + + /** Return token's start column */ + int getColumn() const; + + void setColumn(int c); + + bool isInvalid() const; + + static RefToken factory(); + +private: + CommonToken(const CommonToken&); + const CommonToken& operator=(const CommonToken&); +}; + +ANTLR_END_NAMESPACE + +#endif //INC_CommonToken_hpp__ diff --git a/poxml/antlr/antlr/InputBuffer.hpp b/poxml/antlr/antlr/InputBuffer.hpp new file mode 100644 index 00000000..96e62191 --- /dev/null +++ b/poxml/antlr/antlr/InputBuffer.hpp @@ -0,0 +1,158 @@ +#ifndef INC_InputBuffer_hpp__ +#define INC_InputBuffer_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +/**A Stream of characters fed to the lexer from a InputStream that can + * be rewound via mark()/rewind() methods. + *

    + * A dynamic array is used to buffer up all the input characters. Normally, + * "k" characters are stored in the buffer. More characters may be stored during + * guess mode (testing syntactic predicate), or when LT(i>k) is referenced. + * Consumption of characters is deferred. In other words, reading the next + * character is not done by conume(), but deferred until needed by LA or LT. + *

    + * + * @see antlr.CharQueue + */ + +#include "antlr/config.hpp" +#include "antlr/CircularQueue.hpp" +#include + +ANTLR_BEGIN_NAMESPACE(antlr) + +class InputBuffer { +protected: + // char source + // leave to subclasses + + // Number of active markers + int nMarkers; // = 0; + + // Additional offset used when markers are active + int markerOffset; // = 0; + + // Number of calls to consume() since last LA() or LT() call + int numToConsume; // = 0; + + // Circular queue + CircularQueue queue; + +public: + /** Create a character buffer */ + InputBuffer(); + + virtual ~InputBuffer() {} + + /** This method updates the state of the input buffer so that + * the text matched since the most recent mark() is no longer + * held by the buffer. So, you either do a mark/rewind for + * failed predicate or mark/commit to keep on parsing without + * rewinding the input. + */ + void commit(); + + /** Mark another character for deferred consumption */ + virtual void consume(); + + /** Ensure that the character buffer is sufficiently full */ + virtual void fill(int amount); + + /** Override this in subclasses to get the next character */ + virtual int getChar()=0; + + ANTLR_USE_NAMESPACE(std)string getLAChars() const; + + ANTLR_USE_NAMESPACE(std)string getMarkedChars() const; + + virtual bool isMarked() const; + + /** Get a lookahead character */ + virtual int LA(int i); + + /**Return an integer marker that can be used to rewind the buffer to + * its current state. + */ + virtual int mark(); + + /**Rewind the character buffer to a marker. + * @param mark Marker returned previously from mark() + */ + virtual void rewind(int mark); + +protected: + /** Sync up deferred consumption */ + void syncConsume(); + +private: + InputBuffer(const InputBuffer& other); + InputBuffer& operator=(const InputBuffer& other); +}; + +/** Sync up deferred consumption */ +inline void InputBuffer::syncConsume() { +#ifdef OLD_CODE + while (numToConsume > 0) { + if (nMarkers > 0) + { + // guess mode -- leave leading characters and bump offset. + markerOffset++; + } else { + // normal mode -- remove first character + queue.removeFirst(); + } + numToConsume--; + } +#endif + + if (numToConsume > 0) { + if (nMarkers > 0) { + markerOffset += numToConsume; + } else { + queue.removeItems( numToConsume ); + } + numToConsume = 0; + } +} + +/** Get a lookahead character */ +inline int InputBuffer::LA(int i) +{ + fill(i); + return queue.elementAt(markerOffset + i - 1); +} + +ANTLR_END_NAMESPACE + +#endif //INC_InputBuffer_hpp__ diff --git a/poxml/antlr/antlr/LLkParser.hpp b/poxml/antlr/antlr/LLkParser.hpp new file mode 100644 index 00000000..8b8db188 --- /dev/null +++ b/poxml/antlr/antlr/LLkParser.hpp @@ -0,0 +1,82 @@ +#ifndef INC_LLkParser_hpp__ +#define INC_LLkParser_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/Parser.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/**An LL(k) parser. + * + * @see antlr.Token + * @see antlr.TokenBuffer + * @see antlr.LL1Parser + */ +class LLkParser : public Parser { +protected: + int k; + +public: +// LLkParser(int k_); + + LLkParser(const ParserSharedInputState& lexer, int k_); + + LLkParser(TokenBuffer& tokenBuf, int k_); + + LLkParser(TokenStream& lexer, int k_); + + /**Consume another token from the input stream. Can only write sequentially! + * If you need 3 tokens ahead, you must consume() 3 times. + *

    + * Note that it is possible to overwrite tokens that have not been matched. + * For example, calling consume() 3 times when k=2, means that the first token + * consumed will be overwritten with the 3rd. + */ + void consume(); + + int LA(int i); + + RefToken LT(int i); + +private: + void trace(const ANTLR_USE_NAMESPACE(std)string& ee, const ANTLR_USE_NAMESPACE(std)string& rname); +public: + void traceIn(const ANTLR_USE_NAMESPACE(std)string& rname); + void traceOut(const ANTLR_USE_NAMESPACE(std)string& rname); +}; + +ANTLR_END_NAMESPACE + +#endif //INC_LLkParser_hpp__ diff --git a/poxml/antlr/antlr/LexerSharedInputState.hpp b/poxml/antlr/antlr/LexerSharedInputState.hpp new file mode 100644 index 00000000..dba2a5f4 --- /dev/null +++ b/poxml/antlr/antlr/LexerSharedInputState.hpp @@ -0,0 +1,49 @@ +#ifndef INC_LexerSharedInputState_hpp__ +#define INC_LexerSharedInputState_hpp__ + +#include "antlr/config.hpp" +#include "antlr/InputBuffer.hpp" +#include "antlr/RefCount.hpp" +#include + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** This object contains the data associated with an + * input stream of characters. Multiple lexers + * share a single LexerSharedInputState to lex + * the same input stream. + */ +class LexerInputState { +public: + LexerInputState(InputBuffer* inbuf); + LexerInputState(InputBuffer& inbuf); + LexerInputState(ANTLR_USE_NAMESPACE(std)istream& in); + ~LexerInputState(); + + int column; + int line; + int tokenStartColumn; + int tokenStartLine; + int guessing; + /** What file (if known) caused the problem? */ + ANTLR_USE_NAMESPACE(std)string filename; + InputBuffer& getInput(); +private: + InputBuffer* input; + bool inputResponsible; + + // we don't want these: + LexerInputState(const LexerInputState&); + LexerInputState& operator=(const LexerInputState&); +}; + +typedef RefCount LexerSharedInputState; + +inline InputBuffer& LexerInputState::getInput() +{ + return *input; +} + +ANTLR_END_NAMESPACE + +#endif //INC_LexerSharedInputState_hpp__ diff --git a/poxml/antlr/antlr/Makefile.am b/poxml/antlr/antlr/Makefile.am new file mode 100644 index 00000000..bafa1347 --- /dev/null +++ b/poxml/antlr/antlr/Makefile.am @@ -0,0 +1,45 @@ +noinst_HEADERS = \ + ANTLRException.hpp \ + AST.hpp \ + ASTArray.hpp \ + ASTFactory.hpp \ + ASTNULLType.hpp \ + ASTPair.hpp \ + ASTRefCount.hpp \ + BaseAST.hpp \ + BitSet.hpp \ + CharBuffer.hpp \ + CharScanner.hpp \ + CharStreamException.hpp \ + CharStreamIOException.hpp \ + CircularQueue.hpp \ + CommonAST.hpp \ + CommonASTWithHiddenTokens.hpp \ + CommonHiddenStreamToken.hpp \ + CommonToken.hpp \ + InputBuffer.hpp \ + LLkParser.hpp \ + LexerSharedInputState.hpp \ + MismatchedCharException.hpp \ + MismatchedTokenException.hpp \ + NoViableAltException.hpp \ + NoViableAltForCharException.hpp \ + Parser.hpp \ + ParserSharedInputState.hpp \ + RecognitionException.hpp \ + RefCount.hpp \ + SemanticException.hpp \ + String.hpp \ + Token.hpp \ + TokenBuffer.hpp \ + TokenStream.hpp \ + TokenStreamBasicFilter.hpp \ + TokenStreamException.hpp \ + TokenStreamHiddenTokenFilter.hpp \ + TokenStreamIOException.hpp \ + TokenStreamRecognitionException.hpp \ + TokenStreamRetryException.hpp \ + TokenStreamSelector.hpp \ + TreeParser.hpp \ + TreeParserSharedInputState.hpp \ + config.hpp diff --git a/poxml/antlr/antlr/MismatchedCharException.hpp b/poxml/antlr/antlr/MismatchedCharException.hpp new file mode 100644 index 00000000..ea923a9d --- /dev/null +++ b/poxml/antlr/antlr/MismatchedCharException.hpp @@ -0,0 +1,127 @@ +#ifndef INC_MismatchedCharException_hpp__ +#define INC_MismatchedCharException_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/RecognitionException.hpp" +#include "antlr/BitSet.hpp" +#include "antlr/CharScanner.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +class MismatchedCharException : public RecognitionException { +public: + // Types of chars +#ifndef NO_STATIC_CONSTS + static const int CHAR = 1; + static const int NOT_CHAR = 2; + static const int RANGE = 3; + static const int NOT_RANGE = 4; + static const int SET = 5; + static const int NOT_SET = 6; +#else + enum { + CHAR = 1, + NOT_CHAR = 2, + RANGE = 3, + NOT_RANGE = 4, + SET = 5, + NOT_SET = 6 + }; +#endif + +public: + // One of the above + int mismatchType; + + // what was found on the input stream + int foundChar; + + // For CHAR/NOT_CHAR and RANGE/NOT_RANGE + int expecting; + + // For RANGE/NOT_RANGE (expecting is lower bound of range) + int upper; + + // For SET/NOT_SET + BitSet set; + +protected: + // who knows...they may want to ask scanner questions + CharScanner* scanner; + +public: + MismatchedCharException(); + + // Expected range / not range + MismatchedCharException( + int c, + int lower, + int upper_, + bool matchNot, + CharScanner* scanner_ + ); + + // Expected token / not token + MismatchedCharException( + int c, + int expecting_, + bool matchNot, + CharScanner* scanner_ + ); + + // Expected BitSet / not BitSet + MismatchedCharException( + int c, + BitSet set_, + bool matchNot, + CharScanner* scanner_ + ); + + MismatchedCharException( + const ANTLR_USE_NAMESPACE(std)string& s, + int line + ); + ~MismatchedCharException() throw() {} + + /** + * Returns the error message that happened on the line/col given. + * Copied from toString(). + */ + ANTLR_USE_NAMESPACE(std)string getMessage() const; +}; + +ANTLR_END_NAMESPACE + +#endif //INC_MismatchedCharException_hpp__ diff --git a/poxml/antlr/antlr/MismatchedTokenException.hpp b/poxml/antlr/antlr/MismatchedTokenException.hpp new file mode 100644 index 00000000..ae4a82cd --- /dev/null +++ b/poxml/antlr/antlr/MismatchedTokenException.hpp @@ -0,0 +1,167 @@ +#ifndef INC_MismatchedTokenException_hpp__ +#define INC_MismatchedTokenException_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/RecognitionException.hpp" +#include "antlr/BitSet.hpp" +#include "antlr/Token.hpp" +#include "antlr/AST.hpp" +#include + +ANTLR_BEGIN_NAMESPACE(antlr) + +class MismatchedTokenException : public RecognitionException { +private: + // Token names array for formatting + ANTLR_USE_NAMESPACE(std)vector tokenNames; + +public: + // The token that was encountered + const RefToken token; + // The offending AST node if tree walking + const RefAST node; + + ANTLR_USE_NAMESPACE(std)string tokenText; // taken from node or token object + + // Types of tokens +#ifndef NO_STATIC_CONSTS + static const int TOKEN = 1; + static const int NOT_TOKEN = 2; + static const int RANGE = 3; + static const int NOT_RANGE = 4; + static const int SET = 5; + static const int NOT_SET = 6; +#else + enum { + TOKEN = 1, + NOT_TOKEN = 2, + RANGE = 3, + NOT_RANGE = 4, + SET = 5, + NOT_SET = 6 + }; +#endif + +public: + // One of the above + int mismatchType; + + // For TOKEN/NOT_TOKEN and RANGE/NOT_RANGE + int expecting; + + // For RANGE/NOT_RANGE (expecting is lower bound of range) + int upper; + + // For SET/NOT_SET + BitSet set; + + MismatchedTokenException(); + + // Expected range / not range + MismatchedTokenException( + const ANTLR_USE_NAMESPACE(std)vector& tokenNames_, + RefAST node_, + int lower, + int upper_, + bool matchNot + ); + + // Expected token / not token + MismatchedTokenException( + const ANTLR_USE_NAMESPACE(std)vector& tokenNames_, + RefAST node_, + int expecting_, + bool matchNot + ); + + // Expected BitSet / not BitSet + MismatchedTokenException( + const ANTLR_USE_NAMESPACE(std)vector& tokenNames_, + RefAST node_, + BitSet set_, + bool matchNot + ); + + // Expected range / not range + MismatchedTokenException( + const ANTLR_USE_NAMESPACE(std)vector& tokenNames_, + RefToken token_, + int lower, + int upper_, + bool matchNot, + const ANTLR_USE_NAMESPACE(std)string& fileName_ + ); + + // Expected token / not token + MismatchedTokenException( + const ANTLR_USE_NAMESPACE(std)vector& tokenNames_, + RefToken token_, + int expecting_, + bool matchNot, + const ANTLR_USE_NAMESPACE(std)string& fileName_ + ); + + // Expected BitSet / not BitSet + MismatchedTokenException( + const ANTLR_USE_NAMESPACE(std)vector& tokenNames_, + RefToken token_, + BitSet set_, + bool matchNot, + const ANTLR_USE_NAMESPACE(std)string& fileName_ + ); + ~MismatchedTokenException() throw() {} + + /** + * @deprecated As of ANTLR 2.7.0 + */ + ANTLR_USE_NAMESPACE(std)string getErrorMessage() const; + + /** + * Returns the error message that happened on the line/col given. + * Copied from toString(). + */ + ANTLR_USE_NAMESPACE(std)string getMessage() const; + +private: + ANTLR_USE_NAMESPACE(std)string tokenName(int tokenType) const; + +public: + ANTLR_USE_NAMESPACE(std)string toString() const; + +}; + +ANTLR_END_NAMESPACE + +#endif //INC_MismatchedTokenException_hpp__ diff --git a/poxml/antlr/antlr/NoViableAltException.hpp b/poxml/antlr/antlr/NoViableAltException.hpp new file mode 100644 index 00000000..f85bcf96 --- /dev/null +++ b/poxml/antlr/antlr/NoViableAltException.hpp @@ -0,0 +1,71 @@ +#ifndef INC_NoViableAltException_hpp__ +#define INC_NoViableAltException_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/RecognitionException.hpp" +#include "antlr/Token.hpp" +#include "antlr/AST.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +class NoViableAltException : public RecognitionException { +public: + const RefToken token; + const RefAST node; // handles parsing and treeparsing + + NoViableAltException(RefAST t); + + NoViableAltException(RefToken t,const ANTLR_USE_NAMESPACE(std)string& fileName_); + ~NoViableAltException() throw() {} + + /** + * @deprecated As of ANTLR 2.7.0 + */ + ANTLR_USE_NAMESPACE(std)string getErrorMessage() const; + + /** + * Returns a clean error message (no line number/column information) + */ + ANTLR_USE_NAMESPACE(std)string getMessage() const; + + /** + * Returns a string representation of this exception. + */ + virtual ANTLR_USE_NAMESPACE(std)string toString() const; +}; + +ANTLR_END_NAMESPACE + +#endif //INC_NoViableAltException_hpp__ diff --git a/poxml/antlr/antlr/NoViableAltForCharException.hpp b/poxml/antlr/antlr/NoViableAltForCharException.hpp new file mode 100644 index 00000000..756e9c7f --- /dev/null +++ b/poxml/antlr/antlr/NoViableAltForCharException.hpp @@ -0,0 +1,64 @@ +#ifndef INC_NoViableAltForCharException_hpp__ +#define INC_NoViableAltForCharException_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Institute + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Institute + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/RecognitionException.hpp" +#include "antlr/CharScanner.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +class NoViableAltForCharException : public RecognitionException { +public: + int foundChar; + + NoViableAltForCharException(int c, CharScanner* scanner); + + NoViableAltForCharException(int c, const ANTLR_USE_NAMESPACE(std)string& fileName_, int line_); + ~NoViableAltForCharException() throw() {} + + /** + * @deprecated As of ANTLR 2.7.0 + */ + virtual ANTLR_USE_NAMESPACE(std)string getErrorMessage() const; + + /** + * Returns a clean error message (no line number/column information) + */ + virtual ANTLR_USE_NAMESPACE(std)string getMessage() const; +}; + +ANTLR_END_NAMESPACE + +#endif //INC_NoViableAltForCharException_hpp__ diff --git a/poxml/antlr/antlr/Parser.hpp b/poxml/antlr/antlr/Parser.hpp new file mode 100644 index 00000000..767953d3 --- /dev/null +++ b/poxml/antlr/antlr/Parser.hpp @@ -0,0 +1,213 @@ +#ifndef INC_Parser_hpp__ +#define INC_Parser_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/BitSet.hpp" +#include "antlr/TokenBuffer.hpp" +#include "antlr/RecognitionException.hpp" +#include "antlr/ASTFactory.hpp" +#include "antlr/ParserSharedInputState.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/**A generic ANTLR parser (LL(k) for k>=1) containing a bunch of + * utility routines useful at any lookahead depth. We distinguish between + * the LL(1) and LL(k) parsers because of efficiency. This may not be + * necessary in the near future. + * + * Each parser object contains the state of the parse including a lookahead + * cache (the form of which is determined by the subclass), whether or + * not the parser is in guess mode, where tokens come from, etc... + * + *

    + * During guess mode, the current lookahead token(s) and token type(s) + * cache must be saved because the token stream may not have been informed + * to save the token (via mark) before the try block. + * Guessing is started by: + *

      + *
    1. saving the lookahead cache. + *
    2. marking the current position in the TokenBuffer. + *
    3. increasing the guessing level. + *
    + * + * After guessing, the parser state is restored by: + *
      + *
    1. restoring the lookahead cache. + *
    2. rewinding the TokenBuffer. + *
    3. decreasing the guessing level. + *
    + * + * @see antlr.Token + * @see antlr.TokenBuffer + * @see antlr.TokenStream + * @see antlr.LL1Parser + * @see antlr.LLkParser + */ + +extern bool DEBUG_PARSER; + +class Parser { +protected: + ParserSharedInputState inputState; + + /** Nesting level of registered handlers */ + // int exceptionLevel; + + /** Table of token type to token names */ + ANTLR_USE_NAMESPACE(std)vector tokenNames; + /** AST return value for a rule is squirreled away here */ + RefAST returnAST; + /** AST support code; parser and treeparser delegate to this object */ + ASTFactory astFactory; + +// Parser(); + + Parser(TokenBuffer& input_); + Parser(TokenBuffer* input_); + + Parser(const ParserSharedInputState& state); + +public: + virtual ~Parser(); + +protected: + void setTokenNames(const char** tokenNames_); + +public: + /**Get another token object from the token stream */ + virtual void consume()=0; + + /** Consume tokens until one matches the given token */ + void consumeUntil(int tokenType); + + /** Consume tokens until one matches the given token set */ + void consumeUntil(const BitSet& set); + + /** Get the AST return value squirreled away in the parser */ + RefAST getAST(); + + ASTFactory& getASTFactory(); + + ANTLR_USE_NAMESPACE(std)string getFilename() const; + + virtual ParserSharedInputState getInputState() const; + + ANTLR_USE_NAMESPACE(std)string getTokenName(int num) const; + ANTLR_USE_NAMESPACE(std)vector getTokenNames() const; + + /** Return the token type of the ith token of lookahead where i=1 + * is the current token being examined by the parser (i.e., it + * has not been matched yet). + */ + virtual int LA(int i)=0; + + /**Return the ith token of lookahead */ + virtual RefToken LT(int i)=0; + + // Forwarded to TokenBuffer + virtual int mark(); + + /**Make sure current lookahead symbol matches token type t. + * Throw an exception upon mismatch, which is catch by either the + * error handler or by the syntactic predicate. + */ + void match(int t); + + /**Make sure current lookahead symbol matches the given set + * Throw an exception upon mismatch, which is catch by either the + * error handler or by the syntactic predicate. + */ + void match(const BitSet& b); + + void matchNot(int t); + + static void panic(); + + /** Parser error-reporting function can be overridden in subclass */ + virtual void reportError(const RecognitionException& ex); + + /** Parser error-reporting function can be overridden in subclass */ + virtual void reportError(const ANTLR_USE_NAMESPACE(std)string& s); + + /** Parser warning-reporting function can be overridden in subclass */ + virtual void reportWarning(const ANTLR_USE_NAMESPACE(std)string& s); + + virtual void rewind(int pos); + + /** Set the object used to generate ASTs */ +// void setASTFactory(ASTFactory astFactory_); + + /** Specify the type of node to create during tree building */ + void setASTNodeFactory(ASTFactory::factory_type factory); + + void setFilename(const ANTLR_USE_NAMESPACE(std)string& f); + + void setInputState(ParserSharedInputState state); + + /** Set or change the input token buffer */ +// void setTokenBuffer(TokenBuffer* t); + + virtual void traceIndent(); + virtual void traceIn(const ANTLR_USE_NAMESPACE(std)string& rname); + virtual void traceOut(const ANTLR_USE_NAMESPACE(std)string& rname); +protected: + int traceDepth; // used to keep track of the indentation for the trace + +protected: + /** Utility class which allows tracing to work even when exceptions are + * thrown. + */ + class Tracer { + private: + Parser* parser; + ANTLR_USE_NAMESPACE(std)string text; + public: + Tracer(Parser* p,const ANTLR_USE_NAMESPACE(std)string& t) + : parser(p), text(t) { parser->traceIn(text); } + ~Tracer() + { parser->traceOut(text); } + private: + Tracer(const Tracer&); // undefined + const Tracer& operator=(const Tracer&); // undefined + }; + +private: + Parser(const Parser&); // undefined + const Parser& operator=(const Parser&); // undefined +}; + +ANTLR_END_NAMESPACE + +#endif //INC_Parser_hpp__ diff --git a/poxml/antlr/antlr/ParserSharedInputState.hpp b/poxml/antlr/antlr/ParserSharedInputState.hpp new file mode 100644 index 00000000..b5599954 --- /dev/null +++ b/poxml/antlr/antlr/ParserSharedInputState.hpp @@ -0,0 +1,42 @@ +#ifndef INC_ParserSharedInputState_hpp__ +#define INC_ParserSharedInputState_hpp__ + +#include "antlr/config.hpp" +#include "antlr/TokenBuffer.hpp" +#include "antlr/RefCount.hpp" +#include + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** This object contains the data associated with an + * input stream of tokens. Multiple parsers + * share a single ParserSharedInputState to parse + * the same stream of tokens. + */ +class ParserInputState { +public: + ParserInputState(TokenBuffer* input_); + ParserInputState(TokenBuffer& input_); + ~ParserInputState(); + +public: + /** Are we guessing (guessing>0)? */ + int guessing; //= 0; + /** What file (if known) caused the problem? */ + ANTLR_USE_NAMESPACE(std)string filename; + TokenBuffer& getInput(); +private: + /** Where to get token objects */ + TokenBuffer* input; + bool inputResponsible; + + // we don't want these: + ParserInputState(const ParserInputState&); + ParserInputState& operator=(const ParserInputState&); +}; + +typedef RefCount ParserSharedInputState; + +ANTLR_END_NAMESPACE + +#endif //INC_ParserSharedInputState_hpp__ diff --git a/poxml/antlr/antlr/RecognitionException.hpp b/poxml/antlr/antlr/RecognitionException.hpp new file mode 100644 index 00000000..c6439111 --- /dev/null +++ b/poxml/antlr/antlr/RecognitionException.hpp @@ -0,0 +1,78 @@ +#ifndef INC_RecognitionException_hpp__ +#define INC_RecognitionException_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/ANTLRException.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +class RecognitionException : public ANTLRException { +public: + ANTLR_USE_NAMESPACE(std)string fileName; // not used by treeparsers + int line; // not used by treeparsers + int column; // not used by treeparsers + + RecognitionException(); + + RecognitionException(const ANTLR_USE_NAMESPACE(std)string& s); + + RecognitionException(const ANTLR_USE_NAMESPACE(std)string& s,const ANTLR_USE_NAMESPACE(std)string& fileName_,int line); + RecognitionException(const ANTLR_USE_NAMESPACE(std)string& s,const ANTLR_USE_NAMESPACE(std)string& fileName_,int line,int column); + ~RecognitionException() throw() {} + + /** + * @return the column number that this exception happened on. + * @author Shawn P. Vincent (svincent@svincent.com) + */ + virtual int getColumn() const; + /** + * @deprecated As of ANTLR 2.7.0 + */ + virtual ANTLR_USE_NAMESPACE(std)string getErrorMessage() const; +protected: + virtual ANTLR_USE_NAMESPACE(std)string getFileLineString() const; +public: + virtual ANTLR_USE_NAMESPACE(std)string getFilename() const; + /** + * @return the line number that this exception happened on. + * @author Shawn P. Vincent (svincent@svincent.com) + */ + virtual int getLine() const; + virtual ANTLR_USE_NAMESPACE(std)string toString() const; +}; + +ANTLR_END_NAMESPACE + +#endif //INC_RecognitionException_hpp__ diff --git a/poxml/antlr/antlr/RefCount.hpp b/poxml/antlr/antlr/RefCount.hpp new file mode 100644 index 00000000..9306576b --- /dev/null +++ b/poxml/antlr/antlr/RefCount.hpp @@ -0,0 +1,87 @@ +#ifndef INC_RefCount_hpp__ +#define INC_RefCount_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +template +class RefCount { +private: + struct Ref { + T* const ptr; + unsigned int count; + + Ref(T* p) : ptr(p), count(1) {} + ~Ref() {delete ptr;} + Ref* increment() {++count;return this;} + bool decrement() {return (--count==0);} + private: + Ref(const Ref&); + Ref& operator=(const Ref&); + }* ref; + +public: + explicit RefCount(T* p=0) + : ref(p ? new Ref(p) : 0) + { + } + RefCount(const RefCount& other) + : ref(other.ref ? other.ref->increment() : 0) + { + } + ~RefCount() + { + if (ref && ref->decrement()) delete ref; + } + RefCount& operator=(const RefCount& other) + { + Ref* tmp=other.ref ? other.ref->increment() : 0; + if (ref && ref->decrement()) delete ref; + ref=tmp; + return *this; + } + + operator T* () const + { return ref ? ref->ptr : 0; } + T* operator->() const + { return ref ? ref->ptr : 0; } + T* get() const + { return ref ? ref->ptr : 0; } +}; + +ANTLR_END_NAMESPACE + +#endif //INC_RefCount_hpp__ diff --git a/poxml/antlr/antlr/SemanticException.hpp b/poxml/antlr/antlr/SemanticException.hpp new file mode 100644 index 00000000..3f1a9447 --- /dev/null +++ b/poxml/antlr/antlr/SemanticException.hpp @@ -0,0 +1,52 @@ +#ifndef INC_SemanticException_hpp__ +#define INC_SemanticException_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/RecognitionException.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +class SemanticException : public RecognitionException { +public: + SemanticException(const ANTLR_USE_NAMESPACE(std)string& s) + : RecognitionException(s) {} + SemanticException(const ANTLR_USE_NAMESPACE(std)string& s,const ANTLR_USE_NAMESPACE(std)string& fileName_,int line_) + : RecognitionException(s,fileName_,line_) {} + ~SemanticException() throw() {} +}; + +ANTLR_END_NAMESPACE + +#endif //INC_SemanticException_hpp__ diff --git a/poxml/antlr/antlr/String.hpp b/poxml/antlr/antlr/String.hpp new file mode 100644 index 00000000..5fac82d6 --- /dev/null +++ b/poxml/antlr/antlr/String.hpp @@ -0,0 +1,47 @@ +#ifndef INC_String_hpp__ +#define INC_String_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include + +ANTLR_BEGIN_NAMESPACE(antlr) + +ANTLR_USE_NAMESPACE(std)string operator+(const ANTLR_USE_NAMESPACE(std)string& lhs,int rhs); + +ANTLR_USE_NAMESPACE(std)string charName(int ch); + +ANTLR_END_NAMESPACE + +#endif //INC_String_hpp__ diff --git a/poxml/antlr/antlr/Token.hpp b/poxml/antlr/antlr/Token.hpp new file mode 100644 index 00000000..b85551c3 --- /dev/null +++ b/poxml/antlr/antlr/Token.hpp @@ -0,0 +1,106 @@ +#ifndef INC_Token_hpp__ +#define INC_Token_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/RefCount.hpp" +#include + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** A token is minimally a token type. Subclasses can add the text matched + * for the token and line info. + */ + +class Token; +typedef RefCount RefToken; + +class Token { +public: + // constants +#ifndef NO_STATIC_CONSTS + static const int MIN_USER_TYPE = 4; + static const int NULL_TREE_LOOKAHEAD = 3; + static const int INVALID_TYPE = 0; + static const int EOF_TYPE = 1; + static const int SKIP = -1; +#else + enum { + MIN_USER_TYPE = 4, + NULL_TREE_LOOKAHEAD = 3, + INVALID_TYPE = 0, + EOF_TYPE = 1, + SKIP = -1 + }; +#endif + + // each Token has at least a token type + int type; //=INVALID_TYPE; + +public: + // the illegal token object + static RefToken badToken; // = new Token(INVALID_TYPE, ""); + + Token(); + Token(int t); + Token(int t, const ANTLR_USE_NAMESPACE(std)string& txt); + + virtual int getColumn() const; + virtual int getLine() const; + virtual ANTLR_USE_NAMESPACE(std)string getText() const; + virtual int getType() const; + + virtual void setColumn(int c); + + virtual void setLine(int l); + virtual void setText(const ANTLR_USE_NAMESPACE(std)string& t); + virtual void setType(int t); + + virtual ANTLR_USE_NAMESPACE(std)string toString() const; + + virtual ~Token(); +private: + Token(const Token&); + const Token& operator=(const Token&); +}; + +#ifdef NEEDS_OPERATOR_LESS_THAN +inline operator<(RefToken l,RefToken r); //{return true;} +#endif + +extern RefToken nullToken; + +ANTLR_END_NAMESPACE + +#endif //INC_Token_hpp__ diff --git a/poxml/antlr/antlr/TokenBuffer.hpp b/poxml/antlr/antlr/TokenBuffer.hpp new file mode 100644 index 00000000..b7c1b25f --- /dev/null +++ b/poxml/antlr/antlr/TokenBuffer.hpp @@ -0,0 +1,141 @@ +#ifndef INC_TokenBuffer_hpp__ +#define INC_TokenBuffer_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/TokenStream.hpp" +#include "antlr/CircularQueue.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/**A Stream of Token objects fed to the parser from a TokenStream that can + * be rewound via mark()/rewind() methods. + *

    + * A dynamic array is used to buffer up all the input tokens. Normally, + * "k" tokens are stored in the buffer. More tokens may be stored during + * guess mode (testing syntactic predicate), or when LT(i>k) is referenced. + * Consumption of tokens is deferred. In other words, reading the next + * token is not done by conume(), but deferred until needed by LA or LT. + *

    + * + * @see antlr.Token + * @see antlr.TokenStream + * @see antlr.TokenQueue + */ +class TokenBuffer { +protected: + // Token source + TokenStream& input; + +private: + // Number of active markers + int nMarkers; + + // Additional offset used when markers are active + int markerOffset; + + // Number of calls to consume() since last LA() or LT() call + int numToConsume; + + // Circular queue + CircularQueue queue; + +public: + /** Create a token buffer */ + TokenBuffer(TokenStream& input_); + + /** Mark another token for deferred consumption */ + void consume(); + +private: + /** Ensure that the token buffer is sufficiently full */ + void fill(int amount); + +public: + /** Get a lookahead token value */ + int LA(int i); + + /** Get a lookahead token */ + RefToken LT(int i); + + /**Return an integer marker that can be used to rewind the buffer to + * its current state. + */ + int mark(); + + /**Rewind the token buffer to a marker. + * @param mark Marker returned previously from mark() + */ + void rewind(int mark); + +private: + /** Sync up deferred consumption */ + void syncConsume(); + +private: + TokenBuffer(const TokenBuffer& other); + const TokenBuffer& operator=(const TokenBuffer& other); +public: +// virtual ~TokenBuffer() {} +}; + +/** Sync up deferred consumption */ +inline void TokenBuffer::syncConsume() +{ +#ifdef OLD_CODE + while (numToConsume > 0) { + if (nMarkers > 0) { + // guess mode -- leave leading tokens and bump offset. + markerOffset++; + } else { + // normal mode -- remove first token + queue.removeFirst(); + } + numToConsume--; + } +#endif + + if (numToConsume > 0) { + if (nMarkers > 0) { + markerOffset += numToConsume; + } else { + queue.removeItems( numToConsume ); + } + numToConsume = 0; + } +} + +ANTLR_END_NAMESPACE + +#endif //INC_TokenBuffer_hpp__ diff --git a/poxml/antlr/antlr/TokenStream.hpp b/poxml/antlr/antlr/TokenStream.hpp new file mode 100644 index 00000000..e8436419 --- /dev/null +++ b/poxml/antlr/antlr/TokenStream.hpp @@ -0,0 +1,54 @@ +#ifndef INC_TokenStream_hpp__ +#define INC_TokenStream_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +/**This interface allows any object to pretend it is a stream + * of tokens. + * @author Terence Parr, MageLang Institute + */ + +#include "antlr/config.hpp" +#include "antlr/Token.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +class TokenStream { +public: + virtual RefToken nextToken()=0; + virtual ~TokenStream() {} +}; + +ANTLR_END_NAMESPACE + +#endif //INC_TokenStream_hpp__ diff --git a/poxml/antlr/antlr/TokenStreamBasicFilter.hpp b/poxml/antlr/antlr/TokenStreamBasicFilter.hpp new file mode 100644 index 00000000..5438878b --- /dev/null +++ b/poxml/antlr/antlr/TokenStreamBasicFilter.hpp @@ -0,0 +1,35 @@ +#ifndef INC_TokenStreamBasicFilter_hpp__ +#define INC_TokenStreamBasicFilter_hpp__ + +#include "antlr/config.hpp" +#include "antlr/BitSet.hpp" +#include "antlr/TokenStream.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** This object is a TokenStream that passes through all + * tokens except for those that you tell it to discard. + * There is no buffering of the tokens. + */ +class TokenStreamBasicFilter : public TokenStream { + /** The set of token types to discard */ +protected: + BitSet discardMask; + + /** The input stream */ +protected: + TokenStream* input; + +public: + TokenStreamBasicFilter(TokenStream& input_); + + void discard(int ttype); + + void discard(const BitSet& mask); + + RefToken nextToken(); +}; + +ANTLR_END_NAMESPACE + +#endif //INC_TokenStreamBasicFilter_hpp__ diff --git a/poxml/antlr/antlr/TokenStreamException.hpp b/poxml/antlr/antlr/TokenStreamException.hpp new file mode 100644 index 00000000..2dc96776 --- /dev/null +++ b/poxml/antlr/antlr/TokenStreamException.hpp @@ -0,0 +1,19 @@ +#ifndef INC_TokenStreamException_hpp__ +#define INC_TokenStreamException_hpp__ + +#include "antlr/config.hpp" +#include "antlr/ANTLRException.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +class TokenStreamException : public ANTLRException { +public: + TokenStreamException() {} + TokenStreamException(const ANTLR_USE_NAMESPACE(std)string& s) + : ANTLRException(s) {} + virtual ~TokenStreamException() throw() {} +}; + +ANTLR_END_NAMESPACE + +#endif //INC_TokenStreamException_hpp__ diff --git a/poxml/antlr/antlr/TokenStreamHiddenTokenFilter.hpp b/poxml/antlr/antlr/TokenStreamHiddenTokenFilter.hpp new file mode 100644 index 00000000..47aad001 --- /dev/null +++ b/poxml/antlr/antlr/TokenStreamHiddenTokenFilter.hpp @@ -0,0 +1,84 @@ +#ifndef INC_TokenStreamHiddenTokenFilter_hpp__ +#define INC_TokenStreamHiddenTokenFilter_hpp__ + +#include "antlr/config.hpp" +#include "antlr/TokenStreamBasicFilter.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/**This object filters a token stream coming from a lexer + * or another TokenStream so that only certain token channels + * get transmitted to the parser. + * + * Any of the channels can be filtered off as "hidden" channels whose + * tokens can be accessed from the parser. + */ +class TokenStreamHiddenTokenFilter : public TokenStreamBasicFilter { + // protected BitSet discardMask; +protected: + BitSet hideMask; + +private: + RefToken nextMonitoredToken; + +protected: + /** track tail of hidden list emanating from previous + * monitored token + */ + RefToken lastHiddenToken; + + RefToken firstHidden; // = null; + +public: + TokenStreamHiddenTokenFilter(TokenStream& input); + +protected: + void consume(); + +private: + void consumeFirst(); + +public: + BitSet getDiscardMask() const; + + /** Return a ptr to the hidden token appearing immediately after + * token t in the input stream. + */ + RefToken getHiddenAfter(RefToken t); + + /** Return a ptr to the hidden token appearing immediately before + * token t in the input stream. + */ + RefToken getHiddenBefore(RefToken t); + + BitSet getHideMask() const; + + /** Return the first hidden token if one appears + * before any monitored token. + */ + RefToken getInitialHiddenToken(); + + void hide(int m); + + void hide(const BitSet& mask); + +protected: + RefToken LA(int i); + +public: +/** Return the next monitored token. + * Test the token following the monitored token. + * If following is another monitored token, save it + * for the next invocation of nextToken (like a single + * lookahead token) and return it then. + * If following is unmonitored, nondiscarded (hidden) + * channel token, add it to the monitored token. + * + * Note: EOF must be a monitored Token. + */ + RefToken nextToken(); +}; + +ANTLR_END_NAMESPACE + +#endif //INC_TokenStreamHiddenTokenFilter_hpp__ diff --git a/poxml/antlr/antlr/TokenStreamIOException.hpp b/poxml/antlr/antlr/TokenStreamIOException.hpp new file mode 100644 index 00000000..9ac6d759 --- /dev/null +++ b/poxml/antlr/antlr/TokenStreamIOException.hpp @@ -0,0 +1,22 @@ +#ifndef INC_TokenStreamIOException_hpp__ +#define INC_TokenStreamIOException_hpp__ + +#include "antlr/config.hpp" +#include "antlr/TokenStreamException.hpp" +#include + +ANTLR_BEGIN_NAMESPACE(antlr) + +class TokenStreamIOException : public TokenStreamException { +public: + ANTLR_USE_NAMESPACE(std)exception io; + + TokenStreamIOException() {} + TokenStreamIOException(const ANTLR_USE_NAMESPACE(std)exception& e) + : TokenStreamException(e.what()), io(e) {} + ~TokenStreamIOException() throw() {} +}; + +ANTLR_END_NAMESPACE + +#endif //INC_TokenStreamIOException_hpp__ diff --git a/poxml/antlr/antlr/TokenStreamRecognitionException.hpp b/poxml/antlr/antlr/TokenStreamRecognitionException.hpp new file mode 100644 index 00000000..4aa4609f --- /dev/null +++ b/poxml/antlr/antlr/TokenStreamRecognitionException.hpp @@ -0,0 +1,21 @@ +#ifndef INC_TokenStreamRecognitionException_hpp__ +#define INC_TokenStreamRecognitionException_hpp__ + +#include "antlr/config.hpp" +#include "antlr/TokenStreamException.hpp" +#include + +ANTLR_BEGIN_NAMESPACE(antlr) + +class TokenStreamRecognitionException : public TokenStreamException { +public: + RecognitionException recog; + + TokenStreamRecognitionException(RecognitionException& re) + : TokenStreamException(re.getMessage()), recog(re) {} + ~TokenStreamRecognitionException() throw() {} +}; + +ANTLR_END_NAMESPACE + +#endif //INC_TokenStreamRecognitionException_hpp__ diff --git a/poxml/antlr/antlr/TokenStreamRetryException.hpp b/poxml/antlr/antlr/TokenStreamRetryException.hpp new file mode 100644 index 00000000..a940d8c4 --- /dev/null +++ b/poxml/antlr/antlr/TokenStreamRetryException.hpp @@ -0,0 +1,17 @@ +#ifndef INC_TokenStreamRetryException_hpp__ +#define INC_TokenStreamRetryException_hpp__ + +#include "antlr/config.hpp" +#include "antlr/TokenStreamException.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +class TokenStreamRetryException : public TokenStreamException { +public: + TokenStreamRetryException() {} + ~TokenStreamRetryException() throw() {} +}; + +ANTLR_END_NAMESPACE + +#endif //INC_TokenStreamRetryException_hpp__ diff --git a/poxml/antlr/antlr/TokenStreamSelector.hpp b/poxml/antlr/antlr/TokenStreamSelector.hpp new file mode 100644 index 00000000..7e7d7398 --- /dev/null +++ b/poxml/antlr/antlr/TokenStreamSelector.hpp @@ -0,0 +1,78 @@ +#ifndef INC_TokenStreamSelector_hpp__ +#define INC_TokenStreamSelector_hpp__ + +#include "antlr/config.hpp" +#include "antlr/TokenStream.hpp" +#include +#include + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** A token stream MUX (multiplexor) knows about n token streams + * and can multiplex them onto the same channel for use by token + * stream consumer like a parser. This is a way to have multiple + * lexers break up the same input stream for a single parser. + * Or, you can have multiple instances of the same lexer handle + * multiple input streams; this works great for includes. + */ +class TokenStreamSelector : public TokenStream { +protected: + /** The set of inputs to the MUX */ +#ifdef OS_NO_ALLOCATOR + typedef ANTLR_USE_NAMESPACE(std)less lessp; + typedef ANTLR_USE_NAMESPACE(std)map inputStreamNames_coll; +#else + typedef ANTLR_USE_NAMESPACE(std)map inputStreamNames_coll; +#endif + inputStreamNames_coll inputStreamNames; + + /** The currently-selected token stream input */ + TokenStream* input; + + /** Used to track stack of input streams */ +#ifdef OS_NO_ALLOCATOR + typedef ANTLR_USE_NAMESPACE(std)stack > streamStack_coll; +#else + typedef ANTLR_USE_NAMESPACE(std)stack streamStack_coll; +#endif + streamStack_coll streamStack; + +public: + TokenStreamSelector(); + ~TokenStreamSelector(); + + void addInputStream(TokenStream* stream, const ANTLR_USE_NAMESPACE(std)string& key); + + /** Return the stream from which tokens are being pulled at + * the moment. + */ + TokenStream* getCurrentStream() const; + + TokenStream* getStream(const ANTLR_USE_NAMESPACE(std)string& sname) const; + + RefToken nextToken(); + + TokenStream* pop(); + + void push(TokenStream* stream); + + void push(const ANTLR_USE_NAMESPACE(std)string& sname); + + /** Abort recognition of current Token and try again. + * A stream can push a new stream (for include files + * for example, and then retry(), which will cause + * the current stream to abort back to this.nextToken(). + * this.nextToken() then asks for a token from the + * current stream, which is the new "substream." + */ + void retry(); + +/** Set the stream without pushing old stream */ + void select(TokenStream* stream); + + void select(const ANTLR_USE_NAMESPACE(std)string& sname); +}; + +ANTLR_END_NAMESPACE + +#endif //INC_TokenStreamSelector_hpp__ diff --git a/poxml/antlr/antlr/TreeParser.hpp b/poxml/antlr/antlr/TreeParser.hpp new file mode 100644 index 00000000..ed474bd1 --- /dev/null +++ b/poxml/antlr/antlr/TreeParser.hpp @@ -0,0 +1,159 @@ +#ifndef INC_TreeParser_hpp__ +#define INC_TreeParser_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ +#include "antlr/config.hpp" +#include "antlr/AST.hpp" +#include "antlr/ASTFactory.hpp" +#include "antlr/BitSet.hpp" +#include "antlr/RecognitionException.hpp" +#include "antlr/TreeParserSharedInputState.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +class TreeParser { +public: + TreeParser(); + TreeParser(const TreeParserSharedInputState& state); + virtual ~TreeParser(); + +protected: + void setTokenNames(const char** tokenNames_); + +public: + /** The AST Null object; the parsing cursor is set to this when + * it is found to be null. This way, we can test the + * token type of a node without having to have tests for null + * everywhere. + */ + static RefAST ASTNULL; + +protected: + /** Where did this rule leave off parsing; avoids a return parameter */ + RefAST _retTree; + + /** guessing nesting level; guessing==0 implies not guessing */ + // int guessing; // = 0; + + /** Nesting level of registered handlers */ + // int exceptionLevel; // = 0; + + TreeParserSharedInputState inputState; + + /** Table of token type to token names */ + ANTLR_USE_NAMESPACE(std)vector tokenNames; + + /** AST return value for a rule is squirreled away here */ + RefAST returnAST; + + /** AST support code; parser and treeparser delegate to this object */ + ASTFactory astFactory; // = new ASTFactory(); + + /** Used to keep track of indent depth with -traceTreeParser */ + int traceDepth; + +public: + /** Get the AST return value squirreled away in the parser */ + RefAST getAST() const { + return returnAST; + } + + ANTLR_USE_NAMESPACE(std)string getTokenName(int num) const; + ANTLR_USE_NAMESPACE(std)vector getTokenNames() const; + +protected: + void match(RefAST t, int ttype); + +public: + /**Make sure current lookahead symbol matches the given set + * Throw an exception upon mismatch, which is catch by either the + * error handler or by the syntactic predicate. + */ + void match(RefAST t, const BitSet& b); + +protected: + void matchNot(RefAST t, int ttype); + +public: + static void panic(); + + /** Parser error-reporting function can be overridden in subclass */ + virtual void reportError(const RecognitionException& ex); + + /** Parser error-reporting function can be overridden in subclass */ + virtual void reportError(const ANTLR_USE_NAMESPACE(std)string& s); + + /** Parser warning-reporting function can be overridden in subclass */ + virtual void reportWarning(const ANTLR_USE_NAMESPACE(std)string& s); + + /** Specify an object with support code (shared by + * Parser and TreeParser. Normally, the programmer + * does not play with this, using setASTNodeType instead. + */ +// void setASTFactory(ASTFactory f); + + /** Specify the type of node to create during tree building */ + void setASTNodeFactory(ASTFactory::factory_type factory); + +protected: + /** Utility class which allows tracing to work even when exceptions are + * thrown. + */ + class Tracer { + private: + TreeParser* parser; + ANTLR_USE_NAMESPACE(std)string text; + RefAST tree; + public: + Tracer(TreeParser* p,const ANTLR_USE_NAMESPACE(std)string& t, RefAST a) + : parser(p), text(t), tree(a) { parser->traceIn(text,tree); } + ~Tracer() + { parser->traceOut(text,tree); } + private: + Tracer(const Tracer&); // undefined + const Tracer& operator=(const Tracer&); // undefined + }; + +public: + void traceIndent(); + void traceIn(const ANTLR_USE_NAMESPACE(std)string& rname, RefAST t); + void traceOut(const ANTLR_USE_NAMESPACE(std)string& rname, RefAST t); + +private: + TreeParser(const TreeParser& other); + TreeParser& operator=(const TreeParser& other); +}; + +ANTLR_END_NAMESPACE + +#endif //INC_TreeParser_hpp__ diff --git a/poxml/antlr/antlr/TreeParserSharedInputState.hpp b/poxml/antlr/antlr/TreeParserSharedInputState.hpp new file mode 100644 index 00000000..8f7b0922 --- /dev/null +++ b/poxml/antlr/antlr/TreeParserSharedInputState.hpp @@ -0,0 +1,34 @@ +#ifndef INC_TreeParserSharedInputState_hpp__ +#define INC_TreeParserSharedInputState_hpp__ + +#include "antlr/config.hpp" +#include "antlr/RefCount.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** This object contains the data associated with an + * input AST. Multiple parsers + * share a single TreeParserSharedInputState to parse + * the same tree or to have the parser walk multiple + * trees. + */ +class TreeParserInputState { +public: + TreeParserInputState(); + ~TreeParserInputState(); + +public: + /** Are we guessing (guessing>0)? */ + int guessing; //= 0; + +private: + // we don't want these: + TreeParserInputState(const TreeParserInputState&); + TreeParserInputState& operator=(const TreeParserInputState&); +}; + +typedef RefCount TreeParserSharedInputState; + +ANTLR_END_NAMESPACE + +#endif //INC_TreeParserSharedInputState_hpp__ diff --git a/poxml/antlr/antlr/config.hpp b/poxml/antlr/antlr/config.hpp new file mode 100644 index 00000000..8ac94a3a --- /dev/null +++ b/poxml/antlr/antlr/config.hpp @@ -0,0 +1,168 @@ +#ifndef INC_config_hpp__ +#define INC_config_hpp__ + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +/* + * Just a simple configuration file to differentiate between the + * various compilers used. + */ + +/* + Some compilers do not accept namespaces std:: for example. + In this case, just define #define ANTLR_USE_NAMESPACE(_x_). + + See SunWorkShop 4.2 for example. + */ +#define ANTLR_USE_NAMESPACE(_x_) _x_:: +#define ANTLR_USING_NAMESPACE(_x_) using namespace _x_; +#define ANTLR_BEGIN_NAMESPACE(_x_) namespace _x_ { +#define ANTLR_END_NAMESPACE } +#define ANTLR_C_USING(_x_) + +#if defined(_MSC_VER) && !defined(__ICL) // Microsoft Visual C++ + +// This warning really gets on my nerves. +// It's the one about symbol longer than 256 chars, and it happens +// all the time with STL. +#pragma warning( disable : 4786 ) + +// Now, some defines for shortcomings in the MS compiler: + +// Not allowed to put 'static const int XXX=20;' in a class definition +#define NO_STATIC_CONSTS +// Using vector requires operator<(X,X) to be defined +#define NEEDS_OPERATOR_LESS_THAN +// No strcasecmp in the C library (so use stricmp instead) +// - Anyone know which is in which standard? +#define NO_STRCASECMP + +#endif + +#if defined(__ICL) +#define NO_STRCASECMP +#endif + +// +// SunPro Compiler (Using OBJECTSPACE STL) +// +#ifdef __SUNPRO_CC + +#if (__SUNPRO_CC >= 0x500) + +#define NEEDS_OPERATOR_LESS_THAN +#define NO_TEMPLATE_PARTS + +#else + +#undef namespace +#define namespace + + +#if (__SUNPRO_CC == 0x420) + +/* This code is specif to SunWspro Compiler 4.2, and will compile with + the objectspace 2.1 toolkit for Solaris2.6 */ +#define HAS_NOT_CASSERT_H +#define HAS_NOT_CSTRING_H +#define HAS_NOT_CCTYPE_H +#define HAS_NOT_CSTDIO_H +#define HAS_OSTREAM_H + +/* #define OS_SOLARIS_2_6 +#define OS_NO_WSTRING +#define OS_NO_ALLOCATORS +#define OS_MULTI_THREADED +#define OS_SOLARIS_NATIVE +#define OS_REALTIME +#define __OSVERSION__=5 +#define SVR4 +*/ + +// ObjectSpace + some specific templates constructions with stl. +/* #define OS_NO_ALLOCATOR */ + +// This great compiler does not have the namespace feature. +#undef ANTLR_USE_NAMESPACE +#define ANTLR_USE_NAMESPACE(_x_) +#undef ANTLR_USING_NAMESPACE +#define ANTLR_USING_NAMESPACE(_x_) +#undef ANTLR_BEGIN_NAMESPACE +#define ANTLR_BEGIN_NAMESPACE(_x_) +#undef ANTLR_END_NAMESPACE +#define ANTLR_END_NAMESPACE + +#endif + +#undef explicit +#define explicit + +#define exception os_exception +#define bad_exception os_bad_exception + +// Not allowed to put 'static const int XXX=20;' in a class definition +#define NO_STATIC_CONSTS +// Using vector requires operator<(X,X) to be defined +#define NEEDS_OPERATOR_LESS_THAN + +#endif + +#endif + +// +// Inprise C++ Builder 3.0 +// +#ifdef __BCPLUSPLUS__ + +#define NO_TEMPLATE_PARTS +#define NO_STRCASECMP +#endif + +#ifdef _AIX +#include +#endif + +// +// Metrowerks Codewarrior +// +#ifdef __MWERKS__ +#if (__MWERKS__ <= 0x2201) +#define NO_TEMPLATE_PARTS +#define ANTLR_REALLY_NO_STRCASECMP +#endif + +#undef ANTLR_C_USING +#define ANTLR_C_USING(_x_) using std:: ## _x_; +#endif + +#endif //INC_config_hpp__ diff --git a/poxml/antlr/configure.in b/poxml/antlr/configure.in new file mode 100644 index 00000000..66f8cf15 --- /dev/null +++ b/poxml/antlr/configure.in @@ -0,0 +1,22 @@ +AC_INIT(src/Parser.cpp) + +PACKAGE=libantlr +VERSION="2.7.1" +LIBANTLR_SO_VERSION=0:0:0 + +AM_INIT_AUTOMAKE($PACKAGE, $VERSION) + +AM_DISABLE_SHARED +AM_PROG_LIBTOOL +AC_PROG_CXX +AC_PROG_CXXCPP +AC_PROG_RANLIB + +test "$ac_cv_prog_gxx" = 'yes' && CXXFLAGS="$CXXFLAGS -W -Wall -pipe" + +AC_SUBST(LIBANTLR_SO_VERSION) +AC_SUBST(LIBTOOL_DEPS) + +AC_OUTPUT(Makefile \ + src/Makefile \ + antlr/Makefile) diff --git a/poxml/antlr/src/ANTLRException.cpp b/poxml/antlr/src/ANTLRException.cpp new file mode 100644 index 00000000..42632e71 --- /dev/null +++ b/poxml/antlr/src/ANTLRException.cpp @@ -0,0 +1,57 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1998 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1998 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/ANTLRException.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +ANTLRException::ANTLRException() : text("") +{} + +ANTLRException::ANTLRException(const ANTLR_USE_NAMESPACE(std)string& s) +: text(s) +{} + +ANTLRException::~ANTLRException() throw() +{} + +ANTLR_USE_NAMESPACE(std)string ANTLRException::toString() const +{ return text; } + +ANTLR_USE_NAMESPACE(std)string ANTLRException::getMessage() const +{ return text; } + +const char* ANTLRException::what() const throw() +{ return text.c_str(); } + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/ASTFactory.cpp b/poxml/antlr/src/ASTFactory.cpp new file mode 100644 index 00000000..e44386f7 --- /dev/null +++ b/poxml/antlr/src/ASTFactory.cpp @@ -0,0 +1,218 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1998 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1998 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/ASTFactory.hpp" +#include "antlr/CommonAST.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** AST Support code shared by TreeParser and Parser. + * We use delegation to share code (and have only one + * bit of code to maintain) rather than subclassing + * or superclassing (forces AST support code to be + * loaded even when you don't want to do AST stuff). + * + * Typically, setASTNodeType is used to specify the + * type of node to create, but you can override + * create to make heterogeneous nodes etc... + */ + +ASTFactory::ASTFactory() : nodeFactory(&CommonAST::factory) +{ +} + +/** Add a child to the current AST */ +void ASTFactory::addASTChild(ASTPair& currentAST, RefAST child) +{ + if (child) { + if (!currentAST.root) { + // Make new child the current root + currentAST.root = child; + } + else { + if (!currentAST.child) { + // Add new child to current root + currentAST.root->setFirstChild(child); + } + else { + currentAST.child->setNextSibling(child); + } + } + // Make new child the current child + currentAST.child = child; + currentAST.advanceChildToEnd(); + } +} +/** Create a new empty AST node; if the user did not specify + * an AST node type, then create a default one: CommonAST. + */ +RefAST ASTFactory::create() +{ + RefAST node = nodeFactory(); + node->setType(Token::INVALID_TYPE); + return node; +} + +RefAST ASTFactory::create(int type) +{ + RefAST t = nodeFactory(); + t->initialize(type,""); + return t; +} + +RefAST ASTFactory::create(int type, const ANTLR_USE_NAMESPACE(std)string& txt) +{ + RefAST t = nodeFactory(); + t->initialize(type,txt); + return t; +} + +/** Create a new empty AST node; if the user did not specify + * an AST node type, then create a default one: CommonAST. + */ +RefAST ASTFactory::create(RefAST tr) +{ + if (!tr) + return nullAST; + + RefAST t = nodeFactory(); + t->initialize(tr); + return t; +} + +RefAST ASTFactory::create(RefToken tok) +{ + RefAST t = nodeFactory(); + t->initialize(tok); + return t; +} +/** Copy a single node. clone() is not used because + * we want to return an AST not a plain object...a type + * safety issue. Further, we want to have all AST node + * creation go through the factory so creation can be + * tracked. Returns null if t is null. + */ +RefAST ASTFactory::dup(RefAST t) +{ + return create(t); // if t==null, create returns null +} + +/** Duplicate tree including siblings of root. */ +RefAST ASTFactory::dupList(RefAST t) +{ + RefAST result = dupTree(t); // if t == null, then result==null + RefAST nt = result; + while (t) { // for each sibling of the root + t = t->getNextSibling(); + nt->setNextSibling(dupTree(t)); // dup each subtree, building new tree + nt = nt->getNextSibling(); + } + return result; +} +/**Duplicate a tree, assuming this is a root node of a tree-- + * duplicate that node and what's below; ignore siblings of root node. + */ +RefAST ASTFactory::dupTree(RefAST t) +{ + RefAST result = dup(t); // make copy of root + // copy all children of root. + if (t) { + result->setFirstChild( dupList(t->getFirstChild()) ); + } + return result; +} +/** Make a tree from a list of nodes. The first element in the + * array is the root. If the root is null, then the tree is + * a simple list not a tree. Handles null children nodes correctly. + * For example, build(a, b, null, c) yields tree (a b c). build(null,a,b) + * yields tree (nil a b). + */ +RefAST ASTFactory::make(ANTLR_USE_NAMESPACE(std)vector nodes) +{ + if ( nodes.size()==0 ) + return RefAST(nullASTptr); + RefAST root = nodes[0]; + RefAST tail = RefAST(nullASTptr); + if (root) { + root->setFirstChild(RefAST(nullASTptr)); // don't leave any old pointers set + } + // link in children; + for (unsigned int i=1; isetFirstChild(nodes[i]); + tail = root->getFirstChild(); + } + else { + tail->setNextSibling(nodes[i]); + tail = tail->getNextSibling(); + } + // Chase tail to last sibling + while (tail->getNextSibling()) { + tail = tail->getNextSibling(); + } + } + return root; +} +/** Make a tree from a list of nodes, where the nodes are contained + * in an ASTArray object + */ +RefAST ASTFactory::make(ASTArray* nodes) +{ + RefAST ret = make(nodes->array); + delete nodes; + return ret; +} +/** Make an AST the root of current AST */ +void ASTFactory::makeASTRoot(ASTPair& currentAST, RefAST root) +{ + if (root) { + // Add the current root as a child of new root + root->addChild(currentAST.root); + // The new current child is the last sibling of the old root + currentAST.child = currentAST.root; + currentAST.advanceChildToEnd(); + // Set the new root + currentAST.root = root; + } +} +void ASTFactory::setASTNodeFactory(factory_type factory) +{ + nodeFactory = factory; +} + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/ASTRefCount.cpp b/poxml/antlr/src/ASTRefCount.cpp new file mode 100644 index 00000000..1da98306 --- /dev/null +++ b/poxml/antlr/src/ASTRefCount.cpp @@ -0,0 +1,74 @@ +#include "antlr/ASTRefCount.hpp" +#include "antlr/AST.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +ASTRef::ASTRef(AST* p) + : ptr(p), count(1) +{ + if (p && !p->ref) + p->ref = this; +} + +ASTRef::~ASTRef() +{ + delete ptr; +} + +ASTRef* ASTRef::increment() +{ + ++count; + return this; +} + +bool ASTRef::decrement() +{ + return (--count==0); +} + +ASTRef* ASTRef::getRef(const AST* p) +{ + if (p) { + AST* pp = const_cast(p); + if (pp->ref) + return pp->ref->increment(); + else + return new ASTRef(pp); + } else + return 0; +} + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/BaseAST.cpp b/poxml/antlr/src/BaseAST.cpp new file mode 100644 index 00000000..4080e0e8 --- /dev/null +++ b/poxml/antlr/src/BaseAST.cpp @@ -0,0 +1,320 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1998 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1998 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/BaseAST.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +//bool BaseAST::verboseStringConversion; +//ANTLR_USE_NAMESPACE(std)vector BaseAST::tokenNames; + +void BaseAST::addChild(RefAST c) +{ + if (!c) + return; + RefBaseAST tmp=down; + if (tmp) { + while (tmp->right) + tmp=tmp->right; + tmp->right=c; + } else { + down=c; + } +} + +void BaseAST::doWorkForFindAll( + ANTLR_USE_NAMESPACE(std)vector& v, + RefAST target,bool partialMatch) +{ + // Start walking sibling lists, looking for matches. + for (RefAST sibling=this; + sibling; + sibling=sibling->getNextSibling()) + { + if ( (partialMatch && sibling->equalsTreePartial(target)) || + (!partialMatch && sibling->equalsTree(target)) ) { + v.push_back(sibling); + } + // regardless of match or not, check any children for matches + if ( sibling->getFirstChild() ) { + RefBaseAST(sibling->getFirstChild())->doWorkForFindAll(v, target, partialMatch); + } + } + +} + +/** Is node t equal to this in terms of token type and text? */ +bool BaseAST::equals(RefAST t) const +{ + if (!t) + return false; + return (getText() == t->getText()) && (getType() == t->getType()); +} + +/** Is t an exact structural and equals() match of this tree. The + * 'this' reference is considered the start of a sibling list. + */ +bool BaseAST::equalsList(RefAST t) const +{ + // the empty tree is not a match of any non-null tree. + if (!t) + return false; + + // Otherwise, start walking sibling lists. First mismatch, return false. + RefAST sibling=this; + for (;sibling && t; + sibling=sibling->getNextSibling(), t=t->getNextSibling()) { + // as a quick optimization, check roots first. + if (!sibling->equals(t)) + return false; + // if roots match, do full list match test on children. + if (sibling->getFirstChild()) { + if (!sibling->getFirstChild()->equalsList(t->getFirstChild())) + return false; + } + // sibling has no kids, make sure t doesn't either + else if (t->getFirstChild()) + return false; + } + + if (!sibling && !t) + return true; + + // one sibling list has more than the other + return false; +} + +/** Is 'sub' a subtree of this list? + * The siblings of the root are NOT ignored. + */ +bool BaseAST::equalsListPartial(RefAST sub) const +{ + // the empty tree is always a subset of any tree. + if (!sub) + return true; + + // Otherwise, start walking sibling lists. First mismatch, return false. + RefAST sibling=this; + for (;sibling && sub; + sibling=sibling->getNextSibling(), sub=sub->getNextSibling()) { + // as a quick optimization, check roots first. + if (!sibling->equals(sub)) + return false; + // if roots match, do partial list match test on children. + if (sibling->getFirstChild()) + if (!sibling->getFirstChild()->equalsListPartial(sub->getFirstChild())) + return false; + } + + if (!sibling && sub) + // nothing left to match in this tree, but subtree has more + return false; + + // either both are null or sibling has more, but subtree doesn't + return true; +} + +/** Is tree rooted at 'this' equal to 't'? The siblings + * of 'this' are ignored. + */ +bool BaseAST::equalsTree(RefAST t) const +{ + // check roots first + if (!equals(t)) + return false; + // if roots match, do full list match test on children. + if (getFirstChild()) { + if (!getFirstChild()->equalsList(t->getFirstChild())) + return false; + } + // sibling has no kids, make sure t doesn't either + else if (t->getFirstChild()) + return false; + + return true; +} + +/** Is 'sub' a subtree of the tree rooted at 'this'? The siblings + * of 'this' are ignored. + */ +bool BaseAST::equalsTreePartial(RefAST sub) const +{ + // the empty tree is always a subset of any tree. + if (!sub) + return true; + + // check roots first + if (!equals(sub)) + return false; + // if roots match, do full list partial match test on children. + if (getFirstChild()) + if (!getFirstChild()->equalsListPartial(sub->getFirstChild())) + return false; + + return true; +} + +/** Walk the tree looking for all exact subtree matches. Return + * an ASTEnumerator that lets the caller walk the list + * of subtree roots found herein. + */ +ANTLR_USE_NAMESPACE(std)vector BaseAST::findAll(RefAST target) +{ + ANTLR_USE_NAMESPACE(std)vector roots; + + // the empty tree cannot result in an enumeration + if (target) { + doWorkForFindAll(roots,target,false); // find all matches recursively + } + + return roots; +} + +/** Walk the tree looking for all subtrees. Return + * an ASTEnumerator that lets the caller walk the list + * of subtree roots found herein. + */ +ANTLR_USE_NAMESPACE(std)vector BaseAST::findAllPartial(RefAST target) +{ + ANTLR_USE_NAMESPACE(std)vector roots; + + // the empty tree cannot result in an enumeration + if (target) { + doWorkForFindAll(roots,target,true); // find all matches recursively + } + + return roots; +} + +RefAST BaseAST::getFirstChild() const +{ + return RefAST(down); +} + +RefAST BaseAST::getNextSibling() const +{ + return RefAST(right); +} + +ANTLR_USE_NAMESPACE(std)string BaseAST::getText() const +{ + return ""; +} + +int BaseAST::getType() const +{ + return 0; +} + +void BaseAST::removeChildren() +{ + down=nullAST; +} + +void BaseAST::setFirstChild(RefAST c) +{ + down=c; +} + +void BaseAST::setNextSibling(RefAST n) +{ + right=n; +} + +void BaseAST::setText(const ANTLR_USE_NAMESPACE(std)string& txt) +{ +} + +void BaseAST::setType(int type) +{ +} + +//void BaseAST::setVerboseStringConversion(bool verbose, +// const ANTLR_USE_NAMESPACE(std)vector& names) +//{ +// verboseStringConversion = verbose; +// tokenNames = names; +//} + +ANTLR_USE_NAMESPACE(std)string BaseAST::toString() const +{ +// if ( verboseStringConversion && +// !getText().equalsIgnoreCase(tokenNames[getType()]) && +// !getText().equalsIgnoreCase(Tool.stripFrontBack(tokenNames[getType()],"\"","\"")) ) { +// b.append('['); +// b.append(getText()); +// b.append(",<"); +// b.append(tokenNames[getType()]); +// b.append(">]"); +// return b.toString(); +// } + return getText(); +} + +ANTLR_USE_NAMESPACE(std)string BaseAST::toStringList() const +{ + ANTLR_USE_NAMESPACE(std)string ts=""; + if (getFirstChild()) { + ts+=" ( "; + ts+=toString(); + ts+=getFirstChild()->toStringList(); + ts+=" )"; + } else { + ts+=" "; + ts+=toString(); + } + if (getNextSibling()) + ts+=getNextSibling()->toStringList(); + return ts; +} + +ANTLR_USE_NAMESPACE(std)string BaseAST::toStringTree() const +{ + ANTLR_USE_NAMESPACE(std)string ts=""; + if (getFirstChild()) { + ts+=" ( "; + ts+=toString(); + ts+=getFirstChild()->toStringList(); + ts+=" )"; + } else { + ts+=" "; + ts+=toString(); + } + return ts; +} + +// this is nasty, but it makes the code generation easier +RefAST nullAST; +AST* const nullASTptr=0; + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/BitSet.cpp b/poxml/antlr/src/BitSet.cpp new file mode 100644 index 00000000..a0a1b110 --- /dev/null +++ b/poxml/antlr/src/BitSet.cpp @@ -0,0 +1,76 @@ +#include "antlr/BitSet.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** A BitSet to replace java.util.BitSet. + * Primary differences are that most set operators return new sets + * as opposed to oring and anding "in place". Further, a number of + * operations were added. I cannot contain a BitSet because there + * is no way to access the internal bits (which I need for speed) + * and, because it is final, I cannot subclass to add functionality. + * Consider defining set degree. Without access to the bits, I must + * call a method n times to test the ith bit...ack! + * + * Also seems like or() from util is wrong when size of incoming set is bigger + * than this.length. + * + * This is a C++ version of the Java class described above, with only + * a handful of the methods implemented, because we don't need the + * others at runtime. It's really just a wrapper around vector, + * which should probably be changed to a wrapper around bitset, once + * bitset is more widely available. + * + * @author Terence Parr, MageLang Institute + * @author
    Pete Wells + */ +BitSet::BitSet(int nbits) + : storage(nbits) +{ + for (int i=0;i>5] & (1UL << (i&31))) ? true : false; + } +} + +BitSet::~BitSet() +{ +} + +void BitSet::add(int el) +{ + if ( el < 0 ) + throw ANTLR_USE_NAMESPACE(std)out_of_range(ANTLR_USE_NAMESPACE(std)string("antlr::BitSet.cpp line 49")); + + if( static_cast(el) >= storage.size() ) + storage.resize( el+1, false ); + + storage[el] = true; +} + +bool BitSet::member(int el) const +{ + if ( el < 0 || static_cast(el) >= storage.size()) + return false; + + return storage[el]; +} + +ANTLR_USE_NAMESPACE(std)vector BitSet::toArray() const +{ + ANTLR_USE_NAMESPACE(std)vector elems; + for (unsigned int i=0;iSOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1998 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1998 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +/**A Stream of characters fed to the lexer from a InputStream that can + * be rewound via mark()/rewind() methods. + *

    + * A dynamic array is used to buffer up all the input characters. Normally, + * "k" characters are stored in the buffer. More characters may be stored during + * guess mode (testing syntactic predicate), or when LT(i>k) is referenced. + * Consumption of characters is deferred. In other words, reading the next + * character is not done by conume(), but deferred until needed by LA or LT. + *

    + * + * @see antlr.CharQueue + */ + +#include "antlr/CharBuffer.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** Create a character buffer */ +CharBuffer::CharBuffer(ANTLR_USE_NAMESPACE(std)istream& input_) +: input(input_) +{} + +/** Get the next character from the stream */ +int CharBuffer::getChar() +{ +// try { + return input.get(); +// } +// catch (???& e) { +// throw CharStreamIOException(e); +// } +} + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/CharScanner.cpp b/poxml/antlr/src/CharScanner.cpp new file mode 100644 index 00000000..ff40138d --- /dev/null +++ b/poxml/antlr/src/CharScanner.cpp @@ -0,0 +1,430 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1998 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1998 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/CharScanner.hpp" +#include "antlr/CommonToken.hpp" +#include "antlr/MismatchedCharException.hpp" +#include + +#ifdef HAS_NOT_CCTYPE_H +#include +#else +#include +#endif + +#include + +#ifdef HAS_NOT_CSTRING_H +#include +#else +#include +#endif +#include + +ANTLR_BEGIN_NAMESPACE(antlr) +ANTLR_C_USING(exit) +ANTLR_C_USING(tolower) + +#ifdef ANTLR_REALLY_NO_STRCASECMP +// Apparently, neither strcasecmp nor stricmp is standard, and Codewarrior +// on the mac has neither... +inline int strcasecmp(const char *s1, const char *s2) +{ + while (true) + { + char c1 = tolower(*s1++), + c2 = tolower(*s2++); + if (c1 < c2) return -1; + if (c1 > c2) return 1; + if (c1 == 0) return 0; + } +} +#else +#ifdef NO_STRCASECMP +ANTLR_C_USING(stricmp) +#else +ANTLR_C_USING(strcasecmp) +#endif +#endif + +CharScannerLiteralsLess::CharScannerLiteralsLess(const CharScanner* theScanner) +: scanner(theScanner) +{} + +bool CharScannerLiteralsLess::operator() (const ANTLR_USE_NAMESPACE(std)string& x,const ANTLR_USE_NAMESPACE(std)string& y) const +{ + if (scanner->getCaseSensitiveLiterals()) { + return ANTLR_USE_NAMESPACE(std)less()(x,y); + } else { +#ifdef NO_STRCASECMP + return (stricmp(x.c_str(),y.c_str())<0); +#else + return (strcasecmp(x.c_str(),y.c_str())<0); +#endif + } +} + +CharScanner::CharScanner(InputBuffer& cb) + : saveConsumedInput(true) //, caseSensitiveLiterals(true) + , literals(CharScannerLiteralsLess(this)) + , inputState(new LexerInputState(cb)) + , commitToPath(false) + , traceDepth(0) +{ + setTokenObjectFactory(&CommonToken::factory); +} + +CharScanner::CharScanner(InputBuffer* cb) + : saveConsumedInput(true) //, caseSensitiveLiterals(true) + , literals(CharScannerLiteralsLess(this)) + , inputState(new LexerInputState(cb)) + , commitToPath(false) + , traceDepth(0) +{ + setTokenObjectFactory(&CommonToken::factory); +} + +CharScanner::CharScanner(const LexerSharedInputState& state) + : saveConsumedInput(true) //, caseSensitiveLiterals(true) + , literals(CharScannerLiteralsLess(this)) + , inputState(state) + , commitToPath(false) + , traceDepth(0) +{ + setTokenObjectFactory(&CommonToken::factory); +} + +CharScanner::~CharScanner() +{ +} + +void CharScanner::append(char c) +{ + if (saveConsumedInput) { + int l = text.length(); + if ((l%256) == 0) text.reserve(l+256); + text.replace(l,0,&c,1); + } +} + +void CharScanner::append(const ANTLR_USE_NAMESPACE(std)string& s) +{ + if (saveConsumedInput) + text+=s; +} + +void CharScanner::commit() +{ + inputState->getInput().commit(); +} + +void CharScanner::consume() +{ + if (inputState->guessing == 0) { + int c = LA(1); + if (caseSensitive) { + append(c); + } else { + // use input.LA(), not LA(), to get original case + // CharScanner.LA() would toLower it. + append(inputState->getInput().LA(1)); + } + if (c == '\t') { + tab(); + } + else { + inputState->column++; + } + } + inputState->getInput().consume(); +} + +/** Consume chars until one matches the given char */ +void CharScanner::consumeUntil(int c) +{ + while (LA(1) != EOF_CHAR && LA(1) != c) + { + consume(); + } +} + +/** Consume chars until one matches the given set */ +void CharScanner::consumeUntil(const BitSet& set) +{ + while (LA(1) != EOF_CHAR && !set.member(LA(1))) { + consume(); + } +} + +bool CharScanner::getCaseSensitive() const +{ return caseSensitive; } + +//bool CharScanner::getCaseSensitiveLiterals() const +//{ return caseSensitiveLiterals; } + +int CharScanner::getColumn() const +{ return inputState->column; } + +void CharScanner::setColumn(int c) +{ inputState->column = c; } + +bool CharScanner::getCommitToPath() const +{ return commitToPath; } + +const ANTLR_USE_NAMESPACE(std)string& CharScanner::getFilename() const +{ return inputState->filename; } + +InputBuffer& CharScanner::getInputBuffer() +{ return inputState->getInput(); } + +LexerSharedInputState CharScanner::getInputState() +{ return inputState; } + +int CharScanner::getLine() const +{ return inputState->line; } + +/** return a copy of the current text buffer */ +const ANTLR_USE_NAMESPACE(std)string& CharScanner::getText() const +{ return text; } + +RefToken CharScanner::getTokenObject() const +{ return _returnToken; } + +RefToken CharScanner::makeToken(int t) +{ + RefToken tok=tokenFactory(); + tok->setType(t); + tok->setColumn(inputState->tokenStartColumn); + tok->setLine(inputState->tokenStartLine); + return tok; +} + +int CharScanner::mark() +{ + return inputState->getInput().mark(); +} + +void CharScanner::match(int c) +{ + if ( LA(1) != c ) { + throw MismatchedCharException(LA(1),c,false,this); + } + consume(); +} + +void CharScanner::match(const BitSet& b) +{ + if (!b.member(LA(1))) { + throw MismatchedCharException(LA(1),b,false,this); + } + consume(); +} + +void CharScanner::match(const ANTLR_USE_NAMESPACE(std)string& s) +{ + int len = s.length(); + for (int i=0; ic2) { + throw MismatchedCharException(LA(1),c1,c2,false,this); + } + consume(); +} + +void CharScanner::newline() +{ + ++inputState->line; + inputState->column=1; +} + +/** advance the current column number by an appropriate amount. + * If you do not override this to specify how much to jump for + * a tab, then tabs are counted as one char. This method is + * called from consume(). + */ +void CharScanner::tab() { + // update inputState->column as function of + // inputState->column and tab stops. + // For example, if tab stops are columns 1 and 5 etc... + // and column is 3, then add 2 to column. + ++inputState->column; +} + +void CharScanner::panic() +{ + ANTLR_USE_NAMESPACE(std)cerr << "CharScanner: panic" << ANTLR_USE_NAMESPACE(std)endl; + exit(1); +} + +void CharScanner::panic(const ANTLR_USE_NAMESPACE(std)string& s) +{ + ANTLR_USE_NAMESPACE(std)cerr << "CharScanner: panic: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; + exit(1); +} + +/** Report exception errors caught in nextToken() */ +void CharScanner::reportError(const RecognitionException& ex) +{ + ANTLR_USE_NAMESPACE(std)cerr << ex.toString().c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +/** Parser error-reporting function can be overridden in subclass */ +void CharScanner::reportError(const ANTLR_USE_NAMESPACE(std)string& s) +{ + if (getFilename().empty()) + ANTLR_USE_NAMESPACE(std)cerr << "error: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; + else + ANTLR_USE_NAMESPACE(std)cerr << getFilename().c_str() << ": error: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +/** Parser warning-reporting function can be overridden in subclass */ +void CharScanner::reportWarning(const ANTLR_USE_NAMESPACE(std)string& s) +{ + if (getFilename().empty()) + ANTLR_USE_NAMESPACE(std)cerr << "warning: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; + else + ANTLR_USE_NAMESPACE(std)cerr << getFilename().c_str() << ": warning: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +void CharScanner::resetText() +{ + text=""; + inputState->tokenStartColumn = inputState->column; + inputState->tokenStartLine = inputState->line; +} + +void CharScanner::rewind(int pos) +{ + inputState->getInput().rewind(pos); +} + +void CharScanner::setCaseSensitive(bool t) +{ + caseSensitive = t; +} + +void CharScanner::setCommitToPath(bool commit) +{ + commitToPath = commit; +} + +void CharScanner::setFilename(const ANTLR_USE_NAMESPACE(std)string& f) +{ inputState->filename=f; } + +void CharScanner::setInputState(LexerSharedInputState state) +{ inputState = state; } + +void CharScanner::setLine(int l) +{ inputState->line=l; } + +void CharScanner::setText(const ANTLR_USE_NAMESPACE(std)string& s) +{ text=s; } + +void CharScanner::setTokenObjectFactory(factory_type factory) +{ tokenFactory=factory; } + +/** Test the token text against the literals table + * Override this method to perform a different literals test */ +int CharScanner::testLiteralsTable(int ttype) const +{ + ANTLR_USE_NAMESPACE(std)map::const_iterator i = literals.find(text); + if (i != literals.end()) + ttype = (*i).second; + return ttype; +} + +/** Test the text passed in against the literals table + * Override this method to perform a different literals test + * This is used primarily when you want to test a portion of + * a token. + */ +int CharScanner::testLiteralsTable(const ANTLR_USE_NAMESPACE(std)string& text_, int ttype) const +{ + ANTLR_USE_NAMESPACE(std)map::const_iterator i = literals.find(text_); + if (i != literals.end()) + ttype = (*i).second; + return ttype; +} + +/** Override this method to get more specific case handling */ +int CharScanner::toLower(int c) const +{ + return tolower(c); +} + +void CharScanner::traceIndent() +{ + for( int i = 0; i < traceDepth; i++ ) + ANTLR_USE_NAMESPACE(std)cout << " "; +} + +void CharScanner::traceIn(const ANTLR_USE_NAMESPACE(std)string& rname) +{ + traceDepth++; + traceIndent(); + ANTLR_USE_NAMESPACE(std)cout << "> lexer " << rname.c_str() << "; c==" << LA(1) << ANTLR_USE_NAMESPACE(std)endl; +} + +void CharScanner::traceOut(const ANTLR_USE_NAMESPACE(std)string& rname) +{ + traceIndent(); + ANTLR_USE_NAMESPACE(std)cout << "< lexer " << rname.c_str() << "; c==" << LA(1) << ANTLR_USE_NAMESPACE(std)endl; + traceDepth--; +} + +void CharScanner::uponEOF() +{ +} + +#ifndef NO_STATIC_CONSTS +const int CharScanner::NO_CHAR; +const int CharScanner::EOF_CHAR; +#endif + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/CommonAST.cpp b/poxml/antlr/src/CommonAST.cpp new file mode 100644 index 00000000..3a4067e3 --- /dev/null +++ b/poxml/antlr/src/CommonAST.cpp @@ -0,0 +1,100 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1998 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1998 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/config.hpp" +#include "antlr/CommonAST.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +CommonAST::CommonAST() +: BaseAST(), + ttype( Token::INVALID_TYPE ), + text("") +{ +} + +CommonAST::CommonAST(RefToken t) +: BaseAST(), + ttype( t->getType() ), + text( t->getText() ) +{ +} + +CommonAST::~CommonAST() +{ +} + +ANTLR_USE_NAMESPACE(std)string CommonAST::getText() const +{ + return text; +} + +int CommonAST::getType() const +{ + return ttype; +} + +void CommonAST::initialize(int t,const ANTLR_USE_NAMESPACE(std)string& txt) +{ + setType(t); + setText(txt); +} + +void CommonAST::initialize(RefAST t) +{ + setType(t->getType()); + setText(t->getText()); +} + +void CommonAST::initialize(RefToken t) +{ + setType(t->getType()); + setText(t->getText()); +} + +void CommonAST::setText(const ANTLR_USE_NAMESPACE(std)string& txt) +{ + text = txt; +} + +void CommonAST::setType(int type) +{ + ttype = type; +} + +RefAST CommonAST::factory() +{ + return RefAST(new CommonAST); +} + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/CommonASTWithHiddenTokens.cpp b/poxml/antlr/src/CommonASTWithHiddenTokens.cpp new file mode 100644 index 00000000..d6c242d2 --- /dev/null +++ b/poxml/antlr/src/CommonASTWithHiddenTokens.cpp @@ -0,0 +1,29 @@ +#include "antlr/config.hpp" +#include "antlr/CommonASTWithHiddenTokens.hpp" +#include "antlr/CommonHiddenStreamToken.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +void CommonASTWithHiddenTokens::initialize(int t,const ANTLR_USE_NAMESPACE(std)string& txt) +{ + CommonAST::initialize(t,txt); +} + +void CommonASTWithHiddenTokens::initialize(RefAST t) +{ + CommonAST::initialize(t); +} + +void CommonASTWithHiddenTokens::initialize(RefToken t) +{ + CommonAST::initialize(t); + hiddenBefore = static_cast(t.get())->getHiddenBefore(); + hiddenAfter = static_cast(t.get())->getHiddenAfter(); +} + +RefAST CommonASTWithHiddenTokens::factory() +{ + return RefAST(new CommonASTWithHiddenTokens); +} + +ANTLR_END_NAMESPACE diff --git a/poxml/antlr/src/CommonHiddenStreamToken.cpp b/poxml/antlr/src/CommonHiddenStreamToken.cpp new file mode 100644 index 00000000..d33927cc --- /dev/null +++ b/poxml/antlr/src/CommonHiddenStreamToken.cpp @@ -0,0 +1,46 @@ +#include "antlr/CommonHiddenStreamToken.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +CommonHiddenStreamToken::CommonHiddenStreamToken() +: CommonToken() +{ +} + +CommonHiddenStreamToken::CommonHiddenStreamToken(int t, const ANTLR_USE_NAMESPACE(std)string& txt) +: CommonToken(t,txt) +{ +} + +CommonHiddenStreamToken::CommonHiddenStreamToken(const ANTLR_USE_NAMESPACE(std)string& s) +: CommonToken(s) +{ +} + +RefToken CommonHiddenStreamToken::getHiddenAfter() +{ + return hiddenAfter; +} + +RefToken CommonHiddenStreamToken::getHiddenBefore() +{ + return hiddenBefore; +} + +RefToken CommonHiddenStreamToken::factory() +{ + return RefToken(new CommonHiddenStreamToken); +} + +void CommonHiddenStreamToken::setHiddenAfter(RefToken t) +{ + hiddenAfter = t; +} + +void CommonHiddenStreamToken::setHiddenBefore(RefToken t) +{ + hiddenBefore = t; +} + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/CommonToken.cpp b/poxml/antlr/src/CommonToken.cpp new file mode 100644 index 00000000..ff60bd79 --- /dev/null +++ b/poxml/antlr/src/CommonToken.cpp @@ -0,0 +1,81 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1998 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1998 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/CommonToken.hpp" +#include "antlr/String.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +CommonToken::CommonToken() : Token(), line(1), col(1), text("") +{} + +CommonToken::CommonToken(int t, const ANTLR_USE_NAMESPACE(std)string& txt) + : Token(t), line(1), col(1), text(txt) +{} + +CommonToken::CommonToken(const ANTLR_USE_NAMESPACE(std)string& s) + : Token(), line(1), col(1), text(s) +{} + +int CommonToken::getLine() const +{ return line; } + +ANTLR_USE_NAMESPACE(std)string CommonToken::getText() const +{ return text; } + +void CommonToken::setLine(int l) +{ line=l; } + +void CommonToken::setText(const ANTLR_USE_NAMESPACE(std)string& s) +{ text=s; } + +ANTLR_USE_NAMESPACE(std)string CommonToken::toString() const +{ + return "[\""+getText()+"\",<"+type+">,line="+line+"]"; +} + +int CommonToken::getColumn() const +{ return col; } + +void CommonToken::setColumn(int c) +{ col=c; } + +bool CommonToken::isInvalid() const +{ return type==INVALID_TYPE; } + +RefToken CommonToken::factory() +{ + return RefToken(new CommonToken); +} + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/InputBuffer.cpp b/poxml/antlr/src/InputBuffer.cpp new file mode 100644 index 00000000..058c32ab --- /dev/null +++ b/poxml/antlr/src/InputBuffer.cpp @@ -0,0 +1,109 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1998 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1998 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +/**A Stream of characters fed to the lexer from a InputStream that can + * be rewound via mark()/rewind() methods. + *

    + * A dynamic array is used to buffer up all the input characters. Normally, + * "k" characters are stored in the buffer. More characters may be stored during + * guess mode (testing syntactic predicate), or when LT(i>k) is referenced. + * Consumption of characters is deferred. In other words, reading the next + * character is not done by conume(), but deferred until needed by LA or LT. + *

    + * + * @see antlr.CharQueue + */ + +#include "antlr/InputBuffer.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** Create a character buffer */ +InputBuffer::InputBuffer() +: nMarkers(0), markerOffset(0), numToConsume(0) +{} + +/** This method updates the state of the input buffer so that + * the text matched since the most recent mark() is no longer + * held by the buffer. So, you either do a mark/rewind for + * failed predicate or mark/commit to keep on parsing without + * rewinding the input. + */ +void InputBuffer::commit() +{ + nMarkers--; +} + +/** Mark another character for deferred consumption */ +void InputBuffer::consume() +{ + numToConsume++; +} + +/** Ensure that the character buffer is sufficiently full */ +void InputBuffer::fill(int amount) +{ + syncConsume(); + // Fill the buffer sufficiently to hold needed characters + while (queue.entries() < amount + markerOffset) { + // Append the next character + queue.append(getChar()); + } +} + +bool InputBuffer::isMarked() const +{ + return (nMarkers != 0); +} + +/**Return an integer marker that can be used to rewind the buffer to + * its current state. + */ +int InputBuffer::mark() +{ + syncConsume(); + nMarkers++; + return markerOffset; +} + +/**Rewind the character buffer to a marker. + * @param mark Marker returned previously from mark() + */ +void InputBuffer::rewind(int mark) +{ + syncConsume(); + markerOffset = mark; + nMarkers--; +} + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/LLkParser.cpp b/poxml/antlr/src/LLkParser.cpp new file mode 100644 index 00000000..2f21cd8b --- /dev/null +++ b/poxml/antlr/src/LLkParser.cpp @@ -0,0 +1,105 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1998 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1998 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/LLkParser.hpp" +#include + +ANTLR_BEGIN_NAMESPACE(antlr) + +/**An LL(k) parser. + * + * @see antlr.Token + * @see antlr.TokenBuffer + * @see antlr.LL1Parser + */ + +// LLkParser(int k_); + +LLkParser::LLkParser(const ParserSharedInputState& state, int k_) +: Parser(state), k(k_) +{} + +LLkParser::LLkParser(TokenBuffer& tokenBuf, int k_) +: Parser(tokenBuf), k(k_) +{} + +LLkParser::LLkParser(TokenStream& lexer, int k_) +: Parser(new TokenBuffer(lexer)), k(k_) +{ +} + +/**Consume another token from the input stream. Can only write sequentially! + * If you need 3 tokens ahead, you must consume() 3 times. + *

    + * Note that it is possible to overwrite tokens that have not been matched. + * For example, calling consume() 3 times when k=2, means that the first token + * consumed will be overwritten with the 3rd. + */ +void LLkParser::consume() +{ inputState->getInput().consume(); } + +int LLkParser::LA(int i) +{ return inputState->getInput().LA(i); } + +RefToken LLkParser::LT(int i) +{ return inputState->getInput().LT(i); } + +void LLkParser::trace(const ANTLR_USE_NAMESPACE(std)string& ee, const ANTLR_USE_NAMESPACE(std)string& rname) +{ + traceIndent(); + + ANTLR_USE_NAMESPACE(std)cout << ee.c_str() << rname.c_str() << ((inputState->guessing>0)?"; [guessing]":"; "); + + for (int i = 1; i <= k; i++) + { + if (i != 1) { + ANTLR_USE_NAMESPACE(std)cout << ", "; + } + ANTLR_USE_NAMESPACE(std)cout << "LA(" << i << ")==" << LT(i)->getText().c_str(); + } + + ANTLR_USE_NAMESPACE(std)cout << ANTLR_USE_NAMESPACE(std)endl; +} + +void LLkParser::traceIn(const ANTLR_USE_NAMESPACE(std)string& rname) +{ + traceDepth++; + trace("> ",rname); +} + +void LLkParser::traceOut(const ANTLR_USE_NAMESPACE(std)string& rname) +{ + trace("< ",rname); + traceDepth--; +} + +ANTLR_END_NAMESPACE diff --git a/poxml/antlr/src/LexerSharedInputState.cpp b/poxml/antlr/src/LexerSharedInputState.cpp new file mode 100644 index 00000000..a95f33a8 --- /dev/null +++ b/poxml/antlr/src/LexerSharedInputState.cpp @@ -0,0 +1,55 @@ +#include "antlr/LexerSharedInputState.hpp" +#include "antlr/CharBuffer.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** This object contains the data associated with an + * input stream of characters. Multiple lexers + * share a single LexerSharedInputState to lex + * the same input stream. + */ + +LexerInputState::LexerInputState(InputBuffer* inbuf) +: column(1) +, line(1) +, tokenStartColumn(1) +, tokenStartLine(1) +, guessing(0) +, filename("") +, input(inbuf) +, inputResponsible(true) +{ +} + +LexerInputState::LexerInputState(InputBuffer& inbuf) +: column(1) +, line(1) +, tokenStartColumn(1) +, tokenStartLine(1) +, guessing(0) +, filename("") +, input(&inbuf) +, inputResponsible(false) +{ +} + +LexerInputState::LexerInputState(ANTLR_USE_NAMESPACE(std)istream& in) +: column(1) +, line(1) +, tokenStartColumn(1) +, tokenStartLine(1) +, guessing(0) +, filename("") +, input(new CharBuffer(in)) +, inputResponsible(true) +{ +} + +LexerInputState::~LexerInputState() +{ + if (inputResponsible) + delete input; +} + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/Makefile.am b/poxml/antlr/src/Makefile.am new file mode 100644 index 00000000..7a5d2426 --- /dev/null +++ b/poxml/antlr/src/Makefile.am @@ -0,0 +1,39 @@ + +# Make #include work.. +INCLUDES=-I$(srcdir)/.. +KDE_CXXFLAGS = $(USE_EXCEPTIONS) + +noinst_LTLIBRARIES = libantlr.la + +libantlr_la_LDFLAGS = -no-undefined + +libantlr_la_SOURCES = \ + ANTLRException.cpp \ + ASTFactory.cpp \ + ASTRefCount.cpp \ + BaseAST.cpp \ + BitSet.cpp \ + CharBuffer.cpp \ + CharScanner.cpp \ + CommonAST.cpp \ + CommonASTWithHiddenTokens.cpp \ + CommonHiddenStreamToken.cpp \ + CommonToken.cpp \ + InputBuffer.cpp \ + LLkParser.cpp \ + LexerSharedInputState.cpp \ + MismatchedCharException.cpp \ + MismatchedTokenException.cpp \ + NoViableAltException.cpp \ + NoViableAltForCharException.cpp \ + Parser.cpp \ + ParserSharedInputState.cpp \ + RecognitionException.cpp \ + String.cpp \ + Token.cpp \ + TokenBuffer.cpp \ + TokenStreamBasicFilter.cpp \ + TokenStreamHiddenTokenFilter.cpp \ + TokenStreamSelector.cpp \ + TreeParser.cpp \ + TreeParserSharedInputState.cpp diff --git a/poxml/antlr/src/MismatchedCharException.cpp b/poxml/antlr/src/MismatchedCharException.cpp new file mode 100644 index 00000000..4dede0e8 --- /dev/null +++ b/poxml/antlr/src/MismatchedCharException.cpp @@ -0,0 +1,153 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1999 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1999 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/MismatchedCharException.hpp" +#include "antlr/String.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +MismatchedCharException::MismatchedCharException() +: RecognitionException("Mismatched char") +{} + +// Expected range / not range +MismatchedCharException::MismatchedCharException( + int c, + int lower, + int upper_, + bool matchNot, + CharScanner* scanner_ +) : RecognitionException("Mismatched char", + scanner_->getFilename(), + scanner_->getLine(), + scanner_->getColumn()) + , mismatchType(matchNot ? NOT_RANGE : RANGE) + , foundChar(c) + , expecting(lower) + , upper(upper_) + , scanner(scanner_) +{ +} + +// Expected token / not token +MismatchedCharException::MismatchedCharException( + int c, + int expecting_, + bool matchNot, + CharScanner* scanner_ +) : RecognitionException("Mismatched char", + scanner_->getFilename(), + scanner_->getLine(), + scanner_->getColumn()) + , mismatchType(matchNot ? NOT_CHAR : CHAR) + , foundChar(c) + , expecting(expecting_) + , scanner(scanner_) +{ +} + +// Expected BitSet / not BitSet +MismatchedCharException::MismatchedCharException( + int c, + BitSet set_, + bool matchNot, + CharScanner* scanner_ +) : RecognitionException("Mismatched char", + scanner_->getFilename(), + scanner_->getLine(), + scanner_->getColumn()) + , mismatchType(matchNot ? NOT_SET : SET) + , foundChar(c) + , set(set_) + , scanner(scanner_) +{ +} + +MismatchedCharException::MismatchedCharException( + const ANTLR_USE_NAMESPACE(std)string& s, + int line +) : RecognitionException(s) +{ +} + +/** + * Returns the error message that happened on the line/col given. + * Copied from toString(). + */ +ANTLR_USE_NAMESPACE(std)string MismatchedCharException::getMessage() const +{ + ANTLR_USE_NAMESPACE(std)string s; + + switch (mismatchType) { + case CHAR : + s += "expecting '" + charName(expecting) + "', found '" + charName(foundChar) + "'"; + break; + case NOT_CHAR : + s += "expecting anything but '" + charName(expecting) + "'; got it anyway"; + break; + case RANGE : + s += "expecting token in range: '" + charName(expecting) + "'..'" + charName(upper) + "', found '" + charName(foundChar) + "'"; + break; + case NOT_RANGE : + s += "expecting token NOT in range: " + charName(expecting) + "'..'" + charName(upper) + "', found '" + charName(foundChar) + "'"; + break; + case SET : + case NOT_SET : + { + s += ANTLR_USE_NAMESPACE(std)string("expecting ") + (mismatchType == NOT_SET ? "NOT " : "") + "one of ("; + ANTLR_USE_NAMESPACE(std)vector elems = set.toArray(); + for (int i = 0; i < (int) elems.size(); i++) { + s += " '"; + s += charName(elems[i]); + s += "'"; + } + s += "), found '" + charName(foundChar) + "'"; + } + break; + default : + s += RecognitionException::getMessage(); + break; + } + + return s; +} + +#ifndef NO_STATIC_CONSTS +const int MismatchedCharException::CHAR; +const int MismatchedCharException::NOT_CHAR; +const int MismatchedCharException::RANGE; +const int MismatchedCharException::NOT_RANGE; +const int MismatchedCharException::SET; +const int MismatchedCharException::NOT_SET; +#endif + +ANTLR_END_NAMESPACE diff --git a/poxml/antlr/src/MismatchedTokenException.cpp b/poxml/antlr/src/MismatchedTokenException.cpp new file mode 100644 index 00000000..b8b10808 --- /dev/null +++ b/poxml/antlr/src/MismatchedTokenException.cpp @@ -0,0 +1,223 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1998 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1998 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/MismatchedTokenException.hpp" +#include "antlr/String.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +MismatchedTokenException::MismatchedTokenException() +: RecognitionException("Mismatched Token: expecting any AST node","",1) +, token(0) +, node(nullASTptr) +{ +} + +// Expected range / not range +MismatchedTokenException::MismatchedTokenException( + const ANTLR_USE_NAMESPACE(std)vector& tokenNames_, + RefAST node_, + int lower, + int upper_, + bool matchNot +) : RecognitionException("Mismatched Token") + , tokenNames(tokenNames_) + , token(0) + , node(node_) + , tokenText( (node_ ? node_->toString(): ANTLR_USE_NAMESPACE(std)string("")) ) + , mismatchType(matchNot ? NOT_RANGE : RANGE) + , expecting(lower) + , upper(upper_) +{ + fileName = ""; +} + +// Expected token / not token +MismatchedTokenException::MismatchedTokenException( + const ANTLR_USE_NAMESPACE(std)vector& tokenNames_, + RefAST node_, + int expecting_, + bool matchNot +) : RecognitionException("Mismatched Token") + , tokenNames(tokenNames_) + , token(0) + , node(node_) + , tokenText( (node_ ? node_->toString(): ANTLR_USE_NAMESPACE(std)string("")) ) + , mismatchType(matchNot ? NOT_TOKEN : TOKEN) + , expecting(expecting_) +{ + fileName = ""; +} + +// Expected BitSet / not BitSet +MismatchedTokenException::MismatchedTokenException( + const ANTLR_USE_NAMESPACE(std)vector& tokenNames_, + RefAST node_, + BitSet set_, + bool matchNot +) : RecognitionException("Mismatched Token") + , tokenNames(tokenNames_) + , token(0) + , node(node_) + , tokenText( (node_ ? node_->toString(): ANTLR_USE_NAMESPACE(std)string("")) ) + , mismatchType(matchNot ? NOT_SET : SET) + , set(set_) +{ + fileName = ""; +} + +// Expected range / not range +MismatchedTokenException::MismatchedTokenException( + const ANTLR_USE_NAMESPACE(std)vector& tokenNames_, + RefToken token_, + int lower, + int upper_, + bool matchNot, + const ANTLR_USE_NAMESPACE(std)string& fileName_ +) : RecognitionException("Mismatched Token",fileName_,token_->getLine(),token_->getColumn()) + , tokenNames(tokenNames_) + , token(token_) + , node(nullASTptr) + , tokenText(token_->getText()) + , mismatchType(matchNot ? NOT_RANGE : RANGE) + , expecting(lower) + , upper(upper_) +{ +} + +// Expected token / not token +MismatchedTokenException::MismatchedTokenException( + const ANTLR_USE_NAMESPACE(std)vector& tokenNames_, + RefToken token_, + int expecting_, + bool matchNot, + const ANTLR_USE_NAMESPACE(std)string& fileName_ +) : RecognitionException("Mismatched Token",fileName_,token_->getLine(),token_->getColumn()) + , tokenNames(tokenNames_) + , token(token_) + , node(nullASTptr) + , tokenText(token_->getText()) + , mismatchType(matchNot ? NOT_TOKEN : TOKEN) + , expecting(expecting_) +{ +} + +// Expected BitSet / not BitSet +MismatchedTokenException::MismatchedTokenException( + const ANTLR_USE_NAMESPACE(std)vector& tokenNames_, + RefToken token_, + BitSet set_, + bool matchNot, + const ANTLR_USE_NAMESPACE(std)string& fileName_ +) : RecognitionException("Mismatched Token",fileName_,token_->getLine(),token_->getColumn()) + , tokenNames(tokenNames_) + , token(token_) + , node(nullASTptr) + , tokenText(token_->getText()) + , mismatchType(matchNot ? NOT_SET : SET) + , set(set_) +{ +} + +// deprecated As of ANTLR 2.7.0 +ANTLR_USE_NAMESPACE(std)string MismatchedTokenException::getErrorMessage() const +{ + return getMessage(); +} + +ANTLR_USE_NAMESPACE(std)string MismatchedTokenException::getMessage() const +{ + ANTLR_USE_NAMESPACE(std)string s; + switch (mismatchType) { + case TOKEN: + s += "expecting " + tokenName(expecting) + ", found '" + tokenText + "'"; + break; + case NOT_TOKEN: + s += "expecting anything but " + tokenName(expecting) + "; got it anyway"; + break; + case RANGE: + s += "expecting token in range: " + tokenName(expecting) + ".." + tokenName(upper) + ", found '" + tokenText + "'"; + break; + case NOT_RANGE: + s += "expecting token NOT in range: " + tokenName(expecting) + ".." + tokenName(upper) + ", found '" + tokenText + "'"; + break; + case SET: + case NOT_SET: + { + s += ANTLR_USE_NAMESPACE(std)string("expecting ") + (mismatchType == NOT_SET ? "NOT " : "") + "one of ("; + ANTLR_USE_NAMESPACE(std)vector elems = set.toArray(); + for (int i = 0; i < (int) elems.size(); i++) + { + s += " "; + s += tokenName(elems[i]); + } + s += "), found '" + tokenText + "'"; + } + break; + default: + s = RecognitionException::getMessage(); + break; + } + return s; +} + +ANTLR_USE_NAMESPACE(std)string MismatchedTokenException::tokenName(int tokenType) const +{ + if (tokenType == Token::INVALID_TYPE) { + return ""; + } + else if (tokenType < 0 || tokenType >= (int) tokenNames.size()) { + return ANTLR_USE_NAMESPACE(std)string("<") + tokenType + ">"; + } + else { + return tokenNames[tokenType]; + } +} + +ANTLR_USE_NAMESPACE(std)string MismatchedTokenException::toString() const { + if (token) { + return getFileLineString() + getMessage(); + } + return getMessage(); +} + +#ifndef NO_STATIC_CONSTS +const int MismatchedTokenException::TOKEN; +const int MismatchedTokenException::NOT_TOKEN; +const int MismatchedTokenException::RANGE; +const int MismatchedTokenException::NOT_RANGE; +const int MismatchedTokenException::SET; +const int MismatchedTokenException::NOT_SET; +#endif + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/NoViableAltException.cpp b/poxml/antlr/src/NoViableAltException.cpp new file mode 100644 index 00000000..433f4325 --- /dev/null +++ b/poxml/antlr/src/NoViableAltException.cpp @@ -0,0 +1,82 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1998 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1998 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/NoViableAltException.hpp" +#include "antlr/String.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +NoViableAltException::NoViableAltException(RefAST t) +: RecognitionException("NoViableAlt") +, token(0) +, node(t) +{ + fileName = ""; +} + +NoViableAltException::NoViableAltException(RefToken t,const ANTLR_USE_NAMESPACE(std)string& fileName_) +: RecognitionException("NoViableAlt") // line ")+t.getLine()+" token is "+t.getText()) +, token(t) +, node(nullASTptr) +{ + line = t->getLine(); + column = t->getColumn(); + fileName = fileName_; +} + +ANTLR_USE_NAMESPACE(std)string NoViableAltException::getErrorMessage() const +{ + return getMessage(); +} + +ANTLR_USE_NAMESPACE(std)string NoViableAltException::getMessage() const +{ + if (token) + return ANTLR_USE_NAMESPACE(std)string("unexpected token: ")+token->getText(); + + // must a tree parser error if token==null + if (!node) { + return "unexpected end of subtree"; + } + return ANTLR_USE_NAMESPACE(std)string("unexpected AST node: ")+node->toString(); +} + +ANTLR_USE_NAMESPACE(std)string NoViableAltException::toString() const +{ + if (token) + return getFileLineString()+getMessage(); + else + return getMessage(); +} + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/NoViableAltForCharException.cpp b/poxml/antlr/src/NoViableAltForCharException.cpp new file mode 100644 index 00000000..2ff9120f --- /dev/null +++ b/poxml/antlr/src/NoViableAltForCharException.cpp @@ -0,0 +1,71 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Institute + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Institute + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/NoViableAltForCharException.hpp" +#include "antlr/String.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +NoViableAltForCharException::NoViableAltForCharException(int c, CharScanner* scanner) +: RecognitionException("NoViableAlt") +, foundChar(c) +{ + line = scanner->getLine(); + fileName = scanner->getFilename(); +} + +NoViableAltForCharException::NoViableAltForCharException(int c, const ANTLR_USE_NAMESPACE(std)string& fileName_, int line_) +: RecognitionException("NoViableAlt") +, foundChar(c) +{ + line = line_; + fileName = fileName_; +} + +/** + * @deprecated As of ANTLR 2.7.0 + */ +ANTLR_USE_NAMESPACE(std)string NoViableAltForCharException::getErrorMessage() const +{ + return getMessage(); +} + +/** + * Returns a clean error message (no line number/column information) + */ +ANTLR_USE_NAMESPACE(std)string NoViableAltForCharException::getMessage() const +{ + return ANTLR_USE_NAMESPACE(std)string("unexpected char: ")+charName(foundChar); +} + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/Parser.cpp b/poxml/antlr/src/Parser.cpp new file mode 100644 index 00000000..5a0388d4 --- /dev/null +++ b/poxml/antlr/src/Parser.cpp @@ -0,0 +1,304 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1998 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1998 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/Parser.hpp" + +#include "antlr/BitSet.hpp" +#include "antlr/TokenBuffer.hpp" +#include "antlr/MismatchedTokenException.hpp" +//#include "antlr/ASTFactory.hpp" +#include +#include + +ANTLR_BEGIN_NAMESPACE(antlr) +ANTLR_C_USING(exit) + +/**A generic ANTLR parser (LL(k) for k>=1) containing a bunch of + * utility routines useful at any lookahead depth. We distinguish between + * the LL(1) and LL(k) parsers because of efficiency. This may not be + * necessary in the near future. + * + * Each parser object contains the state of the parse including a lookahead + * cache (the form of which is determined by the subclass), whether or + * not the parser is in guess mode, where tokens come from, etc... + * + *

    + * During guess mode, the current lookahead token(s) and token type(s) + * cache must be saved because the token stream may not have been informed + * to save the token (via mark) before the try block. + * Guessing is started by: + *

      + *
    1. saving the lookahead cache. + *
    2. marking the current position in the TokenBuffer. + *
    3. increasing the guessing level. + *
    + * + * After guessing, the parser state is restored by: + *
      + *
    1. restoring the lookahead cache. + *
    2. rewinding the TokenBuffer. + *
    3. decreasing the guessing level. + *
    + * + * @see antlr.Token + * @see antlr.TokenBuffer + * @see antlr.TokenStream + * @see antlr.LL1Parser + * @see antlr.LLkParser + */ + +bool DEBUG_PARSER=false; + +Parser::Parser(TokenBuffer& input) +: inputState(new ParserInputState(input)), traceDepth(0) +{ +} + +Parser::Parser(TokenBuffer* input) +: inputState(new ParserInputState(input)), traceDepth(0) +{ +} + +Parser::Parser(const ParserSharedInputState& state) +: inputState(state), traceDepth(0) +{ +} + +Parser::~Parser() +{ +} + +void Parser::setTokenNames(const char** tokenNames_) +{ + while (*tokenNames_) { + tokenNames.push_back(*(tokenNames_++)); + } +} + +/** Consume tokens until one matches the given token */ +void Parser::consumeUntil(int tokenType) +{ + while (LA(1) != Token::EOF_TYPE && LA(1) != tokenType) + consume(); +} + +/** Consume tokens until one matches the given token set */ +void Parser::consumeUntil(const BitSet& set) +{ + while (LA(1) != Token::EOF_TYPE && !set.member(LA(1))) + consume(); +} + +/** Get the AST return value squirreled away in the parser */ +RefAST Parser::getAST() +{ + return returnAST; +} + +ASTFactory& Parser::getASTFactory() +{ + return astFactory; +} + +ANTLR_USE_NAMESPACE(std)string Parser::getFilename() const +{ + return inputState->filename; +} + +ParserSharedInputState Parser::getInputState() const +{ + return inputState; +} + +ANTLR_USE_NAMESPACE(std)string Parser::getTokenName(int num) const +{ + return tokenNames[num]; +} + +ANTLR_USE_NAMESPACE(std)vector Parser::getTokenNames() const +{ + return tokenNames; +} + +// Forwarded to TokenBuffer +int Parser::mark() +{ + return inputState->getInput().mark(); +} + +/**Make sure current lookahead symbol matches token type t. + * Throw an exception upon mismatch, which is catch by either the + * error handler or by the syntactic predicate. + */ +void Parser::match(int t) +{ + if ( DEBUG_PARSER ) + { + traceIndent(); + ANTLR_USE_NAMESPACE(std)cout << "enter match(" << t << ") with LA(1)=" << LA(1) << ANTLR_USE_NAMESPACE(std)endl; + } + if ( LA(1)!=t ) { + if ( DEBUG_PARSER ) + { + traceIndent(); + ANTLR_USE_NAMESPACE(std)cout << "token mismatch: " << LA(1) << "!=" << t << ANTLR_USE_NAMESPACE(std)endl; + } + throw MismatchedTokenException(tokenNames, LT(1), t, false, getFilename()); + } else { + // mark token as consumed -- fetch next token deferred until LA/LT + consume(); + } +} + +/**Make sure current lookahead symbol matches the given set + * Throw an exception upon mismatch, which is catch by either the + * error handler or by the syntactic predicate. + */ +void Parser::match(const BitSet& b) +{ + if ( DEBUG_PARSER ) + { + traceIndent(); + ANTLR_USE_NAMESPACE(std)cout << "enter match(" << "bitset" /*b.toString()*/ + << ") with LA(1)=" << LA(1) << ANTLR_USE_NAMESPACE(std)endl; + } + if ( !b.member(LA(1)) ) { + if ( DEBUG_PARSER ) + { + traceIndent(); + ANTLR_USE_NAMESPACE(std)cout << "token mismatch: " << LA(1) << " not member of " + << "bitset" /*b.toString()*/ << ANTLR_USE_NAMESPACE(std)endl; + } + throw MismatchedTokenException(tokenNames, LT(1), b, false, getFilename()); + } else { + // mark token as consumed -- fetch next token deferred until LA/LT + consume(); + } +} + +void Parser::matchNot(int t) +{ + if ( LA(1)==t ) { + // Throws inverted-sense exception + throw MismatchedTokenException(tokenNames, LT(1), t, true, getFilename()); + } else { + // mark token as consumed -- fetch next token deferred until LA/LT + consume(); + } +} + +void Parser::panic() +{ + ANTLR_USE_NAMESPACE(std)cerr << "Parser: panic" << ANTLR_USE_NAMESPACE(std)endl; + exit(1); +} + +/** Parser error-reporting function can be overridden in subclass */ +void Parser::reportError(const RecognitionException& ex) +{ + ANTLR_USE_NAMESPACE(std)cerr << ex.toString().c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +/** Parser error-reporting function can be overridden in subclass */ +void Parser::reportError(const ANTLR_USE_NAMESPACE(std)string& s) +{ + if ( getFilename().empty() ) + ANTLR_USE_NAMESPACE(std)cerr << "error: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; + else + ANTLR_USE_NAMESPACE(std)cerr << getFilename().c_str() << ": error: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +/** Parser warning-reporting function can be overridden in subclass */ +void Parser::reportWarning(const ANTLR_USE_NAMESPACE(std)string& s) +{ + if ( getFilename().empty() ) + ANTLR_USE_NAMESPACE(std)cerr << "warning: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; + else + ANTLR_USE_NAMESPACE(std)cerr << getFilename().c_str() << ": warning: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +void Parser::rewind(int pos) +{ + inputState->getInput().rewind(pos); +} + +/** Set the object used to generate ASTs */ +// void setASTFactory(ASTFactory astFactory_); + +/** Specify the type of node to create during tree building */ +void Parser::setASTNodeFactory(ASTFactory::factory_type factory) +{ + astFactory.setASTNodeFactory(factory); +} + +void Parser::setFilename(const ANTLR_USE_NAMESPACE(std)string& f) +{ + inputState->filename = f; +} + +void Parser::setInputState(ParserSharedInputState state) +{ + inputState = state; +} + +/** Set or change the input token buffer */ +// void setTokenBuffer(TokenBuffer* t); + +void Parser::traceIndent() +{ + for( int i = 0; i < traceDepth; i++ ) + ANTLR_USE_NAMESPACE(std)cout << " "; +} + +void Parser::traceIn(const ANTLR_USE_NAMESPACE(std)string& rname) +{ + traceDepth++; + + for( int i = 0; i < traceDepth; i++ ) + ANTLR_USE_NAMESPACE(std)cout << " "; + + ANTLR_USE_NAMESPACE(std)cout << "> " << rname.c_str() << "; LA(1)==" << LT(1)->getText().c_str() << + ((inputState->guessing>0)?" [guessing]":"") << ANTLR_USE_NAMESPACE(std)endl; +} + +void Parser::traceOut(const ANTLR_USE_NAMESPACE(std)string& rname) +{ + for( int i = 0; i < traceDepth; i++ ) + ANTLR_USE_NAMESPACE(std)cout << " "; + + ANTLR_USE_NAMESPACE(std)cout << "< " << rname.c_str() << "; LA(1)==" << LT(1)->getText().c_str() << + ((inputState->guessing>0)?" [guessing]":"") << ANTLR_USE_NAMESPACE(std)endl; + + traceDepth--; +} + +ANTLR_END_NAMESPACE diff --git a/poxml/antlr/src/ParserSharedInputState.cpp b/poxml/antlr/src/ParserSharedInputState.cpp new file mode 100644 index 00000000..102aba87 --- /dev/null +++ b/poxml/antlr/src/ParserSharedInputState.cpp @@ -0,0 +1,37 @@ +#include "antlr/ParserSharedInputState.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** This object contains the data associated with an + * input stream of tokens. Multiple parsers + * share a single ParserSharedInputState to parse + * the same stream of tokens. + */ + +ParserInputState::ParserInputState(TokenBuffer* input_) +: guessing(0) +, input(input_) +, inputResponsible(true) +{ +} + +ParserInputState::ParserInputState(TokenBuffer& input_) +: guessing(0) +, input(&input_) +, inputResponsible(false) +{ +} + +ParserInputState::~ParserInputState() +{ + if (inputResponsible) + delete input; +} + +TokenBuffer& ParserInputState::getInput() +{ + return *input; +} + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/RecognitionException.cpp b/poxml/antlr/src/RecognitionException.cpp new file mode 100644 index 00000000..1d1bd53d --- /dev/null +++ b/poxml/antlr/src/RecognitionException.cpp @@ -0,0 +1,87 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1998 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1998 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/RecognitionException.hpp" +#include "antlr/String.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +RecognitionException::RecognitionException() +: ANTLRException("parsing error"), line(1), column(1) +{} + +RecognitionException::RecognitionException(const ANTLR_USE_NAMESPACE(std)string& s) +: ANTLRException(s) +{} + +RecognitionException::RecognitionException(const ANTLR_USE_NAMESPACE(std)string& s,const ANTLR_USE_NAMESPACE(std)string& fileName_,int line_) +: ANTLRException(s), fileName(fileName_), line(line_) +{} + +RecognitionException::RecognitionException(const ANTLR_USE_NAMESPACE(std)string& s,const ANTLR_USE_NAMESPACE(std)string& fileName_,int line_,int column_) +: ANTLRException(s), fileName(fileName_), line(line_), column(column_) +{} + +int RecognitionException::getColumn() const +{ + return column; +} + +ANTLR_USE_NAMESPACE(std)string RecognitionException::getErrorMessage() const +{ + return getMessage(); +} + +ANTLR_USE_NAMESPACE(std)string RecognitionException::getFileLineString() const +{ + if ( fileName.length() ) + return fileName+": "+line+": "; + else + return ANTLR_USE_NAMESPACE(std)string("line ")+line+": "; +} + +ANTLR_USE_NAMESPACE(std)string RecognitionException::getFilename() const +{ + return fileName; +} + +int RecognitionException::getLine() const +{ + return line; +} + +ANTLR_USE_NAMESPACE(std)string RecognitionException::toString() const +{ + return getFileLineString()+getMessage(); +} + +ANTLR_END_NAMESPACE diff --git a/poxml/antlr/src/String.cpp b/poxml/antlr/src/String.cpp new file mode 100644 index 00000000..6d9df7a5 --- /dev/null +++ b/poxml/antlr/src/String.cpp @@ -0,0 +1,61 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1998 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1998 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/String.hpp" + +#ifdef HAS_NOT_CSTDIO_H +#include +#else +#include +#endif + +ANTLR_BEGIN_NAMESPACE(antlr) +ANTLR_C_USING(sprintf) + +ANTLR_USE_NAMESPACE(std)string operator+(const ANTLR_USE_NAMESPACE(std)string& lhs,int rhs) +{ + char tmp[100]; + sprintf(tmp,"%d",rhs); + return lhs+tmp; +} + +ANTLR_USE_NAMESPACE(std)string charName(int ch) +{ + if (ch == EOF) + return "EOF"; + else { + return ANTLR_USE_NAMESPACE(std)string(1, static_cast(ch)); + } +} + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/Token.cpp b/poxml/antlr/src/Token.cpp new file mode 100644 index 00000000..f307774f --- /dev/null +++ b/poxml/antlr/src/Token.cpp @@ -0,0 +1,108 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1998 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1998 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/Token.hpp" +#include "antlr/String.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +RefToken Token::badToken(new Token(Token::INVALID_TYPE, "")); + +Token::Token() : type(INVALID_TYPE) +{ +} + +Token::Token(int t) : type(t) +{ +} + +Token::Token(int t, const ANTLR_USE_NAMESPACE(std)string& txt) + : type(t) +{ + type=t; + setText(txt); +} + +int Token::getColumn() const +{ + return 0; +} + +int Token::getLine() const +{ + return 0; +} + +ANTLR_USE_NAMESPACE(std)string Token::getText() const +{ + return ""; +} + +int Token::getType() const +{ + return type; +} + +void Token::setColumn(int c) +{} + +void Token::setLine(int l) +{} + +void Token::setText(const ANTLR_USE_NAMESPACE(std)string& t) +{} + +void Token::setType(int t) +{ + type=t; +} + +ANTLR_USE_NAMESPACE(std)string Token::toString() const +{ + return "[\""+getText()+"\",<"+type+">]"; +} + +Token::~Token() +{} + +RefToken nullToken; + +#ifndef NO_STATIC_CONSTS +const int Token::MIN_USER_TYPE; +const int Token::NULL_TREE_LOOKAHEAD; +const int Token::INVALID_TYPE; +const int Token::EOF_TYPE; +const int Token::SKIP; +#endif + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/TokenBuffer.cpp b/poxml/antlr/src/TokenBuffer.cpp new file mode 100644 index 00000000..ded5df9b --- /dev/null +++ b/poxml/antlr/src/TokenBuffer.cpp @@ -0,0 +1,107 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1998 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1998 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ + +#include "antlr/TokenBuffer.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/**A Stream of Token objects fed to the parser from a TokenStream that can + * be rewound via mark()/rewind() methods. + *

    + * A dynamic array is used to buffer up all the input tokens. Normally, + * "k" tokens are stored in the buffer. More tokens may be stored during + * guess mode (testing syntactic predicate), or when LT(i>k) is referenced. + * Consumption of tokens is deferred. In other words, reading the next + * token is not done by conume(), but deferred until needed by LA or LT. + *

    + * + * @see antlr.Token + * @see antlr.TokenStream + * @see antlr.TokenQueue + */ + +/** Create a token buffer */ +TokenBuffer::TokenBuffer(TokenStream& input_) +: input(input_) +{ nMarkers=0; markerOffset=0; numToConsume=0; } + +/** Mark another token for deferred consumption */ +void TokenBuffer::consume() +{ numToConsume++; } + +/** Ensure that the token buffer is sufficiently full */ +void TokenBuffer::fill(int amount) +{ + syncConsume(); + // Fill the buffer sufficiently to hold needed tokens + while (queue.entries() < amount + markerOffset) { + // Append the next token + queue.append(input.nextToken()); + } +} + +/** Get a lookahead token value */ +int TokenBuffer::LA(int i) +{ + fill(i); + return queue.elementAt(markerOffset+i-1)->type; +} + +/** Get a lookahead token */ +RefToken TokenBuffer::LT(int i) +{ + fill(i); + return queue.elementAt(markerOffset+i-1); +} + +/**Return an integer marker that can be used to rewind the buffer to + * its current state. + */ +int TokenBuffer::mark() +{ + syncConsume(); + nMarkers++; + return markerOffset; +} + +/**Rewind the token buffer to a marker. + * @param mark Marker returned previously from mark() + */ +void TokenBuffer::rewind(int mark) +{ + syncConsume(); + markerOffset=mark; + nMarkers--; +} + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/TokenStreamBasicFilter.cpp b/poxml/antlr/src/TokenStreamBasicFilter.cpp new file mode 100644 index 00000000..71257f46 --- /dev/null +++ b/poxml/antlr/src/TokenStreamBasicFilter.cpp @@ -0,0 +1,34 @@ +#include "antlr/TokenStreamBasicFilter.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** This object is a TokenStream that passes through all + * tokens except for those that you tell it to discard. + * There is no buffering of the tokens. + */ +TokenStreamBasicFilter::TokenStreamBasicFilter(TokenStream& input_) +: input(&input_) +{ +} + +void TokenStreamBasicFilter::discard(int ttype) +{ + discardMask.add(ttype); +} + +void TokenStreamBasicFilter::discard(const BitSet& mask) +{ + discardMask = mask; +} + +RefToken TokenStreamBasicFilter::nextToken() +{ + RefToken tok = input->nextToken(); + while ( tok && discardMask.member(tok->getType()) ) { + tok = input->nextToken(); + } + return tok; +} + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/TokenStreamHiddenTokenFilter.cpp b/poxml/antlr/src/TokenStreamHiddenTokenFilter.cpp new file mode 100644 index 00000000..827ca382 --- /dev/null +++ b/poxml/antlr/src/TokenStreamHiddenTokenFilter.cpp @@ -0,0 +1,146 @@ +#include "antlr/TokenStreamHiddenTokenFilter.hpp" +#include "antlr/CommonHiddenStreamToken.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/**This object filters a token stream coming from a lexer + * or another TokenStream so that only certain token channels + * get transmitted to the parser. + * + * Any of the channels can be filtered off as "hidden" channels whose + * tokens can be accessed from the parser. + */ + +TokenStreamHiddenTokenFilter::TokenStreamHiddenTokenFilter(TokenStream& input) +: TokenStreamBasicFilter(input) +{ +} + +void TokenStreamHiddenTokenFilter::consume() +{ + nextMonitoredToken = input->nextToken(); +} + +void TokenStreamHiddenTokenFilter::consumeFirst() +{ + consume(); + + // Handle situation where hidden or discarded tokens + // appear first in input stream + RefToken p; + // while hidden or discarded scarf tokens + while ( hideMask.member(LA(1)->getType()) || discardMask.member(LA(1)->getType()) ) { + if ( hideMask.member(LA(1)->getType()) ) { + if ( !p ) { + p = LA(1); + } + else { + static_cast(p.get())->setHiddenAfter(LA(1)); + static_cast(LA(1).get())->setHiddenBefore(p); // double-link + p = LA(1); + } + lastHiddenToken = p; + if (!firstHidden) + firstHidden = p; // record hidden token if first + } + consume(); + } +} + +BitSet TokenStreamHiddenTokenFilter::getDiscardMask() const +{ + return discardMask; +} + +/** Return a ptr to the hidden token appearing immediately after + * token t in the input stream. + */ +RefToken TokenStreamHiddenTokenFilter::getHiddenAfter(RefToken t) +{ + return static_cast(t.get())->getHiddenAfter(); +} + +/** Return a ptr to the hidden token appearing immediately before + * token t in the input stream. + */ +RefToken TokenStreamHiddenTokenFilter::getHiddenBefore(RefToken t) +{ + return static_cast(t.get())->getHiddenBefore(); +} + +BitSet TokenStreamHiddenTokenFilter::getHideMask() const +{ + return hideMask; +} + +/** Return the first hidden token if one appears + * before any monitored token. + */ +RefToken TokenStreamHiddenTokenFilter::getInitialHiddenToken() +{ + return firstHidden; +} + +void TokenStreamHiddenTokenFilter::hide(int m) +{ + hideMask.add(m); +} + +void TokenStreamHiddenTokenFilter::hide(const BitSet& mask) +{ + hideMask = mask; +} + +RefToken TokenStreamHiddenTokenFilter::LA(int i) +{ + return nextMonitoredToken; +} + +/** Return the next monitored token. +* Test the token following the monitored token. +* If following is another monitored token, save it +* for the next invocation of nextToken (like a single +* lookahead token) and return it then. +* If following is unmonitored, nondiscarded (hidden) +* channel token, add it to the monitored token. +* +* Note: EOF must be a monitored Token. +*/ +RefToken TokenStreamHiddenTokenFilter::nextToken() +{ + // handle an initial condition; don't want to get lookahead + // token of this splitter until first call to nextToken + if ( !LA(1) ) { + consumeFirst(); + } + + // we always consume hidden tokens after monitored, thus, + // upon entry LA(1) is a monitored token. + RefToken monitored = LA(1); + // point to hidden tokens found during last invocation + static_cast(monitored.get())->setHiddenBefore(lastHiddenToken); + lastHiddenToken = nullToken; + + // Look for hidden tokens, hook them into list emanating + // from the monitored tokens. + consume(); + RefToken p = monitored; + // while hidden or discarded scarf tokens + while ( hideMask.member(LA(1)->getType()) || discardMask.member(LA(1)->getType()) ) { + if ( hideMask.member(LA(1)->getType()) ) { + // attach the hidden token to the monitored in a chain + // link forwards + static_cast(p.get())->setHiddenAfter(LA(1)); + // link backwards + if (p != monitored) { //hidden cannot point to monitored tokens + static_cast(LA(1).get())->setHiddenBefore(p); + } + p = lastHiddenToken = LA(1); + } + consume(); + } + return monitored; +} + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/TokenStreamSelector.cpp b/poxml/antlr/src/TokenStreamSelector.cpp new file mode 100644 index 00000000..2e6527a8 --- /dev/null +++ b/poxml/antlr/src/TokenStreamSelector.cpp @@ -0,0 +1,97 @@ +#include "antlr/TokenStreamSelector.hpp" +#include "antlr/TokenStreamRetryException.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** A token stream MUX (multiplexor) knows about n token streams + * and can multiplex them onto the same channel for use by token + * stream consumer like a parser. This is a way to have multiple + * lexers break up the same input stream for a single parser. + * Or, you can have multiple instances of the same lexer handle + * multiple input streams; this works great for includes. + */ + +TokenStreamSelector::TokenStreamSelector() +: input(0) +{ +} + +TokenStreamSelector::~TokenStreamSelector() +{ +} + +void TokenStreamSelector::addInputStream(TokenStream* stream, const ANTLR_USE_NAMESPACE(std)string& key) +{ + inputStreamNames[key] = stream; +} + +TokenStream* TokenStreamSelector::getCurrentStream() const +{ + return input; +} + +TokenStream* TokenStreamSelector::getStream(const ANTLR_USE_NAMESPACE(std)string& sname) const +{ + inputStreamNames_coll::const_iterator i = inputStreamNames.find(sname); + if (i == inputStreamNames.end()) { + throw ANTLR_USE_NAMESPACE(std)string("TokenStream ")+sname+" not found"; + } + return (*i).second; +} + +RefToken TokenStreamSelector::nextToken() +{ + // keep looking for a token until you don't + // get a retry exception + for (;;) { + try { + return input->nextToken(); + } + catch (TokenStreamRetryException& r) { + // just retry "forever" + } + } +} + +TokenStream* TokenStreamSelector::pop() +{ + TokenStream* stream = streamStack.top(); + streamStack.pop(); + select(stream); + return stream; +} + +void TokenStreamSelector::push(TokenStream* stream) +{ + streamStack.push(input); + select(stream); +} + +void TokenStreamSelector::push(const ANTLR_USE_NAMESPACE(std)string& sname) +{ + streamStack.push(input); + select(sname); +} + +void TokenStreamSelector::retry() +{ + throw TokenStreamRetryException(); +} + +/** Set the stream without pushing old stream */ +void TokenStreamSelector::select(TokenStream* stream) +{ + input = stream; +} + +void TokenStreamSelector::select(const ANTLR_USE_NAMESPACE(std)string& sname) +{ + inputStreamNames_coll::const_iterator i = inputStreamNames.find(sname); + if (i == inputStreamNames.end()) { + throw ANTLR_USE_NAMESPACE(std)string("TokenStream ")+sname+" not found"; + } + input = (*i).second; +} + +ANTLR_END_NAMESPACE + diff --git a/poxml/antlr/src/TreeParser.cpp b/poxml/antlr/src/TreeParser.cpp new file mode 100644 index 00000000..6d302737 --- /dev/null +++ b/poxml/antlr/src/TreeParser.cpp @@ -0,0 +1,165 @@ +/** + * SOFTWARE RIGHTS + *

    + * ANTLR 2.6.0 MageLang Insitute, 1998 + *

    + * We reserve no legal rights to the ANTLR--it is fully in the + * public domain. An individual or company may do whatever + * they wish with source code distributed with ANTLR or the + * code generated by ANTLR, including the incorporation of + * ANTLR, or its output, into commerical software. + *

    + * We encourage users to develop software with ANTLR. However, + * we do ask that credit is given to us for developing + * ANTLR. By "credit", we mean that if you use ANTLR or + * incorporate any source code into one of your programs + * (commercial product, research project, or otherwise) that + * you acknowledge this fact somewhere in the documentation, + * research report, etc... If you like ANTLR and have + * developed a nice tool with the output, please mention that + * you developed it using ANTLR. In addition, we ask that the + * headers remain intact in our source code. As long as these + * guidelines are kept, we expect to continue enhancing this + * system and expect to make other tools available as they are + * completed. + *

    + * The ANTLR gang: + * @version ANTLR 2.6.0 MageLang Insitute, 1998 + * @author Terence Parr, MageLang Institute + * @author
    John Lilley, Empathy Software + * @author
    Pete Wells + */ +#include "antlr/TreeParser.hpp" +#include "antlr/ASTNULLType.hpp" +#include "antlr/MismatchedTokenException.hpp" +#include +#include + +ANTLR_BEGIN_NAMESPACE(antlr) +ANTLR_C_USING(exit) + +TreeParser::TreeParser() +: inputState(new TreeParserInputState()), traceDepth(0) +{ +} + +TreeParser::TreeParser(const TreeParserSharedInputState& state) +: inputState(state), traceDepth(0) +{ +} + +TreeParser::~TreeParser() +{ +} + +void TreeParser::setTokenNames(const char** tokenNames_) +{ + while (*tokenNames_) { + tokenNames.push_back(*(tokenNames_++)); + } +} + +/** The AST Null object; the parsing cursor is set to this when + * it is found to be null. This way, we can test the + * token type of a node without having to have tests for null + * everywhere. + */ +RefAST TreeParser::ASTNULL(new ASTNULLType); + +/** Get the AST return value squirreled away in the parser */ +//RefAST getAST() const { +// return returnAST; +//} + +void TreeParser::match(RefAST t, int ttype) +{ + if (!t || t==ASTNULL || t->getType()!=ttype) + throw MismatchedTokenException(); +} + +/**Make sure current lookahead symbol matches the given set + * Throw an exception upon mismatch, which is caught by either the + * error handler or by the syntactic predicate. + */ +void TreeParser::match(RefAST t, const BitSet& b) +{ + if ( !t || t==ASTNULL || !b.member(t->getType()) ) { + throw MismatchedTokenException(); + } +} + +void TreeParser::matchNot(RefAST t, int ttype) +{ + //ANTLR_USE_NAMESPACE(std)cout << "match(" << ttype << "); cursor is " << t.toString() << ANTLR_USE_NAMESPACE(std)endl; + if ( !t || t==ASTNULL || t->getType()==ttype ) { + throw MismatchedTokenException(); + } +} + +void TreeParser::panic() +{ + ANTLR_USE_NAMESPACE(std)cerr << "TreeWalker: panic" << ANTLR_USE_NAMESPACE(std)endl; + exit(1); +} + +/** Parser error-reporting function can be overridden in subclass */ +void TreeParser::reportError(const RecognitionException& ex) +{ + ANTLR_USE_NAMESPACE(std)cerr << ex.toString().c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +/** Parser error-reporting function can be overridden in subclass */ +void TreeParser::reportError(const ANTLR_USE_NAMESPACE(std)string& s) +{ + ANTLR_USE_NAMESPACE(std)cerr << "error: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +/** Parser warning-reporting function can be overridden in subclass */ +void TreeParser::reportWarning(const ANTLR_USE_NAMESPACE(std)string& s) +{ + ANTLR_USE_NAMESPACE(std)cerr << "warning: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +/** Specify an object with support code (shared by + * Parser and TreeParser. Normally, the programmer + * does not play with this, using setASTNodeType instead. + */ +// void TreeParser::setASTFactory(ASTFactory f); + +/** Specify the type of node to create during tree building */ +void TreeParser::setASTNodeFactory(ASTFactory::factory_type factory) +{ + astFactory.setASTNodeFactory(factory); +} + +/** Procedure to write out an indent for traceIn and traceOut */ +void TreeParser::traceIndent() +{ + for( int i = 0; i < traceDepth; i++ ) + ANTLR_USE_NAMESPACE(std)cout << " "; +} + +void TreeParser::traceIn(const ANTLR_USE_NAMESPACE(std)string& rname, RefAST t) +{ + traceDepth++; + traceIndent(); + + ANTLR_USE_NAMESPACE(std)cout << "> " << rname.c_str() + << "(" << (t ? t->toString().c_str() : "null") << ")" + << ((inputState->guessing>0)?" [guessing]":"") + << ANTLR_USE_NAMESPACE(std)endl; +} + +void TreeParser::traceOut(const ANTLR_USE_NAMESPACE(std)string& rname, RefAST t) +{ + traceIndent(); + + ANTLR_USE_NAMESPACE(std)cout << "< " << rname.c_str() + << "(" << (t ? t->toString().c_str() : "null") << ")" + << ((inputState->guessing>0)?" [guessing]":"") + << ANTLR_USE_NAMESPACE(std)endl; + + traceDepth--; +} + +ANTLR_END_NAMESPACE diff --git a/poxml/antlr/src/TreeParserSharedInputState.cpp b/poxml/antlr/src/TreeParserSharedInputState.cpp new file mode 100644 index 00000000..89f1d5dc --- /dev/null +++ b/poxml/antlr/src/TreeParserSharedInputState.cpp @@ -0,0 +1,22 @@ +#include "antlr/TreeParserSharedInputState.hpp" + +ANTLR_BEGIN_NAMESPACE(antlr) + +/** This object contains the data associated with an + * input AST. Multiple parsers + * share a single TreeParserSharedInputState to parse + * the same tree or to have the parser walk multiple + * trees. + */ + +TreeParserInputState::TreeParserInputState() +: guessing(0) +{ +} + +TreeParserInputState::~TreeParserInputState() +{ +} + +ANTLR_END_NAMESPACE + diff --git a/poxml/gettext.g b/poxml/gettext.g new file mode 100644 index 00000000..8da92334 --- /dev/null +++ b/poxml/gettext.g @@ -0,0 +1,168 @@ + +header "pre_include_hpp" { +#include +using namespace std; +#include "parser.h" +} + +options { + language="Cpp"; +} + +{ +#include +#include "GettextLexer.hpp" +#include "GettextParser.hpp" +#include "antlr/AST.hpp" +#include "antlr/CommonAST.hpp" + +/* +int main() +{ + ANTLR_USING_NAMESPACE(std) + ANTLR_USING_NAMESPACE(antlr) + try { + GettextLexer lexer(cin); + GettextParser parser(lexer); + parser.file(); + + } catch(exception& e) { + cerr << "exception: " << e.what() << endl; + } +} +*/ +} + +class GettextParser extends Parser; + +options { + codeGenMakeSwitchThreshold = 3; + codeGenBitsetTestThreshold = 4; +} + +file returns [ MsgList ml ] +{ +string c, mi, ms; +MsgBlock mb; +MsgList ml2; +} + : (comment T_MSGID) => (mb=file_block ml2=file { ml = ml2; ml.append(mb); } ) + | (comment EOF) => c=comment { (void)c; } + ; + +file_block returns [ MsgBlock mb ] +{ +string c, mi, mip, ms; +} + : c=comment mi=msgid + ( + ( ms=msgstr { + mb.comment = QString::fromUtf8(c.c_str()); + mb.msgid = QString::fromUtf8(mi.c_str()); + mb.msgstr = QString::fromUtf8(ms.c_str()); + } + ) + | + ( mip=msgid_plural ms=msgstr_plural { + mb.comment = QString::fromUtf8(c.c_str()); + mb.msgid = QString::fromUtf8(mi.c_str()); + mb.msgid_plural = QString::fromUtf8(mip.c_str()); + mb.msgstr = QString::fromUtf8(ms.c_str()); + } + ) + ) + ; + +comment returns [string s] +{ +string r; +} + : (c:T_COMMENT r=comment { s = c->getText() + r; } ) + | /* nothing */ + ; + +msgid returns [string s] + : T_MSGID t:T_STRING { s = t->getText(); } + ; + +msgid_plural returns [string s] + : T_MSGID_PLURAL t:T_STRING { s = t->getText(); } + ; + +msgstr returns [string s] + : T_MSGSTR t:T_STRING { s = t->getText(); } + ; + +msgstr_plural returns [string s] + : ( + T_MSGSTR L_BRACKET n:T_INT R_BRACKET t:T_STRING { s = t->getText(); } + )+ + ; + +class GettextLexer extends Lexer; +options { + charVocabulary = '\u0000'..'\u00FF'; + testLiterals=false; // don't automatically test for literals +} + +WS + : (' ' | '\t' + | ('\n' | "\r\n") { newline(); } + ) { $setType(ANTLR_USE_NAMESPACE(antlr)Token::SKIP); } + ; + +L_BRACKET: '[' ; + +R_BRACKET: ']' ; + +T_INT : ( '0'..'9' )+ + ; + +T_COMMENT : '#' (~'\n')* + ; + +MSG_TAG : "msg" ( ("id") ( + "" { $setType(T_MSGID); } + | "_plural" { $setType(T_MSGID_PLURAL); } + ) + | "str" { $setType(T_MSGSTR); } + ) + ; + +T_STRING + : ('"'! (ESC|~'"')* ('"'! (' ' | 't')*! '\n'! { newline(); } (' '! | '\t'!)*))+ + ; + +// copied from example +protected +ESC : '\\' + ( 'n' + | 'r' + | 't' + | 'b' + | 'f' + | '"' + | '\'' + | '\\' + | ('0'..'3') + ( + options { + warnWhenFollowAmbig = false; + } + : ('0'..'9') + ( + options { + warnWhenFollowAmbig = false; + } + : '0'..'9' + )? + )? + | ('4'..'7') + ( + options { + warnWhenFollowAmbig = false; + } + : ('0'..'9') + )? + ) + ; diff --git a/poxml/lauri.po b/poxml/lauri.po new file mode 100644 index 00000000..9dd0d025 --- /dev/null +++ b/poxml/lauri.po @@ -0,0 +1,442 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Free Software Foundation, Inc. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2001-02-09 01:25+0100\n" +"PO-Revision-Date: 2001-07-02 20:31MET\n" +"Last-Translator: Stephan Kulow \n" +"Language-Team: german \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: KBabel 0.9.2\n" + +#. Tag: title +#: lauri.xml:16 +#, no-c-format +msgid "test document" +msgstr "Testtext" + +#. Tag: author +#: lauri.xml:17 +#, no-c-format +msgid "StephanKulow" +msgstr "StephanKulow" + +#. Tag: para +#: lauri.xml:19 +#, no-c-format +msgid "This is nonsense" +msgstr "Dies ist Schwachsinn" + +#. Tag: keyword +#: lauri.xml:20 +#, no-c-format +msgid "KDE" +msgstr "KDE.de" + +#. Tag: title +#: lauri.xml:25 +#, no-c-format +msgid ""Text" for &lauri;" +msgstr ""Text" für &lauri;" + +#. Tag: title +#: lauri.xml:27 +#, no-c-format +msgid "Section 1" +msgstr "Abschnitt 1" + +#. Tag: title +#: lauri.xml:29 +#, no-c-format +msgid "Section 1.1" +msgstr "Abschnitt 1.1" + +#. Tag: para +#: lauri.xml:31 +#, no-c-format +msgid "" +"Warum kann ich meinem Rechner nicht " +"einfach ausschalten?Hier noch" +msgstr "" +"Warum kann ich meinem Rechner nicht " +"einfach ausschalten?Hier noch" + +#. Tag: para +#: lauri.xml:34 +#, no-c-format +msgid "me can't be turned off." +msgstr "Das me kann nicht ausgemacht werden." + +#. Tag: para +#: lauri.xml:36 +#, no-c-format +msgid "Leading "Text" for &lauri;" +msgstr ""Starttext" für &lauri;" + +#. Tag: para +#: lauri.xml:39 +#, no-c-format +msgid "" +"we pretend her name was Höpfner, but that is a good " +"name too" +msgstr "" +"sagen wir mal sie heisst Höpfner, aber ist auch gut" + +#. Tag: para +#: lauri.xml:39 +#, no-c-format +msgid " Shift help " +msgstr " Schieber Hilfe " + +#. Tag: primary +#: lauri.xml:45 +#, no-c-format +msgid "kde" +msgstr "kde.de" + +#. Tag: para +#: lauri.xml:46 +#, no-c-format +msgid "" +"an archive of the developer's mailing list is at lists.kde.org." +msgstr "" +"Es gibt ein Archiv lists.kde.org." + +#. Tag: trans_comment +#: lauri.xml:51 +#, no-c-format +msgid "GIVE_ME_CREDIT" +msgstr "Habe ich gemacht - toll ne?" + +#. Tag: term +#: lauri.xml:56 +#, no-c-format +msgid "Text 1" +msgstr "Schrift 1" + +#. Tag: para +#: lauri.xml:60 +#, no-c-format +msgid "Text 2" +msgstr "Schrift 2" + +#. Tag: para +#: lauri.xml:63 +#, no-c-format +msgid "Text 3 \"everything\"" +msgstr "Text 3 \"alles\"" + +#. Tag: term +#: lauri.xml:65 +#, no-c-format +msgid "Everything" +msgstr "Alles" + +#. Tag: para +#: lauri.xml:65 +#, no-c-format +msgid "Is correct" +msgstr "Ist klar" + +#. Tag: term +#: lauri.xml:66 +#, no-c-format +msgid "Nothing" +msgstr "Nichts" + +#. Tag: para +#: lauri.xml:66 +#, no-c-format +msgid "Is wrong" +msgstr "ist falsch" + +#. Tag: para +#: lauri.xml:68 +#, no-c-format +msgid "Text 4 \\\"even more\\\"" +msgstr "Text 4 \\\"noch mehr\\\"" + +#. Tag: para +#: lauri.xml:73 +#, no-c-format +msgid "Text 4 \\\"even less\\\"" +msgstr "Text 4 \\\"noch weniger\\\"" + +#. Tag: menuchoice +#: lauri.xml:85 +#, no-c-format +msgid "" +"CtrlN FileNew" +msgstr "" +"CtrlN DateiNeu" + +#. Tag: action +#: lauri.xml:88 +#, no-c-format +msgid "This starts a new Document in a new instance of the editor." +msgstr "Dies macht alles neu." + +#. Tag: title +#: lauri.xml:96 +#, no-c-format +msgid "What XML looks like" +msgstr "Wie XML aussieht" + +#. Tag: para +#: lauri.xml:98 +#, no-c-format +msgid "" +"Here is an example of an XML file used by Columbo " +"to describe a search site on the Internet:" +msgstr "Dies ist Columbo:" + +#. Tag: programlisting +#: lauri.xml:110 +#, no-c-format +msgid "" +"\n" +"\n" +"\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"\n" +" \n" +"]]>" +msgstr "" +"\n" +"\n" +"\n" +"\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"\n" +" \n" +"]]>" + +#. Tag: para +#: lauri.xml:113 +#, no-c-format +msgid "" +"This instruction is normally used to declare the DTD of the document. Here " +"no DTD is used, and only the name of the root element (search) appears." +msgstr "Das ist normalerweise DTD" + +#. Tag: para +#: lauri.xml:120 +#, no-c-format +msgid "" +"search begins the root element. Here, " +"it extends to the end of the document (search)." +msgstr "" +"search begins the root element. Here, " +"it extends to the end of the document (search)." + +#. Tag: para +#: lauri.xml:127 +#, no-c-format +msgid "" +"This is an example of an empty element. Empty elements do not need a closing " +"tag (which would be </input> in this case)." +msgstr "" +"This is an example of an empty element. Empty elements do not need a closing " +"tag (which would be </input> in this case)." + +#. Tag: title +#: lauri.xml:139 +#, no-c-format +msgid "The ugly part" +msgstr "Der schlimme Teil" + +#. Tag: para +#: lauri.xml:140 +#, no-c-format +msgid "Ending Text:" +msgstr "Ende:" + +#. Tag: literallayout +#: lauri.xml:143 +#, no-c-format +msgid "" +"Matthias Hoelzer\n" +"KDE-Verein i.G.\n" +"Account-Nr. 2798296\n" +"\n" +"Staedtische Sparkasse Wuerzburg\n" +"Hofstrasse 9\n" +"97070 Wuerzburg\n" +"Germany\n" +"BLZ 790 500 00\n" +"SWIFT-Address: BYLA DE 77\n" +"\n" +"print \"$b4 /path/to/KDE/libs/libpng.a $af\\n\"; \\\n" +"you see it here\n" +"whereever" +msgstr "" +"\n" +"Matthias Hoelzer\n" +"KDE-Verein i.G.\n" +"Account-Nr. 2798296\n" +"\n" +"FrankenSWIFT-Address: BYLA DE 77\n" +"hallo Du" + +#. Tag: screen +#: lauri.xml:146 +#, no-c-format +msgid "" +"Expect ogin: # remember, we do ordinary " +"terminal login\n" +"ID \"\" # kppp sends the id you " +"configured in the main dialog\n" +"Expect for userxyz: # a list of available numbers " +"is shown, the user should choose one \n" +"Send userxyz-home # the user wants to be called " +"back on their home number\n" +"Expect ogin: # The callback process is now " +"running, a new connection, and so a new login.\n" +"ID\n" +"Expect assword: # Now send your password\n" +"Expect > # Wait for the command " +"prompt (the prompt may vary)\n" +"Send start_ppp # this command starts the pppd" +msgstr "" +"\n" +"Expect ogin: # Dies ist alles nicht so " +"schlimm!\n" +"ID \"\" # kppp sends the id you " +"configured in the main dialog\n" +"Expect for userxyz: # a list of available numbers " +"is shown, the user should choose one \n" +"Send userxyz-home # the user wants to be called " +"back on their home number\n" +"Expect ogin: # The callback process is now " +"running, a new connection, and so a new login.\n" +"ID\n" +"Expect assword: # Now send your password\n" +"Expect > # Wait for the command " +"prompt (the prompt may vary)\n" +"Send start_ppp # this command starts the pppd" + +#. Tag: screen +#: lauri.xml:150 +#, no-c-format +msgid "" +"Send # send an empty string\n" +"Expect ID:\n" +"Send itsme\n" +"Expect word:\n" +"Send forgot\n" +"Expect granted\n" +"Send ppp" +msgstr "" +"\n" +"Send # send einen leerenstring\n" +"Expect ID:\n" +"Send ichbins\n" +"Expect word:\n" +"Send forgot\n" +"Expect granted\n" +"Send ppp" + +#. Tag: programlisting +#: lauri.xml:152 +#, no-c-format +msgid "" +"-> #include <qpixmap.h>\n" +"-> #include <qpen.h>\n" +"\n" +" class KScribbleDoc\n" +" {\n" +"\n" +"-> protected:\n" +"\n" +"-> QPen currentPen(){ return pen;}; \n" +" \n" +"-> int penWidth()\n" +"-> { return pen.width(); }\n" +"\n" +" public slots:\n" +" void updateAllViews(KScribbleView *sender);\n" +" \n" +" protected:\n" +" \n" +"-> QPixmap buffer;\n" +" \n" +" private:\n" +"-> QPen pen;\n" +" /** the modified flag of the current document */\n" +" bool modified;" +msgstr "" +"\n" +"-> #include <qpixmap.h>\n" +"-> #include <qpen.h>\n" +" class KScribbleDoc\n" +" {\n" +"-> protected:\n" +"-> QPen currentPen(){ return pen;}; \n" +" \n" +"-> int penWidth()\n" +"-> { return pen.width(); }\n" +" public slots:\n" +" void updateAllViews(KScribbleView *sender);\n" +" \n" +" protected:\n" +" \n" +"-> QPixmap buffer;\n" +" \n" +" private:\n" +"-> QPen pen;\n" +" /** das veraenderte flag of the current document */\n" +" bool modified;" diff --git a/poxml/lauri.xml b/poxml/lauri.xml new file mode 100644 index 00000000..27715981 --- /dev/null +++ b/poxml/lauri.xml @@ -0,0 +1,241 @@ + + + Lauri" > +]> + + + + + +06/08/2000 +0.05.00 + + + +test document +StephanKulow + +This is nonsense +KDE + + + + +"Text" for &lauri; + +Section 1 + +Section 1.1 + + Warum kann ich + meinem Rechner nicht einfach + ausschalten?Hier noch + me can't be turned off. + + + Leading "Text" for &lauri; + + we pretend her name was Höpfner, but that is a good name too + + Shift + + + help + kde + an archive of the +developer's mailing list is at lists.kde.org. + + + + + + + + + Text 1 + + + + Text 2 + + + Text 3 "everything" + EverythingIs correct + NothingIs wrong + + + Text 4 \"even more\" + + + + + Text 4 \"even less\" + + + + + + + + + + + +CtrlN +FileNew + This starts a new Document in a new instance of the +editor. + + + + + + + What XML looks like + + + Here is an example of an XML file used + by Columbo to describe a search site on the + Internet: + + + + + + + + + + + + + + + + + + + + +]]> + + + + + This instruction is normally used to declare the DTD of the + document. Here no DTD is used, and only the name of the root + element (search) appears. + + + + + search begins the root + element. Here, it extends to the end of the document + (search). + + + + + This is an example of an empty element. Empty elements do not + need a closing tag (which would be + </input> in this case). + + + + + + + + +The ugly part + + Ending Text: + + +Matthias Hoelzer +KDE-Verein i.G. +Account-Nr. 2798296 + +Staedtische Sparkasse Wuerzburg +Hofstrasse 9 +97070 Wuerzburg +Germany +BLZ 790 500 00 +SWIFT-Address: BYLA DE 77 + +print "$b4 /path/to/KDE/libs/libpng.a $af\n"; \ +you see it here +whereever + + + + +Expect ogin: # remember, we do ordinary terminal login +ID "" # kppp sends the id you configured in the main dialog +Expect for userxyz: # a list of available numbers is shown, the user should choose one +Send userxyz-home # the user wants to be called back on their home number +Expect ogin: # The callback process is now running, a new connection, and so a new login. +ID +Expect assword: # Now send your password +Expect > # Wait for the command prompt (the prompt may vary) +Send start_ppp # this command starts the pppd + + + + + +Send # send an empty string +Expect ID: +Send itsme +Expect word: +Send forgot +Expect granted +Send ppp + + + +-> #include <qpixmap.h> +-> #include <qpen.h> + + class KScribbleDoc + { + +-> protected: + +-> QPen currentPen(){ return pen;}; + +-> int penWidth() +-> { return pen.width(); } + + public slots: + void updateAllViews(KScribbleView *sender); + + protected: + +-> QPixmap buffer; + + private: +-> QPen pen; + /** the modified flag of the current document */ + bool modified; + + + + + + + diff --git a/poxml/parser.cpp b/poxml/parser.cpp new file mode 100644 index 00000000..c34976bf --- /dev/null +++ b/poxml/parser.cpp @@ -0,0 +1,1008 @@ +// #define POXML_DEBUG + +#include "parser.h" +#include +#include +#include +#include + +using namespace std; + +static const char *singletags[] = {"beginpage","imagedata", "colspec", "spanspec", + "anchor", "xref", "area", + "footnoteref", "void", "inlinegraphic", + "glosssee", "graphic", "xi:include", + 0}; +static const char *cuttingtags[] = {"bridgehead", "trans_comment", "para", "title", "term", + "entry", "contrib", "keyword", "example", + "note", "footnote", "caution", + "informalexample", "remark", "comment", + "imageobject", "varlistentry", "thead", + "tbody", "tgroup", "row", "screenshot", "screeninfo", + "variablelist", "step", "procedure", + "step", "holder", "listitem", "important", + "author", "itemizedlist", "orderedlist", + "caption", "textobject", "mediaobject", + "tip", "glossdef", "inlinemediaobject", + "simplelist", "member", "glossentry", + "areaspec", "corpauthor", "indexterm", + "calloutlist", "callout", "subtitle", + "table", "part", "xi:fallback", "primary", + "secondary", "chapter", "sect1", "sect2", + "figure", "abstract", "sect3", "sect", "sect4", + "warning", "preface", "authorgroup", "keywordset", + "informaltable", "qandaentry", "question", "answer", + "othercredit", "affiliation", "qandaset", + "cmdsynopsis", "funcsynopsis", "funcsynopsisinfo" , + "epigraph", "attribution", "glossary", "chapterinfo", + "glossdiv", "blockingquote", "simplesect", "section", + "qandadiv", "refsect1", "refmeta", "formalpara", + "refentry", "refnamediv", "refpurpose", "refentrytitle", + "refmiscinfo", "refsect2", "refsect3", "refsect1info", + "refsect2info", "refsect3info", "refsection", "refsectioninfo", + "refsynopsisdiv", "refsysnopsisdivinfo", "remark", + "revdescription", "glossentry", "partinfo", + "segmentedlist", "segtitle", "seg", "seglistitem", "screenco", + 0}; +static const char *literaltags[] = {"literallayout", "synopsis", "screen", + "programlisting", 0}; + +bool StructureParser::fatalError ( const QXmlParseException &e ) +{ + cerr << "fatalError " << e.message().latin1() << " " << e.lineNumber() << " " + << e.columnNumber() << endl; + return false; +} + +bool StructureParser::startDocument() +{ + infos_reg = QRegExp("\\s*poxml_line=\"(\\d+)\" poxml_col=\"(\\d+)\""); + do_not_split_reg = QRegExp("\\s*condition=\"do-not-split\""); + message = ""; + inside = 0; + return true; +} + +bool StructureParser::isCuttingTag(const QString &qName) +{ + int index = 0; + while (cuttingtags[index]) { + if (cuttingtags[index] == qName) + return true; + index++; + } + return isLiteralTag(qName); +} + +bool StructureParser::isSingleTag(const QString &qName) +{ + int index = 0; + while (singletags[index]) { + if (singletags[index] == qName) + return true; + index++; + } + return false; +} + +bool StructureParser::isLiteralTag(const QString &qName) +{ + int index = 0; + while (literaltags[index]) { + if (literaltags[index] == qName) + return true; + index++; + } + return false; +} + +bool StructureParser::skippedEntity ( const QString & name ) +{ + if (inside) + message += QString("&%1;").arg(name); + return true; +} + +bool StructureParser::startElement( const QString& , const QString& , + const QString& qName, + const QXmlAttributes & attr ) +{ + QString tname = qName.lower(); + + bool first = false; + + if (isCuttingTag(tname)) { + if (!inside) { + message = QString::null; + list.pc.increasePara(); + startline = locator->lineNumber(); + startcol = locator->columnNumber(); + first = true; + } + inside++; + } + + if (inside) + { + QString tmp = "<" + tname; + for (int i = 0; i < attr.length(); i++) { + tmp += QString(" %1=\"%2\"").arg(attr.qName(i)).arg(attr.value(i)); + } + tmp += QString(" poxml_line=\"%1\"").arg(locator->lineNumber()); + tmp += QString(" poxml_col=\"%1\"").arg(locator->columnNumber()); + + if (isSingleTag(qName)) + tmp += "/>"; + else + tmp += ">"; + message += tmp; + if (first) + startcol -= message.length(); + } + + if (tname == "anchor" || tname.left(4) == "sect" || tname == "chapter") + if (!attr.value("id").isEmpty()) list.pc.addAnchor(attr.value("id")); + + return true; +} + +bool StructureParser::startCDATA() +{ + if ( inside ) + message += ""; + return true; +} + +bool StructureParser::isClosure(const QString &message) +{ + assert(message.at(0) == '<'); + int endindex = 1; + while (!message.at(endindex).isSpace() && message.at(endindex) != '>') + endindex++; + QString tag = message.mid(1, endindex - 1); + return closureTag(message, tag); +} + +bool StructureParser::closureTag(const QString& message, const QString &tag) +{ +#ifdef POXML_DEBUG + qDebug("closureTag %s %s", message.latin1(), tag.latin1()); +#endif + + int inside = 0; + uint index = 0; + while (true) + { + int nextclose = message.find(QRegExp(QString::fromLatin1("]").arg(tag)), index); + int nextstart = message.find(QRegExp(QString::fromLatin1("<%1[>\\s]").arg(tag)), index); + // qDebug("finding %d %d %d %d", nextstart, nextclose, index, inside); + if (nextclose == -1) { +#ifdef POXML_DEBUG + qDebug("ending on no close anymore %d %d %d %d", (!inside && index >= message.length()), inside, index, message.length()); +#endif + return !inside && index >= message.length(); + } + if (nextstart == -1) + nextstart = message.length() + 1; + + if (nextstart < nextclose) { + inside++; + index = nextstart + 1; + while (message.at(index) != '>') + index++; + index++; + } else { + inside--; + index = nextclose + 1; + while (message.at(index) != '>') + index++; + index++; + if (!inside) { +#ifdef POXML_DEBUG + qDebug("ending on exit %d", index >= message.length()); +#endif + return index >= message.length(); + } + } + } +} + +void StructureParser::descape(QString &message) +{ + uint index = 0; + stripWhiteSpace( message ); + + int inside = 0; + bool lastws = false; + + while (index < message.length()) { + switch (message.at(index).latin1()) { + case '\n': + case '\t': + case '\r': + if (!inside) + message[index] = ' '; + case ' ': + if (!inside && lastws) + message[index] = '\010'; + lastws = true; + break; + case '<': { + uint endindex = index+1; + while (endindex < message.length() && !message.at(endindex).isSpace() && + message.at(endindex) != '>') + endindex++; + QString tag = message.mid(index + 1, endindex - index - 1); + if (tag.at(0) == '/') { + if (isLiteralTag(tag.mid(1))) + inside--; + } else + if (isLiteralTag(tag)) + inside++; + break; + } + default: + lastws = false; + } + + index++; + } + message.replace(QRegExp("\010"), ""); +} + +bool StructureParser::formatMessage(MsgBlock &msg) const +{ +#ifdef POXML_DEBUG + qDebug("formatMessage %s", msg.msgid.latin1()); +#endif + + int offset = 0; + bool changed = false; + bool recurse = true; + + if (msg.msgid.isEmpty()) + return true; + + for (int index = 0; msg.msgid.at(index) == ' '; index++, offset++); + stripWhiteSpace( msg.msgid ); + + // removing starting single tags + for (int index = 0; singletags[index]; index++) + { + int slen = strlen(singletags[index]); + + if (msg.msgid.left(slen + 1) == QString::fromLatin1("<%1").arg(singletags[index]) && + !msg.msgid.at( slen + 1 ).isLetterOrNumber() ) + { +#ifdef POXML_DEBUG + qDebug("removing single tag %s", singletags[index]); +#endif + int strindex = strlen(singletags[index]) + 1; + while (msg.msgid.at(strindex) != '>') + strindex++; + msg.msgid = msg.msgid.mid(strindex + 1); + changed = true; + offset += strindex + 1; + for (int index = 0; msg.msgid.at(index) == ' '; index++, offset++) ; + stripWhiteSpace( msg.msgid ); + } + } + + while (msg.msgid.right(2) == "/>") + { + int strindex = msg.msgid.length() - 2; + while (msg.msgid.at(strindex) != '<') + strindex--; + msg.msgid = msg.msgid.left(strindex); + stripWhiteSpace( msg.msgid ); // only removed space at the end + changed = true; + } + + for (int index = 0; msg.msgid.at(index) == ' '; index++, offset++) ; + stripWhiteSpace( msg.msgid ); + + while (true) { + if (msg.msgid.at(0) != '<') + break; + if (msg.msgid.at(msg.msgid.length() - 1) != '>') + break; + int strindex = 1; + while (msg.msgid.at(strindex) != ' ' && msg.msgid.at(strindex) != '>') + strindex++; + QString starttag = msg.msgid.mid(1, strindex - 1); + int endindex = msg.msgid.length() - 2; + while (msg.msgid.at(endindex) != '<' && msg.msgid.at(endindex + 1) != '/') + endindex--; +#ifdef POXML_DEBUG + qDebug("endIndex %d", endindex); +#endif + strindex = endindex; + QString orig = msg.msgid; + + QString endtag = msg.msgid.mid(endindex + 2, msg.msgid.length() - (endindex + 2) - 1); + QString endtag_attr = endtag.mid(endtag.find(' '), endtag.length()); + endtag.replace(infos_reg, ""); + if (endtag == starttag) { + if (!closureTag(msg.msgid, starttag)) + break; + + // removing start/end tags + msg.msgid = msg.msgid.left(endindex); + strindex = 0; + while (msg.msgid.at(strindex) != '>') + strindex++; + QString attr = msg.msgid.left(strindex); + msg.msgid = msg.msgid.mid(strindex + 1); + offset += strindex + 1; + for (int index = 0; msg.msgid.at(index) == ' '; index++, offset++) ; + stripWhiteSpace( msg.msgid ); + msg.tag = starttag; + + if (infos_reg.search(attr) >= 0) { + msg.lines.first().start_line = infos_reg.cap(1).toInt(); + msg.lines.first().start_col = infos_reg.cap(2).toInt(); +#ifdef POXML_DEBUG + qDebug("col %s %s %d", attr.latin1(), msg.msgid.latin1(), msg.lines.first().start_col); +#endif + offset = 0; + + if (infos_reg.search(endtag_attr) >= 0) { + msg.lines.first().end_line = infos_reg.cap(1).toInt(); + msg.lines.first().end_col = infos_reg.cap(2).toInt() + 1; + } + } + if (do_not_split_reg.search(attr) >= 0) { + msg.do_not_split = true; + break; + } + + changed = true; + } else + break; + } + +#ifdef POXML_DEBUG + qDebug("formatMessage result %s %d %d", msg.msgid.latin1(), changed && recurse, msg.lines.first().start_col); +#endif + + msg.lines.first().offset += offset; + if (msg.do_not_split) + recurse = false; + + if (changed && recurse) + formatMessage(msg); + + return !recurse; // indicates an abort +} + +MsgList StructureParser::splitMessage(const MsgBlock &mb) +{ + MsgList result; + + MsgBlock msg1 = mb; + MsgBlock msg2 = mb; + + QString message = mb.msgid; + +#ifdef POXML_DEBUG + qDebug("splitMessage %s", message.latin1()); +#endif + + if (message.at(0) == '<') { + int endindex = 1; + while (!message.at(endindex).isSpace() && message.at(endindex) != '>') + endindex++; + QString tag = message.mid(1, endindex - 1); + + if (closureTag(message, tag)) + goto error; + + if (isCuttingTag(tag)) + { + // if the message starts with a cutting tag, this tag has to + // end in between. We split both messages and format them + int strindex = endindex; + strindex++; + + int inside = 1; + while (true) { +#ifdef POXML_DEBUG + qDebug("inside %s %d", message.mid(strindex, 35).latin1(), inside); +#endif + + // the exception for poxml_* attributes is made in the closing tag + int closing_index = message.find(QRegExp(QString::fromLatin1("]").arg(tag)), + strindex); + int starting_index = message.find(QRegExp(QString::fromLatin1("<%1[\\s>]").arg(tag)), + strindex); + +#ifdef POXML_DEBUG + qDebug("index1 %d %d %d", closing_index, starting_index, strindex); +#endif + + // when a new start was found, we set the start_index after the next match + // (and set strindex to it later - increasing inside) + if (starting_index != -1) { + starting_index += tag.length() + 1; + while (message.at(starting_index) != '>') + starting_index++; + starting_index++; + } + +#ifdef POXML_DEBUG + qDebug("index %d %d %d", closing_index, starting_index, strindex); +#endif + + assert(closing_index != -1); + closing_index += 3 + tag.length(); + while (message.at(closing_index - 1) != '>') + closing_index++; + + if (starting_index == -1) { + strindex = closing_index; +#ifdef POXML_DEBUG + qDebug("set strindex %d", strindex); +#endif + inside--; + if (!inside) + break; + continue; + } + if (closing_index < starting_index) + { + strindex = closing_index; + inside--; + } else { + strindex = starting_index; + inside++; + } + + if (!inside) + break; + } + +#ifdef POXML_DEBUG + qDebug("split into %s -AAAAAANNNNNNDDDDDD- %s", message.left(strindex).latin1(), message.mid(strindex).latin1()); +#endif + msg1.msgid = message.left(strindex); + bool leave = formatMessage(msg1); + + msg2.msgid = message.mid(strindex); + msg2.lines.first().offset += strindex; + leave = leave & formatMessage(msg2); + + if (msg1.lines.first().end_line > msg2.lines.first().start_line || + (msg1.lines.first().end_line == msg2.lines.first().start_line && + msg1.lines.first().end_col > msg2.lines.first().start_col)) + { + msg2.lines.first().start_line = msg1.lines.first().end_line; + msg2.lines.first().start_col = msg1.lines.first().end_col; + } + +#ifdef POXML_DEBUG + qDebug("splited %d-%d(%s) and %d-%d(%s)", msg1.lines.first().end_line,msg1.lines.first().end_col, + msg1.msgid.latin1(), + msg2.lines.first().start_line,msg2.lines.first().start_col, msg2.msgid.latin1()); +#endif + + if (leave) { + result.append(msg1); + result.append(msg2); + return result; + } + result = splitMessage(msg1); + result += splitMessage(msg2); + return result; + } + + } + + if (message.at(message.length() - 1 ) == '>') + { + int endindex = message.length() - 1; + while (endindex >= 0 && (message.at(endindex) != '<' || message.at(endindex + 1) != '/')) + endindex--; + QString tag = message.mid(endindex + 2, message.length() - endindex - 3); + if (tag.find(' ') > 0 ) { + tag = tag.left(tag.find(' ')); + } +#ifdef POXML_DEBUG + qDebug("behind tag %s", tag.latin1()); +#endif + + if (isCuttingTag(tag)) + { + // if the message ends with a cutting tag, this tag has to + // start in between. We split both messages and format them + int strindex = endindex; + + int inside = 1; + while (true) { +#ifdef POXML_DEBUG + qDebug("inside %s %d", message.mid(strindex, 35).latin1(), inside); +#endif + + int closing_index = message.findRev(QRegExp(QString::fromLatin1("]").arg(tag)), + strindex - 1); + int starting_index = message.findRev(QRegExp(QString::fromLatin1("<%1[\\s>]").arg(tag)), + strindex - 1); + +#ifdef POXML_DEBUG + qDebug("index1 %d %d %d", closing_index, starting_index, strindex); +#endif + + if (starting_index == -1) { + assert(inside == 1); + break; + } + + if (closing_index > starting_index) + { + strindex = closing_index; + inside++; + } else { + strindex = starting_index; + inside--; + } + + if (!inside) + break; + } + + +#ifdef POXML_DEBUG + qDebug("split2 into \"%s\" -AAAAAANNNNNNNNNDDDDDDDDDDD- \"%s\"", message.left(strindex).latin1(), message.mid(strindex).latin1()); +#endif + + msg1.msgid = message.left(strindex); + formatMessage(msg1); + + msg2.msgid = message.mid(strindex); + msg2.lines.first().offset += strindex; + formatMessage(msg2); + + if (msg1.lines.first().end_line > msg2.lines.first().start_line || + (msg1.lines.first().end_line == msg2.lines.first().start_line && + msg1.lines.first().end_col > msg2.lines.first().start_col)) + { + msg1.lines.first().end_line = msg2.lines.first().start_line; + msg1.lines.first().end_col = msg2.lines.first().start_col - 1; + } + +#ifdef POXML_DEBUG + qDebug("splited %d-%d(%s) and %d-%d(%s)", msg1.lines.first().end_line,msg1.lines.first().end_col, + msg1.msgid.latin1(), + msg2.lines.first().start_line,msg2.lines.first().start_col, msg2.msgid.latin1()); +#endif + + result = splitMessage(msg1); + result += splitMessage(msg2); + + return result; + } + } +error: + result.append(mb); + return result; +} + +bool StructureParser::endElement( const QString& , const QString&, const QString& qName) +{ + QString tname = qName.lower(); + + // qDebug("endElement %s - %s %d", tname.latin1(), message.latin1(), inside); + + if (inside) { + if (!isSingleTag(qName)) { + message += QString("lineNumber()); + message += QString(" poxml_col=\"%1\"").arg(locator->columnNumber()); + message += ">"; + } + } + + if (isCuttingTag(tname)) { + inside--; + if (!inside) { + MsgBlock m; + descape(message); + m.msgid = message; + + BlockInfo bi; + bi.start_line = startline; + bi.start_col = startcol; + bi.end_line = locator->lineNumber(); + bi.end_col = locator->columnNumber() + 1; + bi.offset = m.lines.first().offset; + m.lines.append(bi); + formatMessage(m); + + MsgList messages = splitMessage(m); + for (MsgList::Iterator it = messages.begin(); + it != messages.end(); ++it) + { +#ifdef POXML_DEBUG + qDebug("parser '%s' %d '%s' %d:%d", (*it).msgid.latin1(), (*it).lines.first().offset, message.mid((*it).lines.first().offset, 15).latin1(), (*it).lines.first().start_line, (*it).lines.first().start_col); +#endif + // if the remaining text still starts with a tag, the poxml_ info + // is most probably more correct + if ((*it).msgid.at(0) == '<' && isClosure((*it).msgid)) { + if (infos_reg.search((*it).msgid) >= 0) { + (*it).lines.first().start_line = infos_reg.cap(1).toInt(); + (*it).lines.first().start_col = infos_reg.cap(2).toInt();; + (*it).lines.first().offset = 0; + } + } + (*it).msgid.replace(infos_reg, QString::null); + + if (!(*it).msgid.isEmpty()) + list.append(*it); + } + } + } + + return true; +} + +bool StructureParser::comment ( const QString &c ) +{ + if (c.left(7) != " TRANS:") + return true; + + assert(false); + return true; +} + +QString StructureParser::escapeLiterals( const QString &_contents) { + QString contents = _contents; + + contents.replace(QRegExp("\n"), "&POXML_LINEFEED;"); + contents.replace(QRegExp("<"), "&POXML_LT;"); + contents.replace(QRegExp(">"), "&POXML_GT;"); + contents.replace(QRegExp("\t"), " "); + contents.replace(QRegExp(" "), "&POXML_SPACE;"); + + return contents; +} + +QString StructureParser::descapeLiterals( const QString &_contents) { + QString contents = _contents; + + contents.replace(QRegExp("&POXML_LINEFEED;"), "\n"); + contents.replace(QRegExp("&POXML_LT;"), "<"); + contents.replace(QRegExp("&POXML_GT;"), ">"); + contents.replace(QRegExp("&POXML_SPACE;"), " "); + contents.replace(QRegExp("!POXML_AMP!"), "&"); + return contents; +} + +void StructureParser::stripWhiteSpace( QString &contents) +{ + contents = contents.stripWhiteSpace(); + bool changed; + do { + changed = false; + if (contents.startsWith("&POXML_LINEFEED;")) { + contents = contents.mid(strlen("&POXML_LINEFEED;"), contents.length()); + changed = true; + } + if (contents.startsWith("&POXML_SPACE;")) { + contents = contents.mid(strlen("&POXML_SPACE;"), contents.length()); + changed = true; + } + if (contents.endsWith("&POXML_LINEFEED;")) { + contents = contents.left(contents.length() - strlen("&POXML_LINEFEED;")); + changed = true; + } + if (contents.endsWith("&POXML_SPACE;")) { + contents = contents.left( contents.length() - strlen("&POXML_SPACE;")); + changed = true; + } + } while (changed); +} + +void StructureParser::cleanupTags( QString &contents ) +{ + contents.replace(QRegExp("&"), "!POXML_AMP!"); + + for (int index = 0; literaltags[index]; index++) { + QRegExp start(QString("<%1[\\s>]").arg(literaltags[index])); + QRegExp end(QString("]").arg(literaltags[index])); + int strindex = 0; + while (true) { + strindex = contents.find(start, strindex); + if (strindex < 0) + break; + while (contents.at(strindex) != '>') + strindex++; + strindex++; // one more + int endindex = contents.find(end, strindex); + QString part = contents.mid(strindex, endindex - strindex); + QString newpart = escapeLiterals(part); + contents.replace(strindex, part.length(), newpart); + // this assumes that literal tags to not overlap + strindex = strindex + newpart.length(); + } + } + + QRegExp unclosed(""); + int index = -1; + while (true) { + index = unclosed.search(contents, index + 1); + if (index < 0) + break; + QString tag = unclosed.cap(1); + contents.replace(index, unclosed.matchedLength(), QString("").arg(tag)); + } + + QRegExp start("<((\\s*[^<>\\s])*)\\s\\s*(/*)>"); + start.setMinimal(true); + + index = -1; + while (true) { + index = start.search(contents, index + 1); + if (index < 0) + break; + QString tag = start.cap(1); + QString cut = start.capturedTexts().last(); + // qDebug("UNCLO %s %d -%s- -%s-", start.cap(0).latin1(), index, tag.latin1(), cut.latin1()); + contents.replace(index, start.matchedLength(), QString("<%1%2>").arg(tag).arg(cut)); + } + QRegExp singletag("<(\\w*)\\s([^><]*)/>"); + + index = -1; + while (true) { + index = singletag.search(contents, index + 1); + if (index < 0) + break; + QString tag = singletag.cap(1); + if (!StructureParser::isSingleTag(tag)) { + contents.replace(index, singletag.matchedLength(), QString("<%1 %2>").arg(tag).arg(singletag.cap(2)).arg(tag)); + } + } + + QRegExp trans_comment(""); + index = -1; + while (true) { + index = trans_comment.search(contents, index + 1); + if (index < 0) + break; + QString msgid = trans_comment.cap(1); + contents.replace(index, trans_comment.matchedLength(), QString("%1").arg(msgid)); + } + +#ifdef POXML_DEBUG + qDebug("final %s", contents.latin1()); +#endif + +} + +static bool removeEmptyTag( QString &contents, const QString & tag) +{ +// qDebug("cont %s %s", contents.latin1(), tag.latin1()); + + QRegExp empty(QString("<%1[^>]*>[\\s\n][\\s\n]*").arg(tag).arg(tag)); + int strindex = 0; + while (true) { + strindex = contents.find(empty, strindex); + if (strindex < 0) + break; + qDebug("found empty tag %s", tag.latin1()); + contents.replace(strindex, empty.matchedLength(), " "); + strindex++; + return true; + } + return false; +} + +void StructureParser::removeEmptyTags( QString &contents ) +{ + bool removed; + do { + removed = false; + + for (int index = 0; cuttingtags[index]; index++) { + if (removeEmptyTag(contents, cuttingtags[index])) { + removed = true; + break; + } + } + // as glossterm has two different semantics, it's likely + // to break something when it's cuttingtag + if (removeEmptyTag(contents, "glossterm")) + removed = true; + + } while (removed); +} + +bool StructureParser::characters(const QString &ch) +{ + if (inside && !ch.isEmpty()) + message += ch; + return true; +} + +QString escape(QString message) +{ + message.replace(QRegExp("\\\\"), "\\\\"); + message.replace(QRegExp("\""), "\\\""); + return message; +} + +void outputMsg(const char *prefix, const QString &message) +{ + QStringList list = QStringList::split('\n', message, true); + QString line; + + if (list.count() == 1) { + line = list.first(); + if (line.isEmpty()) + cout << prefix << " \"\"\n"; + else + cout << prefix << " \"" << escape(line).utf8().data() << "\"\n"; + } else { + cout << prefix << " \"\"\n"; + for (QStringList::ConstIterator it = list.begin(); it != list.end(); it++) { + line = *it; + if (!line.isEmpty()) { + cout << " \"" << escape(line).utf8().data(); + if (it == list.fromLast()) + cout << "\"\n"; + else + cout << "\\n\"\n"; + } else { + cout << " \""; + if (it != list.fromLast()) + cout << "\\n"; + cout << "\"\n"; + } + } + } +} + +QString escapePO(QString msgid) +{ + int index = 0; + while (true) { + index = msgid.find("\\n", index); + if (index == -1) + break; + if (index >= 1 && msgid.at(index - 1) == '\\' && msgid.at(index - 2) != '\\') { + msgid.replace(index - 1, 3, "&POXML_LITERALLINEFEED;"); + index += 3; + } else + msgid.replace(index, 2, "\n"); + } + index = 0; + while (true) { + index = msgid.find("\\\"", index); + if (index == -1) + break; + if (index > 1 && msgid.at(index - 1) == '\\' && msgid.at(index - 2) != '\\') + msgid.replace(index - 1, 3, "&POXML_LITERALQUOTE;"); + else + msgid.replace(index, 2, "\""); + } + index = 0; + while (true) { + index = msgid.find("\\t", index); + if (index == -1) + break; + if (msgid.at(index - 1) == '\\') + msgid.replace(index - 1, 3, "\\t"); + else + msgid.replace(index, 2, "\t"); + } + index = 0; + while (true) { + index = msgid.find("\\\\", index); + if (index == -1) + break; + msgid.replace(index, 2, "\\"); + index += 1; + } + + msgid.replace(QRegExp("&POXML_LITERALLINEFEED;"), "\\n"); + msgid.replace(QRegExp("&POXML_LITERALQUOTE;"), "\\"); + return msgid; +} + + +MsgList parseXML(const char *filename) +{ + StructureParser handler; + QFile xmlFile( filename ); + xmlFile.open(IO_ReadOnly); + + QCString ccontents; + ccontents.fill(0, xmlFile.size() + 1); + memcpy(ccontents.data(), xmlFile.readAll().data(), xmlFile.size()); + xmlFile.close(); + + QString contents = QString::fromUtf8( ccontents ); + StructureParser::cleanupTags(contents); + + while (true) { + int index = contents.find("' || inside) + { + switch (contents.at(endindex).latin1()) { + case '<': + inside++; break; + case '>': + inside--; break; + case '\n': + replacement += '\n'; + break; + default: + break; + } + endindex++; + } + endindex++; + contents.replace(index, endindex - index, replacement); + } + + QTextStream ts(contents.utf8(), IO_ReadOnly); + QXmlInputSource source( ts ); + QXmlSimpleReader reader; + reader.setFeature( "http://trolltech.com/xml/features/report-start-end-entity", true); + reader.setContentHandler( &handler ); + reader.setLexicalHandler( &handler ); + reader.setDTDHandler( &handler ); + // reader.setErrorHandler( &handler ); + reader.parse( source ); + MsgList english = handler.getList(); + + bool changed = false; + + do { + changed = false; + QMap msgids; + + for (MsgList::Iterator it = english.begin(); + it != english.end(); it++) + { + QMap::Iterator found = msgids.find((*it).msgid); + if ((*it).msgid.length() < 4) { + (*it).msgid = QString("<%1>").arg((*it).tag) + (*it).msgid + + QString("").arg((*it).tag); + changed = true; + break; + } + if (found != msgids.end()) { + if (found.data() != (*it).tag) { +#ifdef POXML_DEBUG + qDebug("same msgid for '%s' and '%s'", found.data().latin1(), (*it).tag.latin1()); +#endif + changed = true; + QString msgid = (*it).msgid; + for (MsgList::Iterator it2 = english.begin(); + it2 != english.end(); it2++) + { + if ((*it2).msgid == msgid) + (*it2).msgid = QString("<%1>").arg((*it2).tag) + msgid + QString("").arg((*it2).tag); + } + break; + } + } else { + msgids.insert((*it).msgid, (*it).tag); + } + } + } while (changed); + + return english; +} + diff --git a/poxml/parser.h b/poxml/parser.h new file mode 100644 index 00000000..f63f6cef --- /dev/null +++ b/poxml/parser.h @@ -0,0 +1,124 @@ +#ifndef PARSER_H +#define PARSER_H + +#include +#include +#include + +struct BlockInfo { + int start_line; + int start_col; + int end_line; + int end_col; + + // used to detect sub-messages + int offset; + + BlockInfo() { + start_line = 0; + start_col = 0; + end_line = 0; + end_col = 0; + + // used to detect sub-messages + offset = 0; + } +}; + +class MsgBlock { + public: + MsgBlock() { start = end = 0; do_not_split = false; } + MsgBlock(const MsgBlock &rhs ) { + *this = rhs; + } + QValueList lines; + QString tag; + QString comment; + QString msgid; + QString msgid_plural; + QString msgstr; + QStringList msgstr_plurals; + int start, end; + bool do_not_split; + + void operator=(const MsgBlock& rhs) { + lines = rhs.lines; + tag = rhs.tag; + comment = rhs.comment; + msgid = rhs.msgid; + msgid_plural = rhs.msgid_plural; + msgstr = rhs.msgstr; + msgstr_plurals = rhs.msgstr_plurals; + start = rhs.start; + end = rhs.end; + do_not_split = rhs.do_not_split; + } +}; + +class ParaCounter +{ +public: + ParaCounter() { current = 0; } + void addAnchor(QString anchor) { anchors.insert(anchor, current); } + void increasePara() { current++; } + + QMap anchors; + int current; +}; + +class MsgList : public QValueList +{ +public: + MsgList() {} + ParaCounter pc; +}; + +class StructureParser : public QXmlDefaultHandler +{ +public: + bool startDocument(); + bool startElement( const QString&, const QString&, const QString& , + const QXmlAttributes& ); + bool endElement( const QString&, const QString&, const QString& ); + bool characters( const QString &ch); + static bool isCuttingTag(const QString &tag); + static bool isSingleTag(const QString &qName); + static bool isLiteralTag(const QString &qName); + void setDocumentLocator ( QXmlLocator * l ) { locator = l; } + bool skippedEntity ( const QString & name ); + bool fatalError ( const QXmlParseException & ); + bool comment ( const QString & ); + bool error(const QXmlParseException &e ) { return fatalError(e); } + bool warning(const QXmlParseException &e ) { return fatalError(e); } + MsgList getList() const { return list; } + MsgList splitMessage(const MsgBlock &message); + + virtual bool startCDATA(); + virtual bool endCDATA(); + + static bool closureTag(const QString& message, const QString &tag); + static bool isClosure(const QString &message); + static void descape(QString &message); + static QString escapeLiterals( const QString &contents); + static QString descapeLiterals( const QString &contents); + static void cleanupTags( QString &contents ); + static void removeEmptyTags( QString &contents); + static void stripWhiteSpace( QString &contents); + +private: + bool formatMessage(MsgBlock &message) const; + + QXmlLocator *locator; + QString message; + int inside, startline, startcol; + int line; + MsgList list; + mutable QRegExp infos_reg; + mutable QRegExp do_not_split_reg; +}; + +void outputMsg(const char *prefix, const QString &message); +MsgList parseXML(const char *filename); +QString escapePO(QString msgid); + +#endif diff --git a/poxml/po2xml.cpp b/poxml/po2xml.cpp new file mode 100644 index 00000000..9e8bc1a5 --- /dev/null +++ b/poxml/po2xml.cpp @@ -0,0 +1,261 @@ + // #define POXML_DEBUG + +#include "parser.h" +#include +#include +#include +#include + +#include +#include "GettextLexer.hpp" +#include "GettextParser.hpp" +#include "antlr/AST.hpp" +#include "antlr/CommonAST.hpp" + +using namespace std; + +QString translate(QString xml, QString orig, QString translation) +{ + QString prefix; + while (xml.at(0) == '<' && orig.at(0) != '<') { + // a XML tag as prefix + int index = xml.find('>'); + assert(index != -1); + index++; + while (xml.at(index) == ' ') + index++; + prefix = prefix + xml.left(index); + xml = xml.mid(index, xml.length()); + } + + int index = xml.find(orig); + if (index == -1) { + qWarning("can't find\n%s\nin\n%s", orig.latin1(), xml.latin1()); + exit(1); + } + if (!translation.isEmpty()) + xml.replace(index, orig.length(), translation); + return prefix + xml; +} + +int main( int argc, char **argv ) +{ + if (argc != 3) { + qWarning("usage: %s english-XML translated-PO", argv[0]); + ::exit(1); + } + + MsgList english = parseXML(argv[1]); + MsgList translated; + + try { + ifstream s(argv[2]); + GettextLexer lexer(s); + GettextParser parser(lexer); + translated = parser.file(); + + } catch(exception& e) { + cerr << "exception: " << e.what() << endl; + return 1; + } + + QMap translations; + for (MsgList::ConstIterator it = translated.begin(); + it != translated.end(); ++it) + { + QString msgstr; + QString msgid = escapePO((*it).msgid); + if ((*it).comment.find("fuzzy") < 0) + msgstr = escapePO((*it).msgstr); + +#ifdef POXML_DEBUG + qDebug("inserting translations '%s' -> '%s'", msgid.latin1(),msgstr.latin1()); +#endif + translations.insert(msgid, msgstr); + } + + QFile xml(argv[1]); + xml.open(IO_ReadOnly); + QTextStream ds(&xml); + ds.setEncoding(QTextStream::UnicodeUTF8); + QString xml_text = ds.read(); + xml.close(); + QString output; + QTextStream ts(&output, IO_WriteOnly); + StructureParser::cleanupTags(xml_text); + + QValueList line_offsets; + line_offsets.append(0); + int index = 0; + while (true) { + index = xml_text.find('\n', index) + 1; + if (index <= 0) + break; + line_offsets.append(index); + } + + int old_start_line = -1, old_start_col = -1; + QString old_text; + MsgList::Iterator old_it = english.end(); + + for (MsgList::Iterator it = english.begin(); + it != english.end(); ++it) + { + BlockInfo bi = (*it).lines.first(); + int start_pos = line_offsets[bi.start_line - 1] + bi.start_col; + if (!bi.end_line) + continue; + int end_pos = line_offsets[bi.end_line - 1] + bi.end_col - 1; + + (*it).start = start_pos; + if (old_start_line == bi.start_line && + old_start_col == bi.start_col) + { + (*old_it).end = bi.offset; + (*it).end = end_pos; + } else { + (*it).lines.first().offset = 0; + (*it).end = 0; + } + + old_start_line = bi.start_line; + old_start_col = bi.start_col; + old_it = it; + } + + int old_pos = 0; + + for (MsgList::Iterator it = english.begin(); + it != english.end(); ++it) + { + BlockInfo bi = (*it).lines.first(); + int start_pos = line_offsets[bi.start_line - 1] + bi.start_col; + if (!bi.end_line) + continue; + int end_pos = line_offsets[bi.end_line - 1] + bi.end_col - 1; + + QString xml = xml_text.mid(start_pos, end_pos - start_pos); + int index = 0; + while (true) { + index = xml.find(""); + cout << "\n"; + } + + if ( !have_credit_for_translators) { + outputMsg("msgid", "CREDIT_FOR_TRANSLATORS"); + outputMsg("msgstr", ""); + cout << "\n"; + } + } + + return 0; +} diff --git a/poxml/swappo.cpp b/poxml/swappo.cpp new file mode 100644 index 00000000..94c308ae --- /dev/null +++ b/poxml/swappo.cpp @@ -0,0 +1,38 @@ +#include +using namespace std; +#include "GettextParser.hpp" +#include +#include "GettextLexer.hpp" + +int main(int argc, char **argv) +{ + if ( argc != 2 ) { + qWarning( "usage: %s pofile", argv[0] ); + return -1; + } + + MsgList translated; + + try { + ifstream s(argv[1]); + GettextLexer lexer(s); + GettextParser parser(lexer); + translated = parser.file(); + + } catch(exception& e) { + cerr << "exception: " << e.what() << endl; + return 1; + } + + for (MsgList::ConstIterator it = translated.begin(); + it != translated.end(); ++it) + { + if ( !( *it ).msgstr.isEmpty() ) { + outputMsg("msgid", (*it).msgstr); + outputMsg("msgstr", (*it).msgid); + cout << "\n"; + } + } + +} + diff --git a/poxml/transxx.cpp b/poxml/transxx.cpp new file mode 100644 index 00000000..dc3dde00 --- /dev/null +++ b/poxml/transxx.cpp @@ -0,0 +1,130 @@ +#include +using namespace std; +#include "GettextParser.hpp" +#include +#include "GettextLexer.hpp" + +#include +#include +#include + +int main(int argc, char **argv) +{ + if ( argc != 2 && argc != 4 ) { + qWarning( "usage: %s [--text translation] potfile", argv[0] ); + return -1; + } + + QString translation = "xx"; + QCString filename; + + if( argc == 4 ) { + if( argv[1]!=QString("--text") ) { + qWarning( "usage: %s [--text translation] potfile", argv[0] ); + return -1; + } + translation = QString::fromLocal8Bit(argv[2]); + filename = argv[3]; + } else { + filename = argv[1]; + } + + MsgList translated; + + try { + ifstream s(filename); + GettextLexer lexer(s); + GettextParser parser(lexer); + translated = parser.file(); + + } catch(exception& e) { + cerr << "exception: " << e.what() << endl; + return 1; + } + + const bool is_desktop = filename.find( "desktop_") >= 0; + + // The header is the last item (due too the sorting) + MsgList::const_iterator header = --translated.end(); + if ( ( header == translated.end() ) || ( ! ( *header ).msgid.isEmpty() ) ) + { + cerr << "Cannot find correct header msgid\n"; + cout << "\"Content-Type: text/plain; charset=utf-8\\n\"\n"; + cout << "\"Plural-Forms: nplurals=1; plural=0;\\n\"\n"; + } + else + { + QStringList headerLines = QStringList::split( "\\n", ( *header ).msgstr, false ); + QFileInfo fi( QString::fromLocal8Bit( filename ) ); + QString projectId( "Project-Id-Version: " ); + projectId += fi.baseName( false ); + headerLines.gres( QRegExp( "^Project-Id-Version:.*" ), projectId ); + headerLines.gres( QRegExp( "^Last-Translator:.*" ), "Last-Translator: transxx program " ); + headerLines.gres( QRegExp( "^Language-Team:.*" ), "Language-Team: Test Language " ); + QString revisionDate ( "PO-Revision-Date: " ); + const QDateTime dt = QDateTime::currentDateTime( Qt::UTC ); + revisionDate += dt.toString( "yyyy-MM-dd hh:mm+0000" ); + headerLines.gres( QRegExp( "^PO-Revision-Date:.*" ), revisionDate ); + headerLines << "Plural-Forms: nplurals=1; plural=0;"; + outputMsg ( "msgid", "" ); + outputMsg ( "msgstr", escapePO( headerLines.join("\\n") + "\\n" ) ); + } + cout << "\n"; + + for (MsgList::ConstIterator it = translated.begin(); + it != translated.end(); ++it) + { + QString msgid = ( *it ).msgid; + QString msgid_plural = ( *it ).msgid_plural; + if ( !msgid.isEmpty() ) { + outputMsg("msgid", escapePO( msgid) ); + + if ( ! msgid_plural.isEmpty() ) { + outputMsg("msgid_plural", escapePO( msgid_plural ) ); + } + + QString msgstr; + + if ( msgid.find( "Definition of PluralForm" ) != -1 ) { + outputMsg("msgstr", "NoPlural"); + cout << "\n"; + continue; + } + + if ( is_desktop ) { + msgstr = msgid.left( msgid.find( '=' ) + 1); + msgstr += translation + msgid.mid( msgid.find( '=' ) + 1) + translation; + outputMsg( "msgstr", escapePO(msgstr) ); + cout << "\n"; + continue; + } + + if (msgid.startsWith("_n: ") || msgid.startsWith("_: ") ) { // KDE extentions + msgid = msgid.mid(msgid.find("\\n") + 2, msgid.length()); + } + + if (msgid.endsWith("%")) + msgstr = translation + msgid + " " + translation; + else + msgstr = translation + msgid + translation; + + // Note: msgid has been modified, so we need to go back to the original version by the help of the iterator + // (Gettext is not aware of the KDE-specific handling, so it really wants a \n at start and at end in the msgstr if they were in the msgid ) + if ( ( *it ).msgid.endsWith( "\\n" ) && ! ( *it ).msgid.endsWith( "\\\\n" )) + msgstr += "\n"; + if ( ( *it ).msgid.startsWith( "\\n" ) ) + msgstr.prepend( "\n" ); + + if ( msgid_plural.isEmpty() ) { + outputMsg("msgstr", escapePO( msgstr) ); + } + else + { + outputMsg("msgstr[0]", escapePO( msgstr) ); + } + cout << "\n"; + } + } + +} + diff --git a/poxml/xml2pot.cpp b/poxml/xml2pot.cpp new file mode 100644 index 00000000..593e75be --- /dev/null +++ b/poxml/xml2pot.cpp @@ -0,0 +1,77 @@ +#include "parser.h" +#include +#include +#include +#include + +using namespace std; + +int main( int argc, char **argv ) +{ + if (argc != 2) { + qWarning("usage: %s english-XML", argv[0]); + exit(1); + } + + MsgList english = parseXML(argv[1]); + + QMap msgids; + int index = 0; + + for (MsgList::Iterator it = english.begin(); + it != english.end(); ) + { + if (msgids.contains((*it).msgid)) { + english[msgids[(*it).msgid]].lines += (*it).lines; + MsgList::Iterator tmp = it; + it++; + english.remove(tmp); + } else { + msgids.insert((*it).msgid, index); + index++; + it++; + } + } + + const QDateTime now = QDateTime::currentDateTime( Qt::UTC ); + + cout << "# SOME DESCRIPTIVE TITLE.\n"; + cout << "# FIRST AUTHOR , YEAR.\n"; + cout << "#\n"; + cout << "#, fuzzy\n"; + cout << "msgid \"\"\n"; + cout << "msgstr \"\"\n"; + cout << "\"Project-Id-Version: PACKAGE VERSION\\n\"\n"; + cout << "\"Report-Msgid-Bugs-To: http://bugs.kde.org\\n\"\n"; + cout << "\"POT-Creation-Date: " << now.toString("yyyy-MM-dd hh:mm").utf8().data() << "+0000\\n\"\n"; + cout << "\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n"; + cout << "\"Last-Translator: FULL NAME \\n\"\n"; + cout << "\"Language-Team: LANGUAGE \\n\"\n"; + cout << "\"MIME-Version: 1.0\\n\"\n"; + cout << "\"Content-Type: application/x-xml2pot; charset=UTF-8\\n\"\n"; + cout << "\"Content-Transfer-Encoding: 8bit\\n\"\n"; + cout << "\n"; + + const QString fname = QFileInfo(argv[1]).fileName(); + + for (MsgList::ConstIterator it = english.begin(); + it != english.end(); ++it) + { + cout << "#. Tag: " << (*it).tag.utf8() << endl; + cout << "#: "; + for (QValueList::ConstIterator it2 = + (*it).lines.begin(); it2 != (*it).lines.end(); it2++) { + if (it2 != (*it).lines.begin()) + cout << " "; + cout << fname.utf8().data() << ":" << (*it2).start_line; + + } + cout << "\n"; + cout << "#, no-c-format\n"; + outputMsg("msgid", StructureParser::descapeLiterals( (*it).msgid )); + outputMsg("msgstr", (*it).msgstr ); + cout << "\n"; + } + + return 0; +} diff --git a/scheck/Makefile.am b/scheck/Makefile.am new file mode 100644 index 00000000..3e05068a --- /dev/null +++ b/scheck/Makefile.am @@ -0,0 +1,33 @@ +# This file is part of the KDE libraries +# Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org) +# (C) 1997 Stephan Kulow (coolo@kde.org) + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this library; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +AM_CPPFLAGS = -DQT_PLUGIN + +INCLUDES = $(all_includes) +noinst_HEADERS = scheck.h bitmaps.h +kde_style_LTLIBRARIES = scheck.la +scheck_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +scheck_la_LIBADD = $(LIB_KDEUI) +scheck_la_SOURCES = scheck.cpp +scheck_la_METASOURCES = AUTO + +style_DATA = scheck.themerc +styledir = $(kde_datadir)/kstyle/themes + +EXTRA_DIST = $(style_DATA) diff --git a/scheck/README b/scheck/README new file mode 100644 index 00000000..0f8f8293 --- /dev/null +++ b/scheck/README @@ -0,0 +1,22 @@ +Scheck - An interface style to highlight accel and style guide conflicts + +When starting an application with "KDE_LANG=xx application --style check" +scheck will, for text parts it checks, strip the "xx"s and display the ones +missing them (missing i18n() calls/.desktop entries) with violet background. + +To use it: "program -style check". Yes, there should be a verbose README. + +In short: + +- Orange shows accel conflicts. +- Green proposed accels. +- Dotted red lines show nested groupboxes (not prohibited, but not favored :-). +- Potential style guide violations are marked with yellow, likely ones with red. +- Missing colons are drawn with two small red squares. +- Errors in window titles are marked with "foo|b|ar". +- Violet background show untranslated string. + +Note: Not everything is checked (like list/combo box choices) and +scheck is error-prone so read HIG[*] and think before you change anything. :-) + +*:http://developer.kde.org/documentation/standards/kde/style/basics/index.html diff --git a/scheck/bitmaps.h b/scheck/bitmaps.h new file mode 100644 index 00000000..43e96bd8 --- /dev/null +++ b/scheck/bitmaps.h @@ -0,0 +1,84 @@ +#ifndef __BITMAPS_H +#define __BITMAPS_H + +/* Image bits processed by KPixmap2Bitmaps */ +static const unsigned char radiooff_light_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x10, 0x00, 0x10, + 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x08, 0x00, 0x08, 0x0c, 0x06, + 0xf0, 0x01}; + +static const unsigned char radiooff_gray_bits[] = { + 0xf0, 0x01, 0x0c, 0x06, 0x02, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00}; + +static const unsigned char radiooff_dgray_bits[] = { + 0x00, 0x00, 0xf0, 0x01, 0x0c, 0x02, 0x04, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00}; + +static const unsigned char radiooff_center_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x01, 0xf8, 0x03, 0xfc, 0x07, 0xfc, 0x07, + 0xfc, 0x07, 0xfc, 0x07, 0xfc, 0x07, 0xf8, 0x03, 0xf0, 0x01, 0x00, 0x00, + 0x00, 0x00}; + +static const unsigned char radiomask_bits[] = { + 0xf0, 0x01, 0xfc, 0x07, 0xfe, 0x0b, 0xfe, 0x0b, 0xff, 0x17, 0xff, 0x17, + 0xff, 0x17, 0xff, 0x17, 0xff, 0x17, 0xfe, 0x0b, 0xf2, 0x09, 0x0c, 0x06, + 0xf0, 0x01}; + +// Checkbox "checked" bitmap +static const unsigned char x_bits[] = {0x63, 0x77, 0x3e, 0x1c, 0x3e, 0x77, 0x63}; + +// Arrow bitmaps +static const QCOORD u_arrow[]={-1,-3, 0,-3, -2,-2, 1,-2, -3,-1, 2,-1, -4,0, 3,0, -4,1, 3,1}; +static const QCOORD d_arrow[]={-4,-2, 3,-2, -4,-1, 3,-1, -3,0, 2,0, -2,1, 1,1, -1,2, 0,2}; +static const QCOORD l_arrow[]={-3,-1, -3,0, -2,-2, -2,1, -1,-3, -1,2, 0,-4, 0,3, 1,-4, 1,3}; +static const QCOORD r_arrow[]={-2,-4, -2,3, -1,-4, -1,3, 0,-3, 0,2, 1,-2, 1,1, 2,-1, 2,0}; + +namespace B3 { + const QCOORD u_arrow[]={ 0,-2, 0,-2, -1,-1, 1,-1, -2,0, 2,0, -3,1, 3,1 }; + const QCOORD d_arrow[]={ -3,-2, 3,-2, -2,-1, 2,-1, -1,0, 1,0, 0,1, 0,1 }; + const QCOORD l_arrow[]={ 1,-3, 1,-3, 0,-2, 1,-2, -1,-1, 1,-1, -2,0, 1,0, -1,1, 1,1, 0,2, 1,2, 1,3, 1,3 }; + const QCOORD r_arrow[]={ -2,-3, -2,-3, -2,-2, -1,-2, -2,-1, 0,-1, -2,0, 1,0, -2,1, 0,1, -2,2, -1,2, -2,3, -2,3 }; +} + +#define QCOORDARRLEN(x) sizeof(x)/(sizeof(QCOORD)*2) + + +// Fix Qt's wacky image positions +static const char * const hc_minimize_xpm[] = { +"12 12 2 1", +"# c #000000", +". c None", +"............", +"............", +"............", +"............", +"............", +"............", +"............", +"............", +"...######...", +"...######...", +"............", +"............"}; + +static const char * const hc_close_xpm[] = { +"12 12 2 1", +"# c #000000", +". c None", +"............", +"............", +"............", +"..##....##..", +"...##..##...", +"....####....", +".....##.....", +"....####....", +"...##..##...", +"..##....##..", +"............", +"............"}; + +#endif diff --git a/scheck/scheck.cpp b/scheck/scheck.cpp new file mode 100644 index 00000000..8915205c --- /dev/null +++ b/scheck/scheck.cpp @@ -0,0 +1,2762 @@ +/* + * $Id$ + * + * KDE3 Style Guide compliance check "Style", v0.0.1 + * Copyright (C) 2002 Maksim Orlovich + * (C) 2002 Ryan Cumming + * + * + * Based on the KDE3 HighColor Style (version 1.0): + * Copyright (C) 2001-2002 Karol Szwed + * (C) 2001-2002 Fredrik Hglund + * + * Drawing routines adapted from the KDE2 HCStyle, + * Copyright (C) 2000 Daniel M. Duley + * (C) 2000 Dirk Mueller + * (C) 2001 Martijn Klingens + * + * Portions of code are from the Qt GUI Toolkit, Copyright (C) 1992-2003 Trolltech AS. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "scheck.h" +#include "scheck.moc" +#include "bitmaps.h" + +// -- Style Plugin Interface ------------------------- +class StyleCheckStylePlugin : public QStylePlugin +{ + public: + StyleCheckStylePlugin() {} + ~StyleCheckStylePlugin() {} + + QStringList keys() const + { + return QStringList() << "Check"; + } + + QStyle* create( const QString& key ) + { + if ( key == "check" ) + return new StyleCheckStyle( ); + + + return 0; + } +}; + +KDE_Q_EXPORT_PLUGIN( StyleCheckStylePlugin ) +// --------------------------------------------------- + + +// ### Remove globals +static QBitmap lightBmp; +static QBitmap grayBmp; +static QBitmap dgrayBmp; +static QBitmap centerBmp; +static QBitmap maskBmp; +static QBitmap xBmp; + +static const int itemFrame = 1; +static const int itemHMargin = 3; +static const int itemVMargin = 0; +static const int arrowHMargin = 6; +static const int rightBorder = 12; + +// --------------------------------------------------------------------------- + +#include + +enum ColonMode +{ + ColonlessWidget = 0, + BuddiedWidget = 1, + BuddylessWidget = 2 +}; + +enum AccelMode +{ + NoAccels = 0, + HasAccels = 1 +}; + +enum TitleType +{ + ShortTitle = 0, + LongTitle = 1 +}; + +namespace +{ + + class StyleGuideViolation + { + private: + int m_position; + int m_severity; // 0 = error 1 = warning + public: + enum Severity + { + Error = 0, + Warning = 1, + AccelConflict = 2, + AccelSuggestion = 3, + Untranslated = 4 + }; + + StyleGuideViolation() {} + StyleGuideViolation(int _position, int _severity = Error): m_position(_position), m_severity(_severity) + {} + + operator int() const + { + return m_position; + } + + int position() const + { + return m_position; + } + + int severity() const + { + return m_severity; + } + }; + + class LowerCaseWords + { + private: + static QDict* m_words; + public: + static QDict* words() + { + if (!m_words) + { + m_words = new QDict; + // Prepositions under five letters, except from & under + m_words->insert( "for", (bool*)1); + m_words->insert( "in", (bool*)1); + m_words->insert( "with", (bool*)1); + m_words->insert( "to", (bool*)1); + m_words->insert( "of", (bool*)1); + m_words->insert( "on", (bool*)1); + m_words->insert( "at", (bool*)1); + m_words->insert( "by", (bool*)1); + m_words->insert( "into", (bool*)1); + m_words->insert( "per", (bool*)1); + m_words->insert( "vs", (bool*)1); + + // Conjunctions + m_words->insert( "and", (bool*)1); + m_words->insert( "or", (bool*)1); + m_words->insert( "nor", (bool*)1); + m_words->insert( "but", (bool*)1); + m_words->insert( "if", (bool*)1); + + // Articles + m_words->insert( "the", (bool*)1); + m_words->insert( "a", (bool*)1); + m_words->insert( "as", (bool*)1); + m_words->insert( "an", (bool*)1); + + // Misc + m_words->insert( "http", (bool*)1); + } + return m_words; + } + }; + + QDict* LowerCaseWords::m_words = 0; + + class ExplicitCaseWords + { + private: + static QDict* m_words; + public: + static QDict* words() + { + if (!m_words) + { + // Store the words like this: + // "lowercase", "CorrectCase" + m_words = new QDict(61); + // week day names + m_words->insert( "monday", "Monday"); + m_words->insert( "tuesday", "Tuesday"); + m_words->insert( "wednesday", "Wednesday"); + m_words->insert( "thursday", "Thursday"); + m_words->insert( "friday", "Friday"); + m_words->insert( "saturday", "Saturday"); + m_words->insert( "sunday", "Sunday"); + + // month names + m_words->insert( "january", "January"); + m_words->insert( "february", "February"); + m_words->insert( "march", "March"); + m_words->insert( "april", "April"); + m_words->insert( "may", "May"); + m_words->insert( "june", "June"); + m_words->insert( "july", "July"); + m_words->insert( "august", "August"); + m_words->insert( "september", "September"); + m_words->insert( "october", "October"); + m_words->insert( "november", "November"); + m_words->insert( "december", "December"); + + // displayed KDE names not matched by acronym algorithm + m_words->insert( "konqueror", "Konqueror"); + m_words->insert( "kicker", "Kicker"); + m_words->insert( "kopete", "Kopete"); + m_words->insert( "kate", "Kate"); + m_words->insert( "konsole", "Konsole"); + m_words->insert( "kontour", "Kontour"); + m_words->insert( "kiten", "Kiten"); + m_words->insert( "kooka", "Kooka"); + m_words->insert( "noatun", "Noatun"); + + // computer + m_words->insert( "ctrl", "Ctrl"); + m_words->insert( "java", "Java"); + m_words->insert( "javascript", "JavaScript"); + m_words->insert( "qt", "Qt"); + m_words->insert( "doxygen", "Doxygen"); + m_words->insert( "linux", "Linux"); + m_words->insert( "unix", "UNIX"); + m_words->insert( "internet", "Internet"); + m_words->insert( "web", "Web"); + m_words->insert( "motif", "Motif"); + m_words->insert( "x11", "X11"); + m_words->insert( "socks", "SOCKS"); + m_words->insert( "xing", "Xing"); + m_words->insert( "yamaha", "Yamaha"); + m_words->insert( "hz", "Hz"); + m_words->insert( "khz", "KHz"); + m_words->insert( "mhz", "MHz"); + m_words->insert( "macos", "MacOS"); + m_words->insert( "microsoft", "Microsoft"); + m_words->insert( "adobe", "Adobe"); + m_words->insert( "postscript", "PostScript"); + m_words->insert( "ghostscript", "Ghostscript"); + m_words->insert( "vcard", "vCard"); + m_words->insert( "midi", "MIDI"); + m_words->insert( "isdn", "ISDN"); + m_words->insert( "cd-rom", "CD-ROM"); + } + return m_words; + } + }; + + QDict* ExplicitCaseWords::m_words = 0; +} + +static bool xxMode; + +static QColor severityColor(int severity) +{ + if (severity == StyleGuideViolation::Error) + { + return Qt::red; + } + else if (severity == StyleGuideViolation::AccelConflict) + { + return QColor(255, 128, 0); + } + else if (severity == StyleGuideViolation::AccelSuggestion) + { + return Qt::green; + } + else if (severity == StyleGuideViolation::Untranslated) + { + return QColor(255, 0, 255); + } + else + { + return Qt::yellow; + } +} + +// Removes '&' style accelerators from text strings +static void removeAccelerators(QString &str) +{ + for (unsigned int p = 0; p < str.length(); p++) + { + if (str[p] == '&') + { + str = str.mid(0, p) + str.mid(p+1); + // Skip the next letter, as && mean a literal "&" + p++; + } + } +} + + +static void removeXX(QString &str) +{ + str.replace("xx",""); // simple but atm best working +} + +static QString removedXX(QString str) +{ + if (xxMode) + removeXX(str); + return str; +} + +static QString stripAccelViolations(QString str) +{ + int conflict_pos = str.find("(&&)"); + if (conflict_pos >= 0) + { + str = str.mid(0, conflict_pos) + str.mid(conflict_pos+4); + } + + int suggestion_pos = str.find("(!)"); + if (suggestion_pos >= 0) + { + str = str.mid(0, suggestion_pos) + str.mid(suggestion_pos+3); + } + + return str; +} + +// Must be passed a string with its accelerators removed +QString findAccelViolations(QString str, QValueVector &violations) +{ + int conflict_pos = str.find("(&)"); + + if (conflict_pos >= 0) + str = str.mid(0, conflict_pos) + str.mid(conflict_pos+3); + + int suggestion_pos = str.find("(!)"); + if (suggestion_pos >= 0) + { + str = str.mid(0, suggestion_pos) + str.mid(suggestion_pos+3); + violations.push_back(StyleGuideViolation(suggestion_pos, StyleGuideViolation::AccelSuggestion)); + + // Conditionally "relocate" the conflict + if (conflict_pos >= suggestion_pos) + { + conflict_pos -= 3; + } + } + + if (conflict_pos >= 0) + violations.push_back(StyleGuideViolation(conflict_pos, StyleGuideViolation::AccelConflict)); + + return str; +} + +QString findUntranslatedViolations(QString str, QValueVector &violations) +{ + if (str.find("xx")!=-1) + removeXX(str); + else { + for (unsigned int c=0; c checkSentenceStyle(QString str, ColonMode mode = ColonlessWidget, AccelMode accelMode = HasAccels) +{ + QValueVector violations; + bool afterWhiteSpace = true; + bool firstChar = true; + bool inQuote = false; + + if (xxMode) + str = findUntranslatedViolations(str, violations); + + if (accelMode == HasAccels) + { + // We care not for accelerators while parsing for capitialization + removeAccelerators(str); + str = findAccelViolations(str, violations); + } + + for (unsigned int c=0; cfind(word.lower()); + + // Actual captialization checking + if (correctWord) + { + // We have been told explictly how to capitalize this word + // This overides the next checks + for (unsigned int x = 0;x < word.length();x++) + { + if (word[x] != correctWord[x]) + { + violations.push_back(c + x); + } + } + } + else if (str[c].category() == QChar::Letter_Lowercase) //Lowercase character.. + { + if (firstChar) + violations.push_back(c); + } + else if (str[c].category() == QChar::Letter_Uppercase) + { + if (!firstChar) //A possible violation -- can be a proper name.. + { + //Check whether more capitalized words in here.. To guess acronyms. + bool acronym = false; + for (unsigned int d = c+1; d < str.length(); d++) + { + if (str[d].isSpace() ) + break; + if (str[d].category() == QChar::Letter_Uppercase) + { + acronym = true; + break; + } + } + if (!acronym) + violations.push_back(c); + } + } + } + firstChar = false; + afterWhiteSpace = false; + } + } + + bool endWithColon = false; + int colonIndex = -1; + + for (int c = str.length() - 1; c>=0; c--) + { + if (str[c] == ':') + { + endWithColon = true; + colonIndex = c; + break; + } + if (!str[c].isSpace()) + break; + } + + if ( mode == ColonlessWidget && endWithColon) //Sometimes checkbox is also a label.. So we'll make a colon a warning + { + violations.push_back(StyleGuideViolation(colonIndex,StyleGuideViolation::Warning)); + } + + if (mode == BuddiedWidget && !endWithColon) //We have a buddy but lack a colon --> wrong + { + violations.push_back(-1); + } + + if (mode == BuddylessWidget && endWithColon) //We have no buddy but we do have a colon -- potential error + { + violations.push_back(StyleGuideViolation(colonIndex,StyleGuideViolation::Warning)); + } + + return violations; +} + +static QValueVector checkTitleStyle(QString str, TitleType titleType = ShortTitle, AccelMode accelMode = NoAccels) +{ + QValueVector violations; + bool afterWhiteSpace = true; + + if (xxMode) + str = findUntranslatedViolations(str, violations); + + if (accelMode == HasAccels) + { + // We care not for accelerators while parsing for capitialization + removeAccelerators(str); + str = findAccelViolations(str, violations); + } + + for (unsigned int c=0; cfind(lower_word); + + if ((titleType == ShortTitle) && (lower_word=="and" || lower_word=="a" || lower_word=="an" || lower_word=="the")) + { + // This words are 'red flagged' for short titles + for (unsigned int i = 0;i < word.length();i++) + violations.push_back(StyleGuideViolation(c + i,StyleGuideViolation::Warning)); + } + + if (correctWord) + { + // We're an uppercase word, and may have an unusual + // capitalization (ie, JavaScript) + for (unsigned int x = 0;x < word.length();x++) + { + if (word[x] != correctWord[x]) + violations.push_back(c + x); + } + } + else if (c && !lastWord && LowerCaseWords::words()->find(word.lower())) + { + // We're a lowercase word + if (str[c].category() == QChar::Letter_Uppercase) + violations.push_back(c); + } + else + { + if (str[c].category() == QChar::Letter_Lowercase) + violations.push_back(c); + } + } + + afterWhiteSpace = false; + } + } + + return violations; +} + + +static void renderViolations(const QValueVector& violations, QPainter* p, QRect r, int flags, QString text) +{ + + if (xxMode) + removeXX(text); + + if (violations.size()>0) + { + p->save(); + QFontMetrics qfm = p->fontMetrics (); + + QString parStr = text; + int len = text.length(); + + /***** + Begin code snipped from QPainter, somewhat modified + */ + + + // str.setLength() always does a deep copy, so the replacement code below is safe. + parStr.setLength( len ); + // compatible behaviour to the old implementation. Replace tabs by spaces + QChar *chr = (QChar*)parStr.unicode(); + int l = len; + while ( l-- ) + { + if ( *chr == '\t' || *chr == '\r' || *chr == '\n' ) + *chr = ' '; + chr++; + } + + + if ( flags & Qt::ShowPrefix ) + { + parStr = removedXX(stripAccelViolations(parStr)); + removeAccelerators(parStr); + } + + int w = qfm.width( parStr ); + int h = qfm.height(); + + int xoff = r.x(); + int yoff = r.y() + qfm.ascent(); + + if ( flags & Qt::AlignBottom ) + yoff += r.height() - h; + else if ( flags & Qt::AlignVCenter ) + yoff += ( r.height() - h ) / 2; + if ( flags & Qt::AlignRight ) + xoff += r.width() - w; + else if ( flags & Qt::AlignHCenter ) + xoff += ( r.width() - w ) / 2; + + + + /***** + end code snipped from QPainter... + */ + + int yt = yoff - h; + int yb = yoff;; + + + QRect bnd(xoff, yoff - h, w, h); + + for (unsigned int v = 0; v < violations.size(); v++) + { + if (violations[v] != -1) + { + int left = bnd.left() + + qfm.width(parStr, violations[v]) - 1; + + + int right = bnd.left() + + qfm.width(parStr, violations[v] + 1) - 1; + + + //int right = r.x() + qfm.width(text, violations[v]+1); + //int left = r.x() + qfm.width(text, violations[v]); + + p->fillRect( left, yt, right - left + 1, yb - yt + 1, severityColor(violations[v].severity()) ); + } + else + { + int right = bnd.right(); + + int centerX = right - 1; + int leftX = centerX-h/4; + int rightX = centerX + h/4; + p->setPen(severityColor(violations[v].severity())); + p->drawLine ( leftX, yt + 1, rightX, yt + 1 ); + p->drawLine ( leftX, yt + h/2, rightX, yt + h/2 + 1); + p->drawLine ( leftX, yt+ 1, leftX, yt + h/2 + 1); + p->drawLine ( rightX, yt+ 1, rightX, yt + h/2 + 1); + + p->drawLine ( leftX, yb - h/2, rightX, yb - h/2); + p->drawLine ( leftX, yb, rightX, yb); + p->drawLine ( leftX, yb - h/2, leftX, yb); + p->drawLine ( rightX, yb - h/2, rightX, yb); + } + } + p->restore(); + } +} + +StyleCheckTitleWatcher::StyleCheckTitleWatcher() +{ + QTimer* checkTimer = new QTimer(this); + connect( checkTimer, SIGNAL(timeout()), this, SLOT(slotCheck()) ); + checkTimer->start(1000); +} + + +void StyleCheckTitleWatcher::addWatched(QWidget* w) +{ + watched.push_back(w); + watchedTitles.push_back(w->caption()); +} + +QString StyleCheckTitleWatcher::cleanErrorMarkers(QString in) +{ + //We add # to denote an error...So now remove it.. It helps us check whether it's the same caption as before.. + QString out = ""; + for (unsigned int c = 0; c < in.length(); c++) + { + if (in[c] != '|') + out += in[c]; + } + + return out; +} + +void StyleCheckTitleWatcher::slotCheck() +{ + for (unsigned int c=0; ccaption()); + if (cleaned != watchedTitles[c]) + { + watchedTitles[c] = watched[c]->caption(); + QValueVector violations = checkTitleStyle(watched[c]->caption(), LongTitle, NoAccels); + if (violations.size() == 0) + continue; + + QString out = ""; + QString in = watched[c]->caption(); + int prev = -1; + for (unsigned int v = 0; v < violations.size(); v++) + { + out += in.mid(prev + 1, violations[v] - prev - 1); //Add interval that followed last one.. + out += '|'; + out += in[violations[v]]; + out += '|'; + prev = violations[v]; + } + + out += in.mid(prev + 1); //Add the tail.. + + watched[c]->setCaption(out); + } //If changed. + } //If not null.. + } //for all watched +} + + +StyleCheckStyle::StyleCheckStyle( ) + : KStyle( 0 , ThreeButtonScrollBar ) +{ + hoverWidget = 0L; + topLevelAccelManageTimer = new QTimer(this); + connect(topLevelAccelManageTimer, SIGNAL(timeout()), this, SLOT(slotAccelManage())); + watcher = new StyleCheckTitleWatcher; + xxMode = (QString(getenv("KDE_LANG"))=="xx"); +} + + +StyleCheckStyle::~StyleCheckStyle() +{ + delete watcher; +} + +//We walk down the widget tree until we find something we render, and sic KAccelManager in programmer's mode on those +void StyleCheckStyle::accelManageRecursive(QWidget* widget) +{ + if (&widget->style() == this) + { + KAcceleratorManager::manage(widget, true); + return; + } + + const QObjectList* children = widget->children(); + if (!children) + return; + QObjectListIterator iter(*children); + + QObject* walk; + while ((walk = iter.current())) + { + if (walk->isWidgetType()) + accelManageRecursive(static_cast(walk)); + ++iter; + } +} + + +void StyleCheckStyle::slotAccelManage() +{ + //Walk through top-levels + QWidgetList* topLevels = QApplication::topLevelWidgets(); + if (!topLevels) + return; + + QWidgetListIt iter(*topLevels); + + QWidget* walk; + while ((walk = iter.current())) + { + accelManageRecursive(walk); + ++iter; + } + +} + + +void StyleCheckStyle::polish(QWidget* widget) +{ + /* Having a global view on the widget makes accel + easier to catch. However, just intruding on the main window + is wrong since a style can be used for a subwindow. The upshot is that we defer + accel management to a timer, until things stabilize, and then walk from top-levels down + */ + topLevelAccelManageTimer->start(200, true); + // + + // Put in order of highest occurance to maximise hit rate + if (widget->inherits("QPushButton")) { + widget->installEventFilter(this); + } + + if (widget->inherits("QLabel")) + { + widget->installEventFilter(this); + } + + if (widget->inherits("QGroupBox")) + { + widget->installEventFilter(this); + } + + if (widget->inherits("QMainWindow") || widget->inherits("QDialog" ) ) + { + watcher->addWatched(widget); + } + + KStyle::polish( widget ); +} + + +void StyleCheckStyle::unPolish(QWidget* widget) +{ + if (widget->inherits("QPushButton")) { + widget->removeEventFilter(this); + } + + if (widget->inherits("QLabel")) + { + widget->removeEventFilter(this); + } + + if (widget->inherits("QGroupBox")) + { + widget->removeEventFilter(this); + } + + + + KStyle::unPolish( widget ); +} + + + +// This function draws primitive elements as well as their masks. +void StyleCheckStyle::drawPrimitive( PrimitiveElement pe, + QPainter *p, + const QRect &r, + const QColorGroup &cg, + SFlags flags, + const QStyleOption& opt ) const +{ + bool down = flags & Style_Down; + bool on = flags & Style_On; + + switch(pe) + { + // BUTTONS + // ------------------------------------------------------------------- + case PE_ButtonDefault: { + int x1, y1, x2, y2; + r.coords( &x1, &y1, &x2, &y2 ); + + // Button default indicator + p->setPen( cg.shadow() ); + p->drawLine( x1+1, y1, x2-1, y1 ); + p->drawLine( x1, y1+1, x1, y2-1 ); + p->drawLine( x1+1, y2, x2-1, y2 ); + p->drawLine( x2, y1+1, x2, y2-1 ); + break; + } + + case PE_ButtonDropDown: + case PE_ButtonTool: { + bool sunken = on || down; + int x,y,w,h; + r.rect(&x, &y, &w, &h); + int x2 = x+w-1; + int y2 = y+h-1; + QPen oldPen = p->pen(); + + // Outer frame (round style) + p->setPen(cg.shadow()); + p->drawLine(x+1,y,x2-1,y); + p->drawLine(x,y+1,x,y2-1); + p->drawLine(x+1,y2,x2-1,y2); + p->drawLine(x2,y+1,x2,y2-1); + + // Bevel + p->setPen(sunken ? cg.mid() : cg.light()); + p->drawLine(x+1, y+1, x2-1, y+1); + p->drawLine(x+1, y+1, x+1, y2-1); + p->setPen(sunken ? cg.light() : cg.mid()); + p->drawLine(x+2, y2-1, x2-1, y2-1); + p->drawLine(x2-1, y+2, x2-1, y2-1); + + p->fillRect(x+2, y+2, w-4, h-4, cg.button()); + + p->setPen( oldPen ); + break; + } + + // PUSH BUTTON + // ------------------------------------------------------------------- + case PE_ButtonCommand: { + bool sunken = on || down; + int x, y, w, h; + r.rect(&x, &y, &w, &h); + + if ( sunken ) + kDrawBeButton( p, x, y, w, h, cg, true, + &cg.brush(QColorGroup::Mid) ); + + else if ( flags & Style_MouseOver ) { + QBrush brush(cg.button().light(110)); + kDrawBeButton( p, x, y, w, h, cg, false, &brush ); + } + + // "Flat" button + else if (!(flags & (Style_Raised | Style_Sunken))) + p->fillRect(r, cg.button()); + + else + kDrawBeButton(p, x, y, w, h, cg, false, + &cg.brush(QColorGroup::Button)); + break; + } + + + // BEVELS + // ------------------------------------------------------------------- + case PE_ButtonBevel: { + int x,y,w,h; + r.rect(&x, &y, &w, &h); + bool sunken = on || down; + int x2 = x+w-1; + int y2 = y+h-1; + + // Outer frame + p->setPen(cg.shadow()); + p->drawRect(r); + + // Bevel + p->setPen(sunken ? cg.mid() : cg.light()); + p->drawLine(x+1, y+1, x2-1, y+1); + p->drawLine(x+1, y+1, x+1, y2-1); + p->setPen(sunken ? cg.light() : cg.mid()); + p->drawLine(x+2, y2-1, x2-1, y2-1); + p->drawLine(x2-1, y+2, x2-1, y2-1); + + if (w > 4 && h > 4) { + if (sunken) + p->fillRect(x+2, y+2, w-4, h-4, cg.button()); + else + renderGradient( p, QRect(x+2, y+2, w-4, h-4), + cg.button(), flags & Style_Horizontal ); + } + break; + } + + + // FOCUS RECT + // ------------------------------------------------------------------- + case PE_FocusRect: { + p->drawWinFocusRect( r ); + break; + } + + + // HEADER SECTION + // ------------------------------------------------------------------- + case PE_HeaderSection: { + // Temporary solution for the proper orientation of gradients. + bool horizontal = true; + if (p && p->device()->devType() == QInternal::Widget) { + QHeader* hdr = dynamic_cast(p->device()); + if (hdr) + horizontal = hdr->orientation() == Horizontal; + } + + int x,y,w,h; + r.rect(&x, &y, &w, &h); + bool sunken = on || down; + int x2 = x+w-1; + int y2 = y+h-1; + QPen oldPen = p->pen(); + + // Bevel + p->setPen(sunken ? cg.mid() : cg.light()); + p->drawLine(x, y, x2-1, y); + p->drawLine(x, y, x, y2-1); + p->setPen(sunken ? cg.light() : cg.mid()); + p->drawLine(x+1, y2-1, x2-1, y2-1); + p->drawLine(x2-1, y+1, x2-1, y2-1); + p->setPen(cg.shadow()); + p->drawLine(x, y2, x2, y2); + p->drawLine(x2, y, x2, y2); + + if (sunken) + p->fillRect(x+1, y+1, w-3, h-3, cg.button()); + else + renderGradient( p, QRect(x+1, y+1, w-3, h-3), + cg.button(), !horizontal ); + p->setPen( oldPen ); + break; + } + + + // SCROLLBAR + // ------------------------------------------------------------------- + case PE_ScrollBarSlider: { + // Small hack to ensure scrollbar gradients are drawn the right way. + flags ^= Style_Horizontal; + + drawPrimitive(PE_ButtonBevel, p, r, cg, flags | Style_Enabled | Style_Raised); + + // Draw a scrollbar riffle (note direction after above changes) + // HighColor & Default scrollbar + if (flags & Style_Horizontal) { + if (r.height() >= 15) { + int x = r.x()+3; + int y = r.y() + (r.height()-7)/2; + int x2 = r.right()-3; + p->setPen(cg.light()); + p->drawLine(x, y, x2, y); + p->drawLine(x, y+3, x2, y+3); + p->drawLine(x, y+6, x2, y+6); + + p->setPen(cg.mid()); + p->drawLine(x, y+1, x2, y+1); + p->drawLine(x, y+4, x2, y+4); + p->drawLine(x, y+7, x2, y+7); + } + } else { + if (r.width() >= 15) { + int y = r.y()+3; + int x = r.x() + (r.width()-7)/2; + int y2 = r.bottom()-3; + p->setPen(cg.light()); + p->drawLine(x, y, x, y2); + p->drawLine(x+3, y, x+3, y2); + p->drawLine(x+6, y, x+6, y2); + + p->setPen(cg.mid()); + p->drawLine(x+1, y, x+1, y2); + p->drawLine(x+4, y, x+4, y2); + p->drawLine(x+7, y, x+7, y2); + } + } + break; + } + + + case PE_ScrollBarAddPage: + case PE_ScrollBarSubPage: { + int x, y, w, h; + r.rect(&x, &y, &w, &h); + int x2 = x+w-1; + int y2 = y+h-1; + + p->setPen(cg.shadow()); + + if (flags & Style_Horizontal) { + p->drawLine(x, y, x2, y); + p->drawLine(x, y2, x2, y2); + renderGradient(p, QRect(x, y+1, w, h-2), + cg.mid(), false); + } else { + p->drawLine(x, y, x, y2); + p->drawLine(x2, y, x2, y2); + renderGradient(p, QRect(x+1, y, w-2, h), + cg.mid(), true); + } + break; + } + + + case PE_ScrollBarAddLine: { + drawPrimitive( PE_ButtonBevel, p, r, cg, (flags & Style_Enabled) | + ((flags & Style_Down) ? Style_Down : Style_Raised) ); + + drawPrimitive( ((flags & Style_Horizontal) ? PE_ArrowRight : PE_ArrowDown), + p, r, cg, flags ); + break; + } + + + case PE_ScrollBarSubLine: { + drawPrimitive( PE_ButtonBevel, p, r, cg, (flags & Style_Enabled) | + ((flags & Style_Down) ? Style_Down : Style_Raised) ); + + drawPrimitive( ((flags & Style_Horizontal) ? PE_ArrowLeft : PE_ArrowUp), + p, r, cg, flags ); + break; + } + + + // CHECKBOX (indicator) + // ------------------------------------------------------------------- + case PE_Indicator: { + + bool enabled = flags & Style_Enabled; + bool nochange = flags & Style_NoChange; + + if (xBmp.isNull()) { + xBmp = QBitmap(7, 7, x_bits, true); + xBmp.setMask(xBmp); + } + + int x,y,w,h; + x=r.x(); y=r.y(); w=r.width(); h=r.height(); + int x2 = x+w-1; + int y2 = y+h-1; + + p->setPen(cg.mid()); + p->drawLine(x, y, x2, y); + p->drawLine(x, y, x, y2); + + p->setPen(cg.light()); + p->drawLine(x2, y+1, x2, y2); + p->drawLine(x+1, y2, x2, y2); + + p->setPen(cg.shadow()); + p->drawLine(x+1, y+1, x2-1, y+1); + p->drawLine(x+1, y+1, x+1, y2-1); + + p->setPen(cg.midlight()); + p->drawLine(x2-1, y+2, x2-1, y2-1); + p->drawLine(x+2, y2-1, x2-1, y2-1); + + if ( enabled ) + p->fillRect(x+2, y+2, w-4, h-4, + down ? cg.button(): cg.base()); + else + p->fillRect(x+2, y+2, w-4, h-4, cg.background()); + + if (!(flags & Style_Off)) { + if (on) { + p->setPen(nochange ? cg.dark() : cg.text()); + p->drawPixmap(x+3, y+3, xBmp); + } + else { + p->setPen(cg.shadow()); + p->drawRect(x+2, y+2, w-4, h-4); + p->setPen(nochange ? cg.text() : cg.dark()); + p->drawLine(x+3, (y+h)/2-2, x+w-4, (y+h)/2-2); + p->drawLine(x+3, (y+h)/2, x+w-4, (y+h)/2); + p->drawLine(x+3, (y+h)/2+2, x+w-4, (y+h)/2+2); + } + } + break; + } + + + // RADIOBUTTON (exclusive indicator) + // ------------------------------------------------------------------- + case PE_ExclusiveIndicator: { + + if (lightBmp.isNull()) { + lightBmp = QBitmap(13, 13, radiooff_light_bits, true); + grayBmp = QBitmap(13, 13, radiooff_gray_bits, true); + dgrayBmp = QBitmap(13, 13, radiooff_dgray_bits, true); + centerBmp = QBitmap(13, 13, radiooff_center_bits, true); + centerBmp.setMask( centerBmp ); + } + + // Bevel + kColorBitmaps(p, cg, r.x(), r.y(), &lightBmp , &grayBmp, + NULL, &dgrayBmp); + + // The center fill of the indicator (grayed out when disabled) + if ( flags & Style_Enabled ) + p->setPen( down ? cg.button() : cg.base() ); + else + p->setPen( cg.background() ); + p->drawPixmap( r.x(), r.y(), centerBmp ); + + // Indicator "dot" + if ( on ) { + QColor color = flags & Style_NoChange ? + cg.dark() : cg.text(); + + p->setPen(color); + p->drawLine(5, 4, 7, 4); + p->drawLine(4, 5, 4, 7); + p->drawLine(5, 8, 7, 8); + p->drawLine(8, 5, 8, 7); + p->fillRect(5, 5, 3, 3, color); + } + + break; + } + + + // RADIOBUTTON (exclusive indicator) mask + // ------------------------------------------------------------------- + case PE_ExclusiveIndicatorMask: { + if (maskBmp.isNull()) { + maskBmp = QBitmap(13, 13, radiomask_bits, true); + maskBmp.setMask(maskBmp); + } + + p->setPen(Qt::color1); + p->drawPixmap(r.x(), r.y(), maskBmp); + break; + } + + + // SPLITTER/DOCKWINDOW HANDLES + // ------------------------------------------------------------------- + case PE_DockWindowResizeHandle: + case PE_Splitter: { + int x,y,w,h; + r.rect(&x, &y, &w, &h); + int x2 = x+w-1; + int y2 = y+h-1; + + p->setPen(cg.dark()); + p->drawRect(x, y, w, h); + p->setPen(cg.background()); + p->drawPoint(x, y); + p->drawPoint(x2, y); + p->drawPoint(x, y2); + p->drawPoint(x2, y2); + p->setPen(cg.light()); + p->drawLine(x+1, y+1, x+1, y2-1); + p->drawLine(x+1, y+1, x2-1, y+1); + p->setPen(cg.midlight()); + p->drawLine(x+2, y+2, x+2, y2-2); + p->drawLine(x+2, y+2, x2-2, y+2); + p->setPen(cg.mid()); + p->drawLine(x2-1, y+1, x2-1, y2-1); + p->drawLine(x+1, y2-1, x2-1, y2-1); + p->fillRect(x+3, y+3, w-5, h-5, cg.brush(QColorGroup::Background)); + break; + } + + + // GENERAL PANELS + // ------------------------------------------------------------------- + case PE_Panel: + case PE_PanelPopup: + case PE_WindowFrame: + case PE_PanelLineEdit: { + bool sunken = flags & Style_Sunken; + int lw = opt.isDefault() ? pixelMetric(PM_DefaultFrameWidth) + : opt.lineWidth(); + if (lw == 2) + { + QPen oldPen = p->pen(); + int x,y,w,h; + r.rect(&x, &y, &w, &h); + int x2 = x+w-1; + int y2 = y+h-1; + p->setPen(sunken ? cg.light() : cg.dark()); + p->drawLine(x, y2, x2, y2); + p->drawLine(x2, y, x2, y2); + p->setPen(sunken ? cg.mid() : cg.light()); + p->drawLine(x, y, x2, y); + p->drawLine(x, y, x, y2); + p->setPen(sunken ? cg.midlight() : cg.mid()); + p->drawLine(x+1, y2-1, x2-1, y2-1); + p->drawLine(x2-1, y+1, x2-1, y2-1); + p->setPen(sunken ? cg.dark() : cg.midlight()); + p->drawLine(x+1, y+1, x2-1, y+1); + p->drawLine(x+1, y+1, x+1, y2-1); + p->setPen(oldPen); + } else + KStyle::drawPrimitive(pe, p, r, cg, flags, opt); + + break; + } + + + // MENU / TOOLBAR PANEL + // ------------------------------------------------------------------- + case PE_PanelMenuBar: // Menu + case PE_PanelDockWindow: { // Toolbar + int x2 = r.x()+r.width()-1; + int y2 = r.y()+r.height()-1; + + if (opt.lineWidth()) + { + p->setPen(cg.light()); + p->drawLine(r.x(), r.y(), x2-1, r.y()); + p->drawLine(r.x(), r.y(), r.x(), y2-1); + p->setPen(cg.dark()); + p->drawLine(r.x(), y2, x2, y2); + p->drawLine(x2, r.y(), x2, y2); + + // ### Qt should specify Style_Horizontal where appropriate + renderGradient( p, QRect(r.x()+1, r.y()+1, x2-1, y2-1), + cg.button(), (r.width() < r.height()) && + (pe != PE_PanelMenuBar) ); + } + else + { + renderGradient( p, QRect(r.x(), r.y(), x2, y2), + cg.button(), (r.width() < r.height()) && + (pe != PE_PanelMenuBar) ); + } + + break; + } + + + + // TOOLBAR SEPARATOR + // ------------------------------------------------------------------- + case PE_DockWindowSeparator: { + renderGradient( p, r, cg.button(), + !(flags & Style_Horizontal)); + if ( !(flags & Style_Horizontal) ) { + p->setPen(cg.mid()); + p->drawLine(4, r.height()/2, r.width()-5, r.height()/2); + p->setPen(cg.light()); + p->drawLine(4, r.height()/2+1, r.width()-5, r.height()/2+1); + } else { + p->setPen(cg.mid()); + p->drawLine(r.width()/2, 4, r.width()/2, r.height()-5); + p->setPen(cg.light()); + p->drawLine(r.width()/2+1, 4, r.width()/2+1, r.height()-5); + } + break; + } + + + default: + { + // ARROWS + // ------------------------------------------------------------------- + if (pe >= PE_ArrowUp && pe <= PE_ArrowLeft) + { + QPointArray a; + + // HighColor & Default arrows + switch(pe) { + case PE_ArrowUp: + a.setPoints(QCOORDARRLEN(u_arrow), u_arrow); + break; + + case PE_ArrowDown: + a.setPoints(QCOORDARRLEN(d_arrow), d_arrow); + break; + + case PE_ArrowLeft: + a.setPoints(QCOORDARRLEN(l_arrow), l_arrow); + break; + + default: + a.setPoints(QCOORDARRLEN(r_arrow), r_arrow); + } + + p->save(); + if ( flags & Style_Down ) + p->translate( pixelMetric( PM_ButtonShiftHorizontal ), + pixelMetric( PM_ButtonShiftVertical ) ); + + if ( flags & Style_Enabled ) { + a.translate( r.x() + r.width() / 2, r.y() + r.height() / 2 ); + p->setPen( cg.buttonText() ); + p->drawLineSegments( a ); + } else { + a.translate( r.x() + r.width() / 2 + 1, r.y() + r.height() / 2 + 1 ); + p->setPen( cg.light() ); + p->drawLineSegments( a ); + a.translate( -1, -1 ); + p->setPen( cg.mid() ); + p->drawLineSegments( a ); + } + p->restore(); + + } else + KStyle::drawPrimitive( pe, p, r, cg, flags, opt ); + } + } +} + + +void StyleCheckStyle::drawKStylePrimitive( KStylePrimitive kpe, + QPainter* p, + const QWidget* widget, + const QRect &r, + const QColorGroup &cg, + SFlags flags, + const QStyleOption &opt ) const +{ + switch ( kpe ) + { + // TOOLBAR HANDLE + // ------------------------------------------------------------------- + case KPE_ToolBarHandle: { + int x = r.x(); int y = r.y(); + int x2 = r.x() + r.width()-1; + int y2 = r.y() + r.height()-1; + + if (flags & Style_Horizontal) { + + renderGradient( p, r, cg.button(), false); + p->setPen(cg.light()); + p->drawLine(x+1, y+4, x+1, y2-4); + p->drawLine(x+3, y+4, x+3, y2-4); + p->drawLine(x+5, y+4, x+5, y2-4); + + p->setPen(cg.mid()); + p->drawLine(x+2, y+4, x+2, y2-4); + p->drawLine(x+4, y+4, x+4, y2-4); + p->drawLine(x+6, y+4, x+6, y2-4); + + } else { + + renderGradient( p, r, cg.button(), true); + p->setPen(cg.light()); + p->drawLine(x+4, y+1, x2-4, y+1); + p->drawLine(x+4, y+3, x2-4, y+3); + p->drawLine(x+4, y+5, x2-4, y+5); + + p->setPen(cg.mid()); + p->drawLine(x+4, y+2, x2-4, y+2); + p->drawLine(x+4, y+4, x2-4, y+4); + p->drawLine(x+4, y+6, x2-4, y+6); + + } + break; + } + + + // GENERAL/KICKER HANDLE + // ------------------------------------------------------------------- + case KPE_GeneralHandle: { + int x = r.x(); int y = r.y(); + int x2 = r.x() + r.width()-1; + int y2 = r.y() + r.height()-1; + + if (flags & Style_Horizontal) { + + p->setPen(cg.light()); + p->drawLine(x+1, y, x+1, y2); + p->drawLine(x+3, y, x+3, y2); + p->drawLine(x+5, y, x+5, y2); + + p->setPen(cg.mid()); + p->drawLine(x+2, y, x+2, y2); + p->drawLine(x+4, y, x+4, y2); + p->drawLine(x+6, y, x+6, y2); + + } else { + + p->setPen(cg.light()); + p->drawLine(x, y+1, x2, y+1); + p->drawLine(x, y+3, x2, y+3); + p->drawLine(x, y+5, x2, y+5); + + p->setPen(cg.mid()); + p->drawLine(x, y+2, x2, y+2); + p->drawLine(x, y+4, x2, y+4); + p->drawLine(x, y+6, x2, y+6); + + } + break; + } + + + // SLIDER GROOVE + // ------------------------------------------------------------------- + case KPE_SliderGroove: { + const QSlider* slider = (const QSlider*)widget; + bool horizontal = slider->orientation() == Horizontal; + int gcenter = (horizontal ? r.height() : r.width()) / 2; + + QRect gr; + if (horizontal) + gr = QRect(r.x(), r.y()+gcenter-3, r.width(), 7); + else + gr = QRect(r.x()+gcenter-3, r.y(), 7, r.height()); + + int x,y,w,h; + gr.rect(&x, &y, &w, &h); + int x2=x+w-1; + int y2=y+h-1; + + // Draw the slider groove. + p->setPen(cg.dark()); + p->drawLine(x+2, y, x2-2, y); + p->drawLine(x, y+2, x, y2-2); + p->fillRect(x+2,y+2,w-4, h-4, + slider->isEnabled() ? cg.dark() : cg.mid()); + p->setPen(cg.shadow()); + p->drawRect(x+1, y+1, w-2, h-2); + p->setPen(cg.light()); + p->drawPoint(x+1,y2-1); + p->drawPoint(x2-1,y2-1); + p->drawLine(x2, y+2, x2, y2-2); + p->drawLine(x+2, y2, x2-2, y2); + break; + } + + // SLIDER HANDLE + // ------------------------------------------------------------------- + case KPE_SliderHandle: { + const QSlider* slider = (const QSlider*)widget; + bool horizontal = slider->orientation() == Horizontal; + int x,y,w,h; + r.rect(&x, &y, &w, &h); + int x2 = x+w-1; + int y2 = y+h-1; + + p->setPen(cg.mid()); + p->drawLine(x+1, y, x2-1, y); + p->drawLine(x, y+1, x, y2-1); + p->setPen(cg.shadow()); + p->drawLine(x+1, y2, x2-1, y2); + p->drawLine(x2, y+1, x2, y2-1); + + p->setPen(cg.light()); + p->drawLine(x+1, y+1, x2-1, y+1); + p->drawLine(x+1, y+1, x+1, y2-1); + p->setPen(cg.dark()); + p->drawLine(x+2, y2-1, x2-1, y2-1); + p->drawLine(x2-1, y+2, x2-1, y2-1); + p->setPen(cg.midlight()); + p->drawLine(x+2, y+2, x2-2, y+2); + p->drawLine(x+2, y+2, x+2, y2-2); + p->setPen(cg.mid()); + p->drawLine(x+3, y2-2, x2-2, y2-2); + p->drawLine(x2-2, y+3, x2-2, y2-2); + renderGradient(p, QRect(x+3, y+3, w-6, h-6), + cg.button(), !horizontal); + + // Paint riffles + if (horizontal) { + p->setPen(cg.light()); + p->drawLine(x+5, y+4, x+5, y2-4); + p->drawLine(x+8, y+4, x+8, y2-4); + p->drawLine(x+11,y+4, x+11, y2-4); + p->setPen(slider->isEnabled() ? cg.shadow(): cg.mid()); + p->drawLine(x+6, y+4, x+6, y2-4); + p->drawLine(x+9, y+4, x+9, y2-4); + p->drawLine(x+12,y+4, x+12, y2-4); + } else { + p->setPen(cg.light()); + p->drawLine(x+4, y+5, x2-4, y+5); + p->drawLine(x+4, y+8, x2-4, y+8); + p->drawLine(x+4, y+11, x2-4, y+11); + p->setPen(slider->isEnabled() ? cg.shadow() : cg.mid()); + p->drawLine(x+4, y+6, x2-4, y+6); + p->drawLine(x+4, y+9, x2-4, y+9); + p->drawLine(x+4, y+12, x2-4, y+12); + } + break; + } + + default: + KStyle::drawKStylePrimitive( kpe, p, widget, r, cg, flags, opt); + } +} + + +void StyleCheckStyle::drawControl( ControlElement element, + QPainter *p, + const QWidget *widget, + const QRect &r, + const QColorGroup &cg, + SFlags flags, + const QStyleOption& opt ) const +{ + switch (element) + { + // PUSHBUTTON + // ------------------------------------------------------------------- + case CE_PushButton: { + if ( widget == hoverWidget ) + flags |= Style_MouseOver; + + QPushButton *button = (QPushButton*) widget; + QRect br = r; + bool btnDefault = button->isDefault(); + + if ( btnDefault || button->autoDefault() ) { + // Compensate for default indicator + static int di = pixelMetric( PM_ButtonDefaultIndicator ); + br.addCoords( di, di, -di, -di ); + } + + if ( btnDefault ) + drawPrimitive( PE_ButtonDefault, p, r, cg, flags ); + + drawPrimitive( PE_ButtonCommand, p, br, cg, flags ); + + break; + } + + + // PUSHBUTTON LABEL + // ------------------------------------------------------------------- + case CE_PushButtonLabel: { + const QPushButton* button = (const QPushButton*)widget; + bool active = button->isOn() || button->isDown(); + int x, y, w, h; + r.rect( &x, &y, &w, &h ); + + // Shift button contents if pushed. + if ( active ) { + x += pixelMetric(PM_ButtonShiftHorizontal, widget); + y += pixelMetric(PM_ButtonShiftVertical, widget); + flags |= Style_Sunken; + } + + // Does the button have a popup menu? + if ( button->isMenuButton() ) { + int dx = pixelMetric( PM_MenuButtonIndicator, widget ); + drawPrimitive( PE_ArrowDown, p, QRect(x + w - dx - 2, y + 2, dx, h - 4), + cg, flags, opt ); + w -= dx; + } + + // Draw the icon if there is one + if ( button->iconSet() && !button->iconSet()->isNull() ) { + QIconSet::Mode mode = QIconSet::Disabled; + QIconSet::State state = QIconSet::Off; + + if (button->isEnabled()) + mode = button->hasFocus() ? QIconSet::Active : QIconSet::Normal; + if (button->isToggleButton() && button->isOn()) + state = QIconSet::On; + + QPixmap pixmap = button->iconSet()->pixmap( QIconSet::Small, mode, state ); + p->drawPixmap( x + 4, y + h / 2 - pixmap.height() / 2, pixmap ); + int pw = pixmap.width(); + x += pw + 4; + w -= pw + 4; + } + + QValueVector violations = checkTitleStyle(button->text(), ShortTitle, HasAccels); + renderViolations(violations, p, QRect(x,y,w,h), AlignCenter | ShowPrefix, button->text()); + + // Make the label indicate if the button is a default button or not + if ( active || button->isDefault() ) { + // Draw "fake" bold text - this enables the font metrics to remain + // the same as computed in QPushButton::sizeHint(), but gives + // a reasonable bold effect. + int i; + + // Text shadow + if (button->isEnabled()) // Don't draw double-shadow when disabled + for(i=0; i<2; i++) + drawItem( p, QRect(x+i+1, y+1, w, h), AlignCenter | ShowPrefix, + button->colorGroup(), button->isEnabled(), button->pixmap(), + removedXX(stripAccelViolations(button->text())), -1, + active ? &button->colorGroup().dark() : &button->colorGroup().mid() ); + + // Normal Text + for(i=0; i<2; i++) + drawItem( p, QRect(x+i, y, w, h), AlignCenter | ShowPrefix, + button->colorGroup(), button->isEnabled(), button->pixmap(), + removedXX(stripAccelViolations(button->text())), -1, + active ? &button->colorGroup().light() : &button->colorGroup().buttonText() ); + } else + drawItem( p, QRect(x, y, w, h), AlignCenter | ShowPrefix, button->colorGroup(), + button->isEnabled(), button->pixmap(), removedXX(stripAccelViolations(button->text())), -1, + active ? &button->colorGroup().light() : &button->colorGroup().buttonText() ); + + // Draw a focus rect if the button has focus + if ( flags & Style_HasFocus ) + drawPrimitive( PE_FocusRect, p, + QStyle::visualRect(subRect(SR_PushButtonFocusRect, widget), widget), + cg, flags ); + break; + } + + case CE_TabBarLabel: + { + if ( opt.isDefault() ) + break; + + const QTabBar * tb = (const QTabBar *) widget; + QTab * t = opt.tab(); + + QRect tr = r; + if ( t->identifier() == tb->currentTab() ) + tr.setBottom( tr.bottom() - + pixelMetric( QStyle::PM_DefaultFrameWidth, tb ) ); + + QValueVector violations = checkTitleStyle(t->text(), ShortTitle, HasAccels); + renderViolations(violations, p, r, AlignCenter |ShowPrefix, t->text()); + + drawItem( p, tr, AlignCenter | ShowPrefix, cg, + flags & Style_Enabled, 0, removedXX(stripAccelViolations(t->text())) ); + + if ( (flags & Style_HasFocus) && !t->text().isEmpty() ) + drawPrimitive( PE_FocusRect, p, r, cg ); + break; + } + + + case CE_CheckBoxLabel: + { + const QCheckBox* checkbox = static_cast(widget); + + int alignment = QApplication::reverseLayout() ? AlignRight : AlignLeft; + + QValueVector violations = checkSentenceStyle(checkbox->text()); + + renderViolations(violations, p, r, alignment | AlignVCenter | ShowPrefix, checkbox->text()); + + drawItem(p, r, alignment | AlignVCenter | ShowPrefix, cg, + flags & Style_Enabled, checkbox->pixmap(), removedXX(stripAccelViolations(checkbox->text()))); + + if (flags & Style_HasFocus) + { + QRect fr = visualRect(subRect(SR_CheckBoxFocusRect, widget), widget); + drawPrimitive(PE_FocusRect, p, fr, cg, flags); + } + break; + } + + case CE_RadioButtonLabel: + { + const QRadioButton* rb = static_cast(widget); + + int alignment = QApplication::reverseLayout() ? AlignRight : AlignLeft; + + QValueVector violations = checkSentenceStyle(rb->text()); + + renderViolations(violations, p, r,alignment | AlignVCenter | ShowPrefix, rb->text()); + + drawItem(p, r, alignment | AlignVCenter | ShowPrefix, cg, + flags & Style_Enabled, rb->pixmap(), removedXX(stripAccelViolations(rb->text()))); + + if (flags & Style_HasFocus) + { + QRect fr = visualRect(subRect(SR_CheckBoxFocusRect, widget), widget); + drawPrimitive(PE_FocusRect, p, fr, cg, flags); + } + break; + } + + + // MENUBAR ITEM (sunken panel on mouse over) + // ------------------------------------------------------------------- + case CE_MenuBarItem: + { + QMenuBar *mb = (QMenuBar*)widget; + QMenuItem *mi = opt.menuItem(); + QRect pr = mb->rect(); + + bool active = flags & Style_Active; + bool focused = flags & Style_HasFocus; + + if ( active && focused ) + qDrawShadePanel(p, r.x(), r.y(), r.width(), r.height(), + cg, true, 1, &cg.brush(QColorGroup::Midlight)); + else + renderGradient( p, r, cg.button(), false, + r.x(), r.y()-1, pr.width()-2, pr.height()-2); + + QValueVector violations = checkTitleStyle(mi->text(), ShortTitle, HasAccels); + renderViolations(violations, p, r, AlignCenter | AlignVCenter | ShowPrefix, mi->text()); + + drawItem( p, r, AlignCenter | AlignVCenter | ShowPrefix + | DontClip | SingleLine, cg, flags & Style_Enabled, + mi->pixmap(), removedXX(stripAccelViolations(mi->text())) ); + + break; + } + + + // POPUPMENU ITEM + // ------------------------------------------------------------------- + case CE_PopupMenuItem: { + const QPopupMenu *popupmenu = (const QPopupMenu *) widget; + + QMenuItem *mi = opt.menuItem(); + if ( !mi ) { + // Don't leave blank holes if we set NoBackground for the QPopupMenu. + // This only happens when the popupMenu spans more than one column. + if (! (widget->erasePixmap() && !widget->erasePixmap()->isNull()) ) + p->fillRect(r, cg.brush(QColorGroup::Button) ); + break; + } + + int tab = opt.tabWidth(); + int checkcol = opt.maxIconWidth(); + bool enabled = mi->isEnabled(); + bool checkable = popupmenu->isCheckable(); + bool active = flags & Style_Active; + bool etchtext = styleHint( SH_EtchDisabledText ); + bool reverse = QApplication::reverseLayout(); + int x, y, w, h; + r.rect( &x, &y, &w, &h ); + + if ( checkable ) + checkcol = QMAX( checkcol, 20 ); + + // Are we a menu item separator? + if ( mi->isSeparator() ) { + p->setPen( cg.dark() ); + p->drawLine( x, y, x+w, y ); + p->setPen( cg.light() ); + p->drawLine( x, y+1, x+w, y+1 ); + break; + } + + // Draw the menu item background + if ( active ) + qDrawShadePanel( p, x, y, w, h, cg, true, 1, + &cg.brush(QColorGroup::Midlight) ); + // Draw the transparency pixmap + else if ( widget->erasePixmap() && !widget->erasePixmap()->isNull() ) + p->drawPixmap( x, y, *widget->erasePixmap(), x, y, w, h ); + // Draw a solid background + else + p->fillRect( r, cg.button() ); + + // Do we have an icon? + if ( mi->iconSet() ) { + QIconSet::Mode mode; + QRect cr = visualRect( QRect(x, y, checkcol, h), r ); + + // Select the correct icon from the iconset + if ( active ) + mode = enabled ? QIconSet::Active : QIconSet::Disabled; + else + mode = enabled ? QIconSet::Normal : QIconSet::Disabled; + + // Do we have an icon and are checked at the same time? + // Then draw a "pressed" background behind the icon + if ( checkable && !active && mi->isChecked() ) + qDrawShadePanel( p, cr.x(), cr.y(), cr.width(), cr.height(), + cg, true, 1, &cg.brush(QColorGroup::Midlight) ); + // Draw the icon + QPixmap pixmap = mi->iconSet()->pixmap( QIconSet::Small, mode ); + QRect pmr( 0, 0, pixmap.width(), pixmap.height() ); + pmr.moveCenter( cr.center() ); + p->drawPixmap( pmr.topLeft(), pixmap ); + } + + // Are we checked? (This time without an icon) + else if ( checkable && mi->isChecked() ) { + int cx = reverse ? x+w - checkcol : x; + + // We only have to draw the background if the menu item is inactive - + // if it's active the "pressed" background is already drawn + if ( ! active ) + qDrawShadePanel( p, cx, y, checkcol, h, cg, true, 1, + &cg.brush(QColorGroup::Midlight) ); + + // Draw the checkmark + SFlags cflags = Style_Default; + cflags |= active ? Style_Enabled : Style_On; + + drawPrimitive( PE_CheckMark, p, QRect( cx + itemFrame, y + itemFrame, + checkcol - itemFrame*2, h - itemFrame*2), cg, cflags ); + } + + // Time to draw the menu item label... + int xm = itemFrame + checkcol + itemHMargin; // X position margin + + int xp = reverse ? // X position + x + tab + rightBorder + itemHMargin + itemFrame - 1 : + x + xm; + + int offset = reverse ? -1 : 1; // Shadow offset for etched text + + // Label width (minus the width of the accelerator portion) + int tw = w - xm - tab - arrowHMargin - itemHMargin * 3 - itemFrame + 1; + + // Set the color for enabled and disabled text + // (used for both active and inactive menu items) + p->setPen( enabled ? cg.buttonText() : cg.mid() ); + + // This color will be used instead of the above if the menu item + // is active and disabled at the same time. (etched text) + QColor discol = cg.mid(); + + // Does the menu item draw it's own label? + if ( mi->custom() ) { + int m = itemVMargin; + // Save the painter state in case the custom + // paint method changes it in some way + p->save(); + + // Draw etched text if we're inactive and the menu item is disabled + if ( etchtext && !enabled && !active ) { + p->setPen( cg.light() ); + mi->custom()->paint( p, cg, active, enabled, xp+offset, y+m+1, tw, h-2*m ); + p->setPen( discol ); + } + mi->custom()->paint( p, cg, active, enabled, xp, y+m, tw, h-2*m ); + p->restore(); + } + else { + QValueVector ourViolations; + + QString tmpStr = mi->text(); + removeAccelerators(tmpStr); + findAccelViolations(tmpStr, ourViolations); + + // The menu item doesn't draw it's own label + QString s = stripAccelViolations(mi->text()); + + // Does the menu item have a text label? + if ( !s.isNull() ) { + int t = s.find( '\t' ); + int m = itemVMargin; + int text_flags = AlignVCenter | ShowPrefix | DontClip | SingleLine; + text_flags |= reverse ? AlignRight : AlignLeft; + + // Does the menu item have a tabstop? (for the accelerator text) + if ( t >= 0 ) { + int tabx = reverse ? x + rightBorder + itemHMargin + itemFrame : + x + w - tab - rightBorder - itemHMargin - itemFrame; + + // Draw the right part of the label (accelerator text) + if ( etchtext && !enabled && !active ) { + // Draw etched text if we're inactive and the menu item is disabled + p->setPen( cg.light() ); + p->drawText( tabx+offset, y+m+1, tab, h-2*m, text_flags, removedXX(s.mid( t+1 )) ); + p->setPen( discol ); + } + p->drawText( tabx, y+m, tab, h-2*m, text_flags, removedXX(s.mid( t+1 )) ); + s = s.left( t ); + } + + QValueVector violations = checkTitleStyle(s, ShortTitle, HasAccels); + renderViolations(violations, p, QRect(xp, y+m, tw, h-2*m), text_flags, s); + renderViolations(ourViolations, p, QRect(xp, y+m, tw, h-2*m), text_flags, s); + + + // Draw the left part of the label (or the whole label + // if there's no accelerator) + if ( etchtext && !enabled && !active ) { + // Etched text again for inactive disabled menu items... + p->setPen( cg.light() ); + p->drawText( xp+offset, y+m+1, tw, h-2*m, text_flags, removedXX(s)/*, t*/ ); + p->setPen( discol ); + } + + p->drawText( xp, y+m, tw, h-2*m, text_flags, removedXX(s)/*, t*/ ); + + } + + // The menu item doesn't have a text label + // Check if it has a pixmap instead + else if ( mi->pixmap() ) { + QPixmap *pixmap = mi->pixmap(); + + // Draw the pixmap + if ( pixmap->depth() == 1 ) + p->setBackgroundMode( OpaqueMode ); + + int diffw = ( ( w - pixmap->width() ) / 2 ) + + ( ( w - pixmap->width() ) % 2 ); + p->drawPixmap( x+diffw, y+itemFrame, *pixmap ); + + if ( pixmap->depth() == 1 ) + p->setBackgroundMode( TransparentMode ); + } + } + + // Does the menu item have a submenu? + if ( mi->popup() ) { + PrimitiveElement arrow = reverse ? PE_ArrowLeft : PE_ArrowRight; + int dim = pixelMetric(PM_MenuButtonIndicator); + QRect vr = visualRect( QRect( x + w - arrowHMargin - 2*itemFrame - dim, + y + h / 2 - dim / 2, dim, dim), r ); + + // Draw an arrow at the far end of the menu item + if ( active ) { + if ( enabled ) + discol = cg.buttonText(); + + QColorGroup g2( discol, cg.highlight(), white, white, + enabled ? white : discol, discol, white ); + + drawPrimitive( arrow, p, vr, g2, Style_Enabled ); + } else + drawPrimitive( arrow, p, vr, cg, + enabled ? Style_Enabled : Style_Default ); + } + break; + } + + case CE_HeaderLabel: + { + //Most of code here shamelessly lifted from QCommonStyle. + QRect rect = r; + const QHeader* header = static_cast(widget); + int section = opt.headerSection(); + QIconSet* icon = header->iconSet( section ); + if ( icon ) + { + QPixmap pixmap = icon->pixmap( QIconSet::Small, + flags & Style_Enabled ? QIconSet::Normal : QIconSet::Disabled ); + int pixw = pixmap.width(); + int pixh = pixmap.height(); + // "pixh - 1" because of tricky integer division + QRect pixRect = rect; + pixRect.setY( rect.center().y() - (pixh - 1) / 2 ); + drawItem ( p, pixRect, AlignVCenter, cg, flags & Style_Enabled, + &pixmap, QString::null ); + rect.setLeft( rect.left() + pixw + 2 ); + } + + QString s = header->label( section ); + + QValueVector violations = checkTitleStyle(s, ShortTitle, NoAccels); + renderViolations(violations, p, rect, AlignVCenter, s); + + + drawItem ( p, rect, AlignVCenter, cg, flags & Style_Enabled, + 0, s, -1, &(cg.buttonText()) ); + + break; + } + + default: + KStyle::drawControl(element, p, widget, r, cg, flags, opt); + } +} + + +void StyleCheckStyle::drawControlMask( ControlElement element, + QPainter *p, + const QWidget *widget, + const QRect &r, + const QStyleOption& opt ) const +{ + switch (element) + { + // PUSHBUTTON MASK + // ---------------------------------------------------------------------- + case CE_PushButton: { + int x1, y1, x2, y2; + r.coords( &x1, &y1, &x2, &y2 ); + QCOORD corners[] = { x1,y1, x2,y1, x1,y2, x2,y2 }; + p->fillRect( r, color1 ); + p->setPen( color0 ); + p->drawPoints( QPointArray(4, corners) ); + break; + } + + default: + KStyle::drawControlMask(element, p, widget, r, opt); + } +} + + +void StyleCheckStyle::drawComplexControl( ComplexControl control, + QPainter *p, + const QWidget *widget, + const QRect &r, + const QColorGroup &cg, + SFlags flags, + SCFlags controls, + SCFlags active, + const QStyleOption& opt ) const +{ + switch(control) + { + // COMBOBOX + // ------------------------------------------------------------------- + case CC_ComboBox: { + + // Draw box and arrow + if ( controls & SC_ComboBoxArrow ) { + bool sunken = (active == SC_ComboBoxArrow); + + // Draw the combo + int x,y,w,h; + r.rect(&x, &y, &w, &h); + int x2 = x+w-1; + int y2 = y+h-1; + + p->setPen(cg.shadow()); + p->drawLine(x+1, y, x2-1, y); + p->drawLine(x+1, y2, x2-1, y2); + p->drawLine(x, y+1, x, y2-1); + p->drawLine(x2, y+1, x2, y2-1); + + // Ensure the edge notches are properly colored + p->setPen(cg.button()); + p->drawPoint(x,y); + p->drawPoint(x,y2); + p->drawPoint(x2,y); + p->drawPoint(x2,y2); + + renderGradient( p, QRect(x+2, y+2, w-4, h-4), + cg.button(), false); + + p->setPen(sunken ? cg.light() : cg.mid()); + p->drawLine(x2-1, y+2, x2-1, y2-1); + p->drawLine(x+1, y2-1, x2-1, y2-1); + + p->setPen(sunken ? cg.mid() : cg.light()); + p->drawLine(x+1, y+1, x2-1, y+1); + p->drawLine(x+1, y+2, x+1, y2-2); + + // Get the button bounding box + QRect ar = QStyle::visualRect( + querySubControlMetrics(CC_ComboBox, widget, SC_ComboBoxArrow), + widget ); + + // Are we enabled? + if ( widget->isEnabled() ) + flags |= Style_Enabled; + + // Are we "pushed" ? + if ( active & Style_Sunken ) + flags |= Style_Sunken; + + drawPrimitive(PE_ArrowDown, p, ar, cg, flags); + } + + // Draw an edit field if required + if ( controls & SC_ComboBoxEditField ) + { + const QComboBox * cb = (const QComboBox *) widget; + QRect re = QStyle::visualRect( + querySubControlMetrics( CC_ComboBox, widget, + SC_ComboBoxEditField), widget ); + + // Draw the indent + if (cb->editable()) { + p->setPen( cg.dark() ); + p->drawLine( re.x(), re.y()-1, re.x()+re.width(), re.y()-1 ); + p->drawLine( re.x()-1, re.y(), re.x()-1, re.y()+re.height() ); + } + + if ( cb->hasFocus() ) { + p->setPen( cg.highlightedText() ); + p->setBackgroundColor( cg.highlight() ); + } else { + p->setPen( cg.text() ); + p->setBackgroundColor( cg.button() ); + } + + if ( cb->hasFocus() && !cb->editable() ) { + // Draw the contents + p->fillRect( re.x(), re.y(), re.width(), re.height(), + cg.brush( QColorGroup::Highlight ) ); + + QRect re = QStyle::visualRect( + subRect(SR_ComboBoxFocusRect, cb), widget); + + drawPrimitive( PE_FocusRect, p, re, cg, + Style_FocusAtBorder, QStyleOption(cg.highlight())); + } + } + break; + } + + // TOOLBUTTON + // ------------------------------------------------------------------- + case CC_ToolButton: { + const QToolButton *toolbutton = (const QToolButton *) widget; + + QRect button, menuarea; + button = querySubControlMetrics(control, widget, SC_ToolButton, opt); + menuarea = querySubControlMetrics(control, widget, SC_ToolButtonMenu, opt); + + SFlags bflags = flags, + mflags = flags; + + if (active & SC_ToolButton) + bflags |= Style_Down; + if (active & SC_ToolButtonMenu) + mflags |= Style_Down; + + if (controls & SC_ToolButton) + { + // If we're pressed, on, or raised... + if (bflags & (Style_Down | Style_On | Style_Raised)) + drawPrimitive(PE_ButtonTool, p, button, cg, bflags, opt); + + // Check whether to draw a background pixmap + else if ( toolbutton->parentWidget() && + toolbutton->parentWidget()->backgroundPixmap() && + !toolbutton->parentWidget()->backgroundPixmap()->isNull() ) + { + QPixmap pixmap = *(toolbutton->parentWidget()->backgroundPixmap()); + p->drawTiledPixmap( r, pixmap, toolbutton->pos() ); + } + else if (widget->parent()) + { + if (widget->parent()->inherits("QToolBar")) + { + QToolBar* parent = (QToolBar*)widget->parent(); + QRect pr = parent->rect(); + + renderGradient( p, r, cg.button(), + parent->orientation() == Qt::Vertical, + r.x(), r.y(), pr.width()-2, pr.height()-2); + } + else if (widget->parent()->inherits("QToolBarExtensionWidget")) + { + QWidget* parent = (QWidget*)widget->parent(); + QToolBar* toolbar = (QToolBar*)parent->parent(); + QRect tr = toolbar->rect(); + + if ( toolbar->orientation() == Qt::Horizontal ) { + renderGradient( p, r, cg.button(), false, r.x(), r.y(), + r.width(), tr.height() ); + } else { + renderGradient( p, r, cg.button(), true, r.x(), r.y(), + tr.width(), r.height() ); + } + } + } + } + + // Draw a toolbutton menu indicator if required + if (controls & SC_ToolButtonMenu) + { + if (mflags & (Style_Down | Style_On | Style_Raised)) + drawPrimitive(PE_ButtonDropDown, p, menuarea, cg, mflags, opt); + drawPrimitive(PE_ArrowDown, p, menuarea, cg, mflags, opt); + } + + if (toolbutton->hasFocus() && !toolbutton->focusProxy()) { + QRect fr = toolbutton->rect(); + fr.addCoords(3, 3, -3, -3); + drawPrimitive(PE_FocusRect, p, fr, cg); + } + + break; + } + + + default: + KStyle::drawComplexControl(control, p, widget, + r, cg, flags, controls, active, opt); + break; + } +} + + +void StyleCheckStyle::drawComplexControlMask( ComplexControl control, + QPainter *p, + const QWidget *widget, + const QRect &r, + const QStyleOption& opt ) const +{ + switch (control) + { + // COMBOBOX & TOOLBUTTON MASKS + // ------------------------------------------------------------------- + case CC_ComboBox: + case CC_ToolButton: { + int x1, y1, x2, y2; + r.coords( &x1, &y1, &x2, &y2 ); + QCOORD corners[] = { x1,y1, x2,y1, x1,y2, x2,y2 }; + p->fillRect( r, color1 ); + p->setPen( color0 ); + p->drawPoints( QPointArray(4, corners) ); + break; + } + + default: + KStyle::drawComplexControlMask(control, p, widget, r, opt); + } +} + + +QRect StyleCheckStyle::subRect(SubRect r, const QWidget *widget) const +{ + // We want the focus rect for buttons to be adjusted from + // the Qt3 defaults to be similar to Qt 2's defaults. + // ------------------------------------------------------------------- + if (r == SR_PushButtonFocusRect ) { + const QPushButton* button = (const QPushButton*) widget; + QRect wrect(widget->rect()); + int dbw1 = 0, dbw2 = 0; + + if (button->isDefault() || button->autoDefault()) { + dbw1 = pixelMetric(PM_ButtonDefaultIndicator, widget); + dbw2 = dbw1 * 2; + } + + int dfw1 = pixelMetric(PM_DefaultFrameWidth, widget) * 2, + dfw2 = dfw1 * 2; + + return QRect(wrect.x() + dfw1 + dbw1 + 1, + wrect.y() + dfw1 + dbw1 + 1, + wrect.width() - dfw2 - dbw2 - 1, + wrect.height() - dfw2 - dbw2 - 1); + } else + return KStyle::subRect(r, widget); +} + + +int StyleCheckStyle::pixelMetric(PixelMetric m, const QWidget *widget) const +{ + switch(m) + { + // BUTTONS + // ------------------------------------------------------------------- + case PM_ButtonMargin: // Space btw. frame and label + return 4; + + case PM_ButtonDefaultIndicator: { + return 3; + } + + case PM_MenuButtonIndicator: { // Arrow width + return 8; + } + + // CHECKBOXES / RADIO BUTTONS + // ------------------------------------------------------------------- + case PM_ExclusiveIndicatorWidth: // Radiobutton size + case PM_ExclusiveIndicatorHeight: + case PM_IndicatorWidth: // Checkbox size + case PM_IndicatorHeight: { + return 13; // 13x13 + } + + default: + return KStyle::pixelMetric(m, widget); + } +} + + +QSize StyleCheckStyle::sizeFromContents( ContentsType contents, + const QWidget* widget, + const QSize &contentSize, + const QStyleOption& opt ) const +{ + switch (contents) + { + // PUSHBUTTON SIZE + // ------------------------------------------------------------------ + case CT_PushButton: { + const QPushButton* button = (const QPushButton*) widget; + int w = contentSize.width(); + int h = contentSize.height(); + int bm = pixelMetric( PM_ButtonMargin, widget ); + int fw = pixelMetric( PM_DefaultFrameWidth, widget ) * 2; + + w += bm + fw + 6; // ### Add 6 to make way for bold font. + h += bm + fw; + + // Ensure we stick to standard width and heights. + if ( button->isDefault() || button->autoDefault() ) { + if ( w < 80 && !button->pixmap() ) + w = 80; + + // Compensate for default indicator + int di = pixelMetric( PM_ButtonDefaultIndicator ); + w += di * 2; + h += di * 2; + } + + if ( h < 22 ) + h = 22; + + return QSize( w, h ); + } + + + // POPUPMENU ITEM SIZE + // ----------------------------------------------------------------- + case CT_PopupMenuItem: { + if ( ! widget || opt.isDefault() ) + return contentSize; + + const QPopupMenu *popup = (const QPopupMenu *) widget; + bool checkable = popup->isCheckable(); + QMenuItem *mi = opt.menuItem(); + int maxpmw = opt.maxIconWidth(); + int w = contentSize.width(), h = contentSize.height(); + + if ( mi->custom() ) { + w = mi->custom()->sizeHint().width(); + h = mi->custom()->sizeHint().height(); + if ( ! mi->custom()->fullSpan() ) + h += 2*itemVMargin + 2*itemFrame; + } + else if ( mi->widget() ) { + } else if ( mi->isSeparator() ) { + w = 10; // Arbitrary + h = 2; + } + else { + if ( mi->pixmap() ) + h = QMAX( h, mi->pixmap()->height() + 2*itemFrame ); + else { + // Ensure that the minimum height for text-only menu items + // is the same as the icon size used by KDE. + h = QMAX( h, 16 + 2*itemFrame ); + h = QMAX( h, popup->fontMetrics().height() + + 2*itemVMargin + 2*itemFrame ); + } + + if ( mi->iconSet() ) + h = QMAX( h, mi->iconSet()->pixmap( + QIconSet::Small, QIconSet::Normal).height() + + 2 * itemFrame ); + } + + if ( ! mi->text().isNull() && mi->text().find('\t') >= 0 ) + w += 12; + else if ( mi->popup() ) + w += 2 * arrowHMargin; + + if ( maxpmw ) + w += maxpmw + 6; + if ( checkable && maxpmw < 20 ) + w += 20 - maxpmw; + if ( checkable || maxpmw > 0 ) + w += 12; + + w += rightBorder; + + return QSize( w, h ); + } + + + default: + return KStyle::sizeFromContents( contents, widget, contentSize, opt ); + } +} + + +// Fix Qt's wacky image alignment +QPixmap StyleCheckStyle::stylePixmap(StylePixmap stylepixmap, + const QWidget* widget, + const QStyleOption& opt) const +{ + switch (stylepixmap) { + case SP_TitleBarMinButton: + return QPixmap((const char **)hc_minimize_xpm); + case SP_TitleBarCloseButton: + return QPixmap((const char **)hc_close_xpm); + default: + break; + } + + return KStyle::stylePixmap(stylepixmap, widget, opt); +} + + +bool StyleCheckStyle::eventFilter( QObject *object, QEvent *event ) +{ + if (KStyle::eventFilter( object, event )) + return true; + + + // Handle push button hover effects. + QPushButton* button = dynamic_cast(object); + if ( button ) + { + if ( (event->type() == QEvent::Enter) && + (button->isEnabled()) ) { + hoverWidget = button; + button->repaint( false ); + } + else if ( (event->type() == QEvent::Leave) && + (object == hoverWidget) ) { + hoverWidget = 0L; + button->repaint( false ); + } + } + + if ( event->type() == QEvent::Paint && object->inherits("QLabel") ) + { + QLabel* lb = static_cast(object); + if (lb->pixmap() || lb->picture() || lb->movie() || (lb->textFormat() == Qt::RichText) || + (lb->textFormat() == Qt::AutoText && QStyleSheet::mightBeRichText(lb->text())) ) + { + return false; + } + + QPainter p(lb); + + QRect cr = lb->contentsRect(); + + int m = lb->indent(); + if ( m < 0 && lb->frameWidth() ) // no indent, but we do have a frame + m = lb->fontMetrics().width('x') / 2 - lb->margin(); + if ( m > 0 ) + { + int hAlign = QApplication::horizontalAlignment( lb->alignment() ); + if ( hAlign & AlignLeft ) + cr.setLeft( cr.left() + m ); + if ( hAlign & AlignRight ) + cr.setRight( cr.right() - m ); + if ( lb->alignment() & AlignTop ) + cr.setTop( cr.top() + m ); + if ( lb->alignment() & AlignBottom ) + cr.setBottom( cr.bottom() - m ); + } + + QValueVector violations; + + if (QCString(lb->name()) == "KJanusWidgetTitleLabel" || lb->font().bold()) + { + // We're a page title + violations = checkTitleStyle(lb->text(), LongTitle, lb->buddy() ? HasAccels : NoAccels); + } + else + { + // We're probably, maybe, not a page title label + // Further checks might be needed, depending on how often this comes up in the wild + violations = checkSentenceStyle(lb->text(), lb->buddy() ? BuddiedWidget: BuddylessWidget, lb->buddy() ? HasAccels : NoAccels); + } + + if (lb->buddy()) + { + renderViolations(violations, &p, cr,lb->alignment() | ShowPrefix, lb->text() ); + // ordinary text or pixmap label + drawItem( &p, cr, lb->alignment(), lb->colorGroup(), lb->isEnabled(), + 0, removedXX(stripAccelViolations(lb->text())) ); + } + else + { + renderViolations(violations, &p, cr,lb->alignment(), lb->text() ); + + // ordinary text or pixmap label + drawItem( &p, cr, lb->alignment(), lb->colorGroup(), lb->isEnabled(), + 0, removedXX(stripAccelViolations(lb->text())) ); + } + + p.end(); + + return true; + } + + if ( event->type() == QEvent::Paint && object->inherits("QGroupBox") ) + { + QPaintEvent * pevent = static_cast(event); + QGroupBox* gb = static_cast(object); + bool nestedGroupBox = false; + QString stripped_title = removedXX(stripAccelViolations(gb->title())); + + //Walk parent hierarchy to check whether any are groupboxes too.. + QObject* parent = gb; + + // GCC suggested parentheses around assignment used as truth value + // I suggested that it could eat me. GCC won. + while ( (parent = parent->parent()) ) + { + if (parent->inherits("QGroupBox")) + { + nestedGroupBox = true; + break; + } + } + + QPainter paint( gb ); + if ( stripped_title.length() ) + { + // draw title + QFontMetrics fm = paint.fontMetrics(); + int h = fm.height(); + int tw = fm.width( stripped_title, stripped_title.length() ) + 2*fm.width(QChar(' ')); + int x; + if ( gb->alignment() & AlignHCenter ) // center alignment + x = gb->frameRect().width()/2 - tw/2; + else if ( gb->alignment() & AlignRight ) // right alignment + x = gb->frameRect().width() - tw - 8; + else if ( gb->alignment() & AlignLeft ) // left alignment + x = 8; + else + { // auto align + if( QApplication::reverseLayout() ) + x = gb->frameRect().width() - tw - 8; + else + x = 8; + } + QRect r( x, 0, tw, h ); + + QValueVector violations = checkTitleStyle( gb->title(), ShortTitle, HasAccels ); + + renderViolations( violations, &paint, r, AlignCenter | ShowPrefix, gb->title() ); + + drawItem(&paint, r, AlignCenter | ShowPrefix, gb->colorGroup(), + gb->isEnabled(), 0, stripped_title ); + + paint.setClipRegion( pevent->region().subtract( r ) ); + } + + if (nestedGroupBox) + { + paint.save(); + QPen errorPen(Qt::red, 4, QPen::DashDotDotLine); + paint.setPen(errorPen); + paint.drawRect( gb->frameRect() ); + paint.restore(); + } + else + { + drawPrimitive( QStyle::PE_GroupBoxFrame, &paint, gb->frameRect(), + gb->colorGroup(), QStyle::Style_Default, + QStyleOption(gb->lineWidth(), gb->midLineWidth(), + gb->frameShape(), gb->frameShadow()) ); + } + return true; //We already drew the everything + } + + return false; +} + + +void StyleCheckStyle::renderGradient( QPainter* p, const QRect& r, + QColor clr, bool, int, int, int, int) const +{ + p->fillRect(r, clr); + return; +} + +// vim: set noet ts=4 sw=4: +// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off; diff --git a/scheck/scheck.h b/scheck/scheck.h new file mode 100644 index 00000000..ba91ca01 --- /dev/null +++ b/scheck/scheck.h @@ -0,0 +1,173 @@ +/* + * $Id$ + * + * KDE3 Style Guide compliance check "Style", v0.0.1 + * Copyright (C) 2002 Maksim Orlovich + * (C) 2002 Ryan Cumming + * + * + * Based on the KDE3 HighColor Style (version 1.0): + * Copyright (C) 2001-2002 Karol Szwed + * (C) 2001-2002 Fredrik Hglund + * + * Drawing routines adapted from the KDE2 HCStyle, + * Copyright (C) 2000 Daniel M. Duley + * (C) 2000 Dirk Mueller + * (C) 2001 Martijn Klingens + * + * Portions of code are from the Qt GUI Toolkit, Copyright (C) 1992-2000 Trolltech AS. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * + */ + +#ifndef STYLE_CHECK_H +#define STYLE_CHECK_H + +#include +#include +#include +#include +#include + +#include +#include +#include + + +class QPopupMenu; + +class StyleCheckTitleWatcher: public QObject +{ + Q_OBJECT + + public: + StyleCheckTitleWatcher(); + void addWatched(QWidget* w); + public slots: + void slotCheck(); + private: + QString cleanErrorMarkers(QString in); + QValueVector > watched; + QValueVector watchedTitles; +}; + +class StyleCheckStyle : public KStyle +{ + Q_OBJECT + + public: + StyleCheckStyle( ); + virtual ~StyleCheckStyle(); + + void polish( QWidget* widget ); + void unPolish( QWidget* widget ); + + + void drawKStylePrimitive( KStylePrimitive kpe, + QPainter* p, + const QWidget* widget, + const QRect &r, + const QColorGroup &cg, + SFlags flags = Style_Default, + const QStyleOption& = QStyleOption::Default ) const; + + void drawPrimitive( PrimitiveElement pe, + QPainter* p, + const QRect &r, + const QColorGroup &cg, + SFlags flags = Style_Default, + const QStyleOption& = QStyleOption::Default ) const; + + void drawControl( ControlElement element, + QPainter *p, + const QWidget *widget, + const QRect &r, + const QColorGroup &cg, + SFlags flags = Style_Default, + const QStyleOption& = QStyleOption::Default ) const; + + void drawControlMask( ControlElement element, + QPainter *p, + const QWidget *widget, + const QRect &r, + const QStyleOption& = QStyleOption::Default ) const; + + void drawComplexControl( ComplexControl control, + QPainter *p, + const QWidget *widget, + const QRect &r, + const QColorGroup &cg, + SFlags flags = Style_Default, + SCFlags controls = SC_All, + SCFlags active = SC_None, + const QStyleOption& = QStyleOption::Default ) const; + + void drawComplexControlMask( ComplexControl control, + QPainter *p, + const QWidget *widget, + const QRect &r, + const QStyleOption& = QStyleOption::Default ) const; + + int pixelMetric( PixelMetric m, + const QWidget *widget = 0 ) const; + + QSize sizeFromContents( ContentsType contents, + const QWidget *widget, + const QSize &contentSize, + const QStyleOption& opt ) const; + + QRect subRect( SubRect r, + const QWidget *widget ) const; + + // Fix Qt3's wacky image positions + QPixmap stylePixmap( StylePixmap stylepixmap, + const QWidget *widget = 0, + const QStyleOption& = QStyleOption::Default ) const; + + protected: + bool eventFilter( QObject *object, QEvent *event ); + + void renderGradient( QPainter* p, + const QRect& r, + QColor clr, + bool horizontal, + int px=0, + int py=0, + int pwidth=-1, + int pheight=-1 ) const; + + QTimer *topLevelAccelManageTimer; + QWidget *hoverWidget; + + private slots: + void slotAccelManage(); + + private: + void accelManageRecursive(QWidget* widget); + + StyleCheckTitleWatcher* watcher; + + // Disable copy constructor and = operator + StyleCheckStyle( const StyleCheckStyle & ); + StyleCheckStyle& operator=( const StyleCheckStyle & ); +}; + +// vim: set noet ts=4 sw=4: +// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off; + +#endif diff --git a/scheck/scheck.themerc b/scheck/scheck.themerc new file mode 100644 index 00000000..24e02251 --- /dev/null +++ b/scheck/scheck.themerc @@ -0,0 +1,55 @@ +[Misc] +Name=Scheck +Name[cs]=Kontrola stylu +Name[hi]=शेक +Name[hu]=Stílusellenőrző +Name[ta]=Sசரிபார்ப்பு +Comment=Development style for searching accelerator and style guide conflicts +Comment[bg]=Стил за търсене на ускорител и конфликти +Comment[bs]=Stil razvoja za pretragu konflikata akceleratora i konflikata sa stilskim vodičem +Comment[ca]=Estil de desenvolupament per a cercar conflictes amb acceleradors i la guia d'estil +Comment[cs]=Vývojový styl pro hledání konfliktů v akcelerátorech a textech +Comment[cy]=Arddull datblygu er chwilio am wrthdaro yn y cyflymyddion a'r canllawiau steil +Comment[da]=Udviklingsstil til søgning efter accelerator- og stilguide konflikter +Comment[de]=Entwicklerstil zum Auffinden von Konfikten bei Kurzbefehlen und Gestaltungsrichtlinien +Comment[el]=Στυλ ανάπτυξης για επιταχυντή αναζήτησης και αντιθέσεις στον οδηγό στυλ +Comment[es]=Estilo de desarrollo para buscar conflictos de accesos rápidos y guías de estilo +Comment[et]=Arendusstiil kiirklahvi- ja stiilijuhendi konfliktide leidmiseks +Comment[eu]=Bizkortzaile eta estilo-gida gatazken bilaketarako garapen estiloa +Comment[fa]=سبک پیشرفته برای جستجوی شتاب‌ده و ناسازگاریهای راهنمای سبک +Comment[fi]=Kehitystyyli pikanäppäinten ja tyylioppaan ristiriitojen etsimiseen +Comment[fr]=Style de développement pour la recherche de conflits d'accélérateurs et guide de style +Comment[gl]=Estilo de desenvolvemento para procurar conflitos en aceleradores e de guías de estilo +Comment[hi]=त्वरक खोज तथा स्टाइल गाइड कान्फ्लिक्ट्स के लिए डेवलपमेंट शैली +Comment[hu]=Fejlesztői stílus a gyorsbillentyűk és a stíluselőírások összeegyeztetéséhez +Comment[is]=Þróunarstíll til að leita að árekstrum milli flýtilykla og stílaleiðarvísa +Comment[it]=Stile di sviluppo per cercare conflitti nei tasti acceleratori e negli stili +Comment[ja]=アクセラレータやスタイルガイドの衝突を探すための開発スタイル +Comment[ka]=განვითარების სტილი ძიების აქსელერატორისთვის და სტილთა გიდის კონფლიქტები +Comment[kk]=Акселератор мен жазу стилінің қайшылықтарын іздейтін жобалау стилі +Comment[ms]=Gaya pembangunan untuk cari pintasan dan gaya pengurusan konflik +Comment[nb]=Utviklingsstil for å finne konflikter med retningslinjene og mellom snarveistaster +Comment[nds]=Stil för't Finnen vun Tastkombinatschoon- un Stilregel-Problemen +Comment[ne]=गतिबर्धक र शैली मार्गदर्शन द्वन्द खोज्नका लागि बिकास शैली +Comment[nl]=Ontwikkelstijl voor het zoeken naar sneltoets- en stijlgidsconflicten +Comment[nn]=Utviklingsstil for å finna konfliktar med retningslinjene og mellom snarvegstastar +Comment[pl]=Styl programistyczny do wyszukiwania konfliktów klawiszy skrótu i prowadzenia stylu +Comment[pt]=Estilo de desenvolvimento para procurar conflitos de aceleradores e de guias de estilo +Comment[pt_BR]=Estilo de desenvolvimento para procurar conflitos de aceleradores e guias de estilo +Comment[ru]=Стиль разработки для поиска конфликтов акселераторов и стиля написания программ +Comment[sk]=Vývojársky štýl pre hľadanie konfliktov v akcelerátoroch a štýle +Comment[sl]=Razvijalski slog za iskanje pospeševalnikov in konfliktov v slogih +Comment[sr]=Развојни стил за тражење сукоба са стилским водичем и међу пречицама за брзи приступ. +Comment[sr@Latn]=Razvojni stil za traženje sukoba sa stilskim vodičem i među prečicama za brzi pristup. +Comment[sv]=Utvecklingsstil för att leta efter konflikter för snabbtangenter och stilguide +Comment[ta]=தேடுதல் முடுக்கி மற்றும் பாணி வழிகாட்டி எதிர்மறைகளுக்கு தேவையான மேம்பாடு. +Comment[tg]=Навъи коркард барои ҷустуҷӯи низоии акселаторҳо ва навъи навишти барнома +Comment[tr]=Arama hızlandırıcısı ve biçem kılavuz çelişmeleri için geliştirme biçemi +Comment[uk]=Стиль розробки для пошуку конфліктів акселераторів та правил стилю +Comment[zh_CN]=搜索加速键和样式指南冲突的开发样式 + +[KDE] +WidgetStyle=Check + +[Desktop Entry] +Hidden=true diff --git a/scheck/status.txt b/scheck/status.txt new file mode 100644 index 00000000..4860b230 --- /dev/null +++ b/scheck/status.txt @@ -0,0 +1,41 @@ +TODO/BUG: Handle multiline labels, richtext labels. + +Capitalization checking: + +Title style -- lower case exception list may have to be expanded + +window and dialog box titles - Partially working +group box / group line labels - Done// ??? What are group line labels? +button labels - Done. +tab labels - Done. +listview column headers - Done +menu titles / menu items - Done +derivatives of KCommand - Not a widget. +combobox items - Not sure of what to do with those below yet +listbox items +tree list items +other heading/title text - Hard to distinguish from regular labels, only works for KJanusWidget titles + +Sentence style + +edit box labels +list box labels +combo box labels +slider labels +spin box labels - If all those are regular labels, then done (need to check) +check box labels - Done +option button labels - Done + +pop-up hint text - TODO +other non heading/title text - Kind of -- hard to distinguish from title labels. + +Other violations + +Check for nested groupboxes. - Done, when parenting hierarchy isn't bizzare. + +Other todo: + + +Widgets on menubar +Menu order. +Augment title checks to check for file - App naming diff --git a/scripts/Makefile.am b/scripts/Makefile.am new file mode 100644 index 00000000..3cea8486 --- /dev/null +++ b/scripts/Makefile.am @@ -0,0 +1,57 @@ +# sorted by function +bin_SCRIPTS = \ + create_makefile create_makefiles adddebug \ + cheatmake makeobj kde-build build-progress.sh pruneemptydirs \ + cvsbackport cvsversion cvscheck cvslastchange cvslastlog cvsrevertlast \ + noncvslist cvs-clean cvs2dist cvsblame cvsforwardport create_cvsignore \ + colorsvn create_svnignore nonsvnlist svn2dist svnaddcurrentdir svnbackport svnforwardport \ + svn-clean svngettags svnlastchange svnlastlog svnrevertlast svnversions \ + svnchangesince findmissingcrystal kdesvn-build \ + kdedoc qtdoc extractrc extractattr zonetab2pot.py \ + licensecheck fixkdeincludes fixuifiles includemocs \ + cxxmetric extend_dmalloc kdekillall kdelnk2desktop.py \ + package_crystalsvg png2mng.pl kdemangen.pl + +# Install syntax highlighting file for KWrite/Kate. +kdesvn_build_syntaxdir = $(kde_datadir)/katepart/syntax +kdesvn_build_syntax_DATA = kdesvn-buildrc.xml + +# sorted by bin_SCRIPTS reference +man_MANS = \ + kde-build.1 \ + kdesvn-build.1 \ + cvsversion.1 cvscheck.1 \ + noncvslist.1 \ + cvsblame.1 \ + includemocs.1 + +CLEANFILES = $(man_MANS) + +# sorted alphabetically +noinst_SCRIPTS = \ + add_trace.pl alldcop.rb authors2xml.pl check_licenses colorcvs \ + cvsaddcurrentdir fixheaders kDebug2kdDebug.sh + +EXTRA_DIST = completions kde-emacs \ + colorcvsrc-sample gettext.patch kde-buildrc \ + kde-devel-emacs.el kde-devel-gdb kde-devel-vim.vim \ + kde.supp kdesvn-buildrc-sample + +# it'd be too good if this worked everywhere ... +#%.1: % +# pod2man $< $@ + +kde-build.1: kde-build + pod2man $(srcdir)/kde-build > $@ +kdesvn-build.1: kdesvn-build + pod2man $(srcdir)/kdesvn-build > $@ +cvsversion.1: cvsversion + pod2man $(srcdir)/cvsversion > $@ +cvscheck.1: cvscheck + pod2man $(srcdir)/cvscheck > $@ +noncvslist.1: noncvslist + pod2man $(srcdir)/noncvslist > $@ +cvsblame.1: cvsblame + pod2man $(srcdir)/cvsblame > $@ +includemocs.1: includemocs + pod2man $(srcdir)/includemocs > $@ diff --git a/scripts/README b/scripts/README new file mode 100644 index 00000000..ca3811e5 --- /dev/null +++ b/scripts/README @@ -0,0 +1,186 @@ + +Stuff in this directory: + +=== DEBUGGING SUPPORT + +adddebug Modifies the Makefile to add debug info (-g) + +add_trace.pl Modifies a source file to add a trace as the first line + of every method, using kdDebug, and showing args values. + +kDebug2kdDebug.sh Script to convert old KDE debugging statements to their + modern equivalents. + +extend_dmalloc Script to run gdb on return-addresses + +kdekillall Kills the process "kdeinit: with signal + +=== PROGRAMMING SUPPORT + +cheatmake Helper for saving time when recompiling, skipping files that + haven't changed in a meaningful way (e.g. if you only change + a comment...) + +check_licenses Old license checker for source files + (Use licensecheck instead) + +licensecheck Simple license checker for source files + +create_makefile Create the Makefile in a directory containing a Makefile.am + Saves time compared to re-running configure completely + +create_makefiles The recursive version of it - needs create_makefile. + +fixheaders Adds header files as it recognices make error output + +fixkdeincludes Tries to reduce the number of includes in KDE source files + +fixuifiles Fixes up Qt Designer .ui files (version, accels, generated names) + To use before any commit of a .ui file. + +fixfsfaddr.sed Script for sed to fix old FSF addresses + +includemocs Adds missing "#include foobar.moc" lines + +kdedoc Open a kde help page in kfm/konqueror + +qtdoc Open a qt help page in kfm/konqueror + +kde-spellcheck.pl A script to check source code for misspelings and optionally + correct them. + +=== MODERNIZATION SCRIPTS + +rc2kcfgxt.pl Reads an existing KConfig rc file and creates a best-guess + version of a KConfigXT XML file. + +kdelnk2desktop.py Converts old-style .kdelnk files to their modern .desktop + equivalents. + +=== USEFUL DATA FOR EXTERNAL PROGRAMS + +kde-devel-emacs.el An emacs file that contains many helpful functions and keybindings + A must for anyone using [X]Emacs to develop KDE/Qt/C++ applications. + +kde-devel-gdb A gdb macro definition file, to e.g. print QStrings from gdb. + +kde-devel-vim.vim A vim script that contains many helpful functions and keybindings + for vim-using KDE developers. + +kde.supp Some valgrind suppressions handy for ignoring stuff we don't + care about when valgrinding kde applications + +completions/ Contains useful scripts to enhance the auto-complete feature of some shells. + +=== INFORMATION EXTRACTION + +alldcop.rb Shows an pseudo-XML representation of the DCOP interfaces for + currently-running KDE applications. Does not require + Korundum. + +authors2xml.pl Extract author information from C++ files and print it out + in DocBook format as a list + +makeobj Script around make which basicly checks if it's in srcdir + or in builddir and changes to the right directory for + calling /usr/bin/make + +extractrc Extract text tags from designer's UI files and XML GUI-RC files + +extractattr Same as extractrc, but for use by Scripty. + +findmissingcrystal Looks at Crystal icons to find ones which are still + unchanged from kdeclassic. + +zonetab2pot.py Reads timezone list as its first argument or from + /usr/share/zoneinfo/zone.tab, and converts it to a PO file + template. + +kdemangen.pl Script to use the output from a KDE application's --author and + --help-all output to automatically generate a man page for the + application. + +png2mng.pl Script to convert a series of numbered .png files into a .mng + animation. + +package_crystalsvg Script to package all svg files recursively from the current + directory. + +=== SOURCE CONTROL UTILITIES (CVS and Subversion) +=== All CVS utilities have a corresponding svn version. + +colorcvs Colorizes cvs commands. + +create_cvsignore Create a .cvsignore file (using the contents of Makefile.am) + +cvs-clean Recursively wipes out everything not registered in the CVS + server, starting from the current directory. + +cvs2dist Create a standalone source distribution tarball for an app + in a KDE CVS module. + +cvsaddcurrentdir Add all files in and below the current dir to cvs. + *.c, *.h, *.C, *.cpp, *.cc are added automatically, + *~, *.o, *.so, *.lo, *.la, .libs/, .deps/, .#* are ignored. + You will be asked for the remaining files. + +cvsblame Bonsai-like cvs annotate + +cvscheck Offline check for status of files in a checked-out module. + +cvsgettags List the available CVS tags for a given set of files, or + recursively for all files and directories. No equivalent for + svn. + +noncvslist List all files in a checked out CVS module that are unknown to + to the CVS server. + +cvsversion Display CVS version of a file without connecting to the server. + +cxxmetric Counts lines of code, comments and blank space in C and C++ + source files. + +cvslastchange launches "cvs diff -u" to display the last applied changes for a + given file. HEAD branch only. + +cvslastlog Shows the log associated with the last change on a given file. + +cvsremovealltags Remove all tags from a CVS file. Use with care, and for instance + after copying a file on the server. + +cvsrevertlast Reverts all the files given on the command by one version, then + you can commit them. + +cvsbackport Backport the last commit in HEAD, to a branch. + +cvsforwardport Forwardport the last commit in a branch to HEAD + +pruneemptydirs Detects stale source dirs in a CVS tree + +cvslastreferenced Goes through the whole history of a file to find all modifications + referencing a specific string. It's useful if you want to know + when a function has been removed/modified/added to a file if a + recent cvs annotate doesn't reference it anymore. + +=== KDE BUILD SCRIPTS + +kde-build Updates and recompiles a local CVS or Subversion tree + +build-progress.sh Displays the progress of kde-build, times needed to complete each + step. And sets the titlebar of the terminal to the directory that + make is processing + +kdesvn-build Updates and recompiles a local Subversion tree. + +=== OTHERS + +gettext.patch Patch for gettext-0.10.35 to give xgettext the functionality to + extract scoped messages + +---------------- +Looking to add a script? + +Just add it to this README. For easy man pages see the perlpod manpage; the +man page for many of these tools is in the script itself. + +$Id$ diff --git a/scripts/add_trace.pl b/scripts/add_trace.pl new file mode 100644 index 00000000..bad152c3 --- /dev/null +++ b/scripts/add_trace.pl @@ -0,0 +1,124 @@ +## add_trace.pl +## Script to add a kdDebug() call as the first line of each method +## including as many parameters as possible (i.e. those supported by kdDebug) +## Very useful for tracing. +## +## Usage: perl -i add_trace.pl myfile.cpp +## +## Generates all statement with kdDebug(0) so that it is very easy +## to remove them afterwards : +## perl -pi -e 'if (/kdDebug\(0\)/) { $_ = ""; }' myfile.cpp +## perl -pi -e 'if (/#include \/\/ inserted by add_trace/) { $_ = ""; }' myfile.cpp +## +## needs perl 5.8+ for the Tie::File module +## +## Written by David Faure , licensed under pizzaware. +## 18/03/2000 + +use Tie::File; + +sub insert_kdebug() +{ +# inserts a line "include " at the beginning of each file + for ($i=0;$i<$#ARGV;$i++) + { + tie @LOG, 'Tie::File', $ARGV[$i]; + unshift @LOG, '#include // inserted by add_trace'; + } +} + +insert_kdebug(); +$insignature=0; +while (<>) +{ + if ( $insignature ) + { + $statement .= $_; + chop; + $oneline .= $_; + } + elsif ( /^[^\s]+\s*[^\s]+::[^\s]+/ && !/typedef\s/ && !/^\s*class\s/ && !/^[^\s]+\s*[^\s]+::[^\s]+.*;/ && !/ *\/\// ) + # A function declaration starts. A function is not a typedef, not a class, not a command and not a comment. + { + $insignature = 1; + $statement = $_; + chop; + $oneline = $_; + } + elsif ( /^\/\/.*/) + { + # comment + # do nothing + $insignature = 0; + } + # [^\s]+ means, one ore more characters that are no spaces + elsif ( /^[^\s]+\s*[^\s]+::[^\s]+.*\}/ && !/typedef\s/ && !/^\s*class\s/ ) + { + # declaration and implementation in one line + # do nothing + $insignature = 0; + } + if ( $insignature ) + { + if ( /\{/ ) # End of signature + { + $insignature = 0; + $_ = $oneline; + #print STDERR "Signature : $_\n"; + print $statement; + $line = " kdDebug(0)"; + if ( m/([^\*\s]+::[^\s]+)\(/ ) + { + $line = $line . " << \"$1\""; + } + ## Ok now extract args + s/\/\*.*\*\///; + s/\s*\/\/.*//; # remove comments + s/^.*\([\s]*//; # Remove everything before first '(' + s/\s*\)\s*:\s+.*$/,/; # Remove any ") : blah", replace with a ',' + s/\s*\).*\{\s*$/,/; # Remove anything after ')', replace with a ',' + s/ const[&] / /g; + #print STDERR "Args list : $_\n"; + @args = split( ",", $_ ); + foreach (@args) + { + s/^\s*//; + s/\s*$//; + #print STDERR "Argument: $_\n"; + ## Pointer ? + if ( m/[a-zA-Z0-9_\s]+\*\s*([a-zA-Z0-9_]+)/ ) { + $line = $line . " << \" $1=\" << " . $1; + } + ## int, long ? + elsif ( m/^int\s+([a-zA-Z0-9_]+)/ || m/^long\s*([a-zA-Z0-9_]+)/ ) { + $line = $line . " << \" $1=\" << " . $1; + } + ## bool + elsif ( m/^bool\s+([a-zA-Z0-9_]+)/ ) { + $line = $line . " << \" $1=\" << (" . $1 . " ? \"true\" : \"false\" )"; + } + ## QString and friends + elsif ( m/QString[\&\s]+([a-zA-Z0-9_]+)/ || m/QCString[\&\s]*([a-zA-Z0-9_]+)/ ) { + $line = $line . " << \" $1=\" << " . $1; + } + ## KURL + elsif ( m/KURL[\&\s]+([a-zA-Z0-9_]+)/ ) { + $line = $line . " << \" $1=\" << " . $1 . ".url()"; + } + } + $line = $line . " << endl;\n"; + if ( !m/\}/ ) {print $line;} + #print STDERR "Debug call added : $line\n"; + } + } + else + { + # Normal line + print; + } +} +if ( $insignature ) +{ + print STDERR "Warning, unterminated method signature !! Check the file !\n"; + print $statement; +} diff --git a/scripts/adddebug b/scripts/adddebug new file mode 100755 index 00000000..8968e89c --- /dev/null +++ b/scripts/adddebug @@ -0,0 +1,63 @@ +#!/bin/sh +# Modifies the Makefile in the current directory (and optionally its subdirs), +# - to add debug info (-g3) +# - optionally (default) remove optimizations (-O[1-9]?) +# - optionally remove -DNDEBUG and -DNO_DEBUG + +mxdp="-maxdepth 1" +for i in $*; do + case $i in + -k) keep=1;; + -r) mxdp=;; + -n) ndebug=1;; + *) echo -e "Usage: adddebug [-k] [-r] [-n]\n -k: keep optimizations (removed by default)\n -r: recursive (process all subdirectories)\n -n: compile without NDEBUG and NO_DEBUG being defined (makes kdDebug calls work)"; exit 1;; + esac +done + +xpr='s/^((C|CXX|LD)FLAGS[ \t]*=.*)$/\1 -g3/' +if test -z $keep; then + xpr="$xpr;"'s/[\t ]-O[1-9]?\b//g' + xpr="$xpr;"'s/[\t ]-march=\S+\b//g' +fi +if test -z $ndebug; then + xpr="$xpr;"'s/[\t ]-DNDEBUG\b//g' + xpr="$xpr;"'s/[\t ]-DNO_DEBUG\b//g' +fi +find . $mxdp -name Makefile -exec perl -pi -e "$xpr" {} \; + +using_unsermake= +if head -n 1 Makefile | grep unsermake >/dev/null; then + using_unsermake=new +fi +if head -n 1 Makefile | grep automake >/dev/null; then + using_unsermake=old +fi + +top_builddir=`grep '^top_builddir' Makefile | sed -e 's/^.*= *//'` + +if test "$using_unsermake" = "new"; then + # the idea: grab the cxxflags from MakeVars, modify them, and write them down + # in every Makefile after the line that says .FORWARDS + toplevelMakefile=$top_builddir/Makefile + if [ -f $toplevelMakefile ]; then + # warning this uses sed, so the '?' in the perl regexp above doesn't work here + cxxflags=`grep ^CXXFLAGS $toplevelMakefile | sed -e 's/[\t ]-O[1-9]\b//g;s/[\t ]-march=\S+\b//g;s/[\t ]-DNDEBUG\b//g;s/[\t ]-DNO_DEBUG\b//g'` + xpr="s/^CXXFLAGS\s*=.*//; if ( /^\.FORWARDS:/) { "'$_'" .= \"\n$cxxflags -g3\"; }" + find . $mxdp -name Makefile -exec perl -pi -e "$xpr" {} \; + else + echo "ERROR: top_builddir is $top_builddir but $makevars not found" + fi + +elif test "$using_unsermake" = "old"; then + # the idea: grab the cxxflags from MakeVars, modify them, and write them down + # in every Makefile after the line that includes MakeVars + makevars=$top_builddir/MakeVars + if [ -f $makevars ]; then + # warning this uses sed, so the '?' in the perl regexp above doesn't work here + cxxflags=`grep ^CXXFLAGS $makevars | sed -e 's/[\t ]-O[1-9]\b//g;s/[\t ]-march=\S+\b//g;s/[\t ]-DNDEBUG\b//g;s/[\t ]-DNO_DEBUG\b//g'` + xpr="s/^CXXFLAGS\s*=.*//; if ( /^include .*MakeVars$/) { "'$_'" .= \"\n$cxxflags -g3\"; }" + find . $mxdp -name Makefile -exec perl -pi -e "$xpr" {} \; + else + echo "ERROR: top_builddir is $top_builddir but $makevars not found" + fi +fi diff --git a/scripts/alldcop.rb b/scripts/alldcop.rb new file mode 100755 index 00000000..b15a1fdd --- /dev/null +++ b/scripts/alldcop.rb @@ -0,0 +1,91 @@ +#!/usr/bin/env ruby + +module DCOP + + def dump_all_apps + + `dcop`.split(/\n/).each do + + |app| + + DCOP.dump_app(app) + + end + + end + + def dump_app(app) + + print "\n" + + `dcop #{app}`.split(/\n/).each do + + |object| + + DCOP.dump_object(app, object) + + end + + print "\n" + + end + + def dump_object(app, object) + + object.gsub!(/\(default\)/, '') + object.strip! + + print " \n" unless object == "(default)" + + `dcop #{app} #{object}`.split(/\n/).each do + + |method| + + DCOP.dump_method(app, object, method) + + end + + print " \n" + + end + + def dump_method(app, object, method) + + return_type, method_name, arg_str = method.split(/[ \(]/, 3) + + arg_str.gsub!(/\)$/, '') + + arg_list = arg_str.split(',') + + print " \n" + return + + else + + print ">\n" + + arg_list.each do + + |arg| + + type, name = arg.split + + print " \n" + + end + + print " \n" + + end + + end + + module_function :dump_all_apps, :dump_app, :dump_object, :dump_method + +end + +DCOP.dump_all_apps if __FILE__ == $0 diff --git a/scripts/authors2xml.pl b/scripts/authors2xml.pl new file mode 100755 index 00000000..c2e34438 --- /dev/null +++ b/scripts/authors2xml.pl @@ -0,0 +1,27 @@ +#!/usr/bin/perl -w +# Extract author information from C++ files +# and print it out in DocBook format as a list +# Daniel Naber +# $Id$ + +my $file = $ARGV[0]; +if( ! $file ) { + print "Usage: $0 \n"; + exit; +} + +open(IN, $file) || die "Cannot open '$file': $!\n"; +undef $/; +my $str = (); +close(IN); + +print "\n"; +while( $str =~ m/addAuthor\s*\(\s*"(.*?)",\s*.*?,\s*"(.*?)"/gs ) { + my ($name, $email) = ($1, $2); + print "$name $email\n"; + #print "$name, $email\n"; +} +print "\n"; + +print STDERR "Warning: maybe you need to fix umlauts manually...\n"; +exit; diff --git a/scripts/build-progress.sh b/scripts/build-progress.sh new file mode 100644 index 00000000..05a9dfd7 --- /dev/null +++ b/scripts/build-progress.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# This method gives some kind of status message in the title bar of Konsole, +# xterm, etc.. Thanks have to go to Malte Starostik +# for the code :-) +set_title() { +if ([ "$TERM" = "xterm" ] || [ "$TERM" = "xterm-color" ] || [ "$TERM" = "screen" ]) && tty -s; then + echo -ne "\033]0;$1\007" +fi +} +. ./kde-buildrc +set_title "Progress of kde-build script..." + +cd $KDELOGDIR +while true; do + dir=`ls -t | head -n 1 | xargs grep "Entering directory" | tail -n 1 | awk "{print \\$4}" | sed "s'$KDESRCDIR/''g"` + set_title "Building $dir" + clear + grep --no-filename -i "time needed" *build* | \ + sed "s/\:T/ T/g" + sleep 5 +done; diff --git a/scripts/cheatmake b/scripts/cheatmake new file mode 100755 index 00000000..9659a6cc --- /dev/null +++ b/scripts/cheatmake @@ -0,0 +1,80 @@ +#!/bin/sh +# Helper for saving time when recompiling, skipping files that haven't +# changed in a meaningful way (e.g. if you only change a comment...) +# +# LGPL v2, David Faure + +usage() +{ + echo "Usage:" + echo " $0 hidechange file Hides the fact that file was changed (use with care!)" + echo " $0 show Lists what make currently has to rebuild" + echo " $0 why file Explains why make must rebuild file" + exit 1; +} + +if [ $# -eq 0 ]; then usage; fi +CDPATH= +builddir=$PWD + +# 'srcdir != builddir' stuff +if test ! -f Makefile && test -n "$OBJ_SUBDIR"; then + builddir=$OBJ_SUBDIR +else + if test ! -f Makefile && test -n "$OBJ_REPLACEMENT"; then + builddir=`pwd | sed -e "$OBJ_REPLACEMENT"` + fi +fi + +if test ! -f Makefile && test -n "$OBJ_SUBDIR"; then + builddir=$OBJ_SUBDIR +fi +cd $builddir +srcdir=`egrep '^srcdir *=' Makefile | sed -e "s#srcdir *= *##"` +UNSERMAKE=`type -p unsermake` +using_unsermake= +if head -n 1 Makefile | grep unsermake >/dev/null; then + using_unsermake=new +fi +if head -n 1 Makefile | grep automake >/dev/null; then + using_unsermake=old +fi + + +case $1 in + hidechange ) + if [ $# -ne 2 ]; then usage; fi + ## TODO: unsermake support (or support in unsermake itself) + deps=`make SUBDIRS='' -n | grep '\-o' | sed -e 's/.*-o \([^ ]*\).*/\1/'`; + if [ -n "$deps" ]; then + oldestdep=`ls -t $deps 2>/dev/null | tail -1` + thefile=$2 + if [ -f $srcdir/$thefile ]; then + thefile=$srcdir/$thefile + fi + echo + echo "Setting date of $thefile back in time with:" + echo "touch -r $oldestdep $thefile" ; touch -r $oldestdep $thefile + fi + ;; + show ) + if [ $# -ne 1 ]; then usage; fi + if test "$using_unsermake" != "new"; then + # Look at the commands that make will issue, and extract "-o output". + # The only trouble, with libtool, is that this gives us .libs/foo.o instead of foo.lo + make SUBDIRS='' -n | grep '\-o' | sed -e 's/.*-o \([^ ]*\).*/\1/' + else + # Solve the above problem (when using new-unsermake) by watching the "echo" lines for creating .lo files + # separately from the rest of the "-o target" lines (for libs and binaries) + # (For libs and bins another way would be to grep for "linking") + # The "ls" is for sorting by date + $UNSERMAKE -n | perl -e 'while(<>) { if (/by libtool/) { s/.*> //; print; } if (m/-o ([^ ]*)/ && $1!~/\.o$/) { print "$1\n"; } }' | xargs --no-run-if-empty ls -t -1 + fi + ;; + why ) + if [ $# -ne 2 ]; then usage; fi + ## TODO: unsermake support (or support in unsermake itself) + make SUBDIRS='' -n -d $2 | egrep -e "(newer than target \`$2'|Must)" + ;; + * ) usage ;; +esac diff --git a/scripts/check_licenses b/scripts/check_licenses new file mode 100755 index 00000000..6accd259 --- /dev/null +++ b/scripts/check_licenses @@ -0,0 +1,88 @@ +#!/usr/bin/perl -w + +unless (scalar(@ARGV) == 1) +{ + die "Usage: check_licenses directory"; +} + +my $gpl = 'General Public License'; +my $gp2 = 'This is free software; it comes under the GNU'; +my $gp3 = 'License: GPL with the following explicit clarification'; +my $x11 = 'TORT OR OTHERWISE'; +my $bsd = 'INCLUDING NEGLIGENCE OR OTHERWISE'; +my $gen = 'generated'; + +sub nameok() +{ + my $f = shift; + + if ($f =~ /\.C$/ or $f =~ /\.cpp$/ or $f =~ /\.c$/ or $f =~ /\.h$/) + { + if ($f =~ /\.cpp$/) + { + if + ( + $f !~ /meta_unload\.cpp$/ + and $f !~ /_stub\.cpp/ + and $f !~ /_skel.cpp/ + and $f !~ /_closure\.cpp/ + and $f !~ /moc\.cpp/ + ) + { + return 1; + } + else + { + return 0; + } + } + else + { + return 1; + } + } + else + { + return 0; + } +} + +sub recursive_check() +{ + my $dir = shift; + + opendir (DIR, $dir) or die "Can't open $dir"; + + my @filenames = grep { /^[^\.]/ } readdir(DIR); + + for my $f (@filenames) + { + my $filename = "$dir/$f"; + + if (-d $filename) + { + &recursive_check($filename); + } + elsif (-f $filename and &nameok($filename)) + { + open (IN, "<$filename") or die "Can't open $filename"; + + my $license = "!"; + + while () + { + if (/$gpl/) { $license = "G"; last; } + if (/$gp2/) { $license = "G"; last; } + if (/$gp3/) { $license = "G"; last; } + if (/$x11/) { $license = "X"; last; } + if (/$bsd/) { $license = "B"; last; } + if (/$gen/) { $license = "g"; last; } + } + + print "$license $filename\n"; + } + } +} + +&recursive_check($ARGV[0]); + diff --git a/scripts/colorcvs b/scripts/colorcvs new file mode 100755 index 00000000..41c7716d --- /dev/null +++ b/scripts/colorcvs @@ -0,0 +1,175 @@ +#! /usr/bin/env perl + +# colorcvs +# +# based on colorgcc +# +# Requires the ANSIColor module from CPAN. +# +# Usage: +# +# In a directory that occurs in your PATH _before_ the directory +# where cvs lives, create a softlink to colorcvs: +# +# cvs -> colorcvs +# +# That's it. When "cvs" is invoked, colorcvs is run instead. +# +# The default settings can be overridden with ~/.colorcvsrc. +# See the colorcvsrc-sample for more information. +# +# Note: +# +# colorcvs will only emit color codes if: +# +# (1) tts STDOUT is a tty. +# (2) the value of $TERM is not listed in the "nocolor" option. +# (3) the cvs command is not a commit or import (as the text editor +# opened by cvs will often be hampered by colorcvs). +# +# If colorcvs colorizes the output, cvs's STDERR will be +# combined with STDOUT. Otherwise, colorcvs just passes the output from +# cvs through without modification. +# +# Copyright 2002 Neil Stevens +# +# Copyright 1999 Jamie Moyers +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + +use Term::ANSIColor; +use IPC::Open3; + +sub initDefaults +{ + $cvsPath = "/usr/bin/cvs"; + + $nocolor{"dumb"} = "true"; + + $colors{"P"} = color("reset"); + $colors{"U"} = color("reset"); + $colors{"C"} = color("bold red"); + $colors{"M"} = color("bold yellow"); + $colors{"A"} = color("cyan"); + $colors{"R"} = color("cyan"); + $colors{"?"} = color("bold"); + $colors{"server"} = color("bold green"); + $colors{"warning"} = color("bold cyan"); +} + +sub loadPreferences +{ +# Usage: loadPreferences("filename"); + + my($filename) = @_; + + open(PREFS, "<$filename") || return; + + while() + { + next if (m/^\#.*/); # It's a comment. + next if (!m/(.*):\s*(.*)/); # It's not of the form "foo: bar". + + $option = $1; + $value = $2; + + if ($option =~ /cvs/) + { + $cvsPath = $value; + } + elsif ($option eq "nocolor") + { + # The nocolor option lists terminal types, separated by + # spaces, not to do color on. + foreach $termtype (split(/\s+/, $value)) + { + $nocolor{$termtype} = "true"; + } + } + else + { + $colors{$option} = color($value); + } + } + close(PREFS); +} + +# +# Main program +# + +# Set up default values for colors and cvs path. +initDefaults(); + +# Read the configuration file, if there is one. +$configFile = $ENV{"HOME"} . "/.colorcvsrc"; +if (-f $configFile) +{ + loadPreferences($configFile); +} + +# Get the terminal type. +$terminal = $ENV{"TERM"} || "dumb"; + +$commit = 0; +foreach (@ARGV) +{ + if(/^ci$/ || /^commit$/ || /^import$/) + { + $commit = 1; + } +} + +# If it's in the list of terminal types not to color, or if +# we're writing to something that's not a tty, don't do color. +if (! -t STDOUT || $commit == 1 || $nocolor{$terminal}) +{ + exec $cvsPath, @ARGV + or die("Couldn't exec"); +} + +# Keep the pid of the cvs process so we can get its return +# code and use that as our return code. +$cvs_pid = open3('<&STDIN', \*CVSOUT, \*CVSOUT, $cvsPath, @ARGV); +$cvsName = $cvsPath; +$cvsName =~ s,.*/(.*)$,\1,; + +# Colorize the output from the cvs program. +while() +{ + chomp; + if (m/^(.) .+/) # S filename + { + print($colors{$1}, $_, color("reset")); + } + elsif (m/warning:/) # warning + { + print($colors{"warning"}, $_, color("reset")); + } + elsif (m/^$cvsName[^:]*: / || m/^cvs server: /) # server message + { + print($colors{"server"}, $_, color("reset")); + } + else # Anything else + { + # Print normally. + print(color("reset"), $_); + } + print "\n"; +} + +# Get the return code of the cvs program and exit with that. +waitpid($cvs_pid, 0); +exit ($? >> 8); diff --git a/scripts/colorcvsrc-sample b/scripts/colorcvsrc-sample new file mode 100644 index 00000000..3b8eeef9 --- /dev/null +++ b/scripts/colorcvsrc-sample @@ -0,0 +1,36 @@ +# Sample .colorcvsrc +# These are the defaults + +# path to the cvs binary +cvs: /usr/bin/cvs + +# Don't do color if our terminal type ($TERM) is one of these. +# (List all terminal types on one line, seperated by whitespace.) +nocolor: dumb + +# The following groups of attributes may be combined for a given color: +# +# clear black on_black +# reset red on_red +# bold green on_green +# underline yellow on_yellow +# underscore blue on_blue +# blink magenta on_magenta +# reverse cyan on_cyan +# concealed white on_whit + +# colors for different types of status messages + +P: reset +U: reset +C: bold red +M: bold yellow +A: cyan +R: cyan +?: bold + +# this is for server messages +server: bold green + +# this is for warnings +warning: bold cyan diff --git a/scripts/colorsvn b/scripts/colorsvn new file mode 100755 index 00000000..f89e1c2f --- /dev/null +++ b/scripts/colorsvn @@ -0,0 +1,201 @@ +#! /usr/bin/env perl + +# colorsvn +# +# based on colorgcc +# +# Requires the ANSIColor module from CPAN. +# +# Usage: +# +# In a directory that occurs in your PATH _before_ the directory +# where svn lives, create a softlink to colorsvn: +# +# svn -> colorsvn +# +# That's it. When "svn" is invoked, colorsvn is run instead. +# +# The default settings can be overridden with ~/.colorcvsrc. +# See the colorcvsrc-sample for more information. +# +# Note: +# +# colorsvn will only emit color codes if: +# +# (1) tts STDOUT is a tty. +# (2) the value of $TERM is not listed in the "nocolor" option. +# (3) the svn command is not a commit or import (as the text editor +# opened by svn will often be hampered by colorsvn). +# +# If colorsvn colorizes the output, svn's STDERR will be +# combined with STDOUT. Otherwise, colorsvn just passes the output from +# svn through without modification. +# +# Copyright 2002 Neil Stevens +# +# Copyright 1999 Jamie Moyers +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + +use Term::ANSIColor; +use IPC::Open3; + +sub initDefaults +{ + $svnPath = "/usr/bin/svn"; + + $nocolor{"dumb"} = "true"; + + $colors{"P"} = color("reset"); + $colors{"U"} = color("reset"); + $colors{" "} = color("reset"); + $colors{"C"} = color("bold red"); + $colors{"M"} = color("bold yellow"); + $colors{'G'} = color("bold yellow"); + $colors{"A"} = color("cyan"); + $colors{"R"} = color("cyan"); + $colors{"D"} = color("red"); + $colors{"I"} = color("bold"); + $colors{"?"} = color("bold"); + $colors{"!"} = color("bold"); + $colors{"~"} = color("bold red"); + $colors{"server"} = color("bold green"); + $colors{"warning"} = color("bold cyan"); + + # Applies when only the properties changed + $propcolors{"C"} = color("bold red"); + $propcolors{"M"} = color("yellow"); +} + +sub loadPreferences +{ +# Usage: loadPreferences("filename"); + + my($filename) = @_; + + open(PREFS, "<$filename") || return; + + while() + { + next if (m/^\#.*/); # It's a comment. + next if (!m/(.*):\s*(.*)/); # It's not of the form "foo: bar". + + $option = $1; + $value = $2; + + if ($option =~ /svn/) + { + $svnPath = $value; + } + elsif ($option eq "nocolor") + { + # The nocolor option lists terminal types, separated by + # spaces, not to do color on. + foreach $termtype (split(/\s+/, $value)) + { + $nocolor{$termtype} = "true"; + } + } + elsif ($option =~ /prop (.)/) + { + # Property color + $propcolors{$1} = color($value); + } + else + { + $colors{$option} = color($value); + } + } + close(PREFS); +} + +# +# Main program +# + +# Set up default values for colors and svn path. +initDefaults(); + +# Read the configuration file, if there is one. +$configFile = $ENV{"HOME"} . "/.colorcvsrc"; +if (-f $configFile) +{ + loadPreferences($configFile); +} + +# Get the terminal type. +$terminal = $ENV{"TERM"} || "dumb"; + +$commit = 0; +foreach (@ARGV) +{ + if(/^ci$/ || /^commit$/ || /^import$/ || /^prop/ || /^p[delsg]$/) + { + $commit = 1; + break; + } + elsif (! /^-/) + { + break; + } +} + +# If it's in the list of terminal types not to color, or if +# we're writing to something that's not a tty, don't do color. +if (! -t STDOUT || $commit == 1 || $nocolor{$terminal}) +{ + exec $svnPath, @ARGV + or die("Couldn't exec"); +} + +-f $svnPath or die ("$svnPath not found, add svn=/full/path/to/svn to ~/.colorcvsrc"); + +# Keep the pid of the svn process so we can get its return +# code and use that as our return code. +$svn_pid = open3('<&STDIN', \*SVNOUT, \*SVNOUT, $svnPath, @ARGV); +$svnName = $svnPath; +$svnName =~ s,.*/(.*)$,\1,; + +# Colorize the output from the svn program. +while() +{ + chomp; + if (m/^ (.).+/) # Property changed only + { + print($propcolors{$1}, $_, color("reset")); + } + elsif (m/^(.).+/) # S filename + { + print($colors{$1}, $_, color("reset")); + } + elsif (m/warning:/) # warning + { + print($colors{"warning"}, $_, color("reset")); + } + elsif (m/^$svnName[^:]*: / || m/^svn server: /) # server message + { + print($colors{"server"}, $_, color("reset")); + } + else # Anything else + { + # Print normally. + print(color("reset"), $_); + } + print "\n"; +} + +# Get the return code of the svn program and exit with that. +waitpid($svn_pid, 0); +exit ($? >> 8); diff --git a/scripts/completions/bash/dcop b/scripts/completions/bash/dcop new file mode 100644 index 00000000..675bf6cf --- /dev/null +++ b/scripts/completions/bash/dcop @@ -0,0 +1,52 @@ +# dcop completion +# +# Inputs: +# $1 -- name of the command whose arguments are being completed +# $2 -- word being completed +# $3 -- ord preceding the word being completed +# $COMP_LINE -- current command line +# $COMP_PONT -- cursor position +# $COMP_WORDS -- array containing individual words in the current +# command line +# $COMP_CWORD -- index into ${COMP_WORDS} of the word containing the +# current cursor position +# Output: +# COMPREPLY array variable contains possible completions +# +# dcop syntax: +# dcop [ application [object [function [arg1] [arg2] [arg3] ... ] ] ] +# +_complete_dcop () +{ + local wordlist + + COMPREPLY=() + wordlist="" + + if (( $COMP_CWORD == 1 )); then + # + # Application. This one is easy, just return all names that dcop + # gives us. + # + wordlist=$(dcop) + elif (( $COMP_CWORD == 2 )); then + # + # Object. 'dcop ' returns all objects the application + # supports plus (default). The parenthesis in (default) should be + # omitted when using it as an argument so we need to remove them. + # + wordlist=$(dcop ${COMP_WORDS[1]} | sed -e "s,(default),default,") + elif (( $COMP_CWORD == 3 )); then + # + # Function. 'dcop ' returns functions of the + # form 'type functionname(arguments)'. We need to return a list with + # the functionnames. + # + wordlist=$(dcop ${COMP_WORDS[1]} ${COMP_WORDS[2]} | sed -e "s,.* \(.*\)(.*,\1,") + fi + + COMPREPLY=( $(compgen -W "$wordlist" "$2") ) + return 0 +} + +complete -F _complete_dcop dcop diff --git a/scripts/completions/zsh/_dcop b/scripts/completions/zsh/_dcop new file mode 100644 index 00000000..c3fe6a50 --- /dev/null +++ b/scripts/completions/zsh/_dcop @@ -0,0 +1,11 @@ +#compdef dcop + +local tmp + +if [ CURRENT -eq 4 ]; then + tmp=(`$words[1,CURRENT-1] 2>/dev/null | sed -e "s,.* \(.*\)(.*,\1,"`) +else + tmp=(`$words[1,CURRENT-1] 2>/dev/null | sed -e "s,(default),default,"`) +fi + +compadd -a tmp diff --git a/scripts/completions/zsh/_kcmshell b/scripts/completions/zsh/_kcmshell new file mode 100644 index 00000000..57f9fc82 --- /dev/null +++ b/scripts/completions/zsh/_kcmshell @@ -0,0 +1,16 @@ +#compdef kcmshell=kcmshell appletproxy=appletproxy + +local i resource tmp dir flags +if [ "$service" = "kcmshell" ]; then + resource="apps"; + dir="/Settings"; + flags=":t:r"; +else + resource="data"; + dir="/kicker/applets"; + flags=":t" +fi +for i in `kde-config --path $resource| sed -e 's/:/ /g'`; do + tmp=($i/$dir/**/*.desktop($flags)) + compadd -a tmp +done diff --git a/scripts/completions/zsh/_kdeinit_wrapper b/scripts/completions/zsh/_kdeinit_wrapper new file mode 100644 index 00000000..a5fd75a0 --- /dev/null +++ b/scripts/completions/zsh/_kdeinit_wrapper @@ -0,0 +1,6 @@ +#compdef kdeinit_wrapper + +# Yes, this could be improved + +_arguments \ + '*::arguments: _normal' diff --git a/scripts/completions/zsh/_kdekillall b/scripts/completions/zsh/_kdekillall new file mode 100644 index 00000000..16ab40dc --- /dev/null +++ b/scripts/completions/zsh/_kdekillall @@ -0,0 +1,8 @@ +#compdef kdekillall + +local progs +progs=(`ps x | grep kdeinit: | grep -v Running | grep -v grep | sed 's,.*kdeinit: ,,' | sed 's, .*,,'`) + +_alternative \ + 'signals:: _signals -p' \ + 'compadd $progs' diff --git a/scripts/completions/zsh/_makeobj b/scripts/completions/zsh/_makeobj new file mode 100644 index 00000000..738a952c --- /dev/null +++ b/scripts/completions/zsh/_makeobj @@ -0,0 +1,30 @@ +#compdef makeobj + +local index olddir dir subdir + +olddir=$PWD +index="$words[(I)-[fCI]]" +if ! ((index)); then + if [ ! -f Makefile ]; then + if [ -n "$OBJ_SUBDIR" ]; then + dir=$PWD + subdir=. + while [ -n "$dir" -a $dir != '/' -a ! -f $dir/$OBJ_SUBDIR/$subdir/Makefile ]; do + dir=$dir(:h) + subdir=$dir(:t)/$subdir + done + if -f $dir/$OBJ_SUBDIR/$subdir/Makefile; then + cd $dir/$OBJ_SUBDIR/$subdir + fi + elif [ -n "$OBJ_REPLACEMENT" ]; then + dir=$(echo $PWD | sed -e "$OBJ_REPLACEMENT") + if [ -f $dir/Makefile ]; then + cd $dir + fi + fi + fi +fi + +_make + +cd $olddir diff --git a/scripts/create_cvsignore b/scripts/create_cvsignore new file mode 100755 index 00000000..2fb4c23e --- /dev/null +++ b/scripts/create_cvsignore @@ -0,0 +1,55 @@ +#!/bin/sh +# This script makes a preliminary .cvsignore in the current dir by +# adding some standard stuff according to Makefile.am. +# License: GPL + +addignore() { + test -f .cvsignore || \ + ( touch .cvsignore && echo "created new .cvsignore" && cvs add .cvsignore ) + grep -q "^$1\$" .cvsignore || \ + ( echo "$1" >> .cvsignore && echo "added $1 to .cvsignore" ) +} + +recurse=0 +if test $# -eq 1; then + if test "$1" = "-r"; then + recurse=1 + fi +fi + +handledir() { + ( + cd $1 + if test -f Makefile.am; then + if test $recurse -eq 1; then + echo "Entering $1" + fi + addignore Makefile + addignore Makefile.in + + bins=`perl -p -e 's/\\\s*\n/ /g' Makefile.am | grep _PROGRAMS | sed -e 's/.*=\s*//;s/#.*//;s/\$([^)]*)//'` + if test -n "$bins"; then + for prog in $bins; do + addignore "$prog" + done + fi + else + echo "Skipping $1" + fi + ) +} + + +if test -f Makefile.am; then + if test $recurse -eq 1; then + find . -type d | grep -v CVS | sed -e 's,/$,,' | \ + while read dir; do + handledir $dir + done + else + handledir . + fi +else + echo "No Makefile.am found!" +fi + diff --git a/scripts/create_makefile b/scripts/create_makefile new file mode 100755 index 00000000..e83b61d7 --- /dev/null +++ b/scripts/create_makefile @@ -0,0 +1,110 @@ +#!/usr/bin/env bash + +# Create Makefile.in and Makefile in a directory (containing a Makefile.am !) +# Saves time compared to re-running configure completely + +if [ $# -ne 1 ]; then + echo "$0 : creates a Makefile from a Makefile.am" + echo + echo "Usage : $0 relativepath/Makefile" + echo "So the argument is the file you want to create." + echo +else + if test -f config.status && test -f configure; then + : + else + if test ! -f Makefile && test -n "$OBJ_SUBDIR"; then + cd $OBJ_SUBDIR + else + if test ! -f Makefile && test -n "$OBJ_REPLACEMENT"; then + objdir=`pwd | sed -e "$OBJ_REPLACEMENT"` + cd $objdir + fi + fi + + if test ! -f Makefile && test -n "$OBJ_SUBDIR"; then + cd $OBJ_SUBDIR + fi + + if test ! -f Makefile; then + echo "$0: in the current directory there is no Makefile" + echo "you will have to run it from the top build dir." + echo "if you do not have a Makefile there - rerun configure" + exit + fi + + fi + + # Handle arg with missing "/Makefile" + relpath=$1 + if test -n "`echo $relpath | grep \/$`"; then + relpath=`echo $relpath | sed 's/\/$//'` + fi + if test -z "`echo $relpath | grep 'Makefile$'`"; then + relpath="$relpath/Makefile" + fi + + # Strip leading ./, otherwise config.status chokes + relpath=`echo "$relpath" | sed -e 's,^\./,,'` + + # Go up to toplevel dir + while test ! -f config.status; do + relpath="`basename $PWD`/$relpath" + cd .. + done + + # Find out top_srcdir. + top_srcdir=`egrep '^top_srcdir *=' Makefile | sed -e "s#top_srcdir *= *##"` + + ( + if cd $top_srcdir ; then + # Check if unsermake or automake was used to create the toplevel Makefile.in + # (the one in $relpath might not exist yet) + if test -n "`sed -n -e '1p' Makefile.in | grep unsermake`"; then + if test -n "`sed -n -e '1p' Makefile.in | grep automake`"; then # old unsermake + if test -z "$UNSERMAKE"; then + echo "unsermake was used to build this module, but \$UNSERMAKE isn't set!" + exit 1 + fi + $UNSERMAKE $relpath + exit 2 + else # new unsermake + UNSERMAKE=`type -p unsermake` + if test ! -x "$UNSERMAKE"; then + echo 'Makefile was created with unsermake, but there' + echo 'is no unsermake in $PATH' + exit 1 + fi + $UNSERMAKE -c $relpath + fi + else + # Suck in AUTOCONF/AUTOMAKE detection code + UNSERMAKE=no + eval `admin/detect-autoconf.pl` + /bin/sh admin/missing --run $AUTOMAKE $relpath || exit + if test -f admin/am_edit; then perl admin/am_edit $relpath.in ;\ + else + if test -f ../admin/am_edit; then perl ../admin/am_edit $relpath.in ; \ + fi + fi + fi + fi + ) + case $? in + 1) + exit 1 + ;; + 2) + createrulesfile="true" + ;; + *) + ;; + esac + if test -f `dirname $relpath`; then + rm `dirname $relpath` + fi + CONFIG_FILES=$relpath CONFIG_HEADERS= ./config.status + if test "$createrulesfile" = "true"; then + CONFIG_FILES="$relpath.rules $relpath.calls" CONFIG_HEADERS= ./config.status + fi +fi diff --git a/scripts/create_makefiles b/scripts/create_makefiles new file mode 100755 index 00000000..955edb90 --- /dev/null +++ b/scripts/create_makefiles @@ -0,0 +1,34 @@ +#!/bin/sh +# Usage : create_makefiles dir +# (to be run from toplevel directory) +# Will re-create all Makefiles in dir and its subdirs +# Needs create_makefile in the path. +# +# David Faure + +if test ! -f Makefile && test -n "$OBJ_REPLACEMENT"; then + objdir=`pwd | sed -e "$OBJ_REPLACEMENT"` + cd $objdir +fi + +if test ! -f Makefile && test -n "$OBJ_SUBDIR"; then + cd $OBJ_SUBDIR +fi + +if test ! -f Makefile; then + echo "$0: in the current directory there is no Makefile" + echo "you will have to run it from the top build dir." + echo "if you do not have a Makefile there - rerun configure" + exit +fi + +srcdir=`egrep '^srcdir *=' Makefile | sed -e "s#srcdir *= *##"` + +( cd $srcdir ; find $1 -type d | sed -e 's,/$,,' ) | \ + while read a; do + if test -f "$srcdir/$a/Makefile.am"; then + test -d "$a" || mkdir -p "$a" + create_makefile "$a/Makefile" + fi + done + diff --git a/scripts/create_svnignore b/scripts/create_svnignore new file mode 100755 index 00000000..fed482fa --- /dev/null +++ b/scripts/create_svnignore @@ -0,0 +1,82 @@ +#!/bin/sh +# This script makes a preliminary svn:ignore in the current dir by +# adding some standard stuff according to Makefile.am. +# License: GPL + +addignore() { + if ! test -f svnignore.tmp; then + svn pg svn:ignore . | sed -e "s, *,," | grep -v '^$' > svnignore.tmp + addedsomething=0 + fi + if ! grep -q "^$1\$" svnignore.tmp; then + echo "$1" >> svnignore.tmp && echo "added $1 to svn:ignore" + sort -u -o svnignore.tmp svnignore.tmp + addedsomething=1 + fi +} + +recurse=0 +if test $# -eq 1; then + if test "$1" = "-r"; then + recurse=1 + fi +fi + +handledir() { + ( + cd $1 + trap "rm svnignore.tmp" 1 2 15 + if test -f Makefile.am; then + if test $recurse -eq 1; then + echo "Entering $1" + fi + addignore Makefile + addignore Makefile.in + + bins=`perl -p -e 's/\\\s*\n/ /g' Makefile.am | egrep '_PROGRAMS|_LTLIBRARIES|_LIBRARIES' | sed -e 's/.*=\s*//;s/#.*//;s/\$([^)]*)//'` + if test -n "$bins"; then + addignore ".libs" + addignore ".deps" + for prog in $bins; do + addignore "$prog" + done + fi + grep -q LIBRARIES Makefile.am && addignore ".libs" + grep -q METASOURCES Makefile.am && addignore "*.moc" + fgrep -q .skel Makefile.am && addignore "*.kidl" + fgrep -q .skel Makefile.am && addignore "*_skel.c*" + fgrep -q .stub Makefile.am && addignore "*_stub.cpp" + if fgrep -q .ui Makefile.am; then + uis=`ls -1 *.ui 2>/dev/null` + for ui in $uis; do + addignore ${ui/.ui/.h} + addignore ${ui/.ui/.cpp} + done + fi + + grep -q "^include.*/Doxyfile.am$" Makefile.am && addignore "Doxyfile" + + if test "$addedsomething" = 1; then + svn propset svn:ignore -F svnignore.tmp . + fi + rm svnignore.tmp + else + echo "Skipping $1" + fi + ) +} + + +if test -f Makefile.am; then + if test $recurse -eq 1; then + find . -type d | egrep -v 'CVS|.svn' | sed -e 's,/$,,' | \ + while read dir; do + handledir $dir + done + else + handledir . + fi +else + echo "No Makefile.am found!" +fi + diff --git a/scripts/cvs-clean b/scripts/cvs-clean new file mode 100755 index 00000000..f6ae254d --- /dev/null +++ b/scripts/cvs-clean @@ -0,0 +1,90 @@ +#! /usr/bin/env perl + +# +# This script recursively (beginning with the current directory) +# wipes out everything not registered in CVS. +# +# written by Oswald Buddenhagen +# inspired by the "old" cvs-clean target from Makefile.common +# +# This file is free software in terms of the modified BSD licence. +# See kdelibs/doc/common/bsd-license.html for the exact conditions. +# + +use File::Path; + +my $dry_run = 0; + +sub newfiles() +{ + my ($indir, $incvs) = @_; + for my $n (keys (%$incvs)) { delete $$indir{$n} } + return sort (keys (%$indir)); +} + +sub cvsclean() +{ + my $dir = shift; + my (%dirsdir, %filesdir, %dirscvs, %filescvs); + my $dnam = $dir ? $dir : "."; + if (!opendir (DIR, $dnam)) { + print STDERR "Cannot enter \"".$dnam."\".\n"; + return; + } + for my $fn (grep (!/^\.\.?$/, readdir (DIR))) { + if (-d $dir.$fn) { + $fn eq "CVS" or $dirsdir{$fn} = 1; + } else { + $filesdir{$fn} = 1; + } + } + closedir (DIR); + if (!open (FILE, "<".$dir."CVS/Entries")) { + print STDERR "No CVS information in \"".$dnam."\".\n"; + return; + } + while () { + m%^D/([^/]+)/.*$% and $dirscvs{$1} = 1; + m%^/([^/]+)/.*$% and $filescvs{$1} = 1; + } + close (FILE); + if (open (FILE, "<".$dir."CVS/Entries.Log")) { + while () { + m%^A D/([^/]+)/.*$% and $dirscvs{$1} = 1; + m%^A /([^/]+)/.*$% and $filescvs{$1} = 1; + m%^R D/([^/]+)/.*$% and delete $dirscvs{$1}; + m%^R /([^/]+)/.*$% and delete $filescvs{$1}; + } + close (FILE); + } + for my $fn (&newfiles (\%filesdir, \%filescvs)) { + print ("F ".$dir.$fn."\n"); + unlink($dir.$fn) unless $dry_run; + } + for my $fn (&newfiles (\%dirsdir, \%dirscvs)) { + print ("D ".$dir.$fn."\n"); + rmtree($dir.$fn, 0, 0) unless $dry_run; + } + for my $fn (sort (keys (%dirscvs))) { + &cvsclean ($dir.$fn."/"); + } +} + +my $usage = + "usage: cvs-clean [options]\n". + " --help | -h print usage information\n". + " --dry-run | -n print intended actions; don't change filesystem\n"; + +foreach my $arg (@ARGV) { + if ($arg eq '-h' || $arg eq '--help') { + print $usage; + exit (0); + } elsif ($arg eq '-n' || $arg eq '--dry-run') { + $dry_run = 1; + } else { + print STDERR "cvs-clean: unknown argument '".$arg."'\n\n".$usage; + exit (1); + } +} + +&cvsclean (""); diff --git a/scripts/cvs2dist b/scripts/cvs2dist new file mode 100755 index 00000000..dba8a4d9 --- /dev/null +++ b/scripts/cvs2dist @@ -0,0 +1,626 @@ +#! /usr/bin/env bash + +# This is cvs2dist +# Webpage: http://www.katzbrown.com/shiritsu/programming/cvs2dist/ +# Newest version is always available there! +# Please report bugs to . + +# Original author (of cvs2pack.sh) was Sebastian Stein +# Heavy, heavy modifications by Jason Katz-Brown +# Some modifications for i18n inclusion by Dominique Devriese +# Added --no-i18n-lang and --replace-files by Michael Buesch +# License: GPL (http://www.gnu.org/) +# Last modification: 2004/10/13 + +cmdline="$@" + +returndir=`pwd` +override="README ChangeLog INSTALL AUTHORS AUTHOR COPYING COPYING.LIB TODO" +remove="config.cache config.log config.status Makefile configure inst-apps CVS acinclude.m4 aclocal.m4 config.h config.h.bot config.h.in configure.files libtool stamp-h stamp-h.in stamp-h1 subdirs *.moc *.la .libs .deps .cvsignore autom4te.cache {arch} .arch-ids" +toplevelremove="configure.in.bot" +# whitespace seperated list of languages to never include. +always_skip_languages="xx" + + +exit_cleanup() +{ + echo -n "Cleaning up... " + if [ -d $temp_dir ]; then + test -z "$debug" && rm -Rf $temp_dir + fi + echo "done." +} + +trap "exit_cleanup; exit 1" SIGINT SIGTERM + +test -e ~/.cvs2distrc && extraoptions=`cat ~/.cvs2distrc` + +# getopt usage from the getopt bash example +# --log has optional argument +TEMP=`getopt \ +-o v:n:r:e:a:B:dhmbgol \ +--long log::,version:,name:,required-header:,required-header-error-message:,make-unpackaged,help,no-bz2,no-bzip2,no-gzip,only-directory,remove-hidden,admin-dir:,branch:,no-i18n,no-i18n-lang:,cvs-root:,debug,replace-files: \ +-n cvs2dist -- $extraoptions "$@"` + +if [ $? != 0 ]; then + echo "Aborted." >&2 + exit 1 +fi + +eval set -- "$TEMP" + +log="/dev/null" +calclog=0 +doi18n="yes" +noi18nlang="" +replace_files="" +cvsroot="" +debug="" + +while true; do + case "$1" in + -v|--version) version=$2; shift 2 ;; + -n|--name) name=$2; shift 2 ;; + -a|--admin-dir) admindir=$2; + if [ `echo $admindir | sed -e 's#^/##'` = $admindir ]; then + admindir="`pwd`/$admindir" + fi + shift 2 ;; + -B|--branch) branch="-r$2"; shift 2;; + --debug) debug="non-empty"; shift 1 ;; + --cvs-root) cvsroot="$2"; shift 2 ;; + -r|--required-header) requiredheader=$2; shift 2 ;; + --no-i18n) doi18n="no"; shift 1 ;; + --no-i18n-lang) noi18nlang="$2"; shift 2 ;; + -e|--required-header-error-message) requiredmsg=$2; shift 2 ;; + -m|--make-unpackaged) leavedir="non-empty"; shift 1 ;; + -h|--help) showhelp="non-empty"; shift 1 ;; + -b|--no-bz2|--no-bzip2) nobzip2="non-empty"; shift 1 ;; + -o|--only-directory) + nogzip="non-empty" + nobzip2="non-empty" + leavedir="non-empty" + shift 1 ;; + -g|--no-gzip) nogzip="non-empty"; shift 1 ;; + -d|--remove-hidden) removehidden="non-empty"; shift 1 ;; + -l) calclog=1; shift 1 ;; + --log) + case "$2" in + # no-argument case + "") calclog=1; shift 2 ;; + # something, should be a file + *) log=$2 + origlog=$log + if [ `echo $log | sed -e 's#^/##'` = $log ]; then + log="`pwd`/$log" + fi + + shift 2 ;; + esac ;; + --replace-files) replace_files="$2"; shift 2 ;; + --) shift + break ;; + *) echo "Aborted." + exit 1 ;; + esac +done + +count=0 +for arg do + test $count = 0 && module=$arg + test $count = 1 && directory=$arg + + if [ $count -ge 2 ]; then + modarg=$arg + if [ `echo $modarg | sed -e 's#^/##'` = $modarg ]; then + modarg="`pwd`/$modarg" + fi + addfiles="$addfiles $modarg" + fi + + let count count++ +done + +# test if a module and directory name was given +if [ ! -z $showhelp ] || [ -z $module ] || [ -z $directory ]; then + echo "Usage: cvs2dist module directory [options] [addfile1] [addfile2] ..." + echo "" + echo " -n --name " + echo " -v --version " + echo " --cvs-root the value to use as cvs root" + echo " variable. It is not necessary if you use --no-i18n." + echo " if this is not given, it is taken from the CVSROOT " + echo " environment variable." + echo " --admin-dir admin/ location (default is module/admin)" + echo " (symbolic links are OK.)" + echo " -B --branch use branch for i18n checkouts." + echo " --no-i18n don't try to automatically checkout the translations from cvs." + echo " --no-i18n-lang exclude all languages in the given comma" + echo " seperated list. example:" + echo " --no-i18n-lang uk,de,en_GB" + echo " --log= If logfile unspecified, default file is used." + echo " (The '=' is essential and may not be ommited" + echo " when specifying the logfile.)" + echo " -l Log to default logfile." + echo " -m --make-unpackaged Also makes an unpacked distribution" + echo " in the current directory." + echo " -g --no-gzip Do not create gzip package." + echo " -b --no-bz2 Do not create bzip2 package." + echo " --no-bzip2 Alias for the above." + echo " -o --only-directory Alias for -mbg (no packages, only a directory.)" + echo " -r --required-header
    Errors if header is not installed." + echo " -e --required-header-error-message " + echo " Error message for above." + echo " -d --remove-hidden Remove hidden files/directories from packages" + echo " --replace-files " + echo " is a comma separated list of file pairs" + echo " which should be replaced in the final distribution package." + echo " Each element of a pair is separated by an @" + echo -n " Example: --replace-files take_this_file@and_move_it_here," + echo "configure.in.bot.dist@configure.in.bot" + echo " The filenames are all relative to your package root." + echo " Please be careful! Try to avoid the usage of .. in the path. It" + echo " may break stuff! There can not be blank characters in the paths." + echo " -h --help Show this help" + echo "" + echo " Name defaults to the last part of the directory." + echo "By default, creates name[-version].tar.gz if gzip is installed" + echo "and/or name[-version].tar.bz2 if bzip2 is installed in the current" + echo "directory. Removes all temporary files it creates. Produces" + echo "tarballs that are standard distribution tarballs with a" + echo "configure script." + echo " Unless --no-i18n is given, it automatically tries to check out " + echo "strings and documentation translation from cvs." + echo " Arguments after the second are added to the toplevel directory" + echo "in the package. Short options can be combined, eg -dm to create" + echo "a directory and remove hidden files. ~/.cvs2distrc is added to" + echo "the beginning of command line arguments if it exists." + echo " '--' signifies the end of options." + echo "" + echo "Example: cvs2dist /sources/kdegames kolf/objects/picture \\" + echo " -n kolf-picture -v 0.9 -r \"kolf/game.h\" \\" + echo " --log ~/tmp/extra-file" + echo "" + echo " Creates packages of the kolf picture plugin, naming the" + echo " packages kolf-picture-0.9 and logging the process. For configure" + echo " to succeed, kolf/game.h must be installed or an error will occur." + echo " ~/tmp/extra-file is added to the packages." + echo "" + echo "Webpage: http://www.katzbrown.com/shiritsu/programming/cvs2dist/" + echo "Authors: Jason Katz-Brown ," + echo " Sebastian Stein ," + echo " Dominique Devriese " + exit 1 +fi + +# expand module +module=`echo $module | sed -e 's#/$##'` +if [ `echo $module | sed -e 's#^/##'` = $module ]; then + module="`pwd`/$module" +fi + +# test if the given module is a directory +test -d $module +if [ $? -ne 0 ]; then + echo "$module is not a directory." + echo "Aborted." + exit 1 +fi + +# go to our module +cd $module + +directory=`echo $directory | sed -e 's#^/##'` + +if [ -z $name ]; then + name=$directory + # get rid of trailing slash + name=`echo $name | sed -e 's#/$##'` + # leading slash + name=`echo $name | sed -e 's#^/##'` + + if [ `echo $name | sed -e 's#/##'` != $name ]; then + name=`echo $name | sed -e 's#^.*/##'` + fi +fi + +test $calclog = 1 && log="$returndir/cvs2dist-$name.log" && origlog="cvs2dist-$name.log" + +# test if the given name is a sub-directory +if [ ! -d "$directory" ]; then + echo "$directory is not a sub-directory of $module." + echo "Aborted." + exit 1 +fi + +test $log != "/dev/null" && echo "Logging to $log." + +echo "cvs2dist log on "`date` > $log +echo "http://katzbrown.com/shiritsu/programming/cvs2dist/" >> $log +echo -n "Module: $module; Directory: $directory; Name: $name; " >> $log +if [ -z $version ]; then + echo "Version unspecified." >> $log +else + echo "Version $version." >> $log +fi +echo $cmdline >> $log +echo "--------" >> $log + +if [ -z $version ]; then + filename=$name +else + filename="$name-$version" +fi + +if [ -z $cvsroot ]; then + cvsroot="$CVSROOT"; +fi +if [ $doi18n = "yes" ]; then + # a little warning... + echo "Will try to fetch i18n files from KDE's CVS." + echo "If this doesn't work, use --no-i18n." + + if [ -z "$cvsroot" ]; then + echo "No cvs root specified, CVSROOT env var is empty, " >> $log + echo "and --no-i18n option is not given.." >> $log + echo "Using anonymous cvs.." >> $log + cvsroot=":pserver:anonymous@anoncvs.kde.org:/home/kde" + # append an entry to ~/.cvspass so the user will not be asked + # for a pwd. Thanks to coolo for the idea.. + if ! grep "anoncvs.kde.org" ~/.cvspass >/dev/null 2>&1; then + echo "/1 :pserver:anonymous@anoncvs.kde.org:2401/home/kde A" >> ~/.cvspass + fi + fi +fi + +# the temporary directory +temp_dir="$module/cvs2dist-tmp" +temp_dist="$temp_dir/$filename" + +echo "Temporary directory is $temp_dir." >> $log + +# make a temporary directory +test -d $temp_dir && rm -Rf $temp_dir + +mkdir -p $temp_dist + +# check if we were able to create temp_dir +test -d $temp_dist +if [ $? -ne 0 ]; then + echo "Could not create temporary directory $temp_dist." + echo "$temp_dist could not be created." >> $log + echo "Aborted." + exit 1 +fi + +test -z "$requiredmsg" && requiredmsg="Install development package needed first! $requiredheader, for one, is missing." + +### configure.in.in +if [ ! -z $requiredheader ]; then + naiyou="KDE_CHECK_HEADER( +$requiredheader, +[], +[AC_MSG_ERROR(\"$requiredmsg\")] +)" + echo $naiyou > $temp_dir/configure.in.in + echo "configure.in.in contents: " >> $log + echo "--------" >> $log + echo $naiyou >> $log + appaddfiles="$appaddfiles $temp_dir/configure.in.in" +fi + +# copy all files of the module to temp_dir/name +cp -RL $module/$directory $temp_dist/$name + +echo "cp -R $module/$directory $temp_dist/$name" >> $log + +modulename="$module" +# get rid of trailing slash +modulename=`echo $modulename | sed -e 's#/$##'` +# get the last part +modulename=`echo $modulename | sed -e 's#^.*/##'` + +remove="$remove $modulename.lsm" + +# we check out kde-i18n/subdirs in temp_dir/kde-i18n.. +if [ $doi18n = "yes" ]; then + pushd $temp_dir + echo "cvs co kde-i18n/subdirs" >> $log + cvs -z4 -q -d "$cvsroot" co $branch -P kde-i18n/subdirs > /dev/null 2>&1 + i18nlangs_tmp="$(cat kde-i18n/subdirs)" + skiplist="`echo $noi18nlang | sed -e 's/,/ /g'`" + skiplist="$skiplist $always_skip_languages" + for lang in $i18nlangs_tmp; do + must_skip="no" + for skip in $skiplist; do + if [ "$lang" = "$skip" ]; then + must_skip="yes" + fi + done + if [ "$must_skip" = "no" ]; then + i18nlangs="$i18nlangs $lang" + fi + done + echo "available languages: $i18nlangs" >> $log + popd +fi + +# if a handbook exists, we also copy it to the directory +if [ -d $module/doc/$name ]; then + mkdir -p $temp_dist/doc/$name + cp -Rf $module/doc/$name $temp_dist/doc + find $module/doc/ -maxdepth 1 ! -xtype d | xargs --replace={} cp {} $temp_dist/doc + + if [ $doi18n = "yes" ]; then + pushd $temp_dir + for lang in $i18nlangs; do + test -d $temp_dist/doc/$lang && rm -Rf $temp_dist/doc/$lang + docdirname="kde-i18n/$lang/docs/$modulename/$name" + echo "cvs co $docdirname" >> $log + cvs -z4 -q -d "$cvsroot" co $branch -P "$docdirname" > /dev/null 2>&1 + if [ ! -d "$docdirname" ]; then + echo "$lang's $name documentation does not exist." >> $log + continue + fi + echo -n "Copying $lang's $name documentation over... " + cp -R $docdirname $temp_dist/doc/$lang + # we don't want KDE_DOCS = AUTO, cause that makes the + # build system assume that the name of the app is the + # same as the name of the dir the Makefile.am is in. + # Instead, we explicitly pass the name.. + echo "KDE_LANG=$lang" > $temp_dist/doc/$lang/Makefile.am + echo "KDE_DOCS=$name" >> $temp_dist/doc/$lang/Makefile.am + echo "done." + echo "$lang documentation included." >> $log + done + popd + fi +fi + +# clean up doc directory +if [ -d $temp_dist/doc/$name ]; then + pushd $temp_dist/doc/$name + echo -n "make clean in $temp_dist/doc/$name/... " + echo >> $log + echo "make clean in $temp_dist/doc/$name/" >> $log + make clean >> $log + echo "--------" >> $log + echo "done." + popd +fi + +if [ $doi18n = "yes" ]; then + test -d $temp_dist/po && rm -Rf $temp_dist/po + mkdir $temp_dist/po + + pushd $temp_dir/ + for lang in $i18nlangs; do + pofilename="kde-i18n/$lang/messages/$modulename/$name.po"; + echo "cvs co $pofilename" >> $log + cvs -z4 -q -d "$cvsroot" co $branch -P "$pofilename" > /dev/null 2>&1 + if [ ! -f "$pofilename" ]; then + echo "$lang's $name.po does not exist." >> $log + continue + fi + + dest=$temp_dist/po/$lang + mkdir $dest + echo -n "Copying $lang's $name.po over... " + echo "$lang's $name.po file included." >> $log + cp $pofilename $dest + echo "done." + + echo "KDE_LANG = $lang +SUBDIRS = \$(AUTODIRS) +POFILES = AUTO" > $dest/Makefile.am + + subdirs="non_empty" + done + + if [ -z "$subdirs" ]; then + rm -Rf $temp_dist/po + else + echo "SUBDIRS = \$(AUTODIRS)" > $temp_dist/po/Makefile.am + fi +fi + +# copy the admin directory +if [ -z $admindir ]; then + cp -pRL $module/admin $temp_dist + echo "cp -pRL $module/admin $temp_dist" >> $log +else + cp -pRL $admindir $temp_dist + echo "cp -pRL $admindir $temp_dist" >> $log +fi + +# and all files from the base dir, except directories +echo "Copying over files from the module directory:" >> $log +find $module ! -xtype d -maxdepth 1 | xargs --verbose --replace={} cp {} $temp_dist 2>> $log +echo "--------" >> $log + +# we now enter the temp_dist and delete all unwanted files +# and add wanted files +cd $temp_dist + +# override top-level files +echo "Override files: " >> $log +for file in $override; do + test -e $temp_dist/$name/$file && mv $temp_dist/$name/$file . && echo "mv $temp_dist/$name/$file ." >> $log +done + +test ! -z "$addfiles" && echo "Addfiles: " >> $log +for file in $addfiles; do + test -e $file && cp -R $file $temp_dist && echo "cp -R $file $temp_dist" >> $log +done + +test ! -z "$appaddfiles" && echo "Application addfiles: " >> $log +for file in $appaddfiles; do + test -e $file && cp -R $file $temp_dist/$name/ && echo "cp -R $file $temp_dist/$name/" >> $log +done + +echo "--------" >> $log + +# replace all user requested files +if [ -n "$replace_files" ]; then + echo "Replace user requested files" >> $log + pair_list="`echo $replace_files | sed -e 's/,/ /g'`" + for pair in $pair_list; do + pair_tmp="`echo $pair | sed -e 's/@/ /g'`" + from="`echo $pair_tmp | awk '//{print $1}'`" + to="`echo $pair_tmp | awk '//{print $2}'`" + if [ -z "$from" ] || [ -z "$to" ]; then + echo "bogus pair \"$from@$to\"" >> $log + continue + fi + echo "Replacing \"$to\" by \"$from\"" >> $log + from_path="$temp_dist/$name/$from" + to_path="$temp_dist/$name/$to" + if [ ! -f "$from_path" ]; then + echo "$from_path does not exist!" >> $log + echo "" + echo "Warning: \"$from\" does not exist!" + echo -n "Please enter the path in --replace-files relative " + echo "to the package root of your project." + echo "Your package root is: \"$module/$directory\"" + echo "" + continue + fi + rm -f "$to_path" && \ + mv "$from_path" "$to_path" + if [ $? -ne 0 ]; then + echo -n "Moving \"$from_path\" failed! " >> $log + echo -n "This should not happen." >> $log + if [ -f "$to_path" ]; then + echo "" + else + echo " No \"$to\" will exist in the archive!" >> $log + fi + fi + done + echo "--------" >> $log +fi + +# version file, if it doesn't exist +test ! -z $version && test ! -e VERSION && echo "$name version $version" > VERSION + +cd $temp_dist/$name +echo "make clean in $temp_dist/$name/:" >> $log +echo "" >> $log +echo -n "make clean in $temp_dist/$name/... " +make clean >> $log +echo "--------" >> $log +echo "done." + +cd $temp_dist + +# remove files +echo "Remove files: " >> $log +for file in $remove; do + find . -name $file | xargs rm -Rf + echo "find . -name $file | xargs rm -Rf" >> $log +done + +# remove more files +echo "Remove toplevel files: " >> $log +for file in $toplevelremove; do + test -e $file && rm -Rf $file && echo "rm -Rf $file" >> $log +done +unset file + +# remove hidden files +test ! -z $removehidden && echo "Remove hidden files: " >> $log && find $temp_dist -name ".*" -and ! -name "." | xargs --verbose rm -Rf 2>> $log + +echo "--------" >> $log + +cd $temp_dist + +# create the configure script +# working directory is $temp_dist +echo "make -f Makefile.cvs in $temp_dist/:" >> $log +echo "" >> $log +echo -n "make -f Makefile.cvs in $temp_dist/... " +make -f Makefile.cvs >> $log 2>> $log +echo "rm Makefile.cvs" >> $log +rm -f Makefile.cvs +echo "rm autom4te.cache" >> $log +rm -Rf autom4te.cache +echo "--------" >> $log +echo "done." + +echo "Directory will be $filename." + +# play around with our tar file in temp_dir +cd $temp_dir + +if [ ! -z $leavedir ]; then + test -e $returndir/$filename && rm -Rf $returndir/$filename + cp -R $filename $returndir +fi + +# make a tar of temp_dist +if [ -z $nogzip ] || [ -z $nobzip2 ]; then + echo -n "Tarring... " + echo "tar cf $filename.tar $filename" >> $log + tar cf $filename.tar $filename >> $log + echo "done." +fi + +if [ -z $nogzip ] && [ ! -z `which gzip 2> /dev/null` ]; then + cp $filename.tar $filename.tar.tmp + echo -n "running gzip... " + echo "gzip $filename.tar" >> $log + gzip $filename.tar + echo "done." + mv $filename.tar.tmp $filename.tar + mv $filename.tar.gz $returndir + echo "mv $filename.tar.gz $returndir" >> $log +fi +if [ -z $nobzip2 ] && [ ! -z `which bzip2 2> /dev/null` ]; then + echo -n "running bzip2... " + echo "bzip2 $filename.tar" >> $log + bzip2 $filename.tar + echo "done." + mv $filename.tar.bz2 $returndir + echo "mv $filename.tar.bz2 $returndir" >> $log +fi + +test -e $filename.tar && rm -f $filename.tar + +# cleanup all tempoaries +exit_cleanup + +cd $returndir + +test -z $leavedir && test ! -z $nobzip2 && test ! -z $nogzip && echo "Finished - no created files." && exit 1 + +# cool, everything went ok! +if [ -z $leavedir ]; then + echo "Finished. Created packages:" +else + if [ -z $nogzip ] || [ -z $nobzip2 ]; then + echo "Finished. Created packages/directories:" + else + echo "Finished. Created directory:" + fi +fi + +echo "--------" >> $log +echo "Done: " >> $log +echo "Files are in `pwd`." >> $log + +if [ ! -z $leavedir ] && [ -d $filename ]; then + ls -ld $filename + ls -ld $filename >> $log +fi +if [ -z $nogzip ] && [ -e $filename.tar.gz ]; then + ls -l $filename.tar.gz + ls -l $filename.tar.gz >> $log +fi +if [ -z $nobzip2 ] && [ -e $filename.tar.bz2 ]; then + ls -l $filename.tar.bz2 + ls -l $filename.tar.bz2 >> $log +fi +if [ $log != "/dev/null" ]; then + echo "Created log:" + ls -l $origlog +fi diff --git a/scripts/cvsaddcurrentdir b/scripts/cvsaddcurrentdir new file mode 100755 index 00000000..56185f27 --- /dev/null +++ b/scripts/cvsaddcurrentdir @@ -0,0 +1,30 @@ +#!/bin/sh +#Alexander Neundorf +#copyright 2002, GPL + +#call this script to add all files in and below the current dir to cvs +#it adds *.c, *.h, *.C, *.cpp, *.cc automatically +#*~, *.o, *.so, *.lo, *.la, .libs/, .deps/, .#* are ignored +#it asks for the remaining files + + +#ignore dirs "CVS", ".deps", ".libs" +#ignore files *.o, *.so, *.lo, *.la, *~, .#* +FOUND=`find |grep -v "^\.$"| grep -v CVS| grep -v "\.[ls]\?o$"|grep -v "~$"|grep -v "\.libs/"|grep -v "\.deps/" |grep -v "\.depend/"| grep -v "/\.#" |grep -v "\.la$"` +#echo $FOUND + +ask_for_adding() { +echo +read -p "Add file $file to cvs ? (y/n) " answer rest +#if [ "$answer" != "y" ]; then echo $file; fi +if [ "$answer" == "y" ]; then cvs add $file; fi +} + + +for file in $FOUND +do +#matches all *.h, *.c, *.cpp, *.C, *.cpp, *.cc (and some others too) + echo $file | grep "\.[cCh][cp]\?p\?$" && cvs add $file + echo $file | grep -v "\.[cCh][cp]\?p\?$" && ask_for_adding +done + diff --git a/scripts/cvsbackport b/scripts/cvsbackport new file mode 100755 index 00000000..085b27ae --- /dev/null +++ b/scripts/cvsbackport @@ -0,0 +1,32 @@ +#!/bin/sh +# Backport the last change in HEAD, to a branch. +# Usage: cvsbackport +# WARNING: the branch tag is hardcoded into the script, make sure to check it! +# +# Initial author: Dirk Mueller +# Support for multiple command-line arguments: David Faure + +BRANCH=KDE_3_4_BRANCH +echo "Backporting to $BRANCH" +TMPFILE=`mktemp cvsbackport.XXXXXX` || exit 1 +files=$* +until test $# -eq 0; do + + echo "looking for last change to $1..." + CVSLASTCHANGE_KEEP_WHITESPACE=1 cvslastchange $1 > $TMPFILE + echo "browsing last change to $1..." + less $TMPFILE + cvs up -r$BRANCH $1 + patch < $TMPFILE + rm -f $TMPFILE + echo "showing diff for $1..." + cvs diff $1 | less + + shift +done + +echo "Press ENTER now to commit ($files) or Ctrl+C to abort" +read confirm + +cvs commit $files +cvs up -A $files diff --git a/scripts/cvsblame b/scripts/cvsblame new file mode 100755 index 00000000..bd3635ed --- /dev/null +++ b/scripts/cvsblame @@ -0,0 +1,252 @@ +#! /usr/bin/env perl + +# cvs blame inspired by Bonsai +# Author: Bernd Gehrmann + +=head1 NAME + +cvsblame - Shows a blame-annotated representation of a CVS controlled file in Konqueror. + +=head1 SYNOPSIS + +cvsblame + +=head1 DESCRIPTION + +cvsblame opens Konqueror to display the output of cvs annotate of +a cvs controlled file. When the mouse is on a revision number shown +in the second column, a popup with the respective log message +appears. + +In the popup, a proper mailto: link to the author of a revision +can be created as follows: In your home directory, make a file +.cvsblame. In that file, enter for each repository you are working +with a line like + + accounts :pserver:gehrmab@cvs.kde.org:/home/kde /home/bernd/.kdeaccounts + +where the accounts file contains a simple list of cvs usernames in +the first column and the respective mail address in the second. + +=head1 BUGS + +=over 4 + +=item Does not really work for filenames which are not in the current directory + +=item Is a hack. Really. + +=back + +=head1 AUTHOR + +Bernd Gehrmann + +=cut + +$file = $ARGV[0]; +$outputfile = `kde-config --path tmp` || './#'; # if we put the file in the cwd, then we keep a '#' at the beggining to help CVS ignore it +($outputfile) = split(/:/,$outputfile); +chomp $outputfile; +$outputfile .= "cvsblame.$$.html"; +$configfile = $ENV{HOME}. "/.cvsblame"; +$rootfile = "`pwd`/CVS/Root"; +$cvsroot = `cat "$rootfile"`; +chop $cvsroot; + +# +# Look for a username -> mail address mapping +# + +if (open(CONFIG, $configfile)) { + while () { + if (/accounts\s*([^\s]*)\s+([^\s]*)/) { + if ($1 eq $cvsroot) { + $accountfile = $2; + } + } + } + close CONFIG; +} +if ($accountfile) { + open(ACCOUNTS, $accountfile) || die "Account file not found: $accountfile"; + while () { + if (/([^\s]*)\s+([^\s].*[^\s])\s+([^\s]+)/) { + $mail{$1} = "$2 <$3>"; + } + elsif (/([^\s]*)\s+([^\s]*)/) { + $mail{$1} = $2; + } + } +} + + +# +# The real work, first the html header +# + + +open(OUTPUT, ">$outputfile"); +print OUTPUT < + + + Blame annotation for $file + + + + +

    $file

    + +EOF + +# +# Information from cvs annotate +# + +$color = 1; +$lineno = 1; +$oldrevision = ""; +$oldlineno = ""; +$oldrevstr = ""; +open (ANNOTATE, "cvs annotate \"$file\" 2>/dev/null|"); +while () { + chop; + $line = $_; + $revision = substr $line, 0, 13; + $revision =~ s/\s//g; + $author = substr $line, 14, 9; + $author =~ s/\s//g; + $date = substr $line, 23, 9; + $content = substr $line, 35; + $content =~ s/\&/&/g; + $content =~ s/\/>/g; + $revstr = "log$revision"; + $revstr =~ s/\./_/g; + if ($revision eq $oldrevision) { + if ($lineno == $oldlineno+20) { + $linkstr = ""; + $linkendstr = ""; + $revauthor = "$author $revision"; + $oldlineno = $lineno; + } else { + $linkstr = ""; + $linkendstr = ""; + $revauthor = ""; + } + } else { + $color = ($color == 0)? 1 : 0; + $linkstr = ""; + $linkendstr = ""; + $revauthor = "$author $revision"; + $oldlineno = $lineno; + $oldrevision = $revision; + } + print OUTPUT "\n"; + $lineno++; +} +close ANNOTATE; + +# +# Finally, the html footer +# + +print OUTPUT < + + + +EOF + +close OUTPUT; + +system("kfmclient openProfile webbrowsing $outputfile"); + +exit 0; diff --git a/scripts/cvscheck b/scripts/cvscheck new file mode 100755 index 00000000..1e1da5a8 --- /dev/null +++ b/scripts/cvscheck @@ -0,0 +1,385 @@ +#! /usr/bin/env perl + +use POSIX qw(mktime ctime); +use Time::Local qw( timegm ); + +# Offline check for status of files in a checked-out +# CVS module. +# Artistic License, Dirk Mueller 2001-2003 + +# based on cvschanged by +# Sirtaj Singh Kang Nov 1998. + +if ( defined $ARGV[0] && $ARGV[0] =~ /(?:-h|--help)/) { + print "cvscheck (c) 2001-2003 Dirk Mueller \n\nUsage:\n"; + print " cvscheck [options] \n\n"; + print "Prints information about the status of your local CVS checkout without\n"; + print "communicating with the server (therefore in speed only limited by your\n"; + print "hard-disk throughput, much unlike cvs -n up).\n\n"; + print "Every file is printed with a status character in front of its name:\n"; + print "? foobar.c file is not known to CVS - maybe you should add it?\n"; + print "M foobar.c file is for sure locally modified.\n"; + print "m foobar.c file *might* have local changes (needs a diff with the server).\n"; + print "C foobar.c file has a CVS conflict and therefore cannot be committed.\n"; + print "U foobar.c file is in CVS but its somehow missing in your local checkout.\n"; + print "T foobar.c file has an unusual sticky CVS tag.\n"; + print "A foobar.c you cvs add'ed this file but did not yet commit.\n"; + print "R foobar.c you cvs rm'ed this file but did not yet commit.\n"; + +print < 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3, 'May' => 4, + 'Jun' => 5, 'Jul' => 6, 'Aug' => 7, 'Sep' => 8, 'Oct' => 9, + 'Nov' => 10, 'Dec' => 11); + +%showoptions = (); +$optionlocal = 0; + +sub printinfo($) +{ + print @_ if (defined($showoptions{"all"})); +} + +# convert text stamp to GMT +sub strToTime +{ + my( $timestr ) = @_; + + if( ! ($timestr =~ + /^(\w+)\s*(\w+)\s*(\d+)\s*(\d+):(\d+):(\d+)\s*(\d+)/) ) { + + return -1; + } + + # CVS timestamps are in GMT. + + my( $tm ) = timegm( $6, $5, $4, $3, $months{ $2 }, $7 - 1900); + + return $tm; +} + +sub processEntries +{ + my ( $dir ) = @_; + my %dirunknown = (); + + opendir (DIR, "$dir") || warn "Couldn't read '$dir'"; + # first assume all are unknown + while ( $e = readdir(DIR) ) { + next if ($e eq "."); + next if ($e eq ".."); + next if ($e eq "RCS"); + next if ($e eq "SCCS"); + next if ($e eq "CVS"); + next if ($e eq "CVS.adm"); + next if ($e eq "RCSLOG"); + next if ($e eq "tags"); + next if ($e eq "TAGS"); + next if ($e eq ".make.state"); + next if ($e eq ".nse_depinfo"); + next if ($e eq "core"); + next if ($e eq ".libs"); + next if ($e eq ".deps"); + next if ($e =~ /^.+~$/); + next if ($e =~ /^\#.+$/); + next if ($e =~ /^\.\#.+$/); + next if ($e =~ /^,.+$/); + next if ($e =~ /^_\$.+$/); + next if ($e =~ /^.+\$$/); + next if ($e =~ /^.+\.old$/); + next if ($e =~ /^.+\.bak$/); + next if ($e =~ /^.+\.BAK$/); + next if ($e =~ /^.+\.orig$/); + next if ($e =~ /^.+\.rej$/); + next if ($e =~ /^\.del-.+$/); + next if ($e =~ /^.+\.a$/); + next if ($e =~ /^.+\.olb$/); + next if ($e =~ /^.+\.o$/); + next if ($e =~ /^.*\.obj$/); + next if ($e =~ /^.+\.so$/); + next if ($e =~ /^.+\.Z$/); + next if ($e =~ /^.+\.elc$/); + next if ($e =~ /^.+\.ln$/); + next if ($e =~ /^cvslog\..*$/); + + # kde specific entries + # TODO read from CVSROOT/cvsignore - if it's been checked out! + next if ($e eq "config.cache"); + next if ($e eq "config.log"); + next if ($e eq "config.status"); + next if ($e eq "index.cache.bz2"); + next if ($e eq ".memdump"); + next if ($e eq "autom4te.cache"); + next if ($e eq "autom4te.cache"); + next if ($e eq "Makefile.rules"); + next if ($e eq "Makefile.calls"); + next if ($e eq "Makefile.rules.in"); + next if ($e eq "Makefile.calls.in"); + next if ($e =~ /^.*\.moc$/); + next if ($e =~ /^.+\.gmo$/); + next if ($e =~ /^.+\.moc\.[^\.]+$/); + next if ($e =~ /^.+\.lo$/); + next if ($e =~ /^.+\.la$/); + next if ($e =~ /^.+\.rpo$/); + next if ($e =~ /^.+\.closure$/); + next if ($e =~ /^.+\.all_cpp\.cpp$/); + next if ($e =~ /^.+\.all_C\.C$/); + next if ($e =~ /^.+\.all_cc\.cc$/); + next if ($e =~ /^.+_meta_unload\.[^\.]+$/); + next if ($e =~ /^.+\.kidl$/); + next if ($e =~ /^.+_skel\.[^\.]+$/); + + # Qt specific entries + next if ($e eq ".ui"); + next if ($e eq ".moc"); + next if ($e eq ".obj"); + + $dirunknown{$e} = 1; + } + closedir(DIR); + if( open(CVSIGNORE, $dir."/.cvsignore") ) { + while() { + s/\s*$//; + my $line = $_; + foreach my $entry ( split(/ /,$line) ) { + if ($entry =~ /[\*\?]/) { + my $pattern = quotemeta $entry; + $pattern =~ s/\\\*/.*/g; + $pattern =~ s/\\\?/./g; + foreach $m (keys (%dirunknown)) { + $dirunknown{$m} = 0 if ($m =~ /^$pattern$/); + } + next; + } + $dirunknown{$entry} = 0; + } + } + close(CVSIGNORE); + } + + if ( !open( ENTRIES, $dir."/CVS/Entries" ) ) { + &printinfo("I CVS/Entries missing in $dir\n"); + return; + } + my $oldstandardtag = defined($defaulttag{$dir}) ? $defaulttag{$dir} : ""; + my $staginfo = ""; + if( open(CVSTAG, $dir."/CVS/Tag" ) ) { + my $line = ; + if($line =~ /^[TDN](.+)$/) { + $standardtag = $1; + $staginfo = $1; + } + else { + # something with D - assume HEAD + $oldstandardtag = $standardtag = ""; # its HEAD + &printinfo("I $dir has unknown stickyness: $line"); + } + close(CVSTAG); + } + else { + $standardtag = ""; # its HEAD + $staginfo = "(HEAD)"; + } + &printinfo("I $dir has sticky tag $staginfo\n") if($standardtag ne $oldstandardtag); + while( ) { + if ( m#^\s*D/([^/]+)/# ) { + if (-d "$dir/$1" && !$optionlocal) { + push ( @dirqueue, "$dir/$1" ); + $defaulttag{"$dir/$1"} = $standardtag; + } + $dirunknown{$1} = 0; + next; + } + + next if !m#^\s*/([^/]+)/([-]*[\d\.]*)/([^/]+)/([^/]*)/(\S*)$#; + $fname = $1; + $ver = $2; + $stamp = $3; + $options = $4; + $tag = $5; + $tag = $1 if ($tag =~ /^[TD](.+)$/); + + $dirunknown{$fname} = 0; + + my $taginfo=""; + if(defined($showoptions{"all"})) { + if ( $tag ne $standardtag ) { + if ($tag eq "") { + $taginfo = " (HEAD)"; + } + else { + $taginfo = " ($tag)"; + } + } + if ($options =~ /^\-k(.)$/) { + $taginfo .= " (no RCS-tags)" if($1 eq "o"); + $taginfo .= " (RCS binary file)" if($1 eq "b"); + $taginfo .= " (RCS values only)" if($1 eq "v"); + $taginfo .= " (RCS keywords only)" if($1 eq "k"); + } + } + my $state = $stamp; + if( $stamp =~ m(^(.+)\+(.+)$) ) { + $state = $1; + $stamp = $2; + } + if ( $state =~ /merge/ ) { + # modified version merged with update from server + # check for a conflict + if ( open (F, "$dir/$fname") ) { + my @conflict = grep /^<<<<<<; + close (F); + if( @conflict ) { + push @conflicts, "$dir/$fname$taginfo"; + next; + } + } + else { + push @missing, "$dir/$fname$taginfo"; + next; + } + } + if ( $ver =~ /^\-.*/ ) { + push @removed, "$dir/$fname$taginfo"; + next; + } + $mtm = strToTime( $stamp ); + if( $mtm < 0 ) { + if ( $ver eq "0" ) { + push @uncommitted, "$dir/$fname$taginfo"; + } + else { + push @merged, "$dir/$fname$taginfo"; + } + next; + } + @sparams = lstat( "$dir/$fname" ); + + if ( $#sparams < 0 ) { + push @missing, "$dir/$fname$taginfo"; + next; + } + if( $mtm < $sparams[ 9 ] ) { + push @modified, "$dir/$fname$taginfo"; + next; + } + if ( $tag ne $standardtag ) { + push @tagged, "$dir/$fname$taginfo"; + } + } + close( ENTRIES ); + + my @unknownlist = sort keys (%dirunknown); + foreach $entry (@unknownlist) { + next if ($dirunknown{$entry} == 0); + # ignore unusual files + next if (-l "$dir/$entry" ); + # its a CVS directory ? might be a different module + if (-d "$dir/$entry" and -d "$dir/$entry/CVS") { + $defaulttag{"$dir/$entry"} = $standardtag; + push ( @dirqueue, "$dir/$entry" ); + next; + } + push @unknown, "$dir/$entry"; + } +} + +sub printlist($$@) +{ + my ($status, $type, @flist) = @_; + + return if (not defined($showoptions{"all"}) and + not defined($showoptions{"$type"})); + + if(defined($showoptions{"all"})) { + foreach (@flist) { + s/\.\///; + print "$status $_\n"; + } + } + else { + foreach(@flist) { + print "$_\n"; + } + } +} + +foreach $f ( @unknown ) { + $f =~ s/^\.\///; + print "? $f\n"; +} +foreach (@ARGV) { + $showoptions{"unknown"}++ if(/^(?:-u|--unknown)$/); + $showoptions{"modified"}++ if(/^(?:-m|--modified)$/); + $showoptions{"missing"}++ if(/^(?:--missing)$/); + $showoptions{"tagged"}++ if(/^(?:-t|--tagged)$/); + $showoptions{"added"}++ if(/^(?:-a|--added)$/); + $showoptions{"removed"}++ if(/^(?:-r|--removed)$/); + $showoptions{"conflicts"}++ if(/^(?:-c|--conflicts)$/); + $optionlocal++ if(/^(?:-l|--local)$/); + + next if (/^-/); + push (@dirqueue, "./$_"); +} + +# if no special flags set, show all files +$showoptions{"all"}++ if(scalar(keys(%showoptions)) == 0); + +# Try current directory if none specified +push(@dirqueue, ".") if( $#dirqueue < 0 ); + +# process directory queue +while ($#dirqueue >= 0) { + processEntries( pop @dirqueue ); +} + +&printlist("?", "unknown", @unknown); +&printlist("M", "modified", @modified); +&printlist("m", "modified", @merged); +&printlist("U", "missing", @missing); +&printlist("T", "tagged", @tagged); +&printlist("A", "added", @uncommitted); +&printlist("R", "removed", @removed); +&printlist("C", "conflicts", @conflicts); + +=head1 NAME + +cvscheck -- Lists all files in checked out CVS modules that have been +edited or changed locally. No connection is required to the CVS server, +therefore being extremely fast. + +=head1 AUTHOR + +Dirk Mueller +based on cvschanged by Sirtaj Singh Kang + +=cut diff --git a/scripts/cvsforwardport b/scripts/cvsforwardport new file mode 100755 index 00000000..10397845 --- /dev/null +++ b/scripts/cvsforwardport @@ -0,0 +1,32 @@ +#!/bin/sh +# Forwardport the last change in a branch to HEAD +# Usage: cvsforwardport +# WARNING: the branch tag is hardcoded into the script, make sure to check it! +# +# Initial author: Dirk Mueller +# Support for multiple command-line arguments: David Faure + +BRANCH=KDE_3_4_BRANCH +echo "Forwardporting to HEAD" +TMPFILE=`mktemp cvsforwardport.XXXXXX` || exit 1 +files=$* +until test $# -eq 0; do + + echo "looking for last change to $1..." + CVSLASTCHANGE_KEEP_WHITESPACE=1 cvslastchange $1 > $TMPFILE + echo "browsing last change to $1..." + less $TMPFILE + cvs up -A $1 + patch < $TMPFILE + rm -f $TMPFILE + echo "showing diff for $1..." + cvs diff $1 | less + + shift +done + +echo "Press ENTER now to commit ($files) or Ctrl+C to abort" +read confirm + +cvs commit $files +cvs up -r $BRANCH $files diff --git a/scripts/cvsgettags b/scripts/cvsgettags new file mode 100755 index 00000000..9869eb06 --- /dev/null +++ b/scripts/cvsgettags @@ -0,0 +1,38 @@ +#!/bin/sh +# +# copyright (C) 2004 Roberto Teixeira +# +# This script is release under the GPL +# + +# +# This very simple script can be used to fetch available +# cvs tags for a group of files. +# +# Its usage is simple, simply type something like +# +# gettags myfile.cpp myfile.h +# +# to get the available cvs tags for myfile.cpp and myfile.h +# otherwise simply type +# +# gettags +# +# to fetch all available tags in the current directory and its +# subdirectories. +# + +usage() +{ + echo "Ex.:" + echo " $0 [file1] [file2] ..." +} + +if test -z "$1"; then + echo "Will test tags for all files and subdirectories under `pwd`. Hope it's right" + echo "but if it is not, please inform the files you want to fetch the tags from." + + usage +fi + +cvs log $@|tr "\n" ""|sed 's/^.*symbolic names:\(.*\)keyword subst.*$/\1/'|tr "" "\n" diff --git a/scripts/cvslastchange b/scripts/cvslastchange new file mode 100755 index 00000000..9c4688be --- /dev/null +++ b/scripts/cvslastchange @@ -0,0 +1,55 @@ +#! /usr/bin/env perl + +use File::Basename; + +sub usage() +{ + print "Usage:\n"; + print " $0 [ | | \n" . + " M + - ]\n"; + print "\n"; + + exit 5; +} + +my $filename; + +my $argc = scalar @ARGV; + +if ($argc > 0 and $argc < 3 ) { + $filename = $ARGV[0]; + $cvsversion= dirname($0) . "/cvsversion"; + $version=`$cvsversion $filename`; + chomp $version; + $version=$ARGV[1] if ($argc > 1 and $ARGV[1] =~ /^[\d\.]{3,}$/); +} elsif ($argc == 5) { + $filename=$ARGV[3] if (-f $ARGV[3]); + $version=$ARGV[4] if ($ARGV[4] =~ /^[\d\.]{3,}$/); +} else { + &usage(); +} + +die "$0: filename expected" if(!length($filename)); +die "$filename: $!\n" if (! -f $filename); + +my $vold = $version; +my $vnew = $version; + +if ($version=~/^.*\.1$/) { + $vold = $1 if ($version=~/^(\d+\.\d+(?:\.\d+\.\d+)*)\.\d+\.1$/); +} +else { + if ($version=~/^(.*)\.([^.]*)$/) { $v1 = $1; $v2 = $2 } + $v2old = ${v2}-1; + $vold = $v1 . '.' . $v2old; +} +my $base = basename($filename); +my $dir = dirname($filename); +my $cmd = "cd $dir; cvs -f log -N -r$vnew $base"; +print "$cmd\n"; +system("$cmd"); +my $whitespace = ""; +$whitespace = "-b" unless (defined $ENV{"CVSLASTCHANGE_KEEP_WHITESPACE"}); +$cmd = "cd $dir; cvs -f diff -kk $whitespace -p -u -r$vold -r$vnew $base"; +print "$cmd\n"; +system("$cmd"); diff --git a/scripts/cvslastlog b/scripts/cvslastlog new file mode 100755 index 00000000..d1b45812 --- /dev/null +++ b/scripts/cvslastlog @@ -0,0 +1,8 @@ +#!/bin/sh +# cvslastlog - prints log of last commit for a file +# Depends on the version of the local file, not the one on the server +# Requires cvsversion +# David Faure, faure@kde.org + +cvs log -N -r`cvsversion $1` $1 + diff --git a/scripts/cvslastreferenced b/scripts/cvslastreferenced new file mode 100755 index 00000000..666a5b6d --- /dev/null +++ b/scripts/cvslastreferenced @@ -0,0 +1,64 @@ +#!/usr/bin/perl -w +# Written by Zack Rusin +# +# This file is free software, licensed under the BSD licence. +# That means that you can do anything you want with it except +# eating too much candy since that totally messes up your teeth +# and here in KDE land we care about your teeth. They're out +# greatest priority right next to scoring chicks of course... +# + +# This script goes through the whole history of a file to find +# all modifications referencing specific string. It's useful if +# you want to know when a function has been removed/modified/added +# to a file if a recent cvs annotate doesn't anymore reference it. + +our $file; +our $func; + +sub check_file +{ + my $rev1 = shift; + my $rev2 = shift; + + my $output = `cvs diff -r $rev1 -r $rev2 $file`; + + if ( $output =~ /(^[+-].+$func.+$)/m ) { + print "FOUND IN: cvs diff -r $rev1 -r $rev2 $file\n"; + $_ = $1; + s/^([-+])\s*(.+)/$1 $2/; + return $_; + } + return 0; +} + +sub get_revision +{ + my $output = `cvsversion $file`; + chomp $output; + return $output; +} + +my $argc = scalar @ARGV; + +die "$0 " if ( $argc != 2 ); +$func = $ARGV[0]; +$file = $ARGV[1]; + +my $current_revision = get_revision( $file ); + +$current_revision =~ /(\d+)\.(\d+)/; +$base = $1; +$working = $2; + +while ( $working > 1 ) { + my $older = $working - 1; + my $res = check_file( "$base.$older", "$base.$working"); + + if ( $res ) { + print "\t($res)\n"; + } + --$working; +} + +print "Didn't find a reference to that $func in $file\n"; diff --git a/scripts/cvsrevertlast b/scripts/cvsrevertlast new file mode 100755 index 00000000..96808514 --- /dev/null +++ b/scripts/cvsrevertlast @@ -0,0 +1,17 @@ +#!/bin/sh +# (C) 2001 Charles Samuels +# +# This script reverts all the files given on the command +# by one version, then you can commit them. This +# is like a less polite version of cvsblame ;) +# + +for i in $@ ; +do + text=`cvs status "$i" | grep '[^s]Repository revision:.*$'` + current=`echo $text | awk '{print $3}'` + previous=`echo $current | awk -F . '{ ORS="."; OFS="\n"; for (i=1; i + +=head1 DESCRIPTION + +cvsversion displays the version in CVS of a file, as known by the local +checked out directory. No connection is required to the CVS server. +It can be used in other scripts, or simply to ask +for diffs using + +cvs diff -r [-r ] + +=head1 EXAMPLES + + cd baseline/kdelibs ; cvsversion configure.in + cvsversion baseline/kdelibs/configure.in + +=head1 AUTHOR + +David Faure + +=cut diff --git a/scripts/cxxmetric b/scripts/cxxmetric new file mode 100755 index 00000000..0fae0a9b --- /dev/null +++ b/scripts/cxxmetric @@ -0,0 +1,223 @@ +#!/usr/bin/perl -w + +# Simple Source metrics for C++ +# Taj Sun Apr 26 03:31:00 EST 1998 +# $Id$ + +use strict; + +our $bigblank = 0; +our $bigcommt = 0; +our $bigstrcount = 0; +our $bigstrsize = 0; +our $bigtotal = 0; +our $numfiles = 0; + +our $blocklen = 0; +our $numblocks = 0; +our $blockdepth = 0; +our $blockstart = 0; +our $blockmin = -1; +our $blockmax = 0; +our @blenlist = (); +our @extensions = ( ".cpp", ".cc", ".C", ".c", ".h", ".tcc" ); + +sub processFile +{ + our ( $file ) = @_; + our $blank = 0; + our $comment = 0; + our $total = 0; + our $incomment = 0; + our $strcount = 0; + our $strsize = 0; + + open( SOURCE, "$file" ) || + die "cxxmetric.pl: Couldn't read from $file.\n"; + + while( ) { + $total++; + + if ( /^\s*$/ ) { + $blank++; + next; + } + + if ( m#^\s*//# ) { + $comment++; + next; + } + + if ( /{/ ) { + # block start + $blockdepth += s/{/{/g; + + $blockstart = $. if $blockdepth == 1; + } + + if ( /}/ ) { + # block end + + $blockdepth -= s/}/}/g; + + if( $blockdepth == 0 ) { + my $thisblocklen = $. - $blockstart; + push @blenlist, $thisblocklen; + + $blocklen += $thisblocklen; + $numblocks++; + + if( $blockmax < $thisblocklen ) { + $blockmax = $thisblocklen; + } + + if ( $blockmin == -1 + || $blockmin > $thisblocklen ) { + $blockmin = $thisblocklen; + } + } + } + + my $start = 0; + my $stop = 0; + + if ( m#/\*# ) { + $start = 1; + } + + if( m#\*/# ) { + $stop = 1; + } + + if( $start ) { + $incomment = 1 unless $stop; + $comment++; + } + elsif ( $stop ) { + $comment++; + $incomment = 0; + } + elsif ( $incomment ) { + $comment++; + } + else { + my $line = $_; + countStrings( $line ); + } + } + + our $code = $total - ($comment + $blank ); + $bigtotal += $total; + $bigcommt += $comment; + $bigblank += $blank; + $bigstrcount += $strcount; + $bigstrsize += $strsize; + + our $stravglen = $strcount ? ($strsize / $strcount) : 0; + + write; +} + +sub buildFileList { + my ( $dir ) = @_; + my @fileList = glob( "$dir/*" ); + my @cxxList; + + foreach my $file (@fileList) { + + if( -d $file ) { + push @cxxList, buildFileList( $file ); + } + else { + foreach my $extension (@extensions) { + if( substr( $file, length( $file ) - length( $extension ) ) eq $extension ) { + push @cxxList, $file; + } + } + } + } + + return @cxxList; +} + +sub countStrings +{ + my $line = shift; + + foreach my $string ( split( /("[^"]*)\"/, $line ) ) { + next unless $string =~ /^\"/; + + our $strcount++; + our $strsize += length( $string ) - 1; + } +} + +sub pct +{ + my( $top, $bottom ) = @_; + + return 0 if $bottom == 0; + + return int(( $top * 100 ) / $bottom); +} + +our @files; + +if( @ARGV == 0 ) { + @files = buildFileList("."); +} +else { + @files = @ARGV; +} + +foreach my $file ( @files ) { + processFile( $file ); + ++$numfiles; +} + +our $total = $bigtotal; +our $comment = $bigcommt; +our $blank = $bigblank; +our $code = $bigtotal - ($bigcommt + $bigblank ); +our $file = "Total"; + +print "\n"; +write; + +print "\nPercentage Code:\t" , pct( $code, $total ),"%\n"; +print "Percentage Comment:\t" , pct( $comment, $total ),"%\n"; +print "Percentage Blank:\t" , pct( $blank, $total ),"%\n"; +print "Percentage Cmt/Code:\t" , pct( $comment, $code ),"%\n"; +print "Average Code/File:\t" , int($code/$numfiles)," lines\n" + unless $numfiles == 0; + +if ( $numblocks > 0 ) { + my $avg = int( $blocklen / $numblocks ); + @blenlist = sort @blenlist; + my $median = $blenlist[ int( $numblocks / 2 )]; +print< ) { + if ($_ =~ m/ra=(0x[0-9a-fA-F]+)/) { + push(@addresses, $1); + } +} +close(malloc); +open(SORT, "|sort -u > $malloc.tmp"); + +foreach $address (@addresses) { + print SORT "$address\n"; +} +close(SORT); + +@addresses = (); + +open(SORT, "< $malloc.tmp"); +while ( ) { + chomp $_; + push(@addresses, $_); +} +close(SORT); +unlink $malloc.tmp; + +open (gdb, "|gdb -nx -q $command > $malloc.tmp") || die "Could not run gdb: $!\n"; +$| = 1; + +# get rid of the (gdb) +printf (gdb "set prompt\n"); +printf (gdb "echo \\n\n"); + +# load in the shared libraries +printf (gdb "sharedlibrary\n"); + +# run the program to have _definitly_ the informations +# we need from the shared libraries. Unfortunatly gdb 4.18's +# version of sharedlibrary does nothing ;( +printf (gdb "b main\n"); +printf (gdb "run\n"); + +foreach $address (@addresses) { + + printf (gdb "echo -----------------------------------------------\\n\n"); + # printf (gdb "echo Address = '%s'\n", $address); + printf (gdb "x %s\n", $address); + printf (gdb "info line *(%s)\n", $address); +} +printf (gdb "quit\ny\n"); +# $| = 0; + +close(gdb); + +%lines = (); + +open(malloc, "< $malloc.tmp"); + +$count = 0; +$address = ""; +$line = ""; + +while ( ) { + + # ignore our own input + if ($_ =~ m/^x 0x/ || $_ =~ m/^echo ------/ || $_ =~ m/^info line/) { + next; + } + + if ($_ =~ m/^--------/) { + if ($line) { + $lines{$address} = "$line"; + } + $count = 0; + $address = ""; + $line = ""; + } else { + $count = $count + 1; + } + + if ($count == 1 && $_ =~ m/(0x[0-9a-fA-F]+)\s*<(.*)>:\s*(\S+)/) { + $address = $1; + $line = "$2<$3>"; + } + + if ($count == 2 && $_ =~ m/Line ([0-9]+) of \"([^\"]*)\"/) { + $line = "$2:$1"; + } + +} + +if ($line) { + $lines{$address} = "$line"; +} + +close(malloc); + +open(malloc, $malloc); + +while ( ) { + if ($_ =~ m/ra=(0x[0-9a-fA-F]+)/) { + $address = $1; + if (defined($lines{$address})) { + $_ =~ s/ra=$address/$lines{$address}/; + print STDOUT $_; + } else { + print STDOUT $_; + } + } else { + print STDOUT $_; + } +} diff --git a/scripts/extractattr b/scripts/extractattr new file mode 100755 index 00000000..324b1f56 --- /dev/null +++ b/scripts/extractattr @@ -0,0 +1,158 @@ +#! /usr/bin/env perl + +# +# Copyright (c) 2004 Richard Evans +# +# License: LGPL 2.0 +# + +sub usage +{ + warn <<"EOF"; + +extractattr [flags] filenames + +This script extracts element attributes from designer (.ui) and XMLGIU (.rc) files +and writes on standard output (usually redirected to rc.cpp) the equivalent +i18n() calls so that xgettext can parse them. + +--attr=spec : Specify the attribute to be extracted. The specification + consists of the following comma separated arguments: + + Element,attribute[,context] + + The context is optional and overrides the name set by + --context below. Repeat the flag to specify multiple + attributes: + + --attr=Title,data --attr=Description,data,Stencils + +--context=name : Give i18n calls a context name: i18n("name", ...) +--lines : Include source line numbers in comments (deprecated, it is switched on by default now) +--help|? : Display this summary + +EOF + + exit; +} + +########################################################################################### + +use strict; +use warnings; +use Getopt::Long; + +########################################################################################### +# Add options here as necessary - perldoc Getopt::Long for details on GetOptions + +GetOptions ( "attr=s" => \my @opt_attr, + "context=s" => \my $opt_context, + "lines" => \my $opt_lines, + "help|?" => \&usage ); + +unless ( @ARGV ) +{ + warn "No filename specified"; + exit; +} + +unless ( @opt_attr ) +{ + warn "No attributes specified"; + exit; +} + +########################################################################################### +# Program start proper - NB $. is the current line number + +my $code =<<'EOF'; +our $file_name; + +for $file_name ( @ARGV ) +{ + my $fh; + + unless ( open $fh, "<", $file_name ) + { + warn "Failed to open: '$file_name': $!"; + next; + } + + while ( <$fh> ) + { + last if $. == 1 and $_ !~ /^(?:]*?| . + quotemeta($attribute) . qq|="([^"]+)"/ and write_i18n('| . $context . qq|', \$1);\n|; + } + + return "$code\n"; +} + +sub write_i18n +{ + my ($context, $text) = @_; + + our $file_name; + + unless ( $text ) + { + print "// Skipped empty message at $file_name line $.\n"; + return; + } + + $text =~ s/<//g; + $text =~ s/'/\'/g; + $text =~ s/"/\"/g; + $text =~ s/&/&/g; + + # Escape characters exactly like uic does it + # (As extractrc needs it, we follow the same rule to avoid to be different.) + $text =~ s/\\/\\\\/g; # escape \ + $text =~ s/\"/\\\"/g; # escape " + $text =~ s/\r//g; # remove CR (Carriage Return) + $text =~ s/\n/\\n\"\n\"/g; # escape LF (Line Feed). uic also change the code line at a LF, we do not do that. + + $context ||= $opt_context; + + print "//i18n: file $file_name line $.\n"; + print qq|i18n("|; + print qq|$context", "| if $context; + print qq|$text");\n|; +} + diff --git a/scripts/extractrc b/scripts/extractrc new file mode 100755 index 00000000..54c1123a --- /dev/null +++ b/scripts/extractrc @@ -0,0 +1,174 @@ +#! /usr/bin/env perl + +### TODO: other copyrights, license? +# Copyright (c) 2004 Richard Evans + +sub usage +{ + warn <<"EOF"; + +extractrc [flags] filenames + +This script extracts messages from designer (.ui) and XMLGUI (.rc) files and +writes on standard output (usually redirected to rc.cpp) the equivalent +i18n() calls so that xgettext can parse them. + +--tag=name : Also extract the tag name(s). Repeat the flag to specify + multiple names: --tag=tag_one --tag=tag_two + +--tag-group=group : Use a group of tags - uses 'default' if omitted. + Valid groups are: @{[TAG_GROUPS()]} + +--context=name : Give i18n calls a context name: i18n("name", ...) +--lines : Include source line numbers in comments (deprecated, it is switched on by default now) +--help|? : Display this summary + +EOF + + exit; +} + +########################################################################################### + +use strict; +use warnings; +use Getopt::Long; + +use constant TAG_GROUP => +{ + default => "[tT][eE][xX][tT]|title|string|whatsthis|tooltip|label", + koffice => "Example|GroupName|Text|Comment|Syntax|TypeName", + none => "", +}; + +use constant TAG_GROUPS => join ", ", map "'$_'", sort keys %{&TAG_GROUP}; + + +########################################################################################### +# Add options here as necessary - perldoc Getopt::Long for details on GetOptions + +GetOptions ( "tag=s" => \my @opt_extra_tags, + "tag-group=s" => \my $opt_tag_group, + "context=s" => \my $opt_context, # I18N context + "lines" => \my $opt_lines, + "help|?" => \&usage ); + +unless( @ARGV ) +{ + warn "No filename specified"; + exit; +} + +$opt_tag_group ||= "default"; + +die "Unknown tag group: '$opt_tag_group', should be one of " . TAG_GROUPS + unless exists TAG_GROUP->{$opt_tag_group}; + +my $tags = TAG_GROUP->{$opt_tag_group}; +my $extra_tags = join "", map "|" . quotemeta, @opt_extra_tags; +my $text_string = qr/($tags$extra_tags)( [^>]*)?>/; # Precompile regexp + + +########################################################################################### +# Program start proper - NB $. is the current line number + +for my $file_name ( @ARGV ) +{ + my $fh; + + unless ( open $fh, "<", $file_name ) + { + # warn "Failed to open: '$file_name': $!"; + next; + } + + my $string = ""; + my $in_text = 0; + my $start_line_no = 0; + my $in_skipped_prop = 0; + my $tag = ""; + my $attr = ""; + my $context = ""; + + while ( <$fh> ) + { + last if $. == 1 and $_ !~ /^(?:/g; + $text =~ s/&/&/g; + + # We need to escape characters exactly like uic does it: + $text =~ s/\\/\\\\/g; # escape \ + $text =~ s/\"/\\\"/g; # escape " + $text =~ s/\r//g; # remove CR (Carriage Return) + $text =~ s/\n/\\n\"\n\"/g; # escape LF (Line Feed). uic also change the code line at a LF, we do not do that. + + if ( $text cmp "" ) + { + print "//i18n: file $file_name line $.\n"; + print "// xgettext: no-c-format\n"; + print q|i18n("|; + print qq|$context","| if $context; # We have a I18N context + print qq|$text");\n|; + } + else + { + #print "// Skipped empty message at $file_name line $.\n"; + # - seems this comment may confuse old custom xgettext of KDE3; not needed anyway + } + + $string =~ s/^.*<\/$text_string//o; + $in_text = 0; + + # Text can be multiline in .ui files (possibly), but we warn about it in XMLGUI .rc files. + + warn "there is floating in: '$file_name'" if $. != $start_line_no and $file_name =~ /\.rc$/i; + } + + close $fh or warn "Failed to close: '$file_name': $!"; + + die "parsing error in $file_name" if $in_text; +} + diff --git a/scripts/findmissingcrystal b/scripts/findmissingcrystal new file mode 100755 index 00000000..258119c3 --- /dev/null +++ b/scripts/findmissingcrystal @@ -0,0 +1,32 @@ +#!/bin/sh +# +# Small script to look at Crystal icons and see which ones are still the +# same as kdeclassic/hicolor. + +if [ -z "$1" ] ; then + echo "usage: findmissingcrystal module" + exit 1 +fi + +for icon in `find $1 -name cr*.png` ; do + fullname=`echo $icon | sed 's,.*cr,,'` + res=`echo $fullname | cut -d- -f1` + type=`echo $fullname | cut -d- -f2` + name=`echo $fullname | cut -d- -f3` + dir="kdeartwork/IconThemes/kdeclassic/${res}x${res}/${type}s/" + if [ -d "$dir" ]; then + classic=`find "${dir}" -name "$name"` + if [ -s "$classic" ]; then + diff=`diff $icon $classic` + if [ -z "$diff" ]; then + echo "ERR/same: $icon" + else + echo "OK /diff: $icon" + fi + else + echo "OK /new : $icon" + fi + else + echo "OK /new : $icon" + fi +done diff --git a/scripts/fixfsfaddr.sed b/scripts/fixfsfaddr.sed new file mode 100644 index 00000000..ab96de23 --- /dev/null +++ b/scripts/fixfsfaddr.sed @@ -0,0 +1,30 @@ +#! /usr/bin/sed + +# Copyright 2005 Nicolas GOUTTE +# License LGPL V2+ + +# The script helps to fix the FSF address +# Use: +# find . -name .svn -prune , type f | xargs fgrep -l "Free Software Foundation" | xargs sed -i -f fixfsfaddr.sed +# Note: you should check the changes before committing them. + +# Implementation note: we need to replace phrase by phrase, as +# the wrapping of the FSF address is at different places. + +# Current FSF address: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + +# Old address: 59 Temple Place, Suite 330, Boston, MA 02111-1307 +s/59 Temple Place,/51 Franklin Street,/ +s/59 Temple Place -/51 Franklin Street,/ +s/Suite 330,/Fifth Floor,/ +s/Suite 330$/Fifth Floor/ +s/02111-1307/02110-1301/ + +# Very old address: 675 Mass Ave, Cambridge, MA 02139 +s/675 Mass Ave/51 Franklin Street, Fifth Floor/ +s/Cambridge/Boston/ +s/02139/02110-1301/ +# Warning: the last two replaces seem to match the address of the MIT too. + +# Typo in KDE: Franklin Steet +s/Franklin Steet/Franklin Street/ diff --git a/scripts/fixheaders b/scripts/fixheaders new file mode 100644 index 00000000..b1b0a15b --- /dev/null +++ b/scripts/fixheaders @@ -0,0 +1,214 @@ +#! /usr/bin/env perl + +# this script is written by Stephan Kulow with +# much help from Sirtaj Singh Kang + +if ($ARGV[0]) { + $topdir = $ARGV[0]; +} else { + $topdir=`pwd`; +} +chomp $topdir; + +$lastdir = '.'; + + +# this is the important part. Left from the '=>' you find the regular +# expression for the g++ output and right from '=>' the header file to +# include +%messages = +( + 'implicit declaration of function `int i18n\(\.\.\.\)\'' => "klocale", + '`i18n\' undeclared \(first use this function\)' => "klocale", + 'variable `class QPixmap \S*\' has initializer but incomplete type' => "qpixmap", + '`kapp\' undeclared \(first use this function\)' => "kapplication", + 'no matching function for call to `KLocale::' => "klocale", + '`klocale\' undeclared \(first use this function\)' => "klocale", + 'no matching function for call to `QPopupMenu::' => "qpopupmenu", + '`QTextStream\' undeclared \(first use this function\)' => "qtextstream", + '`QTextStream\' was not declared in this scope' => "qtextstream", + 'incomplete type `QSocketNotifier\'' => "qsocketnotifier", + 'no matching function for call to `KConfig' => "kconfig", + 'variable `class KConfig \S*\' has initializer but incomplete type' => "kconfig", + 'implicit declaration of function `int kdDebug' => "kdebug", + 'implicit declaration of function `int kdWarning' => "kdebug", + '`QFile\' undeclared \(first use this function' => "qfile", + 'variable `QFile \S*\' has initializer but incomplete type' => "qfile", + 'type `KConfigBase\' is not a base type for type `KConfig' => "kconfig", + 'invalid use of undefined type `class QAccel' => "qaccel", + 'invalid use of undefined type `class KAboutData' => "kaboutdata", + 'incomplete type `KAboutData\'' => "kaboutdata", + 'incomplete type `QGrid\'' => "qgrid", + 'invalid use of undefined type `class QGrid\'' => "qgrid", + 'aggregate `class KConfig \S*\' has incomplete type' => "kconfig", + '`stderr\' undeclared \(first use this function' => "stdio", + 'invalid use of undefined type `class KConfig' => "kconfig", + 'implicit declaration of function `int f?printf' => "stdio", + 'no method `KGlobal::' => "kglobal", + '`KGlobal\' undeclared \(first use this function\)' => "kglobal", + 'implicit declaration of function `int locate\(\.\.\.\)' => "kstddirs", + '`locate\' undeclared \(first use this function\)' => "kstddirs", + 'no matching function for call to `KStandardDirs' => "kstddirs", + 'no method `KStandardDirs::' => "kstddirs", + 'variable `class QFile \S*\' has initializer but incomplete type' => "qfile", + 'implicit declaration of function `int ICON\(\.\.\.\)' => "kiconloader", + '`QMessageBox\' undeclared \(first use this function\)' => "qmessagebox", + 'no matching function for call to `QBoxLayout::QBoxLayout' => "qlayout", + '`QUriDrag\' undeclared \(first use this function\)' => "qdragobject", + '`kdDebug\' undeclared \(first use this function\)' => "kdebug", + '`kdWarning\' undeclared \(first use this function\)' => "kdebug", + 'no matching function for call to `KMenuBar::insertItem\(QString, KPopupMenu' => "kpopupmenu", + 'no matching function for call to `KMenuBar::' => "kmenubar", + 'invalid use of undefined type `class QPointArray' => "qpointarray", + 'variable `QPainter \S*\' has initializer but incomplete type' => "qpainter", + 'invalid use of undefined type `class QRegExp' => "qregexp", + 'invalid use of undefined type `class QPushButton' => "qpushbutton", + 'cannot convert `QPushButton \*\' to `QButton \*' => "qpushbutton", + 'invalid use of undefined type `class QButton' => "qbutton", + '`QButton\' undeclared \(first use this function\)' => "qbutton", + 'no method `QCursor::pos' => "qcursor", + '`DesktopIcon\' undeclared \(first use this function\)' => "kiconloader", + '`BarIcon\' undeclared \(first use this function\)' => "kiconloader", + '`SmallIcon\' undeclared \(first use this function\)' => "kiconloader", + '`UserIcon\' undeclared \(first use this function\)' => "kiconloader", + 'implicit declaration of function `int UserIcon\(...\)\'' => "kiconloader", + '`KIcon\' undeclared \(first use this function\)' => "kiconloader", + 'invalid use of undefined type `class KIconLoader' => "kiconloader", + 'invalid use of undefined type `class KInstance' => "kinstance", + 'invalid use of undefined type `class DCOPClient' => "dcopclient", + '`DCOPClient\' undeclared \(first use this function\)' => "dcopclient", + 'invalid use of undefined type `class KStatusBar\'' => "kstatusbar", + 'invalid use of undefined type `class QLabel\'' => "qlabel", + 'invalid use of undefined type `class QImage\'' => "qimage", + 'invalid use of undefined type `class QImageIO\'' => "qimage", + 'invalid use of undefined type `class QLineEdit\'' => "qlineedit", + 'invalid use of undefined type `class QComboBox\'' => "qcombobox", + 'invalid use of undefined type `class QStyle\'' => "qstyle", + 'invalid use of undefined type `class KPopupMenu\'' => "kpopupmenu", + 'invalid use of undefined type `class QPopupMenu\'' => "qpopupmenu", + 'cannot convert `KPopupMenu \*\' to `QPopupMenu \*' => "kpopupmenu", + 'aggregate `QPopupMenu \S*\' has incomplete type' => "qpopupmenu", + 'invalid use of undefined type `class KURL' => "kurl", + 'no method `QApplication::' => "qapplication", + 'no method `QFile::' => "qfile", + 'error: \'Q3CString\' is used as a type' => "q3cstring", + 'error: ISO C\+\+ forbids declaration of \`Q3CString\' with' => "q3cstring", + 'error: incomplete type \'QPixmap\' cannot be used' => 'qpixmap', + 'error: invalid use of undefined type `struct QVector' => 'qvector', + 'error: incomplete type `Q3ValueList' => 'q3valuelist', + 'error: variable `Q3ValueList<' => 'q3valuelist', + 'error: `Q3PointArray\' undeclared' => 'q3pointarray', + 'error: invalid use of undefined type \`struct QColor' => 'qcolor', + 'error: `QX11Info::' => 'qx11info_x11', + 'error: incomplete type \'QX11Info' => 'qx11info_x11', + 'error: \'Q3AsciiDi' => 'q3asciidict' +); + +# Initial values are simply to get into the while +$exitcode=2; # exit-code from last 'make' command +$addedone=1; # 1 when something has been fixed, means we can try again + +while ( $exitcode != 0 && $addedone != 0 ) +{ + $addedone = 0; + + %changes = (); + open(INPUT,"makeobj -j10 -k 2>&1 |") || die "Couldn't run makeobj"; + while() + { + if (/make.*Entering directory \`(.+)\'/) { + $lastdir = $1; + $lastdir =~ s/^$topdir\///; + print STDERR "entering $lastdir\n"; + next; + } + if (/^([^:]*):\d*: (.*)$/) { + $file = $1; + $line = $2; + if ($file !~ m,^\/,) { + $file = "$lastdir/$file"; + } + #print STDERR "file=$file\n"; + } else { + # This could be a continuation line + if ( defined $line && $line ne "" ) { + $line .= $_; + #print STDERR "file still $file\n"; + #print STDERR "line=$line\n"; + } else { + # Compilation line, or other unparsable line -> ignore + print STDOUT $_; + next; + } + } + + #print STDERR "Already having changes for $file\n" if defined($changes{$file}); + next if defined($changes{$file}); + + print STDOUT $_; + + foreach $message (keys %messages) { + if ($line =~ /$message/) { + $changes{$file} = $messages{$message}; + $addedone = fixFile($file, $messages{$message}); + } + } + + if (defined($changes{$file})) { $file=""; $line=""; } + } # end of while() + close(INPUT); + $exitcode=($?>>8); +} # end of main while + +sub fixFile +{ + my( $file, $adding ) = @_; + + my $lastinclude = ""; + + # read file + open ( FILE, "$file" ) || die "Can't read $file"; + + my $flines; + my $cpplevel = 0; + + while () { + $flines .= $_; + if ($_ =~ m/^#if/ && $_ !~ m/^#ifn/) { + $cpplevel = $cpplevel + 1; + } + if ($_ =~ m/^#endif/) { + $cpplevel = $cpplevel - 1; + } + if ($cpplevel == 0 && $_ =~ m/^(#include\s*[\"<]q\S*\.h[\">]\S*)/) { + $lastinclude = $1; + } + } + + close FILE; + + if (!$lastinclude) { + print STDERR "ERROR: no include found in $file! (tried to add $adding.h)\n"; + return 0; + } + + if ($flines =~ m/#include\s*[\"<]$adding\.h[\">]\s*\n/) { + print STDERR "ERROR: $adding.h already included in $file!\n"; + return 0; + } + if ($flines =~ /(\n$lastinclude)/) { + $flines =~ s/$lastinclude(.*\n)/$lastinclude$1#include <$adding.h>\n/; + print STDERR "ADDED <$adding.h> after \"$lastinclude\" in $file\n"; + } else { + print STDERR "ERROR: can't find $lastinclude in $file\n"; + return 0; + } + + # write file + + rename($file, "$file.old"); + open( FILE, ">$file" ) || die "Can't write to $file"; + print FILE $flines; + close FILE; + return 1; +} diff --git a/scripts/fixkdeincludes b/scripts/fixkdeincludes new file mode 100644 index 00000000..54e4e9f6 --- /dev/null +++ b/scripts/fixkdeincludes @@ -0,0 +1,756 @@ +#!/usr/bin/perl -w +# tries to reduce the number of includes in KDE source files +# (c) 2001-2003 Dirk Mueller + +use File::Basename; +use Cwd; + +# declaration of useful subroutines +sub find_src_includes($); +sub find_fixable_sources ($); +sub find_fixable_headers($); +sub find_removable_includes ($); +sub warn_before_modifying ($); +sub remove_include ($$$); +sub replace_include ($$$); +sub replace_include_type ($$); +sub fix_duplicates($); +sub fix_compat_includes($); +sub fix_unnecessary($); +sub fix_include_type($); +sub copy_file($$); +sub process_source_file($); +sub extract_gcc_error($); + +# some global variables +$verbose = 0; # turns on debugging +$modify = 0; # if 1 it should try to fix the files as well +$experimental = 0; # try&error if an include is obsolete (slow!!) +@explicitfiles = (); # filled in if passing files on the command line + +# statistic variables +$exp_success = 0; +$exp_failure = 0; + +while (defined ($ARGV[0])) +{ + $_ = shift; + if (/^--help$|^-h$/) { + print "Usage: fixkdeincludes [--verbose | -v] [--experimental | -e ] [--modify | -m ]\n"; + exit 0; + } + elsif (/^--verbose$|^-v$/) { + $verbose = 1; # Oh is there a problem...? + } + elsif (/^--modify$|^-m$/) { + $modify = 1; + } + elsif (/^--experimental$|^-e$/) { + $modify = 1; + $experimental = 1; + } + elsif (!/^-/) { + push @explicitfiles, $_; + } +} + +$cppExt = "(cpp|cc|cxx|C|c\\+\\+)"; +$hExt = "(h|H|hh|hxx|hpp|h\\+\\+)"; + +# list of compat headers. scroll down ... much of boring stuff here.. +%compatmap = ( + 'qapp.h' => "qapplication.h", + 'qarray.h' => "qmemarray.h", + 'qbitarry.h' => "qbitarray.h", + 'qbttngrp.h' => "qbuttongroup.h", + 'qchkbox.h' => "qcheckbox.h", + 'qclipbrd.h' => "qclipboard.h", + 'qcollect.h' => "qptrcollection.h", + 'qcollection.h' => "qptrcollection.h", + 'qcombo.h' => "qcombobox.h", + 'qconnect.h' => "qconnection.h", + 'qdatetm.h' => "qdatetime.h", + 'qdrawutl.h' => "qdrawutil.h", + 'qdstream.h' => "qdatastream.h", + 'qfiledef.h' => "private/qfiledefs_p.h", + 'qfiledlg.h' => "qfiledialog.h", + 'qfileinf.h' => "qfileinfo.h", + 'qfontdta.h' => "qfontdata.h", + 'qfontinf.h' => "qfontinfo.h", + 'qfontmet.h' => "qfontmetrics.h", + 'qgrpbox.h' => "qgroupbox.h", + 'qintcach.h' => "qintcache.h", + 'qiodev.h' => "qiodevice.h", + 'qlcdnum.h' => "qlcdnumber.h", + 'qlined.h' => "qlineedit.h", + 'qlist.h' => "qptrlist.h", + 'qmenudta.h' => "qmenudata.h", + 'qmetaobj.h' => "qmetaobject.h", + 'qmlined.h' => "qtmultilineedit.h", + 'qmsgbox.h' => "qmessagebox.h", + 'qmultilinedit.h' => "qmultilineedit.h", + 'qobjcoll.h' => "qobjectlist.h>\n\#include "qobjectdefs.h", + 'qpaintd.h' => "qpaintdevice.h", + 'qpaintdc.h' => "qpaintdevicedefs.h", + 'qpdevmet.h' => "qpaintdevicemetrics.h", + 'qpmcache.h' => "qpixmapcache.h", + 'qpntarry.h' => "qpointarray.h", + 'qpopmenu.h' => "qpopupmenu.h", + 'qprndlg.h' => "qprintdialog.h", + 'qprogbar.h' => "qprogressbar.h", + 'qprogdlg.h' => "qprogressdialog.h", + 'qpsprn.h' => "private/qpsprinter_p.h", + 'qpushbt.h' => "qpushbutton.h", + 'qqueue.h' => "qptrqueue.h", + 'qradiobt.h' => "qradiobutton.h", + 'qrangect.h' => "qrangecontrol.h", + 'qscrbar.h' => "qscrollbar.h", + 'qsocknot.h' => "qsocketnotifier.h", + 'qstack.h' => "qptrstack.h", + 'qtabdlg.h' => "qtabdialog.h", + 'qtstream.h' => "qtextstream.h", + 'qvector.h' => "qptrvector.h", + 'qwidcoll.h' => "qwidgetlist.h>\n\#include "qwindowdefs.h", + +# and now the KDE specific compat includes + 'kapp.h' => "kapplication.h", + 'kstddirs.h' => "kstandarddirs.h", + 'kuniqueapp.h' => "kuniqueapplication.h", + 'ktmainwindow.h'=> "kmainwindow.h", + 'kcolorbtn.h' => "kcolorbutton.h", + 'kcolordlg.h' => "kcolordialog.h", + 'kxmlgui.h' => "kxmlguifactory.h", + 'kdebugclasses.h' => "kdebug.h", +); + + +# now it starts to get interesting again + +# Look for source files in the given directory ($dir, first parameter) +sub find_fixable_sources ($) +{ + # for now I grep the directory (requires srcdir==builddir) + # actually it should read the Makefile and + # find the _SOURCES / _OBJECTS tags that are put there by + # automake and am_edit, but thats an excercise to the reader ;-) + + my ( $dir ) = @_; + + opendir (DIR, "$dir") || die "Couldn't read '$dir'\n"; + my @sources = grep { /^.*\.$cppExt$/o } readdir(DIR); + closedir(DIR); + + print "found sources: [ " . join(' ', @sources) . " ] in $dir\n" if ($verbose); + + # prefix them with $dir + my @retsources = (); + foreach $source(@sources) { + push @retsources, "$dir/$source"; + } + + return @retsources; +} + +# Look for header files in the given directory ($dir, first parameter) +sub find_fixable_headers ($) +{ + # for now I grep the directory (requires srcdir==builddir) + # actually it should read the Makefile and + # find the _HEADERS tags that are put there by + # automake and am_edit, but thats an excercise to the reader ;-) + + my ( $dir ) = @_; + + opendir (DIR, "$dir") || die "Couldn't read '$dir'\n"; + my @headers = grep { /^.*\.$hExt$/o } readdir(DIR); + closedir(DIR); + + print "found headers: [ " . join(' ', @headers) . " ] in $dir\n" if ($verbose); + + # prefix them with $dir + my @retheaders = (); + foreach $source(@headers) { + push @retheaders, "$dir/$source"; + } + + return @retheaders; +} + +sub find_removable_includes ($) +{ + my $srcfile = shift @_; + open(SRC, "< $srcfile") || die "find_removable_includes: couldn't open '$srcfile'\n"; + + my @includes = (); + + # we skip all includes that are somehow ifdefed + + my $cpplevel = 0; + $cpplevel = -1 if ($srcfile=~m/^.*\.$hExt$/); # plan for header-protection #ifndef/#define/#endif + while () { + if ($_ =~ m/^\#if/) { + $cpplevel = $cpplevel + 1; + next; + } + if ($_ =~ m/^\#endif/) { + $cpplevel = $cpplevel - 1; + next; + } + #if ($cpplevel == 0 && $_ =~ m/^\#include\s*[\"<]([qk]\S*\.h)[\">]\S*/) { + if ($cpplevel == 0 && (($_ =~ m/^\#include\s*\"(\S+\.h)\"\S*/) || ($_ =~ m/^\#include\s*\<([qk]\S+.h)\>\S*/))) { + push @includes, $1; + next; + } + } + close SRC; + + print "No fixable includes found in $srcfile\n" if ($verbose and not @includes); + print "found includes: [ " . join(' ', @includes) . " ]\n" if ($verbose and @includes); + + return @includes; +} + +sub find_installed_headers($) +{ + my $sdir = shift @_; + my @includes = (); + + open(I, "<$sdir/Makefile.am") || die "couldn't open $sdir/Makefile.am $!"; + + my $data = join('', ); + $data =~ s/\\\s*\n/ /g; + + # now search for not installed headers + foreach my $line (split /^/, $data) { + if($line =~ /^(\w+)_HEADERS\s*=(.*)$/) { + next if $1 eq "noinst"; + push @includes, split (' ', $2); + } + } + close(I); + return @includes; +} + +# first parameter: srcfile +# second parameter: include to remove +# third parameter is the duplicate level: this include is removed $level times +sub remove_include ($$$) +{ + my $srcfile = shift @_; + my $include = quotemeta(shift @_); + my $level = shift @_; + + die "$srcfile is not read/writeable!\n" if( ! -r $srcfile || ! -w $srcfile); + open(I, "< $srcfile") or die "remove_include: couldn't open '$srcfile'\n"; + my @contents = ; + close(I); + + # ok, CPU time doesn't count so we do it the lazy way + # we should remove the last occurence of the include in the + # file because in case its a duplicate removing the first + # one could make a difference. + my @revcontents = reverse @contents; + @contents = (); + + # we skip all inludes that are somehow ifdefed + # note the logic is reversed because it operates + # on reversed lines :) + my $cpplevel = 0; + $cpplevel = -1 if ($srcfile=~m/^.*\.$hExt$/); # plan for header-protection #ifndef/#define/#endif + foreach $line (@revcontents) { + if ($line =~ m/^\#if/) { + $cpplevel = $cpplevel - 1; + push @contents, $line; + next; + } + + if ($line =~ m/^\#endif/) { + $cpplevel = $cpplevel + 1; + push @contents, $line; + next; + } + + if ($level && $cpplevel == 0 && + (($line =~ m/^\#include\s*\"$include\"\S*/) || ($line =~ m/^\#include\s*\<$include\>\S*/))) { + $level = $level - 1; + # skipping the line.. + next; + } + + push @contents, $line; + } + + # now we have the fixed contents in @contents, although in wrong order + open(O, "> $srcfile") || die "remove_include: couldn't open '$srcfile' for writing\n"; + print O reverse @contents; + close (O); +} + +# first parameter: srcfile +# second parameter: include to replace +# third parameter the include file to replace it with +sub replace_include ($$$) +{ + my $srcfile = shift @_; + my $include = quotemeta(shift @_); + my $destinclude = shift @_; + + die "$srcfile is not read/writeable!\n" if( ! -r $srcfile || ! -w $srcfile); + open(I, "< $srcfile") or die "replace_include: couldn't open '$srcfile'\n"; + my @contents = ; + close(I); + + # ok, CPU time doesn't count so we do it the lazy way + my @revcontents = reverse @contents; + @contents = (); + + # we skip all inludes that are somehow ifdefed + # note the logic is reversed because it operates + # on reversed lines :) + my $cpplevel = 0; + $cpplevel = -1 if ($srcfile=~m/^.*\.$hExt$/); # plan for header-protection #ifndef/#define/#endif + foreach $line (@revcontents) { + if ($line =~ m/^\#if/) { + $cpplevel = $cpplevel - 1; + push @contents, $line; + next; + } + + if ($line =~ m/^\#endif/) { + $cpplevel = $cpplevel + 1; + push @contents, $line; + next; + } + + if ($cpplevel == 0 && + (($line =~ m/^\#include\s*\"$include\"\S*/) || ($line =~ m/^\#include\s*\<$include\>\S*/))) + { + print "HAH! found $include to replace in $srcfile!\n" if($verbose); + $line =~ s/(\#include\s*[\"<])$include([\">]\S*)/$1$destinclude$2/; + } + + push @contents, $line; + } + + # now we have the fixed contents in @contents + open(O, "> $srcfile") || die "replace_include: couldn't open '$srcfile' for writing\n"; + print O reverse @contents; + close (O); +} + +# fixes #include -> #include "foo.h" +sub replace_include_type ($$) +{ + my ($srcfile, $include) = @_; + + die "$srcfile is not read/writeable!\n" if( ! -r $srcfile || ! -w $srcfile); + open(I, "< $srcfile") or die "replace_include: couldn't open '$srcfile'\n"; + my @contents = ; + close(I); + + grep(s/^(\#include)\s*<$include>(.*)$/$1 \"$include\"$2/, @contents); + + # now we have the fixed contents in @contents + open(O, "> $srcfile") || die "replace_include: couldn't open '$srcfile' for writing\n"; + print O @contents; + close (O); +} + +sub fix_duplicates($) +{ + my $srcfile = shift @_; + + my @includes = &find_removable_includes($srcfile); + + my %inclMap = (); + + # initialize + foreach $include (@includes) { + $inclMap{$include} = 0; + } + + # count number of occurences + foreach $include (@includes) { + $inclMap{$include} = $inclMap{$include} + 1; + } + + # check for duplicates + foreach $include (keys %inclMap) { + next if $inclMap{$include} <= 1; + + print "$srcfile: duplicate level ". $inclMap{$include} .": ". $include ."\n"; + + &remove_include($srcfile, $include, $inclMap{$include} - 1) if($modify); + } +} + +sub extract_gcc_error($) +{ + my $out = shift; + + # print "out: $out\n"; + + while ($out =~ m/^(.*?):([0-9]+):(.*)$/mg) # filename:lineno:message + { + my $field1 = $1 || ""; + my $field2 = $2 || ""; + my $field3 = $3 || ""; + + # print "f1: $field1, f2: $field2, f3: $field3\n"; + + next if ($field3 =~ m/\s+warning:.*/); + next if ($field3 =~ m/^\s*$/); + return basename($field1); + } + return "BUG!"; +} + +sub fix_compat_includes($) +{ + my $srcfile = shift @_; + + my @includes = &find_removable_includes($srcfile); + + my %inclMap = (); + + # initialize + foreach $include (@includes) { + $inclMap{$include} = 0; + } + + # count number of occurences + foreach $include (@includes) { + $inclMap{$include} = $inclMap{$include} + 1; + } + + # check for compat headers + foreach $include (keys %inclMap) { + if( defined $compatmap{$include}) { + print "$srcfile: compat header: $include, to be replaced by ". $compatmap{$include} ."\n"; + &replace_include($srcfile, $include, $compatmap{$include}) if($modify); + } + } +} + +sub fix_include_type($) +{ + my $srcfile = shift @_; + my $srcdir = dirname($srcfile); + + open(I, "<$srcfile") || die "couldn't open $srcfile in _fix_include_type"; + my @bracketincs = grep s/^\s*\#include\s*<([^>]+)>\s*$/$1/, ; + close(I); + + foreach my $include (@bracketincs) { + next if (!(-r "$srcdir/$include")); + next if (grep (/^$include$/, @instheaders)); + next if ($include eq "config.h"); # oh don't get me started on that + + print "$srcfile: #include <$include> should use #include \"...\"\n"; + &replace_include_type($srcfile, $include) if($modify); + } +} + +# copies a file from src to dest, overwrites destination if exists +sub copy_file($$) +{ + my $src = shift(@_); + my $dst = shift(@_); + + open(I, "< $src") or die "copy_file: can't open $src for input\n"; + my @fcontents = ; + close(I); + open(O, "> $dst") or die "copy_file: can't open $dst for output\n"; + print O @fcontents; + close(O); +} + +# interrupt handler for fix_unnecessary +sub sighandler_fix_unnecessary() +{ + my($sig) = @_; + print "Caught a SIG$sig--shutting down after restoring $srcfile\n"; + chdir($srcdir); + unlink $srcfile || warn "couldn't unlink $srcfile"; + rename $localbackup, $srcfile || warn "couldn't rename $localbackup to $srcfile"; + exit(1); +} + +sub fix_unnecessary($) +{ + local $srcfile = shift @_; + local $srcdir = dirname($srcfile); + + # find canonical path for srcdir + my $origdir = cwd; + chdir($srcdir); + $srcdir = cwd; + print "srcdir=$srcdir\n" if($verbose); + + my $builddir = $srcdir; + my $makecmd = "make"; + if (defined $ENV{"OBJ_REPLACEMENT"}) + { + # we have to use sed here, because perl can't do s#a#b# + $builddir = `echo $srcdir | sed -e "\$OBJ_REPLACEMENT"`; + chomp $builddir; + $makecmd = "makeobj"; + } + print "builddir=$builddir\n" if($verbose); + + my $tot = $exp_success + $exp_failure; + print "=============== $srcfile (successes: $exp_success; total: $tot)\n"; + + $srcfile = basename($srcfile); + + # first figure out some details + my @includes = &find_removable_includes($srcfile); + + my $blanksrc = $srcfile; + $blanksrc =~ s/(.*)\.[^\.]+/$1/; + + print "Checking for initial compilation: "; + chdir($builddir); + my $objextension = "BUG"; + if($srcfile =~ /\.$hExt$/o) { + $output = `$makecmd all 2>&1`; + $objextension = "all" if ( 0 == ($? >> 8)); + } + else { + unlink "$blanksrc.lo"; + my $output = `$makecmd $blanksrc.lo 2>&1`; + $objextension = ".lo" if ( 0 == ($? >> 8)); + if($objextension eq "BUG") { + print "failed with .lo... "; + unlink "$blanksrc.o"; + $output = `$makecmd $blanksrc.o 2>&1`; + $objextension = ".o" if ( 0 == ($? >> 8)); + } + if($srcfile =~ /$hExt/) { + $output = `$makecmd $blanksrc.o 2>&1`; + $objextension = ".o" if ( 0 == ($? >> 8)); + } + } + if($objextension eq "BUG") { + warn "can't figure out right compile command for $srcfile :-(\n" . + "??? unused, or didn't compile in the first place?\n" . + "$output"; + chdir($origdir); + exit 1; + } + + print "worked with $objextension\n"; + + # now try to drop some includes + foreach $include (@includes) { + # kdatastream is special because + # it will break the application if removed even + # if it continues to compile + next if( $include eq "kdatastream.h"); + # I also like to have kdebug.h still in + # so that it's easy to add kdDebug calls + next if( $include eq "kdebug.h"); + # avoid this one as it might cause + # certain code parts to be disabled from compilation + next if( $include eq "qmodules.h"); + # don't remove this one either. causes conditional + # code to be compiled incorrectly + next if( $include eq "kdeversion.h"); + # don't remove the config.h include + # conditional code may depend on this file + next if( $include eq "config.h"); + # check if it is its own header file + my $blankhdr = $include; + $blankhdr =~ s/(.*)\.[^\.]+/$1/; + next if ($blankhdr eq $blanksrc); + + chdir($srcdir); + + local $localbackup = $srcfile . "#fixkdeincludes"; + + # preserve timestamp if possible for CVS + unlink $localbackup; + rename $srcfile, $localbackup; + copy_file($localbackup, $srcfile); + + # revert to backup in case of interrupt (Ctrl+C) + $SIG{'INT'} = \&sighandler_fix_unnecessary; + + # check if it still compiles + if($verbose) { + chdir($builddir); + # testing headers? need to compile everything + if($objextension eq "all") { + # wait a second for makefile timestamp comparisons + sleep 1; + `$makecmd all 2>&1`; + } + else { + unlink "$blanksrc$objextension"; + `$makecmd $blanksrc$objextension 2>&1`; + } + die "unexpected error $output\nexitcode=" . ($? >> 8) if($? >> 8); + chdir($srcdir); + } + + # duplicates have to be nuked here , so it will be dropped maximum once + print "trying without $include: "; + &remove_include($srcfile, $include, 1); + + chdir($builddir); + + # try if it compiles + if($objextension eq "all") { + sleep 1; + $output=`$makecmd $objextension 2>&1`; + } + else { + unlink "$builddir/$blanksrc$objextension"; + $output=`$makecmd $blanksrc$objextension 2>&1`; + } + my $retcode = ($? >> 8); + #print "retcode=$retcode\n$output" if ($verbose); + + chdir($srcdir); + if($retcode == 0) { + # wow, it worked, lets continue! + print "SUCCESS!\n"; + $SIG{'INT'} = 'DEFAULT'; + unlink $localbackup; + $exp_success = $exp_success + 1; + } + else { + # is this a fixable error? + if($objextension eq "all" and + &extract_gcc_error($output) ne $srcfile) { + print "failed (error in " . &extract_gcc_error($output) . ")\n"; + # FIXME: implement fixup of the compilation error + # so that we can be much more agressive in removing + # unneeded includes from headers + } + else + { + # better luck next time + print "FATALLY failed\n"; + } + unlink $srcfile; + rename $localbackup, $srcfile; + $SIG{'INT'} = 'DEFAULT'; + + $exp_failure = $exp_failure + 1; + } + } + + print "\n"; + + chdir($origdir); +} + +sub process_source_file($) +{ + local $file = shift @_; + my $pure = basename($file); + print "Checking: $file\n" if($verbose); + &fix_include_type($file); + &fix_compat_includes($file); + &fix_duplicates($file); + &fix_unnecessary($file) if ($experimental && !grep (/^$pure$/, @instheaders)); + print "\n" if ($verbose); +} + +sub check_for_automake_srcdir($) +{ + my $dir = shift; + + return 0 if !( -r "$dir/Makefile.am"); + return 1 if !( -r "$dir/Makefile"); + + # ok, now its either srcdir with srcdir==builddir, or its builddir, + # which we don't want. + + open(I, "<$dir/Makefile") || die "couldn't read $dir/Makefile"; + while() { + if(/^srcdir\s*=\s*(\S+)/) { + close(I); + + if($1 ne ".") { + print "Skipping build dir: $dir\n" if($verbose); + return 0; + } + return 1; + } + } + + close(I); + # ok, this makefile isn't generated by automake, we don't want that + return 0; +} + +############################################################################# +# here is the main logic +# + +# warn about modified files +if($modify) { + `cvscheck | grep '^[MmC]'`; + print "WARNING: you have pending local changes. You might commit them by accident!\n\n" if($? >> 8 == 0); +} + +if($experimental) { + print "WARNING: The experimental mode is indeed experimental.\n"; + print "It tries to reduce includes by testing if it would compile\n"; + print "without a particular include. It might introduce subtle bugs\n"; + print "or break compilation for make check or make final.\n\n"; + print "This operation mode is known to be unsafe. You've been warned.\n"; +} + +# process files from the command line, if any +if ( $#explicitfiles >= 0 ) { + foreach $file( @explicitfiles ) { + &process_source_file( $file ); + } + exit 0; +} + +# first generate a list of subdirectories +@dirlist = (); +push @dirlist, "." if (&check_for_automake_srcdir(".")); +die "current directory isn't srcdir!" if (!scalar @dirlist); +foreach $dir ( @dirlist ) { + opendir (DIR, "$dir") || warn "Couldn't read '$dir'"; + my $subdir = ""; + while( $subdir = readdir(DIR)) { + next if ($subdir =~ /^\./); + next if !( -d "$dir/$subdir"); + next if (! &check_for_automake_srcdir("$dir/$subdir")); + + push @dirlist, "$dir/$subdir"; + } + closedir(DIR); +} + +# now iterate over all subdirs +foreach $dir(@dirlist) { + + # check if this directory wants not to be fixed + if(open(M, "$dir/Makefile.am")) { + my @mcontents = grep /(\-UQT_NO_COMPAT|\-UKDE_NO_COMPAT)/, ; + close(M); + if ( @mcontents ) { + print "Skipping directory: $dir\n"; + next; + } + } + + @headers = &find_fixable_headers($dir); + @instheaders = &find_installed_headers($dir); + foreach $file(@headers) { + &process_source_file($file); + } + @sources = &find_fixable_sources($dir); + foreach $file(@sources) { + &process_source_file($file); + } +} diff --git a/scripts/fixuifiles b/scripts/fixuifiles new file mode 100755 index 00000000..785b45b4 --- /dev/null +++ b/scripts/fixuifiles @@ -0,0 +1,293 @@ +#!/usr/bin/perl -w +# fixuifiles processes .ui files and removes some insanity: +# * Too high minimum Qt version (see $minversion_* in the top of the script) +# * Hardcoded untranslatable Alt+Letter accels (auto-added by Qt Designer) +# * Captions that are equal to classname (auto-added by Qt Designer) + +# This script is licensed under the GPL version 2. +# (c) 2004 David Faure +# Based on fixkdeincludes, (c) 2001-2003 Dirk Mueller + +use strict; +use File::Basename; +use Cwd; + +# Fix the version number in .ui files if it's bigger than this: +my $default_minversion_maj = 3; +my $default_minversion_min = 3; + +# Known words which are ok as captions +my %knowncaptions = ( + 'Settings' => '', + 'Statistics' => '', + 'General' => '', + 'Tracks' => '', + 'Constants' => '', + 'Preferences' => '', + 'Encryption' => '' +); + +# declaration of useful subroutines +sub process_ui_file($); +sub find_ui_files($); +sub read_required_version($); + +# some global variables +my $verbose = 0; # turns on debugging +my $omit_Qt_check = 0; # turns off Qt version checking +my @explicitfiles = (); # filled in if passing files on the command line +my $minversion_maj = $default_minversion_maj; +my $minversion_min = $default_minversion_min; + +while (defined ($ARGV[0])) +{ + $_ = shift; + if (/^--help$|^-h$/) { + print "Usage: fixuifiles [OPTIONS] files...\n"; + print "Options are:\n"; + print "\t-v, --verbose\tBe verbose\n"; + print "\t--omitqtcheck\tDoes not check for Qt minimum version\n"; + exit 0; + } + elsif (/^--verbose$|^-v$/) { + $verbose = 1; + }elsif (/^--omitqtcheck/) { + $omit_Qt_check = 1; + } + elsif (!/^-/) { + push @explicitfiles, $_; + } +} + +# Find .ui files in the given dir +sub find_ui_files($) +{ + my ( $dir ) = @_; + + opendir (DIR, "$dir") || die "Couldn't read '$dir'\n"; + my @files = grep { /^.*\.ui$/ } readdir(DIR); + closedir(DIR); + + #print "found files: [ " . join(' ', @files) . " ] in $dir\n" if ($verbose); + + # prefix them with $dir + my @retfiles = (); + foreach my $file(@files) { + push @retfiles, "$dir/$file"; + } + + return @retfiles; +} + +# Ensure the version at the top of the file is not too high +sub fix_version($) +{ + my $srcfile = shift @_; + open(SRC, "< $srcfile") || die "fix_version: couldn't open '$srcfile'\n"; + my @contents = ; + my @fixedcontents = (); + close(SRC); + my $needfix = 0; + my $foundversion = 0; + foreach my $line (@contents) { + if (!$foundversion && $line =~ m/version=\"([0-9]+)\.([0-9]+)(\.[0-9]+)?\"/) { + my $version_maj = $1; + my $version_min = $2; + if ( $version_maj > $minversion_maj || + ( $version_maj == $minversion_maj && $version_min > $minversion_min ) ) { + $line =~ s/version=\"[0-9]+\.[0-9]+\"/version=\"$minversion_maj.$minversion_min\"/o; + $needfix = 1; + print "$srcfile: version was $version_maj.$version_min, set to $minversion_maj.$minversion_min\n"; + } + $foundversion = 1; + } + push @fixedcontents, $line; + } + if (!$foundversion) { + # TODO improve so that the script adds the necessary line + print "$srcfile has no UI version, please fix it\n"; + } + if ($needfix) { + open(SRC, "> $srcfile") || die "fix_version: couldn't open '$srcfile' for writing\n"; + print SRC @fixedcontents; + close(SRC); + } +} + +# Ensure no auto-added Alt+letter accel exists - those are untranslatable +sub fix_accels($) +{ + my $srcfile = shift @_; + open(SRC, "< $srcfile") || die "fix_accels: couldn't open '$srcfile'\n"; + my @contents = ; + close(SRC); + return if ( !grep( /Alt\+[A-Z]<\/string>/, @contents )); + my @fixedcontents = (); + + my $firstline; + my $accelsremoved = 0; + my $inside_accel = 0; + # inside_accel is 0 before + # 1 after and before + # 2 after if alt+letter, and before + foreach my $line (@contents) { + if ( $inside_accel == 1 ) { + if ( $line =~ m/(Alt\+[A-Z])<\/string>/ ) { + print "$srcfile: accel $1 removed\n" if ($verbose); + $inside_accel = 2; + $accelsremoved++; + } else { # Not alt+letter, keep accel + push @fixedcontents, $firstline; + $inside_accel = 0; + } + } + if ($line =~ m/property name=\"accel\"/) { + $inside_accel = 1; + $firstline = $line; + } + if ($inside_accel == 0) { + push @fixedcontents, $line; + } + $inside_accel = 0 if ($inside_accel && $line =~ m/<\/property>/); + } + if ($accelsremoved) { + print "$srcfile: $accelsremoved accels removed\n"; + open(SRC, "> $srcfile") || die "fix_accels: couldn't open '$srcfile' for writing\n"; + print SRC @fixedcontents; + close(SRC); + } +} + +# Ensure no auto-added caption exists - it's pretty stupid to have to +# translate Form1 or MyClassName +sub fix_captions($) +{ + my $srcfile = shift @_; + open(SRC, "< $srcfile") || die "fix_captions: couldn't open '$srcfile'\n"; + my @contents = ; + close(SRC); + my @fixedcontents = (); + + my $firstline; + my $class = ""; + my $captionsremoved = 0; + my $inside_caption = 0; + # inside_caption is 0 before + # 1 after and before + # 2 after if caption should be removed, and before + foreach my $line (@contents) { + $class = $1 if ($line =~ m/(.*)<\/class>/); + if ( $inside_caption == 1 ) { + $line =~ m/(.*)<\/string>/ || die "Malformed XML (no string under caption)"; + my $caption = $1; + print "$srcfile: caption='$caption' class='$class'\n" if ($verbose); + if ( ( $caption eq $class && !defined $knowncaptions{$caption} ) || + ($caption =~ m/Form[0-9]/) ) { + if ( $caption =~ m/^[A-Z][a-z]*$/ ) { + print "$srcfile: removing caption '$caption' (warning! could be real caption)\n"; + } else { + print "$srcfile: removing caption '$caption'\n"; + } + $inside_caption = 2; + $captionsremoved++; + } else { # Real caption, keep it + print "$srcfile: keeping caption '$caption'\n" if ($verbose); + push @fixedcontents, $firstline; + $inside_caption = 0; + } + } + if ($line =~ m/property name=\"caption\"/) { + $inside_caption = 1; + $firstline = $line; + } + if ($inside_caption == 0) { + push @fixedcontents, $line; + } + $inside_caption = 0 if ($inside_caption && $line =~ m/<\/property>/); + } + if ($captionsremoved) { + open(SRC, "> $srcfile") || die "fix_captions: couldn't open '$srcfile' for writing\n"; + print SRC @fixedcontents; + close(SRC); + } +} + +# Find a .qt_minversion in $dir or any parent directory. +sub read_required_version($) +{ + my $dir = Cwd::abs_path( shift @_ ); + + $minversion_maj = $default_minversion_maj; + $minversion_min = $default_minversion_min; + while ( length($dir) > 1 ) { + my $versfile = "$dir/.qt_minversion"; + my $version; + if ( open (VERSFILE, "< $versfile") ) { + while () { + $version = $_ if (!/^#/); + } + close(VERSFILE); + } else { + $versfile = "$dir/configure.in.in"; + if ( open (VERSFILE, "< $versfile") ) { + while () { + $version = $1 if m/^#MIN_CONFIG\(([0-9]+.[0-9]+)\)/; + } + close(VERSFILE); + } + } + if (defined $version && $version =~ m/([0-9]+)\.([0-9]+)/) { + $minversion_maj = $1; + $minversion_min = $2; + print "Found min version $1.$2 in $versfile\n" if ($verbose); + return; + } + $dir = dirname($dir); + } +} + +# Process one .ui file +sub process_ui_file($) +{ + my $file = shift @_; + &read_required_version( dirname($file) ); + + print "Checking: $file\n" if($verbose); + &fix_version($file) if(!$omit_Qt_check); + &fix_accels($file); + &fix_captions($file); +} + +############################################################################# +# here is the main logic +# + +# process files from the command line, if any +if ( $#explicitfiles >= 0 ) { + foreach my $file( @explicitfiles ) { + &process_ui_file( $file ); + } + exit 0; +} + +# first generate a list of subdirectories +my @dirlist = (); +push @dirlist, "."; +foreach my $dir ( @dirlist ) { + opendir (DIR, "$dir") || warn "Couldn't read '$dir'"; + my $subdir = ""; + while( $subdir = readdir(DIR)) { + next if ($subdir =~ /^\./); + next if !( -d "$dir/$subdir"); + push @dirlist, "$dir/$subdir"; + } + closedir(DIR); +} + +# now iterate over all subdirs +foreach my $dir(@dirlist) { + my @uifile = find_ui_files($dir); + foreach my $file(@uifile) { + &process_ui_file($file); + } +} diff --git a/scripts/gettext.patch b/scripts/gettext.patch new file mode 100644 index 00000000..9470c6f9 --- /dev/null +++ b/scripts/gettext.patch @@ -0,0 +1,194 @@ +diff -ru src.orig/xget-lex.c src/xget-lex.c +--- src.orig/xget-lex.c Fri May 1 06:45:12 1998 ++++ src/xget-lex.c Fri Apr 27 16:05:06 2001 +@@ -78,17 +78,18 @@ + + enum token_type_ty + { +- token_type_character_constant, +- token_type_eof, +- token_type_eoln, +- token_type_hash, +- token_type_lp, +- token_type_comma, +- token_type_name, +- token_type_number, +- token_type_string_literal, +- token_type_symbol, +- token_type_white_space ++ token_type_character_constant = 0, ++ token_type_eof = 1, ++ token_type_eoln = 2, ++ token_type_hash = 3, ++ token_type_lp = 4, ++ token_type_rp = 5, ++ token_type_comma = 6, ++ token_type_name = 7, ++ token_type_number = 8, ++ token_type_string_literal = 9, ++ token_type_symbol = 10, ++ token_type_white_space = 11 + }; + typedef enum token_type_ty token_type_ty; + +@@ -941,6 +942,10 @@ + tp->type = token_type_lp; + return; + ++ case ')': ++ tp->type = token_type_rp; ++ return; ++ + case ',': + tp->type = token_type_comma; + return; +@@ -1236,6 +1241,11 @@ + tp->type = xgettext_token_type_lp; + return; + ++ case token_type_rp: ++ last_non_comment_line = newline_count; ++ tp->type = xgettext_token_type_rp; ++ return; ++ + case token_type_comma: + last_non_comment_line = newline_count; + +diff -ru src.orig/xget-lex.h src/xget-lex.h +--- src.orig/xget-lex.h Fri May 1 06:45:23 1998 ++++ src/xget-lex.h Fri Apr 27 16:05:06 2001 +@@ -22,13 +22,14 @@ + + enum xgettext_token_type_ty + { +- xgettext_token_type_eof, +- xgettext_token_type_keyword1, +- xgettext_token_type_keyword2, +- xgettext_token_type_lp, +- xgettext_token_type_comma, +- xgettext_token_type_string_literal, +- xgettext_token_type_symbol ++ xgettext_token_type_eof = 0, ++ xgettext_token_type_keyword1 = 1, ++ xgettext_token_type_keyword2 = 2, ++ xgettext_token_type_lp = 3, ++ xgettext_token_type_rp = 4, ++ xgettext_token_type_comma = 5, ++ xgettext_token_type_string_literal = 6, ++ xgettext_token_type_symbol = 7 + }; + typedef enum xgettext_token_type_ty xgettext_token_type_ty; + +diff -ru src.orig/xgettext.c src/xgettext.c +--- src.orig/xgettext.c Wed Apr 29 18:57:50 1998 ++++ src/xgettext.c Fri Apr 27 16:33:46 2001 +@@ -835,7 +835,8 @@ + int is_cpp_file; + { + int state; +- ++ char *msgid = 0; ++ + /* Inform scanner whether we have C++ files or not. */ + if (is_cpp_file) + xgettext_lex_cplusplus (); +@@ -861,8 +862,12 @@ + State 3 = seen one of our keywords with string in second parameter + State 4 = was in state 3 and now saw a left paren + State 5 = waiting for comma after being in state 4 +- State 6 = saw comma after being in state 5 */ ++ State 6 = saw comma after being in state 5 ++ State 7 = after comma and being in state 2 ++ State 8 = after string and being in state 7 ++ */ + xgettext_lex (&token); ++ + switch (token.type) + { + case xgettext_token_type_keyword1: +@@ -886,18 +891,62 @@ + state = 0; + } + continue; ++ ++ case xgettext_token_type_rp: ++ if (state == 2 || state == 8) { ++ token.string = strdup(msgid); ++ remember_a_message (mlp, &token); ++ free(msgid); ++ msgid = 0; ++ state = 0; ++ } ++ continue; + + case xgettext_token_type_comma: +- state = state == 5 ? 6 : 0; ++ switch (state) { ++ case 5: ++ state = 6; ++ break; ++ case 2: ++ state = 7; ++ break; ++ case 8: { ++ char *newstring = (char*)malloc(strlen(msgid) + 2); ++ strcpy(newstring, "_n:"); ++ strcat(newstring, msgid + 2); ++ free(msgid); ++ token.string = newstring; ++ remember_a_message (mlp, &token); ++ msgid = 0; ++ state = 0; ++ break; ++ } ++ default: ++ state = 0; ++ break; ++ } + continue; + + case xgettext_token_type_string_literal: + if (extract_all || state == 2 || state == 6) + { +- remember_a_message (mlp, &token); +- state = 0; ++ if (msgid) ++ free(msgid); ++ msgid = strdup(token.string); ++ // state = 0; + } +- else ++ else if (state == 7) ++ { ++ if (msgid) { ++ char *newstring = (char*)malloc(strlen(msgid) + strlen(token.string) + 20); ++ sprintf(newstring, "_: %s\n%s", msgid, token.string); ++ free(msgid); ++ free(token.string); ++ token.string = msgid = newstring; ++ state = 8; ++ } ++ } ++ else + { + free (token.string); + state = (state == 4 || state == 5) ? 5 : 0; +@@ -905,8 +954,8 @@ + continue; + + case xgettext_token_type_symbol: +- state = (state == 4 || state == 5) ? 5 : 0; +- continue; ++ state = (state == 4 || state == 5) ? 5 : 0; ++ continue; + + default: + state = 0; +@@ -915,6 +964,7 @@ + case xgettext_token_type_eof: + break; + } ++ + break; + } + diff --git a/scripts/includemocs b/scripts/includemocs new file mode 100755 index 00000000..32df1b20 --- /dev/null +++ b/scripts/includemocs @@ -0,0 +1,102 @@ +#! /usr/bin/env perl + +use strict; +use Cwd; +use File::Find; + +my %dir2files=(); +my $cppExt=" cpp cc cxx C c++ "; +my $cppFiles="*.cpp *.cc *.cxx *.C *.c++"; + +sub collectthing() +{ + if (/\.([^.]+)$/) { + my $ext=$1; + if (" h H hh hxx h++ " =~ / $ext /) { + my $line=`grep -l '^[{ \t]*Q_OBJECT' $_ 2> /dev/null`; + chomp($line); + if ($line) { + $dir2files{$File::Find::dir}->{headers}->{$_} = 1; + } + } elsif ($cppExt =~ / $ext /) { + $dir2files{$File::Find::dir}->{sources}->{$_} = 1; + } + } +} + +sub checkdir($) +{ + my ($dir)=@_; + chdir($dir); + my $hdrs=$dir2files{$dir}->{headers}; + my $srcs=$dir2files{$dir}->{sources}; + foreach my $h (keys %$hdrs) { + (my $name=$h) =~ s/\.[^.]+$//; + my @answer = `grep -l "^#include[ ]*.$name\.moc." $cppFiles 2> /dev/null`; + if (@answer == 0) { + my $s; + foreach my $e (split(/\s+/, $cppExt)) { + if (exists $srcs->{$name.".".$e}) { + $s=$dir."/".$name.".".$e; last; + } + } + if ($s) { + print "echo >> $s ;\n"; + print "echo '#include \"$name.moc\"' >> $s ;\n"; + } else { + print "echo \"can't guess a C++ file for $dir/$h\" ;\n"; + } + } + } +} + +find (\&collectthing, cwd()); + +foreach my $k (keys %dir2files) { + print STDERR "Directory $k:\n headers=["; + print STDERR join(", ", keys %{$dir2files{$k}->{headers}}); + print STDERR "]\n sources=["; + print STDERR join(", ", keys %{$dir2files{$k}->{sources}}); + print STDERR "]\n"; + checkdir($k); +} + +=head1 NAME + +includemocs -- handle mocifyable headers, whose .moc file is nowhere included. + +=head1 SYNOPSIS + + includemocs + +=head1 DESCRIPTION + +Header files declaring a QObject descendant have to be run through moc to +produce a .moc file. This .moc file has to be compiled, for which two +possibilities exists: compile it separately, or #include it in the C++ file +implementing that above mentioned class. The latter is more efficient in term +of compilation speed. + +This script searches in the current directory and its subdirs for header files +declaring a QObject descendant class. If it finds some, it looks, if there is +a C++ file containing an '#include' for the generated .moc file. If thats not +the case, it tries to guess into which C++ file that '#include' is placed best +(based on the filename). If it fails to guess a proper place, it mentions +that. + +On stdout commands are ouput, suitable for a shell, which, when +evaluated, add the suggested '#include' at the end of the files. + +On stderr some informational messages are printed. + +=head1 EXAMPLES + + cd kdebase ; includemocs + cd kdebase ; `eval includemocs 2> /dev/null` + +=head1 AUTHOR + +Michael Matz + +=cut + diff --git a/scripts/kDebug2kdDebug.sh b/scripts/kDebug2kdDebug.sh new file mode 100755 index 00000000..a238845e --- /dev/null +++ b/scripts/kDebug2kdDebug.sh @@ -0,0 +1,154 @@ +## kDebug2kdDebug.sh +## Script to port from qDebug, kdebug, kDebugInfo etc. to kdDebug/kdWarning/... +## Example: +## kDebugInfo( [area,] "format %a - %b", arga, argb ) +## becomes +## kdDebug( [area] ) << "format " << arga << " - " << argb << endl; +## +## Written by David Faure , licensed under GPL. +## 17/03/2000 + +find $1 -name '*[cCph]' -type f | xargs grep -H -i 'ebug(\|warning(' \ +| grep -v 'kdDebug\|kdWarning' \ +| grep -v include \ +| sed -e "s#:.*##" \ +| sort -u \ +| while read file; do +echo -n "working on $file " +cp $file $file.tmp +perl -w -i -e \ +' +$inkdebug=0; +while (<>) +{ + if ( $inkdebug ) + { + chop; + #print "Reading line : " . $_ . "\n"; + $statement .= $_; + } + elsif ( /kdebug\s*\(/ || /kDebug[a-zA-Z]*\s*\(/ || /qDebug\s*/ || /qWarning\s*/ ) + { + # Very old kdebug stuff :) + s/kdebug\s*\(\s*KDEBUG_INFO,/kDebugInfo\(/; + s/kdebug\s*\(\s*0,/kDebugInfo\(/; + s/kdebug\s*\(\s*KDEBUG_WARN,/kDebugWarning\(/; + s/kdebug\s*\(\s*KDEBUG_ERROR,/kDebugError\(/; + s/kdebug\s*\(\s*KDEBUG_FATAL,/kDebugFatal\(/; + + $inkdebug = 1; + chop; + $statement = $_; + } + + if ( $inkdebug ) + { + if ( /\)\s*;/ ) # look for ); + { + $inkdebug = 0; + $_ = $statement; + ## Ok, now we have the full line + ## 1 - Parse + if (s/(^.*kDebug[a-zA-Z]*)\s*\(\s*//) { + $line=$1; # has the indentation, //, and the kDebug* name + } elsif (s/(^.*qDebug)\s*\(\s*// || s/(^.*qWarning)\s*\(\s*//) { + $line=$1; + } else { die "parse error on kDebug/qDebug/qWarning..."; } + $line=$1; # has the indentation, //, and the kDebug* name + $line =~ s/kDebugInfo/kdDebug/; + $line =~ s/kDebugArea/kdDebug/; + $line =~ s/qDebug/kdDebug/; + $line =~ s/qWarning/kdWarning/; + $line =~ s/kDebugWarning/kdWarning/; + $line =~ s/kDebugError/kdError/; + $line =~ s/kDebugFatal/kdFatal/; + $area = ""; + if ( s/^([0-9]+)\s*,\s*//) # There is an area + { + $area = $1; # Store it + $line .= "(" . $area . ")"; + } elsif ( s/^(KBABEL[^,]*)\s*,\s*//) + { # Example of support for #defined area (here KBABEL.*) + $area = $1; # Store it + $line .= "(" . $area . ")"; + } else + { $line .= "()"; } # You can set an area here if converting qDebugs + + $arguments = ""; # for final test + $commented = 0; + if ( !s/^\"([^\"]*)\"// ) # There is no format + { + s/\s*\)\s*;\s*$//; + $commented = s/\s*\)\s*;\s*\*\/$//; # terminating with */ + $line = $line . " << " . $_ ; + } else + { + $format = $1; + # If we stopped on a \" we need to keep adding to format + while ( $format =~ m/\\$/ ) + { s/^([^\"]*)\"// || die "problem"; $format .= "\"" . $1; } + s/\s*\)\s*;\s*$/,/; # replace trailing junk with , for what follows + $commented = s/\s*\)\s*;\s*\*\/$/,/; # terminating with */ + $arguments = $_; + + ## 2 - Look for %x + @stringbits = split( "(%[0-9]*[a-z])", $format ); + foreach ( @stringbits ) + { + #print $_ . "\n"; + if ( /(%[0-9]*[a-z])/ ) # This item is a format + { + ## 3 - Find argument + # kludge for QString(a,b) constructions + $arguments =~ s/(QString\s*\([^,]+,[^,]+\))/QStrKLUDGE/; + $kludge = $1; + $arguments =~ s/\s*([^,]+)\s*,//; + # Remove trailing .ascii() and latin1() + $arg = $1; + $arg =~ s/QStrKLUDGE/$kludge/; ## restore original arg + $arg =~ s/\.ascii\(\)$//; # remove + $arg =~ s/\.latin1\(\)$//; # remove + $arg =~ s/debugString\(([^\)]+)\)/$1/; # remove + # If "a ? b : c" then add parenthesis + if ( $arg =~ m/.+\s*\?\s*.+\s*:\s*.+/ ) { + $arg = "(" . $arg . ")"; + } + $line = $line . " << " . $arg; + } else # This item is some litteral + { + $line = $line . " << \"" . $_ . "\"" if ($_); + } + } + + } + $arguments =~ s/,$//; # Remove trailing slash before next check + if ( $arguments ) { + print STDERR "Non-processed (Information lost! Check the file!) : " . $arguments . "\n"; + } + $line = $line . " << endl;\n"; + if ( $commented ) { $line .= "\*/"; } + print $line; + } + } + else + { + # Normal line + print; + } +} +if ( $inkdebug ) +{ + print STDERR "Warning, unterminated kDebug call !! Check the file !\n"; + print $arguments; +} +' $file.tmp +if cmp -s $file $file.tmp > /dev/null 2>&1 ; then + echo "unchanged" + rm $file.tmp +else + echo "patching" + mv $file.tmp $file +fi + +done + diff --git a/scripts/kde-build b/scripts/kde-build new file mode 100755 index 00000000..5c9a111d --- /dev/null +++ b/scripts/kde-build @@ -0,0 +1,898 @@ +#! /usr/bin/env bash +################################################################################ +# Updates and recompiles a local KDE tree from SVN # +# (c) 2000, 2001, 2002, 2003 by Frerich Raabe # +# (c) 2002, 2003 by Stephan Kulow # +################################################################################ +# Do not edit this file, change kde-buildrc instead! # +################################################################################ + +# These strings are defined as variables to make the output look more +# consistent. +# +str_okay="done!" +str_error="failed!" + +# The variables whose name is prefixed with ERR_ hold the error codes which +# are returned by the script and depend on the reason for aborting the +# execution. +# +# No error has been noticed, everything seems to be fine. +# +err_no_error="0" + +# Could not change into a directory of a module - wrong owner/access +# settings? +# +err_change_into_mod_dir="1" + +# Could not find the file 'Makefile.in' for a module, mostly happens if +# 'make -f Makefile.cvs' hasn't been executed for a module. +# +err_no_makefile_in="2" + +# The 'configure' command failed for a module because the system doesn't +# support certain features - I hope you activated logfile generation... ;) +# +err_configure_fail="3" + +# The compilation of a module failed - if the module is defined in +# $critical_modules (see below) the execution is aborted, otherwise the script +# will continue with the next module. +# +err_compile_fail="4" + +# The installation of a module failed - this mostly happens if there's not +# enough free disk space on the partition which $KDEDIRS is mounted to. +# +err_install_fail="5" + +# The $KDESRCDIR variable wasn't set or contains a non-existant directory. +# +err_inv_kdesrcdir="6" + +# The $QTDIR variable wasn't set, points to a non-existant directory or +# doesn't contain a bin/, lib/, or include/ subdirectory. +# +err_inv_qtdir="7" + +# .... + +# The configuration file couldn't be found. +# +err_no_config="11" + +# You can't mix CVS_CLEAN and INCREMENTAL_BUILD +# +err_cvsclean_incremental="12" + +# You can't mix BUILD_CLEAN and INCREMENTAL_BUILD +# +err_buildclean_incremental="13" + +# Certain modules depend on others - those "base" modules which are required +# by others to compile and/or run should be listed here to ensure that the +# script is aborted in case on of these modules doesn't build. +# These modules needs to be build and installed in this specific order! +# +critical_modules="arts kdelibs kdebase" + +# Internal variable, do not change. +# +dateformat="`date +%Y%m%d`" + +# Private variables fro controlling kppp +# +we_started_kppp="FALSE" + +# Connects to the internet using kppp if desired +# +kppp_connect() +{ +if [ "$USE_KPPP" = "TRUE" ]; then + kppp_process=`dcopfind -a kppp-*` + if [ "$kppp_process" = "" ]; then + #kppp not running + kppp > /dev/null 2>&1 & + sleep $KPPP_LOAD_TIME + `dcop $(dcopfind -a kppp-*) KpppIface beginConnect` + #wait for a while + sleep $KPPP_CONNECT_TIME + kppp_connected=`dcop $(dcopfind -a kppp-*) KpppIface isConnected` + if [ "$kppp_connected" = "true" ]; then + we_started_kppp="TRUE" + echo Connected OK + else + echo Could not connect, maybe you need to increase KPPP_CONNECT_TIME + fi + + else + kppp_connected=`dcop $(dcopfind -a kppp-*) KpppIface isConnected` + if [ "$kppp_connected" = "false" ]; then + #Start a connection + `dcop $(dcopfind -a kppp-*) KpppIface beginConnect` + #wait for a while + sleep $KPPP_CONNECT_TIME + kppp_connected=`dcop $(dcopfind -a kppp-*) KpppIface isConnected` + if [ "$kppp_connected" = "true" ]; then + we_started_kppp="TRUE" + echo Connected OK + else + echo Could not connect, maybe you need to increase KPPP_CONNECT_TIME + fi + else + echo Kppp is already connected to the internet + fi + fi +fi +} + +# Disconnects from the internet using kppp if desired +# +kppp_disconnect() +{ +if [ "$USE_KPPP" = "TRUE" ]; then + if [ $(dcopfind -a kppp-*) = "" ]; then + #kppp not running + echo Kppp was not running so cannot be disconnected + else + if [ "$we_started_kppp" = "TRUE" ]; then + echo Disconnecting kppp + `dcop $(dcopfind -a kppp-*) KpppIface disconnect` + else + echo We didnt connect using kppp so we wont disconnect + fi + fi +fi +} + +# This method gives some kind of status message in the title bar of Konsole, +# xterm, etc.. Thanks have to go to Malte Starostik +# for the code :-) +set_title() { + which printf > /dev/null 2>&1 || return + if ([ "$TERM" = "xterm" ] || [ "$TERM" = "xterm-color" ] || [ "$TERM" = "screen" ]) && tty -s; then + printf "\033]0;$1\007" + fi +} + +# moves a log file to be named $1, so one can see on first glance +move_logfile() { + rename_logfile=`echo $logfile | sed -e "s,-build-,-$1-,"` + mv "$logfile" "$rename_logfile" + logfile=$rename_logfile +} + +# Executes the given command, logging the output to $logfile if requested. +# +log_cmd() { + if [ -n "$logfile" ]; then + eval "$1 >> $logfile 2>&1" + else + eval "$1" + fi + return $? +} + +module_variable() { + eval "$1=\$BASE_$1" + varname="$1_"`echo $module|tr a-z A-Z|tr - _|sed "s/\(.*\)-[0-9]*/\1/g"`; # e.g. $1_ARTS etc. + varvalue=`eval echo '$'$varname` + if [ -n "$varvalue" ]; then + eval "$1=\"$varvalue\"" + fi +} + +# Inserts a "-> blah <---" style separator into $logfile, and starts a timer +log_section() { + [ -z "$logfile" ] && return 0 + len=`echo $1 | wc -m | sed -e "s,^ *,,"` + echo "------------------------------------------------------------------------" \ + | sed -e "s,^,-> $1 <," -e "s,.\{$len\}$,," >> $logfile 2>&1 + starttime=`date +%s` +} + +# Computes the time elapsed since the last log_section call, +# inserts a "----" separator, and the time needed, into $logfile +log_endsection() { + [ -z "$logfile" ] && return 0 + compute_time + echo "----------------------------------------------------------------------------" >> $logfile 2>&1 + printf "%s for module %s done. Time needed: %02d:%02d:%02d\n" "$1" $module $hours $minutes $seconds >> $logfile 2>&1 + echo "" >> $logfile 2>&1 +} + +# This function computes the time which was needed for a certain task, using +# expr (instead of shell-specific features) for portability. +# +compute_time() { + duration=`expr \`date +%s\` - $starttime` + hours=`expr $duration / 3600` + minutes=`expr $duration % 3600 / 60` + seconds=`expr $duration % 60` +} + +prepare_update() { + # This checks whether the user wants a certain branch and generates the + # command line. + # + cmd_update="svn" + if test -d $KDESRCDIR/$module; then + test -z "$SUBDIR" && { cmd_update="$cmd_update update" ; return; } + cmd_update="$cmd_update switch -r HEAD" + else + cmd_update="$cmd_update co" + fi + if [ -z "$ACCOUNT" ]; then + cmd_update="$cmd_update $ANONSVNROOT$SUBDIR" + else + cmd_update="$cmd_update" + if [ "$SSHACCOUNT" = "yes" ]; then + cmd_update="$cmd_update svn+ssh://$ACCOUNT@svn.kde.org/home/kde$SUBDIR" + else + cmd_update="$cmd_update https://$ACCOUNT@svn.kde.org/home/kde$SUBDIR" + fi + fi + if [ -n "$CHECKOUT_PARTIAL" -a "$1" = "toponly" ]; then # TODO + cmd_update="$cmd_update -N" + fi + if test ! -d $KDESRCDIR/$module; then + cmd_update="$cmd_update $module" + fi +} + +# This function installs a compiled CVS module. +# +install_module() { + echo -n " Installing..." + set_title "Installing module $module..." + log_section "Installation" + + if log_cmd "$cmd_make_install"; then + log_endsection "Installation" + if [ -n "$KDELOGDIR" ]; then + echo "Build of module $module successfully finished at `date +%c`" >> $logfile + [ -n "$cmd_compress" ] && eval "$cmd_compress $logfile" + fi + echo "$str_okay" + echo "Module $module successfully installed in $KDEDIRS!" + move_logfile "finished" + else + echo "$str_error" + move_logfile "failed" + [ -n "$cmd_compress" ] && eval "$cmd_compress $logfile" + [ $critical -eq 0 ] || exit $err_compile_fail + fi +} + +make_makefile_cvs() { + cmd_make_makefile_cvs="$MAKE -f Makefile.cvs" + if [ "$NICECOMPILE" = yes ]; then + cmd_make_makefile_cvs="nice $cmd_make_makefile_cvs" + fi + unset UNSERMAKE || true + if [ -n "$USE_UNSERMAKE" ] && \ + !(echo $NO_UNSERMAKE_MODULES | grep $module > /dev/null 2>&1); then + export UNSERMAKE=$USE_UNSERMAKE + fi + + if [ "$INCREMENTAL_BUILD" = "yes" -a -e "Makefile.in" ]; then + cmd_make_makefile_cvs="echo Warning: no Makefile.cvs for module $module - as requested" + fi + + if log_cmd "$cmd_make_makefile_cvs"; then + echo "$str_okay" + else + echo "$str_error" + fi +} + +# Disable zsh feature +CDPATH= + +# Get current configuration, bail out if it couldn't be found. +# Search order is $PWD:$HOME:`dirname $0` +# The name in $HOME is .kde-buildrc (note the leading '.') +if [ -e "$PWD/kde-buildrc" ]; then + rc_file="$PWD/kde-buildrc" +else + if [ -e "$HOME/.kde-buildrc" ]; then + rc_file="$HOME/.kde-buildrc" + else + if [ -e "`dirname $0`/kde-buildrc" ]; then + rc_file="`dirname $0`/kde-buildrc" + else + echo "ERROR: Cannot locate configuration file kde-buildrc!"; exit $err_no_config + fi + fi +fi + +INCREMENTAL_BUILD="no" + +. $rc_file + +if [ "${INCREMENTAL_BUILD}" = "yes" -a "${CVS_CLEAN}" = "yes" ]; then + echo "ERROR: Cannot use CVS_CLEAN=\"yes\" and INCREMENTAL_BUILD=\"yes\" together!"; exit $err_cvsclean_incremental +fi + +if [ "${INCREMENTAL_BUILD}" = "yes" -a "${BUILD_CLEAN}" = "yes" ]; then + echo "ERROR: Cannot use BUILD_CLEAN=\"yes\" and INCREMENTAL_BUILD=\"yes\" together!"; exit $err_buildclean_incremental +fi + +domakecvs=1 +dosvnupdate=1 +dousage=0 +specifiedModules="" + +# Parse args +# +while [ $# != 0 ] +do + arg=$1 + case "$arg" in + --help) + dousage=1 + break ;; + --version) + # Show version + echo "$0 version 0.8.15" + exit $err_no_error ;; + --no-update) + dosvnupdate=0 ;; + --no-check) + domakecvs=0 ;; + --incremental) + INCREMENTAL_BUILD="yes" + ;; + --full) + INCREMENTAL_BUILD="no" + ;; + -*) + echo "Unhandled option $arg" + dousage=1 + break ;; + *) + specifiedModules="$specifiedModules $arg" ;; + esac + shift +done + +# Show help info +if [ "$dousage" -eq 1 ]; then + echo "usage: $0 [--options] [modules]" + echo "--no-update Do not update from SVN." + echo "--no-check Do not re-run Makefile.cvs." + echo "--incremental Start make right after update - risky. This also" + echo " disables the BUILD_CLEAN and CVS_CLEAN options." + echo "--full Do full setup even when INCREMENTAL_BUILD is set." + echo "--help Show this message." + exit $err_no_error +fi + +# Expand ~ +QTDIR=`echo "$QTDIR" | sed -e "s,^\~/,$HOME/,"` +KDESRCDIR=`echo "$KDESRCDIR" | sed -e "s,^\~/,$HOME/,"` +KDEBUILDDIR=`echo "$KDEBUILDDIR" |sed -e "s,^\~/,$HOME/,"` +KDEDIRS=`echo "$KDEDIRS" |sed -e "s,^\~/,$HOME/,"` +KDELOGDIR=`echo "$KDELOGDIR" |sed -e "s,^\~/,$HOME/,"` + +# Make sure some paths are according to the rc file. Note that there are AFAIK +# UNIX flavors which don't support LD_LIBRARY_PATH +PATH=$QTDIR/bin:$KDEDIRS/bin:$PATH +MANPATH=$QTDIR/doc/man:$MANPATH +LD_LIBRARY_PATH=$KDEDIRS/lib:$QTDIR/lib:$LD_LIBRARY_PATH + +if [ -n "$USE_UNSERMAKE" ]; then + if [ -n "$MAKE" ]; then + echo "overwriting MAKE=$USE_UNSERMAKE" + fi + MAKE=$USE_UNSERMAKE + UNSERMAKE_PATH=`dirname $USE_UNSERMAKE` + PATH=$UNSERMAKE_PATH:$PATH +fi + +if [ -z "$MAKE" ]; then + MAKE=make +fi + +if [ -n "$specifiedModules" ]; then + # In case someone with a kdebase dir in $PWD does autocompletion.. + modules=$( echo $specifiedModules | sed -e 's/\///g' ) +else + if [ "$ONLYLISTEDMODULES" = yes ]; then + if [ "$USEKDESUPPORT" = yes ]; then + modules="kdesupport $critical_modules $KDEMODULES" + else + modules="$critical_modules $KDEMODULES" + fi + else + modules="$critical_modules" + # This generates in 'modules' a list of the modules which shall be updated. + # + potential_modules=`find $KDESRCDIR -type d -mindepth 1 \ + -maxdepth 1 -follow | sed -e "s@.*/?*@@"` + for module in $potential_modules; do + if [ -d $KDESRCDIR/$module/CVS -a -w $KDESRCDIR/$module ] \ + && !(echo $EXCLUDE | grep -q $module) \ + && !(echo $modules | grep -q $module) ; then + modules="$modules $module" + fi + done + fi +fi + +# Various checks to ensure that the user didn't specify invalid data which +# would make our script break. +# +if [ -n "$KDELOGDIR" -a ! -d "$KDELOGDIR" ]; then + if ! mkdir -p "$KDELOGDIR" > /dev/null 2>&1; then + echo "WARNING: Could not create logfile-directory." + echo "WARNING: Logfile generation deactivated." + KDELOGDIR="" + fi +else + if [ -n "$KDELOGDIR" -a ! -w "$KDELOGDIR" ]; then + echo "WARNING: Could not obtain write access to specified logfile-directory." + echo "WARNING: Logfile generation deactivated." + KDELOGDIR="" + fi +fi + +if [ -n "$KDELOGDIR" ]; then + str_error="$str_error Check the logfile in $KDELOGDIR for further information." +fi + +if [ ! -d "$KDESRCDIR" ]; then + echo "ERROR: Invalid source directory specified!"; exit $err_inv_kdesrcdir +fi +if [ ! -d "$QTDIR" -o ! -d "$QTDIR/lib" -o ! -d "$QTDIR/bin" -o ! -d "$QTDIR/include" ]; then + echo "ERROR: Invalid Qt directory specified!"; exit $err_inv_qtdir +fi + +if [ "$COMPRESSLOGS" = yes ]; then + if which bzip2 > /dev/null 2>&1; then + cmd_compress="`which bzip2` -f " + else + if which gzip > /dev/null 2>&1; then + cmd_compress="`which gzip` -f " + else + echo "WARNING: Neither bzip2 nor gzip was found, disabling compression of logfiles." + cmd_compress="" + fi + fi +fi + +if which sudo > /dev/null 2>&1; then + cmd_sudo="sudo" +else + cmd_sudo="su -c" +fi + +# Clean the installation directory if selected. +# +if test "$INSTALLFROMSCRATCH" = yes && test -z "$specifiedModules" ; then + mkdir -p $KDEDIRS 2> /dev/null + if [ ! -w $KDEDIRS ]; then + echo "Enter the root password to clean the installation directory." + echo "WARNING: All files and directories in $KDEDIRS will be deleted!" + echo -n "Please enter root " + eval "$cmd_sudo rm -rf $KDEDIRS/*" + else + rm -rf $KDEDIRS/* + fi +fi + +# Optionally activate cheap tweaks. +# +if [ "$TWEAKCOMPILE" = yes ]; then + CFLAGS="-O0" + CXXFLAGS="-O0" + export CFLAGS CXXFLAGS +fi + +# Preprocess configuration keys, expand keywords (DISTCC) +# +if echo $MAKE_OPTS_COMPILE | grep DISTCC > /dev/null; then + if [ -n "$DISTCC_HOSTS" ]; then + hosts=0 + for host in $DISTCC_HOSTS; do + hosts=$((${hosts} + 1)) + done + hosts=$((${hosts} * 2)) + else + hosts=1 + fi + MAKE_OPTS_COMPILE=`echo $MAKE_OPTS_COMPILE | sed -e "s,DISTCC,$hosts,"` +fi + +if echo $MAKE_OPTS_COMPILE | grep TEAMBUILDER > /dev/null; then + if ! which tbcompiler > /dev/null 2>&1; then + echo "TEAMBUILDER is not in PATH and OPTS_COMPILE has TEAMBUILDER" + exit 1 + fi + hosts=`tbcompiler -joblimit` + if [ -z "$hosts" ]; then + hosts=1 + fi + MAKE_OPTS_COMPILE=`echo $MAKE_OPTS_COMPILE | sed -e "s,TEAMBUILDER,$hosts,"` +fi + +#Connect to the internet before we start enything +# +kppp_connect + +# Guess what? We'll finally start checking out the modules. :-) +# +# If we want to use unsermake, update that at the very beginning, so that +# all the modules make use of the most recent unsermake version. +# +if [ -n "$USE_UNSERMAKE" ] && [ "$dosvnupdate" -eq 1 ]; then + echo -n "Updating unsermake copy in $UNSERMAKE_PATH..." + cd "$UNSERMAKE_PATH" + svn up > /dev/null 2>&1 + echo $str_okay +fi + +BASE_CONFIGUREFLAGS="$CONFIGUREFLAGS" +BASE_SUBDIR=$SUBDIR +BASE_CHECKOUT_PARTIAL="$CHECKOUT_PARTIAL" + +for module in $modules; do + module_variable SUBDIR + SUBDIR=`echo $SUBDIR | sed -e "s,@MODULE@,$module,"` + module_variable CHECKOUT_PARTIAL + + if [ -n "$KDELOGDIR" ]; then + rm -f $KDELOGDIR/$module-build-* + rm -f $KDELOGDIR/$module-failed-* + rm -f $KDELOGDIR/$module-finished-* + logfile="$KDELOGDIR/$module-build-$dateformat.log" + echo "===============================================================================" > $logfile + echo "Build log of module $module, started on `date +%c`" >> $logfile + echo "===============================================================================" >> $logfile + echo "" >> $logfile + fi + + set_title "Updating module $module..." + if [ "$dosvnupdate" -eq 0 ]; then + echo -n "Checking module $module (no svn update)..." + cmd_update_raw="echo Warning: no svn update of module $module - as requested" + else + if [ -n "$BRANCH" ]; then + echo -n "Updating module $module ($BRANCH)..." + else + echo -n "Updating module $module ..." + fi + prepare_update + cmd_update_raw="$cmd_update" + fi + + log_section "Update $cmd_update" + + if [ "$dosvnupdate" -ne 0 -a -n "$CHECKOUT_PARTIAL" ]; then + cmd_update_raw="$cmd_update -l $module" + fi + + if test -d $KDESRCDIR/$module; then + cd $KDESRCDIR/$module + else + cd $KDESRCDIR + fi + + if ! log_cmd "$cmd_update_raw"; then + echo "$str_error" + continue + fi + + if [ "$dosvnupdate" -ne 0 -a -n "$CHECKOUT_PARTIAL" ]; then + prepare_update toponly + echo "defined subdirs!" + for part_dir in $CHECKOUT_PARTIAL; do + echo -n "Updating subdirectory $part_dir" + log_cmd "$cmd_update $module/$part_dir" + echo " $str_okay" + done + echo -n "Final touch..." + fi + + if [ "$CVS_CLEAN" = "yes" -a -e admin/Makefile.common -a "$INCREMENTAL_BUILD" != "yes" ]; then + cmd_make_cvs_clean="$MAKE -f admin/Makefile.common cvs-clean" + fi + + if ! log_cmd "$cmd_make_cvs_clean"; then + echo "$str_error" + continue + fi + + if [ ! -e Makefile.cvs ]; then + echo "$str_okay" + continue + fi + + if [ $domakecvs = 1 ] ; then + make_makefile_cvs + else + echo "$src_okay" + continue + fi + + log_endsection "Updating" +done + +# Now disconnect from the internet as we've finished updating +# +kppp_disconnect + +for module in $modules; do + module_variable CONFIGUREFLAGS + + # Nothing to do for kde-common + [ "$module" = "kde-common" ] && continue + logfile="$KDELOGDIR/$module-build-$dateformat.log" + + critical=0 + for m in $critical_modules; do if [ $m = $module ]; then critical=1; fi; done + + if ! cd $KDESRCDIR/$module; then + if [ $critical -eq 1 ]; then + echo "ERROR: Could not change into directory $KDESRCDIR/$module!" + exit $err_change_into_mod_dir + else + echo "WARNING: Could not change into directory $KDESRCDIR/$module." + echo "WARNING: Skipping module $module." + continue + fi + fi + + # Check whether 'make -f Makefile.cvs' has been called. + # + if [ ! -e "Makefile.in" ]; then + if [ $critical -eq 1 ]; then + echo "ERROR: Please execute '$MAKE -f Makefile.cvs' first for module $module!" + exit $err_no_makefile_in + else + echo "WARNING: '$MAKE -f Makefile.cvs' seems not to be executed for" + echo "WARNING: module $module, skipping compilation." + continue + fi + fi + + echo "Building module: $module" + + if [ -n "$KDEBUILDDIR" ]; then + + if [ "$BUILD_CLEAN" = "yes" -a "$INCREMENTAL_BUILD" != "yes" ]; then + if [ -d $KDEBUILDDIR/$module ]; then + echo -n " Removing build dir..." + set_title "Removing build dir for module $module..." + rm -rf $KDEBUILDDIR/$module + echo $str_okay + fi + fi + mkdir -p $KDEBUILDDIR/$module + cd $KDEBUILDDIR/$module + fi + + # Configure the module. + # + echo -n " Configuring..." + set_title "Configuring module $module..." + if [ -n "$KDEBUILDDIR" ]; then + cmd_configure="$KDESRCDIR/$module/configure $CONFIGUREFLAGS --with-qt-dir=$QTDIR" + else + cmd_configure="./configure $CONFIGUREFLAGS --with-qt-dir=$QTDIR" + fi + if echo $CONFIGUREFLAGS | grep -v -- "--prefix=" 2>/dev/null >/dev/null; then + cmd_configure="$cmd_configure --prefix=$KDEDIRS" + fi + [ "$NICECOMPILE" = yes ] && cmd_configure="nice $cmd_configure" + configure_skipped=0 + if [ "$INCREMENTAL_BUILD" = "yes" -a -e "Makefile" ]; then + cmd_configure_orig=$cmd_configure + cmd_configure="echo Warning: no configure for module $module - as requested" + configure_skipped=1 + fi + log_section "Configuration" + if [ -n "$KDELOGDIR" -a $configure_skipped -ne 1 ]; then + echo $cmd_configure >> $logfile + fi + + if ! log_cmd "$cmd_configure"; then + echo "$str_error" + move_logfile "failed" + [ -n "$cmd_compress" ] && eval "$cmd_compress $logfile" + [ $critical -eq 0 ] || exit $err_compile_fail + continue + fi + + log_endsection "Configuration" + if [ $configure_skipped -eq 1 ]; then + echo " skipped." + else + echo "$str_okay" + fi + + if [ $configure_skipped -eq 1 ]; then + echo -n " Updating Makefile..." + cmd_make_makefile="$MAKE $MAKE_OPTS Makefile" + [ "$NICECOMPILE" = yes ] && cmd_make_makefile="nice $cmd_make_makefile" + log_section "Updating Makefile" + if [ -n "$KDELOGDIR" ]; then + echo $cmd_make_makefile >> $logfile + fi + if ! log_cmd "$cmd_make_makefile"; then + echo -n " Falling back to normal Makefile.cvs ..." + worked=0 + + if [ -n "$KDEBUILDDIR" ]; then + cd $KDESRCDIR/$module # get back to the supermarket + fi + rm Makefile.in + make_makefile_cvs + if [ -n "$KDEBUILDDIR" ]; then + cd $KDEBUILDDIR/$module # got the sugar, honey? + fi + echo -n " Configuring..." + if log_cmd "$cmd_configure_orig"; then + if log_cmd "$cmd_make_makefile"; then + worked=1 + fi + fi + if [ $worked -ne 1 ]; then + echo "$str_error" + move_logfile "failed" + [ -n "$cmd_compress" ] && eval "$cmd_compress $logfile" + [ $critical -eq 0 ] || exit $err_compile_fail + continue + fi + fi + + log_endsection "Updating Makefile" + echo "$str_okay" + fi + + # Compile the module. + # + echo -n " Compiling..." + set_title "Compiling module $module..." + use_compile_target=no + [ -f $KDESRCDIR/$module/Makefile.am.in ] && [ -n "$MAKE_OPTS_COMPILE" ] && [ -n "$USE_UNSERMAKE" ] \ + && ! (echo $NO_UNSERMAKE_MODULES | grep $module > /dev/null 2>&1) \ + && use_compile_target=yes + if [ "$use_compile_target" = yes ]; then + cmd_make="$MAKE $MAKE_OPTS_COMPILE" + else + cmd_make="$MAKE $MAKE_OPTS" + fi + + [ $critical -eq 0 ] && cmd_make="$cmd_make -k" + [ "$NICECOMPILE" = yes ] && cmd_make="nice $cmd_make" + [ "$use_compile_target" = yes ] && cmd_make="$cmd_make compile" + + log_section "Compilation $cmd_make" + log_cmd "$cmd_make" + retval=$? + + if [ $retval -eq 0 ]; then + log_endsection "Compilation" + echo "$str_okay" + + # Link the module + if [ "$use_compile_target" = yes ]; then + echo -n " Linking..." + set_title "Linking module $module..." + cmd_make="$MAKE $MAKE_OPTS" + [ $critical -eq 0 ] && cmd_make="$cmd_make -k" + [ "$NICECOMPILE" = yes ] && cmd_make="nice $cmd_make" + + log_section "Linking $cmd_make" + log_cmd "$cmd_make" + retval=$? + if [ $retval -eq 0 ]; then + log_endsection "Linking" + echo "$str_okay" + fi + fi + fi + + if [ $retval -eq 0 ]; then + # Install the module. + # + cmd_make_install="$MAKE $MAKE_OPTS" + [ $critical -eq 0 ] && cmd_make_install="$cmd_make_install -k" + [ "$NICECOMPILE" = yes ] && cmd_make_install="nice $cmd_make_install" + cmd_make_install="$cmd_make_install install" + if [ ! -w $KDEDIRS ]; then + eval "$cmd_sudo \"$cmd_make_install\"" + else + install_module + fi + else + echo "$str_error" + # Attempt to display the actual error + grep -m1 -A2 ":[0-9]*: error:" $logfile + move_logfile "failed" + [ -n "$cmd_compress" ] && eval "$cmd_compress $logfile" + [ $critical -eq 0 ] || exit $err_compile_fail + fi +done + +set_title "KDE build finished." + +exit $err_no_error + +=head1 NAME + +kde-build - Updates and recompiles a tree of KDE modules + +=head1 SYNOPSIS + + kde-build + +=head1 DESCRIPTION + +kde-build has been designed to keep a local copy of several KDE +modules up to date and recompile them. Those modules have to be saved in a +common directory, e.g. something like + + ~/kde-src/ + | + +-> kdelibs/ + | + +-> kdebase/ + | + \-> kdenetwork/ + +In this case, the KDE source directory would be ~/kde-src/. The script will +take care of compiling them in the correct order, checks for dependencies +and resolves them as far as possible. + +Please note that, prior to first invocation of the script, the configuration +file 'F' has to be modified to reflect the local environment, +such as paths etc. + +=head1 RETURN VALUE + +The following error codes are returned by the script. + +0 - No error seems to have occured. + +1 - The script could not change into the directory of a module. + +2 - The script could not open the file 'Makefile.in' of a module. + +3 - The configuration of a module failed. + +4 - The compilation of a module failed. + +5 - The installation of a module failed. + +6 - An invalid source directory was specified. + +7 - An invalid Qt directory was specified. + +11 - The configuration file F couldn't be loaded. + +12 - Both CVS_CLEAN and INCREMENTAL_BUILD were set + +13 - Both BUILD_CLEAN and INCREMENTAL_BUILD were set + +=head1 EXAMPLES + + cd ~/scripts/; vi ./kde-buildrc; ./kde-build + +=head1 BUGS + +Probably. + +=head1 TODO + +Add a DIAGNOSIS section to this man page. + +=head1 AUTHOR + +Frerich Raabe + +=cut + +# vim:et:ts=2:sw=2 diff --git a/scripts/kde-buildrc b/scripts/kde-buildrc new file mode 100644 index 00000000..e3e31d14 --- /dev/null +++ b/scripts/kde-buildrc @@ -0,0 +1,205 @@ +################################################################################ +# Configures the kde-build script. # +# (c) 2000, 2001, 2002, 2003 by Frerich Raabe # +# (c) 2002, 2003 by Stephan Kulow # +################################################################################ + +# Where are your KDE sources? +# +KDESRCDIR="$PWD" + +# Where your KDE should be build, leave empty if built in KDESRCDIR +# +KDEBUILDDIR="" + +# Where shall I put the binaries? +# +KDEDIRS="/opt/kde" + +# Where is your Qt 3.3.x? +# +QTDIR="/usr/lib/qt3" + +# Should kppp be used to connect to the internet? +# +USE_KPPP="FALSE" + +#Set this higher if it takes a long time to load kppp on your machine +# +KPPP_LOAD_TIME="5" + +#Set this higher if it takes a long time for you to connect +# +KPPP_CONNECT_TIME="50" + +# If you would like logfiles of the compilation process, specify a directory +# here in which the logfiles will be saved. If you want to disable logfile +# generation, leave this blank. +# +KDELOGDIR="$KDESRCDIR/log" + +# Do you want the logfiles to be compressed? Set this variable to "yes" to +# make the script compress the logfiles using bzip2 (using gzip as a +# fallback is bzip2 cannot be found) and thereby save some diskspace. +# +COMPRESSLOGS="no" + +# Whether or not you want to compile and install the kdesupport module. +# +USEKDESUPPORT="yes" + +# Add modules you want to get compiled to this space-seperated list. Note that +# you don't have to mention the fundamental modules 'arts, 'kdesupport', +# 'kdelibs' and 'kdebase' here since the kde-build script will care about them +# automatically. +# See http://wiki.kdenews.org/tiki-index.php?page=KDE+CVS+Structure for a list +# of available modules including extragear-* or koffice. + +KDEMODULES="kdetoys" + +#KDEMODULES="kdeaccessibility kdeadmin kdeartwork kdebindings kdeedu kdegames kdegraphics kdemultimedia kdenetwork kdepim kdesdk kdetoys kdeutils kdeaddons kdevelop kdewebdev koffice" +#KDEMODULES="$KDEMODULES extragear-libs extragear-multimedia extragear-sysadmin extragear-office extragear-addons extragear-graphics extragear-network extragear-pim extragear-toys extragear-utils" + + +# If you only want to svn update the listed modules, set it to yes. +# +ONLYLISTEDMODULES=yes + +# Do you want a clean install? This is recommended but please note that +# you cannot use your previous KDE desktop while the compilation is +# running. Set this to "no" to install the new snapshot over the previous +# one, otherwise set it to "yes". +# +INSTALLFROMSCRATCH="no" + +# Do you plan to use this box otherwise while compiling? If so, you'd +# probably set this variable to "yes". If this is set to "no", the +# compilation process will try to eat up all the ressources, which speeds up +# the overall progress but makes it a PITA to work on this box. ;) +# +NICECOMPILE="yes" + +# Set this variable to "yes" to activate certain cheap tweaks to speed up the +# compilation process. Those tweaks mainly consist of lowering the +# optimization of the resulting binary code. +# +TWEAKCOMPILE="no" + +# For SVN users only: Do you have a SVN account? If so, set this variable to +# the correct name, otherwise leave this blank to use anonymous SVN. +# +ACCOUNT="" + +# For SVN users only: set this to "yes" in case you access svn.kde.org via +# SSH. Otherwise, set this to "no." +# +SSHACCOUNT="no" + +# In case you left the ACCOUNT value empty (and thus use anonymous SVN +# access), you can specify an anonsvn mirror here. Check +# http://developer.kde.org/source/anonsvn.html for a list of mirror servers. +# +ANONSVNROOT="svn://anonsvn.kde.org/home/kde" + +# Do you want any special path from subversion? If so, specify its path here e.g. +# /branches/KDE/3.3 or /tags/KDE/3.3.0. If you want the checked out branch, +# leave this empty. Use "/trunk" if you want the development branch for sure +# (leaving it empty will simply update whatever is there) +# +# you can specify a module specific branch in through PATH_ARTS +# +SUBDIR="/trunk/KDE/@MODULE@" +SUBDIR_KDESUPPORT=/trunk/kdesupport +for esubdir in libs multimedia sysadmin office addons graphics network pim toys utils; do + var=SUBDIR_EXTRAGEAR_`echo $esubdir | tr 'a-z' 'A-Z'` + eval "$var=/trunk/extragear/$esubdir" +done + +# SUBDIR_ARTS=/branches/arts/1.3 + +# Do you want only some subdirs from some module? Specify similiar to the +# below syntax +#CHECKOUT_PARTIAL_KDEMULTIMEDIA=juk +#CHECKOUT_PARTIAL_KDEEXTRAGEAR_2="kile konversation" + +# If there are any modules in $KDESRCDIR which you don't want to be updated, +# you can specify them in this space-seperated list, e.g. "qt-copy kde-common". +# +EXCLUDE="" + +# --- TODO: not supported anymore with svn --- +# If you would like a virgin svn copy set this field to "yes", otherwise set +# this to "no" (if set to "yes", 'make -f admin/Makefile.common cvs-clean' +# is executed for every module). +# Developers might find this pretty dangerous considering that they could have +# forgotten to 'svn add' a file... ;-) +# Users who want to stay at the bleeding edge will want to activate this in +# order to make sure there aren't any remains of a previous compile. +# +#CVS_CLEAN="no" + +# Rely on the dependencies for Makefiles and configure? +# If you dare to trust the build system, set it to "yes" ;-) +# +INCREMENTAL_BUILD="yes" + +# Flags to be passed to the 'configure' script. +# A note: --enable-debug adds minimum debug symbols while an appended +# =full gives you the full power and fills up your beloved hdd much better +# --disable-closure is actually prefered if it works for you. It will +# not create closure targets but link with some compiler flags to make +# sure the compiler will fail when undefined symbols are there - which +# is much faster if it works for you. +# +# You can define module-specific configure flags using +# CONFIGUREFLAGS_ARTS, CONFIGUREFLAGS_KDELIBS, etc. +# NOTE: These used to be appended to the general $CONFIGUREFLAGS value. +# This is no longer true, you need to use $CONFIGUREFLAGS in the _MODNAME +# version. In case the module name contains dashes ('-'), those needs to +# be replaced with underscores. +# +CONFIGUREFLAGS="--enable-debug --disable-closure" +# example: CONFIGUREFLAGS_KDEPIM="$CONFIGUREFLAGS --enable-debug=full" +# example: CONFIGUREFLAGS_KDEEXTRAGEAR_2="$CONFIGUREFLAGS --without-java" + +# Set it to the path for unsermake if you want to test it instead of +# automake. +# USE_UNSERMAKE="$KDESRCDIR/kdenonbeta/unsermake/unsermake" + +# List modules here for which unsermake should not be used. +# +NO_UNSERMAKE_MODULES="kdenonbeta kdebindings" + +# If you use an extra build directory (KDEBUILDDIR), setting this to yes will +# remove a module's build directory before configure is called. +# +BUILD_CLEAN=no + +# If you would like to pass any parameters to make add them here. If you +# do not want to add any parameters leave this empty. +# example: +# MAKE_OPTS="-j 4 -l 4" +# +MAKE_OPTS="" + +# If you use unsermake, you can define different flags for the actual +# compilation process. This is useful if you distribute the compilation +# process over several computers, but need to link on one. So you would +# define -j3 here and nothing above. +# You can also use the special "DISTCC" keyword here; if found, it will +# be replaced by the number of hosts listed in the $DISTCC_HOSTS +# environment variable, multiplied by two. Hence, 'DISTCC_HOSTS="a b c"' and +# 'MAKE_OPTS_COMPILE="-jDISTCC"' will result in 'MAKE_OPTS_COMPILE="-j6"'. +# If you set something here, the value will be used alone, otherwise +# MAKE_OPTS will be used alone. +# You can use the special "TEAMBUILDER" keyword too, it will put the +# returned value of tbcompiler -joblimit in there. +# +#MAKE_OPTS_COMPILE="" + +# If you would like to compile with a different make, please set it +# here. +# +#MAKE=make + +# vim:et:ts=2:sw=2 diff --git a/scripts/kde-devel-emacs.el b/scripts/kde-devel-emacs.el new file mode 100644 index 00000000..55955f0f --- /dev/null +++ b/scripts/kde-devel-emacs.el @@ -0,0 +1,1890 @@ +;; -*- emacs-lisp -*- + +; To use this file, add this to your .emacs, uncommented : +;(load "cc-engine.elc") +;(load "~/kde2/kdesdk/scripts/kde-devel-emacs.el") +; (setq auto-mode-alist +; (append '(("\\.h$" . c++-mode)) auto-mode-alist)) + + +; Tip: also add (gnuserv-start), to be able to use gnuclient to open new files from a shell + +; Add (setq magic-keys-mode t) to your .xemacs/init.el or ~/.emacs (before loading this file) +; to enable the magic keys in C++ mode (auto-insertion of spaces and newlines). + +; See the end of this file for the list of key bindings and for customizing them + +; This file is maintained by David Faure + + +; Global variables used to differentiate between different emacs +; versions : +; emacs - t if GNU/Emacs is used +; xemacs - t if XEmacs is being used + +(if (string= (substring (emacs-version) 0 6) "XEmacs") + (progn + (setq emacs nil) + (setq xemacs t)) + (progn + (setq emacs t) + (setq xemacs nil))) + + +;; ------- First part, from Arnt's "c++ stuff" + +(defun agulbra-c++-tab (arg) + "Do the right thing about tabs in c++ mode" + (interactive "*P") + (cond + ((and (not (looking-at "[A-Za-z0-9]")) + (save-excursion + (forward-char -1) + (looking-at "[A-Za-z0-9:>_\\-\\&\\.(){}\\*\\+/]"))) + (dabbrev-expand arg)) + (t + (save-excursion + (beginning-of-line) + (c-indent-command))))) + +(defun agulbra-clean-out-spaces () + "Remove spaces at ends of lines" + (interactive) + (and (not buffer-read-only) + (save-excursion + (goto-char (point-min)) + (let ((count 0) + (bmp (buffer-modified-p))) + (while (re-search-forward "[ \t]+$" nil t) + (setq count (1+ count)) + (replace-match "" t t)) + (set-buffer-modified-p bmp) + (and (buffer-modified-p) + (basic-save-buffer)))))) + +; the above used to contain (untabify (point-min) (point-max)) too + +;; it seems that recursion in agulbra-clean-out-spaces trashes Gnu/Emacs stack +;; one of the functions in there has to behave differently than its XEmacs +;; counterpart does, if you're reading this in a middle of may 2002 then +;; please email me (Zack) at zackrat@att.net and bug me to finally fix this +(defun agulbra-c++-clean-out-spaces () + "Remove spaces at ends of lines, only in c++ mode" + (interactive) + (and (eq major-mode 'c++-mode) + (if xemacs + (agulbra-clean-out-spaces) + ))) + +(add-hook 'find-file-hooks 'agulbra-c++-clean-out-spaces) +(add-hook 'write-file-hooks 'agulbra-c++-clean-out-spaces) + +(defun agulbra-delete-into-nomenclature (&optional arg) + "Delete forward until the end of a nomenclature section or word. +With arg, to it arg times." + (interactive "p") + (save-excursion + (let ((b (point-marker))) + (c-forward-into-nomenclature arg) + (delete-region b (point-marker))))) + + +(setq c++-mode-hook + (lambda () + (font-lock-mode) + (c-set-style "stroustrup") + (setq c-tab-always-indent nil + insert-tab-mode nil + indent-tabs-mode nil + fume-auto-rescan-buffer-p nil + c-basic-offset 4 + c-access-key "\\<\\(signals\\|k_dcop\\|\\(public\\|protected\\|private\\)\\([ ]+slots\\)?\\)\\>:" + c-hanging-comment-ender-p nil + c-offsets-alist (append '((case-label . 0) + (access-label . -) + (label . 0) + (statement-cont . c-lineup-math) + ) c-offsets-alist)) + (cond ((string-match "^\\(.*/qt/src\\)/.*/" buffer-file-truename) + (progn + (make-local-variable 'compile-command) + (setq compile-command + (concat "make -k -j 3 -C " + (substring buffer-file-truename + (match-beginning 1) (match-end 1)) + " GNUmakefile.debug && make -k -j 3 -C " + (substring buffer-file-truename + (match-beginning 1) (match-end 1)) + " -f GNUmakefile.debug")))) + ((string-match "^\\\(.*/2x/src\\\)/.*/" buffer-file-truename) + (progn + (make-local-variable 'compile-command) + (setq compile-command + (concat "make -k -C " + (substring buffer-file-truename + (match-beginning 1) + (match-end 1))))))) + (define-key c++-mode-map "\C-m" 'newline-and-indent) + (define-key c++-mode-map "\C-i" 'agulbra-c++-tab) + (define-key c++-mode-map "\ef" 'c-forward-into-nomenclature) + (define-key c++-mode-map "\ed" 'agulbra-delete-into-nomenclature) + (define-key c++-mode-map "\eb" 'c-backward-into-nomenclature) + + ; Add (setq magic-keys-mode t) to your .emacs (before loading this file) + ; to enable the magic keys in C++ mode. + (and (boundp 'magic-keys-mode) + (progn + (define-key c++-mode-map [\(] 'insert-parens) + (define-key c++-mode-map [\)] 'insert-parens2) + (define-key c++-mode-map [,] 'insert-comma) + (define-key c++-mode-map [\{] 'insert-curly-brace) + )) +)) + +(setq c-mode-hook + (lambda () + (font-lock-mode) + (setq c-tab-always-indent nil + c-basic-offset 4 + c-offsets-alist (append '((case-label . 4) + (access-label . -) + (label . 0) + (statement-cont . c-lineup-math) + ) c-offsets-alist)))) + + +(defun agulbra-make-member () + "make a skeleton member function in the .cpp or .cc file" + (interactive) + (let ((class nil) + (function nil) + (file (buffer-file-name)) + (insertion-string nil) + (start nil)) + (save-excursion + (and (re-search-backward "^class[ \t]" nil t) + (progn + (forward-word 1) + (while (looking-at "[ \t]*Q_EXPORT") + (forward-word 2)) + (while (looking-at "[ \t]") + (forward-char 1)) + (setq start (point)) + (while (looking-at "[A-Za-z0-9_]") + (forward-char 1)) + (setq class (buffer-substring start (point)))))) + (progn + (and (looking-at "$") + (progn + (search-backward ")" nil t) + (forward-char) + (backward-sexp))) + (and (stringp class) + (re-search-backward "^[ \t]") + (progn + (while (looking-at "[ \t]") + (forward-char 1)) + (setq start (point)) + (and (search-forward "(" nil t) + (progn + (forward-char -1) + (forward-sexp))) + (and (looking-at "[ \t]+const") + (forward-word 1)) + (and (looking-at ";") + (setq function (buffer-substring start (point)))) + (re-search-forward "(" nil t)))) + (and (stringp function) + (progn ;; get rid of virtual, static, multiple spaces, default values. + (and (string-match "[ \t]*\\[ \t]*" function) + (setq function (replace-match " " t t function))) + (and (string-match "^\\(virtual\\>\\)?[ \t]*" function) + (setq function (replace-match "" t t function))) + (and (string-match "^\\(static\\>\\)?[ \t]*" function) + (setq function (replace-match "" t t function))) + (while (string-match " +" function) + (setq function (replace-match " " t t function))) + (while (string-match "\t+" function) + (setq function (replace-match " " t t function))) + (while (string-match " ?=[^,)]+" function) + (setq function (replace-match " " t t function))) + (while (string-match " +," function) + (setq function (replace-match "," t t function))))) + (and (stringp function) + (stringp class) + (stringp file) + (progn + (cond ((string-match (concat "^ *" class "[ \\t]*(") function) + (progn + (setq insertion-string + (concat + (replace-match + (concat class "::" class "(") + t t function) + "\n{\n \n}\n")))) + ((string-match (concat "^ *~" class "[ \\t]*(") function) + (progn + (setq insertion-string + (concat + (replace-match + (concat class "::~" class "(") + t t function) + "\n{\n \n}\n")))) + ((string-match " *\\([a-zA-Z0-9_]+\\)[ \\t]*(" function) + (progn + (setq insertion-string + (concat + (replace-match + (concat " " class "::" "\\1(") + t nil function) + "\n{\n \n}\n")))) + (t + (error (concat "Can't parse declaration ``" + function "'' in class ``" class + "'', aborting")))) + (stringp insertion-string)) + (string-match "\\.h$" file) + (setq f (replace-match ".cpp" t t file)) + (if (file-readable-p f ) + (message "") + (progn + (string-match "\\.h$" file) + (setq f (replace-match ".cc" t t file)) + )) + (find-file f) + (progn + (goto-char (point-max)) + (insert insertion-string) + (forward-char -3) + (save-excursion + (and (string-match ".*/" file) + (setq file (replace-match "" t nil file))) + (or (re-search-backward + (concat "^#include *\"" file "\"$") nil t) + (progn + (goto-char (point-min)) + (re-search-forward "^$" nil t) + (insert "\n#include \"" file "\"\n"))))))) + (fume-rescan-buffer) +) + + +(setq compilation-error-regexp-systems-list '(gnu of comma 4bsd) + compilation-ask-about-save nil) + + +(defun c-guess-basic-syntax () + (save-excursion + (save-restriction + (beginning-of-line) + (let* ((indent-point (point)) + (case-fold-search nil) + (fullstate (c-parse-state)) + (state fullstate) + literal containing-sexp char-before-ip char-after-ip lim + syntax placeholder c-in-literal-cache inswitch-p + tmpsymbol keyword injava-inher special-brace-list + ;; narrow out any enclosing class or extern "C" block + (inclass-p (c-narrow-out-enclosing-class state indent-point)) + inenclosing-p) + ;; check for meta top-level enclosing constructs, possible + ;; extern language definitions, possibly (in C++) namespace + ;; definitions. + (save-excursion + (save-restriction + (widen) + (if (and inclass-p + (progn + (goto-char (aref inclass-p 0)) + (looking-at (concat c-extra-toplevel-key "[^_]")))) + (let ((enclosing (match-string 1))) + (cond + ((string-equal enclosing "extern") + (setq inenclosing-p 'extern)) + ((string-equal enclosing "namespace") + (setq inenclosing-p 'namespace)) + ))))) + ;; get the buffer position of the most nested opening brace, + ;; if there is one, and it hasn't been narrowed out + (save-excursion + (goto-char indent-point) + (skip-chars-forward " \t}") + (skip-chars-backward " \t") + (while (and state + (not containing-sexp)) + (setq containing-sexp (car state) + state (cdr state)) + (if (consp containing-sexp) + ;; if cdr == point, then containing sexp is the brace + ;; that opens the sexp we close + (if (= (cdr containing-sexp) (point)) + (setq containing-sexp (car containing-sexp)) + ;; otherwise, ignore this element + (setq containing-sexp nil)) + ;; ignore the bufpos if its been narrowed out by the + ;; containing class or does not contain the indent point + (if (or (<= containing-sexp (point-min)) + (>= containing-sexp indent-point)) + (setq containing-sexp nil))))) + + ;; set the limit on the farthest back we need to search + (setq lim (or containing-sexp + (if (consp (car fullstate)) + (cdr (car fullstate)) + nil) + (point-min))) + + ;; cache char before and after indent point, and move point to + ;; the most likely position to perform the majority of tests + (goto-char indent-point) + (skip-chars-forward " \t") + (setq char-after-ip (char-after)) + (c-backward-syntactic-ws lim) + (setq char-before-ip (char-before)) + (goto-char indent-point) + (skip-chars-forward " \t") + + ;; are we in a literal? + (setq literal (c-in-literal lim)) + + ;; now figure out syntactic qualities of the current line + (cond + ;; CASE 1: in a string. + ((memq literal '(string)) + (c-add-syntax 'string (c-point 'bopl))) + ;; CASE 2: in a C or C++ style comment. + ((memq literal '(c c++)) + (c-add-syntax literal (car (c-literal-limits lim)))) + ;; CASE 3: in a cpp preprocessor macro continuation. + ((and (eq literal 'pound) + (/= (save-excursion + (c-beginning-of-macro lim) + (setq placeholder (point))) + (c-point 'boi))) + (c-add-syntax 'cpp-macro-cont placeholder)) + ;; CASE 4: In-expression statement. + ((and (or c-inexpr-class-key c-inexpr-block-key c-lambda-key) + (setq placeholder (c-looking-at-inexpr-block))) + (setq tmpsymbol (assq (car placeholder) + '((inexpr-class . class-open) + (inexpr-statement . block-open)))) + (if tmpsymbol + ;; It's a statement block or an anonymous class. + (setq tmpsymbol (cdr tmpsymbol)) + ;; It's a Pike lambda. Check whether we are between the + ;; lambda keyword and the argument list or at the defun + ;; opener. + (setq tmpsymbol (if (eq char-after-ip ?{) + 'inline-open + 'lambda-intro-cont))) + (goto-char (cdr placeholder)) + (back-to-indentation) + (c-add-syntax tmpsymbol (point)) + (unless (eq (point) (cdr placeholder)) + (c-add-syntax (car placeholder)))) + ;; CASE 5: Line is at top level. + ((null containing-sexp) + (cond + ;; CASE 5A: we are looking at a defun, brace list, class, + ;; or inline-inclass method opening brace + ((setq special-brace-list + (or (and c-special-brace-lists + (c-looking-at-special-brace-list)) + (eq char-after-ip ?{))) + (cond + ;; CASE 5A.1: extern language or namespace construct + ((save-excursion + (goto-char indent-point) + (skip-chars-forward " \t") + (and (c-safe (progn (c-backward-sexp 2) t)) + (looking-at (concat c-extra-toplevel-key "[^_]")) + (setq keyword (match-string 1) + placeholder (point)) + (or (and (string-equal keyword "namespace") + (setq tmpsymbol 'namespace-open)) + (and (string-equal keyword "extern") + (progn + (c-forward-sexp 1) + (c-forward-syntactic-ws) + (eq (char-after) ?\")) + (setq tmpsymbol 'extern-lang-open))) + )) + (goto-char placeholder) + (c-add-syntax tmpsymbol (c-point 'boi))) + ;; CASE 5A.2: we are looking at a class opening brace + ((save-excursion + (goto-char indent-point) + (skip-chars-forward " \t{") + ;; TBD: watch out! there could be a bogus + ;; c-state-cache in place when we get here. we have + ;; to go through much chicanery to ignore the cache. + ;; But of course, there may not be! BLECH! BOGUS! + (let ((decl + (let ((c-state-cache nil)) + (c-search-uplist-for-classkey (c-parse-state)) + ))) + (and decl + (setq placeholder (aref decl 0))) + )) + (c-add-syntax 'class-open placeholder)) + ;; CASE 5A.3: brace list open + ((save-excursion + (c-beginning-of-statement-1 lim) + ;; c-b-o-s could have left us at point-min + (and (bobp) + (c-forward-syntactic-ws indent-point)) + (if (looking-at "typedef[^_]") + (progn (c-forward-sexp 1) + (c-forward-syntactic-ws indent-point))) + (setq placeholder (c-point 'boi)) + (or (consp special-brace-list) + (and (or (save-excursion + (goto-char indent-point) + (setq tmpsymbol nil) + (while (and (> (point) placeholder) + (= (c-backward-token-1 1 t) 0) + (/= (char-after) ?=)) + (if (and (not tmpsymbol) + (looking-at "new\\>[^_]")) + (setq tmpsymbol 'topmost-intro-cont))) + (eq (char-after) ?=)) + (looking-at "enum[ \t\n]+")) + (save-excursion + (while (and (< (point) indent-point) + (= (c-forward-token-1 1 t) 0) + (not (memq (char-after) '(?\; ?\())))) + (not (memq (char-after) '(?\; ?\())) + )))) + (if (and (c-major-mode-is 'java-mode) + (eq tmpsymbol 'topmost-intro-cont)) + ;; We're in Java and have found that the open brace + ;; belongs to a "new Foo[]" initialization list, + ;; which means the brace list is part of an + ;; expression and not a top level definition. We + ;; therefore treat it as any topmost continuation + ;; even though the semantically correct symbol still + ;; is brace-list-open, on the same grounds as in + ;; case 10B.2. + (progn + (c-beginning-of-statement-1 lim) + (c-forward-syntactic-ws) + (c-add-syntax 'topmost-intro-cont (c-point 'boi))) + (c-add-syntax 'brace-list-open placeholder))) + ;; CASE 5A.4: inline defun open + ((and inclass-p (not inenclosing-p)) + (c-add-syntax 'inline-open) + (c-add-class-syntax 'inclass inclass-p)) + ;; CASE 5A.5: ordinary defun open + (t + (goto-char placeholder) + (if inclass-p + (c-add-syntax 'defun-open (c-point 'boi)) + (c-add-syntax 'defun-open (c-point 'bol))) + ))) + ;; CASE 5B: first K&R arg decl or member init + ((c-just-after-func-arglist-p) + (cond + ;; CASE 5B.1: a member init + ((or (eq char-before-ip ?:) + (eq char-after-ip ?:)) + ;; this line should be indented relative to the beginning + ;; of indentation for the topmost-intro line that contains + ;; the prototype's open paren + ;; TBD: is the following redundant? + (if (eq char-before-ip ?:) + (forward-char -1)) + (c-backward-syntactic-ws lim) + ;; TBD: is the preceding redundant? + (if (eq (char-before) ?:) + (progn (forward-char -1) + (c-backward-syntactic-ws lim))) + (if (eq (char-before) ?\)) + (c-backward-sexp 1)) + (setq placeholder (point)) + (save-excursion + (and (c-safe (c-backward-sexp 1) t) + (looking-at "throw[^_]") + (c-safe (c-backward-sexp 1) t) + (setq placeholder (point)))) + (goto-char placeholder) + (c-add-syntax 'member-init-intro (c-point 'boi)) + ;; we don't need to add any class offset since this + ;; should be relative to the ctor's indentation + ) + ;; CASE 5B.2: K&R arg decl intro + (c-recognize-knr-p + (c-add-syntax 'knr-argdecl-intro (c-point 'boi)) + (if inclass-p (c-add-class-syntax 'inclass inclass-p))) + ;; CASE 5B.3: Inside a member init list. + ((c-beginning-of-member-init-list lim) + (c-forward-syntactic-ws) + (c-add-syntax 'member-init-cont (point))) + ;; CASE 5B.4: Nether region after a C++ or Java func + ;; decl, which could include a `throws' declaration. + (t + (c-beginning-of-statement-1 lim) + (c-add-syntax 'func-decl-cont (c-point 'boi)) + ))) + ;; CASE 5C: inheritance line. could be first inheritance + ;; line, or continuation of a multiple inheritance + ((or (and c-baseclass-key + (progn + (when (eq char-after-ip ?,) + (skip-chars-forward " \t") + (forward-char)) + (looking-at c-baseclass-key))) + (and (or (eq char-before-ip ?:) + ;; watch out for scope operator + (save-excursion + (and (eq char-after-ip ?:) + (c-safe (progn (forward-char 1) t)) + (not (eq (char-after) ?:)) + ))) + (save-excursion + (c-backward-syntactic-ws lim) + (if (eq char-before-ip ?:) + (progn + (forward-char -1) + (c-backward-syntactic-ws lim))) + (back-to-indentation) + (looking-at c-class-key))) + ;; for Java + (and (c-major-mode-is 'java-mode) + (let ((fence (save-excursion + (c-beginning-of-statement-1 lim) + (point))) + cont done) + (save-excursion + (while (not done) + (cond ((looking-at c-Java-special-key) + (setq injava-inher (cons cont (point)) + done t)) + ((or (not (c-safe (c-forward-sexp -1) t)) + (<= (point) fence)) + (setq done t)) + ) + (setq cont t))) + injava-inher) + (not (c-crosses-statement-barrier-p (cdr injava-inher) + (point))) + )) + (cond + ;; CASE 5C.1: non-hanging colon on an inher intro + ((eq char-after-ip ?:) + (c-backward-syntactic-ws lim) + (c-add-syntax 'inher-intro (c-point 'boi)) + ;; don't add inclass symbol since relative point already + ;; contains any class offset + ) + ;; CASE 5C.2: hanging colon on an inher intro + ((eq char-before-ip ?:) + (c-add-syntax 'inher-intro (c-point 'boi)) + (if inclass-p (c-add-class-syntax 'inclass inclass-p))) + ;; CASE agulbrahack.1: + ((and inclass-p + c-access-key + (looking-at c-access-key)) + (c-add-syntax 'access-label (c-point 'bonl)) + ) + ;; CASE 5C.3: in a Java implements/extends + (injava-inher + (let ((where (cdr injava-inher)) + (cont (car injava-inher))) + (goto-char where) + (cond ((looking-at "throws[ \t\n]") + (c-add-syntax 'func-decl-cont + (progn (c-beginning-of-statement-1 lim) + (c-point 'boi)))) + (cont (c-add-syntax 'inher-cont where)) + (t (c-add-syntax 'inher-intro + (progn (goto-char (cdr injava-inher)) + (c-beginning-of-statement-1 lim) + (point)))) + ))) + ;; CASE 5C.4: a continued inheritance line + (t + (c-beginning-of-inheritance-list lim) + (c-add-syntax 'inher-cont (point)) + ;; don't add inclass symbol since relative point already + ;; contains any class offset + ))) + ;; CASE 5D: this could be a top-level compound statement, a + ;; member init list continuation, or a template argument + ;; list continuation. + ((c-with-syntax-table (if (c-major-mode-is 'c++-mode) + c++-template-syntax-table + (syntax-table)) + (save-excursion + (while (and (= (c-backward-token-1 1 t lim) 0) + (not (looking-at "[;{<,]")))) + (eq (char-after) ?,))) + (goto-char indent-point) + (c-beginning-of-member-init-list lim) + (cond + ;; CASE 5D.1: hanging member init colon, but watch out + ;; for bogus matches on access specifiers inside classes. + ((and (save-excursion + (setq placeholder (point)) + (c-backward-token-1 1 t lim) + (and (eq (char-after) ?:) + (not (eq (char-before) ?:)))) + (save-excursion + (goto-char placeholder) + (back-to-indentation) + (or + (/= (car (save-excursion + (parse-partial-sexp (point) placeholder))) + 0) + (and + (if c-access-key (not (looking-at c-access-key)) t) + (not (looking-at c-class-key)) + (if c-bitfield-key (not (looking-at c-bitfield-key)) t)) + ))) + (goto-char placeholder) + (c-forward-syntactic-ws) + (c-add-syntax 'member-init-cont (point)) + ;; we do not need to add class offset since relative + ;; point is the member init above us + ) + ;; CASE 5D.2: non-hanging member init colon + ((progn + (c-forward-syntactic-ws indent-point) + (eq (char-after) ?:)) + (skip-chars-forward " \t:") + (c-add-syntax 'member-init-cont (point))) + ;; CASE 5D.3: perhaps a multiple inheritance line? + ((save-excursion + (c-beginning-of-statement-1 lim) + (setq placeholder (point)) + (looking-at c-inher-key)) + (goto-char placeholder) + (c-add-syntax 'inher-cont (c-point 'boi))) + ;; CASE 5D.4: perhaps a template list continuation? + ((save-excursion + (goto-char indent-point) + (skip-chars-backward "^<" lim) + ;; not sure if this is the right test, but it should + ;; be fast and mostly accurate. + (setq placeholder (point)) + (and (eq (char-before) ?<) + (not (c-in-literal lim)))) + ;; we can probably indent it just like an arglist-cont + (goto-char placeholder) + (c-beginning-of-statement-1 lim) + (c-add-syntax 'template-args-cont (c-point 'boi))) + ;; CASE 5D.5: perhaps a top-level statement-cont + (t + (c-beginning-of-statement-1 lim) + ;; skip over any access-specifiers + (and inclass-p c-access-key + (while (looking-at c-access-key) + (forward-line 1))) + ;; skip over comments, whitespace + (c-forward-syntactic-ws indent-point) + (c-add-syntax 'statement-cont (c-point 'boi))) + )) + ;; CASE 5E: we are looking at a access specifier + ((and inclass-p + c-access-key + (looking-at c-access-key)) + (c-add-syntax 'access-label (c-point 'bonl)) + (c-add-class-syntax 'inclass inclass-p)) + ;; CASE 5F: extern-lang-close or namespace-close? + ((and inenclosing-p + (eq char-after-ip ?})) + (setq tmpsymbol (if (eq inenclosing-p 'extern) + 'extern-lang-close + 'namespace-close)) + (c-add-syntax tmpsymbol (aref inclass-p 0))) + ;; CASE 5G: we are looking at the brace which closes the + ;; enclosing nested class decl + ((and inclass-p + (eq char-after-ip ?}) + (save-excursion + (save-restriction + (widen) + (forward-char 1) + (and (c-safe (progn (c-backward-sexp 1) t)) + (= (point) (aref inclass-p 1)) + )))) + (c-add-class-syntax 'class-close inclass-p)) + ;; CASE 5H: we could be looking at subsequent knr-argdecls + ((and c-recognize-knr-p + ;; here we essentially use the hack that is used in + ;; Emacs' c-mode.el to limit how far back we should + ;; look. The assumption is made that argdecls are + ;; indented at least one space and that function + ;; headers are not indented. + (let ((limit (save-excursion + (re-search-backward "^[^ \^L\t\n#]" nil 'move) + (point)))) + (save-excursion + (c-backward-syntactic-ws limit) + (setq placeholder (point)) + (while (and (memq (char-before) '(?\; ?,)) + (> (point) limit)) + (beginning-of-line) + (setq placeholder (point)) + (c-backward-syntactic-ws limit)) + (and (eq (char-before) ?\)) + (or (not c-method-key) + (progn + (c-forward-sexp -1) + (forward-char -1) + (c-backward-syntactic-ws) + (not (or (memq (char-before) '(?- ?+)) + ;; or a class category + (progn + (c-forward-sexp -2) + (looking-at c-class-key)) + ))))) + )) + (save-excursion + (c-beginning-of-statement-1) + (not (looking-at "typedef[ \t\n]+")))) + (goto-char placeholder) + (c-add-syntax 'knr-argdecl (c-point 'boi))) + ;; CASE 5I: ObjC method definition. + ((and c-method-key + (looking-at c-method-key)) + (c-add-syntax 'objc-method-intro (c-point 'boi))) + ;; CASE 5J: we are at the topmost level, make sure we skip + ;; back past any access specifiers + ((progn + (c-backward-syntactic-ws lim) + (while (and inclass-p + c-access-key + (not (bobp)) + (save-excursion + (c-safe (progn (c-backward-sexp 1) t)) + ;; agulbrahack 2 + (and (looking-at "slots:") + (c-backward-sexp 1)) + (looking-at c-access-key))) + (c-backward-sexp 1) + (c-backward-syntactic-ws lim)) + (or (bobp) + (memq (char-before) '(?\; ?\})))) + ;; real beginning-of-line could be narrowed out due to + ;; enclosure in a class block + (save-restriction + (widen) + (c-add-syntax 'topmost-intro (c-point 'bol)) + (if inclass-p + (progn + (goto-char (aref inclass-p 1)) + (or (= (point) (c-point 'boi)) + (goto-char (aref inclass-p 0))) + (cond + ((eq inenclosing-p 'extern) + (c-add-syntax 'inextern-lang (c-point 'boi))) + ((eq inenclosing-p 'namespace) + (c-add-syntax 'innamespace (c-point 'boi))) + (t (c-add-class-syntax 'inclass inclass-p))) + )) + )) + ;; CASE 5K: we are at an ObjC or Java method definition + ;; continuation line. + ((and c-method-key + (progn + (c-beginning-of-statement-1 lim) + (beginning-of-line) + (looking-at c-method-key))) + (c-add-syntax 'objc-method-args-cont (point))) + ;; CASE 5L: we are at the first argument of a template + ;; arglist that begins on the previous line. + ((eq (char-before) ?<) + (c-beginning-of-statement-1 lim) + (c-forward-syntactic-ws) + (c-add-syntax 'template-args-cont (c-point 'boi))) + ;; CASE 5M: we are at a topmost continuation line + (t + (c-beginning-of-statement-1 lim) + (c-forward-syntactic-ws) + (c-add-syntax 'topmost-intro-cont (c-point 'boi))) + )) ; end CASE 5 + ;; (CASE 6 has been removed.) + ;; CASE 7: line is an expression, not a statement. Most + ;; likely we are either in a function prototype or a function + ;; call argument list + ((not (or (and c-special-brace-lists + (save-excursion + (goto-char containing-sexp) + (c-looking-at-special-brace-list))) + (eq (char-after containing-sexp) ?{))) + (c-backward-syntactic-ws containing-sexp) + (cond + ;; CASE 7A: we are looking at the arglist closing paren + ((and (or (c-major-mode-is 'pike-mode) + ;; Don't check this in Pike since it allows a + ;; comma after the last arg. + (not (eq char-before-ip ?,))) + (memq char-after-ip '(?\) ?\]))) + (goto-char containing-sexp) + (setq placeholder (c-point 'boi)) + (when (and (c-safe (backward-up-list 1) t) + (> (point) placeholder)) + (forward-char) + (skip-chars-forward " \t") + (setq placeholder (point))) + (c-add-syntax 'arglist-close placeholder)) + ;; CASE 7B: Looking at the opening brace of an + ;; in-expression block or brace list. + ((eq char-after-ip ?{) + (goto-char indent-point) + (setq placeholder (c-point 'boi)) + (goto-char containing-sexp) + (if (c-inside-bracelist-p placeholder + (cons containing-sexp state)) + (progn + (c-add-syntax 'brace-list-open (c-point 'boi)) + (c-add-syntax 'inexpr-class)) + (c-add-syntax 'block-open (c-point 'boi)) + (c-add-syntax 'inexpr-statement))) + ;; CASE 7C: we are looking at the first argument in an empty + ;; argument list. Use arglist-close if we're actually + ;; looking at a close paren or bracket. + ((memq char-before-ip '(?\( ?\[)) + (goto-char containing-sexp) + (setq placeholder (c-point 'boi)) + (when (and (c-safe (backward-up-list 1) t) + (> (point) placeholder)) + (forward-char) + (skip-chars-forward " \t") + (setq placeholder (point))) + (c-add-syntax 'arglist-intro placeholder)) + ;; CASE 7D: we are inside a conditional test clause. treat + ;; these things as statements + ((save-excursion + (goto-char containing-sexp) + (and (c-safe (progn (c-forward-sexp -1) t)) + (looking-at "\\[^_]"))) + (goto-char (1+ containing-sexp)) + (c-forward-syntactic-ws indent-point) + (c-beginning-of-statement-1 containing-sexp) + (if (eq char-before-ip ?\;) + (c-add-syntax 'statement (point)) + (c-add-syntax 'statement-cont (point)) + )) + ;; CASE 7E: maybe a continued method call. This is the case + ;; when we are inside a [] bracketed exp, and what precede + ;; the opening bracket is not an identifier. + ((and c-method-key + (eq (char-after containing-sexp) ?\[) + (save-excursion + (goto-char (1- containing-sexp)) + (c-backward-syntactic-ws (c-point 'bod)) + (if (not (looking-at c-symbol-key)) + (c-add-syntax 'objc-method-call-cont containing-sexp)) + ))) + ;; CASE 7F: we are looking at an arglist continuation line, + ;; but the preceding argument is on the same line as the + ;; opening paren. This case includes multi-line + ;; mathematical paren groupings, but we could be on a + ;; for-list continuation line + ((save-excursion + (goto-char (1+ containing-sexp)) + (skip-chars-forward " \t") + (not (eolp))) + (goto-char containing-sexp) + (setq placeholder (c-point 'boi)) + (when (and (c-safe (backward-up-list 1) t) + (> (point) placeholder)) + (forward-char) + (skip-chars-forward " \t") + (setq placeholder (point))) + (c-add-syntax 'arglist-cont-nonempty placeholder)) + ;; CASE 7G: we are looking at just a normal arglist + ;; continuation line + (t (c-beginning-of-statement-1 containing-sexp) + (forward-char 1) + (c-forward-syntactic-ws indent-point) + (c-add-syntax 'arglist-cont (c-point 'boi))) + )) + ;; CASE 8: func-local multi-inheritance line + ((and c-baseclass-key + (save-excursion + (goto-char indent-point) + (skip-chars-forward " \t") + (looking-at c-baseclass-key))) + (goto-char indent-point) + (skip-chars-forward " \t") + (cond + ;; CASE 8A: non-hanging colon on an inher intro + ((eq char-after-ip ?:) + (c-backward-syntactic-ws lim) + (c-add-syntax 'inher-intro (c-point 'boi))) + ;; CASE 8B: hanging colon on an inher intro + ((eq char-before-ip ?:) + (c-add-syntax 'inher-intro (c-point 'boi))) + ;; CASE 8C: a continued inheritance line + (t + (c-beginning-of-inheritance-list lim) + (c-add-syntax 'inher-cont (point)) + ))) + ;; CASE 9: we are inside a brace-list + ((setq special-brace-list + (or (and c-special-brace-lists + (save-excursion + (goto-char containing-sexp) + (c-looking-at-special-brace-list))) + (c-inside-bracelist-p containing-sexp state))) + (cond + ;; CASE 9A: In the middle of a special brace list opener. + ((and (consp special-brace-list) + (save-excursion + (goto-char containing-sexp) + (eq (char-after) ?\()) + (eq char-after-ip (car (cdr special-brace-list)))) + (goto-char (car (car special-brace-list))) + (skip-chars-backward " \t") + (if (and (bolp) + (assoc 'statement-cont + (setq placeholder (c-guess-basic-syntax)))) + (setq syntax placeholder) + (c-beginning-of-statement-1 lim) + (c-forward-token-1 0) + (if (looking-at "typedef\\>") (c-forward-token-1 1)) + (c-add-syntax 'brace-list-open (c-point 'boi)))) + ;; CASE 9B: brace-list-close brace + ((if (consp special-brace-list) + ;; Check special brace list closer. + (progn + (goto-char (car (car special-brace-list))) + (save-excursion + (goto-char indent-point) + (back-to-indentation) + (or + ;; We were between the special close char and the `)'. + (and (eq (char-after) ?\)) + (eq (1+ (point)) (cdr (car special-brace-list)))) + ;; We were before the special close char. + (and (eq (char-after) (cdr (cdr special-brace-list))) + (= (c-forward-token-1) 0) + (eq (1+ (point)) (cdr (car special-brace-list))))))) + ;; Normal brace list check. + (and (eq char-after-ip ?}) + (c-safe (progn (forward-char 1) + (c-backward-sexp 1) + t)) + (= (point) containing-sexp))) + (c-add-syntax 'brace-list-close (c-point 'boi))) + (t + ;; Prepare for the rest of the cases below by going to the + ;; token following the opening brace + (if (consp special-brace-list) + (progn + (goto-char (car (car special-brace-list))) + (c-forward-token-1 1 nil indent-point)) + (goto-char containing-sexp)) + (forward-char) + (let ((start (point))) + (c-forward-syntactic-ws indent-point) + (goto-char (max start (c-point 'bol)))) + (skip-chars-forward " \t\n\r" indent-point) + (cond + ;; CASE 9C: we're looking at the first line in a brace-list + ((= (point) indent-point) + (goto-char containing-sexp) + (c-add-syntax 'brace-list-intro (c-point 'boi)) + ) ; end CASE 9C + ;; CASE 9D: this is just a later brace-list-entry or + ;; brace-entry-open + (t (if (or (eq char-after-ip ?{) + (and c-special-brace-lists + (save-excursion + (goto-char indent-point) + (c-forward-syntactic-ws (c-point 'eol)) + (c-looking-at-special-brace-list (point))))) + (c-add-syntax 'brace-entry-open (point)) + (c-add-syntax 'brace-list-entry (point)) + )) ; end CASE 9D + )))) ; end CASE 9 + ;; CASE 10: A continued statement + ((and (not (memq char-before-ip '(?\; ?:))) + (or (not (eq char-before-ip ?})) + (c-looking-at-inexpr-block-backward containing-sexp)) + (> (point) + (save-excursion + (c-beginning-of-statement-1 containing-sexp) + (c-forward-syntactic-ws) + (setq placeholder (point)))) + (/= placeholder containing-sexp)) + (goto-char indent-point) + (skip-chars-forward " \t") + (let ((after-cond-placeholder + (save-excursion + (goto-char placeholder) + (if (and c-conditional-key (looking-at c-conditional-key)) + (progn + (c-safe (c-skip-conditional)) + (c-forward-syntactic-ws) + (if (eq (char-after) ?\;) + (progn + (forward-char 1) + (c-forward-syntactic-ws))) + (point)) + nil)))) + (cond + ;; CASE 10A: substatement + ((and after-cond-placeholder + (>= after-cond-placeholder indent-point)) + (goto-char placeholder) + (if (eq char-after-ip ?{) + (c-add-syntax 'substatement-open (c-point 'boi)) + (c-add-syntax 'substatement (c-point 'boi)))) + ;; CASE 10B: open braces for class or brace-lists + ((setq special-brace-list + (or (and c-special-brace-lists + (c-looking-at-special-brace-list)) + (eq char-after-ip ?{))) + (cond + ;; CASE 10B.1: class-open + ((save-excursion + (goto-char indent-point) + (skip-chars-forward " \t{") + (let ((decl (c-search-uplist-for-classkey (c-parse-state)))) + (and decl + (setq placeholder (aref decl 0))) + )) + (c-add-syntax 'class-open placeholder)) + ;; CASE 10B.2: brace-list-open + ((or (consp special-brace-list) + (save-excursion + (goto-char placeholder) + (looking-at "\\")) + (save-excursion + (goto-char indent-point) + (while (and (> (point) placeholder) + (= (c-backward-token-1 1 t) 0) + (/= (char-after) ?=))) + (eq (char-after) ?=))) + ;; The most semantically accurate symbol here is + ;; brace-list-open, but we report it simply as a + ;; statement-cont. The reason is that one normally + ;; adjusts brace-list-open for brace lists as + ;; top-level constructs, and brace lists inside + ;; statements is a completely different context. + (goto-char indent-point) + (c-beginning-of-closest-statement) + (c-add-syntax 'statement-cont (c-point 'boi))) + ;; CASE 10B.3: The body of a function declared inside a + ;; normal block. This can only occur in Pike. + ((and (c-major-mode-is 'pike-mode) + (progn + (goto-char indent-point) + (not (c-looking-at-bos)))) + (c-beginning-of-closest-statement) + (c-add-syntax 'defun-open (c-point 'boi))) + ;; CASE 10B.4: catch-all for unknown construct. + (t + ;; Can and should I add an extensibility hook here? + ;; Something like c-recognize-hook so support for + ;; unknown constructs could be added. It's probably a + ;; losing proposition, so I dunno. + (goto-char placeholder) + (c-add-syntax 'statement-cont (c-point 'boi)) + (c-add-syntax 'block-open)) + )) + ;; CASE 10C: iostream insertion or extraction operator + ((looking-at "<<\\|>>") + (goto-char placeholder) + (and after-cond-placeholder + (goto-char after-cond-placeholder)) + (while (and (re-search-forward "<<\\|>>" indent-point 'move) + (c-in-literal placeholder))) + ;; if we ended up at indent-point, then the first + ;; streamop is on a separate line. Indent the line like + ;; a statement-cont instead + (if (/= (point) indent-point) + (c-add-syntax 'stream-op (c-point 'boi)) + (c-backward-syntactic-ws lim) + (c-add-syntax 'statement-cont (c-point 'boi)))) + ;; CASE 10D: continued statement. find the accurate + ;; beginning of statement or substatement + (t + (c-beginning-of-statement-1 after-cond-placeholder) + ;; KLUDGE ALERT! c-beginning-of-statement-1 can leave + ;; us before the lim we're passing in. It should be + ;; fixed, but I'm worried about side-effects at this + ;; late date. Fix for v5. + (goto-char (or (and after-cond-placeholder + (max after-cond-placeholder (point))) + (point))) + (c-add-syntax 'statement-cont (point))) + ))) + ;; CASE 11: an else clause? + ((looking-at "\\[^_]") + (c-backward-to-start-of-if containing-sexp) + (c-add-syntax 'else-clause (c-point 'boi))) + ;; CASE 12: Statement. But what kind? Lets see if its a + ;; while closure of a do/while construct + ((progn + (goto-char indent-point) + (skip-chars-forward " \t") + (and (looking-at "while\\b[^_]") + (save-excursion + (c-backward-to-start-of-do containing-sexp) + (setq placeholder (point)) + (looking-at "do\\b[^_]")) + )) + (goto-char placeholder) + (c-add-syntax 'do-while-closure (c-point 'boi))) + ;; CASE 13: A catch or finally clause? This case is simpler + ;; than if-else and do-while, because a block is required + ;; after every try, catch and finally. + ((save-excursion + (and (cond ((c-major-mode-is 'c++-mode) + (looking-at "\\[^_]")) + ((c-major-mode-is 'java-mode) + (looking-at "\\<\\(catch\\|finally\\)\\>[^_]"))) + (c-safe (c-backward-sexp) t) + (eq (char-after) ?{) + (c-safe (c-backward-sexp) t) + (if (eq (char-after) ?\() + (c-safe (c-backward-sexp) t) + t) + (looking-at "\\<\\(try\\|catch\\)\\>[^_]") + (setq placeholder (c-point 'boi)))) + (c-add-syntax 'catch-clause placeholder)) + ;; CASE 14: A case or default label + ((looking-at c-switch-label-key) + (goto-char containing-sexp) + ;; check for hanging braces + (if (/= (point) (c-point 'boi)) + (c-forward-sexp -1)) + (c-add-syntax 'case-label (c-point 'boi))) + ;; CASE 15: any other label + ((looking-at c-label-key) + (goto-char containing-sexp) + ;; check for hanging braces + (if (/= (point) (c-point 'boi)) + (c-forward-sexp -1)) + (c-add-syntax 'label (c-point 'boi))) + ;; CASE 16: block close brace, possibly closing the defun or + ;; the class + ((eq char-after-ip ?}) + (let* ((lim (c-safe-position containing-sexp fullstate)) + (relpos (save-excursion + (goto-char containing-sexp) + (if (/= (point) (c-point 'boi)) + (c-beginning-of-statement-1 lim)) + (c-point 'boi)))) + (cond + ;; CASE 16A: closing a lambda defun or an in-expression + ;; block? + ((save-excursion + (goto-char containing-sexp) + (setq placeholder (c-looking-at-inexpr-block))) + (setq tmpsymbol (if (eq (car placeholder) 'inlambda) + 'inline-close + 'block-close)) + (goto-char containing-sexp) + (back-to-indentation) + (if (= containing-sexp (point)) + (c-add-syntax tmpsymbol (point)) + (goto-char (cdr placeholder)) + (back-to-indentation) + (c-add-syntax tmpsymbol (point)) + (if (/= (point) (cdr placeholder)) + (c-add-syntax (car placeholder))))) + ;; CASE 16B: does this close an inline or a function in + ;; an extern block or namespace? + ((progn + (goto-char containing-sexp) + (setq placeholder (c-search-uplist-for-classkey state))) + (goto-char (aref placeholder 0)) + (if (looking-at (concat c-extra-toplevel-key "[^_]")) + (c-add-syntax 'defun-close relpos) + (c-add-syntax 'inline-close relpos))) + ;; CASE 16C: if there an enclosing brace that hasn't + ;; been narrowed out by a class, then this is a + ;; block-close + ((and (not inenclosing-p) + (c-most-enclosing-brace state) + (or (not (c-major-mode-is 'pike-mode)) + ;; In Pike it can be a defun-close of a + ;; function declared in a statement block. Let + ;; it through to be handled below. + (or (c-looking-at-bos) + (progn + (c-beginning-of-statement-1) + (looking-at c-conditional-key))))) + (c-add-syntax 'block-close relpos)) + ;; CASE 16D: find out whether we're closing a top-level + ;; class or a defun + (t + (save-restriction + (narrow-to-region (point-min) indent-point) + (let ((decl (c-search-uplist-for-classkey (c-parse-state)))) + (if decl + (c-add-class-syntax 'class-close decl) + (c-add-syntax 'defun-close relpos))))) + ))) + ;; CASE 17: statement catchall + (t + ;; we know its a statement, but we need to find out if it is + ;; the first statement in a block + (goto-char containing-sexp) + (forward-char 1) + (c-forward-syntactic-ws indent-point) + ;; now skip forward past any case/default clauses we might find. + (while (or (c-skip-case-statement-forward fullstate indent-point) + (and (looking-at c-switch-label-key) + (not inswitch-p))) + (setq inswitch-p t)) + ;; we want to ignore non-case labels when skipping forward + (while (and (looking-at c-label-key) + (goto-char (match-end 0))) + (c-forward-syntactic-ws indent-point)) + (cond + ;; CASE 17A: we are inside a case/default clause inside a + ;; switch statement. find out if we are at the statement + ;; just after the case/default label. + ((and inswitch-p + (progn + (goto-char indent-point) + (c-beginning-of-statement-1 containing-sexp) + (setq placeholder (point)) + (beginning-of-line) + (when (re-search-forward c-switch-label-key + (max placeholder (c-point 'eol)) t) + (setq placeholder (match-beginning 0))))) + (goto-char indent-point) + (skip-chars-forward " \t") + (if (eq (char-after) ?{) + (c-add-syntax 'statement-case-open placeholder) + (c-add-syntax 'statement-case-intro placeholder))) + ;; CASE 17B: continued statement + ((eq char-before-ip ?,) + (goto-char indent-point) + (c-beginning-of-closest-statement) + (c-add-syntax 'statement-cont (c-point 'boi))) + ;; CASE 17C: a question/colon construct? But make sure + ;; what came before was not a label, and what comes after + ;; is not a globally scoped function call! + ((or (and (memq char-before-ip '(?: ??)) + (save-excursion + (goto-char indent-point) + (c-backward-syntactic-ws lim) + (back-to-indentation) + (not (looking-at c-label-key)))) + (and (memq char-after-ip '(?: ??)) + (save-excursion + (goto-char indent-point) + (skip-chars-forward " \t") + ;; watch out for scope operator + (not (looking-at "::"))))) + (goto-char indent-point) + (c-beginning-of-closest-statement) + (c-add-syntax 'statement-cont (c-point 'boi))) + ;; CASE 17D: any old statement + ((< (point) indent-point) + (let ((safepos (c-most-enclosing-brace fullstate)) + relpos done) + (goto-char indent-point) + (c-beginning-of-statement-1 safepos) + ;; It is possible we're on the brace that opens a nested + ;; function. + (if (and (eq (char-after) ?{) + (save-excursion + (c-backward-syntactic-ws safepos) + (not (eq (char-before) ?\;)))) + (c-beginning-of-statement-1 safepos)) + (if (and inswitch-p + (looking-at c-switch-label-key)) + (progn + (goto-char (match-end 0)) + (c-forward-syntactic-ws))) + (setq relpos (c-point 'boi)) + (while (and (not done) + (<= safepos (point)) + (/= relpos (point))) + (c-beginning-of-statement-1 safepos) + (if (= relpos (c-point 'boi)) + (setq done t)) + (setq relpos (c-point 'boi))) + (c-add-syntax 'statement relpos) + (if (eq char-after-ip ?{) + (c-add-syntax 'block-open)))) + ;; CASE 17E: first statement in an in-expression block + ((setq placeholder + (save-excursion + (goto-char containing-sexp) + (c-looking-at-inexpr-block))) + (goto-char containing-sexp) + (back-to-indentation) + (let ((block-intro (if (eq (car placeholder) 'inlambda) + 'defun-block-intro + 'statement-block-intro))) + (if (= containing-sexp (point)) + (c-add-syntax block-intro (point)) + (goto-char (cdr placeholder)) + (back-to-indentation) + (c-add-syntax block-intro (point)) + (if (/= (point) (cdr placeholder)) + (c-add-syntax (car placeholder))))) + (if (eq char-after-ip ?{) + (c-add-syntax 'block-open))) + ;; CASE 17F: first statement in an inline, or first + ;; statement in a top-level defun. we can tell this is it + ;; if there are no enclosing braces that haven't been + ;; narrowed out by a class (i.e. don't use bod here!) + ((save-excursion + (save-restriction + (widen) + (goto-char containing-sexp) + (c-narrow-out-enclosing-class state containing-sexp) + (not (c-most-enclosing-brace state)))) + (goto-char containing-sexp) + ;; if not at boi, then defun-opening braces are hung on + ;; right side, so we need a different relpos + (if (/= (point) (c-point 'boi)) + (progn + (c-backward-syntactic-ws) + (c-safe (c-forward-sexp (if (eq (char-before) ?\)) + -1 -2))) + ;; looking at a Java throws clause following a + ;; method's parameter list + (c-beginning-of-statement-1) + )) + (c-add-syntax 'defun-block-intro (c-point 'boi))) + ;; CASE 17G: First statement in a function declared inside + ;; a normal block. This can only occur in Pike. + ((and (c-major-mode-is 'pike-mode) + (progn + (goto-char containing-sexp) + (and (not (c-looking-at-bos)) + (progn + (c-beginning-of-statement-1) + (not (looking-at c-conditional-key)))))) + (c-add-syntax 'defun-block-intro (c-point 'boi))) + ;; CASE 17H: first statement in a block + (t (goto-char containing-sexp) + (if (/= (point) (c-point 'boi)) + (c-beginning-of-statement-1 + (if (= (point) lim) + (c-safe-position (point) state) lim))) + (c-add-syntax 'statement-block-intro (c-point 'boi)) + (if (eq char-after-ip ?{) + (c-add-syntax 'block-open))) + )) + ) + ;; now we need to look at any modifiers + (goto-char indent-point) + (skip-chars-forward " \t") + (cond + ;; are we looking at a comment only line? + ((and (looking-at c-comment-start-regexp) + (/= (c-forward-token-1 0 nil (c-point 'eol)) 0)) + (c-add-syntax 'comment-intro)) + ;; we might want to give additional offset to friends (in C++). + ((and (c-major-mode-is 'c++-mode) + (looking-at c-C++-friend-key)) + (c-add-syntax 'friend)) + ;; Start of a preprocessor directive? + ((and (eq literal 'pound) + (= (save-excursion + (c-beginning-of-macro lim) + (setq placeholder (point))) + (c-point 'boi)) + (not (and (c-major-mode-is 'pike-mode) + (eq (char-after (1+ placeholder)) ?\")))) + (c-add-syntax 'cpp-macro))) + ;; return the syntax + syntax)))) + + +(defun agulbra-switch-cpp-h () + "Switch to the corresponding .cpp, .C, .cc or .h file." + (interactive) + (let ((n (buffer-file-name)) + (c nil)) + (cond ((and (string-match "\\.h$" n) + (progn + (setq c (replace-match ".cpp" t t n)) + (file-readable-p c))) + (find-file c)) + ((and (string-match "\\.h$" n) + (progn + (setq c (replace-match ".cc" t t n)) + (file-readable-p c))) + (find-file c)) + ((and (string-match "\\.h$" n) + (progn + (setq c (replace-match ".C" t t n)) + (file-readable-p c))) + (find-file c)) + ((string-match "\\.h$" n) + (find-file (replace-match ".cpp" t t n))) + ((string-match "\\.h$" n) + (find-file (replace-match ".cpp" t t n))) + ;((string-match "_[a-z]+[0-9]*.cpp$" n) + ; (find-file (replace-match ".h" t t n))) + ((string-match "\\.cpp$" n) + (find-file (replace-match ".h" t t n))) + ((string-match "\\.cc$" n) + (find-file (replace-match ".h" t t n))) + ((string-match "\\.c$" n) + (find-file (replace-match ".h" t t n))) + (t + (error "%s is neither .h, .cc, .C or .cpp" n))))) + +;; ----- Second part, contrinuted by Klaralvdalens Datakonsult +(defvar kdab-qt-documentation + "http://doc.trolltech.com/3.0/XXX.html" + "URL for Qt documentation. XXX must be in the string. + Example: file:/packages/kde-src/qt-copy/doc/html/XXX.html") + + +;; special case for include files +;; Please notify blackie@klaralvdalens-datakonsult.se with any modification to this variable! +(defvar kdab-special-includes + '( + (qlayout.h QHBoxLayout QVBoxLayout QGridLayout QBoxLayout) + (qlistview.h QListViewItem QCheckListItem QListViewItemIterator) + (qiconview.h QIconViewItem QIconDragItem QIconDrag) + (qdragobject.h QTextDrag QStoredDrag QUriDag QColorDrag QImageDrag QDragManager) + (qmime.h QMimeSource QMimeSourceFactory QWindowsMime) + (qptrlist.h QPtrListIterator) + (qevent.h QTimerEvent QMouseEvent QWheelEvent QTabletEvent QKeyEvent + QFocusEvent QPaintEvent QMoveEvent QResizeEvent QCloseEvent + QShowEvent QHideEvent QContextMenuEvent QIMEvent QDropEvent + QDragMoveEvent QDragEnterEvent QDragResponseEvent QDragLeaveEvent + QChildEvent QCustomEvent) + (qdatetime.h QTime QDateTime QDate) + + ; Qt/Embedded + (qcopchannel_qws.h QCopChannel) + (qdirectpainter_qws.h QDirectPainter) + (qfontfactorybdf_qws.h QFontFactoryBDF) + (qfontfactoryttf_qws.h QFontFactoryFT) + (qfontmanager_qws.h QGlyphMetrics QGlyph QRenderedFont QDiskFont QFontManager QFontFactory) + (qgfx_qws.h QScreenCursor QPoolEntry QScreen QGfx) + (qgfxlinuxfb_qws.h QLinuxFbScreen) + (qgfxmatroxdefs_qws.h QQnxFbGfx QQnxScreen) + (qgfxraster_qws.h QGfxRasterBase QGfxRaster) + (qgfxvnc_qws.h QRfbRect QRfbPixelFormat QRfbServerInit QRfbSetEncodings + QRfbFrameBufferUpdateRequest QRfbKeyEvent QRfbPointerEvent QRfbClientCutText QVNCServer) + (qkeyboard_qws.h QWSKeyboardHandler) + (qlock_qws.h QLock QLockHolder) + (qmemorymanager_qws.h QMemoryManagerPixmap QMemoryManager) + (qsoundqss_qws.h QWSSoundServer QWSSoundClient QWSSoundServerClient QWSSoundServerSocket) + (qwindowsystem_qws.h QWSInternalWindowInfo QWSScreenSaver QWSWindow QWSSoundServer + QWSServer QWSServer KeyboardFilter QWSClient) + (qwsbeosdecoration_qws.h QWSBeOSDecoration) + (qwscursor_qws.h QWSCursor) + (qwsdecoration_qws.h QWSDecoration) + (qwsdefaultdecoration_qws.h QWSDefaultDecoration) + (qwsdisplay_qws.h QWSWindowInfo QWSDisplay) + (qwshydrodecoration_qws.h QWSHydroDecoration) + (qwskde2decoration_qws.h QWSKDE2Decoration) + (qwskdedecoration_qws.h QWSKDEDecoration) + (qwsmanager_qws.h QWSManager QWSButton) + (qwsmouse_qws.h QWSPointerCalibrationData QWSMouseHandler QCalibratedMouseHandler + QAutoMouseHandlerPrivate QWSMouseHandlerPrivate QVrTPanelHandlerPrivate + QTPanelHandlerPrivate QYopyTPanelHandlerPrivate QCustomTPanelHandlerPrivate + QVFbMouseHandlerPrivate) + (qwsproperty_qws.h QWSPropertyManager) + (qwsregionmanager_qws.h QWSRegionManager) + (qwssocket_qws.h QWSSocket QWSServerSocket) + (qwswindowsdecoration_qws.h QWSWindowsDecoration) + + ; KDE + (kdebug.h kdDebug kdWarning kdError kdFatal kdBacktrace) + + ) "List of special include files which do not follow the normal scheme") + +;; Lookup class `cls' in kdab-special-includes and return the associate include file name +(defun kdab-map-special (cls) + (let ((list kdab-special-includes) + (found nil)) + (while (and list (not found)) + (let* ( (elm (car list)) + (include-file (car elm)) + (classes (cdr elm))) + ( while (and classes (not found)) + (if (string= (downcase cls) (downcase (symbol-name (car classes)))) + (setq found include-file) + (setq classes (cdr classes))))) + (setq list (cdr list))) + (if found + (symbol-name found) + nil) ; return value + )) + + +(defun kdab-word-under-point () + (save-excursion + (let* ((start (if (= (preceding-char) ?\ ) + (point) + (progn (backward-word 1) (point)))) + (end (progn (forward-word 1) (point)))) + (buffer-substring start end)))) + + +;-------------------------------------------------------------------------------- +; Insert include file. +; Place point anywhere on a class, and invoke this function. A result of +; this is that an include line is added (if it does not already exists) for +; the given class. +;-------------------------------------------------------------------------------- +(defun kdab-insert-header () + (interactive "") + (save-excursion + (let* ((word (downcase (kdab-word-under-point))) + (header (cond + ((kdab-map-special word) (kdab-map-special word)) + ((string-match "^qdom" word) "qdom.h") + ((string-match "^qxml" word) "qxml.h") + (t (concat word ".h"))))) + (beginning-of-buffer) + (if (not (re-search-forward (concat "#include *<" header ">") nil t)) + (progn + ; No include existsed + (goto-char (point-max)) ; Using end-of-buffer makes point move, dispete save-excursion + (if (not (re-search-backward "^#include *[\"<][^\">]+\.h *[\">]" nil t)) + (beginning-of-buffer) + (progn (end-of-line) (forward-char 1))) + (if (file-exists-p header) + (progn + ; See this as a local file. + (insert "#include \"" header "\"\n") + (message (concat "inserted " "#include \"" header "\""))) + (progn + (insert "#include <" header ">\n") + (message (concat "inserted " "#include <" header ">"))))) + (message (concat "header file \"" header "\" is already included")))))) + + + + +;-------------------------------------------------------------------------------- +; Start konqueror with documentation for the class under point. +; set `kdab-qt-documentation' to specify the replacement for the documentation +;-------------------------------------------------------------------------------- +(defun kdab-lookup-qt-documentation () + (interactive "") + (save-excursion + (let* ((word (downcase (kdab-word-under-point))) + (url (if (not (string-match "XXX" kdab-qt-documentation)) + (error "didn't find three X's in kdab-qt-documentation") + (replace-match word t t kdab-qt-documentation)))) + (start-process "qt documentation" nil "kfmclient" "openURL" url) + (message (concat "Loading " url))))) + + +;; ----- Third part, contributed by various KDE developers + +;;; func-menu is a package that scans your source file for function definitions +;;; and makes a menubar entry that lets you jump to any particular function +;;; definition by selecting it from the menu. The following code turns this on +;;; for all of the recognized languages. Scanning the buffer takes some time, +;;; but not much. + +(if xemacs + (progn + (require 'func-menu) + (add-hook 'find-file-hooks 'fume-add-menubar-entry) ) + (progn + (require 'imenu))) + ;(add-hook 'find-file-hooks 'imenu)) ) + +;; Switch between the declaration of a class member in .cc/.cpp/.C, and its definition in the .h file +;; Written by David and Reggie after much hair tearing +(defun switch-to-function-def () + (interactive) + (let ((n (buffer-file-name)) + (class "") + (fn "")) + (if (or (string-match "\\.cc$" n) + (string-match "\\.cpp$" n) + (string-match "\\.C$" n)) + (let ((a (fume-function-before-point))) + (and (string-match "^\\(.*\\)::\\(.*\\)$" a) + (progn + (setq class (match-string 1 a)) + (setq fn (match-string 2 a)) + (agulbra-switch-cpp-h) + (goto-char 0) + (re-search-forward class nil t) + (re-search-forward (concat "[ \t]+" fn "[ \t]*(") nil t))))) + (if (string-match "\\.h$" n) + (progn + (save-excursion + (forward-line 0) + (re-search-forward "[ \t]+\\([^ \t(]+\\)[ \t]*(" nil t) + (setq fn (match-string 1)) + (re-search-backward "^class \\([a-zA-Z0-9_]+\\)[ \t]*\\([a-zA-Z0-9_]*\\)" nil t) + (setq class (match-string 1)) + (setq save (match-string 2)) + (and (string-match "Q_EXPORT" class) + (setq class save)) + (message (concat class "::" fn)) + ) + (agulbra-switch-cpp-h) + (goto-char 0) + (re-search-forward (concat "^[^()]*" class "::" fn "[ \t]*(") nil t) + (message c-syntactic-context) + ) +))) + +; Adds the current file to Makefile.am. +; Written by David. +(defun add-file-to-makefile-am () + "add the current file to the _SOURCES tag in the Makefile.am" + (interactive) + (let ((file (buffer-name)) + (makefile "Makefile.am")) + (if (file-readable-p makefile ) + (message "") + (error "Makefile.am not found!") + ) + (find-file makefile) + (goto-char (point-min)) + (if (re-search-forward "_SOURCES" nil t) + (progn + (end-of-line) + ; check if line ends with '\' [had to read make-mode.el to find this one!] + (while (= (char-before) ?\\) + (end-of-line 2)) ; moves to end of next line + (insert " ") + (insert file) + ) + (error "_SOURCES not found") + ) + ) + ) + +; Inserts a kdDebug statement showing the name of the current method. +; You need to create the empty line first. +(defun insert-kdDebug () + (interactive) + (insert "kdDebug() << \"") + (insert (fume-function-before-point)) + (insert "\" << endl;") + ) + +; Creates the ifndef/define/endif statements necessary for a header file +(defun header-protection () + (interactive) + (let ((f (buffer-file-name))) + (if (string-match "^.*/" f) + (setq f (replace-match "" t t f))) + (while (string-match "\\." f) + (setq f (replace-match "_" t t f))) + (save-excursion + (goto-char (point-min)) + (insert "#ifndef " (upcase f) "\n#define " (upcase f) "\n\n") + (goto-char (point-max)) + (insert "\n#endif\n") + ) + ) + ) + + +; Makes '(' insert '(' or ' ( ' where appropiate +(defun insert-parens (arg) (interactive "*P") + (if (not (c-in-literal)) + (let ((n nil)) + (save-excursion + (setq n (or (progn (forward-char -2) (looking-at "if")) + (progn (forward-char -1) (looking-at "for")) + (progn (forward-char -1) (looking-at "case")) + (progn (forward-char -1) (looking-at "while")) + ) + ) + ) + (cond + (n (progn + (insert " ") + (self-insert-command (prefix-numeric-value arg)) + ;(insert " ") + )) + (t ;else + (self-insert-command (prefix-numeric-value arg)) + ;(insert " ") + ))) + (self-insert-command (prefix-numeric-value arg))) + ) + +(defun insert-parens2 (arg) (interactive "*P") + (if (not (c-in-literal)) + (let ((remv nil) (nospac nil)) + (forward-char -2) + (setq remv (looking-at "( ")) ; () -> we'll have to remove that space + (forward-char 1) + (setq nospac (or (looking-at " ") (looking-at "(")) ) ; no space to be added + (forward-char 1) + (cond + (remv (progn + (delete-backward-char 1) + (self-insert-command (prefix-numeric-value arg)))) ; the () case + (nospac (self-insert-command (prefix-numeric-value arg))) ; no space to be added + (t ;else + (if abbrev-mode ; XEmacs + (expand-abbrev)) + ;(insert " ") + (self-insert-command (prefix-numeric-value arg)) + ))) ; normal case, prepend a space + ;;(blink-matching-open) ; show the matching parens + (self-insert-command (prefix-numeric-value arg))) + ) + +; Makes ',' insert ', ' +(defun insert-comma (arg) + (interactive "*P") + (let* ((ch (char-after)) + (spacep (not (or (eq ch ? ) + (c-in-literal) + arg)))) + (self-insert-command (prefix-numeric-value arg)) + (if spacep + (insert " ")))) + +(defun insert-curly-brace (arg) (interactive "*P") + (if (not (c-in-literal)) + (let ((n nil) (o nil)) + (save-excursion + (forward-char -2) + (setq o (looking-at "()")) + (forward-char 1) + (setq n (looking-at ")")) + ) + (cond + (n (progn + (insert " ") + (self-insert-command (prefix-numeric-value arg)) + (newline-and-indent) + (save-excursion + (insert "\n}") + (c-indent-line) + ))) + (o (progn + (newline) + (self-insert-command (prefix-numeric-value arg)) + (newline-and-indent))) + (t (progn ;else + (self-insert-command (prefix-numeric-value arg)) + (save-excursion + (beginning-of-line) + (c-indent-command)))) + )) + (self-insert-command (prefix-numeric-value arg)) + ) +) + +;; have PelDel mode work +(put 'insert-parens 'pending-delete t) +(put 'insert-parens2 'pending-delete t) +(put 'insert-comma 'pending-delete t) +(put 'insert-curly-brace 'pending-delete t) +(put 'newline-and-indent 'pending-delete t) + +; A wheel mouse that doesn't beep, unlike mwheel-install +(defun scroll-me-up () (interactive) (scroll-up 4)) +(defun scroll-me-down () (interactive) (scroll-down 4)) +(defun scroll-me-up-a-bit () (interactive) (scroll-up 1)) +(defun scroll-me-down-a-bit () (interactive) (scroll-down 1)) +(define-key global-map [(button4)] 'scroll-me-down) +(define-key global-map [(button5)] 'scroll-me-up) +(define-key global-map [(shift button4)] 'scroll-me-down-a-bit) +(define-key global-map [(shift button5)] 'scroll-me-up-a-bit) + +; Compilation +(defun makeclean () (interactive) (compile "make clean")) +(defun make () (interactive) (compile "make")) +(defun makeinstall () (interactive) (compile "make install")) +(defun makeinstallexec () (interactive) (compile "make install-exec")) +(defun makethisfile () (interactive) + (let ((f (buffer-name))) + (if (string-match "\.cpp$" f) (setq f (replace-match "\.lo" t t f))) + (if (string-match "\.cc$" f) (setq f (replace-match "\.lo" t t f))) + (compile (concat "make " f )))) + +;; Indentation: 4 characters, no tabs. +(setq c-basic-offset 4) +(setq insert-tab-mode nil) +(setq-default require-final-newline t) +(setq-default next-line-add-newlines nil) + +;; pc-like textmarking +(load "pc-select") +(if xemacs + (pc-select-mode) + (pc-selection-mode)) + +; Move in other window +(defun scroll-other-up () (interactive) (scroll-other-window-down 1)) ; hehe :) +(define-key global-map [(meta up)] 'scroll-other-up) +(defun scroll-other-down () (interactive) (scroll-other-window 1)) +(define-key global-map [(meta down)] 'scroll-other-down) + +;; Some example bindings, feel free to customize :) +(define-key global-map [(f2)] 'grep) +;; FIXME: remember to get these two working on Gnu/Emacs (Zack) +(define-key global-map [(f3)] 'fume-list-functions) +(define-key global-map [(shift f3)] 'fume-prompt-function-goto) +(define-key global-map [(shift button3)] 'mouse-function-menu) +(define-key global-map [(shift f4)] 'makeclean) +(define-key global-map [(f4)] 'make) +(define-key global-map [(f5)] 'makeinstall) +(define-key global-map [(shift f5)] 'makeinstallexec) +(define-key global-map [(shift f6)] 'makethisfile) +(define-key global-map [(f6)] 'agulbra-switch-cpp-h) +(define-key global-map [(f7)] 'switch-to-function-def) +;; imenu does that interactively +(if xemacs + (define-key global-map [(f8)] 'function-menu)) +;(define-key global-map [(f9)] 'agulbra-make-member) ;; uncomment this for a killer feature +(define-key global-map [(f10)] 'kdab-insert-header) +(define-key global-map [(shift f10)] 'kdab-lookup-qt-documentation) +(define-key global-map [(control meta d)] 'insert-kdDebug) + +; Standard Qt/KDE shortcuts: Ctrl+Backspace, Ctrl+Delete +(define-key global-map [(control backspace)] 'backward-kill-word) +(define-key global-map [(control delete)] 'kill-word) + +; Standard Qt/KDE shortcuts: Control Pageup and Pagedown +(define-key global-map [(control prior)] 'beginning-of-buffer) +(define-key global-map [(control next)] 'end-of-buffer) + +; currently no binding for header-protection and add-file-to-makefile-am, +; you need to call them from M-x + +; ----------------------------------------------------------------- +; The above list defines the following bindings: +; +; F2 : offer a grep command +; +; F3/Shift-F3/F8/Shift-RMB : different ways to see the list of methods in the current buffer +; +; F4 : make +; Shift-F4 : make clean +; F5 : make install +; Shift-F5 : make install-exec +; +; Shift-F6 : compile this file [assumes libtool is being used] +; F6 : Switch from .cpp/.cc to .h and vice-versa +; F7 : The same, but try to find the current method in the other file +; F9 (if enabled) : Create a member method in the .cpp, the cursor being on the definition in the .h +; F10: Place point on a class name, and the respective (Qt) include file will be inserted. +; This works with all Qt classes but can easily be extended to KDE classes. +; Shift-F10: Place point on a class name, and press Shift-F10, and konqueror will load +; Qt documentation. Customize the location of the Qt documentation with the +; variable kdab-qt-documentation. XXX will be replace with the class name. +; Example (setq kdab-qt-location "file:/packages/kde-src/qt-copy/doc/html/XXX.html") +; +; Ctrl+Meta+D : insert a kdDebug statement with the name of the current method +; [the new hide-all-windows shortcut conflicts with that, you may have to +; change it, or use Ctrl+Meta+Shift+D (!!)] +; +; Meta Up/Down : scroll the other window (when window is split) + +; Other very useful keybindings to know about: +; C-x r m to set a named bookmark in the buffer +; C-x r b to jump to a named bookmark in the buffer + +(setq-default initial-scratch-message + "File kde-devel-emacs.el is deprecated! +Please use KDE-Emacs from kdesdk/scripts/kde-emacs.") diff --git a/scripts/kde-devel-gdb b/scripts/kde-devel-gdb new file mode 100644 index 00000000..bf69d233 --- /dev/null +++ b/scripts/kde-devel-gdb @@ -0,0 +1,299 @@ +# This file defines handy gdb macros for printing out Qt types +# To use it, add this line to your ~/.gdbinit : +# source /path/to/kde/sources/kdesdk/scripts/kde-devel-gdb + +# Please don't use tabs in this file. When pasting a +# macro definition to gdb, tabs are interpreted as completion. + +# Disable printing of static members. Qt has too many, it clutters the output +set print static-members off + +define printqstring + printqstringdata $arg0.d +end +document printqstring + Prints the contents of a QString +end +define printq4string + printq4stringdata $arg0.d +end +document printq4string + Prints the contents of a Qt QString +end + +define printqstringdata + set $i=0 + set $d = (QStringData *)$arg0 + while $i < $d->len + printf "%c", (char)($d->unicode[$i++].ucs & 0xff) + end + printf "\n" +end +document printqstringdata + Prints the contents of a QStringData + This is useful when the output of another command (e.g. printqmap) + shows {d = 0xdeadbeef} for a QString, i.e. the qstringdata address + instead of the QString object itself. + printqstring $s and printqstringdata $s.d are equivalent. +end + +define printq4stringdata + set $i=0 + set $d = $arg0 + while $i < $d->size + printf "%c", (char)($d->data[$i++] & 0xff) + end + printf "\n" +end +document printq4stringdata + Prints the contents of a Qt4 QString::Data + This is useful when the output of another command (e.g. printqmap) + shows {d = 0xdeadbeef} for a QString, i.e. the qstringdata address + instead of the QString object itself. + printq4string $s and printq4stringdata $s.d are equivalent. +end + +define printqstring_utf8 + set $i=0 + set $s = $arg0 + while $i < $s.d->len + set $uc = (unsigned short) $s.d->unicode[$i++].ucs + if ( $uc < 0x80 ) + printf "%c", (unsigned char)($uc & 0x7f) + else + if ( $uc < 0x0800 ) + printf "%c", (unsigned char)(0xc0 | ($uc >> 6)) + else + printf "%c", (unsigned char)(0xe0 | ($uc >> 12) + printf "%c", (unsigned char)(0x80 | (($uc > 6) &0x3f) + end + printf "%c", (unsigned char)(0x80 | ((uchar) $uc & 0x3f)) + end + end + printf "\n" +end +document printqstring_utf8 + Prints the contents of a QString encoded in utf8. + Nice if you run your debug session in a utf8 enabled terminal. +end + +define printqcstring + print $arg0.shd.data + print $arg0.shd.len +end +document printqcstring + Prints the contents of a QCString (char * data, then length) +end + +define printq4bytearray + print $arg0->d->data +end +document printq4bytearray + Prints the contents of a Qt4 QByteArray (when it contains a string) +end + +define printqfont + print *($arg0).d + printqstring ($arg0).d->request.family + print ($arg0).d->request.pointSize +end +document printqfont + Prints the main attributes from a QFont, in particular the requested + family and point size +end + +define printqcolor + printf "(%d,%d,%d)\n", ($arg0).red(), ($arg0).green(), ($arg0).blue() +end +document printqcolor + Prints a QColor as (R,G,B). + Usage: 'printqcolor +end + +define printqmemarray + # Maybe we could find it out the type by parsing "whatis $arg0"? + set $arr = $arg0 + set $sz = sizeof($arg1) + set $len = $arr->shd->len / $sz + output $len + printf " items in the array\n" + set $i = 0 + while $i < $len + # print "%s[%d] = %s\n", $arr, $i, *($arg1 *)(($arr->vec)[$i]) + print *($arg1 *)(($arr->shd->data) + ($i * $sz)) + set $i++ + end +end +document printqmemarray + Prints the contents of a QMemArray. Pass the type as second argument. +end + +define printqptrvector + # Maybe we could find it out the type by parsing "whatis $arg0"? + set $arr = $arg0 + set $len = $arr->len + output $len + printf " items in the vector\n" + set $i = 0 + while $i < $len + # print "%s[%d] = %s\n", $arr, $i, *($arg1 *)(($arr->vec)[$i]) + print *($arg1 *)(($arr->vec)[$i]) + set $i++ + end +end +document printqptrvector + Prints the contents of a QPtrVector. Pass the type as second argument. +end + +define printqptrvectoritem + set $arr = $arg0 + set $i = $arg2 + print ($arg1 *)(($arr->vec)[$i]) + print *($arg1 *)(($arr->vec)[$i]) +end +document printqptrvectoritem + Print one item of a QPtrVector + Usage: printqptrvectoritem vector type index +end + +define printqmap + set $map = $arg0 + set $len = $map.sh->node_count + output $len + printf " items in the map\n" + set $header = $map.sh->header + # How to parse the key and value types from whatis? + set $it = (QMapNode<$arg1,$arg2> *)($header->left) + while $it != $header + printf " key=" + output $it->key + printf " value=" + output $it->data + printf "\n" + _qmapiterator_inc $it + set $it = (QMapNode<$arg1,$arg2> *)($ret) + end +end +document printqmap + Prints the full contents of a QMap + Usage: 'printqmap map keytype valuetype' +end + + +define _qmapiterator_inc + set $ret = $arg0 + if $ret->right != 0 + set $ret = $ret->right + while $ret->left != 0 + set $ret = $ret->left + end + else + set $y = $ret->parent + while $ret == $y->right + set $ret = $y + set $y = $y->parent + end + if $ret->right != $y + set $ret = $y + end + end +end +document _qmapiterator_inc + Increment a qmap iterator (internal method, used by printqmap) +end + +define printqptrlist + set $list = $arg0 + set $len = $list.numNodes + output $len + printf " items in the list\n" + set $it = $list.firstNode + while $it != 0 + output $it->data + printf "\n" + set $it = $it->next + end +end +document printqptrlist + Prints the contents of a QPtrList. + Usage: printqptrlist mylist +end + +define printqvaluelist + set $list = $arg0 + set $len = $list.sh->nodes + output $len + printf " items in the list\n" + set $it = $list.sh->node->next + set $end = $list.sh->node + while $it != $end + output $it->data + printf "\n" + set $it = $it->next + end +end +document printqvaluelist + Prints the contents of a QValueList. + Usage: printqvaluelist mylist +end + +define printqstringlist + set $list = $arg0 + set $len = $list.sh->nodes + output $len + printf " items in the list\n" + set $it = $list.sh->node->next + set $end = $list.sh->node + while $it != $end + printqstring $it->data + set $it = $it->next + end +end +document printqstringlist + Prints the contents of a QStringList. + Usage: printqstringlist mylist +end + +# Bad implementation, requires a running process. +# Needs to be refined, i.e. figuring out the right void* pointers casts. +# Simon says: each Node contains the d pointer of the QString. +define printq4stringlist + # This is ugly, but we need to avoid conflicts with printq4string's own vars... + set $q4sl_i = 0 + set $q4sl_d = & $arg0 + set $q4sl_sz = $q4sl_d->size() + while $q4sl_i < $q4sl_sz + output $q4sl_i + printf " " + printq4string $q4sl_d->at($q4sl_i++) + end +end +document printq4stringlist + Prints the contents of a Qt4 QStringList. + Usage: printq4stringlist mylist +end + +define printqdatetime + printqdate ($arg0).d + printqtime ($arg0).t +end +document printqdatetime + Prints a QDateTime + Usage: printqdatetime myqdt +end +define printqdate + printf "(Y:%d M:%d D:%d)\n", ($arg0).year(), ($arg0).month(), ($arg0).day() +end +document printqdate + Prints a QDate + Usage: printqdate mydate +end +define printqtime + printf "(H:%d M:%d S:%d)\n", ($arg0).hour(), ($arg0).minute(), ($arg0).second() +end +document printqtime + Prints a QTime + Usage: printqtime mytime +end + + diff --git a/scripts/kde-devel-vim.vim b/scripts/kde-devel-vim.vim new file mode 100644 index 00000000..bcb02b4c --- /dev/null +++ b/scripts/kde-devel-vim.vim @@ -0,0 +1,428 @@ +" To use this file, add this line to your ~/.vimrc:, w/o the dquote +" source /path/to/kde/sources/kdesdk/scripts/kde-devel-vim.vim +" +" For CreateChangeLogEntry() : If you don't want to re-enter your +" Name/Email in each vim session then make sure to have the viminfo +" option enabled in your ~/.vimrc, with the '!' flag, enabling persistent +" storage of global variables. Something along the line of +" set viminfo=%,!,'50,\"100,:100,n~/.viminfo +" should do the trick. + +" Don't include these in filename completions +set suffixes+=.lo,.o,.moc,.la,.closure,.loT + +" Search for headers here +set path=.,/usr/include,/usr/local/include, +if $QTDIR != '' + let &path = &path . $QTDIR . '/include/,' +endif +if $KDEDIR != '' + let &path = &path . $KDEDIR . '/include/,' + let &path = &path . $KDEDIR . '/include/arts/,' +endif +if $KDEDIRS != '' + let &path = &path . substitute( $KDEDIRS, '\(:\|$\)', '/include,', 'g' ) + let &path = &path . substitute( $KDEDIRS, '\(:\|$\)', '/include/arts,', 'g' ) +endif +set path+=, + +" Use makeobj to build +set mp=makeobj + +" If TagList is Loaded then get a funny statusline +" Only works if kde-devel-vim.vim is loaded after taglist. +" Droping this script in ~/.vim/plugin works fine +if exists('loaded_taglist') + let Tlist_Process_File_Always=1 + set statusline=%<%f:[\ %{Tlist_Get_Tag_Prototype_By_Line()}\ ]\ %h%m%r%=%-14.(%l,%c%V%)\ %P +endif + +" Insert tab character in whitespace-only lines, complete otherwise +inoremap =SmartTab() + +if !exists("DisableSmartParens") +" Insert a space after ( or [ and before ] or ) unless preceded by a matching +" paren/bracket or space or inside a string or comment. Comments are only +" recognized as such if they start on the current line :-( +inoremap ( =SmartParens( '(' ) +inoremap [ =SmartParens( '[' ) +inoremap ] =SmartParens( ']', '[' ) +inoremap ) =SmartParens( ')', '(' ) +endif + +" Insert an #include statement for the current/last symbol +inoremap :call AddHeader() + +" Insert a forward declaration for the current/last symbol +" FIXME: not implemented yet +" inoremap :call AddForward() + +" Switch between header and implementation files on ,h +nmap ,h :call SwitchHeaderImpl() + +" Comment selected lines on ,c in visual mode +vmap ,c :s,^,//X ,:noh +" Uncomment selected lines on ,u in visual mode +vmap ,u :s,^//X ,, + +" Insert an include guard based on the file name on ,i +nmap ,i :call IncludeGuard()o + +" Insert simple debug statements into each method +nmap ,d :call InsertMethodTracer() + +" Expand #i to #include <.h> or #include ".h". The latter is chosen +" if the character typed after #i is a dquote +" If the character is > #include <> is inserted (standard C++ headers w/o .h) +iab #i =SmartInclude() + +" Insert a stripped down CVS diff +iab DIFF :call RunDiff() + +" mark 'misplaced' tab characters +set listchars=tab:\ ,trail: +set list + +set incsearch + +function! SmartTab() + let col = col('.') - 1 + if !col || getline('.')[col-1] !~ '\k' + return "\" + else + return "\" + endif +endfunction + +function! SmartParens( char, ... ) + if ! ( &syntax =~ '^\(c\|cpp\|java\)$' ) + return a:char + endif + let s = strpart( getline( '.' ), 0, col( '.' ) - 1 ) + if s =~ '//' + return a:char + endif + let s = substitute( s, '/\*\([^*]\|\*\@!/\)*\*/', '', 'g' ) + let s = substitute( s, "'[^']*'", '', 'g' ) + let s = substitute( s, '"\(\\"\|[^"]\)*"', '', 'g' ) + if s =~ "\\([\"']\\|/\\*\\)" + return a:char + endif + if a:0 > 0 + if strpart( getline( '.' ), col( '.' ) - 3, 2 ) == a:1 . ' ' + return "\" . a:char + endif + if strpart( getline( '.' ), col( '.' ) - 2, 1 ) == ' ' + return a:char + endif + return ' ' . a:char + endif + if a:char == '(' + if strpart( getline( '.' ), col( '.' ) - 3, 2 ) == 'if' || + \strpart( getline( '.' ), col( '.' ) - 4, 3 ) == 'for' || + \strpart( getline( '.' ), col( '.' ) - 6, 5 ) == 'while' || + \strpart( getline( '.' ), col( '.' ) - 7, 6 ) == 'switch' + return ' ( ' + endif + endif + return a:char . ' ' +endfunction + +function! SwitchHeaderImpl() + let headers = '\.\([hH]\|hpp\|hxx\)$' + let impl = '\.\([cC]\|cpp\|cc\|cxx\)$' + let fn = expand( '%' ) + if fn =~ headers + let list = glob( substitute( fn, headers, '.*', '' ) ) + elseif fn =~ impl + let list = glob( substitute( fn, impl, '.*', '' ) ) + endif + while strlen( list ) > 0 + let file = substitute( list, "\n.*", '', '' ) + let list = substitute( list, "[^\n]*", '', '' ) + let list = substitute( list, "^\n", '', '' ) + if ( fn =~ headers && file =~ impl ) || ( fn =~ impl && file =~ headers ) + execute( "edit " . file ) + return + endif + endwhile + echohl ErrorMsg + echo "File switch failed!" + echohl None +endfunction + +function! IncludeGuard() + let guard = toupper( substitute( expand( '%' ), '\([^.]*\)\.h', '\1_h', '' ) ) + call append( '^', '#define ' . guard ) + + + call append( '^', '#ifndef ' . guard ) + call append( '$', '#endif // ' . guard ) + + +endfunction + +function! SmartInclude() + let next = nr2char( getchar( 0 ) ) + if next == '"' + return "#include \".h\"\\\" + endif + if next == '>' + return "#include <>\" + endif + return "#include <.h>\\\" +endfunction + +function! MapIdentHeader( ident ) + " Qt stuff + if a:ident =~ 'Q.*Layout' + return '' + elseif a:ident == 'QListViewItem' || + \a:ident == 'QCheckListItem' || + \a:ident == 'QListViewItemIterator' + return '' + elseif a:ident == 'QIconViewItem' || + \a:ident == 'QIconDragItem' || + \a:ident == 'QIconDrag' + return '' + elseif a:ident =~ 'Q.*Drag' || + \a:ident == 'QDragManager' + return '' + elseif a:ident == 'QMimeSource' || + \a:ident == 'QMimeSourceFactory' || + \a:ident == 'QWindowsMime' + return '' + elseif a:ident == 'QPtrListIterator' + return '' + elseif a:ident =~ 'Q.*Event' + return '' + elseif a:ident == 'QTime' || + \a:ident == 'QDate' + return '' + elseif a:ident == 'QTimeEdit' || + \a:ident == 'QDateTimeEditBase' || + \a:ident == 'QDateEdit' + return '' + elseif a:ident == 'QByteArray' + return '' + elseif a:ident == 'QWidgetListIt' + return '' + elseif a:ident == 'QTab' + return '' + elseif a:ident == 'QColorGroup' + return '' + elseif a:ident == 'QActionGroup' + return '' + elseif a:ident =~ 'Q.*Validator' + return '' + elseif a:ident =~ 'QListBox.*' + return '' + elseif a:ident == 'QChar' || + \a:ident == 'QCharRef' || + \a:ident == 'QConstString' + return '' + elseif a:ident =~ 'QCanvas.*' + return '' + elseif a:ident =~ 'QGL.*' + return '' + elseif a:ident == 'QTableSelection' || + \a:ident == 'QTableItem' || + \a:ident == 'QComboTableItem' || + \a:ident == 'QCheckTableItem' + return '' + elseif a:ident == 'qApp' + return '' + + " KDE stuff + elseif a:ident == 'K\(Double\|Int\)\(NumInput\|SpinBox\)' + return '' + elseif a:ident == 'KConfigGroup' + return '' + elseif a:ident == 'KListViewItem' + return '' + elseif a:ident =~ 'kd\(Debug\|Warning\|Error\|Fatal\|Backtrace\)' + return '' + elseif a:ident == 'kapp' + return '' + elseif a:ident == 'i18n' || + \a:ident == 'I18N_NOOP' + return '' + elseif a:ident == 'locate' || + \a:ident == 'locateLocal' + return '' + + " aRts stuff + elseif a:ident =~ '\arts_\(debug\|info\|warning\|fatal\)' + return '' + + " Standard Library stuff + elseif a:ident =~ '\(std::\)\?\(cout\|cerr\|endl\)' + return '' + elseif a:ident =~ '\(std::\)\?is\(alnum\|alpha\|ascii\|blank\|graph\|lower\|print\|punct\|space\|upper\|xdigit\)' + return '' + endif + + let header = tolower( substitute( a:ident, '::', '/', 'g' ) ) . '.h' + let check = header + while 1 + if filereadable( check ) + return '"' . check . '"' + endif + let slash = match( check, '/' ) + if slash == -1 + return '<' . header . '>' + endif + let check = strpart( check, slash + 1 ) + endwhile +endfunction + +" This is a rather dirty hack, but seems to work somehow :-) (malte) +function! AddHeader() + let s = getline( '.' ) + let i = col( '.' ) - 1 + while i > 0 && strpart( s, i, 1 ) !~ '[A-Za-z0-9_:]' + let i = i - 1 + endwhile + while i > 0 && strpart( s, i, 1 ) =~ '[A-Za-z0-9_:]' + let i = i - 1 + endwhile + let start = match( s, '[A-Za-z0-9_]\+\(::[A-Za-z0-9_]\+\)*', i ) + let end = matchend( s, '[A-Za-z0-9_]\+\(::[A-Za-z0-9_]\+\)*', i ) + if end > col( '.' ) + let end = matchend( s, '[A-Za-z0-9_]\+', i ) + endif + let ident = strpart( s, start, end - start ) + let include = '#include ' . MapIdentHeader( ident ) + + let line = 1 + let incomment = 0 + let appendpos = 0 + let codestart = 0 + while line <= line( '$' ) + let s = getline( line ) + if incomment == 1 + let end = matchend( s, '\*/' ) + if end == -1 + let line = line + 1 + continue + else + let s = strpart( s, end ) + let incomment = 0 + endif + endif + let s = substitute( s, '//.*', '', '' ) + let s = substitute( s, '/\*\([^*]\|\*\@!/\)*\*/', '', 'g' ) + if s =~ '/\*' + let incomment = 1 + elseif s =~ '^' . include + break + elseif s =~ '^#include' && s !~ '\.moc"' + let appendpos = line + elseif codestart == 0 && s !~ '^$' + let codestart = line + endif + let line = line + 1 + endwhile + if line == line( '$' ) + 1 + if appendpos == 0 + call append( codestart - 1, include ) + call append( codestart, '' ) + else + call append( appendpos, include ) + endif + endif +endfunction + +function! RunDiff() + echo 'Diffing....' + read! cvs diff -bB -I \\\#include | egrep -v '(^Index:|^=+$|^RCS file:|^retrieving revision|^diff -u|^[+-]{3})' +endfunction + +function! CreateChangeLogEntry() + let currentBuffer = expand( "%" ) + + if exists( "g:EMAIL" ) + let mail = g:EMAIL + elseif exists( "$EMAIL" ) + let mail = $EMAIL + else + let mail = inputdialog( "Enter Name/Email for Changelog entry: " ) + if mail == "" + echo "Aborted ChangeLog edit..." + return + endif + let g:EMAIL = mail + endif + + if bufname( "ChangeLog" ) != "" && bufwinnr( bufname( "ChangeLog" ) ) != -1 + execute bufwinnr( bufname( "ChangeLog" ) ) . " wincmd w" + else + execute "split ChangeLog" + endif + + let lastEntry = getline( nextnonblank( 1 ) ) + let newEntry = strftime("%Y-%m-%d") . " " . mail + + if lastEntry != newEntry + call append( 0, "" ) + call append( 0, "" ) + call append( 0, newEntry ) + endif + + " like emacs, prepend the current buffer name to the entry. but unlike + " emacs I have no idea how to figure out the current function name :( + " (Simon) + if currentBuffer != "" + let newLine = "\t* " . currentBuffer . ": " + else + let newLine = "\t* " + endif + + call append( 2, newLine ) + + execute "normal 3G$" +endfunction + +function! AddQtSyntax() + if expand( "" ) == "cpp" + syn keyword qtKeywords signals slots emit foreach + syn keyword qtMacros Q_OBJECT Q_WIDGET Q_PROPERTY Q_ENUMS Q_OVERRIDE Q_CLASSINFO Q_SETS SIGNAL SLOT + syn keyword qtCast qt_cast qobject_cast qvariant_cast qstyleoption_cast + syn keyword qtTypedef uchar uint ushort ulong Q_INT8 Q_UINT8 Q_INT16 Q_UINT16 Q_INT32 Q_UINT32 Q_LONG Q_ULONG Q_INT64 Q_UINT64 Q_LLONG Q_ULLONG pchar puchar pcchar qint8 quint8 qint16 quint16 qint32 quint32 qint64 quint64 qlonglong qulonglong + syn keyword kdeKeywords k_dcop k_dcop_signals + syn keyword kdeMacros K_DCOP ASYNC + syn keyword cRepeat foreach + syn keyword cRepeat forever + + hi def link qtKeywords Statement + hi def link qtMacros Type + hi def link qtCast Statement + hi def link qtTypedef Type + hi def link kdeKeywords Statement + hi def link kdeMacros Type + endif +endfunction + +function! InsertMethodTracer() + :normal [[kf(yBjokdDebug() << ""()" << endl; +endfunction + +function! UpdateMocFiles() + if &syntax == "cpp" + let i = 1 + while i < 80 + let s = getline( i ) + if s =~ '^#include ".*\.moc"' + let s = substitute( s, '.*"\(.*\)\.moc"', '\1.h', '' ) + if stridx( &complete, s ) == -1 + let &complete = &complete . ',k' . s + endif + break + endif + let i = i + 1 + endwhile + endif +endfunction + +autocmd Syntax * call AddQtSyntax() +autocmd CursorHold * call UpdateMocFiles() + +" vim: sw=4 sts=4 et diff --git a/scripts/kde-emacs/HACKING b/scripts/kde-emacs/HACKING new file mode 100644 index 00000000..d3a87f22 --- /dev/null +++ b/scripts/kde-emacs/HACKING @@ -0,0 +1,7 @@ +Rules are simple: +1) Make sure that your functions work both on GNU/Emacs and XEmacs. +2) Put general variables in kde-emacs-vars.el, file related variables +inside the related file. +3) Export general functions to kde-emacs-general.el. +4) Always provide documentation for both variables and functions that +you're adding. diff --git a/scripts/kde-emacs/dirvars.el b/scripts/kde-emacs/dirvars.el new file mode 100644 index 00000000..5fba18e7 --- /dev/null +++ b/scripts/kde-emacs/dirvars.el @@ -0,0 +1,200 @@ +;;; -*- local-enable-local-variables: nil -*- +;;; dirvars.el --- Local variables that apply to an entire directory + +;; Copyright (C) 2002 Matt Armstrong + +;; Author: Matt Armstrong +;; Location: http://www.lickey.com/env/elisp/dirvars.el +;; Keywords: files +;; Version: 1.2 +;; Obscure: matt@squeaker.lickey.com|elisp/dirvars.el|20021213043855|48166 + +;; This file is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; This file is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Commentary: + +;; Emacs allows you to specify local variable values for use when +;; editing a file either in the first line or in a local variables +;; list. +;; +;; This file provides similar functionality, but for an entire +;; directory tree. +;; +;; You simply place an .emacs-dirvars file in the root of your +;; project's tree, and you can then set emacs variables like you would +;; in a Local Variables: section at the end of a file. E.g. the +;; contents of a typical dirvars file might look like this: +;; +;; ;; -*- emacs-lisp -*- +;; ;; +;; ;; This file is processed by the dirvars emacs package. Each variable +;; ;; setting below is performed when this dirvars file is loaded. +;; ;; +;; indent-tabs-mode: nil +;; tab-width: 8 +;; show-trailing-whitespace: t +;; indicate-empty-lines: t +;; +;; Much of this code is stolen and modified from the standard Emacs +;; files.el +;; +;; This code refuses to set any symbol that meets any of these +;; criteria (this criteria is stolen from files.el): +;; +;; - the symbol is in the ignored-local-variables list +;; - the symbol has the risky-local-variable property. +;; - the symbol name ends in -hook(s), -function(s), -form(s), +;; -program, -command, or -predicate. + +;;; Todo: + +;; Implement the following changes to keep in line with elisp coding +;; conventions: When a package provides a modification of ordinary +;; Emacs behavior, it is good to include a command to enable and +;; disable the feature, Provide a command named `WHATEVER-mode' which +;; turns the feature on or off, and make it autoload (*note +;; Autoload::). Design the package so that simply loading it has no +;; visible effect--that should not enable the feature.(2) Users will +;; request the feature by invoking the command. +;; +;; Support customize? + +;;; Code: + +(defvar dirvars-enable-flag t + "*Control use of directory variables in files you visit. +The meaningful values are nil and non-nil.") + +(defun dirvars-find-upwards (file-name) + "Find a file in the current directory or one of its parents. + +Returns the fully qualified file name, or nil if it isn't found. + +The FILE-NAME specifies the file name to search for." + ;; Chase links in the source file and search in the dir where it + ;; points. + (setq dir-name (or (and buffer-file-name + (file-name-directory (file-chase-links + buffer-file-name))) + default-directory)) + ;; Chase links before visiting the file. This makes it easier to + ;; use a single file for several related directories. + (setq dir-name (file-chase-links dir-name)) + (setq dir-name (expand-file-name dir-name)) + ;; Move up in the dir hierarchy till we find a change log file. + (let ((file1 (concat dir-name file-name)) + parent-dir) + (while (and (not (file-exists-p file1)) + (progn (setq parent-dir + (file-name-directory + (directory-file-name + (file-name-directory file1)))) + ;; Give up if we are already at the root dir. + (not (string= (file-name-directory file1) + parent-dir)))) + ;; Move up to the parent dir and try again. + (setq file1 (expand-file-name file-name parent-dir))) + ;; If we found the file in a parent dir, use that. Otherwise, + ;; return nil + (if (or (get-file-buffer file1) (file-exists-p file1)) + file1 + nil))) + +(defun dirvars-eat-comment () + (while (looking-at "[ \t\n]*;") + (let ((begin (point))) + (skip-chars-forward " \t\n") + (if (looking-at ";") + (progn + (end-of-line) + (delete-region begin (point))))))) + +(defun dirvars-hack-local-variables (dirvars-file) + (save-excursion + (let ((original-buffer (current-buffer)) + (temp-buffer (get-buffer-create "*dirvars-temp*")) + (enable-local-variables (and ;local-enable-local-variables -- doesn't exist! + enable-local-variables + dirvars-enable-flag)) + (continue t) + (parse-sexp-ignore-comments t) + (lisp-mode-hook nil) + beg) + (set-buffer temp-buffer) + (erase-buffer) + (lisp-mode) + (insert-file dirvars-file) + (goto-char (point-min)) + (catch 'done + (while continue + (if (null (scan-sexps (point) 1)) + (throw 'done nil)) + (goto-char (scan-sexps (point) 1)) + (goto-char (scan-sexps (point) -1)) + (if (eobp) + (throw 'done nil)) + (setq beg (point)) + (skip-chars-forward "^:\n") + (if (not (looking-at ":")) + (error (format "Missing colon in directory variables entry at %d" + (point)))) + (skip-chars-backward " \t") + (let* ((str (buffer-substring beg (point))) + (var (read str)) + val) + ;; Read the variable value. + (skip-chars-forward "^:") + (forward-char 1) + (setq val (read (current-buffer))) + (save-excursion + (set-buffer original-buffer) + (dirvars-hack-one-local-variable dirvars-file + var val)))))))) + +(defun dirvars-hack-one-local-variable (dirvars-file var val) + "\"Set\" one variable in a local variables spec. +A few variable names are treated specially." + (cond ((memq var ignored-local-variables) + nil) + ;; Trap risky variables and such. This is the same logic + ;; that files.el uses. + ((or (get var 'risky-local-variable) + (and + (string-match "-hooks?$\\|-functions?$\\|-forms?$\\|-program$\\|-command$\\|-predicate$" + (symbol-name var)) + (not (get var 'safe-local-variable)))) + (message (format "Ignoring %s in %s" + (symbol-name var) dirvars-file))) + ;;check whether the var should be evaluated + ((eq var 'evaluate) + (eval val)) + ;; Ordinary variable, really set it. + (t (make-local-variable var) + (set var val)))) + +(defun dirvars-hack-local-variables-before () + (let ((dirvars-file (dirvars-find-upwards ".emacs-dirvars"))) + (if dirvars-file + (dirvars-hack-local-variables dirvars-file)))) + +(defadvice hack-local-variables + (before dirvars-hack-local-variables-before) + "Process dirvars before a file's local variables are processed." + (dirvars-hack-local-variables-before)) +(ad-activate 'hack-local-variables) + +(provide 'dirvars) +;;; dirvars.el ends here diff --git a/scripts/kde-emacs/kde-emacs-bindings.el b/scripts/kde-emacs/kde-emacs-bindings.el new file mode 100644 index 00000000..84202dfb --- /dev/null +++ b/scripts/kde-emacs/kde-emacs-bindings.el @@ -0,0 +1,185 @@ +;; kde-emacs-bindings.el +;; +;; Copyright (C) 2002 KDE Development Team +;; +;; This library is free software; you can redistribute it and/or +;; modify it under the terms of the GNU Lesser General Public +;; License as published by the Free Software Foundation; either +;; version 2.1 of the License, or (at your option) any later version. +;; +;; This library is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; Lesser General Public License for more details. +;; +;; You should have received a copy of the GNU Lesser General Public +;; License along with this library; if not, write to the Free Software +;; Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA +;; 02110-1301 USA + +; currently no binding for header-protection and add-file-to-makefile-am, +; you need to call them from M-x + +; ----------------------------------------------------------------- +; The list below defines the following bindings: +; +; F2 : offer a grep command (use C-u F2 if you need to specify options, like -i or -w) +; Shift-F2 : offer a grep command to search in directories below the current too.. +; +; F3/Shift-F3/F8/Shift-RMB : different ways to see the list of methods in the current buffer +; +; F4 : make +; Shift-F4 : make clean +; F5 : make install +; Shift-F5 : make install-exec +; +; Shift-F6 : compile this file [assumes libtool is being used] +; F6 : Switch from .cpp/.cc to .h and vice-versa +; F7 : The same, but try to find the current method in the other file +; F9 : Create a member method in the .cpp, the cursor being on the definition in the .h +; F10: Place point on a class name, and the respective (Qt) include file will be inserted. +; This works with all Qt classes but can easily be extended to KDE classes. +; Shift-F10: Place point on a class name, and "class Blah" will be inserted near the top. +; Meta-F10: Place point on a class name, and press Meta-F10, and konqueror will load +; Qt documentation. Customize the location of the Qt documentation with the +; variable kdab-qt-documentation. XXX will be replace with the class name. +; Example (setq kdab-qt-location "file:/packages/kde-src/qt-copy/doc/html/XXX.html") +; +; M-n: jump to the next error (after compiling) or grep matches +; +; Ctrl+Meta+D : insert a kdDebug statement with the name of the current method +; [the new hide-all-windows shortcut conflicts with that, you may have to +; change it, or use Ctrl+Meta+Shift+D (!!)] +; +; Meta Up/Down : scroll the other window (when window is split) + +; Other very useful keybindings to know about: +; C-x r m to set a named bookmark in the buffer +; C-x r b to jump to a named bookmark in the buffer +; To save bookmarks to a file type: +; M-x bookmark-write +; and to load bookmarks from a file write: +; M-x bookmark-load + +(require 'kde-emacs-core) +(require 'kde-emacs-general) +(require 'kde-emacs-utils) +(require 'klaralv) +(require 'kde-emacs-utils) +(when (featurep 'semantic) + (require 'kde-emacs-semantic) + (require 'kde-emacs-doc)) + +;; Wheelmouse support +(define-key global-map [(button4)] 'scroll-me-down) +(define-key global-map [(button5)] 'scroll-me-up) +(define-key global-map [(shift button4)] 'scroll-me-down-a-bit) +(define-key global-map [(shift button5)] 'scroll-me-up-a-bit) + +;; Some example bindings, feel free to customize :) +(define-key global-map [(meta up)] 'scroll-other-up) +(define-key global-map [(meta down)] 'scroll-other-down) +(define-key global-map [(control j)] 'goto-line) +(global-set-key [(control %)] 'match-paren) ;;for all buffers :) + +(if (featurep 'igrep) + (progn + (setq igrep-find-prune-clause + (format "-type d %s -name CVS -o -name .libs -o -name .deps %s" + (shell-quote-argument "(") + (shell-quote-argument ")"))) + (setq igrep-find-file-clause + (format "-type f %s -name %s %s -name %s %s -name %s %s -name %s" ; -type l + (shell-quote-argument "!") + (shell-quote-argument "*~") ; Emacs backup + (shell-quote-argument "!") + (shell-quote-argument "*,v") ; RCS file + (shell-quote-argument "!") + (shell-quote-argument "s.*") ; SCCS file + (shell-quote-argument "!") + (shell-quote-argument "*.o") ; compiled object + (shell-quote-argument "!") + (shell-quote-argument ".#*") ; Emacs temp file + ) + ) + (define-key global-map [(f2)] 'igrep) + (define-key global-map [(shift f2)] 'igrep-find) + (define-key global-map [(f12)] 'igrep-find) ; on the console, shift f2 gives f12 for some reason.. + ;(setq igrep-files-default 'ignore) ; too hard to use *.cc *.h with it, because of the full path + ) + (define-key global-map [(f2)] 'grep)) +(define-key global-map [(shift backspace)] 'kde-delete-backward-ws) + +;; FIXME: remember to get these working on Gnu/Emacs (Zack) +(when (eq kde-emacs-type 'xemacs) + (define-key c++-mode-map [(f8)] 'function-menu) + (define-key c++-mode-map [(f3)] 'fume-prompt-function-goto) + (define-key c++-mode-map [(shift f3)] 'fume-list-functions) + ) + +(define-key global-map [(shift button3)] 'mouse-function-menu) +(define-key global-map [(shift f4)] 'makeclean) +(define-key global-map [(f4)] 'make) +(define-key global-map [(f5)] 'makeinstall) +(define-key global-map [(shift f5)] 'makeinstallexec) +(define-key global-map [(shift f6)] 'makethisfile) +(if kde-emacs-newline-semicolon + (define-key c++-mode-map "\;" 'insert-semicolon)) +(define-key c++-mode-map [(f6)] 'kde-switch-cpp-h) +(define-key c-mode-map [(f6)] 'kde-switch-cpp-h) +(define-key c++-mode-map [(f7)] 'switch-to-function-def) +(define-key c++-mode-map [(f9)] 'agulbra-make-member) +(define-key c-mode-map [(f9)] 'agulbra-make-member) +(define-key global-map [(meta n)] 'next-error) + +; kde-emacs-headers: +(define-key c++-mode-map [(f10)] 'kdab-insert-header) +(define-key c++-mode-map [(shift f10)] 'kdab-insert-forward-decl) +(define-key c++-mode-map [(meta f10)] 'kdab-lookup-qt-documentation) +(define-key c++-mode-map [(control meta d)] 'insert-kdDebug) + +; Standard Qt/KDE shortcuts: Ctrl+Backspace, Ctrl+Delete +(define-key global-map [(control backspace)] 'backward-kill-word) +(define-key global-map [(control delete)] 'kill-word) + +; Standard Qt/KDE shortcuts: Control Pageup and Pagedown +(define-key global-map [(control prior)] 'beginning-of-buffer) +(define-key global-map [(control next)] 'end-of-buffer) + +; kde-emacs-semantic : +; no binding for kde-license-insert; call it via M-x +(when (featurep 'semantic) + (define-key c++-mode-map [(control c)(control k)(d)] 'kde-doc-function-insert) + (define-key c++-mode-map [(control c)(control k)(m)] 'kde-doc-multiline-insert) + (define-key c++-mode-map [(control c)(control k)(o)] 'kde-doc-oneliner-insert) + (define-key c++-mode-map [(control c)(control k)(e)] 'kde-function-expand-at-point) + (define-key c++-mode-map [(control c)(control k)(s)] 'kde-create-skeletons)) + +(modify-frame-parameters (selected-frame) '((menu-bar-lines . 2))) +(define-key c++-mode-map [menu-bar KDE] + (cons "KDE" c++-mode-map)) +(when (featurep 'semantic) + (define-key c++-mode-map [menu-bar KDE kde-doc-function-insert] + '("kde-doc-function-insert" . kde-doc-function-insert)) + (define-key c++-mode-map [menu-bar KDE kde-function-expand-at-point] + '("kde-function-expand-at-point" . kde-function-expand-at-point)) + (define-key c++-mode-map [menu-bar KDE kde-create-skeletons] + '("kde-create-skeletons" . kde-create-skeletons)) + (define-key c++-mode-map [menu-bar KDE kde-doc-multiline-insert] + '("kde-doc-multiline-insert" . kde-doc-multiline-insert))) +(define-key c++-mode-map [menu-bar KDE makeclean] + '("make clean" . makeclean)) +(define-key c++-mode-map [menu-bar KDE make] + '("make" . make)) +(define-key c++-mode-map [menu-bar KDE makeinstall] + '("make install" . makeinstall)) +(define-key c++-mode-map [menu-bar KDE makethisfile] + '("make this file" . makethisfile)) +(define-key c++-mode-map [menu-bar KDE kdeswitchcpph] + '("Switch to .h/.cpp file" . kde-switch-cpp-h)) +(define-key c++-mode-map [menu-bar KDE insert-kdDebug] + '("Insert kdDebug" . insert-kdDebug)) + + +(provide 'kde-emacs-bindings) + diff --git a/scripts/kde-emacs/kde-emacs-compat.el b/scripts/kde-emacs/kde-emacs-compat.el new file mode 100644 index 00000000..1ff1fe7a --- /dev/null +++ b/scripts/kde-emacs/kde-emacs-compat.el @@ -0,0 +1,77 @@ +;; kde-emacs-compat.el - contains compatibility functions +;; +;; Copyright (C) 2003 Zack Rusin +;; 2003 KDE Developlment team +;; 2003 XEmacs developers +;; +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License +;; as published by the Free Software Foundation; either version 2 +;; of the License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, write to the Free Software +;; Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA +;; 02110-1301, USA. + +(require 'kde-emacs-vars) + +;;GNU/Emacs does not have this one +(if (not (fboundp 'replace-in-string)) + (defun replace-in-string (str regexp newtext &optional literal) + "Replace all matches in STR for REGEXP with NEWTEXT string, + and returns the new string. +Optional LITERAL non-nil means do a literal replacement. +Otherwise treat `\\' in NEWTEXT as special: + `\\&' in NEWTEXT means substitute original matched text. + `\\N' means substitute what matched the Nth `\\(...\\)'. + If Nth parens didn't match, substitute nothing. + `\\\\' means insert one `\\'. + `\\u' means upcase the next character. + `\\l' means downcase the next character. + `\\U' means begin upcasing all following characters. + `\\L' means begin downcasing all following characters. + `\\E' means terminate the effect of any `\\U' or `\\L'." + (if (> (length str) 50) + (with-temp-buffer + (insert str) + (goto-char 1) + (while (re-search-forward regexp nil t) + (replace-match newtext t literal)) + (buffer-string)) + (let ((start 0) newstr) + (while (string-match regexp str start) + (setq newstr (replace-match newtext t literal str) + start (+ (match-end 0) (- (length newstr) (length str))) + str newstr)) + str))) + + ) + +(if (not (fboundp 'read-shell-command)) + (progn + (defvar read-shell-command-map + (let ((map (make-sparse-keymap 'read-shell-command-map))) + (if (eq kde-emacs-type 'xemacs) + (set-keymap-parents map (list minibuffer-local-map)) + (set-keymap-parent map minibuffer-local-map)) + (define-key map "\t" 'comint-dynamic-complete) + (define-key map "\M-\t" 'comint-dynamic-complete) + (define-key map "\M-?" 'comint-dynamic-list-completions) + map) + "Minibuffer keymap used by `shell-command' and related commands.") + (defun read-shell-command (prompt &optional initial-input history default-value) + "Just like read-string, but uses read-shell-command-map: +\\{read-shell-command-map}" + (let ((minibuffer-completion-table nil)) + (read-from-minibuffer prompt initial-input read-shell-command-map + nil (or history 'shell-command-history) + nil default-value))) + )) + +(provide 'kde-emacs-compat) diff --git a/scripts/kde-emacs/kde-emacs-core.el b/scripts/kde-emacs/kde-emacs-core.el new file mode 100644 index 00000000..a954dfa0 --- /dev/null +++ b/scripts/kde-emacs/kde-emacs-core.el @@ -0,0 +1,3823 @@ +;; kde-emacs-core.el - core functionality,e.g. syntax & c++-mode-hook +;; +;; Copyright (C) 2002 KDE Development Team +;; +;; This library is free software; you can redistribute it and/or +;; modify it under the terms of the GNU Lesser General Public +;; License as published by the Free Software Foundation; either +;; version 2.1 of the License, or (at your option) any later version. +;; +;; This library is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; Lesser General Public License for more details. +;; +;; You should have received a copy of the GNU Lesser General Public +;; License along with this library; if not, write to the Free Software +;; Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA +;; 02110-1301 USA + +(require 'kde-emacs-vars) +;*---------------------------------------------------------------------*/ +;* Variables ... */ +;*---------------------------------------------------------------------*/ + +(defcustom kde-tab-behavior 'default + "Specifies the current tab behavior. default will expand try to complete +the symbol at point if at the end of something that looks like an indentifier else +it will indent the current line if the pointer is at the beginning of the line it will +be moved the the start of the indention. abbrev-indent behaves like default, but the +cursor isn't moved to the beginning of the indention with tab is pressed when the cursor +is at the beginning of the line. indent simply indents the line without trying to +complete the symbol" + :group 'kde-devel + :version "0.1" + :type `(choice (const default) (const abbrev-indent) (const indent))) + +;*---------------------------------------------------------------------*/ +;* Functions ... */ +;*---------------------------------------------------------------------*/ + + +;; ------- First part, from Arnt's "c++ stuff" - slightly modified for our needs :) + +(defun agulbra-c++-tab (arg) + "Do the right thing about tabs in c++ mode. +Try to finish the symbol, or indent the line." + (interactive "*P") + (cond + ((and (not (looking-at "[A-Za-z0-9]")) + (save-excursion + (forward-char -1) + (looking-at "[A-Za-z0-9:>_\\-\\&\\.(){}\\*\\+/]"))) + (dabbrev-expand arg)) + (t + (if (eq kde-tab-behavior 'default) + (c-indent-command) + (save-excursion + (beginning-of-line) + (c-indent-command)))))) + +(defun agulbra-clean-out-spaces () + "Remove spaces at ends of lines." + (interactive) + (and (not buffer-read-only) + (save-excursion + (goto-char (point-min)) + (let ((count 0) + (bmp (buffer-modified-p))) + (while (re-search-forward "[ \t]+$" nil t) + (setq count (1+ count)) + (replace-match "" t t)) + (set-buffer-modified-p bmp) + nil + )))) + +; the above used to contain (untabify (point-min) (point-max)) too + +(defun agulbra-c++-clean-out-spaces () + "Remove spaces at ends of lines, only in c++ mode." + (interactive) + (if (eq major-mode 'c++-mode) + (agulbra-clean-out-spaces) + ) + ) + +(defun agulbra-delete-into-nomenclature (&optional arg) + "Delete forward until the end of a nomenclature section or word. +With arg, do it arg times." + (interactive "p") + (save-excursion + (let ((b (point-marker))) + (c-forward-into-nomenclature arg) + (delete-region b (point-marker))))) + + +(if (not (fboundp 'font-lock-add-keywords)) + (defun font-lock-add-keywords (mode keywords &optional append) + "XEmacs doesn't have font-lock-add-keywords so we provide it." + (font-lock-set-defaults) + (if (eq append 'set) + (setq font-lock-keywords keywords) + ; NOTE: write this function for XEmacs - Zack + ;(font-lock-remove-keywords nil keywords) ;to avoid duplicates + (let ((old (if (eq (car-safe font-lock-keywords) t) + (cdr font-lock-keywords) + font-lock-keywords))) + (setq font-lock-keywords (if append + (append old keywords) + (append keywords old)))))) + ) + +(c-add-style "kde-c" '("stroustrup" + (c-basic-offset . 4) + (c-offsets-alist + (case-label . 4) + (access-label . -) + (label . 0) + (statement-cont . c-lineup-math) + ))) + +; ( we use Backquote ( '`' ) instead of "'" because we want +; kde-access-labels to be evaluated... ) +(c-add-style "kde-c++" `("kde-c" + ;;FIXME: 1) fume functions not available on GNU/Emacs + ;; 2) insert-tab-mode no longer present (free variable) + ;; 3) c-hangin-commment-under-p no longer present (free variable) + (if (not (eq kde-tab-behavior 'indent)) + (c-tab-always-indent . nil)) + ; (insert-tab-mode nil) + (indent-tabs-mode . nil) + (if (eq kde-emacs-type 'xemacs) + (fume-auto-rescan-buffer-p nil)) + (c-access-key . ,kde-access-labels) + (c-opt-access-key . ,kde-access-labels) + ; (c-hanging-comment-under-p nil) + (c-offsets-alist . ((case-label . 0) + (inline-open . 0))) + )) + +;; KDE C++ mode +;; Not a "(setq c++-mode-hook ..." because this way we would +;; prune all other hooks! +(defun kde-c++-mode-hook () + (font-lock-mode) + (c-set-style kde-c++-style) + (define-key c++-mode-map "\C-m" 'c-context-line-break) + (when (or + (eq kde-tab-behavior 'default) + (eq kde-tab-behavior 'abbrev-indent)) + (define-key c++-mode-map "\C-i" 'agulbra-c++-tab)) + (define-key c++-mode-map "\ef" 'c-forward-into-nomenclature) + (define-key c++-mode-map "\ed" 'agulbra-delete-into-nomenclature) + (define-key c++-mode-map "\eb" 'c-backward-into-nomenclature) + ;; fontify "public|protected|private slots" with one and the same face :) + ;; NOTE: write face-at-point function to fontify those just like other + ;; access specifiers + (font-lock-add-keywords nil '(("\\<\\(\\(public\\|protected\\|private\\) slots\\)\\>" + . font-lock-reference-face))) + ;; Add (setq magic-keys-mode nil) to your .emacs (before loading this file) + ;; to disable the magic keys in C++ mode. + (and (boundp 'magic-keys-mode) magic-keys-mode + (progn + (define-key c++-mode-map "\(" 'insert-parens) + (define-key c++-mode-map "\)" 'insert-parens2) + (define-key c++-mode-map "\," 'insert-comma) + (define-key c++-mode-map "\{" 'insert-curly-brace) + )) + ) + +(defun kde-c-mode-hook () + (font-lock-mode) + (c-set-style kde-c-style)) + +;; NOTE : This is a completely new c-guess-basic-syntax, it's faster, +;; better, meaner, harder, covers more cases, more c++ syntax, +;; and is in general cooler ;) You have to have the new cc-mode +;; to use it ( 5.30 at least, check it with "M-x c-version") +;; If you don't have 5.30 comment out the following c-guess-basic-syntax +;; and uncomment the one underneath. +(cond + ((string-match "^5\\.30\\." c-version) + (defun c-guess-basic-syntax () + "Return the syntactic context of the current line. +This function does not do any hidden buffer changes." + (save-excursion + (save-restriction + (beginning-of-line) + (c-save-buffer-state + ((indent-point (point)) + (case-fold-search nil) + (paren-state (c-parse-state)) + literal containing-sexp char-before-ip char-after-ip lim + c-syntactic-context placeholder c-in-literal-cache step-type + tmpsymbol keyword injava-inher special-brace-list + ;; narrow out any enclosing class or extern "C" block + (inclass-p (c-narrow-out-enclosing-class paren-state + indent-point)) + ;; `c-state-cache' is shadowed here so that we don't + ;; throw it away due to the narrowing that might be done + ;; by the function above. That means we must not do any + ;; changes during the execution of this function, since + ;; `c-invalidate-state-cache' then would change this local + ;; variable and leave a bogus value in the global one. + (c-state-cache (if inclass-p + (c-whack-state-before (point-min) paren-state) + paren-state)) + (c-state-cache-start (point-min)) + inenclosing-p macro-start in-macro-expr + ;; There's always at most one syntactic element which got + ;; a relpos. It's stored in syntactic-relpos. + syntactic-relpos + (c-stmt-delim-chars c-stmt-delim-chars)) + ;; Check for meta top-level enclosing constructs such as + ;; extern language definitions. + (save-excursion + (save-restriction + (widen) + (when (and inclass-p + (progn + (goto-char (aref inclass-p 0)) + (looking-at c-other-decl-block-key))) + (setq inenclosing-p (match-string 1)) + (if (string-equal inenclosing-p "extern") + ;; Compatibility with legacy choice of name for the + ;; extern-lang syntactic symbols. + (setq inenclosing-p "extern-lang"))))) + + ;; Init some position variables: + ;; + ;; containing-sexp is the open paren of the closest + ;; surrounding sexp or nil if there is none that hasn't been + ;; narrowed out. + ;; + ;; lim is the position after the closest preceding brace sexp + ;; (nested sexps are ignored), or the position after + ;; containing-sexp if there is none, or (point-min) if + ;; containing-sexp is nil. + ;; + ;; c-state-cache is the state from c-parse-state at + ;; indent-point, without any parens outside the region + ;; narrowed by c-narrow-out-enclosing-class. + ;; + ;; paren-state is the state from c-parse-state outside + ;; containing-sexp, or at indent-point if containing-sexp is + ;; nil. paren-state is not limited to the narrowed region, as + ;; opposed to c-state-cache. + (if c-state-cache + (progn + (setq containing-sexp (car paren-state) + paren-state (cdr paren-state)) + (if (consp containing-sexp) + (progn + (setq lim (cdr containing-sexp)) + (if (cdr c-state-cache) + ;; Ignore balanced paren. The next entry + ;; can't be another one. + (setq containing-sexp (car (cdr c-state-cache)) + paren-state (cdr paren-state)) + ;; If there is no surrounding open paren then + ;; put the last balanced pair back on paren-state. + (setq paren-state (cons containing-sexp paren-state) + containing-sexp nil))) + (setq lim (1+ containing-sexp)))) + (setq lim (point-min))) + + ;; If we're in a parenthesis list then ',' delimits the + ;; "statements" rather than being an operator (with the + ;; exception of the "for" clause). This difference is + ;; typically only noticeable when statements are used in macro + ;; arglists. + (when (and containing-sexp + (eq (char-after containing-sexp) ?\()) + (setq c-stmt-delim-chars c-stmt-delim-chars-with-comma)) + + ;; cache char before and after indent point, and move point to + ;; the most likely position to perform the majority of tests + (goto-char indent-point) + (c-backward-syntactic-ws lim) + (setq char-before-ip (char-before)) + (goto-char indent-point) + (skip-chars-forward " \t") + (setq char-after-ip (char-after)) + + ;; are we in a literal? + (setq literal (c-in-literal lim)) + + ;; now figure out syntactic qualities of the current line + (cond + ;; CASE 1: in a string. + ((eq literal 'string) + (c-add-syntax 'string (c-point 'bopl))) + ;; CASE 2: in a C or C++ style comment. + ((and (memq literal '(c c++)) + ;; This is a kludge for XEmacs where we use + ;; `buffer-syntactic-context', which doesn't correctly + ;; recognize "\*/" to end a block comment. + ;; `parse-partial-sexp' which is used by + ;; `c-literal-limits' will however do that in most + ;; versions, which results in that we get nil from + ;; `c-literal-limits' even when `c-in-literal' claims + ;; we're inside a comment. + (setq placeholder (c-literal-limits lim))) + (c-add-syntax literal (car placeholder))) + ;; CASE 3: in a cpp preprocessor macro continuation. + ((and (save-excursion + (when (c-beginning-of-macro) + (setq macro-start (point)))) + (/= macro-start (c-point 'boi)) + (progn + (setq tmpsymbol 'cpp-macro-cont) + (or (not c-syntactic-indentation-in-macros) + (save-excursion + (goto-char macro-start) + ;; If at the beginning of the body of a #define + ;; directive then analyze as cpp-define-intro + ;; only. Go on with the syntactic analysis + ;; otherwise. in-macro-expr is set if we're in a + ;; cpp expression, i.e. before the #define body + ;; or anywhere in a non-#define directive. + (if (c-forward-to-cpp-define-body) + (let ((indent-boi (c-point 'boi indent-point))) + (setq in-macro-expr (> (point) indent-boi) + tmpsymbol 'cpp-define-intro) + (= (point) indent-boi)) + (setq in-macro-expr t) + nil))))) + (c-add-syntax tmpsymbol macro-start) + (setq macro-start nil)) + ;; CASE 11: an else clause? + ((looking-at "else\\>[^_]") + (c-beginning-of-statement-1 containing-sexp) + (c-add-stmt-syntax 'else-clause nil t nil + containing-sexp paren-state)) + ;; CASE 12: while closure of a do/while construct? + ((and (looking-at "while\\>[^_]") + (save-excursion + (prog1 (eq (c-beginning-of-statement-1 containing-sexp) + 'beginning) + (setq placeholder (point))))) + (goto-char placeholder) + (c-add-stmt-syntax 'do-while-closure nil t nil + containing-sexp paren-state)) + ;; CASE 13: A catch or finally clause? This case is simpler + ;; than if-else and do-while, because a block is required + ;; after every try, catch and finally. + ((save-excursion + (and (cond ((c-major-mode-is 'c++-mode) + (looking-at "catch\\>[^_]")) + ((c-major-mode-is 'java-mode) + (looking-at "\\(catch\\|finally\\)\\>[^_]"))) + (and (c-safe (c-backward-syntactic-ws) + (c-backward-sexp) + t) + (eq (char-after) ?{) + (c-safe (c-backward-syntactic-ws) + (c-backward-sexp) + t) + (if (eq (char-after) ?\() + (c-safe (c-backward-sexp) t) + t)) + (looking-at "\\(try\\|catch\\)\\>[^_]") + (setq placeholder (point)))) + (goto-char placeholder) + (c-add-stmt-syntax 'catch-clause nil t nil + containing-sexp paren-state)) + ;; CASE 18: A substatement we can recognize by keyword. + ((save-excursion + (and c-opt-block-stmt-key + (if (c-mode-is-new-awk-p) + (c-awk-prev-line-incomplete-p containing-sexp) ; ACM 2002/3/29 + (not (eq char-before-ip ?\;))) + (not (memq char-after-ip '(?\) ?\] ?,))) + (or (not (eq char-before-ip ?})) + (c-looking-at-inexpr-block-backward c-state-cache)) + (> (point) + (progn + ;; Ought to cache the result from the + ;; c-beginning-of-statement-1 calls here. + (setq placeholder (point)) + (while (eq (setq step-type + (c-beginning-of-statement-1 lim)) + 'label)) + (if (eq step-type 'previous) + (goto-char placeholder) + (setq placeholder (point)) + (if (and (eq step-type 'same) + (not (looking-at c-opt-block-stmt-key))) + ;; Step up to the containing statement if we + ;; stayed in the same one. + (let (step) + (while (eq + (setq step + (c-beginning-of-statement-1 lim)) + 'label)) + (if (eq step 'up) + (setq placeholder (point)) + ;; There was no containing statement afterall. + (goto-char placeholder))))) + placeholder)) + (if (looking-at c-block-stmt-2-key) + ;; Require a parenthesis after these keywords. + ;; Necessary to catch e.g. synchronized in Java, + ;; which can be used both as statement and + ;; modifier. + (and (zerop (c-forward-token-2 1 nil)) + (eq (char-after) ?\()) + (looking-at c-opt-block-stmt-key)))) + (if (eq step-type 'up) + ;; CASE 18A: Simple substatement. + (progn + (goto-char placeholder) + (cond + ((eq char-after-ip ?{) + (c-add-stmt-syntax 'substatement-open nil nil nil + containing-sexp paren-state)) + ((save-excursion + (goto-char indent-point) + (back-to-indentation) + (looking-at c-label-key)) + (c-add-stmt-syntax 'substatement-label nil nil nil + containing-sexp paren-state)) + (t + (c-add-stmt-syntax 'substatement nil nil nil + containing-sexp paren-state)))) + ;; CASE 18B: Some other substatement. This is shared + ;; with case 10. + (c-guess-continued-construct indent-point + char-after-ip + placeholder + lim + paren-state))) + ;; CASE 4: In-expression statement. C.f. cases 7B, 16A and + ;; 17E. + ((and (or c-opt-inexpr-class-key + c-opt-inexpr-block-key + c-opt-lambda-key) + (setq placeholder (c-looking-at-inexpr-block + (c-safe-position containing-sexp paren-state) + containing-sexp))) + (setq tmpsymbol (assq (car placeholder) + '((inexpr-class . class-open) + (inexpr-statement . block-open)))) + (if tmpsymbol + ;; It's a statement block or an anonymous class. + (setq tmpsymbol (cdr tmpsymbol)) + ;; It's a Pike lambda. Check whether we are between the + ;; lambda keyword and the argument list or at the defun + ;; opener. + (setq tmpsymbol (if (eq char-after-ip ?{) + 'inline-open + 'lambda-intro-cont))) + (goto-char (cdr placeholder)) + (back-to-indentation) + (c-add-stmt-syntax tmpsymbol nil t nil + (c-most-enclosing-brace c-state-cache (point)) + (c-whack-state-after (point) paren-state)) + (unless (eq (point) (cdr placeholder)) + (c-add-syntax (car placeholder)))) + ;; CASE 5: Line is at top level. + ((null containing-sexp) + (cond + ;; CASE 5A: we are looking at a defun, brace list, class, + ;; or inline-inclass method opening brace + ((setq special-brace-list + (or (and c-special-brace-lists + (c-looking-at-special-brace-list)) + (eq char-after-ip ?{))) + (cond + ;; CASE 5A.1: Non-class declaration block open. + ((save-excursion + (goto-char indent-point) + (skip-chars-forward " \t") + (and (c-safe (c-backward-sexp 2) t) + (looking-at c-other-decl-block-key) + (setq keyword (match-string 1) + placeholder (point)) + (if (string-equal keyword "extern") + ;; Special case for extern-lang-open. The + ;; check for a following string is disabled + ;; since it doesn't disambiguate anything. + (and ;;(progn + ;; (c-forward-sexp 1) + ;; (c-forward-syntactic-ws) + ;; (eq (char-after) ?\")) + (setq tmpsymbol 'extern-lang-open)) + (setq tmpsymbol (intern (concat keyword "-open")))) + )) + (goto-char placeholder) + (c-add-syntax tmpsymbol (c-point 'boi))) + ;; CASE 5A.2: we are looking at a class opening brace + ((save-excursion + (goto-char indent-point) + (skip-chars-forward " \t{") + (let ((decl (c-search-uplist-for-classkey (c-parse-state)))) + (and decl + (setq placeholder (aref decl 0))) + )) + (c-add-syntax 'class-open placeholder)) + ;; CASE 5A.3: brace list open + ((save-excursion + (c-beginning-of-decl-1 lim) + (while (looking-at c-specifier-key) + (goto-char (match-end 1)) + (c-forward-syntactic-ws indent-point)) + (setq placeholder (c-point 'boi)) + (or (consp special-brace-list) + (and (or (save-excursion + (goto-char indent-point) + (setq tmpsymbol nil) + (while (and (> (point) placeholder) + (zerop (c-backward-token-2 1 t)) + (/= (char-after) ?=)) + (and c-opt-inexpr-brace-list-key + (not tmpsymbol) + (looking-at c-opt-inexpr-brace-list-key) + (setq tmpsymbol 'topmost-intro-cont))) + (eq (char-after) ?=)) + (looking-at c-brace-list-key)) + (save-excursion + (while (and (< (point) indent-point) + (zerop (c-forward-token-2 1 t)) + (not (memq (char-after) '(?\; ?\())))) + (not (memq (char-after) '(?\; ?\())) + )))) + (if (and (not c-auto-newline-analysis) + (c-major-mode-is 'java-mode) + (eq tmpsymbol 'topmost-intro-cont)) + ;; We're in Java and have found that the open brace + ;; belongs to a "new Foo[]" initialization list, + ;; which means the brace list is part of an + ;; expression and not a top level definition. We + ;; therefore treat it as any topmost continuation + ;; even though the semantically correct symbol still + ;; is brace-list-open, on the same grounds as in + ;; case B.2. + (progn + (c-beginning-of-statement-1 lim) + (c-add-syntax 'topmost-intro-cont (c-point 'boi))) + (c-add-syntax 'brace-list-open placeholder))) + ;; CASE 5A.4: inline defun open + ((and inclass-p (not inenclosing-p)) + (c-add-syntax 'inline-open) + (c-add-class-syntax 'inclass inclass-p paren-state)) + ;; CASE 5A.5: ordinary defun open + (t + (goto-char placeholder) + (if (or inclass-p macro-start) + (c-add-syntax 'defun-open (c-point 'boi)) + ;; Bogus to use bol here, but it's the legacy. + (c-add-syntax 'defun-open (c-point 'bol))) + ))) + ;; CASE 5B: first K&R arg decl or member init + ((c-just-after-func-arglist-p lim) + (cond + ;; CASE 5B.1: a member init + ((or (eq char-before-ip ?:) + (eq char-after-ip ?:)) + ;; this line should be indented relative to the beginning + ;; of indentation for the topmost-intro line that contains + ;; the prototype's open paren + ;; TBD: is the following redundant? + (if (eq char-before-ip ?:) + (forward-char -1)) + (c-backward-syntactic-ws lim) + ;; TBD: is the preceding redundant? + (if (eq (char-before) ?:) + (progn (forward-char -1) + (c-backward-syntactic-ws lim))) + (if (eq (char-before) ?\)) + (c-backward-sexp 1)) + (setq placeholder (point)) + (save-excursion + (and (c-safe (c-backward-sexp 1) t) + (looking-at "throw[^_]") + (c-safe (c-backward-sexp 1) t) + (setq placeholder (point)))) + (goto-char placeholder) + (c-add-syntax 'member-init-intro (c-point 'boi)) + ;; we don't need to add any class offset since this + ;; should be relative to the ctor's indentation + ) + ;; CASE 5B.2: K&R arg decl intro + (c-recognize-knr-p + (c-beginning-of-statement-1 lim) + (c-add-syntax 'knr-argdecl-intro (c-point 'boi)) + (if inclass-p + (c-add-class-syntax 'inclass inclass-p paren-state))) + ;; CASE 5B.3: Inside a member init list. + ((c-beginning-of-member-init-list lim) + (c-forward-syntactic-ws) + (c-add-syntax 'member-init-cont (point))) + ;; CASE 5B.4: Nether region after a C++ or Java func + ;; decl, which could include a `throws' declaration. + (t + (c-beginning-of-statement-1 lim) + (c-add-syntax 'func-decl-cont (c-point 'boi)) + ))) + ;; CASE 5C: inheritance line. could be first inheritance + ;; line, or continuation of a multiple inheritance + ((or (and (c-major-mode-is 'c++-mode) + (progn + (when (eq char-after-ip ?,) + (skip-chars-forward " \t") + (forward-char)) + (looking-at c-opt-postfix-decl-spec-key))) + (and (or (eq char-before-ip ?:) + ;; watch out for scope operator + (save-excursion + (and (eq char-after-ip ?:) + (c-safe (forward-char 1) t) + (not (eq (char-after) ?:)) + ))) + (save-excursion + (c-backward-syntactic-ws lim) + (if (eq char-before-ip ?:) + (progn + (forward-char -1) + (c-backward-syntactic-ws lim))) + (back-to-indentation) + (looking-at c-class-key))) + ;; for Java + (and (c-major-mode-is 'java-mode) + (let ((fence (save-excursion + (c-beginning-of-statement-1 lim) + (point))) + cont done) + (save-excursion + (while (not done) + (cond ((looking-at c-opt-postfix-decl-spec-key) + (setq injava-inher (cons cont (point)) + done t)) + ((or (not (c-safe (c-forward-sexp -1) t)) + (<= (point) fence)) + (setq done t)) + ) + (setq cont t))) + injava-inher) + (not (c-crosses-statement-barrier-p (cdr injava-inher) + (point))) + )) + (cond + ;; CASE 5C.1: non-hanging colon on an inher intro + ((eq char-after-ip ?:) + (c-beginning-of-statement-1 lim) + (c-add-syntax 'inher-intro (c-point 'boi)) + ;; don't add inclass symbol since relative point already + ;; contains any class offset + ) + ;; CASE 5C.2: hanging colon on an inher intro + ((eq char-before-ip ?:) + (c-beginning-of-statement-1 lim) + (c-add-syntax 'inher-intro (c-point 'boi)) + (if inclass-p + (c-add-class-syntax 'inclass inclass-p paren-state))) + ;; CASE kde hack: + ((and inclass-p + c-access-key + (looking-at c-access-key)) + (c-add-syntax 'access-label (c-point 'bonl)) + (c-add-class-syntax 'inclass inclass-p paren-state) + ) + ;; CASE 5C.3: in a Java implements/extends + (injava-inher + (let ((where (cdr injava-inher)) + (cont (car injava-inher))) + (goto-char where) + (cond ((looking-at "throws\\>[^_]") + (c-add-syntax 'func-decl-cont + (progn (c-beginning-of-statement-1 lim) + (c-point 'boi)))) + (cont (c-add-syntax 'inher-cont where)) + (t (c-add-syntax 'inher-intro + (progn (goto-char (cdr injava-inher)) + (c-beginning-of-statement-1 lim) + (point)))) + ))) + ;; CASE 5C.4: a continued inheritance line + (t + (c-beginning-of-inheritance-list lim) + (c-add-syntax 'inher-cont (point)) + ;; don't add inclass symbol since relative point already + ;; contains any class offset + ))) + ;; CASE 5D: this could be a top-level initialization, a + ;; member init list continuation, or a template argument + ;; list continuation. + ((c-with-syntax-table (if (c-major-mode-is 'c++-mode) + c++-template-syntax-table + (syntax-table)) + (save-excursion + ;; Note: We use the fact that lim is always after any + ;; preceding brace sexp. + (while (and (zerop (c-backward-token-2 1 t lim)) + (not (looking-at "[;<,=]")))) + (or (memq (char-after) '(?, ?=)) + (and (c-major-mode-is 'c++-mode) + (zerop (c-backward-token-2 1 nil lim)) + (eq (char-after) ?<))))) + (goto-char indent-point) + (setq placeholder + (c-beginning-of-member-init-list lim)) + (cond + ;; CASE 5D.1: hanging member init colon, but watch out + ;; for bogus matches on access specifiers inside classes. + ((and placeholder + (save-excursion + (setq placeholder (point)) + (c-backward-token-2 1 t lim) + (and (eq (char-after) ?:) + (not (eq (char-before) ?:)))) + (save-excursion + (goto-char placeholder) + (back-to-indentation) + (or + (/= (car (save-excursion + (parse-partial-sexp (point) placeholder))) + 0) + (and + (if c-opt-access-key + (not (looking-at c-opt-access-key)) t) + (not (looking-at c-class-key)) + (if c-opt-bitfield-key + (not (looking-at c-opt-bitfield-key)) t)) + ))) + (goto-char placeholder) + (c-forward-syntactic-ws) + (c-add-syntax 'member-init-cont (point)) + ;; we do not need to add class offset since relative + ;; point is the member init above us + ) + ;; CASE 5D.2: non-hanging member init colon + ((progn + (c-forward-syntactic-ws indent-point) + (eq (char-after) ?:)) + (skip-chars-forward " \t:") + (c-add-syntax 'member-init-cont (point))) + ;; CASE 5D.3: perhaps a template list continuation? + ((and (c-major-mode-is 'c++-mode) + (save-excursion + (save-restriction + (c-with-syntax-table c++-template-syntax-table + (goto-char indent-point) + (setq placeholder (c-up-list-backward (point))) + (and placeholder + (eq (char-after placeholder) ?<)))))) + ;; we can probably indent it just like an arglist-cont + (goto-char placeholder) + (c-beginning-of-statement-1 lim t) + (c-add-syntax 'template-args-cont (c-point 'boi))) + ;; CASE 5D.4: perhaps a multiple inheritance line? + ((and (c-major-mode-is 'c++-mode) + (save-excursion + (c-beginning-of-statement-1 lim) + (setq placeholder (point)) + (if (looking-at "static\\>[^_]") + (c-forward-token-2 1 nil indent-point)) + (and (looking-at c-class-key) + (zerop (c-forward-token-2 2 nil indent-point)) + (if (eq (char-after) ?<) + (c-with-syntax-table c++-template-syntax-table + (zerop (c-forward-token-2 1 t indent-point))) + t) + (eq (char-after) ?:)))) + (goto-char placeholder) + (c-add-syntax 'inher-cont (c-point 'boi))) + ;; CASE 5D.5: Continuation of the "expression part" of a + ;; top level construct. + (t + (while (and (eq (car (c-beginning-of-decl-1 containing-sexp)) + 'same) + (save-excursion + (c-backward-syntactic-ws) + (eq (char-before) ?})))) + (c-add-stmt-syntax + (if (eq char-before-ip ?,) + ;; A preceding comma at the top level means that a + ;; new variable declaration starts here. Use + ;; topmost-intro-cont for it, for consistency with + ;; the first variable declaration. C.f. case 5N. + 'topmost-intro-cont + 'statement-cont) + nil nil nil containing-sexp paren-state)) + )) + ;; CASE 5E: we are looking at a access specifier + ((and inclass-p + c-opt-access-key + (looking-at c-opt-access-key)) + (setq placeholder (c-add-class-syntax 'inclass inclass-p + paren-state)) + ;; Append access-label with the same anchor point as inclass gets. + (c-append-syntax 'access-label placeholder)) + ;; CASE 5F: Close of a non-class declaration level block. + ((and inenclosing-p + (eq char-after-ip ?})) + (c-add-syntax (intern (concat inenclosing-p "-close")) + (aref inclass-p 0))) + ;; CASE 5G: we are looking at the brace which closes the + ;; enclosing nested class decl + ((and inclass-p + (eq char-after-ip ?}) + (save-excursion + (save-restriction + (widen) + (forward-char 1) + (and (c-safe (c-backward-sexp 1) t) + (= (point) (aref inclass-p 1)) + )))) + (c-add-class-syntax 'class-close inclass-p paren-state)) + ;; CASE 5H: we could be looking at subsequent knr-argdecls + ((and c-recognize-knr-p + (not (eq char-before-ip ?})) + (save-excursion + (setq placeholder (cdr (c-beginning-of-decl-1 lim))) + (and placeholder + ;; Do an extra check to avoid tripping up on + ;; statements that occur in invalid contexts + ;; (e.g. in macro bodies where we don't really + ;; know the context of what we're looking at). + (not (and c-opt-block-stmt-key + (looking-at c-opt-block-stmt-key))))) + (< placeholder indent-point)) + (goto-char placeholder) + (c-add-syntax 'knr-argdecl (point))) + ;; CASE 5I: ObjC method definition. + ((and c-opt-method-key + (looking-at c-opt-method-key)) + (c-beginning-of-statement-1 lim) + (c-add-syntax 'objc-method-intro (c-point 'boi))) + ;; CASE 5P: AWK pattern or function or continuation + ;; thereof. + ((c-mode-is-new-awk-p) + (setq placeholder (point)) + (c-add-stmt-syntax + (if (and (eq (c-beginning-of-statement-1) 'same) + (/= (point) placeholder)) + 'topmost-intro-cont + 'topmost-intro) + nil nil nil + containing-sexp paren-state)) + ;; CASE 5N: At a variable declaration that follows a class + ;; definition or some other block declaration that doesn't + ;; end at the closing '}'. C.f. case 5D.5. + ((progn + (c-backward-syntactic-ws lim) + (and (eq (char-before) ?}) + (save-excursion + (let ((start (point))) + (if paren-state + ;; Speed up the backward search a bit. + (goto-char (car (car paren-state)))) + (c-beginning-of-decl-1 containing-sexp) + (setq placeholder (point)) + (if (= start (point)) + ;; The '}' is unbalanced. + nil + (c-end-of-decl-1) + (>= (point) indent-point)))))) + (goto-char placeholder) + (c-add-stmt-syntax 'topmost-intro-cont nil nil nil + containing-sexp paren-state)) + ;; CASE 5J: we are at the topmost level, make + ;; sure we skip back past any access specifiers + ((progn + (while (and inclass-p + c-opt-access-key + (not (bobp)) + (save-excursion + (c-safe (progn (c-backward-sexp 1) t)) + (and (looking-at "slots:") + (c-backward-sexp 1)) + (looking-at c-opt-access-key))) + (c-backward-sexp 1) + (c-backward-syntactic-ws lim)) + (or (bobp) + (if (c-mode-is-new-awk-p) + (not (c-awk-prev-line-incomplete-p)) + (memq (char-before) '(?\; ?}))) + (and (c-major-mode-is 'objc-mode) + (progn + (c-beginning-of-statement-1 lim) + (eq (char-after) ?@))))) + ;; real beginning-of-line could be narrowed out due to + ;; enclosure in a class block + (save-restriction + (widen) + (c-add-syntax 'topmost-intro (c-point 'bol)) + ;; Using bol instead of boi above is highly bogus, and + ;; it makes our lives hard to remain compatible. :P + (if inclass-p + (progn + (goto-char (aref inclass-p 1)) + (or (= (point) (c-point 'boi)) + (goto-char (aref inclass-p 0))) + (if inenclosing-p + (c-add-syntax (intern (concat "in" inenclosing-p)) + (c-point 'boi)) + (c-add-class-syntax 'inclass inclass-p paren-state)) + )) + (when (and c-syntactic-indentation-in-macros + macro-start + (/= macro-start (c-point 'boi indent-point))) + (c-add-syntax 'cpp-define-intro) + (setq macro-start nil)) + )) + ;; CASE 5K: we are at an ObjC method definition + ;; continuation line. + ((and c-opt-method-key + (progn + (c-beginning-of-statement-1 lim) + (beginning-of-line) + (looking-at c-opt-method-key))) + (c-add-syntax 'objc-method-args-cont (point))) + ;; CASE 5L: we are at the first argument of a template + ;; arglist that begins on the previous line. + ((eq (char-before) ?<) + (c-beginning-of-statement-1 (c-safe-position (point) paren-state)) + (c-add-syntax 'template-args-cont (c-point 'boi))) + ;; CASE 5M: we are at a topmost continuation line + (t + (c-beginning-of-statement-1 (c-safe-position (point) paren-state)) + (c-add-syntax 'topmost-intro-cont (c-point 'boi))) + )) + ;; (CASE 6 has been removed.) + ;; CASE 7: line is an expression, not a statement. Most + ;; likely we are either in a function prototype or a function + ;; call argument list + ((not (or (and c-special-brace-lists + (save-excursion + (goto-char containing-sexp) + (c-looking-at-special-brace-list))) + (eq (char-after containing-sexp) ?{))) + (cond + ;; CASE 7A: we are looking at the arglist closing paren. + ;; C.f. case 7F. + ((memq char-after-ip '(?\) ?\])) + (goto-char containing-sexp) + (setq placeholder (c-point 'boi)) + (if (and (c-safe (backward-up-list 1) t) + (> (point) placeholder)) + (progn + (forward-char) + (skip-chars-forward " \t")) + (goto-char placeholder)) + (c-add-stmt-syntax 'arglist-close (list containing-sexp) t nil + (c-most-enclosing-brace paren-state (point)) + (c-whack-state-after (point) paren-state))) + ;; CASE 7B: Looking at the opening brace of an + ;; in-expression block or brace list. C.f. cases 4, 16A + ;; and 17E. + ((and (eq char-after-ip ?{) + (progn + (setq placeholder (c-inside-bracelist-p (point) + c-state-cache)) + (if placeholder + (setq tmpsymbol '(brace-list-open . inexpr-class)) + (setq tmpsymbol '(block-open . inexpr-statement) + placeholder + (cdr-safe (c-looking-at-inexpr-block + (c-safe-position containing-sexp + paren-state) + containing-sexp))) + ;; placeholder is nil if it's a block directly in + ;; a function arglist. That makes us skip out of + ;; this case. + ))) + (goto-char placeholder) + (back-to-indentation) + (c-add-stmt-syntax (car tmpsymbol) nil t nil + (c-most-enclosing-brace paren-state (point)) + (c-whack-state-after (point) paren-state)) + (if (/= (point) placeholder) + (c-add-syntax (cdr tmpsymbol)))) + ;; CASE 7C: we are looking at the first argument in an empty + ;; argument list. Use arglist-close if we're actually + ;; looking at a close paren or bracket. + ((memq char-before-ip '(?\( ?\[)) + (goto-char containing-sexp) + (setq placeholder (c-point 'boi)) + (when (and (c-safe (backward-up-list 1) t) + (> (point) placeholder)) + (forward-char) + (skip-chars-forward " \t") + (setq placeholder (point))) + (c-add-syntax 'arglist-intro placeholder)) + ;; CASE 7D: we are inside a conditional test clause. treat + ;; these things as statements + ((progn + (goto-char containing-sexp) + (and (c-safe (c-forward-sexp -1) t) + (looking-at "\\[^_]"))) + (goto-char (1+ containing-sexp)) + (c-forward-syntactic-ws indent-point) + (if (eq char-before-ip ?\;) + (c-add-syntax 'statement (point)) + (c-add-syntax 'statement-cont (point)) + )) + ;; CASE 7E: maybe a continued ObjC method call. This is the + ;; case when we are inside a [] bracketed exp, and what + ;; precede the opening bracket is not an identifier. + ((and c-opt-method-key + (eq (char-after containing-sexp) ?\[) + (progn + (goto-char (1- containing-sexp)) + (c-backward-syntactic-ws (c-point 'bod)) + (if (not (looking-at c-symbol-key)) + (c-add-syntax 'objc-method-call-cont containing-sexp)) + ))) + ;; CASE 7F: we are looking at an arglist continuation line, + ;; but the preceding argument is on the same line as the + ;; opening paren. This case includes multi-line + ;; mathematical paren groupings, but we could be on a + ;; for-list continuation line. C.f. case 7A. + ((progn + (goto-char (1+ containing-sexp)) + (skip-chars-forward " \t") + (and (not (eolp)) + (not (looking-at "\\\\$")))) + (goto-char containing-sexp) + (setq placeholder (c-point 'boi)) + (if (and (c-safe (backward-up-list 1) t) + (> (point) placeholder)) + (progn + (forward-char) + (skip-chars-forward " \t")) + (goto-char placeholder)) + (c-add-stmt-syntax 'arglist-cont-nonempty (list containing-sexp) + t nil + (c-most-enclosing-brace c-state-cache (point)) + (c-whack-state-after (point) paren-state))) + ;; CASE 7G: we are looking at just a normal arglist + ;; continuation line + (t (c-forward-syntactic-ws indent-point) + (c-add-syntax 'arglist-cont (c-point 'boi))) + )) + ;; CASE 8: func-local multi-inheritance line + ((and (c-major-mode-is 'c++-mode) + (save-excursion + (goto-char indent-point) + (skip-chars-forward " \t") + (looking-at c-opt-postfix-decl-spec-key))) + (goto-char indent-point) + (skip-chars-forward " \t") + (cond + ;; CASE 8A: non-hanging colon on an inher intro + ((eq char-after-ip ?:) + (c-backward-syntactic-ws lim) + (c-add-syntax 'inher-intro (c-point 'boi))) + ;; CASE 8B: hanging colon on an inher intro + ((eq char-before-ip ?:) + (c-add-syntax 'inher-intro (c-point 'boi))) + ;; CASE 8C: a continued inheritance line + (t + (c-beginning-of-inheritance-list lim) + (c-add-syntax 'inher-cont (point)) + ))) + ;; CASE 9: we are inside a brace-list + ((and (not (c-mode-is-new-awk-p)) ; Maybe this isn't needed (ACM, 2002/3/29) + (setq special-brace-list + (or (and c-special-brace-lists + (save-excursion + (goto-char containing-sexp) + (c-looking-at-special-brace-list))) + (c-inside-bracelist-p containing-sexp paren-state)))) + (cond + ;; CASE 9A: In the middle of a special brace list opener. + ((and (consp special-brace-list) + (save-excursion + (goto-char containing-sexp) + (eq (char-after) ?\()) + (eq char-after-ip (car (cdr special-brace-list)))) + (goto-char (car (car special-brace-list))) + (skip-chars-backward " \t") + (if (and (bolp) + (assoc 'statement-cont + (setq placeholder (c-guess-basic-syntax)))) + (setq c-syntactic-context placeholder) + (c-beginning-of-statement-1 + (c-safe-position (1- containing-sexp) paren-state)) + (c-forward-token-2 0) + (while (looking-at c-specifier-key) + (goto-char (match-end 1)) + (c-forward-syntactic-ws)) + (c-add-syntax 'brace-list-open (c-point 'boi)))) + ;; CASE 9B: brace-list-close brace + ((if (consp special-brace-list) + ;; Check special brace list closer. + (progn + (goto-char (car (car special-brace-list))) + (save-excursion + (goto-char indent-point) + (back-to-indentation) + (or + ;; We were between the special close char and the `)'. + (and (eq (char-after) ?\)) + (eq (1+ (point)) (cdr (car special-brace-list)))) + ;; We were before the special close char. + (and (eq (char-after) (cdr (cdr special-brace-list))) + (zerop (c-forward-token-2)) + (eq (1+ (point)) (cdr (car special-brace-list))))))) + ;; Normal brace list check. + (and (eq char-after-ip ?}) + (c-safe (goto-char (c-up-list-backward (point))) t) + (= (point) containing-sexp))) + (if (eq (point) (c-point 'boi)) + (c-add-syntax 'brace-list-close (point)) + (setq lim (c-most-enclosing-brace c-state-cache (point))) + (c-beginning-of-statement-1 lim) + (c-add-stmt-syntax 'brace-list-close nil t t lim + (c-whack-state-after (point) paren-state)))) + (t + ;; Prepare for the rest of the cases below by going to the + ;; token following the opening brace + (if (consp special-brace-list) + (progn + (goto-char (car (car special-brace-list))) + (c-forward-token-2 1 nil indent-point)) + (goto-char containing-sexp)) + (forward-char) + (let ((start (point))) + (c-forward-syntactic-ws indent-point) + (goto-char (max start (c-point 'bol)))) + (c-skip-ws-forward indent-point) + (cond + ;; CASE 9C: we're looking at the first line in a brace-list + ((= (point) indent-point) + (if (consp special-brace-list) + (goto-char (car (car special-brace-list))) + (goto-char containing-sexp)) + (if (eq (point) (c-point 'boi)) + (c-add-syntax 'brace-list-intro (point)) + (setq lim (c-most-enclosing-brace c-state-cache (point))) + (c-beginning-of-statement-1 lim) + (c-add-stmt-syntax 'brace-list-intro nil t t lim + (c-whack-state-after (point) paren-state)))) + ;; CASE 9D: this is just a later brace-list-entry or + ;; brace-entry-open + (t (if (or (eq char-after-ip ?{) + (and c-special-brace-lists + (save-excursion + (goto-char indent-point) + (c-forward-syntactic-ws (c-point 'eol)) + (c-looking-at-special-brace-list (point))))) + (c-add-syntax 'brace-entry-open (point)) + (c-add-syntax 'brace-list-entry (point)) + )) + )))) + ;; CASE 10: A continued statement or top level construct. + ((and (if (c-mode-is-new-awk-p) + (c-awk-prev-line-incomplete-p containing-sexp) ; ACM 2002/3/29 + (and (not (memq char-before-ip '(?\; ?:))) + (or (not (eq char-before-ip ?})) + (c-looking-at-inexpr-block-backward c-state-cache)))) + (> (point) + (save-excursion + (c-beginning-of-statement-1 containing-sexp) + (setq placeholder (point)))) + (/= placeholder containing-sexp)) + ;; This is shared with case 18. + (c-guess-continued-construct indent-point + char-after-ip + placeholder + containing-sexp + paren-state)) + ;; CASE 14: A case or default label + ((looking-at c-label-kwds-regexp) + (goto-char containing-sexp) + (setq lim (c-most-enclosing-brace c-state-cache containing-sexp)) + (c-backward-to-block-anchor lim) + (c-add-stmt-syntax 'case-label nil t nil + lim paren-state)) + ;; CASE 15: any other label + ((looking-at c-label-key) + (goto-char containing-sexp) + (setq lim (c-most-enclosing-brace c-state-cache containing-sexp)) + (save-excursion + (setq tmpsymbol + (if (and (eq (c-beginning-of-statement-1 lim) 'up) + (looking-at "switch\\>[^_]")) + ;; If the surrounding statement is a switch then + ;; let's analyze all labels as switch labels, so + ;; that they get lined up consistently. + 'case-label + 'label))) + (c-backward-to-block-anchor lim) + (c-add-stmt-syntax tmpsymbol nil t nil + lim paren-state)) + ;; CASE 16: block close brace, possibly closing the defun or + ;; the class + ((eq char-after-ip ?}) + ;; From here on we have the next containing sexp in lim. + (setq lim (c-most-enclosing-brace paren-state)) + (goto-char containing-sexp) + (cond + ;; CASE 16E: Closing a statement block? This catches + ;; cases where it's preceded by a statement keyword, + ;; which works even when used in an "invalid" context, + ;; e.g. a macro argument. + ((c-after-conditional) + (c-backward-to-block-anchor lim) + (c-add-stmt-syntax 'block-close nil t nil + lim paren-state)) + ;; CASE 16A: closing a lambda defun or an in-expression + ;; block? C.f. cases 4, 7B and 17E. + ((setq placeholder (c-looking-at-inexpr-block + (c-safe-position containing-sexp paren-state) + nil)) + (setq tmpsymbol (if (eq (car placeholder) 'inlambda) + 'inline-close + 'block-close)) + (goto-char containing-sexp) + (back-to-indentation) + (if (= containing-sexp (point)) + (c-add-syntax tmpsymbol (point)) + (goto-char (cdr placeholder)) + (back-to-indentation) + (c-add-stmt-syntax tmpsymbol nil t nil + (c-most-enclosing-brace paren-state (point)) + (c-whack-state-after (point) paren-state)) + (if (/= (point) (cdr placeholder)) + (c-add-syntax (car placeholder))))) + ;; CASE 16B: does this close an inline or a function in + ;; a non-class declaration level block? + ((setq placeholder (c-search-uplist-for-classkey paren-state)) + (c-backward-to-decl-anchor lim) + (back-to-indentation) + (if (save-excursion + (goto-char (aref placeholder 0)) + (looking-at c-other-decl-block-key)) + (c-add-syntax 'defun-close (point)) + (c-add-syntax 'inline-close (point)))) + ;; CASE 16F: Can be a defun-close of a function declared + ;; in a statement block, e.g. in Pike or when using gcc + ;; extensions. Might also trigger it with some macros + ;; followed by blocks, and this gives sane indentation + ;; then too. Let it through to be handled below. + ;; C.f. cases B.3 and 17G. + ((and (not inenclosing-p) + lim + (save-excursion + (and (not (c-looking-at-bos)) + (eq (c-beginning-of-statement-1 lim nil nil t) 'same) + (setq placeholder (point))))) + (back-to-indentation) + (if (/= (point) containing-sexp) + (goto-char placeholder)) + (c-add-stmt-syntax 'defun-close nil t nil + lim paren-state)) + ;; CASE 16C: if there an enclosing brace that hasn't + ;; been narrowed out by a class, then this is a + ;; block-close. C.f. case 17H. + ((and (not inenclosing-p) lim) + ;; If the block is preceded by a case/switch label on + ;; the same line, we anchor at the first preceding label + ;; at boi. The default handling in c-add-stmt-syntax is + ;; really fixes it better, but we do like this to keep + ;; the indentation compatible with version 5.28 and + ;; earlier. + (while (and (/= (setq placeholder (point)) (c-point 'boi)) + (eq (c-beginning-of-statement-1 lim) 'label))) + (goto-char placeholder) + (if (looking-at c-label-kwds-regexp) + (c-add-syntax 'block-close (point)) + (goto-char containing-sexp) + ;; c-backward-to-block-anchor not necessary here; those + ;; situations are handled in case 16E above. + (c-add-stmt-syntax 'block-close nil t nil + lim paren-state))) + ;; CASE 16D: find out whether we're closing a top-level + ;; class or a defun + (t + (save-restriction + (narrow-to-region (point-min) indent-point) + (let ((decl (c-search-uplist-for-classkey (c-parse-state)))) + (if decl + (c-add-class-syntax 'class-close decl paren-state) + (goto-char containing-sexp) + (c-backward-to-decl-anchor lim) + (back-to-indentation) + (c-add-syntax 'defun-close (point))))) + ))) + ;; CASE 17: Statement or defun catchall. + (t + (goto-char indent-point) + ;; Back up statements until we find one that starts at boi. + (while (let* ((prev-point (point)) + (last-step-type (c-beginning-of-statement-1 + containing-sexp))) + (if (= (point) prev-point) + (progn + (setq step-type (or step-type last-step-type)) + nil) + (setq step-type last-step-type) + (/= (point) (c-point 'boi))))) + (cond + ;; CASE 17B: continued statement + ((and (eq step-type 'same) + (/= (point) indent-point)) + (c-add-stmt-syntax 'statement-cont nil nil nil + containing-sexp paren-state)) + ;; CASE 17A: After a case/default label? + ((progn + (while (and (eq step-type 'label) + (not (looking-at c-label-kwds-regexp))) + (setq step-type + (c-beginning-of-statement-1 containing-sexp))) + (eq step-type 'label)) + (c-add-stmt-syntax (if (eq char-after-ip ?{) + 'statement-case-open + 'statement-case-intro) + nil t nil containing-sexp paren-state)) + ;; CASE 17D: any old statement + ((progn + (while (eq step-type 'label) + (setq step-type + (c-beginning-of-statement-1 containing-sexp))) + (eq step-type 'previous)) + (c-add-stmt-syntax 'statement nil t nil + containing-sexp paren-state) + (if (eq char-after-ip ?{) + (c-add-syntax 'block-open))) + ;; CASE 17I: Inside a substatement block. + ((progn + ;; The following tests are all based on containing-sexp. + (goto-char containing-sexp) + ;; From here on we have the next containing sexp in lim. + (setq lim (c-most-enclosing-brace paren-state containing-sexp)) + (c-after-conditional)) + (c-backward-to-block-anchor lim) + (c-add-stmt-syntax 'statement-block-intro nil t nil + lim paren-state) + (if (eq char-after-ip ?{) + (c-add-syntax 'block-open))) + ;; CASE 17E: first statement in an in-expression block. + ;; C.f. cases 4, 7B and 16A. + ((setq placeholder (c-looking-at-inexpr-block + (c-safe-position containing-sexp paren-state) + nil)) + (setq tmpsymbol (if (eq (car placeholder) 'inlambda) + 'defun-block-intro + 'statement-block-intro)) + (back-to-indentation) + (if (= containing-sexp (point)) + (c-add-syntax tmpsymbol (point)) + (goto-char (cdr placeholder)) + (back-to-indentation) + (c-add-stmt-syntax tmpsymbol nil t nil + (c-most-enclosing-brace c-state-cache (point)) + (c-whack-state-after (point) paren-state)) + (if (/= (point) (cdr placeholder)) + (c-add-syntax (car placeholder)))) + (if (eq char-after-ip ?{) + (c-add-syntax 'block-open))) + ;; CASE 17F: first statement in an inline, or first + ;; statement in a top-level defun. we can tell this is it + ;; if there are no enclosing braces that haven't been + ;; narrowed out by a class (i.e. don't use bod here). + ((save-excursion + (save-restriction + (widen) + (c-narrow-out-enclosing-class paren-state containing-sexp) + (not (c-most-enclosing-brace paren-state)))) + (c-backward-to-decl-anchor lim) + (back-to-indentation) + (c-add-syntax 'defun-block-intro (point))) + ;; CASE 17G: First statement in a function declared inside + ;; a normal block. This can occur in Pike and with + ;; e.g. the gcc extensions. Might also trigger it with + ;; some macros followed by blocks, and this gives sane + ;; indentation then too. C.f. cases B.3 and 16F. + ((save-excursion + (and (not (c-looking-at-bos)) + (eq (c-beginning-of-statement-1 lim nil nil t) 'same) + (setq placeholder (point)))) + (back-to-indentation) + (if (/= (point) containing-sexp) + (goto-char placeholder)) + (c-add-stmt-syntax 'defun-block-intro nil t nil + lim paren-state)) + ;; CASE 17H: First statement in a block. C.f. case 16C. + (t + ;; If the block is preceded by a case/switch label on the + ;; same line, we anchor at the first preceding label at + ;; boi. The default handling in c-add-stmt-syntax is + ;; really fixes it better, but we do like this to keep the + ;; indentation compatible with version 5.28 and earlier. + (while (and (/= (setq placeholder (point)) (c-point 'boi)) + (eq (c-beginning-of-statement-1 lim) 'label))) + (goto-char placeholder) + (if (looking-at c-label-kwds-regexp) + (c-add-syntax 'statement-block-intro (point)) + (goto-char containing-sexp) + ;; c-backward-to-block-anchor not necessary here; those + ;; situations are handled in case 17I above. + (c-add-stmt-syntax 'statement-block-intro nil t nil + lim paren-state)) + (if (eq char-after-ip ?{) + (c-add-syntax 'block-open))) + )) + ) + ;; now we need to look at any modifiers + (goto-char indent-point) + (skip-chars-forward " \t") + ;; are we looking at a comment only line? + (when (and (looking-at c-comment-start-regexp) + (/= (c-forward-token-2 0 nil (c-point 'eol)) 0)) + (c-append-syntax 'comment-intro)) + ;; we might want to give additional offset to friends (in C++). + (when (and c-opt-friend-key + (looking-at c-opt-friend-key)) + (c-append-syntax 'friend)) + + ;; Set syntactic-relpos. + (let ((p c-syntactic-context)) + (while (and p + (if (integerp (car-safe (cdr-safe (car p)))) + (progn + (setq syntactic-relpos (car (cdr (car p)))) + nil) + t)) + (setq p (cdr p)))) + + ;; Start of or a continuation of a preprocessor directive? + (if (and macro-start + (eq macro-start (c-point 'boi)) + (not (and (c-major-mode-is 'pike-mode) + (eq (char-after (1+ macro-start)) ?\")))) + (c-append-syntax 'cpp-macro) + (when (and c-syntactic-indentation-in-macros macro-start) + (if in-macro-expr + (when (or + (< syntactic-relpos macro-start) + (not (or + (assq 'arglist-intro c-syntactic-context) + (assq 'arglist-cont c-syntactic-context) + (assq 'arglist-cont-nonempty c-syntactic-context) + (assq 'arglist-close c-syntactic-context)))) + ;; If inside a cpp expression, i.e. anywhere in a + ;; cpp directive except a #define body, we only let + ;; through the syntactic analysis that is internal + ;; in the expression. That means the arglist + ;; elements, if they are anchored inside the cpp + ;; expression. + (setq c-syntactic-context nil) + (c-add-syntax 'cpp-macro-cont macro-start)) + (when (and (eq macro-start syntactic-relpos) + (not (assq 'cpp-define-intro c-syntactic-context)) + (save-excursion + (goto-char macro-start) + (or (not (c-forward-to-cpp-define-body)) + (<= (point) (c-point 'boi indent-point))))) + ;; Inside a #define body and the syntactic analysis is + ;; anchored on the start of the #define. In this case + ;; we add cpp-define-intro to get the extra + ;; indentation of the #define body. + (c-add-syntax 'cpp-define-intro))))) + ;; return the syntax + c-syntactic-context))))) + ((>= (string-to-number c-version) 5.29) + (defun c-guess-basic-syntax () + "Return the syntactic context of the current line." + (save-excursion + (save-restriction + (beginning-of-line) + (let* ((indent-point (point)) + (case-fold-search nil) + (paren-state (c-parse-state)) + literal containing-sexp char-before-ip char-after-ip lim + syntax placeholder c-in-literal-cache step-type + tmpsymbol keyword injava-inher special-brace-list + ;; narrow out any enclosing class or extern "C" block + (inclass-p (c-narrow-out-enclosing-class paren-state + indent-point)) + ;; c-state-cache is shadowed here. That means we must + ;; not do any changes during the execution of this + ;; function, since c-check-state-cache then would change + ;; this local variable and leave a bogus value in the + ;; global one. + (c-state-cache (if inclass-p + (c-whack-state-before (point-min) paren-state) + paren-state)) + (c-state-cache-start (point-min)) + inenclosing-p macro-start in-macro-expr + ;; There's always at most one syntactic element which got + ;; a relpos. It's stored in syntactic-relpos. + syntactic-relpos + (c-stmt-delim-chars c-stmt-delim-chars)) + ;; check for meta top-level enclosing constructs, possible + ;; extern language definitions, possibly (in C++) namespace + ;; definitions. + (save-excursion + (save-restriction + (widen) + (if (and inclass-p + (progn + (goto-char (aref inclass-p 0)) + (looking-at c-other-decl-block-key))) + (let ((enclosing (match-string 1))) + (cond + ((string-equal enclosing "extern") + (setq inenclosing-p 'extern)) + ((string-equal enclosing "namespace") + (setq inenclosing-p 'namespace)) + ))))) + + ;; Init some position variables: + ;; + ;; containing-sexp is the open paren of the closest + ;; surrounding sexp or nil if there is none that hasn't been + ;; narrowed out. + ;; + ;; lim is the position after the closest preceding brace sexp + ;; (nested sexps are ignored), or the position after + ;; containing-sexp if there is none, or (point-min) if + ;; containing-sexp is nil. + ;; + ;; c-state-cache is the state from c-parse-state at + ;; indent-point, without any parens outside the region + ;; narrowed by c-narrow-out-enclosing-class. + ;; + ;; paren-state is the state from c-parse-state outside + ;; containing-sexp, or at indent-point if containing-sexp is + ;; nil. paren-state is not limited to the narrowed region, as + ;; opposed to c-state-cache. + (if c-state-cache + (progn + (setq containing-sexp (car paren-state) + paren-state (cdr paren-state)) + (if (consp containing-sexp) + (progn + (setq lim (cdr containing-sexp)) + (if (cdr c-state-cache) + ;; Ignore balanced paren. The next entry + ;; can't be another one. + (setq containing-sexp (car (cdr c-state-cache)) + paren-state (cdr paren-state)) + ;; If there is no surrounding open paren then + ;; put the last balanced pair back on paren-state. + (setq paren-state (cons containing-sexp paren-state) + containing-sexp nil))) + (setq lim (1+ containing-sexp)))) + (setq lim (point-min))) + + ;; If we're in a parenthesis list then ',' delimits the + ;; "statements" rather than being an operator (with the + ;; exception of the "for" clause). This difference is + ;; typically only noticeable when statements are used in macro + ;; arglists. + (when (and containing-sexp + (eq (char-after containing-sexp) ?\()) + (setq c-stmt-delim-chars c-stmt-delim-chars-with-comma)) + + ;; cache char before and after indent point, and move point to + ;; the most likely position to perform the majority of tests + (goto-char indent-point) + (c-backward-syntactic-ws lim) + (setq char-before-ip (char-before)) + (goto-char indent-point) + (skip-chars-forward " \t") + (setq char-after-ip (char-after)) + + ;; are we in a literal? + (setq literal (c-in-literal lim)) + + ;; now figure out syntactic qualities of the current line + (cond + ;; CASE 1: in a string. + ((eq literal 'string) + (c-add-syntax 'string (c-point 'bopl))) + ;; CASE 2: in a C or C++ style comment. + ((memq literal '(c c++)) + (c-add-syntax literal (car (c-literal-limits lim)))) + ;; CASE 3: in a cpp preprocessor macro continuation. + ((and (save-excursion + (when (c-beginning-of-macro) + (setq macro-start (point)))) + (/= macro-start (c-point 'boi)) + (progn + (setq tmpsymbol 'cpp-macro-cont) + (or (not c-syntactic-indentation-in-macros) + (save-excursion + (goto-char macro-start) + ;; If at the beginning of the body of a #define + ;; directive then analyze as cpp-define-intro + ;; only. Go on with the syntactic analysis + ;; otherwise. in-macro-expr is set if we're in a + ;; cpp expression, i.e. before the #define body + ;; or anywhere in a non-#define directive. + (if (c-forward-to-cpp-define-body) + (let ((indent-boi (c-point 'boi indent-point))) + (setq in-macro-expr (> (point) indent-boi) + tmpsymbol 'cpp-define-intro) + (= (point) indent-boi)) + (setq in-macro-expr t) + nil))))) + (c-add-syntax tmpsymbol macro-start) + (setq macro-start nil)) + ;; CASE 11: an else clause? + ((looking-at "else\\>[^_]") + (c-beginning-of-statement-1 containing-sexp) + (c-add-stmt-syntax 'else-clause t containing-sexp paren-state)) + ;; CASE 12: while closure of a do/while construct? + ((and (looking-at "while\\>[^_]") + (save-excursion + (prog1 (eq (c-beginning-of-statement-1 containing-sexp) + 'beginning) + (setq placeholder (point))))) + (goto-char placeholder) + (c-add-stmt-syntax 'do-while-closure t containing-sexp paren-state)) + ;; CASE 13: A catch or finally clause? This case is simpler + ;; than if-else and do-while, because a block is required + ;; after every try, catch and finally. + ((save-excursion + (and (cond ((c-major-mode-is 'c++-mode) + (looking-at "catch\\>[^_]")) + ((c-major-mode-is 'java-mode) + (looking-at "\\(catch\\|finally\\)\\>[^_]"))) + (and (c-safe (c-backward-syntactic-ws) + (c-backward-sexp) + t) + (eq (char-after) ?{) + (c-safe (c-backward-syntactic-ws) + (c-backward-sexp) + t) + (if (eq (char-after) ?\() + (c-safe (c-backward-sexp) t) + t)) + (looking-at "\\(try\\|catch\\)\\>[^_]") + (setq placeholder (point)))) + (goto-char placeholder) + (c-add-stmt-syntax 'catch-clause t containing-sexp paren-state)) + ;; CASE 18: A substatement we can recognize by keyword. + ((save-excursion + (and c-opt-block-stmt-key + (not (eq char-before-ip ?\;)) + (not (memq char-after-ip '(?\) ?\] ?,))) + (or (not (eq char-before-ip ?})) + (c-looking-at-inexpr-block-backward c-state-cache)) + (> (point) + (progn + ;; Ought to cache the result from the + ;; c-beginning-of-statement-1 calls here. + (setq placeholder (point)) + (while (eq (setq step-type + (c-beginning-of-statement-1 lim)) + 'label)) + (if (eq step-type 'previous) + (goto-char placeholder) + (setq placeholder (point)) + (if (and (eq step-type 'same) + (not (looking-at c-opt-block-stmt-key))) + ;; Step up to the containing statement if we + ;; stayed in the same one. + (let (step) + (while (eq + (setq step + (c-beginning-of-statement-1 lim)) + 'label)) + (if (eq step 'up) + (setq placeholder (point)) + ;; There was no containing statement afterall. + (goto-char placeholder))))) + placeholder)) + (if (looking-at c-block-stmt-2-key) + ;; Require a parenthesis after these keywords. + ;; Necessary to catch e.g. synchronized in Java, + ;; which can be used both as statement and + ;; modifier. + (and (= (c-forward-token-1 1 nil) 0) + (eq (char-after) ?\()) + (looking-at c-opt-block-stmt-key)))) + (if (eq step-type 'up) + ;; CASE 18A: Simple substatement. + (progn + (goto-char placeholder) + (cond + ((eq char-after-ip ?{) + (c-add-stmt-syntax 'substatement-open nil + containing-sexp paren-state)) + ((save-excursion + (goto-char indent-point) + (back-to-indentation) + (looking-at c-label-key)) + (c-add-stmt-syntax 'substatement-label nil + containing-sexp paren-state)) + (t + (c-add-stmt-syntax 'substatement nil + containing-sexp paren-state)))) + ;; CASE 18B: Some other substatement. This is shared + ;; with case 10. + (c-guess-continued-construct indent-point + char-after-ip + placeholder + lim + paren-state))) + ;; CASE 4: In-expression statement. C.f. cases 7B, 16A and + ;; 17E. + ((and (or c-opt-inexpr-class-key + c-opt-inexpr-block-key + c-opt-lambda-key) + (setq placeholder (c-looking-at-inexpr-block + (c-safe-position containing-sexp paren-state) + containing-sexp))) + (setq tmpsymbol (assq (car placeholder) + '((inexpr-class . class-open) + (inexpr-statement . block-open)))) + (if tmpsymbol + ;; It's a statement block or an anonymous class. + (setq tmpsymbol (cdr tmpsymbol)) + ;; It's a Pike lambda. Check whether we are between the + ;; lambda keyword and the argument list or at the defun + ;; opener. + (setq tmpsymbol (if (eq char-after-ip ?{) + 'inline-open + 'lambda-intro-cont))) + (goto-char (cdr placeholder)) + (back-to-indentation) + (c-add-stmt-syntax tmpsymbol t + (c-most-enclosing-brace c-state-cache (point)) + (c-whack-state-after (point) paren-state)) + (unless (eq (point) (cdr placeholder)) + (c-add-syntax (car placeholder)))) + ;; CASE 5: Line is at top level. + ((null containing-sexp) + (cond + ;; CASE 5A: we are looking at a defun, brace list, class, + ;; or inline-inclass method opening brace + ((setq special-brace-list + (or (and c-special-brace-lists + (c-looking-at-special-brace-list)) + (eq char-after-ip ?{))) + (cond + ;; CASE 5A.1: extern language or namespace construct + ((save-excursion + (goto-char indent-point) + (skip-chars-forward " \t") + (and (c-safe (progn (c-backward-sexp 2) t)) + (looking-at c-other-decl-block-key) + (setq keyword (match-string 1) + placeholder (point)) + (or (and (string-equal keyword "namespace") + (setq tmpsymbol 'namespace-open)) + (and (string-equal keyword "extern") + (progn + (c-forward-sexp 1) + (c-forward-syntactic-ws) + (eq (char-after) ?\")) + (setq tmpsymbol 'extern-lang-open))) + )) + (goto-char placeholder) + (c-add-syntax tmpsymbol (c-point 'boi))) + ;; CASE 5A.2: we are looking at a class opening brace + ((save-excursion + (goto-char indent-point) + (skip-chars-forward " \t{") + (let ((decl (c-search-uplist-for-classkey (c-parse-state)))) + (and decl + (setq placeholder (aref decl 0))) + )) + (c-add-syntax 'class-open placeholder)) + ;; CASE 5A.3: brace list open + ((save-excursion + (c-beginning-of-decl-1 lim) + (if (looking-at "typedef\\>[^_]") + (progn (c-forward-sexp 1) + (c-forward-syntactic-ws indent-point))) + (setq placeholder (c-point 'boi)) + (or (consp special-brace-list) + (and (or (save-excursion + (goto-char indent-point) + (setq tmpsymbol nil) + (while (and (> (point) placeholder) + (= (c-backward-token-1 1 t) 0) + (/= (char-after) ?=)) + (if (and (not tmpsymbol) + (looking-at "new\\>[^_]")) + (setq tmpsymbol 'topmost-intro-cont))) + (eq (char-after) ?=)) + (looking-at "enum\\>[^_]")) + (save-excursion + (while (and (< (point) indent-point) + (= (c-forward-token-1 1 t) 0) + (not (memq (char-after) '(?\; ?\())))) + (not (memq (char-after) '(?\; ?\())) + )))) + (if (and (c-major-mode-is 'java-mode) + (eq tmpsymbol 'topmost-intro-cont)) + ;; We're in Java and have found that the open brace + ;; belongs to a "new Foo[]" initialization list, + ;; which means the brace list is part of an + ;; expression and not a top level definition. We + ;; therefore treat it as any topmost continuation + ;; even though the semantically correct symbol still + ;; is brace-list-open, on the same grounds as in + ;; case 10B.2. + (progn + (c-beginning-of-statement-1 lim) + (c-add-syntax 'topmost-intro-cont (c-point 'boi))) + (c-add-syntax 'brace-list-open placeholder))) + ;; CASE 5A.4: inline defun open + ((and inclass-p (not inenclosing-p)) + (c-add-syntax 'inline-open) + (c-add-class-syntax 'inclass inclass-p paren-state)) + ;; CASE 5A.5: ordinary defun open + (t + (goto-char placeholder) + (if (or inclass-p macro-start) + (c-add-syntax 'defun-open (c-point 'boi)) + ;; Bogus to use bol here, but it's the legacy. + (c-add-syntax 'defun-open (c-point 'bol))) + ))) + ;; CASE 5B: first K&R arg decl or member init + ((c-just-after-func-arglist-p nil lim) + (cond + ;; CASE 5B.1: a member init + ((or (eq char-before-ip ?:) + (eq char-after-ip ?:)) + ;; this line should be indented relative to the beginning + ;; of indentation for the topmost-intro line that contains + ;; the prototype's open paren + ;; TBD: is the following redundant? + (if (eq char-before-ip ?:) + (forward-char -1)) + (c-backward-syntactic-ws lim) + ;; TBD: is the preceding redundant? + (if (eq (char-before) ?:) + (progn (forward-char -1) + (c-backward-syntactic-ws lim))) + (if (eq (char-before) ?\)) + (c-backward-sexp 1)) + (setq placeholder (point)) + (save-excursion + (and (c-safe (c-backward-sexp 1) t) + (looking-at "throw[^_]") + (c-safe (c-backward-sexp 1) t) + (setq placeholder (point)))) + (goto-char placeholder) + (c-add-syntax 'member-init-intro (c-point 'boi)) + ;; we don't need to add any class offset since this + ;; should be relative to the ctor's indentation + ) + ;; CASE 5B.2: K&R arg decl intro + (c-recognize-knr-p + (c-beginning-of-statement-1 lim) + (c-add-syntax 'knr-argdecl-intro (c-point 'boi)) + (if inclass-p + (c-add-class-syntax 'inclass inclass-p paren-state))) + ;; CASE 5B.3: Inside a member init list. + ((c-beginning-of-member-init-list lim) + (c-forward-syntactic-ws) + (c-add-syntax 'member-init-cont (point))) + ;; CASE 5B.4: Nether region after a C++ or Java func + ;; decl, which could include a `throws' declaration. + (t + (c-beginning-of-statement-1 lim) + (c-add-syntax 'func-decl-cont (c-point 'boi)) + ))) + ;; CASE 5C: inheritance line. could be first inheritance + ;; line, or continuation of a multiple inheritance + ((or (and (c-major-mode-is 'c++-mode) + (progn + (when (eq char-after-ip ?,) + (skip-chars-forward " \t") + (forward-char)) + (looking-at c-opt-decl-spec-key))) + (and (or (eq char-before-ip ?:) + ;; watch out for scope operator + (save-excursion + (and (eq char-after-ip ?:) + (c-safe (progn (forward-char 1) t)) + (not (eq (char-after) ?:)) + ))) + (save-excursion + (c-backward-syntactic-ws lim) + (if (eq char-before-ip ?:) + (progn + (forward-char -1) + (c-backward-syntactic-ws lim))) + (back-to-indentation) + (looking-at c-class-key))) + ;; for Java + (and (c-major-mode-is 'java-mode) + (let ((fence (save-excursion + (c-beginning-of-statement-1 lim) + (point))) + cont done) + (save-excursion + (while (not done) + (cond ((looking-at c-opt-decl-spec-key) + (setq injava-inher (cons cont (point)) + done t)) + ((or (not (c-safe (c-forward-sexp -1) t)) + (<= (point) fence)) + (setq done t)) + ) + (setq cont t))) + injava-inher) + (not (c-crosses-statement-barrier-p (cdr injava-inher) + (point))) + )) + (cond + ;; CASE 5C.1: non-hanging colon on an inher intro + ((eq char-after-ip ?:) + (c-beginning-of-statement-1 lim) + (c-add-syntax 'inher-intro (c-point 'boi)) + ;; don't add inclass symbol since relative point already + ;; contains any class offset + ) + ;; CASE 5C.2: hanging colon on an inher intro + ((eq char-before-ip ?:) + (c-beginning-of-statement-1 lim) + (c-add-syntax 'inher-intro (c-point 'boi)) + (if inclass-p + (c-add-class-syntax 'inclass inclass-p paren-state))) + ;; KDE Hack Start: + ((and inclass-p + c-access-key + (looking-at c-access-key)) + (c-add-syntax 'access-label (c-point 'bonl)) + (setq placeholder (c-add-class-syntax 'inclass inclass-p + paren-state))) + ;;(nconc syntax (list (cons 'access-label placeholder)))) + ;; KDE Hack End. + ;; CASE 5C.3: in a Java implements/extends + (injava-inher + (let ((where (cdr injava-inher)) + (cont (car injava-inher))) + (goto-char where) + (cond ((looking-at "throws\\>[^_]") + (c-add-syntax 'func-decl-cont + (progn (c-beginning-of-statement-1 lim) + (c-point 'boi)))) + (cont (c-add-syntax 'inher-cont where)) + (t (c-add-syntax 'inher-intro + (progn (goto-char (cdr injava-inher)) + (c-beginning-of-statement-1 lim) + (point)))) + ))) + ;; CASE 5C.4: a continued inheritance line + (t + (c-beginning-of-inheritance-list lim) + (c-add-syntax 'inher-cont (point)) + ;; don't add inclass symbol since relative point already + ;; contains any class offset + ))) + ;; CASE 5D: this could be a top-level initialization, a + ;; member init list continuation, or a template argument + ;; list continuation. + ((c-with-syntax-table (if (c-major-mode-is 'c++-mode) + c++-template-syntax-table + (syntax-table)) + (save-excursion + ;; Note: We use the fact that lim is always after any + ;; preceding brace sexp. + (while (and (= (c-backward-token-1 1 t lim) 0) + (not (looking-at "[;<,=]")))) + (or (memq (char-after) '(?, ?=)) + (and (c-major-mode-is 'c++-mode) + (= (c-backward-token-1 1 nil lim) 0) + (eq (char-after) ?<))))) + (goto-char indent-point) + (c-beginning-of-member-init-list lim) + (cond + ;; CASE 5D.1: hanging member init colon, but watch out + ;; for bogus matches on access specifiers inside classes. + ((and (save-excursion + (setq placeholder (point)) + (c-backward-token-1 1 t lim) + (and (eq (char-after) ?:) + (not (eq (char-before) ?:)))) + (save-excursion + (goto-char placeholder) + (back-to-indentation) + (or + (/= (car (save-excursion + (parse-partial-sexp (point) placeholder))) + 0) + (and + (if c-opt-access-key + (not (looking-at c-opt-access-key)) t) + (not (looking-at c-class-key)) + (if c-opt-bitfield-key + (not (looking-at c-opt-bitfield-key)) t)) + ))) + (goto-char placeholder) + (c-forward-syntactic-ws) + (c-add-syntax 'member-init-cont (point)) + ;; we do not need to add class offset since relative + ;; point is the member init above us + ) + ;; CASE 5D.2: non-hanging member init colon + ((progn + (c-forward-syntactic-ws indent-point) + (eq (char-after) ?:)) + (skip-chars-forward " \t:") + (c-add-syntax 'member-init-cont (point))) + ;; CASE 5D.3: perhaps a template list continuation? + ((and (c-major-mode-is 'c++-mode) + (save-excursion + (save-restriction + (c-with-syntax-table c++-template-syntax-table + (goto-char indent-point) + (setq placeholder (c-up-list-backward (point))) + (and placeholder + (eq (char-after placeholder) ?<)))))) + ;; we can probably indent it just like an arglist-cont + (goto-char placeholder) + (c-beginning-of-statement-1 lim t) + (c-add-syntax 'template-args-cont (c-point 'boi))) + ;; CASE 5D.4: perhaps a multiple inheritance line? + ((and (c-major-mode-is 'c++-mode) + (save-excursion + (c-beginning-of-statement-1 lim) + (setq placeholder (point)) + (if (looking-at "static\\>[^_]") + (c-forward-token-1 1 nil indent-point)) + (and (looking-at c-class-key) + (= (c-forward-token-1 2 nil indent-point) 0) + (if (eq (char-after) ?<) + (c-with-syntax-table c++-template-syntax-table + (= (c-forward-token-1 1 t indent-point) 0)) + t) + (eq (char-after) ?:)))) + (goto-char placeholder) + (c-add-syntax 'inher-cont (c-point 'boi))) + ;; CASE 5D.5: Continuation of the "expression part" of a + ;; top level construct. + (t + (while (and (eq (car (c-beginning-of-decl-1 containing-sexp)) + 'same) + (save-excursion + (c-backward-syntactic-ws) + (eq (char-before) ?})))) + (c-add-stmt-syntax + (if (eq char-before-ip ?,) + ;; A preceding comma at the top level means that a + ;; new variable declaration starts here. Use + ;; topmost-intro-cont for it, for consistency with + ;; the first variable declaration. C.f. case 5N. + 'topmost-intro-cont + 'statement-cont) + nil containing-sexp paren-state)) + )) + ;; CASE 5E: we are looking at a access specifier + ((and inclass-p + c-opt-access-key + (looking-at c-opt-access-key)) + (setq placeholder (c-add-class-syntax 'inclass inclass-p + paren-state)) + ;; Append access-label with the same anchor point as inclass gets. + (nconc syntax (list (cons 'access-label placeholder)))) + ;; CASE 5F: extern-lang-close or namespace-close? + ((and inenclosing-p + (eq char-after-ip ?})) + (setq tmpsymbol (if (eq inenclosing-p 'extern) + 'extern-lang-close + 'namespace-close)) + (c-add-syntax tmpsymbol (aref inclass-p 0))) + ;; CASE 5G: we are looking at the brace which closes the + ;; enclosing nested class decl + ((and inclass-p + (eq char-after-ip ?}) + (save-excursion + (save-restriction + (widen) + (forward-char 1) + (and (c-safe (progn (c-backward-sexp 1) t)) + (= (point) (aref inclass-p 1)) + )))) + (c-add-class-syntax 'class-close inclass-p paren-state)) + ;; CASE 5H: we could be looking at subsequent knr-argdecls + ((and c-recognize-knr-p + (not (eq char-before-ip ?})) + (save-excursion + (setq placeholder (cdr (c-beginning-of-decl-1 lim))) + (and placeholder + ;; Do an extra check to avoid tripping up on + ;; statements that occur in invalid contexts + ;; (e.g. in macro bodies where we don't really + ;; know the context of what we're looking at). + (not (and c-opt-block-stmt-key + (looking-at c-opt-block-stmt-key))))) + (< placeholder indent-point)) + (goto-char placeholder) + (c-add-syntax 'knr-argdecl (point))) + ;; CASE 5I: ObjC method definition. + ((and c-opt-method-key + (looking-at c-opt-method-key)) + (c-beginning-of-statement-1 lim) + (c-add-syntax 'objc-method-intro (c-point 'boi))) + ;; CASE 5N: At a variable declaration that follows a class + ;; definition or some other block declaration that doesn't + ;; end at the closing '}'. C.f. case 5D.5. + ((progn + (c-backward-syntactic-ws lim) + (and (eq (char-before) ?}) + (save-excursion + (let ((start (point))) + (if paren-state + ;; Speed up the backward search a bit. + (goto-char (car (car paren-state)))) + (c-beginning-of-decl-1 containing-sexp) + (setq placeholder (point)) + (if (= start (point)) + ;; The '}' is unbalanced. + nil + (c-end-of-decl-1) + (> (point) indent-point)))))) + (goto-char placeholder) + (c-add-stmt-syntax 'topmost-intro-cont nil + containing-sexp paren-state)) + ;; CASE 5J: we are at the topmost level, make + ;; sure we skip back past any access specifiers + ((progn + (while (and inclass-p + c-opt-access-key + (not (bobp)) + (save-excursion + (c-safe (progn (c-backward-sexp 1) t)) + (and (looking-at "slots:") + (c-backward-sexp 1)) + (looking-at c-opt-access-key))) + (c-backward-sexp 1) + (c-backward-syntactic-ws lim)) + (or (bobp) + (memq (char-before) '(?\; ?})) + (and (c-major-mode-is 'objc-mode) + (progn + (c-beginning-of-statement-1 lim) + (eq (char-after) ?@))))) + ;; real beginning-of-line could be narrowed out due to + ;; enclosure in a class block + (save-restriction + (widen) + (c-add-syntax 'topmost-intro (c-point 'bol)) + ;; Using bol instead of boi above is highly bogus, and + ;; it makes our lives hard to remain compatible. :P + (if inclass-p + (progn + (goto-char (aref inclass-p 1)) + (or (= (point) (c-point 'boi)) + (goto-char (aref inclass-p 0))) + (cond + ((eq inenclosing-p 'extern) + (c-add-syntax 'inextern-lang (c-point 'boi))) + ((eq inenclosing-p 'namespace) + (c-add-syntax 'innamespace (c-point 'boi))) + (t (c-add-class-syntax 'inclass inclass-p paren-state))) + )) + (when (and c-syntactic-indentation-in-macros + macro-start + (/= macro-start (c-point 'boi indent-point))) + (c-add-syntax 'cpp-define-intro) + (setq macro-start nil)) + )) + ;; CASE 5K: we are at an ObjC method definition + ;; continuation line. + ((and c-opt-method-key + (progn + (c-beginning-of-statement-1 lim) + (beginning-of-line) + (looking-at c-opt-method-key))) + (c-add-syntax 'objc-method-args-cont (point))) + ;; CASE 5L: we are at the first argument of a template + ;; arglist that begins on the previous line. + ((eq (char-before) ?<) + (c-beginning-of-statement-1 (c-safe-position (point) paren-state)) + (c-add-syntax 'template-args-cont (c-point 'boi))) + ;; CASE 5M: we are at a topmost continuation line + ;; KDE Hack 2 + ;; NOTE: is there a way to detect these sooner ? + (t + (c-beginning-of-statement-1 (c-safe-position (point) paren-state)) + (if (re-search-forward c-access-key (point-at-eol) t) + (progn + (c-add-syntax 'topmost-intro (c-point 'bol)) + (c-add-class-syntax 'inclass inclass-p paren-state) + ) + (progn + (c-add-syntax 'topmost-intro-cont (c-point 'boi)) + )) + ) + )) + ;; (CASE 6 has been removed.) + ;; CASE 7: line is an expression, not a statement. Most + ;; likely we are either in a function prototype or a function + ;; call argument list + ((not (or (and c-special-brace-lists + (save-excursion + (goto-char containing-sexp) + (c-looking-at-special-brace-list))) + (eq (char-after containing-sexp) ?{))) + (cond + ;; CASE 7A: we are looking at the arglist closing paren + ((memq char-after-ip '(?\) ?\])) + (goto-char containing-sexp) + (setq placeholder (c-point 'boi)) + (when (and (c-safe (backward-up-list 1) t) + (> (point) placeholder)) + (forward-char) + (skip-chars-forward " \t") + (setq placeholder (point))) + (c-add-syntax 'arglist-close placeholder)) + ;; CASE 7B: Looking at the opening brace of an + ;; in-expression block or brace list. C.f. cases 4, 16A + ;; and 17E. + ((and (eq char-after-ip ?{) + (progn + (setq placeholder (c-inside-bracelist-p (point) + c-state-cache)) + (if placeholder + (setq tmpsymbol '(brace-list-open . inexpr-class)) + (setq tmpsymbol '(block-open . inexpr-statement) + placeholder + (cdr-safe (c-looking-at-inexpr-block + (c-safe-position containing-sexp + paren-state) + containing-sexp))) + ;; placeholder is nil if it's a block directly in + ;; a function arglist. That makes us skip out of + ;; this case. + ))) + (goto-char placeholder) + (back-to-indentation) + (c-add-stmt-syntax (car tmpsymbol) t + (c-most-enclosing-brace paren-state (point)) + (c-whack-state-after (point) paren-state)) + (if (/= (point) placeholder) + (c-add-syntax (cdr tmpsymbol)))) + ;; CASE 7C: we are looking at the first argument in an empty + ;; argument list. Use arglist-close if we're actually + ;; looking at a close paren or bracket. + ((memq char-before-ip '(?\( ?\[)) + (goto-char containing-sexp) + (setq placeholder (c-point 'boi)) + (when (and (c-safe (backward-up-list 1) t) + (> (point) placeholder)) + (forward-char) + (skip-chars-forward " \t") + (setq placeholder (point))) + (c-add-syntax 'arglist-intro placeholder)) + ;; CASE 7D: we are inside a conditional test clause. treat + ;; these things as statements + ((progn + (goto-char containing-sexp) + (and (c-safe (progn (c-forward-sexp -1) t)) + (looking-at "\\[^_]"))) + (goto-char (1+ containing-sexp)) + (c-forward-syntactic-ws indent-point) + (if (eq char-before-ip ?\;) + (c-add-syntax 'statement (point)) + (c-add-syntax 'statement-cont (point)) + )) + ;; CASE 7E: maybe a continued ObjC method call. This is the + ;; case when we are inside a [] bracketed exp, and what + ;; precede the opening bracket is not an identifier. + ((and c-opt-method-key + (eq (char-after containing-sexp) ?\[) + (progn + (goto-char (1- containing-sexp)) + (c-backward-syntactic-ws (c-point 'bod)) + (if (not (looking-at c-symbol-key)) + (c-add-syntax 'objc-method-call-cont containing-sexp)) + ))) + ;; CASE 7F: we are looking at an arglist continuation line, + ;; but the preceding argument is on the same line as the + ;; opening paren. This case includes multi-line + ;; mathematical paren groupings, but we could be on a + ;; for-list continuation line + ((progn + (goto-char (1+ containing-sexp)) + (skip-chars-forward " \t") + (and (not (eolp)) + (not (looking-at "\\\\$")))) + (goto-char containing-sexp) + (setq placeholder (c-point 'boi)) + (when (and (c-safe (backward-up-list 1) t) + (> (point) placeholder)) + (forward-char) + (skip-chars-forward " \t") + (setq placeholder (point))) + (c-add-syntax 'arglist-cont-nonempty placeholder)) + ;; CASE 7G: we are looking at just a normal arglist + ;; continuation line + (t (c-forward-syntactic-ws indent-point) + (c-add-syntax 'arglist-cont (c-point 'boi))) + )) + ;; CASE 8: func-local multi-inheritance line + ((and (c-major-mode-is 'c++-mode) + (save-excursion + (goto-char indent-point) + (skip-chars-forward " \t") + (looking-at c-opt-decl-spec-key))) + (goto-char indent-point) + (skip-chars-forward " \t") + (cond + ;; CASE 8A: non-hanging colon on an inher intro + ((eq char-after-ip ?:) + (c-backward-syntactic-ws lim) + (c-add-syntax 'inher-intro (c-point 'boi))) + ;; CASE 8B: hanging colon on an inher intro + ((eq char-before-ip ?:) + (c-add-syntax 'inher-intro (c-point 'boi))) + ;; CASE 8C: a continued inheritance line + (t + (c-beginning-of-inheritance-list lim) + (c-add-syntax 'inher-cont (point)) + ))) + ;; CASE 9: we are inside a brace-list + ((setq special-brace-list + (or (and c-special-brace-lists + (save-excursion + (goto-char containing-sexp) + (c-looking-at-special-brace-list))) + (c-inside-bracelist-p containing-sexp paren-state))) + (cond + ;; CASE 9A: In the middle of a special brace list opener. + ((and (consp special-brace-list) + (save-excursion + (goto-char containing-sexp) + (eq (char-after) ?\()) + (eq char-after-ip (car (cdr special-brace-list)))) + (goto-char (car (car special-brace-list))) + (skip-chars-backward " \t") + (if (and (bolp) + (assoc 'statement-cont + (setq placeholder (c-guess-basic-syntax)))) + (setq syntax placeholder) + (c-beginning-of-statement-1 + (c-safe-position (1- containing-sexp) paren-state)) + (c-forward-token-1 0) + (if (looking-at "typedef\\>[^_]") (c-forward-token-1 1)) + (c-add-syntax 'brace-list-open (c-point 'boi)))) + ;; CASE 9B: brace-list-close brace + ((if (consp special-brace-list) + ;; Check special brace list closer. + (progn + (goto-char (car (car special-brace-list))) + (save-excursion + (goto-char indent-point) + (back-to-indentation) + (or + ;; We were between the special close char and the `)'. + (and (eq (char-after) ?\)) + (eq (1+ (point)) (cdr (car special-brace-list)))) + ;; We were before the special close char. + (and (eq (char-after) (cdr (cdr special-brace-list))) + (= (c-forward-token-1) 0) + (eq (1+ (point)) (cdr (car special-brace-list))))))) + ;; Normal brace list check. + (and (eq char-after-ip ?}) + (c-safe (progn (goto-char (c-up-list-backward (point))) + t)) + (= (point) containing-sexp))) + (if (eq (point) (c-point 'boi)) + (c-add-syntax 'brace-list-close (point)) + (setq lim (c-most-enclosing-brace c-state-cache (point))) + (c-beginning-of-statement-1 lim) + (c-add-stmt-syntax 'brace-list-close t lim + (c-whack-state-after (point) paren-state) + t))) + (t + ;; Prepare for the rest of the cases below by going to the + ;; token following the opening brace + (if (consp special-brace-list) + (progn + (goto-char (car (car special-brace-list))) + (c-forward-token-1 1 nil indent-point)) + (goto-char containing-sexp)) + (forward-char) + (let ((start (point))) + (c-forward-syntactic-ws indent-point) + (goto-char (max start (c-point 'bol)))) + (c-skip-ws-forward indent-point) + (cond + ;; CASE 9C: we're looking at the first line in a brace-list + ((= (point) indent-point) + (if (consp special-brace-list) + (goto-char (car (car special-brace-list))) + (goto-char containing-sexp)) + (if (eq (point) (c-point 'boi)) + (c-add-syntax 'brace-list-intro (point)) + (setq lim (c-most-enclosing-brace c-state-cache (point))) + (c-beginning-of-statement-1 lim) + (c-add-stmt-syntax 'brace-list-intro t lim + (c-whack-state-after (point) paren-state) + t))) + ;; CASE 9D: this is just a later brace-list-entry or + ;; brace-entry-open + (t (if (or (eq char-after-ip ?{) + (and c-special-brace-lists + (save-excursion + (goto-char indent-point) + (c-forward-syntactic-ws (c-point 'eol)) + (c-looking-at-special-brace-list (point))))) + (c-add-syntax 'brace-entry-open (point)) + (c-add-syntax 'brace-list-entry (point)) + )) + )))) + ;; CASE 10: A continued statement or top level construct. + ((and (not (memq char-before-ip '(?\; ?:))) + (or (not (eq char-before-ip ?})) + (c-looking-at-inexpr-block-backward c-state-cache)) + (> (point) + (save-excursion + (c-beginning-of-statement-1 containing-sexp) + (setq placeholder (point)))) + (/= placeholder containing-sexp)) + ;; This is shared with case 18. + (c-guess-continued-construct indent-point + char-after-ip + placeholder + containing-sexp + paren-state)) + ;; CASE 14: A case or default label + ((looking-at c-label-kwds-regexp) + (goto-char containing-sexp) + (setq lim (c-most-enclosing-brace c-state-cache containing-sexp)) + (c-backward-to-block-anchor lim) + (c-add-stmt-syntax 'case-label t lim paren-state)) + ;; CASE 15: any other label + ((looking-at c-label-key) + (goto-char containing-sexp) + (setq lim (c-most-enclosing-brace c-state-cache containing-sexp)) + (save-excursion + (setq tmpsymbol + (if (and (eq (c-beginning-of-statement-1 lim) 'up) + (looking-at "switch\\>[^_]")) + ;; If the surrounding statement is a switch then + ;; let's analyze all labels as switch labels, so + ;; that they get lined up consistently. + 'case-label + 'label))) + (c-backward-to-block-anchor lim) + (c-add-stmt-syntax tmpsymbol t lim paren-state)) + ;; CASE 16: block close brace, possibly closing the defun or + ;; the class + ((eq char-after-ip ?}) + ;; From here on we have the next containing sexp in lim. + (setq lim (c-most-enclosing-brace paren-state)) + (goto-char containing-sexp) + (cond + ;; CASE 16E: Closing a statement block? This catches + ;; cases where it's preceded by a statement keyword, + ;; which works even when used in an "invalid" context, + ;; e.g. a macro argument. + ((c-after-conditional) + (c-backward-to-block-anchor lim) + (c-add-stmt-syntax 'block-close t lim paren-state)) + ;; CASE 16A: closing a lambda defun or an in-expression + ;; block? C.f. cases 4, 7B and 17E. + ((setq placeholder (c-looking-at-inexpr-block + (c-safe-position containing-sexp paren-state) + nil)) + (setq tmpsymbol (if (eq (car placeholder) 'inlambda) + 'inline-close + 'block-close)) + (goto-char containing-sexp) + (back-to-indentation) + (if (= containing-sexp (point)) + (c-add-syntax tmpsymbol (point)) + (goto-char (cdr placeholder)) + (back-to-indentation) + (c-add-stmt-syntax tmpsymbol t + (c-most-enclosing-brace paren-state (point)) + (c-whack-state-after (point) paren-state)) + (if (/= (point) (cdr placeholder)) + (c-add-syntax (car placeholder))))) + ;; CASE 16B: does this close an inline or a function in + ;; an extern block or namespace? + ((setq placeholder (c-search-uplist-for-classkey paren-state)) + (c-backward-to-decl-anchor lim) + (back-to-indentation) + (if (save-excursion + (goto-char (aref placeholder 0)) + (looking-at c-other-decl-block-key)) + (c-add-syntax 'defun-close (point)) + (c-add-syntax 'inline-close (point)))) + ;; CASE 16F: Can be a defun-close of a function declared + ;; in a statement block, e.g. in Pike or when using gcc + ;; extensions. Might also trigger it with some macros + ;; followed by blocks, and this gives sane indentation + ;; then too. Let it through to be handled below. + ;; C.f. cases B.3 and 17G. + ((and (not inenclosing-p) + lim + (save-excursion + (and (not (c-looking-at-bos)) + (eq (c-beginning-of-statement-1 lim nil nil t) 'same) + (setq placeholder (point))))) + (back-to-indentation) + (if (/= (point) containing-sexp) + (goto-char placeholder)) + (c-add-stmt-syntax 'defun-close t lim paren-state)) + ;; CASE 16C: if there an enclosing brace that hasn't + ;; been narrowed out by a class, then this is a + ;; block-close. C.f. case 17H. + ((and (not inenclosing-p) lim) + ;; If the block is preceded by a case/switch label on + ;; the same line, we anchor at the first preceding label + ;; at boi. The default handling in c-add-stmt-syntax is + ;; really fixes it better, but we do like this to keep + ;; the indentation compatible with version 5.28 and + ;; earlier. + (while (and (/= (setq placeholder (point)) (c-point 'boi)) + (eq (c-beginning-of-statement-1 lim) 'label))) + (goto-char placeholder) + (if (looking-at c-label-kwds-regexp) + (c-add-syntax 'block-close (point)) + (goto-char containing-sexp) + ;; c-backward-to-block-anchor not necessary here; those + ;; situations are handled in case 16E above. + (c-add-stmt-syntax 'block-close t lim paren-state))) + ;; CASE 16D: find out whether we're closing a top-level + ;; class or a defun + (t + (save-restriction + (narrow-to-region (point-min) indent-point) + (let ((decl (c-search-uplist-for-classkey (c-parse-state)))) + (if decl + (c-add-class-syntax 'class-close decl paren-state) + (goto-char containing-sexp) + (c-backward-to-decl-anchor lim) + (back-to-indentation) + (c-add-syntax 'defun-close (point))))) + ))) + ;; CASE 17: Statement or defun catchall. + (t + (goto-char indent-point) + ;; Back up statements until we find one that starts at boi. + (while (let* ((prev-point (point)) + (last-step-type (c-beginning-of-statement-1 + containing-sexp))) + (if (= (point) prev-point) + (progn + (setq step-type (or step-type last-step-type)) + nil) + (setq step-type last-step-type) + (/= (point) (c-point 'boi))))) + (cond + ;; CASE 17B: continued statement + ((and (eq step-type 'same) + (/= (point) indent-point)) + (c-add-stmt-syntax 'statement-cont nil + containing-sexp paren-state)) + ;; CASE 17A: After a case/default label? + ((progn + (while (and (eq step-type 'label) + (not (looking-at c-label-kwds-regexp))) + (setq step-type + (c-beginning-of-statement-1 containing-sexp))) + (eq step-type 'label)) + (c-add-stmt-syntax (if (eq char-after-ip ?{) + 'statement-case-open + 'statement-case-intro) + t containing-sexp paren-state)) + ;; CASE 17D: any old statement + ((progn + (while (eq step-type 'label) + (setq step-type + (c-beginning-of-statement-1 containing-sexp))) + (eq step-type 'previous)) + (c-add-stmt-syntax 'statement t containing-sexp paren-state) + (if (eq char-after-ip ?{) + (c-add-syntax 'block-open))) + ;; CASE 17I: Inside a substatement block. + ((progn + ;; The following tests are all based on containing-sexp. + (goto-char containing-sexp) + ;; From here on we have the next containing sexp in lim. + (setq lim (c-most-enclosing-brace paren-state containing-sexp)) + (c-after-conditional)) + (c-backward-to-block-anchor lim) + (c-add-stmt-syntax 'statement-block-intro t lim paren-state) + (if (eq char-after-ip ?{) + (c-add-syntax 'block-open))) + ;; CASE 17E: first statement in an in-expression block. + ;; C.f. cases 4, 7B and 16A. + ((setq placeholder (c-looking-at-inexpr-block + (c-safe-position containing-sexp paren-state) + nil)) + (setq tmpsymbol (if (eq (car placeholder) 'inlambda) + 'defun-block-intro + 'statement-block-intro)) + (back-to-indentation) + (if (= containing-sexp (point)) + (c-add-syntax tmpsymbol (point)) + (goto-char (cdr placeholder)) + (back-to-indentation) + (c-add-stmt-syntax tmpsymbol t + (c-most-enclosing-brace c-state-cache (point)) + (c-whack-state-after (point) paren-state)) + (if (/= (point) (cdr placeholder)) + (c-add-syntax (car placeholder)))) + (if (eq char-after-ip ?{) + (c-add-syntax 'block-open))) + ;; CASE 17F: first statement in an inline, or first + ;; statement in a top-level defun. we can tell this is it + ;; if there are no enclosing braces that haven't been + ;; narrowed out by a class (i.e. don't use bod here). + ;; However, we first check for statements that we can + ;; recognize by keywords. That increases the robustness in + ;; cases where statements are used on the top level, + ;; e.g. in macro definitions. + ((save-excursion + (save-restriction + (widen) + (c-narrow-out-enclosing-class paren-state containing-sexp) + (not (c-most-enclosing-brace paren-state)))) + (c-backward-to-decl-anchor lim) + (back-to-indentation) + (c-add-syntax 'defun-block-intro (point))) + ;; CASE 17G: First statement in a function declared inside + ;; a normal block. This can occur in Pike and with + ;; e.g. the gcc extensions. Might also trigger it with + ;; some macros followed by blocks, and this gives sane + ;; indentation then too. C.f. cases B.3 and 16F. + ((save-excursion + (and (not (c-looking-at-bos)) + (eq (c-beginning-of-statement-1 lim nil nil t) 'same) + (setq placeholder (point)))) + (back-to-indentation) + (if (/= (point) containing-sexp) + (goto-char placeholder)) + (c-add-stmt-syntax 'defun-block-intro t lim paren-state)) + ;; CASE 17H: First statement in a block. C.f. case 16C. + (t + ;; If the block is preceded by a case/switch label on the + ;; same line, we anchor at the first preceding label at + ;; boi. The default handling in c-add-stmt-syntax is + ;; really fixes it better, but we do like this to keep the + ;; indentation compatible with version 5.28 and earlier. + (while (and (/= (setq placeholder (point)) (c-point 'boi)) + (eq (c-beginning-of-statement-1 lim) 'label))) + (goto-char placeholder) + (if (looking-at c-label-kwds-regexp) + (c-add-syntax 'statement-block-intro (point)) + (goto-char containing-sexp) + ;; c-backward-to-block-anchor not necessary here; those + ;; situations are handled in case 17I above. + (c-add-stmt-syntax 'statement-block-intro t lim paren-state)) + (if (eq char-after-ip ?{) + (c-add-syntax 'block-open))) + )) + ) + ;; now we need to look at any modifiers + (goto-char indent-point) + (skip-chars-forward " \t") + ;; are we looking at a comment only line? + (when (and (looking-at c-comment-start-regexp) + (/= (c-forward-token-1 0 nil (c-point 'eol)) 0)) + (c-add-syntax 'comment-intro)) + ;; we might want to give additional offset to friends (in C++). + (when (and c-opt-friend-key + (looking-at c-opt-friend-key)) + (c-add-syntax 'friend)) + ;; Start of or a continuation of a preprocessor directive? + (if (and macro-start + (eq macro-start (c-point 'boi)) + (not (and (c-major-mode-is 'pike-mode) + (eq (char-after (1+ macro-start)) ?\")))) + (c-add-syntax 'cpp-macro) + (when (and c-syntactic-indentation-in-macros macro-start) + (if in-macro-expr + (when (or (< syntactic-relpos macro-start) + (not (or (assq 'arglist-intro syntax) + (assq 'arglist-cont syntax) + (assq 'arglist-cont-nonempty syntax) + (assq 'arglist-close syntax)))) + ;; If inside a cpp expression, i.e. anywhere in a + ;; cpp directive except a #define body, we only let + ;; through the syntactic analysis that is internal + ;; in the expression. That means the arglist + ;; elements, if they are anchored inside the cpp + ;; expression. + (setq syntax `((cpp-macro-cont . ,macro-start)))) + (when (and (eq macro-start syntactic-relpos) + (not (assq 'cpp-define-intro syntax)) + (save-excursion + (goto-char macro-start) + (or (not (c-forward-to-cpp-define-body)) + (<= (point) (c-point 'boi indent-point))))) + ;; Inside a #define body and the syntactic analysis is + ;; anchored on the start of the #define. In this case + ;; we add cpp-define-intro to get the extra + ;; indentation of the #define body. + (c-add-syntax 'cpp-define-intro))))) + ;; return the syntax + syntax))))) + ((defun c-guess-basic-syntax () + (save-excursion + (save-restriction + (beginning-of-line) + (let* ((indent-point (point)) + (case-fold-search nil) + (fullstate (c-parse-state)) + (state fullstate) + literal containing-sexp char-before-ip char-after-ip lim + syntax placeholder c-in-literal-cache inswitch-p + tmpsymbol keyword injava-inher special-brace-list + ;; narrow out any enclosing class or extern "C" block + (inclass-p (c-narrow-out-enclosing-class state indent-point)) + inenclosing-p) + ;; check for meta top-level enclosing constructs, possible + ;; extern language definitions, possibly (in C++) namespace + ;; definitions. + (save-excursion + (save-restriction + (widen) + (if (and inclass-p + (progn + (goto-char (aref inclass-p 0)) + (looking-at (concat c-extra-toplevel-key "[^_]")))) + (let ((enclosing (match-string 1))) + (cond + ((string-equal enclosing "extern") + (setq inenclosing-p 'extern)) + ((string-equal enclosing "namespace") + (setq inenclosing-p 'namespace)) + ))))) + ;; get the buffer position of the most nested opening brace, + ;; if there is one, and it hasn't been narrowed out + (save-excursion + (goto-char indent-point) + (skip-chars-forward " \t}") + (skip-chars-backward " \t") + (while (and state + (not containing-sexp)) + (setq containing-sexp (car state) + state (cdr state)) + (if (consp containing-sexp) + ;; if cdr == point, then containing sexp is the brace + ;; that opens the sexp we close + (if (= (cdr containing-sexp) (point)) + (setq containing-sexp (car containing-sexp)) + ;; otherwise, ignore this element + (setq containing-sexp nil)) + ;; ignore the bufpos if its been narrowed out by the + ;; containing class or does not contain the indent point + (if (or (<= containing-sexp (point-min)) + (>= containing-sexp indent-point)) + (setq containing-sexp nil))))) + ;; (imenu "agulbra-c++-tab") + ;; set the limit on the farthest back we need to search + (setq lim (or containing-sexp + (if (consp (car fullstate)) + (cdr (car fullstate)) + nil) + (point-min))) + + ;; cache char before and after indent point, and move point to + ;; the most likely position to perform the majority of tests + (goto-char indent-point) + (skip-chars-forward " \t") + (setq char-after-ip (char-after)) + (c-backward-syntactic-ws lim) + (setq char-before-ip (char-before)) + (goto-char indent-point) + (skip-chars-forward " \t") + + ;; are we in a literal? + (setq literal (c-in-literal lim)) + + ;; now figure out syntactic qualities of the current line + (cond + ;; CASE 1: in a string. + ((memq literal '(string)) + (c-add-syntax 'string (c-point 'bopl))) + ;; CASE 2: in a C or C++ style comment. + ((memq literal '(c c++)) + (c-add-syntax literal (car (c-literal-limits lim)))) + ;; CASE 3: in a cpp preprocessor macro continuation. + ((and (eq literal 'pound) + (/= (save-excursion + (c-beginning-of-macro lim) + (setq placeholder (point))) + (c-point 'boi))) + (c-add-syntax 'cpp-macro-cont placeholder)) + ;; CASE 4: In-expression statement. + ((and (or c-inexpr-class-key c-inexpr-block-key c-lambda-key) + (setq placeholder (c-looking-at-inexpr-block))) + (setq tmpsymbol (assq (car placeholder) + '((inexpr-class . class-open) + (inexpr-statement . block-open)))) + (if tmpsymbol + ;; It's a statement block or an anonymous class. + (setq tmpsymbol (cdr tmpsymbol)) + ;; It's a Pike lambda. Check whether we are between the + ;; lambda keyword and the argument list or at the defun + ;; opener. + (setq tmpsymbol (if (eq char-after-ip ?{) + 'inline-open + 'lambda-intro-cont))) + (goto-char (cdr placeholder)) + (back-to-indentation) + (c-add-syntax tmpsymbol (point)) + (unless (eq (point) (cdr placeholder)) + (c-add-syntax (car placeholder)))) + ;; CASE 5: Line is at top level. + ((null containing-sexp) + (cond + ;; CASE 5A: we are looking at a defun, brace list, class, + ;; or inline-inclass method opening brace + ((setq special-brace-list + (or (and c-special-brace-lists + (c-looking-at-special-brace-list)) + (eq char-after-ip ?{))) + (cond + ;; CASE 5A.1: extern language or namespace construct + ((save-excursion + (goto-char indent-point) + (skip-chars-forward " \t") + (and (c-safe (progn (c-backward-sexp 2) t)) + (looking-at (concat c-extra-toplevel-key "[^_]")) + (setq keyword (match-string 1) + placeholder (point)) + (or (and (string-equal keyword "namespace") + (setq tmpsymbol 'namespace-open)) + (and (string-equal keyword "extern") + (progn + (c-forward-sexp 1) + (c-forward-syntactic-ws) + (eq (char-after) ?\")) + (setq tmpsymbol 'extern-lang-open))) + )) + (goto-char placeholder) + (c-add-syntax tmpsymbol (c-point 'boi))) + ;; CASE 5A.2: we are looking at a class opening brace + ((save-excursion + (goto-char indent-point) + (skip-chars-forward " \t{") + ;; TBD: watch out! there could be a bogus + ;; c-state-cache in place when we get here. we have + ;; to go through much chicanery to ignore the cache. + ;; But of course, there may not be! BLECH! BOGUS! + (let ((decl + (let ((c-state-cache nil)) + (c-search-uplist-for-classkey (c-parse-state)) + ))) + (and decl + (setq placeholder (aref decl 0))) + )) + (c-add-syntax 'class-open placeholder)) + ;; CASE 5A.3: brace list open + ((save-excursion + (c-beginning-of-statement-1 lim) + ;; c-b-o-s could have left us at point-min + (and (bobp) + (c-forward-syntactic-ws indent-point)) + (if (looking-at "typedef[^_]") + (progn (c-forward-sexp 1) + (c-forward-syntactic-ws indent-point))) + (setq placeholder (c-point 'boi)) + (or (consp special-brace-list) + (and (or (save-excursion + (goto-char indent-point) + (setq tmpsymbol nil) + (while (and (> (point) placeholder) + (= (c-backward-token-1 1 t) 0) + (/= (char-after) ?=)) + (if (and (not tmpsymbol) + (looking-at "new\\>[^_]")) + (setq tmpsymbol 'topmost-intro-cont))) + (eq (char-after) ?=)) + (looking-at "enum[ \t\n]+")) + (save-excursion + (while (and (< (point) indent-point) + (= (c-forward-token-1 1 t) 0) + (not (memq (char-after) '(?\; ?\())))) + (not (memq (char-after) '(?\; ?\())) + )))) + (if (and (c-major-mode-is 'java-mode) + (eq tmpsymbol 'topmost-intro-cont)) + ;; We're in Java and have found that the open brace + ;; belongs to a "new Foo[]" initialization list, + ;; which means the brace list is part of an + ;; expression and not a top level definition. We + ;; therefore treat it as any topmost continuation + ;; even though the semantically correct symbol still + ;; is brace-list-open, on the same grounds as in + ;; case 10B.2. + (progn + (c-beginning-of-statement-1 lim) + (c-forward-syntactic-ws) + (c-add-syntax 'topmost-intro-cont (c-point 'boi))) + (c-add-syntax 'brace-list-open placeholder))) + ;; CASE 5A.4: inline defun open + ((and inclass-p (not inenclosing-p)) + (c-add-syntax 'inline-open) + (c-add-class-syntax 'inclass inclass-p)) + ;; CASE 5A.5: ordinary defun open + (t + (goto-char placeholder) + (if inclass-p + (c-add-syntax 'defun-open (c-point 'boi)) + (c-add-syntax 'defun-open (c-point 'bol))) + ))) + ;; CASE 5B: first K&R arg decl or member init + ((c-just-after-func-arglist-p) + (cond + ;; CASE 5B.1: a member init + ((or (eq char-before-ip ?:) + (eq char-after-ip ?:)) + ;; this line should be indented relative to the beginning + ;; of indentation for the topmost-intro line that contains + ;; the prototype's open paren + ;; TBD: is the following redundant? + (if (eq char-before-ip ?:) + (forward-char -1)) + (c-backward-syntactic-ws lim) + ;; TBD: is the preceding redundant? + (if (eq (char-before) ?:) + (progn (forward-char -1) + (c-backward-syntactic-ws lim))) + (if (eq (char-before) ?\)) + (c-backward-sexp 1)) + (setq placeholder (point)) + (save-excursion + (and (c-safe (c-backward-sexp 1) t) + (looking-at "throw[^_]") + (c-safe (c-backward-sexp 1) t) + (setq placeholder (point)))) + (goto-char placeholder) + (c-add-syntax 'member-init-intro (c-point 'boi)) + ;; we don't need to add any class offset since this + ;; should be relative to the ctor's indentation + ) + ;; CASE 5B.2: K&R arg decl intro + (c-recognize-knr-p + (c-add-syntax 'knr-argdecl-intro (c-point 'boi)) + (if inclass-p (c-add-class-syntax 'inclass inclass-p))) + ;; CASE 5B.3: Inside a member init list. + ((c-beginning-of-member-init-list lim) + (c-forward-syntactic-ws) + (c-add-syntax 'member-init-cont (point))) + ;; CASE 5B.4: Nether region after a C++ or Java func + ;; decl, which could include a `throws' declaration. + (t + (c-beginning-of-statement-1 lim) + (c-add-syntax 'func-decl-cont (c-point 'boi)) + ))) + ;; CASE 5C: inheritance line. could be first inheritance + ;; line, or continuation of a multiple inheritance + ((or (and c-baseclass-key + (progn + (when (eq char-after-ip ?,) + (skip-chars-forward " \t") + (forward-char)) + (looking-at c-baseclass-key))) + (and (or (eq char-before-ip ?:) + ;; watch out for scope operator + (save-excursion + (and (eq char-after-ip ?:) + (c-safe (progn (forward-char 1) t)) + (not (eq (char-after) ?:)) + ))) + (save-excursion + (c-backward-syntactic-ws lim) + (if (eq char-before-ip ?:) + (progn + (forward-char -1) + (c-backward-syntactic-ws lim))) + (back-to-indentation) + (looking-at c-class-key))) + ;; for Java + (and (c-major-mode-is 'java-mode) + (let ((fence (save-excursion + (c-beginning-of-statement-1 lim) + (point))) + cont done) + (save-excursion + (while (not done) + (cond ((looking-at c-Java-special-key) + (setq injava-inher (cons cont (point)) + done t)) + ((or (not (c-safe (c-forward-sexp -1) t)) + (<= (point) fence)) + (setq done t)) + ) + (setq cont t))) + injava-inher) + (not (c-crosses-statement-barrier-p (cdr injava-inher) + (point))) + )) + (cond + ;; CASE 5C.1: non-hanging colon on an inher intro + ((eq char-after-ip ?:) + (c-backward-syntactic-ws lim) + (c-add-syntax 'inher-intro (c-point 'boi)) + ;; don't add inclass symbol since relative point already + ;; contains any class offset + ) + ;; CASE 5C.2: hanging colon on an inher intro + ((eq char-before-ip ?:) + (c-add-syntax 'inher-intro (c-point 'boi)) + (if inclass-p (c-add-class-syntax 'inclass inclass-p))) + ;; CASE agulbrahack.1: + ((and inclass-p + c-access-key + (looking-at c-access-key)) + (c-add-syntax 'access-label (c-point 'bonl)) + (c-add-class-syntax 'inclass inclass-p) + ) + ;; CASE 5C.3: in a Java implements/extends + (injava-inher + (let ((where (cdr injava-inher)) + (cont (car injava-inher))) + (goto-char where) + (cond ((looking-at "throws[ \t\n]") + (c-add-syntax 'func-decl-cont + (progn (c-beginning-of-statement-1 lim) + (c-point 'boi)))) + (cont (c-add-syntax 'inher-cont where)) + (t (c-add-syntax 'inher-intro + (progn (goto-char (cdr injava-inher)) + (c-beginning-of-statement-1 lim) + (point)))) + ))) + ;; CASE 5C.4: a continued inheritance line + (t + (c-beginning-of-inheritance-list lim) + (c-add-syntax 'inher-cont (point)) + ;; don't add inclass symbol since relative point already + ;; contains any class offset + ))) + ;; CASE 5D: this could be a top-level compound statement, a + ;; member init list continuation, or a template argument + ;; list continuation. + ((c-with-syntax-table (if (c-major-mode-is 'c++-mode) + c++-template-syntax-table + (syntax-table)) + (save-excursion + (while (and (= (c-backward-token-1 1 t lim) 0) + (not (looking-at "[;{<,]")))) + (eq (char-after) ?,))) + (goto-char indent-point) + (c-beginning-of-member-init-list lim) + (cond + ;; CASE 5D.1: hanging member init colon, but watch out + ;; for bogus matches on access specifiers inside classes. + ((and (save-excursion + (setq plaaceholder (point)) + (c-backward-token-1 1 t lim) + (and (eq (char-after) ?:) + (not (eq (char-before) ?:)))) + (save-excursion + (goto-char placeholder) + (back-to-indentation) + (or + (/= (car (save-excursion + (parse-partial-sexp (point) placeholder))) + 0) + (and + (if c-access-key (not (looking-at c-access-key)) t) + (not (looking-at c-class-key)) + (if c-bitfield-key (not (looking-at c-bitfield-key)) t)) + ))) + (goto-char placeholder) + (c-forward-syntactic-ws) + (c-add-syntax 'member-init-cont (point)) + ;; we do not need to add class offset since relative + ;; point is the member init above us + ) + ;; CASE 5D.2: non-hanging member init colon + ((progn + (c-forward-syntactic-ws indent-point) + (eq (char-after) ?:)) + (skip-chars-forward " \t:") + (c-add-syntax 'member-init-cont (point))) + ;; CASE 5D.3: perhaps a multiple inheritance line? + ((save-excursion + (c-beginning-of-statement-1 lim) + (setq placeholder (point)) + (looking-at c-inher-key)) + (goto-char placeholder) + (c-add-syntax 'inher-cont (c-point 'boi))) + ;; CASE 5D.4: perhaps a template list continuation? + ((save-excursion + (goto-char indent-point) + (skip-chars-backward "^<" lim) + ;; not sure if this is the right test, but it should + ;; be fast and mostly accurate. + (setq placeholder (point)) + (and (eq (char-before) ?<) + (not (c-in-literal lim)))) + ;; we can probably indent it just like an arglist-cont + (goto-char placeholder) + (c-beginning-of-statement-1 lim) + (c-add-syntax 'template-args-cont (c-point 'boi))) + ;; CASE 5D.5: perhaps a top-level statement-cont + (t + (c-beginning-of-statement-1 lim) + ;; skip over any access-specifiers + (and inclass-p c-access-key + (while (looking-at c-access-key) + (forward-line 1))) + ;; skip over comments, whitespace + (c-forward-syntactic-ws indent-point) + (c-add-syntax 'statement-cont (c-point 'boi))) + )) + ;; CASE 5E: we are looking at a access specifier + ((and inclass-p + c-access-key + (looking-at c-access-key)) + (c-add-syntax 'access-label (c-point 'bonl)) + (c-add-class-syntax 'inclass inclass-p)) + ;; CASE 5F: extern-lang-close or namespace-close? + ((and inenclosing-p + (eq char-after-ip ?})) + (setq tmpsymbol (if (eq inenclosing-p 'extern) + 'extern-lang-close + 'namespace-close)) + (c-add-syntax tmpsymbol (aref inclass-p 0))) + ;; CASE 5G: we are looking at the brace which closes the + ;; enclosing nested class decl + ((and inclass-p + (eq char-after-ip ?}) + (save-excursion + (save-restriction + (widen) + (forward-char 1) + (and (c-safe (progn (c-backward-sexp 1) t)) + (= (point) (aref inclass-p 1)) + )))) + (c-add-class-syntax 'class-close inclass-p)) + ;; CASE 5H: we could be looking at subsequent knr-argdecls + ((and c-recognize-knr-p + ;; here we essentially use the hack that is used in + ;; Emacs' c-mode.el to limit how far back we should + ;; look. The assumption is made that argdecls are + ;; indented at least one space and that function + ;; headers are not indented. + (let ((limit (save-excursion + (re-search-backward "^[^ \^L\t\n#]" nil 'move) + (point)))) + (save-excursion + (c-backward-syntactic-ws limit) + (setq placeholder (point)) + (while (and (memq (char-before) '(?\; ?,)) + (> (point) limit)) + (beginning-of-line) + (setq placeholder (point)) + (c-backward-syntactic-ws limit)) + (and (eq (char-before) ?\)) + (or (not c-method-key) + (progn + (c-forward-sexp -1) + (forward-char -1) + (c-backward-syntactic-ws) + (not (or (memq (char-before) '(?- ?+)) + ;; or a class category + (progn + (c-forward-sexp -2) + (looking-at c-class-key)) + ))))) + )) + (save-excursion + (c-beginning-of-statement-1) + (not (looking-at "typedef[ \t\n]+")))) + (goto-char placeholder) + (c-add-syntax 'knr-argdecl (c-point 'boi))) + ;; CASE 5I: ObjC method definition. + ((and c-method-key + (looking-at c-method-key)) + (c-add-syntax 'objc-method-intro (c-point 'boi))) + ;; CASE 5J: we are at the topmost level, make sure we skip + ;; back past any access specifiers + ((progn + (c-backward-syntactic-ws lim) + (while (and inclass-p + c-access-key + (not (bobp)) + (save-excursion + (c-safe (progn (c-backward-sexp 1) t)) + ;; agulbrahack 2 + (and (looking-at "slots:") + (c-backward-sexp 1)) + (looking-at c-access-key))) + (c-backward-sexp 1) + (c-backward-syntactic-ws lim)) + (or (bobp) + (memq (char-before) '(?\; ?\})))) + ;; real beginning-of-line could be narrowed out due to + ;; enclosure in a class block + (save-restriction + (widen) + (c-add-syntax 'topmost-intro (c-point 'bol)) + (if inclass-p + (progn + (goto-char (aref inclass-p 1)) + (or (= (point) (c-point 'boi)) + (goto-char (aref inclass-p 0))) + (cond + ((eq inenclosing-p 'extern) + (c-add-syntax 'inextern-lang (c-point 'boi))) + ((eq inenclosing-p 'namespace) + (c-add-syntax 'innamespace (c-point 'boi))) + (t (c-add-class-syntax 'inclass inclass-p))) + )) + )) + ;; CASE 5K: we are at an ObjC or Java method definition + ;; continuation line. + ((and c-method-key + (progn + (c-beginning-of-statement-1 lim) + (beginning-of-line) + (looking-at c-method-key))) + (c-add-syntax 'objc-method-args-cont (point))) + ;; CASE 5L: we are at the first argument of a template + ;; arglist that begins on the previous line. + ((eq (char-before) ?<) + (c-beginning-of-statement-1 lim) + (c-forward-syntactic-ws) + (c-add-syntax 'template-args-cont (c-point 'boi))) + ;; CASE 5M: we are at a topmost continuation line + (t + (c-beginning-of-statement-1 lim) + (c-forward-syntactic-ws) + (c-add-syntax 'topmost-intro-cont (c-point 'boi))) + )) ; end CASE 5 + ;; (CASE 6 has been removed.) + ;; CASE 7: line is an expression, not a statement. Most + ;; likely we are either in a function prototype or a function + ;; call argument list + ((not (or (and c-special-brace-lists + (save-excursion + (goto-char containing-sexp) + (c-looking-at-special-brace-list))) + (eq (char-after containing-sexp) ?{))) + (c-backward-syntactic-ws containing-sexp) + (cond + ;; CASE 7A: we are looking at the arglist closing paren + ((and (or (c-major-mode-is 'pike-mode) + ;; Don't check this in Pike since it allows a + ;; comma after the last arg. + (not (eq char-before-ip ?,))) + (memq char-after-ip '(?\) ?\]))) + (goto-char containing-sexp) + (setq placeholder (c-point 'boi)) + (when (and (c-safe (backward-up-list 1) t) + (> (point) placeholder)) + (forward-char) + (skip-chars-forward " \t") + (setq placeholder (point))) + (c-add-syntax 'arglist-close placeholder)) + ;; CASE 7B: Looking at the opening brace of an + ;; in-expression block or brace list. + ((eq char-after-ip ?{) + (goto-char indent-point) + (setq placeholder (c-point 'boi)) + (goto-char containing-sexp) + (if (c-inside-bracelist-p placeholder + (cons containing-sexp state)) + (progn + (c-add-syntax 'brace-list-open (c-point 'boi)) + (c-add-syntax 'inexpr-class)) + (c-add-syntax 'block-open (c-point 'boi)) + (c-add-syntax 'inexpr-statement))) + ;; CASE 7C: we are looking at the first argument in an empty + ;; argument list. Use arglist-close if we're actually + ;; looking at a close paren or bracket. + ((memq char-before-ip '(?\( ?\[)) + (goto-char containing-sexp) + (setq placeholder (c-point 'boi)) + (when (and (c-safe (backward-up-list 1) t) + (> (point) placeholder)) + (forward-char) + (skip-chars-forward " \t") + (setq placeholder (point))) + (c-add-syntax 'arglist-intro placeholder)) + ;; CASE 7D: we are inside a conditional test clause. treat + ;; these things as statements + ((save-excursion + (goto-char containing-sexp) + (and (c-safe (progn (c-forward-sexp -1) t)) + (looking-at "\\[^_]"))) + (goto-char (1+ containing-sexp)) + (c-forward-syntactic-ws indent-point) + (c-beginning-of-statement-1 containing-sexp) + (if (eq char-before-ip ?\;) + (c-add-syntax 'statement (point)) + (c-add-syntax 'statement-cont (point)) + )) + ;; CASE 7E: maybe a continued method call. This is the case + ;; when we are inside a [] bracketed exp, and what precede + ;; the opening bracket is not an identifier. + ((and c-method-key + (eq (char-after containing-sexp) ?\[) + (save-excursion + (goto-char (1- containing-sexp)) + (c-backward-syntactic-ws (c-point 'bod)) + (if (not (looking-at c-symbol-key)) + (c-add-syntax 'objc-method-call-cont containing-sexp)) + ))) + ;; CASE 7F: we are looking at an arglist continuation line, + ;; but the preceding argument is on the same line as the + ;; opening paren. This case includes multi-line + ;; mathematical paren groupings, but we could be on a + ;; for-list continuation line + ((save-excursion + (goto-char (1+ containing-sexp)) + (skip-chars-forward " \t") + (not (eolp))) + (goto-char containing-sexp) + (setq placeholder (c-point 'boi)) + (when (and (c-safe (backward-up-list 1) t) + (> (point) placeholder)) + (forward-char) + (skip-chars-forward " \t") + (setq placeholder (point))) + (c-add-syntax 'arglist-cont-nonempty placeholder)) + ;; CASE 7G: we are looking at just a normal arglist + ;; continuation line + (t (c-beginning-of-statement-1 containing-sexp) + (forward-char 1) + (c-forward-syntactic-ws indent-point) + (c-add-syntax 'arglist-cont (c-point 'boi))) + )) + ;; CASE 8: func-local multi-inheritance line + ((and c-baseclass-key + (save-excursion + (goto-char indent-point) + (skip-chars-forward " \t") + (looking-at c-baseclass-key))) + (goto-char indent-point) + (skip-chars-forward " \t") + (cond + ;; CASE 8A: non-hanging colon on an inher intro + ((eq char-after-ip ?:) + (c-backward-syntactic-ws lim) + (c-add-syntax 'inher-intro (c-point 'boi))) + ;; CASE 8B: hanging colon on an inher intro + ((eq char-before-ip ?:) + (c-add-syntax 'inher-intro (c-point 'boi))) + ;; CASE 8C: a continued inheritance line + (t + (c-beginning-of-inheritance-list lim) + (c-add-syntax 'inher-cont (point)) + ))) + ;; CASE 9: we are inside a brace-list + ((setq special-brace-list + (or (and c-special-brace-lists + (save-excursion + (goto-char containing-sexp) + (c-looking-at-special-brace-list))) + (c-inside-bracelist-p containing-sexp state))) + (cond + ;; CASE 9A: In the middle of a special brace list opener. + ((and (consp special-brace-list) + (save-excursion + (goto-char containing-sexp) + (eq (char-after) ?\()) + (eq char-after-ip (car (cdr special-brace-list)))) + (goto-char (car (car special-brace-list))) + (skip-chars-backward " \t") + (if (and (bolp) + (assoc 'statement-cont + (setq placeholder (c-guess-basic-syntax)))) + (setq syntax placeholder) + (c-beginning-of-statement-1 lim) + (c-forward-token-1 0) + (if (looking-at "typedef\\>") (c-forward-token-1 1)) + (c-add-syntax 'brace-list-open (c-point 'boi)))) + ;; CASE 9B: brace-list-close brace + ((if (consp special-brace-list) + ;; Check special brace list closer. + (progn + (goto-char (car (car special-brace-list))) + (save-excursion + (goto-char indent-point) + (back-to-indentation) + (or + ;; We were between the special close char and the `)'. + (and (eq (char-after) ?\)) + (eq (1+ (point)) (cdr (car special-brace-list)))) + ;; We were before the special close char. + (and (eq (char-after) (cdr (cdr special-brace-list))) + (= (c-forward-token-1) 0) + (eq (1+ (point)) (cdr (car special-brace-list))))))) + ;; Normal brace list check. + (and (eq char-after-ip ?}) + (c-safe (progn (forward-char 1) + (c-backward-sexp 1) + t)) + (= (point) containing-sexp))) + (c-add-syntax 'brace-list-close (c-point 'boi))) + (t + ;; Prepare for the rest of the cases below by going to the + ;; token following the opening brace + (if (consp special-brace-list) + (progn + (goto-char (car (car special-brace-list))) + (c-forward-token-1 1 nil indent-point)) + (goto-char containing-sexp)) + (forward-char) + (let ((start (point))) + (c-forward-syntactic-ws indent-point) + (goto-char (max start (c-point 'bol)))) + (skip-chars-forward " \t\n\r" indent-point) + (cond + ;; CASE 9C: we're looking at the first line in a brace-list + ((= (point) indent-point) + (goto-char containing-sexp) + (c-add-syntax 'brace-list-intro (c-point 'boi)) + ) ; end CASE 9C + ;; CASE 9D: this is just a later brace-list-entry or + ;; brace-entry-open + (t (if (or (eq char-after-ip ?{) + (and c-special-brace-lists + (save-excursion + (goto-char indent-point) + (c-forward-syntactic-ws (c-point 'eol)) + (c-looking-at-special-brace-list (point))))) + (c-add-syntax 'brace-entry-open (point)) + (c-add-syntax 'brace-list-entry (point)) + )) ; end CASE 9D + )))) ; end CASE 9 + ;; CASE 10: A continued statement + ((and (not (memq char-before-ip '(?\; ?:))) + (or (not (eq char-before-ip ?})) + (c-looking-at-inexpr-block-backward containing-sexp)) + (> (point) + (save-excursion + (c-beginning-of-statement-1 containing-sexp) + (c-forward-syntactic-ws) + (setq placeholder (point)))) + (/= placeholder containing-sexp)) + (goto-char indent-point) + (skip-chars-forward " \t") + (let ((after-cond-placeholder + (save-excursion + (goto-char placeholder) + (if (and c-conditional-key (looking-at c-conditional-key)) + (progn + (c-safe (c-skip-conditional)) + (c-forward-syntactic-ws) + (if (eq (char-after) ?\;) + (progn + (forward-char 1) + (c-forward-syntactic-ws))) + (point)) + nil)))) + (cond + ;; CASE 10A: substatement + ((and after-cond-placeholder + (>= after-cond-placeholder indent-point)) + (goto-char placeholder) + (if (eq char-after-ip ?{) + (c-add-syntax 'substatement-open (c-point 'boi)) + (c-add-syntax 'substatement (c-point 'boi)))) + ;; CASE 10B: open braces for class or brace-lists + ((setq special-brace-list + (or (and c-special-brace-lists + (c-looking-at-special-brace-list)) + (eq char-after-ip ?{))) + (cond + ;; CASE 10B.1: class-open + ((save-excursion + (goto-char indent-point) + (skip-chars-forward " \t{") + (let ((decl (c-search-uplist-for-classkey (c-parse-state)))) + (and decl + (setq placeholder (aref decl 0))) + )) + (c-add-syntax 'class-open placeholder)) + ;; CASE 10B.2: brace-list-open + ((or (consp special-brace-list) + (save-excursion + (goto-char placeholder) + (looking-at "\\")) + (save-excursion + (goto-char indent-point) + (while (and (> (point) placeholder) + (= (c-backward-token-1 1 t) 0) + (/= (char-after) ?=))) + (eq (char-after) ?=))) + ;; The most semantically accurate symbol here is + ;; brace-list-open, but we report it simply as a + ;; statement-cont. The reason is that one normally + ;; adjusts brace-list-open for brace lists as + ;; top-level constructs, and brace lists inside + ;; statements is a completely different context. + (goto-char indent-point) + (c-beginning-of-closest-statement) + (c-add-syntax 'statement-cont (c-point 'boi))) + ;; CASE 10B.3: The body of a function declared inside a + ;; normal block. This can only occur in Pike. + ((and (c-major-mode-is 'pike-mode) + (progn + (goto-char indent-point) + (not (c-looking-at-bos)))) + (c-beginning-of-closest-statement) + (c-add-syntax 'defun-open (c-point 'boi))) + ;; CASE 10B.4: catch-all for unknown construct. + (t + ;; Can and should I add an extensibility hook here? + ;; Something like c-recognize-hook so support for + ;; unknown constructs could be added. It's probably a + ;; losing proposition, so I dunno. + (goto-char placeholder) + (c-add-syntax 'statement-cont (c-point 'boi)) + (c-add-syntax 'block-open)) + )) + ;; CASE 10C: iostream insertion or extraction operator + ((looking-at "<<\\|>>") + (goto-char placeholder) + (and after-cond-placeholder + (goto-char after-cond-placeholder)) + (while (and (re-search-forward "<<\\|>>" indent-point 'move) + (c-in-literal placeholder))) + ;; if we ended up at indent-point, then the first + ;; streamop is on a separate line. Indent the line like + ;; a statement-cont instead + (if (/= (point) indent-point) + (c-add-syntax 'stream-op (c-point 'boi)) + (c-backward-syntactic-ws lim) + (c-add-syntax 'statement-cont (c-point 'boi)))) + ;; CASE 10D: continued statement. find the accurate + ;; beginning of statement or substatement + (t + (c-beginning-of-statement-1 after-cond-placeholder) + ;; KLUDGE ALERT! c-beginning-of-statement-1 can leave + ;; us before the lim we're passing in. It should be + ;; fixed, but I'm worried about side-effects at this + ;; late date. Fix for v5. + (goto-char (or (and after-cond-placeholder + (max after-cond-placeholder (point))) + (point))) + (c-add-syntax 'statement-cont (point))) + ))) + ;; CASE 11: an else clause? + ((looking-at "\\[^_]") + (c-backward-to-start-of-if containing-sexp) + (c-add-syntax 'else-clause (c-point 'boi))) + ;; CASE 12: Statement. But what kind? Lets see if its a + ;; while closure of a do/while construct + ((progn + (goto-char indent-point) + (skip-chars-forward " \t") + (and (looking-at "while\\b[^_]") + (save-excursion + (c-backward-to-start-of-do containing-sexp) + (setq placeholder (point)) + (looking-at "do\\b[^_]")) + )) + (goto-char placeholder) + (c-add-syntax 'do-while-closure (c-point 'boi))) + ;; CASE 13: A catch or finally clause? This case is simpler + ;; than if-else and do-while, because a block is required + ;; after every try, catch and finally. + ((save-excursion + (and (cond ((c-major-mode-is 'c++-mode) + (looking-at "\\[^_]")) + ((c-major-mode-is 'java-mode) + (looking-at "\\<\\(catch\\|finally\\)\\>[^_]"))) + (c-safe (c-backward-sexp) t) + (eq (char-after) ?{) + (c-safe (c-backward-sexp) t) + (if (eq (char-after) ?\() + (c-safe (c-backward-sexp) t) + t) + (looking-at "\\<\\(try\\|catch\\)\\>[^_]") + (setq placeholder (c-point 'boi)))) + (c-add-syntax 'catch-clause placeholder)) + ;; CASE 14: A case or default label + ((looking-at c-switch-label-key) + (goto-char containing-sexp) + ;; check for hanging braces + (if (/= (point) (c-point 'boi)) + (c-forward-sexp -1)) + (c-add-syntax 'case-label (c-point 'boi))) + ;; CASE 15: any other label + ((looking-at c-label-key) + (goto-char containing-sexp) + ;; check for hanging braces + (if (/= (point) (c-point 'boi)) + (c-forward-sexp -1)) + (c-add-syntax 'label (c-point 'boi))) + ;; CASE 16: block close brace, possibly closing the defun or + ;; the class + ((eq char-after-ip ?}) + (let* ((lim (c-safe-position containing-sexp fullstate)) + (relpos (save-excursion + (goto-char containing-sexp) + (if (/= (point) (c-point 'boi)) + (c-beginning-of-statement-1 lim)) + (c-point 'boi)))) + (cond + ;; CASE 16A: closing a lambda defun or an in-expression + ;; block? + ((save-excursion + (goto-char containing-sexp) + (setq placeholder (c-looking-at-inexpr-block))) + (setq tmpsymbol (if (eq (car placeholder) 'inlambda) + 'inline-close + 'block-close)) + (goto-char containing-sexp) + (back-to-indentation) + (if (= containing-sexp (point)) + (c-add-syntax tmpsymbol (point)) + (goto-char (cdr placeholder)) + (back-to-indentation) + (c-add-syntax tmpsymbol (point)) + (if (/= (point) (cdr placeholder)) + (c-add-syntax (car placeholder))))) + ;; CASE 16B: does this close an inline or a function in + ;; an extern block or namespace? + ((progn + (goto-char containing-sexp) + (setq placeholder (c-search-uplist-for-classkey state))) + (goto-char (aref placeholder 0)) + (if (looking-at (concat c-extra-toplevel-key "[^_]")) + (c-add-syntax 'defun-close relpos) + (c-add-syntax 'inline-close relpos))) + ;; CASE 16C: if there an enclosing brace that hasn't + ;; been narrowed out by a class, then this is a + ;; block-close + ((and (not inenclosing-p) + (c-most-enclosing-brace state) + (or (not (c-major-mode-is 'pike-mode)) + ;; In Pike it can be a defun-close of a + ;; function declared in a statement block. Let + ;; it through to be handled below. + (or (c-looking-at-bos) + (progn + (c-beginning-of-statement-1) + (looking-at c-conditional-key))))) + (c-add-syntax 'block-close relpos)) + ;; CASE 16D: find out whether we're closing a top-level + ;; class or a defun + (t + (save-restriction + (narrow-to-region (point-min) indent-point) + (let ((decl (c-search-uplist-for-classkey (c-parse-state)))) + (if decl + (c-add-class-syntax 'class-close decl) + (c-add-syntax 'defun-close relpos))))) + ))) + ;; CASE 17: statement catchall + (t + ;; we know its a statement, but we need to find out if it is + ;; the first statement in a block + (goto-char containing-sexp) + (forward-char 1) + (c-forward-syntactic-ws indent-point) + ;; now skip forward past any case/default clauses we might find. + (while (or (c-skip-case-statement-forward fullstate indent-point) + (and (looking-at c-switch-label-key) + (not inswitch-p))) + (setq inswitch-p t)) + ;; we want to ignore non-case labels when skipping forward + (while (and (looking-at c-label-key) + (goto-char (match-end 0))) + (c-forward-syntactic-ws indent-point)) + (cond + ;; CASE 17A: we are inside a case/default clause inside a + ;; switch statement. find out if we are at the statement + ;; just after the case/default label. + ((and inswitch-p + (progn + (goto-char indent-point) + (c-beginning-of-statement-1 containing-sexp) + (setq placeholder (point)) + (beginning-of-line) + (when (re-search-forward c-switch-label-key + (max placeholder (c-point 'eol)) t) + (setq placeholder (match-beginning 0))))) + (goto-char indent-point) + (skip-chars-forward " \t") + (if (eq (char-after) ?{) + (c-add-syntax 'statement-case-open placeholder) + (c-add-syntax 'statement-case-intro placeholder))) + ;; CASE 17B: continued statement + ((eq char-before-ip ?,) + (goto-char indent-point) + (c-beginning-of-closest-statement) + (c-add-syntax 'statement-cont (c-point 'boi))) + ;; CASE 17C: a question/colon construct? But make sure + ;; what came before was not a label, and what comes after + ;; is not a globally scoped function call! + ((or (and (memq char-before-ip '(?: ??)) + (save-excursion + (goto-char indent-point) + (c-backward-syntactic-ws lim) + (back-to-indentation) + (not (looking-at c-label-key)))) + (and (memq char-after-ip '(?: ??)) + (save-excursion + (goto-char indent-point) + (skip-chars-forward " \t") + ;; watch out for scope operator + (not (looking-at "::"))))) + (goto-char indent-point) + (c-beginning-of-closest-statement) + (c-add-syntax 'statement-cont (c-point 'boi))) + ;; CASE 17D: any old statement + ((< (point) indent-point) + (let ((safepos (c-most-enclosing-brace fullstate)) + relpos done) + (goto-char indent-point) + (c-beginning-of-statement-1 safepos) + ;; It is possible we're on the brace that opens a nested + ;; function. + (if (and (eq (char-after) ?{) + (save-excursion + (c-backward-syntactic-ws safepos) + (not (eq (char-before) ?\;)))) + (c-beginning-of-statement-1 safepos)) + (if (and inswitch-p + (looking-at c-switch-label-key)) + (progn + (goto-char (match-end 0)) + (c-forward-syntactic-ws))) + (setq relpos (c-point 'boi)) + (while (and (not done) + (<= safepos (point)) + (/= relpos (point))) + (c-beginning-of-statement-1 safepos) + (if (= relpos (c-point 'boi)) + (setq done t)) + (setq relpos (c-point 'boi))) + (c-add-syntax 'statement relpos) + (if (eq char-after-ip ?{) + (c-add-syntax 'block-open)))) + ;; CASE 17E: first statement in an in-expression block + ((setq placeholder + (save-excursion + (goto-char containing-sexp) + (c-looking-at-inexpr-block))) + (goto-char containing-sexp) + (back-to-indentation) + (let ((block-intro (if (eq (car placeholder) 'inlambda) + 'defun-block-intro + 'statement-block-intro))) + (if (= containing-sexp (point)) + (c-add-syntax block-intro (point)) + (goto-char (cdr placeholder)) + (back-to-indentation) + (c-add-syntax block-intro (point)) + (if (/= (point) (cdr placeholder)) + (c-add-syntax (car placeholder))))) + (if (eq char-after-ip ?{) + (c-add-syntax 'block-open))) + ;; CASE 17F: first statement in an inline, or first + ;; statement in a top-level defun. we can tell this is it + ;; if there are no enclosing braces that haven't been + ;; narrowed out by a class (i.e. don't use bod here!) + ((save-excursion + (save-restriction + (widen) + (goto-char containing-sexp) + (c-narrow-out-enclosing-class state containing-sexp) + (not (c-most-enclosing-brace state)))) + (goto-char containing-sexp) + ;; if not at boi, then defun-opening braces are hung on + ;; right side, so we need a different relpos + (if (/= (point) (c-point 'boi)) + (progn + (c-backward-syntactic-ws) + (c-safe (c-forward-sexp (if (eq (char-before) ?\)) + -1 -2))) + ;; looking at a Java throws clause following a + ;; method's parameter list + (c-beginning-of-statement-1) + )) + (c-add-syntax 'defun-block-intro (c-point 'boi))) + ;; CASE 17G: First statement in a function declared inside + ;; a normal block. This can only occur in Pike. + ((and (c-major-mode-is 'pike-mode) + (progn + (goto-char containing-sexp) + (and (not (c-looking-at-bos)) + (progn + (c-beginning-of-statement-1) + (not (looking-at c-conditional-key)))))) + (c-add-syntax 'defun-block-intro (c-point 'boi))) + ;; CASE 17H: first statement in a block + (t (goto-char containing-sexp) + (if (/= (point) (c-point 'boi)) + (c-beginning-of-statement-1 + (if (= (point) lim) + (c-safe-position (point) state) lim))) + (c-add-syntax 'statement-block-intro (c-point 'boi)) + (if (eq char-after-ip ?{) + (c-add-syntax 'block-open))) + )) + ) + ;; now we need to look at any modifiers + (goto-char indent-point) + (skip-chars-forward " \t") + (cond + ;; are we looking at a comment only line? + ((and (looking-at c-comment-start-regexp) + (/= (c-forward-token-1 0 nil (c-point 'eol)) 0)) + (c-add-syntax 'comment-intro)) + ;; we might want to give additional offset to friends (in C++). + ((and (c-major-mode-is 'c++-mode) + (looking-at c-C++-friend-key)) + (c-add-syntax 'friend)) + ;; Start of a preprocessor directive? + ((and (eq literal 'pound) + (= (save-excursion + (c-beginning-of-macro lim) + (setq placeholder (point))) + (c-point 'boi)) + (not (and (c-major-mode-is 'pike-mode) + (eq (char-after (1+ placeholder)) ?\")))) + (c-add-syntax 'cpp-macro))) + ;; return the syntax + syntax)))))) + +(add-hook 'find-file-hooks 'agulbra-c++-clean-out-spaces) +(add-hook 'write-file-hooks 'agulbra-c++-clean-out-spaces) + +(add-hook 'c++-mode-hook 'kde-c++-mode-hook) +(add-hook 'c-mode-hook 'kde-c-mode-hook) +; always end a file with a newline +(setq-default require-final-newline t) +; 'next-line won't be adding newlines +(setq-default next-line-add-newlines nil) +(setq compilation-error-regexp-systems-list '(gnu of comma 4bsd) + compilation-ask-about-save nil) + +(provide 'kde-emacs-core) diff --git a/scripts/kde-emacs/kde-emacs-doc.el b/scripts/kde-emacs/kde-emacs-doc.el new file mode 100644 index 00000000..5fca1361 --- /dev/null +++ b/scripts/kde-emacs/kde-emacs-doc.el @@ -0,0 +1,322 @@ +;; kde-emacs-doc.el +;; +;; Copyright (C) 2002 Zack Rusin +;; +;; This library is free software; you can redistribute it and/or +;; modify it under the terms of the GNU Lesser General Public +;; License as published by the Free Software Foundation; either +;; version 2.1 of the License, or (at your option) any later version. +;; +;; This library is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; Lesser General Public License for more details. +;; +;; You should have received a copy of the GNU Lesser General Public +;; License along with this library; if not, write to the Free Software +;; Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA +;; 02110-1301 USA +;; +;; +;;; Documentation : +;; Interactive functions: +;; kde-license-insert - inserts the chosen license header in the current +;; buffer, +;; kde-doc-function-insert - insert documentation skeleton for the function +;; at the current location +;; kde-doc-multiline-insert - inserts blank mutliline comment skeleton +;; kde-doc-oneliner-insert - inserts blank one line comment skeleton +;; +;;; TODO : +;; - add interactive functions to insert file, class, brief, +;; and group comments, +;; - change the way commenting works after license insertion, +;; - add syntax higlighting for doxygen/kdoc keywords +;; - add more license headers + + +(require 'kde-emacs-core) +(require 'kde-emacs-semantic) + +;*---------------------------------------------------------------------*/ +;* Licenses ... */ +;*---------------------------------------------------------------------*/ + +(defvar LGPL "This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA +02110-1301 USA" + "GNU LGPL license header.") + +(defvar GPL "This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA +02110-1301, USA." + "GNU GPL license header.") + +(defvar FDL "Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.1 +or any later version published by the Free Software Foundation; +with the Invariant Sections being LIST THEIR TITLES, with the +Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. +A copy of the license is included in the section entitled \"GNU +Free Documentation License\"." + "GNU FDL license header.") + +(defvar BSD "Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + "BSD license header.") + + +;;---------------- +;; Variables | +;;---------------- + +(defconst kde-doc-styles + '( + (javadoc . + ((start . "/**") + (end . "*/") + (separator . "\n*") + (oneliner . "///") + (element . "/**< */") + (param . "@param %s") + (return . "@return") + (seealso . "@see") + (class . "") + (brief . "@brief") + (file . "@file %s") + )) + (qt . + ((start . "/*!") + (end . "*/") + (separator . "\n") + (oneliner . "//!") + (element . "/*!< */") + (param . "\\param %s") + (return . "\\return") + (seealso . "\\sa") + (class . "\\class") + (brief . "\\brief") + (file . "\\file %s") + )) + ) + "Documentation styles used by KDE.") + +(defcustom kde-doc-style 'javadoc + "Current documentation style. This variable is buffer local." + :group 'kde-devel + :version "0.1" + :type (if (boundp 'kde-doc-styles) + `(choice ,@(mapcar (lambda (s) `(const ,(car s))) kde-doc-styles)) + 'symbol)) +(make-variable-buffer-local 'kde-doc-style) + +(defcustom kde-license-comment-style 'box + "Style to be used for `kde-license-insert'. +See `comment-styles' for a list of available styles." + :group 'kde-devel + :version "0.1" + :type (if (boundp 'comment-styles) + `(choice ,@(mapcar (lambda (s) `(const ,(car s))) comment-styles)) + 'symbol)) + +;*---------------------------------------------------------------------*/ +;* Functions ... */ +;*---------------------------------------------------------------------*/ + +(defun kde-license-header () + (let ((ret (file-name-nondirectory (buffer-file-name)))) + (setq ret (concat ret " \n\n")) + (setq ret (concat ret "Copyright (C) " (format-time-string "%Y ") + kde-full-name " <"kde-email">\n\n")) + )) + +(defun kde-license-insert (license) + "Inserts the chosen license header at the top of the current +buffer." + (interactive (list (completing-read + "Which license do you want to use? " + '(("GNU GPL" 1) ("GNU LGPL" 2) ("GNU FDL" 3) ("BSD" 4)) + nil t nil))) + (save-excursion + (let ((start (point-min)) + (end) + ) + (setq comment-style kde-license-comment-style) + (goto-char start) + (if license + (progn + (cond ((string= license "GNU GPL") + (insert (kde-license-header)) + (insert GPL) + ) + ((string= license "GNU LGPL") + (insert (kde-license-header)) + (insert LGPL) + ) + ((string= license "GNU FDL") + (insert (kde-license-header)) + (insert FDL) + ) + ((string= license "BSD") + (insert (kde-license-header)) + (insert BSD) + ) + ) + (insert "\n") + (setq end (point)) + (comment-region start end) + ) + ) + ) + )) + +(defmacro kde-doc-type-string (arg) + "Maps doc element from kde-doc-style to string." + `(cdr (assoc ,arg (assoc kde-doc-style kde-doc-styles))) + ) + +(defun kde-doc-param-string (ARG) + "Substitues %s in the param string with ARG." + (let ((par (kde-doc-type-string 'param))) + (if (string-match "\%s" par) + (replace-match ARG t t par) + par)) + ) + +(defun kde-function-documentation (function) + (let ((ret "") (rettype (semantic-token-type function))) + (setq ret (kde-doc-type-string 'start)) + (setq ret (concat ret (kde-doc-type-string 'separator))) + (dolist (elt (semantic-token-function-args function) ret) + (setq ret (concat ret (kde-doc-type-string 'separator) " " + (kde-doc-param-string (semantic-token-name elt)))) + ) + (if (not (or + (kde-is-constructor function) + (semantic-token-function-destructor function))) + (progn + (if (listp rettype) + (setq rettype (car rettype))) + (if (not (string= rettype "void")) + (setq ret (concat ret (kde-doc-type-string 'separator) " " (kde-doc-type-string 'return))) + ) + ) + ) + (setq ret (concat ret "\n" (kde-doc-type-string 'end) )) + )) + +(defun kde-doc-function-insert () + "Inserts skeleton function documentation for a function +at the current location." + (interactive) + (save-excursion + (let* ((pt (point)) + (token (kde-function-at-point pt)) + (ret "") + (start) (end) + ) + (if (not token) + (error "There's no function at %d." pt) + (progn + (setq ret (kde-function-documentation token)) + (goto-char (semantic-token-start token)) + (previous-line) + (goto-char (point-at-eol)) + (setq start (point)) + (insert "\n " ret) + (setq end (semantic-token-end token)) + (indent-region start end nil) + ) + ) + ))) + +(defun kde-doc-oneliner-insert () + "Insert oneliner comment at the current point. If the line is not empty newline is inserted." + (interactive) + (let ((thisblank)(pt)) + (save-excursion + (beginning-of-line) + (setq pt (point)) + (setq thisblank (looking-at "[ \t]*$")) + (if (not thisblank) + (progn + (newline) + (goto-char pt) + )) + (insert (kde-doc-type-string 'oneliner)) + (setq pt (point-at-eol)) + (end-of-line) + ) + (goto-char pt) + )) + +(defun kde-doc-multiline-insert () + "Inserts blank multiline comment at point. If the current line isn't blank +the functions inserts a newline." + (interactive) + (let ((thisblank)(start) (end)) + (save-excursion + (beginning-of-line) + (setq start (point)) + (setq thisblank (looking-at "[ \t]*$")) + (if (not thisblank) + (progn + (newline) + (goto-char start) + )) + ;; The blank to fix sometimes occuring + ;; weird behavior in indent-region + (insert " " + (kde-doc-type-string 'start) + (kde-doc-type-string 'separator) "\n" + (kde-doc-type-string 'end) + ) + (setq end (point)) + (indent-region start end nil) + ) + (goto-char start) + (end-of-line) + )) + + +(provide 'kde-emacs-doc) diff --git a/scripts/kde-emacs/kde-emacs-general.el b/scripts/kde-emacs/kde-emacs-general.el new file mode 100644 index 00000000..be34047c --- /dev/null +++ b/scripts/kde-emacs/kde-emacs-general.el @@ -0,0 +1,179 @@ +;; kde-emacs-general.el +;; +;; Copyright (C) 2002 KDE Development Team +;; +;; This library is free software; you can redistribute it and/or +;; modify it under the terms of the GNU Lesser General Public +;; License as published by the Free Software Foundation; either +;; version 2.1 of the License, or (at your option) any later version. +;; +;; This library is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; Lesser General Public License for more details. +;; +;; You should have received a copy of the GNU Lesser General Public +;; License along with this library; if not, write to the Free Software +;; Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA +;; 02110-1301 USA + +;;; Commentary: +;; + +;;; Code: + +(require 'kde-emacs-vars) + +;*---------------------------------------------------------------------*/ +;* Functions ... */ +;*---------------------------------------------------------------------*/ + +;; Helper for kde-file-get-cpp-h +(defun kde-find-file (filename basedir) + "Looks for \"filename\" under \"basedir\"" + (if basedir + (let ((path (concat basedir "/" filename))) + (if (file-readable-p path) + path)) + ) +) + +(defun kde-file-get-cpp-h () + "Function returns a corresponding source or header file. The returned +variable is a list of the form (FILENAME IS_READABLE) e.g. when being in +test.h file and having test.cpp file readable in the same directory it will +return (\"test.cpp\" t)." + (interactive) + (let* ((name (buffer-file-name)) + (nname (file-name-sans-extension name)) + (ext (file-name-extension name)) + (path nil) + (ret nil) + (listit nil)) + (cond + ((member ext kde-header-files) + (setq listit kde-source-files) + (while (and listit (not ret)) ; loop over the list but stop once ret is set + (setq path (concat nname "." (car listit))) + (if (file-readable-p path) + (setq ret (cons path t)) + ) + (if (not ret) + (if (string-match "_p$" nname) + (progn + (setq path (concat (substring nname 0 (string-match "_p$" nname)) "." (car listit))) + (if (file-readable-p path) + (setq ret (cons path t)) + ))) + ) + (if (not ret) + (progn ; look in kde-source-directory + (setq path (kde-find-file (file-name-nondirectory path) kde-source-directory)) + (if (and + path + (file-readable-p path)) + (setq ret (cons path t)) + )) + ) + (setq listit (cdr listit)) ; ++listit + ) + ; not found, will create one + (if (not ret) + (setq ret (cons (concat nname "." kde-prefered-source-extension) nil )) + )) + + ((member ext kde-source-files) + (setq listit kde-header-files) + (while (and listit (not ret)) ; loop over the list but stop once ret is set + (setq path (concat nname "." (car listit))) + ; look in current dir + (if (file-readable-p path) + (setq ret (cons path t))) + (if (not ret) ;check for header_p.h files + (progn (setq path (concat nname "_p." (car listit))) + (if (file-readable-p path) + (setq ret (cons path t))))) + (if (not (file-readable-p path)) + (progn ; look in kde-include-directory + (setq path (kde-find-file (file-name-nondirectory path) kde-include-directory)) + (if (and + path + (file-readable-p path)) + (setq ret (cons path t)) + )) + ) + (setq listit (cdr listit)) ; ++listit + ) + ; not found, will create one + (if (not ret) + (setq ret (cons (concat nname "." (car kde-header-files)) nil )) + )) + ) + ret + )) + +(defun kde-switch-cpp-h () + "Switches between the source and the header file +(both directions)." + (interactive) + (let ((file (kde-file-get-cpp-h))) + (if (car file) + (find-file (car file)) + (error "Corresponding source file doesn't exist.") + ) + )) + +(defun kde-delete-backward-ws () + "Function deletes all preceding whitespace characters." + (interactive) + (let ((start (point)) + end) + (save-excursion + (setq end (re-search-backward "[^ \t]" (point-at-bol) t)) + (if (not end) + (setq end (point-at-bol)) + (setq end (1+ end)))) + (delete-backward-char (- start end)))) + +(defun kde-skip-blank-lines () + "Skips backwards past blank lines, stopping +at a first non-blank line" + (let* ((start (point-at-bol)) + (end (point-at-eol)) + (mstring (buffer-substring start end)) + (ret 0)) + (while (or + (string-match "^[ \t\r\n]+$" mstring) + (and (string= mstring "") + (= ret 0))) + (setq ret (forward-line -1)) ; if ret != 0, we stop, since we're at the first line... + (setq start (point-at-bol) + end (point-at-eol)) + (setq mstring (buffer-substring start end)) + ) + )) + +(defun kde-comments-begin () + "Skip back from current point past any preceding C-based comments at the beginning of lines. +Presumes no \"/*\" strings are nested within multi-line comments." + (let ((opoint)) + (while (progn (setq opoint (point)) + ;; To previous line + (if (zerop (forward-line -1)) + (cond + ;; If begins with "//" or ends with "*/", then is a + ;; comment. + ((looking-at "[ \t]*\\(//\\|$\\)")) + ((looking-at ".*\\*/[ \t]*$") + (progn (end-of-line) + ;; Avoid //*** single line comments here. + (if (re-search-backward "\\(^\\|[^/]\\)/\\*" nil t) + (progn (beginning-of-line) + (looking-at "[ \t]*/\\*"))))) + (t nil))))) + (goto-char opoint) + ;; Skip past whitespace + (skip-chars-forward " \t\n\r\f") + (beginning-of-line))) + +(provide 'kde-emacs-general) diff --git a/scripts/kde-emacs/kde-emacs-semantic.el b/scripts/kde-emacs/kde-emacs-semantic.el new file mode 100644 index 00000000..1753520b --- /dev/null +++ b/scripts/kde-emacs/kde-emacs-semantic.el @@ -0,0 +1,456 @@ +;; kde-emacs-semantic.el +;; +;; Copyright (C) 2002 Zack Rusin +;; +;; This library is free software; you can redistribute it and/or +;; modify it under the terms of the GNU Lesser General Public +;; License as published by the Free Software Foundation; either +;; version 2.1 of the License, or (at your option) any later version. +;; +;; This library is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; Lesser General Public License for more details. +;; +;; You should have received a copy of the GNU Lesser General Public +;; License along with this library; if not, write to the Free Software +;; Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA +;; 02110-1301 USA + +;;; Commentary: +;; Package provides four interactive functions: +;; - kde-function-doc-insert - creates a skeleton doxygen +;; documentation for function at point. +;; Customize it with kde-func-doc variables. +;; +;; - kde-function-expanded-at-point - returns t if function at point +;; has already been expanded. +;; +;; - kde-function-expand-at-point - expand (creates a stub) for function +;; at point (as long as function is a prototype +;; and haven't been expanded). +;; +;; - kde-create-skeletons - creates stubs for all methods in the current +;; header file. +;; +;; Package is very flexible, look at defcustom variables for things +;; you can customize. + +;;; Problems: +;; Most problems relate to C++ syntax which isn't handled correctly +;; by the Semantic package. For now templates aren't supported, I +;; have a temporary solution for other problems (e.g. const functions, +;; QT/KDE specific access specifiers) + +;;; Code: +(require 'kde-emacs-vars) +(require 'kde-emacs-general) + +;*---------------------------------------------------------------------*/ +;* User configuration ... */ +;*---------------------------------------------------------------------*/ +;;Not yet, not yet +;(defcustom kde-summary-function 'semantic-uml-prototype-nonterminal +; "*Function to use when showing info about the token" +; :group 'kde-devel +; :type semantic-token->text-custom-list +; ) + +(defcustom kde-generate-docs-with-stubs nil + "*Should function documentation be generated with the stubs." + :group 'kde-devel + :type 'boolean) + +(defcustom kde-expand-arg-start "( " + "*A string which specifies how the function arguments format should start. +e.g. \"( \" would start function arguments list like : \"func( int arg\". +and \" (\" will format the begining of the function argument list as +follows : \"func (int arg\"." + :group 'kde-devel + :version "0.1" + :type 'string) + +(defcustom kde-expand-arg-end " )" + "*Just like kde-expand-arg-start but specifies how the list should end." + :group 'kde-devel + :version "0.1" + :type 'string) + +(defcustom kde-expand-arg-break ", " + "*Specifies how the arguments should be separated." + :group 'kde-devel + :version "0.1" + :type 'string) + + +;*---------------------------------------------------------------------*/ +;* Functions ... */ +;*---------------------------------------------------------------------*/ +;; FIXME : semantic doesn't handle QT access specifiers +;(setq-default global-semantic-show-unmatched-syntax-mode nil) +;(setq-default global-semantic-show-dirty-mode nil) + +(defun kde-format-func-arg (arg) + "Formats one argument (from token to string)." + (let ((ret "")) + (if (semantic-token-variable-extra-spec arg 'const) + (setq ret "const ")) + (setq ret (concat ret (car (semantic-token-type arg)))) + (if (semantic-token-variable-extra-spec arg 'pointer) + (dotimes (idx (semantic-token-variable-extra-spec arg 'pointer)) + (setq ret (concat ret "*")) + ) + ) + (if (semantic-token-variable-extra-spec arg 'reference) + (setq ret (concat ret "&")) + ) + (setq ret (concat ret " " (semantic-token-name arg))) + ret + )) + +(defun kde-format-args (token) + "Formats all arguments from token to string. +Token has to be the function variable list e.g. +from semantic-token-function-args" + (let ((res kde-expand-arg-start) (idx 1)) + (dolist (elt token res) + (setq res (concat res (kde-format-func-arg elt))) + (when (< idx (length token)) + (setq res (concat res kde-expand-arg-break))) + (setq idx (1+ idx)) + ) + (setq res (concat res kde-expand-arg-end)) + ;; if it's something like "( )" replace it with "()" + (when (string= res (concat kde-expand-arg-start kde-expand-arg-end)) + (setq res (replace-regexp-in-string "([ \t]+)" "()" res))) + res + )) + +(defun kde-function-in-tokens (FUNC TOKENS) + "Search for function in tokens. FUNC has to be a function +token and TOKENS have to be a list of functions from buffer." + (let ((ret)(elt)) + (while (and TOKENS (not ret)) + (setq elt (car TOKENS)) + (setq TOKENS (cdr TOKENS)) + (if (and (string= (semantic-token-name FUNC) + (semantic-token-name elt)) + (equal (semantic-token-type FUNC) + (semantic-token-type elt)) + ;; FIXME (semantic) : Functions in some classes don't have the + ;; 'parent property set !!! + ;;(string= (semantic-token-function-parent FUNC1) + ;; (semantic-token-function-parent FUNC2)) + (string= (kde-format-args (semantic-token-function-args FUNC)) + (kde-format-args (semantic-token-function-args elt)))) + (setq ret t)) + ) + ret + )) + +(defmacro kde-label-signals (pt) + "Returns none-nil if the current access label == \"signals\"" + `(save-excursion + (goto-char ,pt) + (if (looking-at ":") + (re-search-backward "signals" (point-at-bol) t) + ) + )) + +(defun kde-label-namespace (pt) + "Return the namespace to which the variable/function at point PT belongs to." + (save-excursion + (goto-char pt) + (if (looking-at "::") + (let ((start) (end)) + (re-search-backward "\\b\\w+" (point-at-bol) t) + (setq start (match-beginning 0)) + (setq end (match-end 0)) + (buffer-substring-no-properties start end) + ) + ) + )) + +(defmacro kde-label-slots (pt) + "Return none-nil if at PT there's slots access specifier." + `(save-excursion + (goto-char ,pt) + (if (looking-at ":") + ;; export this regex to a kde-emacs-vars defvar + (re-search-backward "\\(public\\|protected\\|private\\)[ \t]+slots" (point-at-bol) t)) + )) + +(defmacro kde-is-constructor (function) + "Returns t if the FUNCTION is a constructor." + `(semantic-token-function-extra-spec ,function 'constructor) + ) + +(defun kde-function-const (function) + "Returns t if the FUNCTION has been declared as const, e.g. +if given a token representing \"int getInt() const\" this functions +would return t" + (save-excursion + (let ((start (semantic-token-start function)) + (end (semantic-token-end function))) + (goto-char end) + (if (re-search-backward "const\b*;" start t) + t + nil) + ) + )) + +(defun kde-is-prototype (function) + "Returns t if the FUNCTION is only a prototype." + (cond + ((semantic-token-function-extra-spec function 'prototype) + t) + (t + (kde-function-const function)) + )) + + + +(defun kde-function-at-point (pt) + "Return function at pt as a token." + (save-excursion + (let ((token) + (what (semantic-find-nonterminal-by-position pt (current-buffer))) + (ctx)) + (goto-char pt) + (if (eq (semantic-token-token what) 'function) + what + (semantic-find-nonterminal-by-position pt (semantic-token-type-parts what))) + ) + )) + +(defun kde-function-construct (token pclass) + "Constructs a function string from the TOKEN, with the parent class PCLASS." + (let ((fname (semantic-token-name token))) + (if (semantic-token-function-destructor token) + (setq fname (concat "~" fname)) + ) + (if pclass + (setq fname (concat pclass "::" fname)) + ) + (if (and + (not (kde-is-constructor token)) + (not (semantic-token-function-destructor token))) + (progn + (cond + ((stringp (semantic-token-type token)) + (setq fname (concat (semantic-token-type token) "\n" fname)) + ) + (t + (setq fname (concat (car (semantic-token-type token)) "\n" fname))) + ) + (if (semantic-token-function-extra-spec token 'const) + (setq fname (concat "const " fname)) + ) + ) + ) + (setq fname (concat fname (kde-format-args (semantic-token-function-args token)))) + (if (kde-function-const token) + (setq fname (concat fname " const" )) + ) + (setq fname (concat fname "\n{" "\n}")) + fname + ) + ) + +(defun kde-class-expand (class-token) + "Returns stubs for member functions as a string. +class-token has to be a token representing either a class or a struct." + (let ((ret "") + (name (semantic-token-name class-token)) + (parents (semantic-token-type-parent class-token)) + (parts (semantic-token-type-parts class-token)) + (cur-token) + (cur-token-name) + (asignal) + (aslot) + (namespace) + ) + (dolist (elt parts ret) + (setq cur-token (semantic-token-token elt)) + (setq cur-token-name (semantic-token-name elt)) + (cond + ((and + (eq cur-token 'type) + (stringp cur-token-name)) + (cond + ((string= cur-token-name "class") + (kde-class-expand elt) + ) + ((string= cur-token-name "enum") + ;;skip enums + ) + ((string= cur-token-name "struct") + (kde-class-expand elt) + ) + ) + ) + ((and + (eq cur-token 'function) + (stringp cur-token-name)) + ;;FUNCTION - generate a skeleton for it + (if (and (kde-is-prototype elt) + (not asignal)) + (setq ret (concat ret (kde-function-construct elt name) "\n\n")) + ) + ;(insert (kde-function-documentation elt) "\n") + ) + ((and + (eq cur-token 'label) + (stringp cur-token-name)) + (setq aslot nil + asignal nil) + ;;LABEL - unsets both signals and slots + ) + ((and + (eq cur-token 'variable) + cur-token-name) + ;;VARIABLE - doesn't handle static variables correctly right now + ) + ((not (stringp cur-token-name)) + (cond + ((kde-label-signals (car (semantic-token-extent elt))) + ;;SIGNALS - next prototypes belong to signals and we don't want to + ;; expand those + (setq asignal t + aslot nil) + ) + ((kde-label-namespace (car (semantic-token-extent elt))) + ;;NAMESPACE - semantic doesn't handle things like Qt::ButtonState correctly + ;; so we do ;) + (setq namespace (kde-label-namespace (car (semantic-token-extent elt)))) + ) + ((kde-label-slots (car (semantic-token-extent elt))) + ;;SLOTS - for now just unset signals + (setq aslot t + asignal nil) + ) + (t + (insert "something else at " (number-to-string (car (semantic-token-extent elt))) "\n")) + )) + (t + (insert "Unknown type :: " (prin1-to-string elt) " >>" (prin1-to-string cur-token) "\n")) + ) + ) + ret + ) + ) + +(defun kde-expand-tokens (tokens) + "Expands smenatic tokens to strings." + (let ((ret "")) + (dolist (elt tokens ret) + (cond + ((eq (semantic-token-token elt) 'type) + (setq ret (concat ret (kde-class-expand elt))) + ) + ((eq (semantic-token-token elt) 'function) + (if (kde-is-prototype elt) + (setq ret (concat ret (kde-function-construct elt nil) "\n\n")) + ) + ) + ((eq (semantic-token-token elt) 'variable) + ;; skip + ;;(kde-extract-variable elt) + ) + ((eq (semantic-token-token elt) 'include) + ;;ignore includes for now + ) + (t (insert "Unknown type : " (prin1-to-string (semantic-token-type elt)) "\n")) + ) + ) + ) + ) + + +(defun kde-tokens-in-file (FILENAME) + "Returns all tokens from a file with the FILENAME." + (let ((exists (file-readable-p FILENAME)) + (buf (current-buffer)) + (tokens)) + (if exists + (progn + (find-file FILENAME) + (setq tokens (semantic-bovinate-toplevel t)) + (switch-to-buffer buf) + tokens) + nil) + )) + +(defun kde-function-in-file (FUNC FILENAME) + "Returns non-nil if FUNC is in a file named FILENAME" + (let ((tokens (kde-tokens-in-file FILENAME))) + (if tokens + (kde-function-in-tokens FUNC tokens) + nil + ) + )) + +(defun kde-function-is-expanded (FUNC) + "Returns t if the function FUNC has been expanded." + (let ((file (kde-file-get-cpp-h))) + (if (cdr file) + (if (kde-function-in-file FUNC (car file)) + t + nil + ) + nil) + )) + +(defun kde-function-expanded-at-point (PT) + "Returns non-nil if the function at point PT has already been expanded." + (interactive "d") + (let ((func (kde-function-at-point PT))) + (kde-function-is-expanded func) + ) + ) + +(defun kde-create-skeletons () + "Creates functions stubs in the source file, for all functions +in the current header file." + (interactive) + (let* ((all-tokens (semantic-bovinate-toplevel t)) + (filename (buffer-name)) + (cppfile (car (kde-file-get-cpp-h))) + (funcs (kde-expand-tokens all-tokens))) + (find-file cppfile) + (save-excursion + (insert "#include \"" filename "\"\n\n") + (insert funcs) + ) + ) + ) + +(defun kde-function-expand-at-point (PT) + "Expand function at point PT." + (interactive "d") + (let ((object (semantic-find-nonterminal-by-position PT (current-buffer))) + (func (kde-function-at-point PT)) + (file) + (buf) + (parent)) + (if (and object (equal (semantic-token-type object) "class")) + (setq parent (semantic-token-name object))) + (if (and (not (kde-function-expanded-at-point PT)) + (kde-is-prototype func)) + (progn + (setq func (kde-function-construct func parent)) + (setq file (car (kde-file-get-cpp-h))) + (setq buf (current-buffer)) + (find-file file) + (save-excursion + (goto-char (point-max)) + (insert "\n" func "\n") + ) + (switch-to-buffer buf) + ) + (error "Function already expanded or defined!") + ) + ) + ) + +(provide 'kde-emacs-semantic) diff --git a/scripts/kde-emacs/kde-emacs-tips.texi b/scripts/kde-emacs/kde-emacs-tips.texi new file mode 100644 index 00000000..ee7c0f19 --- /dev/null +++ b/scripts/kde-emacs/kde-emacs-tips.texi @@ -0,0 +1,257 @@ +\input texinfo @c -*-texinfo-*- + +@finalout + +@c %**start of header +@setfilename kde-emacs-tips +@settitle KDE Emacs usefull programming tips +@footnotestyle end +@c @setchapternewpage odd !! we don't want blank pages +@c %**end of header + +@dircategory Emacs +@direntry +* KDE Emacs: (kde-emacs). Emacs mode for editing KDE/QT C++/C code. +@end direntry + +@ifnottex +Copyright @copyright{} 2002 Zack Rusin and KDE Development Team + +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.1 or +any later version published by the Free Software Foundation; with the +Invariant Sections being ``The GNU Manifesto'', ``Distribution'' and +``GNU GENERAL PUBLIC LICENSE'', with the Front-Cover texts being ``A GNU +Manual'', and with the Back-Cover Texts as in (a) below. A copy of the +license is included in the section entitled ``GNU Free Documentation +License'' in the Emacs manual. + +(a) The FSF's Back-Cover Text is: ``You have freedom to copy and modify +this GNU Manual, like GNU software. Copies published by the Free +Software Foundation raise funds for GNU development.'' + +This document is part of a collection distributed under the GNU Free +Documentation License. If you want to distribute this document +separately from the collection, you can do so by adding a copy of the +license to the document, as described in section 6 of the license. +@end ifnottex + +@titlepage +@sp 10 + +@center @titlefont{KDE Emacs Package} +@sp 2 +@center @subtitlefont{KDE Emacs package documentation and programming tips.} +@sp 2 +@author Zack Rusin + +@page +@vskip 0pt plus 1filll +Copyright @copyright{} 2002 Zack Rusin & KDE Development Team +@sp 1 +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.1 or +any later version published by the Free Software Foundation; with the +Invariant Sections being ``The GNU Manifesto'', ``Distribution'' and +``GNU GENERAL PUBLIC LICENSE'', with the Front-Cover texts being ``A GNU +Manual'', and with the Back-Cover Texts as in (a) below. A copy of the +license is included in the section entitled ``GNU Free Documentation +License'' in the Emacs manual. + +(a) The FSF's Back-Cover Text is: ``You have freedom to copy and modify +this GNU Manual, like GNU software. Copies published by the Free +Software Foundation raise funds for GNU development.'' + +This document is part of a collection distributed under the GNU Free +Documentation License. If you want to distribute this document +separately from the collection, you can do so by adding a copy of the +license to the document, as described in section 6 of the license. +@end titlepage + +@node Top, Introduction, (dir), (dir) +@comment node-name, next, previous, up + +@macro kdeemacs +KDE Emacs +@end macro + +@ifinfo +@top @kdeemacs{} + +@kdeemacs{} is an Emacs package with tons of useful features +which ease KDE development process. +KDE Emacs usefull programming tips. + +@end ifinfo + +@menu +* Introduction:: +* Getting Connected:: +* Generating stubs:: +* Tips:: +@end menu + +@comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +@node Introduction, Getting Connected, Top, Top +@comment node-name, next, previous, up +@chapter Introduction +@comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +@comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +@node Getting Connected, Generating stubs, Introduction, Top +@comment node-name, next, previous, up +@chapter Getting Connected +@comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +@menu +* Sect. 2.1:: Installation +* Sect. 2.2:: Files +* Sect. 2.3:: Keybindings +@end menu + +@node Sect. 2.1, Sect. 2.2, Chapter 2, Chapter 2 +@section @code{Installation} +@comment node-name, next, previous, up + +@node Sect. 2.2, Sect. 2.3, Sect. 2.1, Chapter 2 +@section @code{Files} +@comment node-name, next, previous, up + +@node Sect. 2.3, , Sect 2.2, Chapter 2 +@section @code{Keybindings} +@comment node-name, next, previous, up + +@comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +@node Generating stubs , Tips, Getting Connected, Top +@comment node-name, next, previous, up +@chapter Generating stubs +@comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +@comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +@node Tips, Top, Generating stubs, Top +@comment node-name, next, previous, up +@chapter Tips +@comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +@sp 1 +@strong{Q.} @emph{How do I hide #ifdef's in source files without +actually changing them?} + +@strong{A.} Use @code{hide-ifdef-mode} which supports hiding of ifdef +blocks without actually changing the file. In this mode @kbd{C-c @@ +C-d} hides ifdef block and @kbd{C-c @@ C-s} shows it again. + +@sp 1 +@strong{Q.} @emph{How do I get more informations about the keybindings +of the currently active modes?} + +@strong{A.} Type @kbd{M-x describe-mode}. + +@sp 1 +@strong{Q.} @emph{How do I get automatic syntax higlighting of my +custom types?} + +@strong{A.} Use a package named @file{ctypes.el} which does exactly +that. + +@sp 1 +@strong{Q.} @emph{Is it possible to highlight dangerous syntax, just +like Borland JBuilder does it?} + +@strong{A.} Yes, use the @file{cwarn.el} package. + +@sp 1 +@strong{Q.} @emph{How do I easily customize Emacs faces/colors?} + +@strong{A.} Use the @file{color-theme.el} package. + +@sp 1 +@strong{Q.} @emph{How do I set the taskbar Emacs identification string?} + +@strong{A.} To your @file{.emacs} add a line like: +@example +(setq frame-title-format "%b (%m)") +@end example +which will display ``filename (mode)'' type of string in the +taskbar. Type @kbd{C-h v frame-title-format} to get more info. + +@sp 1 +@strong{Q.} @emph{Can I make Emacs jump to the matching parenthesis +with @kbd{%} just like vi?} + +@strong{A.} Yes, just add to your @file{.emacs} something like: +@example +;; Make the % key jump to the matching {}[]() if on another, like VI +(global-set-key "%" 'match-paren) + +(defun match-paren (arg) + "Go to the matching parenthesis if on parenthesis otherwise insert %." + (interactive "p") + (cond ((looking-at "\\s\(") (forward-list 1) (backward-char 1)) + ((looking-at "\\s\)") (forward-char 1) (backward-list 1)) + (t (self-insert-command (or arg 1))))) +@end example + +@sp 1 +@strong{Q.} @emph{Can I have words like FIXME, TODO, HACK or NOTE +higlighted in documentation strings?} + +@strong{A.} Yes, either use @file{code-keywords.el} package or wait +till I'll add it to @kdeemacs{}. + +@sp 1 +@strong{Q.} @emph{I really, really hate identifiersNamedLikeThis. I'd +like to change them to identifiers_named_like_this but the +maintainer of the application/library that I'm hacking on doesn't +agree with me. What can I do? } + +@strong{A.} Use the @file{glasses.el} package which changes +identifiersNamedLikeThis to identifiers_named_like_this in the +buffers you're editing and switches them back to their original form +once you save those buffers. + +@sp 1 +@strong{Q.} @emph{Is it possible to get function completion or +signature display in Emacs? Will it ever be done?} + +@strong{A.} Yes and yes. I've been planning on doing this for quite a +while and hopefully will have this finished pretty soon (no dates +though :) ) The first thing that should be done is writing a few +fixes for the Semantic package (@file{c.bnf} to be more exact), +because Semantic doesn't handle templates, member function declared +as const or KDE access specifiers, once this is done all that will be +left is using semanticdb package which efficiently stores and retrieves +large amounts of tokens and then displaying tokens belonging to types at +point which match current context. + +@sp 1 +@strong{Q.} @emph{Is there a package that would highlight changes that +I made to a certain file?} + +@strong{A.} I wouldn't be writing this if there wouldn't - try +@kbd{M-x highlight-changes-mode}. + +@sp 1 +@strong{Q.} @emph{How to get a diff between the stuff I have in my +local buffer and the file on disk?} + +@strong{A.} Use ibuffer package. After @kbd{M-x ibuffer} typing +@kbd{=} over a file will display a diff between the buffer and the +file on the disk. + +@sp 1 +@strong{Q.} @emph{I want to temporarily highlight certain variable in +a file, how to do it?} + +@strong{A.} Type @kbd{M-x hi-lock-mode}, now @kbd{C-x w h +@emph{regexp} @key{RET} @emph{face} @key{RET}} highlights regexp with +face in the current file and @kbd{C-x w r @emph{regexp} @key{RET}} +unhighlights it. + +@node Concept Index, , Variables Index, Top +@c node-name, next, previous, up +@unnumbered Concept Index + +@printindex cp + +@contents +@bye diff --git a/scripts/kde-emacs/kde-emacs-utils.el b/scripts/kde-emacs/kde-emacs-utils.el new file mode 100644 index 00000000..c6904539 --- /dev/null +++ b/scripts/kde-emacs/kde-emacs-utils.el @@ -0,0 +1,894 @@ +;; kde-emacs-utils.el +;; +;; Copyright (C) 2002-2005 KDE Development Team +;; +;; This library is free software; you can redistribute it and/or +;; modify it under the terms of the GNU Lesser General Public +;; License as published by the Free Software Foundation; either +;; version 2.1 of the License, or (at your option) any later version. +;; +;; This library is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; Lesser General Public License for more details. +;; +;; You should have received a copy of the GNU Lesser General Public +;; License along with this library; if not, write to the Free Software +;; Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA +;; 02110-1301 USA + + +(require 'kde-emacs-vars) +(require 'kde-emacs-general) +(require 'kde-emacs-compat) + +(if (eq kde-emacs-type 'xemacs) + (progn + (require 'func-menu) + (add-hook 'find-file-hooks 'fume-add-menubar-entry)) + (require 'imenu)) + +(defmacro c-safe-scan-lists (from count depth) + "Like `scan-lists' but returns nil instead of signalling errors. +This function does not do any hidden buffer changes." + (if (featurep 'xemacs) + `(scan-lists ,from ,count ,depth nil t) + `(c-safe (scan-lists ,from ,count ,depth)))) + +;; returns non-nil if the given file has a using declaration +;; with the passed namespace +(defun kde-file-has-using (namespace) + (let (found) + (save-excursion + (beginning-of-buffer) + (setq found (re-search-forward "^using" nil 1)) + (if found + (setq found (search-forward namespace (line-end-position) 1)) + ) + ) + found) + ) + +;; returns non-nill if the given file has a "namespace SomeNM" declaration +;; where SomeNM is passed via the namespace argument +(defun kde-file-is-in-namespace (namespace) + (let (found) + (save-excursion + (beginning-of-buffer) + (setq found (re-search-forward "^namespace" nil 1)) + (if found + (setq found (search-forward namespace (line-end-position) 1)) + ) + ) + found) + ) + +; Helper function for parsing our current position in a C++ header file +; returns (namespace (class function)) where (a b) is a cons. +(defun method-under-point () + (let ((class nil) + (namespace "") ; will contain A::B:: + (function nil)) + (save-excursion + (backward-char) ; in case we're after the ';' + (search-forward ";" nil t) ; look for the ';' + (backward-char) + (save-excursion + ; Go up a level, skipping entire classes etc. + ; This is a modified version of (backward-up-list) which doesn't + ; throw an error when not found. + (let ((pos (c-safe-scan-lists (point) -1 1))) + ; +1 added here so that the regexp in the while matches the { too. + (goto-char (if pos (+ pos 1) (point-min)))) + (while (re-search-backward "^[ ]*\\(class\\|namespace\\|struct\\)[ \t][^};]*{" nil t) + (save-excursion + (forward-word 1) + (when (looking-at "[ \t]*[A-Z_]*_EXPORT[A-Z_]*[ \t]") + (forward-word 1) + (re-search-forward "[ \t]" nil t)) + (while (looking-at "[ \t]") + (forward-char 1)) + (setq start (point)) + ; Parse class name ("Foo" or "Foo::Bar::Blah"). + ; Beware of "Foo:" + (while (or (looking-at "[A-Za-z0-9_]") (looking-at "::")) + (while (looking-at "[A-Za-z0-9_]") + (forward-char 1)) + (while (looking-at "::") + (forward-char 2)) + ) + (cond + (class ; class found already, so the rest goes into the namespace + (setq namespace (concat (buffer-substring start (point)) "::" namespace))) + (t ; class==nil + (setq class (buffer-substring start (point))))) + ) + ; Go up one level again + (let ((pos (c-safe-scan-lists (point) -1 1))) + (goto-char (if pos (+ pos 1) (point-min)))) + )) + + ; Back to where we were, parse function name + (let ((end (point))) ; remember where the function decl ends + (search-backward ")" nil t) ; look back for the end of the argument list + (forward-char) + (backward-sexp) ; brings us back to the '(' + (backward-word 1) + (when (looking-at "throw[ \t]") ; exception specification, look for () again + (search-backward ")" nil t) + (forward-char) + (backward-sexp)) + ; now that we moved back enough, go to beginning of line. + ; (we assume that the return type, function name, and '(' are on the same line) + (re-search-backward "^[ \t]*") + (while (looking-at "[ \t]") + (forward-char 1)) + (setq function (buffer-substring (point) end)) + ) + ) ; end of global save-excursion + (cons namespace (cons class function)) ; the returned value + ) + ) + +; get rid of virtual, static, multiple spaces, default values. +(defun canonical-function-sig (function) + (and (string-match "[ \t]*\\[ \t]*" function) + (setq function (replace-match " " t t function))) + (and (string-match "^\\(virtual\\>\\)?[ \t]*" function) + (setq function (replace-match "" t t function))) + (and (string-match "^\\(explicit\\>\\)?[ \t]*" function) + (setq function (replace-match "" t t function))) + (and (string-match "^\\(static\\>\\)?[ \t]*" function) + (setq function (replace-match "" t t function))) + (while (string-match " +" function) ; simplifyWhiteSpace + (setq function (replace-match " " t t function))) + (while (string-match "\t+" function) + (setq function (replace-match " " t t function))) + (while (string-match "^ " function) ; remove leading whitespace + (setq function (replace-match "" t t function))) + (let ((startargs (string-match "(" function))) + (while (string-match " ?=[^,)]+" function startargs) ; remove default values + (setq function (replace-match " " t t function)))) + (while (string-match " +," function) ; remove space before commas + (setq function (replace-match "," t t function))) + function ; the return value +) + +; Helper method which turns the function as seen in the header +; into the signature for its implementation +; Returns the fully-qualified signature of the function implementation +(defun kde-function-impl-sig (namespace class _function) + (let ( + (function (canonical-function-sig _function)) + (insertion-string nil)) + (and (stringp class) + (cond + ((string-match (concat "^ *" class "[ \\t]*(") function) ; constructor + (setq insertion-string + (replace-match + (concat namespace class "::" class "(") + t t function) + )) + ((string-match (concat "^ *~" class "[ \\t]*(") function) ; destructor + (setq insertion-string + (replace-match + (concat namespace class "::~" class "(") + t t function) + )) + )) ; end of "class required" + (if (not (stringp insertion-string)) ; no ctor nor dtor + (if (or (string-match " *\\([a-zA-Z0-9_]+\\)[ \\t]*(" function) ; normal method + (string-match " *\\(operator[^ \\t]+\\)[ \\t]*(" function)) ; operator + (setq insertion-string + (replace-match + (if class + (concat " " namespace class "::" "\\1(") ; c++ method + (concat " " "\\1(")) ; c function + t nil function) + ) + ; else + (error (concat "Can't parse declaration ``" + function "'' in class ``" class + "'', aborting")))) + insertion-string ; the return value + ) + ) + +;; Switch between the declaration of a class member in .cc/.cpp/.C, and its definition in the .h file +;; Written by David and Reggie after much hair tearing +;; Found since, might be worth looking at: http://www.hendawi.com/emacs/sourcepair.el +(defun switch-to-function-def () + (interactive) + (let ((n (buffer-file-name)) + (namespace "") + (class "") + (function "") + found + ) + (if (or (string-match "\\.cc$" n) + (string-match "\\.cpp$" n) + (string-match "\\.C$" n)) + ; TODO replace fume-function-before-point, needed for emacs, + ; and for better namespace support. + ;(progn + ; (let ((pos (kde-scan-lists (point) -1 1 nil t))) ; Go up a level + ; (goto-char (if pos (+ pos 1) (point-min)))) + (let ((a (fume-function-before-point))) + (and (string-match "^\\(.*\\)::\\(.*\\)$" a) + (progn + (setq class (match-string 1 a)) + (setq function (match-string 2 a)) + (kde-switch-cpp-h) + (goto-char 0) + ; Look for beginning of class ("\\s-+" means whitespace including newlines) + (re-search-forward + (concat "\\(class\\|struct\\|namespace\\)\\s-+" + "\\([A-Z_]+_EXPORT[A-Z_]*\\)?\\s-+" ; allow for optional EXPORT macro + class "\\b" ; the classname - with word separator + "[^;]+{" ; the optional inheritance and the '{' + ) nil t) + ;; TODO keep looking, until we find a match that's not inside a comment + (re-search-forward (concat "\\b" (kde-function-regexp-quote function) "[ \t]*(") nil t))))) + (if (string-match "\\.h$" n) + (progn + (let ((mup (method-under-point)) + (sig "") + (pos 0)) + (setq namespace (car mup)) + (setq class (car (cdr mup))) + (setq function (cdr (cdr mup))) + (kde-switch-cpp-h) + + ;; First search with namespace prefixed + (goto-char 0) + (setq sig (kde-remove-newline (kde-function-impl-sig namespace class function))) + (if (string-match "(.*" sig) ; remove args + (setq sig (replace-match "" nil t sig))) + (setq found (re-search-forward (concat "^[^()]*" (kde-function-regexp-quote sig) "[ \t]*(") nil t) ) + + (if (not found) + (progn + ; Now search without name space prefix + + (goto-char 0) + (setq sig (kde-remove-newline (kde-function-impl-sig "" class function))) + + (if (string-match "(.*" sig) ; remove args + (setq sig (replace-match "" nil t sig))) + (re-search-forward (concat "^[^()]*" (kde-function-regexp-quote sig) "[ \t]*(") nil t) ) ) + ))))) + +(defun kde-remove-newline (str) + (replace-in-string str "\n" " ")) +; quote for use as regexp, but replace spaces with "any whitespace" +(defun kde-function-regexp-quote (str) + (replace-in-string (regexp-quote str) "[ \n\t]" "[ \n\t]")) + +; Initial implementation by Arnt Gulbransen +; Current maintainer: David Faure +(defun agulbra-make-member () + "make a skeleton member function in the .cpp or .cc file" + (interactive) + (let* ( + (mup (method-under-point)) + (namespace (car mup)) ; will contain A::B:: + (class (car (cdr mup))) + (function (cdr (cdr mup))) + (file (buffer-file-name)) + (insertion-string (kde-function-impl-sig namespace class function)) + (msubstr nil) + (start nil) + (newcppfile nil) + ) + (setq insertion-string + (concat insertion-string "\n{\n" + (replace-in-string kde-make-member-default-impl "FUNCTION" + ; the function name and args, without newlines + (replace-in-string insertion-string "\n" " " t) + t) + "}\n")) + ; move to next method, to be ready for next call + (backward-char) ; in case we're after the ';' + (re-search-forward ";" nil t) ; end of this method decl + (let ((moveToNext t)) + (while moveToNext + (re-search-forward ";" nil t) ; end of next method decl + (save-excursion + (forward-char -2) ; -1 goes to ';' itself, so go before that + (while (looking-at "[ \t0=]") + (forward-char -1)) + (forward-char 1) + ; move to next method again if we're at a pure virtual method + (setq moveToNext (looking-at "[ \t]*=[ \t]*0;")) + ) + ) + ) + + (setq newcppfile (not (cdr (kde-file-get-cpp-h)))) + (if (string-match "\\.h$" file) + (kde-switch-cpp-h) + ) + (goto-char (point-max)) + (kde-comments-begin) + (kde-skip-blank-lines) + (setq msubstr (buffer-substring (point-at-bol) (point-at-eol))) + (if (string-match "^#include.*moc.*" msubstr) + (progn + (forward-line -1) + (end-of-line) + (insert "\n"))) + (if (string-match "}" msubstr) + (progn + (end-of-line) + (insert "\n") + (forward-line 1) + )) + (when newcppfile + (insert "\n")) + (insert insertion-string) + (forward-char -3) + (c-indent-defun) + (save-excursion + (and (string-match ".*/" file) + (setq file (replace-match "" t nil file))) + (and (string-match "\\.h$" file) + (functionp 'kdab-insert-include-file) + (kdab-insert-include-file file 't nil))) + (when (featurep 'fume-rescan-buffer) + (fume-rescan-buffer)) + )) + +(defun add-file-to-buildsystem () + "Add the current (C++) file to either Makefile.am or a .pro file, whichever exists." + ; Author: David + (interactive) + (if (file-readable-p "Makefile.am") + (add-file-to-makefile-am) + ; else: find a .pro file and add it there + (let* ((files (directory-files "." nil ".pro$" nil t)) + (projfile (car files))) + (if projfile + (add-file-to-project projfile "^SOURCES[ \t]*") ; could be SOURCES= or SOURCES+= + ; else: error + (error "No build system file found") + ))) + ) + +; internal helper for add-file-to-* +(defun add-file-to-project (makefile searchString) + (let ((file (buffer-name))) + (if (not (file-readable-p makefile)) + (error (concat makefile " not found!")) + ) + (find-file makefile) + (goto-char (point-min)) + (if (re-search-forward searchString nil t) + (progn + (end-of-line) + ; check if line ends with '\' [had to read make-mode.el to find this one!] + (while (= (char-before) ?\\) + (end-of-line 2)) ; moves to end of next line + (insert " ") + (insert file) + ) + (error (concat searchString " not found")) + )) + ) + +(defun add-file-to-makefile-am () + "Add the current file to the first _SOURCES line in the Makefile.am" + ; Author: David + (interactive) + (add-file-to-project "Makefile.am" "_SOURCES") + ) + + +; Inserts a kdDebug statement showing the name of the current method. +; You need to create the empty line first. +(defun insert-kdDebug () + (interactive) + (insert "kdDebug() << ") + ;; no unnecessary fume-* functions which aren't available on GNU/Emacs + (insert "k_funcinfo") + (insert " << endl;") + ) + +; finds a string to be used in the header-protection function ( see below ) +(defun kde-header-protection-definable-string () + (let* ((definablestring "") + (f (buffer-file-name)) + (parts (nreverse (split-string f "/"))) + (i) + (first-iter t) + (iters (min (length parts) kde-header-protection-parts-to-show))) + (dotimes (i iters) + (let ((part (pop parts))) + (setq definablestring + (concat + (upcase (replace-in-string part "[\\.-]" "_")) + (if (not first-iter) "_" "") + definablestring + ) + ) + (setq first-iter nil) + ) + ) + definablestring + ) + ) + +; Creates the ifndef/define/endif statements necessary for a header file +(defun header-protection () + (interactive) + (let ((s (kde-header-protection-definable-string))) + (save-excursion + (goto-char (point-min)) + (insert "#ifndef " s "\n#define " s "\n\n") + (goto-char (point-max)) + (insert "\n#endif\n") + ) + ) + ) + +; Makes '(' insert '(' or ' ( ' where appropiate +(defun insert-parens (arg) (interactive "*P") + (if (not (c-in-literal)) + (let ((n nil) (except nil)) + (save-excursion + (setq n (or (progn (forward-char -2) (looking-at "if")) + (progn (forward-char -1) (looking-at "for")) + (progn (forward-char -1) (looking-at "case")) + (progn (forward-char -1) (looking-at "while")) + ) + ) + (setq except (or (progn (forward-char -2) (looking-at "kdDebug")) + (looking-at "kdError") + (progn (forward-char -2) (looking-at "kdWarning")) + ) + ) + ) + (cond + (n (progn + (insert " ") + (self-insert-command (prefix-numeric-value arg)) + (insert kde-emacs-after-parent-string) + )) + (t ;else + (self-insert-command (prefix-numeric-value arg)) + (cond ((not except) (insert kde-emacs-after-parent-string))) + ))) + (self-insert-command (prefix-numeric-value arg))) + ) + +(defun insert-parens2 (arg) (interactive "*P") + (if (not (c-in-literal)) + (let ((remv nil) (nospac nil)) + (forward-char -2) + (setq remv (looking-at "( ")) ; () -> we'll have to remove that space + (forward-char 1) + (setq nospac ; no space to be added + (or (looking-at " ") + (looking-at "(") + (save-excursion ; check for kdDebug(123 + (while (looking-at "[0-9]") + (forward-char -1)) + (forward-char -7) + (or (looking-at "kdDebug(") + (looking-at "kdError(") + (progn (forward-char -2) (looking-at "kdWarning(")) + ) + ) + ) + ) + (forward-char 1) + (cond + (remv (progn + (delete-backward-char 1) + (self-insert-command (prefix-numeric-value arg)))) ; the () case + (nospac (self-insert-command (prefix-numeric-value arg))) ; no space to be added + (t ;else + (if abbrev-mode ; XEmacs + (expand-abbrev)) + (insert kde-emacs-after-parent-string) + (self-insert-command (prefix-numeric-value arg)) + ))) ; normal case, prepend a space + ;;(blink-matching-open) ; show the matching parens + (self-insert-command (prefix-numeric-value arg))) + ) + +; Makes ',' insert ', ' +(defun insert-comma (arg) + (interactive "*P") + (let* ((ch (char-after)) + (spacep (not (or (eq ch ? ) + (c-in-literal) + arg)))) + (self-insert-command (prefix-numeric-value arg)) + (if spacep + (insert " ")))) + +(defun insert-semicolon (arg) + (interactive "*P") + (self-insert-command (prefix-numeric-value arg)) + (newline-and-indent)) + +(defun insert-curly-brace (arg) (interactive "*P") + (if (not (c-in-literal)) + (let ((n nil) (o nil) + (spacep nil) (c nil) + (oneliner nil)) + (save-excursion + (save-excursion + (if (re-search-forward "[a-zA-Z]" (point-at-eol) t) + (setq oneliner t))) + (forward-char -1) ; These three lines are for the situation where + (if (not (looking-at " ")) ; the user already have inserted a space after + (forward-char 1) ; the closing parenthesis + (setq spacep t)) + (forward-char -2) + (setq o (looking-at "()")) + (forward-char 1) + (setq n (looking-at ")")) + (if (and + (not oneliner) + (not (eq + (count-lines (point-min) (point)) + (count-lines (point-min) (point-max))))) + (progn + (next-line 1) + (beginning-of-line) + (if (re-search-forward "[a-zA-Z]" (point-at-eol) t) + (setq c (eq (car (car (c-guess-basic-syntax))) 'substatement))) + ) + ) + ) + (cond + (n (progn + (if (not spacep) (insert " ")) + (self-insert-command (prefix-numeric-value arg)) + (if (not c) (newline-and-indent)) + (if oneliner (end-of-line)) + (save-excursion + (if c + (progn + (next-line 1) + (end-of-line) + )) + (newline-and-indent) + (insert "}")(c-indent-line)) + (c-indent-line) + )) + (o (progn + (newline) + (self-insert-command (prefix-numeric-value arg)) + (newline-and-indent))) + (t (progn ;else + (self-insert-command (prefix-numeric-value arg)) + (save-excursion + (beginning-of-line) + (c-indent-command)))) + )) + (self-insert-command (prefix-numeric-value arg)) + ) +) + +;; have PelDel mode work +(put 'insert-parens 'pending-delete t) +(put 'insert-parens2 'pending-delete t) +(put 'insert-comma 'pending-delete t) +(put 'insert-curly-brace 'pending-delete t) +(put 'newline-and-indent 'pending-delete t) + +; A wheel mouse that doesn't beep, unlike mwheel-install +(defun scroll-me-up () (interactive) (scroll-up 4)) +(defun scroll-me-down () (interactive) (scroll-down 4)) +(defun scroll-me-up-a-bit () (interactive) (scroll-up 1)) +(defun scroll-me-down-a-bit () (interactive) (scroll-down 1)) + +; Compilation +(defun makeclean () + "Executes a \"make clean\" in the current directory" + (interactive) + (compile (concat kde-emacs-make " clean")) + ) + +(defun make () + "Executes a \"make\" in the current directory" + (interactive) + (compile (concat kde-emacs-make " -k")) + ) + +(defun makeinstall () + "Executes a \"make install\" in the current directory" + (interactive) + (compile (concat kde-emacs-make " -k install")) + ) + +(defun makeinstallexec () + "Executes a \"make install-exec\" in the current directory" + (interactive) + (compile (concat kde-emacs-make " -k install-exec")) + ) + +(defun makethisfile () + "Try to compile the currently opened file" + (interactive) + (let ((f (file-name-nondirectory (buffer-file-name))) + (objext nil)) + + (if (file-readable-p "Makefile.am") + (setq objext "\.lo") + (setq objext "\.o")) + (if (string-match "\.cpp$" f) (setq f (replace-match objext t t f))) + (if (string-match "\.cc$" f) (setq f (replace-match objext t t f))) + (compile (concat kde-emacs-make " " f))) + ) + +;; pc-like textmarking +(when kde-use-pc-select + (progn + (load "pc-select") + (if (eq kde-emacs-type 'xemacs) + (funcall 'pc-select-mode) + (funcall 'pc-selection-mode)))) + + +; Move in other window +(defun scroll-other-up () (interactive) (scroll-other-window-down 1)) ; hehe :) +(defun scroll-other-down () (interactive) (scroll-other-window 1)) + +(defun match-paren (arg) + "Go to the matching parenthesis if on parenthesis otherwise insert %." + (interactive "p") + (cond ((looking-at "\\s\(") (forward-list 1) (backward-char 1)) + ((looking-at "\\s\)") (forward-char 1) (backward-list 1)) + (t (self-insert-command (or arg 1))))) + +(defun kde-start-c++-header () + "Start a new C++ header by inserting include guards ( see \ + header-protection function ), inserting a license statement \ + and putting (point) at the correct position" + (interactive) + (header-protection) + (insert "\n") + (beginning-of-buffer) + (kde-license-insert "GNU GPL") + (next-line 1) + (kill-line) + (end-of-buffer) + (next-line -3) + (insert "\n") +) + +(defun kde-year-range-parse-years-string (string) + "parses something like \"2000, 2008-2010\" into a list of the form \ + ((2008 . 2010)(2000 . 2000))" + (let ((pos -1) + (oldpos) + (l (length string)) + (currange "") + (startyear) + (endyear) + (ret) + ) + (while (< pos l) + (setq oldpos (+ pos 1)) + (setq pos (string-match "[,]" string (+ pos 1))) + (unless pos (setq pos l)) + (setq currange (substring string oldpos pos)) + (string-match "[0-9]+" currange) + (setq startyear (string-to-int (match-string 0 currange))) + (setq endyear + (if (string-match "-" currange) + (string-to-int (substring currange (match-end 0))) + startyear)) + (setq ret (cons (cons startyear endyear) ret)) + ) + ret + ) + ) + +(defun kde-year-range-contains-year (ranges year) + "checks whether year is in ranges.. ( ranges is a list as \ + kde-year-range-parse-years-string returns.. " + (let ((ret)) + (dolist (range ranges ret) + (when (and (>= year (car range)) (<= year (cdr range))) + (setq ret t)) + ))) + +(defun kde-year-range-to-string (ranges) + "converts ranges to a string.." + (let ((ret "")) + (dolist (range ranges) + (setq ret + (concat + (int-to-string (car range)) + (if (/= (cdr range) (car range)) + (concat "-" (int-to-string (cdr range))) + "") + ", " + ret) + ) + ) + ; remove extraneous ", " + (setq ret (substring ret 0 (- (length ret) 2))) + ) + ) + +; merges adjacent year ranges into one.. +(defun kde-year-range-cleanup (range) + (let ((origrange range)) + (while (and range (cdr range)) + (let ((years (car range)) (nyears (cadr range))) + (when (>= (+ (cdr nyears) 1) (car nyears)) + (setcar range (cons (car nyears) (cdr years))) + (setcdr range (cddr range))) + ) + (setq range (cdr range)) + ) + origrange + ) + ) + +; adds year to range.. +(defun kde-year-range-add-year (range year) + (while range + (let ((years (car range))) + (cond + ((and (>= year (car years)) (<= year (cdr years)) + ; year is already in the range.. + (setq range nil))) + ((= year (+ (cdr years) 1)) + (setcdr years year) + (setq range nil)) + ((= year (- (car years) 1)) + (setcar years year) + (setq range nil)) + ) + ) + (setq range (cdr range)) + ) + (kde-year-range-cleanup range) + ) + +(defun kde-add-copyright () (interactive) + "Tries to add your kde-full-name and kde-email to the Copyright \ + statements at the top of a file... It tries to figure out \ + if it's already there, and if so, updates the line to include the \ + current year.. ( well, replaces it by a new one, anyway :) )" + (let ((wascomment "")) + (save-excursion + (beginning-of-buffer) + (if (re-search-forward (concat "Copyright ([Cc]) \\([0-9 ,-]*\\) " (regexp-quote kde-full-name)) nil t) + (progn + (beginning-of-line) + (let ((years (kde-year-range-cleanup (kde-year-range-parse-years-string (match-string 1)))) + (new-copyright-string "Copyright (C) ") + (this-year (string-to-int (format-time-string "%Y")))) + (when (not (kde-year-range-contains-year years this-year)) + (kde-year-range-add-year years this-year)) + (setq new-copyright-string + (concat new-copyright-string (kde-year-range-to-string years))) + ; finish new-copyright-string + (setq new-copyright-string + (concat new-copyright-string " " kde-full-name " <" kde-email ">")) + (beginning-of-line) + (re-search-forward "Copyright ([Cc])") + (beginning-of-line) + (setq wascomment + (buffer-substring (point) + (match-beginning 0) + )) + (kill-line nil) + (insert new-copyright-string) + ) + ) + (beginning-of-buffer) + (let ((first-copyright-str (re-search-forward "Copyright ([Cc])" nil t))) + (if first-copyright-str + (progn + (goto-char first-copyright-str) + (beginning-of-line) + (setq wascomment (buffer-substring (point) (match-beginning 0))) + (forward-line 1) + ) + (goto-line 2)) + ) + (beginning-of-line) + (insert "Copyright (C) " (format-time-string "%Y") " " + kde-full-name " <" kde-email ">\n") + (forward-line -1) + ) + (end-of-line) + (let ((end (point))) + (beginning-of-line) + (insert wascomment) + ) + ) + ) + ) + +(defun kde-emacs-file-style-update () + "Updates the style header of this file" + (interactive) + (if (or (eq major-mode 'c++-mode) + (eq major-mode 'c-mode)) + (let ((startpoint) (endpoint) + (firstline) (strings) + (str) (m) (m2) (var) (value) + (final)) + (save-excursion + (beginning-of-buffer) + (setq startpoint (point)) + (setq endpoint (point-at-eol))) + (setq firstline (buffer-substring startpoint endpoint)) + (if (string-match "-\*-\\([A-Za-z0-9\-\+\:\; ]+\\)-\*-" firstline) + (delete-region startpoint endpoint)) + (setq final (concat "-*- " + "Mode: " mode-name "; " + "c-basic-offset: " (prin1-to-string c-basic-offset) "; " + "indent-tabs-mode: " (prin1-to-string indent-tabs-mode) "; " + "tab-width: " (prin1-to-string tab-width) "; " + "-*-")) + (save-excursion + (beginning-of-buffer) + (insert final) + (comment-region (point-at-bol) (point-at-eol)) + (newline))))) + +; Helper for qt-open-header, for Qt 4. Opens a file if it says #include "../foo/bar.h", +; close it and open that file instead; recursively until finding a real file. +(defun qt-follow-includes (file) + (let ((line "") + (begin nil) + (buffer nil)) + (find-file file) + (goto-char 0) + (if (looking-at "#include \"") + (progn + (forward-char 10) + (setq begin (point)) + (re-search-forward "\"" nil t) + (backward-char 1) + (setq file (buffer-substring begin (point))) + (setq buffer (current-buffer)) + (qt-follow-includes file) + (kill-buffer buffer) + ) + ; else: this is the right file, skip the comments and go to the class + (progn + (re-search-forward "^class" nil t) + (beginning-of-line)) + ))) + +(defun qt-open-header () + "Open the Qt header file for the class under point" + (interactive) + (let* ((qtinc (concat (getenv "QTDIR") "/include/")) + (class (thing-at-point 'word)) + (f nil) + (file nil) + (files nil) + ) + (save-excursion + ; The Qt3 case: the includes are directly in $QTDIR/include/, lowercased + (setq f (concat qtinc (downcase class) ".h" )) + (if (file-readable-p f) + (setq file f) + ; For some Qt3/e classes: add _qws + (setq f (concat qtinc (downcase class) "_qws.h" )) + (if (file-readable-p f) + (setq file f) + ; The Qt4 case: the includes are in $QTDIR/include/QSomething/, in original case + (setq files (directory-files qtinc t nil "dirsonly")) + (dolist (f files nil) + (if (file-readable-p (concat f "/" class) ) + (setq file (concat f "/" class)))) + )) + (and file + (qt-follow-includes file)) + ) + )) + +(provide 'kde-emacs-utils) diff --git a/scripts/kde-emacs/kde-emacs-vars.el b/scripts/kde-emacs/kde-emacs-vars.el new file mode 100644 index 00000000..216e64f5 --- /dev/null +++ b/scripts/kde-emacs/kde-emacs-vars.el @@ -0,0 +1,147 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; kde-emacs-vars.el ;; +;; ;; +;; Copyright (C) 2002 Zack Rusin ;; +;; ;; +;; This program is free software; you can redistribute it and/or ;; +;; modify it under the terms of the GNU General Public License ;; +;; as published by the Free Software Foundation; either version 2 ;; +;; of the License, or (at your option) any later version. ;; +;; ;; +;; This program is distributed in the hope that it will be useful, ;; +;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; +;; GNU General Public License for more details. ;; +;; ;; +;; You should have received a copy of the GNU General Public License ;; +;; along with this program; if not, write to the Free Software ;; +;; Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA ;; +;; 02110-1301, USA. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defconst kde-emacs-version "0.2" + "KDE Emacs package version number.") +(defun kde-emacs-version () + "Returns the version of KDE Emacs package." + (interactive) + (message "KDE Emacs version : %s" kde-emacs-version)) + + +(defvar kde-emacs-type + (eval-when-compile + (if (string-match "XEmacs" (emacs-version)) + 'xemacs + 'emacs)) + "The type of Emacs we are running on.") + +;*---------------------------------------------------------------------*/ +;* Constants ... */ +;*---------------------------------------------------------------------*/ + +(defconst kde-access-labels + "\\<\\(signals\\|k_dcop\\|\\(public\\|protected\\|private\\)\\([ ]+slots\\)?\\)\\>:" + "KDE specific access labels regexp.") + +(defconst kde-source-files '("cpp" "cc" "cxx" "CC" "C" "c") + "List of source-file extensions.") + +(defconst kde-header-files '("h" "H" "hh" "hxx" "hpp") + "List of header-file extensions.") + +;*---------------------------------------------------------------------*/ +;* Group ... */ +;*---------------------------------------------------------------------*/ +(defgroup kde-devel nil + "Development utilities." + :tag "KDE devel" + :prefix "kdedevel-" + :group 'programming) + +(defcustom kde-full-name (or user-full-name + (getenv "USER") + "Your Name") + "*Name used by kde-emacs." + :group 'kde-devel + :version "0.1" + :type 'string) + +(defcustom kde-email (or user-mail-address + (concat (getenv "LOGNAME") "@" (getenv "HOSTNAME")) + "Your Email") + "*Email address used by kde-emacs." + :group 'kde-devel + :version "0.1" + :type 'string) + +(defcustom kde-cvs-root (concat (getenv "HOME") "/cvs/kde") + "*Root Directory of KDE CVS Respiratory" + :group 'kde-devel + :type 'string) + +(defcustom magic-keys-mode 't + "Set this variable to true to have some special keybindings. E.g. bind '(' to a function which inserts '( ' when appropriate..." + :group 'kde-devel + :type 'boolean) + +(defcustom kde-emacs-make "make" + "Specifies the make command which KDE Emacs will use" + :group 'kde-devel + :type 'string) + +;;Make styles a list of the format (radio (const kde-c++) (const kde-c) style) +;;and assign it to type. +(defcustom kde-c++-style "kde-c++" + "Set this variable to the CC Mode style you would like loaded when you open a C++ KDE source code file..." + :group 'kde-devel + :type 'string) + +(defcustom kde-c-style "kde-c" + "Set this variable to the CC Mode style you would like loaded when you open a C KDE source code file..." + :group 'kde-devel + :type 'string) + +(defcustom kde-use-pc-select 't + "Set this to nil if you really hate PC Select Mode..." + :group 'kde-devel + :type 'boolean) + +(defcustom kde-emacs-newline-semicolon nil + "Set this to true to have typing \";\" automatically insert +a newline." + :group 'kde-devel + :type 'boolean) + +(defcustom kde-header-protection-parts-to-show 1 + "Set this variable to the number of parts from the file name you want to be used for the defined word in the +header-protection function.. E.g. setting this to 3 makes header-protection define KIG_MISC_NEWTYPE_H for a +file named /home/domi/src/kdenonbeta/kig/misc/newtype.h" + :group 'kde-devel + :type 'integer) + +(defcustom kde-emacs-after-parent-string " " + "Set this to whatever you want to have inserted after the first parenthesis. Works only if +magic-keys-mode is set to true. " + :group 'kde-devel + :type 'string) + +(defcustom kde-include-directory nil + "Set this to the directory holding the includes for the current module/project/whatever." + :group 'kde-devel + :type 'string) + +(defcustom kde-source-directory nil + "Set this to the directory holding the sources for the current module/project/whatever." + :group 'kde-devel + :type 'string) + +(defcustom kde-make-member-default-impl " \n" + "Default implementation added by agulbra-make-member. FUNCTION gets replaced by the full signature of the function/method." + :group 'kde-devel + :type 'string) + +; a grep in the part of kde-source I have gives: +; 5579 files uses .cpp, 1402 uses .cc, 10 uses .cxx, and 1 uses .C +(defconst kde-prefered-source-extension "cpp" + "Source extension which kde-* functions should use for creating new files.") + +(provide 'kde-emacs-vars) diff --git a/scripts/kde-emacs/kde-emacs.el b/scripts/kde-emacs/kde-emacs.el new file mode 100644 index 00000000..b2865c53 --- /dev/null +++ b/scripts/kde-emacs/kde-emacs.el @@ -0,0 +1,66 @@ +;; kde-emacs.el +;; Time-stamp: <2002-06-26 00:49:48 zack> +;; +;; Copyright (C) 2002 Zack Rusin +;; +;; This library is free software; you can redistribute it and/or +;; modify it under the terms of the GNU Lesser General Public +;; License as published by the Free Software Foundation; either +;; version 2.1 of the License, or (at your option) any later version. +;; +;; This library is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; Lesser General Public License for more details. +;; +;; You should have received a copy of the GNU Lesser General Public +;; License along with this library; if not, write to the Free Software +;; Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA +;; 02110-1301 USA + +;;; Installation: +;; +;; Put the following lines in your ".emacs": +;; (add-to-list 'load-path "~/path-to-kde-emacs") +;; (require 'kde-emacs) +;; +;; I also strongly recommend to add the following two lines to +;; .emacs file: +;; (setq kde-full-name "Your Name") +;; (setq kde-email "Your Email") +;; +;; You may want to byte-compile the package to speed it up +;; a bit. To do it in the *scratch* buffer type in the following +;; line: +;; (byte-recompile-directory "~/kde-emacs" t) +;; place the cursor after the closing paren and hit "Ctrl-x Ctrl-e", +;; that's it. +;; +;; All keybindings are in kde-emacs-bindings.el, look at/customize +;; this file before byte-compiling the package! +;; If you want to see things you can customize type: +;; M-x customize-group +;; and type in "kde-devel" group. +;; +;; TODO: +;; - in (if kde-emacs-type... change direct function calls +;; to funcall's +;; + +(require 'cc-mode) ;; needed by kde-emacs-core's test on c-version + +(require 'kde-emacs-compat) +(require 'kde-emacs-core) +(require 'kde-emacs-general) +(require 'klaralv) +(require 'kde-emacs-utils) +(require 'dirvars) + +;; load this only if semantic package is present +(when (featurep 'semantic) + (require 'kde-emacs-semantic) + (require 'kde-emacs-doc)) + +(require 'kde-emacs-bindings) + +(provide 'kde-emacs) diff --git a/scripts/kde-emacs/klaralv.el b/scripts/kde-emacs/klaralv.el new file mode 100644 index 00000000..df29ff78 --- /dev/null +++ b/scripts/kde-emacs/klaralv.el @@ -0,0 +1,422 @@ +;; ------------------------------ COPYRIGHT NOTICE ------------------------------ +;; klaralv.el version 1.3 +;; Copyright Klaralvdalens Datakonsult AB. +;; +;; This program is free software; you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published by the Free +;; Software Foundation; either version 2 of the License, or (at your option) +;; any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +;; for more details. +;; +;; You should have received a copy of the GNU General Public License along +;; with GNU Emacs. If you did not, write to the Free Software Foundation, +;; Inc., 675 Mass Ave., Cambridge, MA 02139, USA. + + +;; ------------------------------ INSTALLATION ------------------------------ +;; To use this file, add the current directory to your load path. +;; you do this by inserting something like the following to your .emacs: +;; (setq load-path (cons "/home/blackie/Emacs/" load-path)) +;; +;; Next insert the following line into your .emacs +;; (require 'klaralv) +;; (global-set-key [(f5)] 'kdab-insert-header) +;; (global-set-key [(shift f5)] 'kdab-insert-forward-decl) +;; (setq kdab-qt-documentation "file://usr/local/qt/html/doc/XXX.html") +;; (global-set-key [(control f5)] 'kdab-lookup-qt-documentation) +;; +;; If you use QTopia, and do not want include files to be prefixed with qpe/, +;; as in qpe/qpeapplication, then insert the following code in your setup +;; (setq kdab-prefix-qpe nil) + +;; ------------------------------ CONFIGURATION ------------------------------ +(defvar kdab-qt-documentation + "http://doc.trolltech.com/3.0/XXX.html" + "URL for Qt documentation. XXX must be in the string. + Example: file://packages/kde-src/qt-copy/doc/html/XXX.html") + +(defvar kdab-qpe-documentation + "file://opt/qtopia/doc/XXX.html" + "URL for QTopia documentatin. XXX must be in the string. + Example: file:/opt/qtopia/doc/XXX.html") + + +(defvar kdab-prefix-qpe 't + "set this to nil if you do not want QPE header files prefixed with qpe/") + +;; special case for include files +;; Please notify blackie@klaralvdalens-datakonsult.se with any modification to this variable! +(defvar kdab-special-includes + '( + (qlayout.h QHBoxLayout QVBoxLayout QGridLayout QBoxLayout) + (qlistview.h QListViewItem QCheckListItem QListViewItemIterator) + (qiconview.h QIconViewItem QIconDragItem QIconDrag) + (qdragobject.h QTextDrag QStoredDrag QUriDag QColorDrag QImageDrag QDragManager) + (qmime.h QMimeSource QMimeSourceFactory QWindowsMime) + (qptrlist.h QPtrListIterator) + (qevent.h QTimerEvent QMouseEvent QWheelEvent QTabletEvent QKeyEvent + QFocusEvent QPaintEvent QMoveEvent QResizeEvent QCloseEvent + QShowEvent QHideEvent QContextMenuEvent QIMEvent QDropEvent + QDragMoveEvent QDragEnterEvent QDragResponseEvent QDragLeaveEvent + QChildEvent QCustomEvent) + (qdatetime.h QTime QDateTime QDate) + (qdatetimeedit.h QTimeEdit QDateTimeEditBase QDateEdit QDateTimeEdit) + (qcstring.h QByteArray) + (qobjectlist.h QObjectListIt QObjectListIterator) + (qwidgetlist.h QWidgetListIt) + (qtabbar.h QTab) + (qpalette.h QColorGroup) + (qaction.h QActionGroup) + (qvalidator.h QIntValidator QDoubleValidator QRegExpValidator) + (qlistbox.h QListBoxItem QListBoxText QListBoxPixmap) + (qstring.h QChar QCharRef QConstString) + (qcanvas.h QCanvasSprite QCanvasPolygonalItem QCanvasRectangle + QCanvasPolygon QCanvasEllipse QCanvasText QCanvasLine + QCanvasChunk QCanvas QCanvasItem QCanvasView QCanvasPixmap) + (qgl.h QGLFormat QGL QGLContext QGLWidget QGLColormap) + (qtable.h QTableSelection QTableItem QComboTableItem QCheckTableItem) + (qsqlfield.h QSqlField QSqlFieldInfo) + (qsqlrecord.h QSqlRecord QSqlRecordInfo) + + ; Qt/Embedded + (qcopchannel_qws.h QCopChannel) + (qdirectpainter_qws.h QDirectPainter) + (qfontfactorybdf_qws.h QFontFactoryBDF) + (qfontfactoryttf_qws.h QFontFactoryFT) + (qfontmanager_qws.h QGlyphMetrics QGlyph QRenderedFont QDiskFont QFontManager QFontFactory) + (qgfx_qws.h QScreenCursor QPoolEntry QScreen QGfx) + (qgfxlinuxfb_qws.h QLinuxFbScreen) + (qgfxmatroxdefs_qws.h QQnxFbGfx QQnxScreen) + (qgfxraster_qws.h QGfxRasterBase QGfxRaster) + (qgfxvnc_qws.h QRfbRect QRfbPixelFormat QRfbServerInit QRfbSetEncodings + QRfbFrameBufferUpdateRequest QRfbKeyEvent QRfbPointerEvent QRfbClientCutText QVNCServer) + (qkeyboard_qws.h QWSKeyboardHandler) + (qlock_qws.h QLock QLockHolder) + (qmemorymanager_qws.h QMemoryManagerPixmap QMemoryManager) + (qsoundqss_qws.h QWSSoundServer QWSSoundClient QWSSoundServerClient QWSSoundServerSocket) + (qwindowsystem_qws.h QWSInternalWindowInfo QWSScreenSaver QWSWindow QWSSoundServer + QWSServer QWSServer KeyboardFilter QWSClient) + (qwsbeosdecoration_qws.h QWSBeOSDecoration) + (qwscursor_qws.h QWSCursor) + (qwsdecoration_qws.h QWSDecoration) + (qwsdefaultdecoration_qws.h QWSDefaultDecoration) + (qwsdisplay_qws.h QWSWindowInfo QWSDisplay) + (qwshydrodecoration_qws.h QWSHydroDecoration) + (qwskde2decoration_qws.h QWSKDE2Decoration) + (qwskdedecoration_qws.h QWSKDEDecoration) + (qwsmanager_qws.h QWSManager QWSButton) + (qwsmouse_qws.h QWSPointerCalibrationData QWSMouseHandler QCalibratedMouseHandler + QAutoMouseHandlerPrivate QWSMouseHandlerPrivate QVrTPanelHandlerPrivate + QTPanelHandlerPrivate QYopyTPanelHandlerPrivate QCustomTPanelHandlerPrivate + QVFbMouseHandlerPrivate) + (qwsproperty_qws.h QWSPropertyManager) + (qwsregionmanager_qws.h QWSRegionManager) + (qwssocket_qws.h QWSSocket QWSServerSocket) + (qwswindowsdecoration_qws.h QWSWindowsDecoration) + (qstatusbar.h statusBar()) + + ; KDE + (kdebug.h kdDebug kdWarning kdError kdFatal kdBacktrace) + (kconfig.h KConfigGroup) + (kiconloader.h BarIcon SmallIcon DesktopIcon KIcon) + (kicondialog.h KIconCanvas KIconButton) + (knuminput.h KDoubleNumInput KIntNumInput) + + ; KDGear - http://www.klaralvdalens-datakonsult.se + (KDCheckableGroupBox.h KDCheckableGroupBox) + (KDCheckableHGroupBox.h KDCheckableHGroupBox) + (KDCheckableVGroupBox.h KDCheckableVGroupBox) + (KDCloseableWidget.h KDCloseableWidget) + (KDConfigDialog.h KDConfigDialog) + (KDConfigWidget.h KDConfigWidget) + (KDDateWidget.h KDDateWidget KDDateTimeWidget) + (KDDirMonitor.h KDDirMonitor) + (KDGridWidget.h KDGridWidget) + (KDListBoxPair.h KDListBoxPair) + (KDMinimizeSplitter.h KDMinimizeSplitter) + (KDSearchableListBox.h KDSearchableListBox) + (KDSemiSizingControl.h KDSemiSizingControl) + (KDShowHideTableControl.h KDShowHideTableControl) + (KDSimpleSizingControl.h KDSimpleSizingControl) + (KDSizingControl.h KDSizingControl) + (KDStream.h KDStream) + (KDTimeWidget.h KDTimeWidget) + + ; KDChart - http://www.klaralvdalens-datakonsult.se + (KDChart.h KDChart) + (KDChartAxisParams.h KDChartAxisParams) + (KDChartBaseSeries.h KDChartBaseSeries) + (KDChartCustomBox.h KDChartCustomBox) + (KDChartData.h KDChartData) + (KDChartEnums.h KDChartEnums) + (KDChartListTable.h KDChartListTableData KDChartListTablePrivate) + (KDChartNotEnoughSpaceException.h KDChartNotEnoughSpaceException) + (KDChartPainter.h KDChartPainter) + (KDChartParams.h KDChartFrameSettings KDChartParams ModeAndChart) + (KDChartPlaneSeries.h KDChartPlaneSeries) + (KDChartPropertySet.h KDChartPropertySet) + (KDChartSeriesCollection.h KDChartSeriesCollection) + (KDChartTable.h KDChartTableData) + (KDChartTableBase.h KDChartTableDataBase) + (KDChartTextPiece.h KDChartTextPiece) + (KDChartUnknownTypeException.h KDChartUnknownTypeException) + (KDChartVectorSeries.h KDChartVectorSeries) + (KDChartVectorTable.h KDChartVectorTableData KDChartVectorTablePrivate) + (KDChartWidget.h KDChartWidget) + (KDFrame.h KDFrame KDFrameCorner) + (KDFrameProfileSection.h KDFrameProfileSection) + + + ; Useful fake entries + (qapplication.h qApp) + (kapplication.h kapp) + (klocale.h i18n I18N_NOOP) + (kstandarddirs.h locate locateLocal) + (stdlib.h getenv) + (unistd.h unlink sleep usleep) + (iostream cout cerr) + (ctype.h isalnum isalpha isascii isblank iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit) + (qeventloop.h eventloop) + + ) + "List of special include files which do not follow the normal scheme") + +(defvar kdab-qpe-includes + '( + (alarmserver.h AlarmServer) + (applnk.h AppLnk DocLnk AppLnkSet DocLnkSet) + (calendar.h Calendar) + (categories.h CategoryGroup CategoryGroup Categories CheckedListView) + (categorymenu.h CategoryMenu) + (categoryselect.h CategoryCombo CategorySelect CategoryEdit CategoryWidget) + (config.h Config) + (contact.h Contact) + (database.h QWSDatabase DatabaseDefaultView Database DatabaseView DatabaseDefaultView) + (datebookdb.h DateBookDB) + (datebookmonth.h DateBookMonthHeader DayItemMonth DateBookMonthTable DateBookMonth DateButton) + (event.h Event EffectiveEvent EffectiveEventSizeSorter EffectiveEventTimeSorter) + (filemanager.h FileManager) + (fileselector.h FileSelectorItem FileSelector) + (finddialog.h FindDialog) + (fontdatabase.h FontDatabase) + (fontmanager.h FontManager) + (global.h Global) + (imageedit.h ImageEdit) + (inputmethodinterface.h InputMethodInterface) + (ir.h Ir) + (lightstyle.h LightStyle) + (lnkproperties.h LnkProperties) + (mediaplayerplugininterface.h MediaPlayerDecoder) + (menubutton.h MenuButton) + (mimetype.h MimeType) + (network.h Network) + (palmtoprecord.h Record) + (palmtopuidgen.h UidGen) + (password.h Password) + (power.h PowerStatus PowerStatusManager ) + (process.h Process) + (qcopenvelope_qws.h QCopEnvelope) + (qdawg.h QDawg) + (qlibrary.h QLibrary) + (qpeapplication.h QPEApplication) + (qpedecoration_qws.h QPEDecoration QPEManager) + (qpedialog.h QPEDialogListener) + (qpemenubar.h QPEMenuToolFocusManager QPEMenuBar) + (qpemessagebox.h QPEMessageBox) + (qpestyle.h QPEStyle : public QWindowsStyle) + (qpetoolbar.h QPEToolBar) + (record.h Record) + (resource.h Resource) + (sound.h Sound) + (storage.h StorageInfo FileSystem) + (task.h Task) + (timeconversion.h TimeConversion) + (timestring.h DateFormat TimeString) + (tzselect.h TZCombo TimeZoneSelector) + )) + +;; ------------------------------ SOURCE CODE ------------------------------ + +;; Merge in qpe classes +(defun kdab-get-special-include-list () + (let (elm header classes (list kdab-qpe-includes) filename (result kdab-special-includes)) + (while list + (setq elm (car list)) + (setq list (cdr list)) + (setq filename (concat (if kdab-prefix-qpe "qpe/" "") (symbol-name (car elm)))) + (setq result (cons (cons (intern filename) (cdr elm)) result))) + result)) + +;; Lookup class `cls' in kdab-special-includes and return the associate include file name +(defun kdab-map-special (cls) + (let ((list (kdab-get-special-include-list)) + (found nil)) + (while (and list (not found)) + (let* ( (elm (car list)) + (include-file (car elm)) + (classes (cdr elm))) + ( while (and classes (not found)) + (if (string= (downcase cls) (downcase (symbol-name (car classes)))) + (setq found include-file) + (setq classes (cdr classes))))) + (setq list (cdr list))) + (if found + (symbol-name found) + nil) ; return value + )) + + + +;-------------------------------------------------------------------------------- +; Insert include file for Qt program. +; Place point anywhere on a Qt class, and invoke this function. A result of +; this is that an include line is added (if it does not already exists) for +; the given class. +;-------------------------------------------------------------------------------- +(defun kdab-insert-header ( prefix ) + "Insert include file for class at point" + (interactive "P") + (save-excursion + (let* ((word-at-point (if prefix + (read-from-minibuffer "Class: ") + (current-word)))) + (kdab-insert-header-non-interactive word-at-point)))) + +;-------------------------------------------------------------------------------- +; insert include file for `word-with-case' non-interactively. +; for an interactive version see kdab-insert-header +;-------------------------------------------------------------------------------- +(defun kdab-insert-header-non-interactive (word-with-case) + (save-excursion + (let* ((word (downcase word-with-case)) + (special-header (cond + ((kdab-map-special word) (kdab-map-special word)) + ((string-match "^qdom" word) "qdom.h") + ((string-match "^qxml" word) "qxml.h") + (t (concat word ".h")))) + header is-local) + + + ;; decide on the header file. + (if (file-exists-p (concat word-with-case ".h")) + (progn ; file exists in given case in pwd. + (setq header (concat word-with-case ".h")) + (setq is-local 't)) + (if (file-exists-p (concat word ".h")) ; file exists in lowercase in pwd + (progn + (setq header (concat word ".h")) + (setq is-local 't)) + (progn ; header in <..> path + (setq header special-header) + (setq is-local nil)))) + + (kdab-insert-include-file header is-local t)))) + +;-------------------------------------------------------------------------------- +; Insert header file for header. If is-local insert it with "" +; otherwise insert it with <> +;-------------------------------------------------------------------------------- +(defun kdab-insert-include-file (header is-local show-message) + (let ((include-file (if is-local + (concat "#include \"" header "\"") + (concat "#include <" header ">")))) + + (beginning-of-buffer) + (if (re-search-forward (concat "^ *// *\\(#include *[<\"][ \t]*" header "[ \t]*[>\"]\\)") nil t) + (progn + (replace-match "\\1") + (when show-message + (message (concat "commented in #include for " header)))) + + (if (not (re-search-forward (concat "#include *[\"<][ \t]*" header "[ \t]*[\">]") nil t)) + (progn + ; No include existed + (goto-char (point-max)) ; Using end-of-buffer makes point move, despite save-excursion + (if (not (re-search-backward "^#include *[\"<][^\">]+\.h *[\">]" nil t)) + (beginning-of-buffer) + (progn (end-of-line) (forward-char 1))) + + ;; Now insert the header + (insert (concat include-file "\n")) + (when show-message + (message (concat "inserted " include-file)))) + (when show-message + (message (concat "header file \"" header "\" is already included"))))))) + + + +;---------------------------------------------------------------------------- +; Insert a forward declaration for a Qt class. +; Place point anywhere on a Qt class, and invoke this function. A +; result of this is that a forward declaration line is added (if it does +; not already exist) for the given class. +;---------------------------------------------------------------------------- +(defun kdab-insert-forward-decl ( prefix ) + (interactive "P") + (save-excursion + (let* ((word (if prefix (read-from-minibuffer "Class: ") + (current-word)))) + (beginning-of-buffer) + (if (re-search-forward (concat "^ *// *\\(class *" word ";\\)") nil t) + (progn + (replace-match "\\1") + (message (concat "commented in forward declaration for " word))) + + (if (not (re-search-forward (concat "class *" word ";") nil t)) + (progn + ; No forward decl existed + ; Look for other forward declarations and insert this one before them + ; (this avoids finding class Private; inside a class, or other stuff in the middle of the file) + (if (re-search-forward "^[ \t]*class .*;" nil t) + (progn + ; Exit namespace foo { class bar; } if necessary + ; This is a modified version of (backward-up-list) which doesn't + ; throw an error when not found. + (goto-char (or (scan-lists (point) -1 1 nil t) (point))) ; ### do multiple times if necessary + (re-search-backward "^[ \t]*namespace " nil t) ; in case of namespace foo\n{ + (beginning-of-line)) + ; No forward declarations found, lets search for include lines. + ; For those we start from the end, because we want to leave file.h first. + (progn (goto-char (point-max)) + (if (re-search-backward "#include" nil t) + (progn (end-of-line) (forward-char 1)) + (beginning-of-buffer)))) + + (progn + (insert "class " word ";\n") + (message (concat "inserted class " word ";")))) + (message (concat "forward decl for \"" word "\" already exists"))))))) + + +(defun is-qpe-class (class) + (let ((list kdab-qpe-includes) classes (found nil)) + (while (and (not found) list) + (setq classes (cdr (car list))) + (while classes + (if (string= (downcase (symbol-name (car classes))) (downcase class)) + (setq found 't)) + (setq classes (cdr classes))) + (setq list (cdr list))) + found)) + +;-------------------------------------------------------------------------------- +; Start konqueror with documentation for the class under point. +; set `kdab-qt-documentation' and `kdab-qpe-documentation' +; to specify the replacement for the documentation +;-------------------------------------------------------------------------------- +(defun kdab-lookup-qt-documentation () + (interactive "") + (save-excursion + (let* ((word (downcase (current-word))) + (doc (if (is-qpe-class word) kdab-qpe-documentation kdab-qt-documentation)) + (url (if (not (string-match "XXX" doc)) + (error "didn't find three X's in kdab-qt-documentation or kdab-qpe-documentation") + (replace-match word t t doc)))) + (start-process "qt documentation" nil "kfmclient" "openURL" url) + (message (concat "Loading " url))))) + +(provide 'klaralv) diff --git a/scripts/kde-spellcheck.pl b/scripts/kde-spellcheck.pl new file mode 100755 index 00000000..2a8974e0 --- /dev/null +++ b/scripts/kde-spellcheck.pl @@ -0,0 +1,1537 @@ +#! /usr/bin/env perl + +# CORRECTIONS GO IN THE __DATA__ SECTION AT THE END OF THIS SCRIPT + +# Checks and corrects common spelling errors in text files - code +# derived from kde-spellcheck.pl (Dirk Mueller ) +# +# Copyright (c) 2004 Richard Evans +# +# License: LGPL 2.0 +# +# 2004-05-14: Richard Evans +# +# Initial revision differs from kde-spellcheck.pl as follows: +# +# Text file detection no longer spawns external program. +# No longer checks cwd if arguments omitted - just specify '.' +# No longer recurses through sub directories without --recurse. +# Can now check internal dictionary for mistakes using aspell. +# Changes are not made unless --make-changes is specified. +# File modification now uses an intermediate file to reduce the +# chance of data loss. +# Fixed bug that missed words with apostrophes. +# Removed the code checking for "nonword misspelling" - I don't +# believe it was doing anything useful, but please correct me +# if that's not the case! +# Corrected some dictionary entries. +# Runs much, much faster. + +sub usage +{ + warn <<"EOF"; + +kde-spellcheck.pl [flags] filenames/directories + +This script checks for, and optionally replaces, commonly misspelled words +with the correct US English equivalents. The behaviour has changed from +kde-spellcheck.pl - to check subdirectories you must specify --recurse, +omitting arguments does not check the current directory, and changes are +not made to files unless you specify --make-changes + +CAUTION IS NEEDED WHEN USING THIS SCRIPT - changes are made to the original +file and are not programming language syntax aware - this is why the script +only suggests the changes to be made unless --make-changes is specified. + +Hidden files, CVS directories, .desktop, and .moc files are excluded +from checking. + +--check-dictionary : Checks the internal dictionary for potential + spelling mistakes - requires aspell with a US + English dictionary, and Text::Aspell installed. + +--suggest-corrections : Behaves as --check-dictionary, but also suggests + spelling corrections. + +--recurse : Check subdirectories recursively. +--quiet : Disable informational output (not recommended). +--make-changes : Displays the changes that would have been made. +--help|? : Display this summary. + +EOF + + exit; +} + +use strict; +use warnings; +use Getopt::Long; +use File::Temp qw( tempfile ); +use File::Copy qw( copy ); + +my $DICTIONARY = build_dictionary_lookup_table(); + +########################################################################################### +# Add options here as necessary - perldoc Getopt::Long for details on GetOptions + +die "kde-spellcheck2 --help for usage\n" + unless GetOptions ( "check-dictionary" => \my $opt_check_dictionary, + "suggest-corrections" => \my $opt_suggest_corrections, + "quiet" => \my $opt_quiet, + "make-changes" => \my $opt_make_changes, + "recurse" => \my $opt_recurse, + "help|?" => \&usage ); + +check_dictionary($opt_suggest_corrections) if $opt_suggest_corrections or $opt_check_dictionary; + +usage() unless @ARGV; + +require File::MMagic; + +my $MIME = File::MMagic->new; + +my @dirqueue; + +$opt_quiet = 0 unless $opt_make_changes; + +sub info; *info = $opt_quiet ? sub {} : sub { print @_ }; + +for ( @ARGV ) +{ + if ( -f ) { check_file($_) } + elsif ( -d _ ) { push @dirqueue, $_ } + else { warn "Unknown: '$_' is neither a directory or file" } +} + +my $dir; + +process_directory($dir) while $dir = pop @dirqueue; + +$opt_make_changes or print <; + + close $fh or warn "Failed to close: '$filename': $!"; + + my $original; + my $line_no = 0; + + for ( @contents ) + { + $line_no++; + $original = $_ unless $opt_make_changes; + + for my $word ( split /[^\w']/ ) # \W would split on apostrophe + { + next unless defined (my $correction = $DICTIONARY->{$word}); + + $file_modified ||= 1; + + s/\b$word\b/$correction/g; + + info "$filename ($line_no): $word => $correction\n"; + } + + print "FROM: $original", + " TO: $_\n" if !$opt_make_changes and $_ ne $original; + } + + return unless $file_modified; + return unless $opt_make_changes; + + info "Correcting: $filename\n"; + + my ($tmp_fh, $tmp_filename) = tempfile(UNLINK => 0); + + eval + { + print $tmp_fh @contents or die "Write"; + + $tmp_fh->flush or die "Flush"; + $tmp_fh->seek(0, 0) or die "Seek"; + }; + + die "$@ failed on: '$tmp_filename': $!" if $@; + + copy($tmp_fh, $filename) or die "Failed to copy: $tmp_filename => $filename: $!\n", + "You can manually restore from: $tmp_filename"; + + close $tmp_fh or warn "Close failed on: '$tmp_filename': $!"; + unlink $tmp_filename or warn "Unlink failed on: '$tmp_filename': $!"; +} + + +# Could be more robustly rewitten with File::Find / File::Find::Rules etc + +sub process_directory +{ + my $directory = shift; + + info "Processing directory: $directory\n"; + + opendir my $dh, $directory or die "Failed to read dir: '$directory': $!"; + + while ( my $entry = readdir($dh) ) + { + if ( $entry =~ /^\./ or + $entry =~ /\.desktop$/ or + $entry =~ /\.moc$/ or + $entry eq "CVS" ) + { + info "Skipping excluded file or directory: $entry\n"; + next; + } + + my $file = "$directory/$entry"; + + if ( -d $file ) + { + push(@dirqueue, $file) if $opt_recurse; + next; + } + + next unless -f _; + + # First use perl's heuristic check to discard files as quickly as possible... + + unless ( -T _ ) + { + info "Skipping binary file: $file\n"; + next; + } + + # ...it's not always good enough though, so now check the Mimetype + + unless ( (my $type = $MIME->checktype_filename($file)) =~ /text/i ) + { + info "Skipping $type file: $file\n"; + next; + } + + check_file($file); + } + + closedir $dh or warn "Failed to close dir: '$directory': $!"; +} + + +######################################################################################################## + +sub check_dictionary +{ + my $suggest_corrections = shift; + + print <new or die "Failed to create Text::Aspell instance"; + + # Despite the docs, set_option doesnt seem to return undef on error ... + + $speller->set_option('lang','en_US') + or die "Text::Aspell failed to select language: 'en_US'", $speller->errstr; + + # ... so try a very simple check + + unless ( $speller->check('color') ) + { + warn "You dont appear to have the en_US dictionary installed - cannot check"; + exit; + } + + print "Checking Lexicon for identical misspelling and corrections:\n"; + + while ( my($key, $value) = each %$DICTIONARY ) + { + print "\n$key" if $key eq $value; + } + + print "\n\nChecking Lexicon for possible misspellings:\n\n"; + + for my $word ( values %$DICTIONARY ) + { + next if $speller->check($word); + + print "$word\n"; + print join(", ", $speller->suggest($word)), "\n\n" if $suggest_corrections; + } + + print "\n"; + + exit; +} + + +######################################################################################################## + +sub build_dictionary_lookup_table +{ + my %hash; + + while () + { + next if /^\s*$/ or /^\s*#/; # Skip blank lines and comments + + next unless /^\s*"([^"]+)"\s+(.*)\s*$/ or /^\s*(\S+)\s+(.*)\s*$/; + + if ( $1 eq $2 ) + { + warn "WARNING: Ignoring identical misspelling and correction: '$1' in __DATA__ offset line $.\n"; + next; + } + + $hash{$1} = $2; + } + + return \%hash; +} + +__DATA__ + +#INCORRECT SPELLING CORRECTION + +aasumes assumes +abailable available +Abbrevation Abbreviation +abbrevations abbreviations +abbriviate abbreviate +abbriviation abbreviation +abilties abilities +Ablolute Absolute +abreviate abbreviate +acces access +accesible accessible +accesing accessing +accomodate accommodate +accross across +Acess Access +achive achieve +achived achieved +achiving achieving +acknoledged acknowledged +acknowledgement acknowledgment +Acknowledgements Acknowledgments +Acknowlege Acknowledge +acommodate accommodate +aconyms acronyms +acording according +acount account +acouting accounting +activ active +actons actions +acually actually +adapater adapter +adatper adapter +addded added +adddress address +Additinoally Additionally +additionaly additionally +Additionaly Additionally +additionnal additional +additonal additional + +#INCORRECT SPELLING CORRECTION + +Addtional Additional +aditional additional +adminstrator administrator +Adminstrator Administrator +adress address +Adress Address +adressed addressed +adresses addresses +advertize advertise +aesthetic esthetic +Afganistan Afghanistan +agressive aggressive +Agressive Aggressive +agressively aggressively +alignement alignment +alligned aligned +Allignment Alignment +allmost almost +allready already +allways always +Allways Always +alook a look +alot a lot +alows allows +alrady already +alreay already +alternativly alternatively +ammount amount +Ammount Amount +analagous analogous +analizer analyzer +analogue analog +analyse analyze +analyses analyzes +anfer after +angainst against +annoucement announcement +announcments announcements +anwer answer +anwser answer +anwsers answers +aplication application +appeareance appearance +appearence appearance +appeares appears +apperarance appearance +appers appears + +#INCORRECT SPELLING CORRECTION + +applicaiton application +Applicalble Applicable +appliction application +appplication application +approciated appreciated +appropiate appropriate +approriate appropriate +approximatly approximately +apropriate appropriate +aquire acquire +aquired acquired +arbitarily arbitrarily +arbitary arbitrary +Arbitary Arbitrary +aribrary arbitrary +aribtrarily arbitrarily +aribtrary arbitrary +arround around +assosciated associated +assosiated associated +assoziated associated +asssembler assembler +assumend assumed +asume assume +asynchonous asynchronous +asyncronous asynchronous +aticles articles +atleast at least +atomicly atomically +attatchment attachment +auhor author +authentification authentication +authoratative authoritative +authorisations authorizations +automaticaly automatically +Automaticaly Automatically +automaticly automatically +autoreplacment autoreplacement +auxilary auxiliary +Auxilary Auxiliary +avaible available +Avaible Available +availble available +availibility availability +availible available +Availible Available +avaliable available +avaluate evaluate +avare aware +aviable available +backrefences backreferences +baloon balloon +basicly basically + +#INCORRECT SPELLING CORRECTION + +Basicly Basically +beautifull beautiful +becuase because +beeep beep +beeing being +beexported be exported +befor before +beggining beginning +begining beginning +behaviour behavior +Behaviour Behavior +Belarussian Belarusian +beteen between +betrween between +betweeen between +Blueish Bluish +bofore before +botton bottom +boudaries boundaries +boundries boundaries +boundry boundary +boxs boxes +bruning burning +buton button +Buxfixes Bugfixes +cacheing caching +calulation calculation +cancelation cancellation +cancelled canceled +cancelling canceling +capabilites capabilities +caracters characters +cataloge catalog +Cataloge Catalog +catalogue catalog +catched caught +ceneration generation +centralised centralized +centre center +Centre Center +changable changeable +chaning changing +characers characters +charachters characters +Characteres Characters +charakters characters +charater character +Chatacter Character +chatwindow chat window +childs children +choosed chose +choosen chosen +Choosen Chosen + +#INCORRECT SPELLING CORRECTION + +chosing choosing +cirumstances circumstances +classess classes +cloumn column +Coffie Coffee +colaboration collaboration +collecion collection +collumns columns +coloum column +coloumn column +colour color +colours colors +colum column +comamnd command +comination combination +commense commence +commerical commercial +comming coming +commited committed +commiting committing +Commiting Committing +commmand command +commuication communication +communcation communication +compability compatibility +comparision comparison +Comparision Comparison +comparisions comparisons +Compatability Compatibility +compatibilty compatibility +compatiblity compatibility +Compedium Compendium +compiiled compiled +compleion completion +completly completely +complient compliant +comsumer consumer +comunication communication +concatonated concatenated +concurent concurrent +configration configuration +Configuraton Configuration +connent connect +connnection connection +consecutivly consecutively +consequtive consecutive +constuctors constructors +contactlist contact list +containg containing +contexual contextual +contigious contiguous +contingous contiguous +continouos continuous + +#INCORRECT SPELLING CORRECTION + +continous continuous +Continous Continuous +contiribute contribute +contoller controller +Contorll Control +controler controller +controling controlling +controll control +conver convert +convient convenient +convinience convenience +conviniently conveniently +coordiator coordinator +Copys Copies +coresponding corresponding +corrent correct +correponds corresponds +Costraints Constraints +Coudn't Couldn't +coursor cursor +Coverted Converted +coypright copyright +cricles circles +criticisim criticism +cryptograhy cryptography +Culculating Calculating +curren current +currenty currently +curteousy courtesy +Custimize Customize +customisation customization +customise customize +Customise Customize +customised customized +cutsom custom +cutt cut +Cutt Cut +datas data +DCOPCient DCOPClient +deactive deactivate +Deamon Daemon +debuging debugging +Debuging Debugging +decriptor descriptor +defaul default +defered deferred +Defininition Definition +defintions definitions +deleteing deleting +Demonsrative Demonstrative +Denstiy Density +depencies dependencies + +#INCORRECT SPELLING CORRECTION + +dependeds depends +dependend dependent +dependig depending +depricated deprecated +derfined defined +derivs derives +descide decide +desciptor descriptor +descryption description +desctroyed destroyed +desiabled disabled +desidered desired +desination destination +deskop desktop +desription description +Desription Description +destiantion destination +determiend determined +determins determines +detremines determines +develloped developed +developerss developers +developped developed +devided divided +devide divide +diabled disabled +diable disable +diaglostic diagnostic +dialag dialog +dialler dialer +Dialler Dialer +dialling dialing +Dialling Dialing +dialogue dialog +diaog dialog +didnt didn't +diffcult difficult +differenciate differentiate +differenly differently +Differntiates Differentiates +dificulty difficulty +Difusion Diffusion +digitised digitized +diplayed displayed +dirctely directly +dirctory directory +direcory directory +directorys directories +directoy directory +disactivate deactivate +disappers disappears +Disbale Disable + +#INCORRECT SPELLING CORRECTION + +discontigous discontiguous +discpline discipline +discription description +disppear disappear +dissassembler disassembler +distingush distinguish +distribtuion distribution +distrubutor distributor +divizor divisor +docucument document +documentaiton documentation +documentors documenters +doens't doesn't +doesnt doesn't +donnot do not +Donot Do not +dont don't +dont't don't +Dou Do +draging dragging +dreamt dreamed +Droped Dropped +duotes quotes +durring during +dynamicly dynamically +eallocate deallocate +eample example +editory editor +efficent efficient +efficently efficiently +effiency efficiency +embedabble embeddable +embedable embeddable +embeddabble embeddable +embeded embedded +emcompass encompass +emty empty +encyption encryption +enhandcements enhancements +enles endless +enought enough +entitities entities +entrys entries +Entrys Entries +enumarated enumerated +envirnment environment +envirnoment environment +enviroment environment +environemnt environment +environent environment +Equador Ecuador +equiped equipped +equlas equals + +#INCORRECT SPELLING CORRECTION + +errorous erroneous +errror error +escriptor descriptor +espacially especially +espesially especially +Evalute Evaluate +everytime every time +exacly exactly +exapmle example +excecpt except +execeeded exceeded +execess excess +exection execution +execuable executable +executeble executable +exept except +exisiting existing +existance existence +exlusively exclusively +exmaple example +experienceing experiencing +explicitely explicitly +explicity explicitly +explit explicit +Expresion Expression +expresions expressions +extented extended +extention extension +Extention Extension +extentions extensions +extesion extension +fabilous fabulous +falg flag +familar familiar +fastes fastest +favourable favorable +favour favor +favourite favorite +favours favors +featue feature +feeded fed +filsystem filesystem +firware firmware +fisrt first +fixiated fixated +fixiate fixate +fixiating fixating +flaged flagged +flavours flavors +focussed focused +folllowed followed +follwing following +folowing following + +#INCORRECT SPELLING CORRECTION + +Folowing Following +footnotexs footnotes +formaly formally +fortunatly fortunately +foward forward +fragement fragment +framesyle framestyle +framset frameset +fucntion function +Fucntion Function +fuction function +fuctions functions +fufill fulfill +fulfiling fulfilling +fullfilled fulfilled +funcion function +funciton function +functin function +funtional functional +funtionality functionality +funtion function +funtions functions +furthur further +gaalxies galaxies +Gamee Game +gernerated generated +ges goes +Ghostscipt Ghostscript +giude guide +globaly globally +goind going +Gostscript Ghostscript +grapphis graphics +greyed grayed +guaranted guaranteed +guarenteed guaranteed +guarrantee guarantee +gziped gzipped +handeling handling +harware hardware +Harware Hardware +hasnt hasn't +havn't haven't +heigt height +heigth height +hiddden hidden +Hierachical Hierarchical +highlighlighted highlighted +highligting highlighting +Higlighting Highlighting +honour honor +honouring honoring + +#INCORRECT SPELLING CORRECTION + +honours honors +horziontal horizontal +hypens hyphens +hysical physical +iconized iconified +illumnating illuminating +imaginery imaginary +i'm I'm +imitatation imitation +immedialely immediately +immediatly immediately +imortant important +imperical empirical +implemantation implementation +implemenation implementation +implenetation implementation +implimention implementation +implmentation implementation +inactiv inactive +incldue include +incomming incoming +Incomming Incoming +incovenient inconvenient +indeces indices +indentical identical +Indentification Identification +indepedancy independency +independant independent +independend independent +indetectable undetectable +indicdate indicate +indice index +indictes indicates +infinitv infinitive +infomation information +informaion information +informatation information +informationon information +informations information +Inifity Infinity +inital initial +initalization initialization +initalized initialized +initalize initialize +Initalize Initialize +initialisation initialization +initialise initialize +initialising initializing +Initialyze Initialize +Initilialyze Initialize +initilization initialization +initilize initialize + +#INCORRECT SPELLING CORRECTION + +Initilize Initialize +innacurate inaccurate +innacurately inaccurately +insde inside +inteface interface +interactivelly interactively +interfer interfere +interfrace interface +interisting interesting +internationalisation internationalization +interrrupt interrupt +interrumped interrupted +interrups interrupts +Interupt Interrupt +intervall interval +intervalls intervals +intiailize initialize +Intial Initial +intialisation initialization +intialization initialization +intialize initialize +Intialize Initialize +intializing initializing +introdutionary introductory +introdution introduction +intrrupt interrupt +intruction instruction +invarient invariant +invokation invocation +Ionisation Ionization +irrevesible irreversible +isntance instance +is'nt isn't +issueing issuing +istory history +Iterface Interface +itselfs itself +journalised journalized +judgement judgment +kdelbase kdebase +keyboad keyboard +klicking clicking +knowlege knowledge +Konquerer Konqueror +konstants constants +kscreensave kscreensaver +labelling labeling +Labelling Labeling +lauching launching +layed laid +learnt learned +leats least +lenght length + +#INCORRECT SPELLING CORRECTION + +Lenght Length +Licenced Licensed +licence license +Licence License +Licens License +liset list +listenening listening +listveiw listview +litle little +litteral literal +localisation localization +losely loosely +maanged managed +maching matching +magnication magnification +magnifcation magnification +mailboxs mailboxes +maillinglists mailinglists +maintainance maintenance +maintainence maintenance +Malicous Malicious +mamage manage +managment management +Managment Management +manangement management +mannually manually +Mantainer Maintainer +manupulation manipulation +marbels marbles +matchs matches +maximimum maximum +Maxium Maximum +mdification modification +mdified modified +menues menus +mesages messages +messanger messenger +messanging messaging +Microsft Microsoft +millimetres millimeters +mimimum minimum +minimise minimize +minimising minimizing +Minimun Minimum +Minium Minimum +minumum minimum +miscelaneous miscellaneous +miscelanous miscellaneous +miscellaneaous miscellaneous +miscellanous miscellaneous +Miscellanous Miscellaneous +mispeled misspelled +mispelled misspelled + +#INCORRECT SPELLING CORRECTION + +mistery mystery +Modifes Modifies +modifing modifying +modul module +mosue mouse +Mozzila Mozilla +mssing missing +Mulitimedia Multimedia +mulitple multiple +multible multiple +multipe multiple +multy multi +mutiple multiple +neccesary necessary +neccessary necessary +necessery necessary +nedd need +neet need +negativ negative +negociated negotiated +negociation negotiation +neighbourhood neighborhood +neighbour neighbor +Neighbour Neighbor +neighbours neighbors +neogtiation negotiation +nessecarry necessary +nessecary necessary +nessesary necessary +nework network +newtork network +nickanme nickname +nonexistant nonexistent +noone nobody +Noone No-one +normalisation normalization +noticable noticeable +nucleous nucleus +obtail obtain +occoured occurred +occouring occurring +occurance occurrence +occurances occurrences +occured occurred +occurence occurrence +occurences occurrences +occure occur +occuring occurring +occurrance occurrence +occurrances occurrences +ocupied occupied +offical official +ommited omitted + +#INCORRECT SPELLING CORRECTION + +onthe on the +opend opened +optimisation optimization +optionnal optional +orangeish orangish +organisational organizational +organisation organization +Organisation Organization +organisations organizations +organised organized +organise organize +organiser organizer +organising organizing +Organising Organizing +orginate originate +Originaly Originally +orignal original +oscilating oscillating +otehr other +ouput output +outputing outputting +overidden overridden +overriden overridden +overriden overridden +ownes owns +pakage package +panelised panelized +paramaters parameters +parametres parameters +parametrize parameterize +paramter parameter +paramters parameters +particip participle +particularily particularly +paticular particular +Pendings Pending +percetages percentages +Perfomance Performance +performace performance +Periferial Peripheral +permision permission +permisions permissions +permissable permissible +Personalizsation Personalization +perticularly particularly +phyiscal physical +plaforms platforms +plese please +politness politeness +posibilities possibilities +posibility possibility + +#INCORRECT SPELLING CORRECTION + +posible possible +positon position +possebilities possibilities +possebility possibility +possibilty possibility +possiblity possibility +posssibility possibility +potentally potentially +practise practice +practising practicing +preceeded preceded +preceeding preceding +precison precision +preemphasised preemphasized +Preemphasised Preemphasized +prefered preferred +Prefered Preferred +preferrable preferable +prefiously previously +preformance performance +prerequisits prerequisites +presense presence +pressentation presentation +prgramm program +Prining Printing +priveleges privileges +priviledge privilege +priviledges privileges +priviliges privileges +probatility probability +probelm problem +proberly properly +problmes problems +proceedure procedure +proctection protection +proecss process +progess progress +programing programming +programme program +programm program +promille per mill +promiscous promiscuous +promped prompted +pronounciation pronunciation +Pronounciation Pronunciation +pronunce pronounce +pronunciated pronounced +properies properties +Propertites Properties +Propogate Propagate +protoypes prototypes + +#INCORRECT SPELLING CORRECTION + +Proxys Proxies +psuedo pseudo +Psuedo Pseudo +pult desk +purposees purposes +quatna quanta +queing queuing +querys queries +queueing queuing +Queueing Queuing +quiten quiet +quiting quitting +readony readonly +realise realize +realy really +REAMDE README +reasonnable reasonable +receieve receive +recepeient recipient +recepient recipient +recevie receive +recevie receive +receving receiving +recieved received +recieve receive +Recieve Receive +reciever receiver +recieves receives +Recieves Receives +recives receives +recognised recognized +recognise recognize +recognises recognizes +recomended recommended +recommanded recommended +recommand recommend +recommented recommended +redialling redialing +reets resets +refered referred +Refering Referring +Refeshes Refreshes +refreshs refreshes +regarless regardless +registaration registration +registred registered +Regsiter Register +regulare regular +regularily regularly +Reigster Register +reimplemenations reimplementations + +#INCORRECT SPELLING CORRECTION + +Reimplemenations Reimplementations +releated related +relection reselection +relevent relevant +relocateable relocatable +remaing remaining +remeber remember +remebers remembers +remotley remotely +renderes renders +renewd renewed +reorienting reorientating +Repalcement Replacement +replys replies +reponsibility responsibility +requeusts requests +resently recently +resetted reset +resistent resistant +resognized recognized +resonable reasonable +resoure resource +responsability responsibility +responsivness responsiveness +resposible responsible +ressources resources +retreived retrieved +retreive retrieve +retults results +Rewritebles Rewritables +richt right +rigths rights +Rigt Right +saftey safety +satified satisfied +savely safely +savety safety +scalled scaled +scather scatter +scenerio scenario +sceptical skeptical +schduler scheduler +Sectionning Sectioning +selction selection +selectde selected +sensistve sensitive +separed separated +separeted separated +sepcified specified +seperated separated +seperately separately +seperate separate +seperate separate + +#INCORRECT SPELLING CORRECTION + +Seperate Separate +seperation separation +seperatly separately +seperator separator +sequencially sequentially +sertificate certificate +serveral several +setted set +sheduled scheduled +sheme scheme +shorctuts shortcuts +shoud should +shouldnt shouldn't +Shouldnt Shouldn't +shure sure +Similarily Similarly +Similiarly Similarly +similiar similar +simlar similar +simpliest simplest +simultaneuosly simultaneously +skript script +slewin slewing +smaple sample +Sombody Somebody +somehwat somewhat +soure source +sparcely sparsely +speakiing speaking +specefied specified +specfic specific +specfied specified +specialised specialized +specifc specific +specifed specified +Specificiation Specification +specifieing specifying +specifing specifying +specifiy specify +Specifiy Specify +speficied specified +speling spelling +spezifying specifying +sprectrum spectrum +standar standard +Startp Startup +Statfeul Stateful +statfull stateful +storeys storys +straighforward straightforward +streched stretched +Streches Stretches +Strech Stretch + +#INCORRECT SPELLING CORRECTION + +Striked Stroked +stuctures structures +styleshets stylesheets +subcribed subscribed +subdirectorys subdirectories +subseqently subsequently +Substracting Subtracting +subystem subsystem +succeded succeeded +succesfully successfully +succesful successful +succesive successive +succesor successor +successfull successful +sucessfull successful +sucessfully successfully +sucessfuly successfully +sucess success +sufficent sufficient +superflous superfluous +supossed supposed +supressed suppressed +supress suppress +suprised surprised +susbstitute substitute +swaped swapped +synchonization synchronization +synchronisation synchronization +Synchronisation Synchronization +synchronised synchronized +synchronises synchronizes +synchronise synchronize +synchronyze synchronize +Syncronization Synchronization +syncronized synchronized +Syncronizes Synchronizes +syncronize synchronize +syncronizing synchronizing +Syncronizing Synchronizing +syncronous synchronous +syncrounous synchronous +syndrom syndrome +syntex syntax +synthetizer synthesizer +syntheziser synthesizer +sytem system +talbs tables +talse false +tecnology technology +temparary temporary +Tempertures Temperatures +terminatin terminating + +#INCORRECT SPELLING CORRECTION + +texured textured +themc them +thet that +threshholds thresholds +threshhold threshold +throtte throttle +throught through +throuth through +tiggered triggered +tihs this +timditiy timidity +Timdity Timidity +timming timing +tranceiver transceiver +Tranfers Transfers +tranfer transfer +Tranlate Translate +tranlation translation +transalted translated +transation transaction +transfering transferring +transferrable transferable +transmiter transmitter +transmiting transmitting +transmition transmission +transmittion transmission +transparancy transparency +transparant transparent +trasfered transferred +traveller traveler +travelling traveling +triggerg triggering +triggerred triggered +truely truly +trys tries +uglyness ugliness +unabiguous unambiguous +unaccesible unaccessible +unallowed disallowed +unamed unnamed +unathorized unauthorized +uncrypted unencrypted +Uncutt Uncut +underlieing underlying +underrrun underrun +undesireable undesirable +undestood understood +Undexpected Unexpected +undoedne undid +unecessary unnecessary +unexperienced inexperienced +unexperience inexperience +unfortunatly unfortunately + +#INCORRECT SPELLING CORRECTION + +Unfortunatly Unfortunately +uniq unique +unitialized uninitialized +unkown unknown +Unmoveable Unmovable +unneccessary unnecessary +unneccessay unnecessary +unsellectected unselected +unsuccesful unsuccessful +unuseable unusable +unusuable unusable +unvailable unavailable +uploades uploads +upppercase uppercase +usally usually +usefull useful +usere user +usuable usable +usuallly usually +Usualy Usually +utilisation utilization +vaild valid +valied valid +valueable valuable +varb verb +vays ways +verfication verification +verically vertically +versins versions +verticaly vertically +verticies vertices +Veryify Verify +vicitim victim +visualisations visualizations +visualisation visualization +Visualisation Visualization +visualise visualize +visul visual +volonteer volunteer +Volumen Volume +Voribis Vorbis +vrtual virtual +waranty warranty +watseful wasteful +weigth weight +wheter whether +whicn which +whishes wishes +whitch which +whith with + +#INCORRECT SPELLING CORRECTION + +Wiazrd Wizard +wich which +wich which +wierd weird +wieving viewing +wiev view +wih with +willl will +wnat want +workimg working +workstatio workstation +woud would +wouldd would +writting writing +Writting Writing +yeld yield +yorself yourself +you'ld you would +yourContryCode yourCountryCode + diff --git a/scripts/kde.supp b/scripts/kde.supp new file mode 100644 index 00000000..213d9bb1 --- /dev/null +++ b/scripts/kde.supp @@ -0,0 +1,155 @@ +# +# Some valgrind suppressions handy for ignoring stuff we don't care +# about when valgrinding kde applications +# +# Library paths and versions from debian unstable, YMMV +# + +# +# pthread errors +# + +{ + pthread_mutex_unlock + core:PThread + fun:__pthread_mutex_unlock +} + +{ + pthread_error/pthread_mutex_destroy + core:PThread + fun:pthread_error + fun:__pthread_mutex_destroy +} + +# +# ld.so errors +# + +{ + strchr/decompose_rpath/_dl_map_object + MemCheck:Cond + fun:strchr + fun:decompose_rpath + fun:_dl_map_object +} + +{ + strlen/libc/_dl_catch_error + MemCheck:Cond + fun:strlen + fun:_dl_open + obj:*libdl-2*.so + fun:_dl_catch_error* +} + +{ + strchr/libc/_dl_catch_error + MemCheck:Cond + fun:strchr + obj:*libc-2.2.?.so + fun:_dl_catch_error +} + +{ + strrchr/_dl_map_object_from_fd/_dl_map_object + MemCheck:Cond + fun:strrchr + fun:_dl_map_object_from_fd + fun:_dl_map_object +} + +{ + strlen/_dl_signal_cerror/_dl_lookup_symbol_internal + Memcheck:Cond + fun:strlen + fun:_dl_signal_cerror + fun:_dl_lookup_symbol_internal + fun:*dlsym +} + +# +# X library errors +# + +{ + libXft(Cond) + MemCheck:Cond + obj:/usr/X11R6/lib/libXft.so.1.1 + obj:/usr/X11R6/lib/libXft.so.1.1 +} + +{ + write(buf)/libc/libICE + Memcheck:Param + write(buf) + fun:__GI___libc_write + fun:_IceTransWrite + fun:_IceWrite + fun:IceFlush +} + +{ + write(buf)/libc/libX11 + Memcheck:Param + write(buf) + fun:__GI___libc_write + fun:_X11TransWrite + fun:_XFlushInt + fun:_XFlush +} + +{ + write(buf)/libc/libX11 + Memcheck:Param + write(buf) + fun:__GI___libc_write + fun:_X11TransWrite + fun:_XFlushInt + fun:_XReply +} + +{ + writev(vector[...]) + Memcheck:Param + writev(vector[...]) + fun:*writev + obj:libX11.so.* + fun:_X11TransWritev + fun:_XSend +} + +# +# SSL errors +# + +{ + various1/libcrypto + Memcheck:Value4 + obj:*libcrypto.so.0.9.7 +} + +{ + various2/libcrypto + Memcheck:Cond + obj:*libcrypto.so.0.9.7 +} + +{ + ssl3_read_bytes1/libssl + Memcheck:Cond + fun:memcpy + fun:ssl3_read_bytes +} + +{ + ssl3_read_bytes2/libssl + Memcheck:Cond + fun:ssl3_read_bytes +} + +{ + ssl3_get_message/libssl + Memcheck:Cond + fun:ssl3_get_message +} diff --git a/scripts/kdedoc b/scripts/kdedoc new file mode 100755 index 00000000..0dc20a66 --- /dev/null +++ b/scripts/kdedoc @@ -0,0 +1,48 @@ +#!/bin/sh +# Run from command line, to open a kde help page in kfm/konqueror +# Argument : The classname (case sensitive !). +# Both doxygen and kdoc docs are supported. + +# You can edit this line to set the directory holding your KDE docs, or you +# can use the environment variable KDEDOCS to avoid future conflicts with this +# file if the default changes. +KDEDOCS=${KDEDOCS:-"$KDEDIR/share/doc/HTML/en/kdelibs-apidocs"} + +if [ $# = 1 ]; then + if [ -e "$KDEDOCS/doxygen.css" ]; then + # Docs are laid out in doxygen style. + if [ -f "$KDEDOCS/class$1.html" ]; then + kfmclient exec "$KDEDOCS/class$1.html" + elif [ -f "$KDEDOCS"/*/html/"class$1.html" ]; then + kfmclient exec "$KDEDOCS"/*/html/"class$1.html" + else + classstring=`echo "$1" | sed -e 's/::/_1_1/'` + if [ -f "$KDEDOCS/class$classstring.html" ]; then + kfmclient exec "$KDEDOCS/class$classstring.html" + elif [ -f "$KDEDOCS"/*/html/"class$classstring.html" ]; then + kfmclient exec "$KDEDOCS"/*/html/"class$classstring.html" + elif [ -f "$KDEDOCS"/class*_1_1"$1.html" ]; then + kfmclient exec "$KDEDOCS"/class*_1_1"$1.html" + elif [ -f "$KDEDOCS"/*/html/class*_1_1"$1.html" ]; then + kfmclient exec "$KDEDOCS"/*/html/class*_1_1"$1.html" + else + echo "No class $1 in $KDEDOCS/*" + exit 1 + fi + fi + elif [ -e "$KDEDOCS/kdecore/index.html" ]; then + # Docs are laid out in kdoc style. + if [ -f "$KDEDOCS"/*/"$1.html" ]; then + kfmclient exec "$KDEDOCS"/*/"$1.html" + else + echo "No class $1 in $KDEDOCS/*" + exit 1 + fi + else + echo "$KDEDOCS does not appear to contain your KDE docs." + exit 1 + fi +else + echo "Usage : $0 " + exit 1 +fi diff --git a/scripts/kdekillall b/scripts/kdekillall new file mode 100755 index 00000000..deb5aef5 --- /dev/null +++ b/scripts/kdekillall @@ -0,0 +1,28 @@ +#! /bin/sh + +case $1 in + -*) signal=$1; shift;; +esac +if [ $# = 0 ]; then + echo "Usage: $0 [-] " + echo 'Kills the process "kdeinit: with signal "' + echo "if is not specified, it defaults to SIGTERM," + echo "see kill -l for a list of possible signals" +else + list=$(ps auwx | grep $USER | awk "/[k]deinit: $1/ {print \$2}") + if test -z "$list"; then + # on newer Linux kernels (>= 2.6.10) KDE is able to use + # prctl(PR_SET_NAME) to change the process name... + list=$(ps auwx | grep $USER | awk "/\[kdeinit\] $1/ {print \$2}") + fi + if test -z "$list"; then + # with KDE 3.4 we changed the view again... + list=$(ps auwx | grep $USER | awk "/$1 \[kdeinit\]/ {print \$2}") + fi + if test -n "$list"; then + kill $signal $list + else + echo 'No process killed' + exit 1 + fi +fi diff --git a/scripts/kdelnk2desktop.py b/scripts/kdelnk2desktop.py new file mode 100755 index 00000000..9d96d87c --- /dev/null +++ b/scripts/kdelnk2desktop.py @@ -0,0 +1,21 @@ +#! /usr/bin/env python + +import os, sys, string + +def help(): + print "Usage: %s .kdelnk ..." + +if len(sys.argv) < 2: + help() + sys.exit() + +for fn in sys.argv[1:]: + print "Doing %s ..." % fn + f = open(fn, 'r').readlines() + + if string.find(f[0], "# KDE Config") == 0: + p = open(fn, 'w') + p.writelines(f[1:]) + p.close() + + os.rename(fn, fn[:-6]+'desktop') diff --git a/scripts/kdemangen.pl b/scripts/kdemangen.pl new file mode 100755 index 00000000..69951c88 --- /dev/null +++ b/scripts/kdemangen.pl @@ -0,0 +1,250 @@ +#! /usr/bin/env perl + +# kdemangen.pl +# Copyright (C) 2003 Dominique Devriese + +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + + +# Here's a little explanation about this script: +# In order to fix long-standing Debian bug #116485, I've written a +# script that takes a KDE app, and generates a nice man page.. It uses +# the app's "--author" and "--help-all" output, along with a description +# from Debian's control file to get the data. I think the script works +# great, the man pages look almost hand-written ;) I encourage you all +# to try this out, and very much welcome any feedback, especially as to +# how to integrate this into either the Debian KDE packaging system or +# the KDE build system.. + +# The idea to do this with a script allows us to easily keep the man +# pages up to date, and is generally very low-trouble for the +# developer... + +# The script is attached at the bottom.. + +# USAGE: +# Suppose you wanted to generate a man page for the KDE-Edu program +# kalzium.. Then you would +# 1 cd to /path/to/kde/srcs/kdeedu/debian ( necessary so the script +# finds the Debian control file.. ) +# 2 run "/path/to/kdemangen.pl $(which kstars) > kstars.1" +# 3 run "man ./kstars.1" to check out the generated page.. + +# PROBLEMS: +# Only works for full KDE applications that use KCmdLineArgs ( +# inherent to my approach, but most KDE apps fulfill this requirement +# ) + +use warnings; +use strict; + +sub optionstonroff + { + my $options = shift; + my $ret = ""; + foreach( split /\n/, $options ) + { + if( /^ (--?[[:alpha:]]+, )?(--[[:alpha:]-]*|-[[:alpha:]])( <[[:alpha:] ]*>| [[:alpha:]]*)? *(.*)$/ ) + { + my $short; + my $long; + my $arg; + my $desc; + if( $1 ) { $short = $1; } else { $short = ""; }; + if( $2 ) { $long = $2; } else { $long = ""; }; + if( $3 ) { $arg = $3; } else { $arg = ""; }; + if( $4 ) { $desc = $4; } else { $desc = ""; }; + $short =~ s/-/\\-/g; + $long =~ s/-/\\-/g; + $arg =~ s/-/\\-/g; + $ret .= ".TP\n"; + $ret .= ".B $short $long $arg\n"; + $ret .= "$desc\n"; + } + elsif( /^ ([[:alpha:]]+) +(.*)$/ ) + { + $ret .= ".TP\n"; + $ret .= ".B $1\n"; + $ret .= "$2\n"; + } + elsif( /^ +(.*)$/ ) + { + $ret .= "$1\n"; + } + elsif( /^(.*)$/ ) + { + $ret .= ".SS $1\n"; + # this means a header like "Qt Options:" I'm wondering + # what to do with this, I don't know enough nroff to + # format it nicely, I'm affraid.. + } + } + return $ret; + }; + +sub sortoptionsfromnroff { + # Zack Cerza + + # Rather than redo Dominique's optionstonroff(), I decided + # to make this function to sort the options sections created + # by his function. + + # What it does is read line-by-line and first determine which + # section it's looking at via the ".SS
    " lines. Once + # it knows, it sets a "$in_
    " variable to "1" and + # begins to write the section data into $
    . When it + # gets to a line that contains only ".SS ", it sets + # $in_
    to "0" and continues. + + # It's a little messy, but it's the only way I could + # get it to work with what little knowledge I had. + + # This is the first time I've used Perl. Be kind. + + my $options = shift; + my $ret=""; + + my $in_gen_opts = "0"; + my $gen_opts = ""; + my $in_qt_opts = "0"; + my $qt_opts = ""; + my $in_kde_opts = "0"; + my $kde_opts = ""; + my $in_opts = "0"; + my $opts = ""; + my $in_args = "0"; + my $args = ""; + + foreach ( split /\n/, $options ) { + if( $in_gen_opts == "1" ) { + if( /^(\.SS )$/ ) { $in_gen_opts = "0"; } + $gen_opts .= $_; + $gen_opts .= "\n"; + } + if( /^(\.SS.+Generic options:)$/ ) + { $in_gen_opts = "1"; $gen_opts .= $1; $gen_opts .= "\n"; } + + if( $in_qt_opts == "1" ) { + if( /^(\.SS )$/ ) { $in_qt_opts = "0"; } + $qt_opts .= $_; + $qt_opts .= "\n"; + } + if( /^(\.SS.+Qt options:)$/ ) + { $in_qt_opts = "1"; $qt_opts .= $1; $qt_opts .= "\n"; } + + if( $in_kde_opts == "1" ) { + if( /^(\.SS )$/ ) { $in_kde_opts = "0"; } + $kde_opts .= $_; + $kde_opts .= "\n"; + } + if( /^(\.SS.+KDE options:)$/ ) + { $in_kde_opts = "1"; $kde_opts .= $1; $kde_opts .= "\n"; } + + if( $in_opts == "1" ) { + if( /^(\.SS )$/ ) { $in_opts = "0"; } + $opts .= $_; + $opts .= "\n"; + } + if( /^(\.SS.+Options:)$/ ) + { $in_opts = "1"; $opts .= $1; $opts .= "\n"; } + + if( $in_args == "1" ) { + if( /^(\.SS )$/ ) { $in_args = "0"; } + $args .= $_; + $args .= "\n"; + } + if( /^(\.SS.+Arguments:)$/ ) + { $in_args = "1"; $args .= ".SS\n"; $args .= $1; $args .= "\n"; } + } + $ret .= $args; + $ret .= $opts; + $ret .= $gen_opts; + $ret .= $kde_opts; + $ret .= $qt_opts; + return $ret; + }; + +sub usage + { + print "This script generates a nice manual page for a KDE app which uses KCmdLineArgs..\n"; + print "USAGE: $0 app\n"; + print "There's more information about how to use this script in the comments at the front of the source..\n" + }; + +if( $#ARGV < 0 ){ + usage(); + exit 1; +} + +my $runapp = "$ARGV[0]"; +if ( ! -x $runapp ) + { + print "Error: $runapp is not executable.\n"; + exit 1; + } +else { $runapp = "KDE_LANG=en_US $runapp"; }; + +my $shortdescription = `$runapp --help | sed -ne '3p'`; +chomp $shortdescription; + +my $synopsis = `$runapp --help | sed -n '1p' | sed -e 's/[^:]*: //'`; +chomp $synopsis; +$synopsis =~ s/-/\\-/g; +my $appname = $synopsis; +$appname =~ s/ .*$//; +my $ucappname = uc $appname; + +my $options = `$runapp --help-all | sed -e '1,4d'`; +$options = optionstonroff( $options ); +$options = sortoptionsfromnroff( $options ); + +my $timespec = ucfirst `date '+%b %G'`; +chomp $timespec; + +my $description = $shortdescription; +if( -r "control" ) + { + $description = `cat control | sed -ne '/^Description:/,/^\$/p' | egrep -v '^\\w*:.*\$' | sed -e 's/^ //' | sed -e 's/^\\.//'`; +# leads to problems in some cases :( +# $description =~ s/KDE ?/\n.SM KDE\n/g; + } + +my $authors = `$runapp --author | sed -ne '2,\$p' | sed -e '\$d' | sed -e 's/^ *//'`; +$authors =~ s/\n/\n.br\n/g; + +print < - automate the kde svn build process + +=back + +=head1 SYNOPSIS + +=over + +=item B I<[options]...> I<[modules]...> + +=back + +=head1 DESCRIPTION + +The B script is used to automate the download, build, +and install process for KDE (using Subversion). + +It is recommended that you first setup a F<.kdesvn-buildrc> file +in your home directory. Please refer to B help file +in KDE help for information on how to write F<.kdesvn-buildrc>, +or consult the sample file which should have been included +with this program. If you don't setup a F<.kdesvn-buildrc>, a +default set of options will be used, and a few modules will be +built by default. + +After setting up F<.kdesvn-buildrc>, you can run this program from +either the command-line or from cron. It will automatically +download the modules from Subversion, create the build +system, and configure and make the modules you tell it to. +You can use this program to install KDE as well, +if you are building KDE for a single user. Note that B +will try to install the modules by default. + +If you DO specify a package name, then your settings will still be +read, but the script will try to build / install the package +regardless of F<.kdesvn-buildrc> + +kdesvn-build reads options in the following order: + +=over + +=item 1. From the command line. + +=item 2. From the file F in the current directory. Note that + the file is not a hidden file. + +=item 3. From the file F<~/.kdesvn-buildrc>. + +=item 4. From a set of internal options. + +=back + +This utility is part of the KDE Software Development Kit. + +=head1 OPTIONS + +=over + +=item B<--quiet>, B<-q> + +With this switch kdesvn-build will only output a general overview of the build +process. Progress output is still displayed if available. + +=item B<--really-quiet> + +With this switch only warnings and errors will be output. + +=item B<--verbose>, B<-v> + +Be very detailed in what is going on, and what actions kdesvn-build is taking. +Only B<--debug> is more detailed. + +=item B<--no-svn> + +Skip contacting the Subversion server. + +=item B<--no-build> + +Skip the build process. + +=item B<--no-install> + +Don't automatically install after build. + +=item B<--svn-only> + +Update from Subversion only (Identical to B<--no-build> at this point). + +=item B<--build-only> + +Build only, do not perform updates or install. + +=item B<--rc-file=EfilenameE> + +Read configuration from filename instead of default. + +=item B<--debug> + +Activates debug mode. + +=item B<--pretend>, B<-p> + +Do not contact the Subversion server, run make, or create / delete files +and directories. Instead, output what the script would have done. + +=item B<--nice=EvalueE> + +Allow you to run the script with a lower priority. The default value is +10 (lower priority by 10 steps). + +=item B<--prefix=/kde/path> + +This option is a shortcut to change the setting for kdedir from the +command line. It implies B<--reconfigure>. + +=item B<--color> + +Add color to the output. + +=item B<--no-color> + +Remove color from the output. + +=item B<--resume> + +Tries to resume the make process from the last time the script was run, +without performing the Subversion update. + +=item B<--resume-from=EpkgE> + +Starts building from the given package, without performing the Subversion +update. + +=item B<--revision=ErevE>, B<-r=ErevE> + +Forces update to revision from Subversion. + +=item B<--refresh-build> + +Start the build from scratch. This means that the build directory for the +module B before make -f Makefile.cvs is run again. You can +use B<--recreate-configure> to do the same thing without deleting the module +build directory. + +=item B<--reconfigure> + +Run configure again, but don't clean the build directory or re-run +make -f Makefile.cvs. + +=item B<--recreate-configure> + +Run make -f Makefile.cvs again to redo the configure script. The build +directory is not deleted. + +=item B<--no-rebuild-on-fail> + +Do not try to rebuild a module from scratch if it failed building. Normally +kdesvn-build will try progressively harder to build the module before giving +up. + +=item B<--build-system-only> + +Create the build infrastructure, but don't actually perform the build. + +=item B<--install> + +Try to install the packages passed on the command line, or all packages in +F<~/.kdesvn-buildrc> that don't have manual-build set. Building and +Subversion updates are not performed. + +=item B<--EoptionE=> + +Any unrecognized options are added to the global configuration, overriding +any value that may exist. + +For example, B<--svn-server=http://path.to.svn.server/> would change the +setting of the global B option for this instance of kdesvn-build. + +=item B<--EmoduleE,EoptionE=> + +Likewise, allow you to override any module specific option from the +command line. + +Example: B<--kdelibs,use-unsermake=false> would disable unsermake for the +kdelibs module. + +=item B<--help> + +Display the help and exit. + +=item B<--author> + +Output the author(s)'s name. + +=item B<--version> + +Output the program version. + +=back + +=head1 EXAMPLES + +=over + +=item B + +=item B I<--no-svn kdelibs> + +=item B I<--refresh-build> I + +=back + +=head1 BUGS + +Since kdesvn-build doesn't generally save information related to the build and +prior settings, you may need to manually re-run kdesvn-build with a flag like +B<--recreate-configure> if you change some options, including B. + +Please use KDE bugzilla at http://bugs.kde.org for information and +reporting bugs. + +=head1 SEE ALSO + +You can find additional information at B home page, +F, or using kdesvn-build +docbook documentation, using the help kioslave, F. + +=head1 AUTHOR + +Michael Pyne + +Man page written by: +Carlos Leonhard Woelz + +=cut + +# Script to handle building KDE from Subversion. All of the configuration is +# stored in the file ~/.kdesvn-buildrc. +# +# Please also see the documentation that should be included with this program, +# in doc.html +# +# Copyright (c) 2003, 2004, 2005 Michael Pyne. +# Home page: http://kdesvn-build.kde.org/ +# +# You may use, alter, and redistribute this software under the terms +# of the GNU General Public License, v2 (or any later version). +# +# TODO: It would be better to have lockfiles in each directory as it's +# being updated, instead of having one big lock for the script. + +use strict; +use warnings; +use Fcntl; # For sysopen constants +use POSIX 'strftime'; +use File::Find; # For our lndir reimplementation. +use Errno qw(:POSIX); + +# Debugging level constants. +use constant { + DEBUG => 0, + WHISPER => 1, + INFO => 2, + NOTE => 3, + WARNING => 4, + ERROR => 5, +}; + +# Some global variables +# Remember kids, global variables are evil! I only get to do this +# because I'm an adult and you're not! :-P +# Options that start with a # will replace values with the same name, +# if the option is actually set. +my %package_opts = ( + 'global' => { + "apidox" => "", + "apply-qt-patches" => "", + "binpath" => "/bin:/usr/bin:/usr/X11R6/bin:/usr/local/bin", + "branch" => "", + "build-dir" => "build", + "build-system-only" => "", + "checkout-only" => "", + "configure-flags" => "--enable-debug", + "colorful-output" => 1, # Use color by default. + "cxxflags" => "-pipe", + "debug" => "", + "debug-level" => INFO, + "dest-dir" => '${MODULE}', # single quotes used on purpose! + "disable-agent-check" => 0, # If true we don't check on ssh-agent + "do-not-compile" => "", + "email-address" => "", + "email-on-compile-error" => "", + "install-after-build" => "1", # Default to true + "inst-apps" => "", + "kdedir" => "$ENV{HOME}/kde", + "libpath" => "", + "log-dir" => "log", + "make-install-prefix" => "", # Some people need sudo + "make-options" => "-j2", + "manual-build" => "", + "manual-update" => "", + "module-base-path" => "", # Used for tags and branches + "niceness" => "10", + "no-svn" => "", + "no-rebuild-on-fail" => "", + "override-url" => "", + "prefix" => "", # Override installation prefix. + "pretend" => "", + "qtdir" => "$ENV{HOME}/kdesvn/build/qt-copy", + "reconfigure" => "", + "recreate-configure" => "", + "refresh-build" => "", + "remove-after-install"=> "none", # { none, builddir, all } + "revision" => 0, + "set-env" => { }, # Hash of environment vars to set + "source-dir" => "$ENV{HOME}/kdesvn", + "stop-on-failure" => "", + "svn-server" => "svn://anonsvn.kde.org/home/kde", + "tag" => "", + "unsermake-options" => "--compile-jobs=2 -p", + "unsermake-path" => "unsermake", + "use-unsermake" => "1", # Default to true now, we may need a blacklist + } +); + +# This is a hash since Perl doesn't have a "in" keyword. +my %ignore_list; # List of packages to refuse to include in the build list. + +# update and build are lists since they support an ordering, which can't be +# guaranteed using a hash unless I want a custom sort function (which isn't +# necessarily a horrible way to go, I just chose to do it this way. +my @update_list; # List of modules to update/checkout. +my @build_list; # List of modules to build. + +# Dictionary of lists of failed modules, keyed by the name of the operation +# that caused the failure (e.g. build). Note that output_failed_module_lists +# uses the key name to display text to the user so it should describe the +# actual category of failure. You should also add the key name to +# output_failed_module_lists since it uses its own sorted list. +my @fail_display_order = qw/build update install/; +my %fail_lists = ( + 'build' => [ ], + 'install' => [ ], + 'update' => [ ], +); + +my $install_flag; # True if we're in install mode. +my $BUILD_ID; # Used by logging subsystem to create a unique log dir. +my $LOG_DATE; # Used by logging subsystem to create logs in same dir. +my @rcfiles = ("./kdesvn-buildrc", "$ENV{HOME}/.kdesvn-buildrc"); +my $rcfile; # the file that was used; set by read_options + +# Colors +my ($RED, $GREEN, $YELLOW, $NORMAL, $BOLD) = ("") x 5; + +# Subroutine definitions + +# I swear Perl must be the only language where the docs tell you to use a +# constant that you'll never find exported without some module from CPAN. +use constant PRIO_PROCESS => 0; + +# I'm lazy and would rather write in shorthand for the colors. This sub +# allows me to do so. Put it right up top to stifle Perl warnings. +sub clr($) +{ + my $str = shift; + + $str =~ s/g\[/$GREEN/g; + $str =~ s/]/$NORMAL/g; + $str =~ s/y\[/$YELLOW/g; + $str =~ s/r\[/$RED/g; + $str =~ s/b\[/$BOLD/g; + + return $str; +} + +# Subroutine which returns true if pretend mode is on. Uses the prototype +# feature so you don't need the parentheses to use it. +sub pretending() +{ + return get_option('global', 'pretend'); +} + +# Subroutine which returns true if debug mode is on. Uses the prototype +# feature so you don't need the parentheses to use it. +sub debugging() +{ + return get_option('global', 'debug-level') <= DEBUG; +} + +# The next few subroutines are used to print output at different importance +# levels to allow for e.g. quiet switches, or verbose switches. The levels are, +# from least to most important: +# debug, whisper, info (default), note (quiet), warning (very-quiet), and error. +# +# You can also use the pretend output subroutine, which is emitted if, and only +# if pretend mode is enabled. +# +# clr is automatically run on the input for all of those functions. +# Also, the terminal color is automatically reset to normal as well so you don't +# need to manually add the ] to reset. + +# Subroutine used to actually display the data, calls clr on each entry first. +sub print_clr(@) +{ + print clr $_ foreach (@_); + print clr "]\n"; +} + +sub debug(@) +{ + print_clr @_ if debugging; +} + +sub whisper(@) +{ + print_clr @_ if get_option('global', 'debug-level') <= WHISPER; +} + +sub info(@) +{ + print_clr @_ if get_option('global', 'debug-level') <= INFO; +} + +sub note(@) +{ + print_clr @_ if get_option('global', 'debug-level') <= NOTE; +} + +sub warning(@) +{ + print_clr @_ if get_option('global', 'debug-level') <= WARNING; +} + +# This sub has the additional side effect of printing the errno value if it +# is set. +sub error(@) +{ + print STDERR (clr $_) foreach (@_); + print " $!\n" if $!; +} + +sub pretend(@) +{ + print_clr @_ if pretending; +} + +# Subroutine to handle removing the lock file upon receiving a signal +sub quit_handler +{ + note "Signal received, terminating."; + finish(5); +} + +# Subroutine that returns the path of a file used to output the results of the +# build process. It accepts one parameter, which changes the kind of file +# returned. If the parameter is set to 'existing', then the file returned is +# the latest file that exists, or undef if no log has been created yet. This +# is useful for the --resume mode. All other values will return the name if a +# file that does not yet exist. +# +# All files will be stored in the log directory. +sub get_output_file +{ + my $logdir; + my $mode; + $mode = shift or $mode = ''; + my $fname; + + debug "get_output_file in mode $mode"; + + if ($mode eq 'existing') + { + # There's two ways of finding the old file. Searching backwards with + # valid combinations of the date and build id, or just reading in the + # name from a known file or location. Since the latter option is much + # easier, that's what I'm going with. Note that this depends on the + # latest symlink being in place. + $logdir = get_subdir_path ('global', 'log-dir'); + $fname = "$logdir/latest/build-status"; + + debug "Old build status file is $fname"; + + # The _ at the end returns the cached file stats to avoid multiple + # stat() calls. + return "" if not -e $fname or not -r _; + + return $fname; + } + + # This call must follow the test above, because it changes the 'latest' + # symlink leading to failures later. + $logdir = get_log_dir('global'); + + $fname = "$logdir/build-status"; + debug "Build status file is $fname"; + + return $fname; +} + +# Subroutine to retrieve a subdirecty path for the given module. +# First parameter is the name of the module, and the second +# parameter is the option key (e.g. build-dir or log-dir). +sub get_subdir_path +{ + my $module = shift; + my $option = shift; + my $dir = get_option($module, $option); + + # If build-dir starts with a slash, it is an absolute path. + return $dir if $dir =~ /^\//; + + # If it starts with a tilde, expand it out. + if ($dir =~ /^~/) + { + $dir =~ s/^~/$ENV{'HOME'}/; + } + else + { + # Relative directory, tack it on to the end of $kdesvn. + my $kdesvndir = get_kdesvn_dir(); + $dir = "$kdesvndir/$dir"; + } + + return $dir; +} + +# Subroutine to return the name of the destination directory for the checkout +# and build routines. Based on the dest-dir option. The return value will be +# relative to the src/build dir. The user may use the '$MODULE' or '${MODULE}' +# sequences, which will be replaced by the name of the module in question. +# +# The first parameter should be the module name. +sub get_dest_dir +{ + my $module = shift; + my $dest_dir = get_option($module, 'dest-dir'); + + $dest_dir =~ s/(\${MODULE})|(\$MODULE\b)/$module/g; + + return $dest_dir; +} + +# Convienience subroutine to get the source root dir. +sub get_kdesvn_dir +{ + return get_option ('global', 'source-dir'); +} + +# Function to work around a Perl language limitation. +# First parameter is the list to search. +# Second parameter is the value to search for. +# Returns true if the value is in the list +sub list_has(\@$) +{ + my ($list_ref, $value) = @_; + return scalar grep ($_ eq $value, @{$list_ref}); +} + +# Subroutine to return the branch prefix. i.e. the part before the branch name +# and module name. +# +# The first parameter is the module in question. +# The second parameter should be 'branches' if we're dealing with a branch or +# 'tags' if we're dealing with a tag. +# +# Ex: 'kdelibs' => 'branches/KDE' +# 'kdevelop' => 'branches/kdevelop' +sub branch_prefix +{ + my $module = shift; + my $type = shift; + + # These modules seem to have their own subdir in /tags. + my @tag_components = qw/arts koffice amarok kst qt taglib/; + + # The map call adds the kde prefix to the module names because I don't feel + # like typing them all in. kdevelop and konstruct are special cases. + my @kde_module_list = ((map {'kde' . $_} qw/-i18n -common accessibility + addons admin artwork base bindings edu games graphics libs + multimedia network nonbeta pim sdk toys utils webdev/), 'kdevelop', + 'konstruct'); + + # KDE proper modules seem to use this pattern. + return "$type/KDE" if list_has(@kde_module_list, $module); + + # If we doing a tag just return 'tags' because the next part is the actual + # tag name, which is added by the caller, unless the module has its own + # subdirectory in /tags. + return "$type" if $type eq 'tags' and not list_has(@tag_components, $module); + + # Everything else. + return "$type/$module"; +} + +# Subroutine to return a module URL for a module using the 'branch' option. +# First parameter is the module in question. +# Second parameter is the type ('tags' or 'branches') +sub handle_branch_tag_option +{ + my ($module, $type) = @_; + my $svn_server = get_option($module, 'svn-server'); + my $branch = branch_prefix($module, $type); + my $branchname = get_option($module, 'tag'); + + if($type eq 'branches') + { + $branchname = get_option($module, 'branch'); + } + + # Remove trailing slashes. + $svn_server =~ s/\/*$//; + + return "$svn_server/$branch/$branchname/$module"; +} + +# Subroutine to return the appropriate SVN URL for a given module, based on +# the user settings. For example, 'kdelibs' -> https://svn.kde.org/home/kde/trunk/KDE/kdelibs +sub svn_module_url +{ + my $module = shift; + my $svn_server = get_option($module, 'svn-server'); + my $branch = get_option($module, 'module-base-path'); + + # Allow user to override normal processing of the module in a few ways, + # to make it easier to still be able to use kdesvn-build even when I + # can't be there to manually update every little special case. + if(get_option($module, 'override-url')) + { + return get_option($module, 'override-url'); + } + + if(get_option($module, 'tag')) + { + return handle_branch_tag_option($module, 'tags'); + } + + if(get_option($module, 'branch')) + { + return handle_branch_tag_option($module, 'branches'); + } + + # The following modules are in /trunk, not /trunk/KDE. There are others, + # but there are the important ones. The hash is associated with the value + # 1 so that we can do a boolean test by looking up the module name. + my @non_trunk_modules = qw(extragear kdenonbeta kdesupport koffice + playground qt-copy valgrind KDE kdereview www l10n); + + my $module_root = $module; + $module_root =~ s/\/.*//; # Remove everything after the first slash + + if (not $branch) + { + $branch = 'trunk/KDE'; + $branch = 'trunk' if list_has(@non_trunk_modules, $module_root); + } + + $branch =~ s/^\/*//; # Eliminate / at beginning of string. + $branch =~ s/\/*$//; # Likewise at the end. + + # Remove trailing slashes. + $svn_server =~ s/\/*$//; + + return "$svn_server/$branch/$module"; +} + +# Convienience subroutine to return the build directory for a module. Use +# this instead of get_subdir_path because this special-cases modules for you, +# such as qt-copy. +# TODO: From what I hear this hack is no longer necessary. Investigate this. +sub get_build_dir +{ + my $module = shift; + + # It is the responsibility of the caller to append $module! + return get_kdesvn_dir() if ($module eq 'qt-copy') and not get_option('qt-copy', 'use-qt-builddir-hack'); + return get_subdir_path($module, 'build-dir'); +} + +# Subroutine to return a list of the different log directories that are used +# by the different modules in the script. +sub get_all_log_directories +{ + my @module_list = keys %package_opts; + my %log_dict; + + # A hash is used to track directories to avoid duplicate entries. + unshift @module_list, "global"; + $log_dict{get_subdir_path($_, 'log-dir')} = 1 foreach @module_list; + + debug "Log directories are ", join (", ", keys %log_dict); + return keys %log_dict; +} + +# Subroutine to determine the build id for this invocation of the script. The +# idea of a build id is that we want to be able to run the script more than +# once in a day and still retain each set of logs. So if we run the script +# more than once in a day, we need to increment the build id so we have a +# unique value. This subroutine sets the global variable $BUILD_ID and +# $LOG_DATE for use by the logging subroutines. +sub setup_logging_subsystem +{ + my $min_build_id = "00"; + my $date = strftime "%F", localtime; # ISO 8601 date + my @log_dirs = get_all_log_directories(); + + for (@log_dirs) + { + my $id = "01"; + $id++ while -e "$_/$date-$id"; + + # We need to use a string comparison operator to keep + # the magic in the ++ operator. + $min_build_id = $id if $id gt $min_build_id; + } + + $LOG_DATE = $date; + $BUILD_ID = $min_build_id; +} + +# Convienience subroutine to return the log directory for a module. +# It also creates the directory and manages the 'latest' symlink. +# +# Returns undef on an error, or the name of the directory otherwise. +sub get_log_dir +{ + my $module = shift; + my $logbase = get_subdir_path($module, 'log-dir'); + my $logpath = "$logbase/$LOG_DATE-$BUILD_ID/$module"; + + $logpath = "$logbase/$LOG_DATE-$BUILD_ID" if $module eq 'global'; + + debug "Log directory for $module is $logpath"; + + if (not -e $logpath and not pretending and not super_mkdir($logpath)) + { + error "Unable to create log directory r[$logpath]"; + return undef; + } + + # Add symlink to the directory. + # TODO: This probably can result in a few dozen unnecessary calls to + # unlink and symlink, fix this. + if (not pretending) + { + unlink("$logbase/latest") if -l "$logbase/latest"; + symlink("$logbase/$LOG_DATE-$BUILD_ID", "$logbase/latest"); + } + + return $logpath; +} + +# This function returns true if the given option doesn't make sense with the +# given module. +# blacklisted($module, $option) +sub blacklisted +{ + my ($module, $option) = @_; + + # Known to not work. + my @unsermake_ban_list = qw/valgrind kde-common qt-copy kdebindings/; + + return list_has(@unsermake_ban_list, $module) if ($option eq 'use-unsermake'); + return 0; +} + +# This subroutine returns an option value for a given module. Some +# globals can't be overridden by a module's choice. If so, the +# module's choice will be ignored, and a warning will be issued. +# +# Option names are case-sensitive! +# +# First parameter: Name of module +# Second paramenter: Name of option +sub get_option +{ + my $module = shift; + my $option = shift; + my $global_opts = $package_opts{'global'}; + my $defaultQtCopyArgs = '-qt-gif -plugin-imgfmt-mng -thread -no-exceptions -debug -dlopen-opengl -plugin-sql-sqlite'; + my @lockedOpts = qw(source-dir svn-server qtdir libpath binpath kdedir + pretend disable-agent-check); + + # These options can't override globals + if (list_has(@lockedOpts, $option) or $module eq 'global') + { + return ${$global_opts}{"#$option"} if exists ${$global_opts}{"#$option"}; + return ${$global_opts}{$option}; + } + + # Don't even try this + return 0 if blacklisted($module, $option); + + my $ref = $package_opts{$module}; + + # Check for a sticky option + return $$ref{"#$option"} if exists $$ref{"#$option"}; + + # Next in order of precedence + if (defined ${$global_opts}{"#$option"} and not + ($module eq 'qt-copy' and $option eq 'configure-flags')) + { + return ${$global_opts}{"#$option"}; + } + + # No sticky options left. + # Configure flags and CXXFLAGS are appended to the global option + if (($module ne 'qt-copy' && $option eq 'configure-flags') + || $option eq 'cxxflags') + { + my $value = ${$global_opts}{$option}; + + if(defined $$ref{$option}) + { + my $modvalue = $$ref{$option}; + $value .= " $modvalue"; + } + + return $value; + } + + # As always qt-copy has to be difficult + if ($module eq 'qt-copy' and $option eq 'configure-flags') + { + return $defaultQtCopyArgs if not defined $$ref{$option}; + return $$ref{$option}; + } + + # Everything else overrides the global, unless of course it's not set. + # If we're reading for global options, we're pretty much done. + return $$ref{$option} if defined $$ref{$option}; + return ${$global_opts}{$option}; +} + +# Subroutine used to handle the checkout-only option. It handles +# updating subdirectories of an already-checked-out module. +# First parameter is the module, all remaining parameters are subdirectories +# to check out. +# +# Returns 0 on success, non-zero on failure. +sub update_module_subdirectories +{ + my $module = shift; + my $result; + + # If we have elements in @path, download them now + for my $dir (@_) + { + info "\tUpdating g[$dir]"; + $result = run_svn($module, "svn-up-$dir", [ 'svn', 'up', $dir ]); + return $result if $result; + } + + return 0; +} + +# Returns true if a module has a base component to their name (e.g. KDE/, +# extragear/, or playground). Note that modules that aren't in trunk/KDE +# don't necessary meet this criteria (e.g. kdereview is a module itself). +sub has_base_module +{ + my $module = shift; + + return $module =~ /^(extragear|playground|KDE)(\/[^\/]+)?$/; +} + +# Subroutine to return the directory that a module will be stored in. +# NOTE: The return value is a hash. The key 'module' will return the final +# module name, the key 'path' will return the full path to the module. The +# key 'fullpath' will return their concatenation. +# For example, with $module == 'KDE/kdelibs', and no change in the dest-dir +# option, you'd get something like: +# { +# 'path' => '/home/user/kdesvn/KDE', +# 'module' => 'kdelibs', +# 'fullpath' => '/home/user/kdesvn/KDE/kdelibs' +# } +# If dest-dir were changed to e.g. extragear-multimedia, you'd get: +# { +# 'path' => '/home/user/kdesvn', +# 'module' => 'extragear-multimedia', +# 'fullpath' => '/home/user/kdesvn/extragear-multimedia' +# } +# First parameter is the module. +# Second parameter is either source or build. +sub get_module_path_dir +{ + my $module = shift; + my $type = shift; + my $destdir = get_dest_dir($module); + my $srcbase = get_kdesvn_dir(); + $srcbase = get_build_dir($module) if $type eq 'build'; + + my $combined = "$srcbase/$destdir"; + + # Remove dup // + $combined =~ s/\/+/\//; + + my @parts = split(/\//, $combined); + my %result = (); + $result{'module'} = pop @parts; + $result{'path'} = join('/', @parts); + $result{'fullpath'} = "$result{path}/$result{module}"; + + return %result; +} + +sub get_fullpath +{ + my ($module, $type) = @_; + my %pathinfo = get_module_path_dir($module, $type); + + return $pathinfo{'fullpath'}; +} + +# Checkout a module that has not been checked out before, along with any +# subdirectories the user desires. +# The first parameter is the module to checkout (including extragear and +# playground modules), all remaining parameters are subdirectories of the +# module to checkout. +# Returns 0 on success, non-zero on failure. +sub checkout_module_path +{ + my ($module, @path) = @_; + my %pathinfo = get_module_path_dir($module, 'source'); + my $result; + my @args; + + if (not -e $pathinfo{'path'} and not super_mkdir($pathinfo{'path'})) + { + error "Unable to create path r[$pathinfo{path}]!"; + return 1; + } + + chdir($pathinfo{'path'}); + + push @args, ('svn', 'co'); + push @args, '-N' if scalar @path; + push @args, svn_module_url($module); + push @args, $pathinfo{'module'}; + + note "Checking out g[$module]"; + $result = run_svn($module, 'svn-co', \@args); + return $result if $result; + + chdir($pathinfo{'module'}) if scalar @path; + + return update_module_subdirectories($module, @path); +} + +# Update a module that has already been checked out, along with any +# subdirectories the user desires. +# The first parameter is the module to checkout (including extragear and +# playground modules), all remaining parameters are subdirectories of the +# module to checkout. +# Returns 0 on success, non-zero on failure. +sub update_module_path +{ + my ($module, @path) = @_; + my $fullpath = get_fullpath($module, 'source'); + my $result; + my @args; + + chdir $fullpath; + + push @args, ('svn', 'up'); + push @args, '-N' if scalar @path; + + note "Updating g[$module]"; + + $result = run_svn($module, 'svn-up', \@args); + + if($result) # Update failed, try svn cleanup. + { + info "\tUpdate failed, trying a cleanup."; + $result = safe_system('svn', 'cleanup'); + + return $result if $result; + + info "\tCleanup complete."; + # Now try again. + + $result = run_svn($module, 'svn-up-2', \@args); + } + + return $result if $result; + + # If the admin dir exists and is a soft link, remove it so that svn can + # update it if need be. The link will automatically be re-created later + # in the process if necessary by the build functions. + unlink ("$fullpath/admin") if -l "$fullpath/admin"; + + return update_module_subdirectories($module, @path); +} + +# Subroutine to run a command with redirected STDOUT and STDERR. First parameter +# is name of the log file (relative to the log directory), and the +# second parameter is a reference to an array with the command and +# its arguments +sub log_command +{ + my $pid; + my $module = shift; + my $filename = shift; + my @command = @{(shift)}; + my $logdir = get_log_dir($module); + + debug "log_command(): Module $module, Command: ", join(' ', @command); + + if (pretending) + { + pretend "\tWould have run g[", join (' ', @command); + return 0; + } + + if ($pid = fork) + { + # Parent + waitpid $pid, 0; + + # If the module fails building, set an internal flag in the module + # options with the name of the log file containing the error message. + my $result = $?; + set_error_logfile($module, "$filename.log") if $result; + + # If we are using the alias to a kdesvn-build function, it should have + # already printed the error message, so clear out errno (but still + # return failure status). + if ($command[0] eq 'kdesvn-build') + { + $! = 0; + } + + return $result; + } + else + { + # Child + if (not defined $logdir or not -e $logdir) + { + # Error creating directory for some reason. + error "\tLogging to std out due to failure creating log dir."; + } + + # Redirect stdout and stderr to the given file. + if (not debugging) + { +# Comment this out because it conflicts with make-install-prefix +# open (STDIN, "$logdir/$filename.log") or do { + error "Error opening $logdir/$filename.log for logfile."; + # Don't abort, hopefully STDOUT still works. + }; + } + else + { + open (STDOUT, "|tee $logdir/$filename.log") or do { + error "Error opening pipe to tee command."; + # Don't abort, hopefully STDOUT still works. + }; + } + + # Make sure we log everything. If the command is svn, it is possible + # that the client will produce output trying to get a password, so + # don't redirect stderr in that case. + open (STDERR, ">&STDOUT") unless $command[0] eq 'svn'; + + # Call internal function, name given by $command[1] + if($command[0] eq 'kdesvn-build') + { + debug "Calling $command[1]"; + + my $cmd = $command[1]; + splice (@command, 0, 2); # Remove first two elements. + + no strict 'refs'; # Disable restriction on symbolic subroutines. + if (not &{$cmd}(@command)) # Call sub + { + exit EINVAL; + } + + exit 0; + } + + # External command. + exec (@command) or do { + my $cmd_string = join(' ', @command); + error <) + { + chomp; + + # Update terminal (\e[K clears the line) if the percentage + # changed. + if (/([0-9]+)% (creating|compiling|linking)/) + { + print STDERR "\r$1% \e[K" unless ($1 == $last); + $last = $1; + } + } + + close(CHILD); + print STDERR "\r\e[K"; + + # If the module fails building, set an internal flag in the module + # options with the name of the log file containing the error message. + my $result = $?; + set_error_logfile($module, "$filename.log") if $result; + + return $result; + } + else + { + # Child + if (not defined $logdir or not -e $logdir) + { + # Error creating directory for some reason. + error "\tLogging to standard output due to failure creating log dir."; + } + + open (STDOUT, "|tee $logdir/$filename.log") or do { + error "Error opening pipe to tee command." + }; + + # Make sure we log everything. + open (STDERR, ">&STDOUT"); + + exec (@command) or do { + my $cmd_string = join(' ', @command); + error < q(-system-zlib -qt-gif -system-libjpeg -system-libpng + -plugin-imgfmt-mng -thread -no-exceptions -debug + -dlopen-opengl), + 'apply-qt-patches' => 'true', + +# See setup_kde35_hack() for why this option is here. + 'module-base-path' => 'branches/qt/3.3', + + 'use-qt-builddir-hack' => 'true', + 'use-unsermake' => 0, + 'set-env' => { }, + }; + + # That handy q() construct above kept the newlines, I don't want them. + $package_opts{'qt-copy'}{'conf-flags'} =~ s/\s+/ /gm; +} + +# Reads in the options from the config file and adds them to the option store. +# The first parameter is a reference to the file handle to read from. +# The second parameter is 'global' if we're reading the global section, or +# 'module' if we should expect an end module statement. +sub parse_module +{ + my ($fh, $module) = @_; + $module = 'global' unless $module; + + # Make sure we acknowledge that we read the module name in from the + # file. + if (not defined $package_opts{$module}) + { + $package_opts{$module} = { + 'set-env' => { } + }; + } + + # Read in each option + while (<$fh>) + { + # Handle line continuation + chomp; + + if(s/\\\s*$//) # Replace \ followed by optional space at EOL and try again. + { + $_ .= <$fh>; + redo unless eof($fh); + } + + s/#.*$//; # Remove comments + next if /^\s*$/; # Skip blank lines + + if($module eq 'global') + { + last if /^end\s+global/; # Stop + } + else + { + last if /^end\s+module/; # Stop + } + + # The option is the first word, followed by the + # flags on the rest of the line. The interpretation + # of the flags is dependant on the option. + my ($option, $value) = /^\s* # Find all spaces + ([-\w]+) # First match, alphanumeric, -, and _ + # (?: ) means non-capturing group, so (.*) is $value + # So, skip spaces and pick up the rest of the line. + (?:\s+(.*))?$/x; + + $value = "" unless defined $value; + + # Simplify this. + $value =~ s/\s+$//; + $value =~ s/^\s+//; + $value =~ s/\s+/ /; + + # Check for false keyword and convert it to Perl false. + $value = 0 if lc($value) =~ /^false$/; + + # Replace tildes with home directory. + 1 while ($value =~ s"(^|:|=)~/"$1$ENV{'HOME'}/"); + + set_option($module, $option, $value); + } +} + +# This subroutine reads in the settings from the user's configuration +# file. +sub read_options +{ + # The options are stored in the file $rcfile + my $success = 0; + my $global_opts = $package_opts{'global'}; + for my $file (@rcfiles) + { + if (open CONFIG, "<$file") + { + $success = 1; + $rcfile = $file; + last; + } + } + + if (not $success) + { + if(scalar @rcfiles == 1) + { + # This can only happen if the user uses --rc-file, if we fail to + # load the file, we need to fail to load. + error <) + { + s/#.*$//; # Remove comments + next if (/^\s*$/); # Skip blank lines + + # First command in .kdesvn-buildrc should be a global + # options declaration, even if none are defined. + if (not /^global\s*$/) + { + error "Invalid configuration file: $rcfile."; + error "Expecting global settings section!"; + exit 1; + } + + # Now read in each global option + parse_module(\*CONFIG, 'global'); + last; + } + + my $using_default = 1; + + # Now read in module settings + while () + { + s/#.*$//; # Remove comments + next if (/^\s*$/); # Skip blank lines + + # Get modulename (has dash, dots, slashes, or letters/numbers) + ($modulename) = /^module\s+([-\/\.\w]+)\s*$/; + + if (not $modulename) + { + warning "Invalid configuration file $rcfile!"; + warning "Expecting a start of module section."; + warning "Global settings will be retained."; + + $modulename = 'null'; # Keep reading the module section though. + } + + # Don't build default modules if user has their own wishes. + if ($using_default) + { + $using_default = 0; + @update_list = @build_list = ( ); + } + + parse_module(\*CONFIG, $modulename); + + next if ($modulename eq 'null'); + + # Done reading options, add this module to the update list + push (@update_list, $modulename) unless exists $ignore_list{$modulename}; + + # Add it to the build list, unless the build is only + # supposed to be done manually. + if (not get_option ($modulename, 'manual-build') and not exists $ignore_list{$modulename}) + { + push (@build_list, $modulename); + } + } + + close CONFIG; + + delete $package_opts{'null'}; # Just in case. + + # For the 3.5 edition we want to set the qt-copy option module-base-path + # to branches/qt/3.3 unless the user already has it set. + unless (exists $package_opts{'qt-copy'}{'module-base-path'}) + { + set_option ('qt-copy', 'module-base-path', 'branches/qt/3.3'); + } + + # If the user doesn't ask to build any modules, build a default set. + # The good question is what exactly should be built, but oh well. + setup_default_modules() if $using_default; +} + +# Subroutine to check if the given module needs special treatment to support +# srcdir != builddir. If this function returns true kdesvn-build will use a +# few hacks to simulate it, and will update e.g. configure paths appropriately +# as well. +sub module_needs_builddir_help +{ + my $module = shift; + my @module_help_list = qw/qt-copy kdebindings valgrind/; + + # qt-copy special case to support use-qt-builddir-hack. + if ($module eq 'qt-copy' and not get_option('qt-copy', 'use-qt-builddir-hack')) + { + return 0; + } + + return list_has(@module_help_list, $module); +} + +# This subroutine reads the set-env option for a given module and initializes +# the environment based on that setting. +sub setup_module_environment +{ + my $module = shift; + my ($key, $value); + + # Let's see if the user has set env vars to be set. + my $env_hash_ref = get_option($module, 'set-env'); + while (($key, $value) = each %{$env_hash_ref}) + { + setenv($key, $value); + } +} + +# Subroutine to initialize some environment variable for building +# KDE from Subversion. Change this section if a dependency changes later. +sub initialize_environment +{ + $ENV{"WANT_AUTOMAKE"} = "1.7"; + $ENV{"WANT_AUTOCONF_2_5"} = "1"; + $ENV{"PATH"} = get_option ('global', 'binpath'); + + my $svnserver = get_option ('global', 'svn-server'); + + my $pc_path = get_option('global', 'kdedir') . "/lib/pkgconfig"; + $pc_path .= ":" . $ENV{'PKG_CONFIG_PATH'} if ( exists $ENV{'PKG_CONFIG_PATH'} ); + $ENV{'PKG_CONFIG_PATH'} = $pc_path; + + if(-t STDOUT and get_option('global', 'colorful-output')) + { + $RED = "\e[31m"; + $GREEN = "\e[32m"; + $YELLOW = "\e[33m"; + $NORMAL = "\e[0m"; + $BOLD = "\e[1m"; + } + + # Set the process priority + setpriority PRIO_PROCESS, 0, get_option('global', 'niceness'); + + setup_module_environment ('global'); +} + +# Subroutine to get a list of modules to install, either from the command line +# if it's not empty, or based on the list of modules successfully built. +sub get_install_list +{ + my @install_list; + + if ($#ARGV > -1) + { + @install_list = @ARGV; + @ARGV = (); + } + else + { + # Get list of built items from $logdir/latest/build-status + my $logdir = get_subdir_path('global', 'log-dir'); + + if (not open BUILTLIST, "<$logdir/latest/build-status") + { + error "Can't determine what modules have built. You must"; + error "specify explicitly on the command line what modules to build."; + exit (1); # Don't finish, no lock has been taken. + } + + while () + { + chomp; + if (/Succeeded/) + { + # Clip to everything before the first colon. + my $module = (split(/:/))[0]; + push @install_list, $module; + } + } + + close BUILTLIST; + } + + return @install_list; +} + +# Print out an error message, and a list of modules that match that error +# message. It will also display the log file name if one can be determined. +# The message will be displayed all in uppercase, with PACKAGES prepended, so +# all you have to do is give a descriptive message of what this list of +# packages failed at doing. +sub output_failed_module_list($@) +{ + my ($message, @fail_list) = @_; + $message = uc $message; # Be annoying + + debug "Message is $message"; + debug "\tfor ", join(', ', @fail_list); + + if (scalar @fail_list > 0) + { + my $homedir = $ENV{'HOME'}; + my $logfile; + + warning "\nr[b[<<< PACKAGES $message >>>]"; + + for (@fail_list) + { + $logfile = get_option($_, '#error-log-file'); + $logfile = "No log file" unless $logfile; + $logfile =~ s|$homedir|~|; + + warning "r[$_] - g[$logfile]"; + } + } +} + +# This subroutine reads the fail_lists dictionary to automatically call +# output_failed_module_list for all the module failures in one function +# call. +sub output_failed_module_lists() +{ + for my $type (@fail_display_order) + { + my @failures = @{$fail_lists{$type}}; + output_failed_module_list("failed to $type", @failures); + } +} + +# This subroutine extract the value from options of the form --option=value, +# which can also be expressed as --option value. The first parameter is the +# option that the user passed to the cmd line (e.g. --prefix=/opt/foo), and +# the second parameter is a reference to the list of command line options. +# The return value is the value of the option (the list might be shorter by +# 1, copy it if you don't want it to change), or undef if no value was +# provided. +sub extract_option_value($\@) +{ + my ($option, $options_ref) = @_; + + if ($option =~ /=/) + { + my @value = split(/=/, $option); + shift @value; # We don't need the first one, that the --option part. + + return undef if (scalar @value == 0); + + # If we have more than one element left in @value it's because the + # option itself has an = in it, make sure it goes back in the answer. + return join('=', @value); + } + + return undef if scalar @{$options_ref} == 0; + return shift @{$options_ref}; +} + +# Utility subroutine to handle setting the environment variable type of value. +# Returns true (non-zero) if this subroutine handled everything, 0 otherwise. +# The first parameter should by the reference to the hash with the 'set-env' +# hash ref, second parameter is the exact option to check, and the third +# option is the value to set that option to. +sub handle_set_env +{ + my ($href, $option, $value) = @_; + + return 0 if $option !~ /^#?set-env$/; + + my ($var, @values) = split(' ', $value); + + $$href{$option} = ( ) unless exists $$href{$option}; + $$href{$option}{$var} = join(' ', @values); + + return 1; +} + +# Sets the option for the given module to the given value. If the data for the +# module doesn't exist yet, it will be defined starting with a default value. +# First parameter: module to set option for (or 'global') +# Second parameter: option name (Preceded by # for a sticky option) +# Third parameter: option value +# Return value is void +sub set_option +{ + my ($module, $option, $value) = @_; + + # Set module options + if (not exists $package_opts{$module}) + { + $package_opts{$module} = { + 'set-env' => { } + }; + } + + return if handle_set_env($package_opts{$module}, $option, $value); + $package_opts{$module}{$option} = $value; +} + +# Subroutine to process the command line arguments. Any arguments so +# processed will be removed from @ARGV. +# The arguments are generally documented in doc.html now. +# NOTE: Don't call finish() from this routine, the lock hasn't been obtained. +# NOTE: The options have not been loaded yet either. Any option which +# requires more than rudimentary processing should set a flag for later work. +sub process_arguments +{ + my $arg; + my $version = "kdesvn-build 0.97.6 (KDE 3.5 Edition)"; + my $author = < + +Many people have contributed code, bugfixes, and documentation. + +Please report bugs using the KDE Bugzilla, at http://bugs.kde.org/ +DONE + + my @argv; + + while ($_ = shift @ARGV) + { + SWITCH: { + /^(--version)$/ && do { print "$version\n"; exit; }; + /^--author$/ && do { print $author; exit; }; + /^(-h)|(--?help)$/ && do { + print < Read configuration from filename instead of default. + --nice= Allows you to run the script with a lower priority + The default value is 10 (lower priority by 10 steps). + --prefix=/kde/path This option is a shortcut to change the setting for + kdedir from the command line. It implies + --reconfigure. + + --resume Tries to resume the make process from the last time + the script was run, without performing the Subversion + update. + --resume-from= Starts building from the given package, without + performing the Subversion update. + --revision (or -r)= Forces update to revision from Subversion. + + --refresh-build Start the build from scratch. + --reconfigure Run configure again, but don't clean the build + directory or re-run make -f Makefile.cvs. + --recreate-configure Run make -f Makefile.cvs again to redo the configure + script. + --no-rebuild-on-fail Don't try to rebuild a module from scratch if it + failed building and we didn't already try to build it + from scratch. + --build-system-only Create the build infrastructure, but don't actually + perform the build. + --install Try to install the packages passed on the command + line, or all packages in ~/.kdesvn-buildrc that don't + have manual-build set. Building and Subversion + updates are not performed. + + --
    ";
    +
    +      print OUTPUT sprintf '%-5i%s%-14s%s%s', $lineno, $linkstr, $revauthor,$linkendstr, $content;
    +      print OUTPUT "
    + + + + + + + Actor + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + +
    + <xsl:value-of select="$element_name"/> + + + + + + + + + Use Case + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + + + + + + + + + +
    + <xsl:value-of select="$element_name"/> + + + + + + + + Interface + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + + + + + + + + + +
    + <xsl:value-of select="$element_name"/> + + + + + + + + + Class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + + + + diagrambeginnamebeginnameend + diagramdocumentationbegindocumentationend + +
    + <xsl:value-of select="@name"/> + + + + + + + + .png + + + + +
    + +
    + &actor; + + + +
    +
    + + +
    + &usecase; + + + +
    +
    + + +
    + &classes; + + +
    +
    + + diagramend +
    + + + + + + + + + + +
    + Specifications: + + + + + + + + + + , + + +
    +
    + +
    + + + + + + + + + + +
    + Realizations: + + + + + + + + + + + , + + +
    +
    + +
    + + + + + + + + + + +
    + Supertypes: + + + + + + + + + + + , + + +
    +
    +
    + + + + + + + + + +
    + Subtypes: + + + + + + + + + + + , + + +
    +
    +
    + + + + + + + + + +
    + Associations + visibility, type, properties. + + + + + + + + +
    +
    +
    + + + + + + + + + + + + + + &nbsp; + + + + + + + + + + + + + + + + + + + + + + Rolename: + + + + + (none) + + + + + + + + + + + + + Navigable: + + + + + + + + + Ordering: + + + + + + + + + + + Multiplicity: + + + + + + + + .. + + + + + + + + + + + + + + + + + + + Attributes: + + + visibility + type + name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Operations: + + + visibility + return + name + + + + + + + + + + + + + + + + + + + + + + + + + void + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &nbsp; + + + parameters: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + classifier + interface + datatype + classifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + : + + + + + + + + + + + + + + + : + + + + + + + + + + + +
    + + +
    &packagename;
    +
    &classname;
    + +
    + + Stereotype: + + Abstarct: + + &yes; + + + &no; + + + Visibility: + + public + + + private + + + protected + + + + +
    +
    +
    +
    + +
    + + + + +
    &attributes;
    + + + - + Static: + + &yes; + + + &no; + + + Visibility: + + public + + + private + + + protected + + + Default �t�: + + + + + +
    + + + +
    &metodes;
    + + + + + abstract + + + public + + + private + + + protected + + + + + ( + + + ) + + + + ¶meters; + +
    + + + + + + +
    +
    + +
    +
    +
    + + + + + + + + , + + + + diff --git a/umbrello/umbrello/docwindow.cpp b/umbrello/umbrello/docwindow.cpp new file mode 100644 index 00000000..d92c13f4 --- /dev/null +++ b/umbrello/umbrello/docwindow.cpp @@ -0,0 +1,223 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "docwindow.h" + +// qt/kde includes +#include +#include +#include +#include + +// local includes +#include "associationwidget.h" +#include "umldoc.h" +#include "umlobject.h" +#include "umlview.h" +#include "umlwidget.h" + + +DocWindow::DocWindow( UMLDoc * doc, QWidget *parent, const char *name ) : QWidget( parent, name ) { + //setup visual display + QHBoxLayout * mainLayout = new QHBoxLayout( this ); + + m_pDocGB = new QGroupBox( i18n( "Documentation" ), this ); + mainLayout -> addWidget( m_pDocGB ); + + QHBoxLayout * docLayout = new QHBoxLayout( m_pDocGB ); + m_pDocMLE = new QMultiLineEdit( m_pDocGB ); + m_pDocMLE -> setText( "" ); + docLayout -> setMargin( fontMetrics().height() ); + docLayout -> addWidget( m_pDocMLE); + m_pDocMLE -> setWordWrap(QMultiLineEdit::WidgetWidth); + + //setup the documentation variables + //show projects documentation to start + m_pUMLDoc = doc; + m_Showing = st_Project; + m_pUMLObject = 0; + m_pUMLView = 0; + m_pUMLWidget = 0; + m_pAssocWidget = 0; + updateDocumentation( true, true ); +} + +DocWindow::~DocWindow() {} + +void DocWindow::showDocumentation( UMLObject * object, bool overwrite ) { + if( object == m_pUMLObject && !overwrite ) + return; + if( object != m_pUMLObject ) + updateDocumentation( true ); + + m_Showing = st_UMLObject; + if( !object ) { + m_pDocMLE->setText( m_pUMLDoc->getDocumentation() ); + m_pUMLObject = 0; + return; + } + m_pUMLObject = object; + m_pDocMLE -> setText( m_pUMLObject -> getDoc() ); +} + +void DocWindow::updateDocumentation( bool clear, bool startup ) { + + bool mark_modified = false; + if( m_pUMLObject ) + { + // the file is marked modified, if the documentation differs + // we don't do this on startup/load of a xmi file, because every time + // modified is set, we get another undo/redo backup point + if ( startup == false && m_pDocMLE -> text() != m_pUMLObject -> getDoc() ) + { + mark_modified = true; + } + m_pUMLObject -> setDoc( m_pDocMLE -> text() ); + + } else if( m_pUMLView ) { + // the file is marked modified, if the documentation differs + // we don't do this on startup/load of a xmi file, because every time + // modified is set, we get another undo/redo backup point + if ( startup == false && m_pDocMLE -> text() != m_pUMLView -> getDoc() ) + { + mark_modified = true; + } + + m_pUMLView -> setDoc( m_pDocMLE -> text() ); + } else if ( m_pUMLWidget ) { + // the file is marked modified, if the documentation differs + // we don't do this on startup/load of a xmi file, because every time + // modified is set, we get another undo/redo backup point + if ( startup == false && m_pDocMLE -> text() != m_pUMLWidget -> getDoc() ) + { + mark_modified = true; + } + + m_pUMLWidget -> setDoc( m_pDocMLE -> text() ); + } else if( m_pAssocWidget ) { + // the file is marked modified, if the documentation differs + // we don't do this on startup/load of a xmi file, because every time + // modified is set, we get another undo/redo backup point + if ( startup == false && m_pDocMLE -> text() != m_pAssocWidget -> getDoc() ) + { + mark_modified = true; + } + + m_pAssocWidget -> setDoc( m_pDocMLE -> text() ); + } else { + // the file is marked modified, if the documentation differs + // we don't do this on startup/load of a xmi file, because every time + // modified is set, we get another undo/redo backup point + if ( startup == false && m_pDocMLE -> text() != m_pUMLDoc->getDocumentation() ) + { + mark_modified = true; + } + + m_pUMLDoc->setDocumentation( m_pDocMLE->text() ); + } + + // now do the setModified call + if (mark_modified == true) + m_pUMLDoc -> setModified( true ); + + // we should show the documentation of the whole project + if( clear ) { + m_pDocMLE->setText( m_pUMLDoc->getDocumentation() ); + m_pUMLObject = 0; + m_pUMLView = 0; + m_pUMLWidget = 0; + m_pAssocWidget = 0; + m_Showing = st_Project; + } + + return; +} + +void DocWindow::showDocumentation( UMLView * view, bool overwrite ) { + if( view == m_pUMLView && !overwrite ) + return; + if( view != m_pUMLView ) + updateDocumentation( true ); + m_Showing = st_UMLView; + if( !view ) { + m_pDocMLE->setText( m_pUMLDoc->getDocumentation() ); + m_pUMLView = 0; + return; + } + m_pUMLView = view; + m_pDocMLE -> setText( m_pUMLView -> getDoc() ); +} + +void DocWindow::showDocumentation( UMLWidget * widget, bool overwrite ) { + if( widget == m_pUMLWidget && !overwrite ) + return; + if( widget != m_pUMLWidget ) + updateDocumentation( true ); + m_Showing = st_UMLWidget; + if( !widget ) { + m_pDocMLE->setText( m_pUMLDoc->getDocumentation() ); + m_pUMLWidget = 0; + return; + } + m_pUMLWidget = widget; + m_pDocMLE -> setText( m_pUMLWidget -> getDoc() ); +} + +void DocWindow::showDocumentation( AssociationWidget * widget, bool overwrite ) { + if( widget == m_pAssocWidget && !overwrite ) + return; + if( widget != m_pAssocWidget ) + updateDocumentation( true ); + m_Showing = st_Association; + if( !widget ) { + m_pDocMLE->setText( m_pUMLDoc->getDocumentation() ); + m_pAssocWidget = 0; + return; + } + m_pAssocWidget = widget; + m_pDocMLE -> setText( m_pAssocWidget -> getDoc() ); +} + +void DocWindow::newDocumentation( ) { + m_pUMLView = 0; + m_pUMLObject = 0; + m_pUMLWidget = 0; + m_pAssocWidget = 0; + m_Showing = st_Project; + m_pDocMLE->setText( m_pUMLDoc->getDocumentation() ); +} + +bool DocWindow::isTyping() +{ + if (m_pDocMLE->hasFocus()) + return true; + else + return false; +} + +void DocWindow::slotAssociationRemoved(AssociationWidget* association) { + if (association == m_pAssocWidget || association->getUMLObject() == m_pUMLObject) { + // In old code, the below line crashed (bugs.kde.org/89860) + // A hotfix was made and detailed analysis was To Be Done: + // newDocumentation() + // However, it seems to have been fixed and the below line seems to work fine + updateDocumentation(true); + } +} + +void DocWindow::slotWidgetRemoved(UMLWidget* widget) { + if (widget == m_pUMLWidget || widget->getUMLObject() == m_pUMLObject) { + updateDocumentation(true); + } +} + +#include "docwindow.moc" diff --git a/umbrello/umbrello/docwindow.h b/umbrello/umbrello/docwindow.h new file mode 100644 index 00000000..fb488c5f --- /dev/null +++ b/umbrello/umbrello/docwindow.h @@ -0,0 +1,168 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef DOCWINDOW_H +#define DOCWINDOW_H + +#include + +class AssociationWidget; +class QGroupBox; +class QMultiLineEdit; +class UMLObject; +class UMLDoc; +class UMLView; +class UMLWidget; + +/** + * @author Paul Hensgen + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + +class DocWindow : public QWidget { + Q_OBJECT +public: + /** + * Constructor + */ + explicit DocWindow( UMLDoc * doc, QWidget *parent = 0, const char *name = 0 ); + + /** + * Deconstructor + */ + ~DocWindow(); + + /** + * Called when a widget wishes to display its documentation in the + * doc window. If there was already documentation there, that will + * be updated before being removed from the view. + * + * Also call this function if you update the documentation in another + * place, such as a properties dialog. Just set overwrite to true. + * + * Overwrite is used when you believe that the documentation window + * is already displaying documentation for the widget you wish to + * display. + * Overwrite just determines whose version is more up to date. + */ + void showDocumentation( UMLObject * object, bool overwrite = false ); + + /** + * This method is the same as the one for UMLObjects except it + * displays documentation for a diagram. + */ + void showDocumentation( UMLView * view, bool overwrite = false ); + + /** + * This method is the same as the one for UMLObjects except it + * displays documentation for an object instance (StateWidget/ + * ObjectWidget). + */ + void showDocumentation( UMLWidget * widget, bool overwrite = false ); + + /** + * This method is the same as the one for UMLObjects except it + * displays documentation for an association instance + * (AssociationWidget). + */ + void showDocumentation( AssociationWidget * widget, bool overwrite = false ); + + /** + * Call when you wish move changes in the doc window back into the + * members documentation. + * + * If clear is true the doc window will display the documentation + * for the current project instead of the widget documentation. + * + * This is usually called before displaying a properties dialog. + * + * @param clear If true, show the documentation of current project + * @param startup If true, no setModified(true) calls will be done and nothing is pushed to the undo stack + */ + void updateDocumentation( bool clear = false, bool startup = false ); + + + /** + * Re-initializes the class for a new document. + */ + void newDocumentation( ); + + /** + * Checks if the user is typing in the documentation edit window + */ + bool isTyping(); + +public slots: + + /** + * An association was removed from the UMLView. + * If the association removed was the association which documentation is + * being shown, m_pAssocWidget is set to 0. + */ + void slotAssociationRemoved(AssociationWidget* association); + + /** + * A widget was removed from the UMLView. + * If the association removed was the association which documentation is + * being shown, m_pUMLWidget is set to 0. + */ + void slotWidgetRemoved(UMLWidget* widget); + +private: + /** + * Used internally to know which type of object we are showing + * documentation for. + */ + enum Showing_Type { + st_Project, + st_UMLView, + st_UMLObject, + st_UMLWidget, + st_Association + }; + + /** + * A pointer to the UMLObject we are going to show documentation. + */ + UMLObject * m_pUMLObject; + + /** + * A pointer to the UMLView we are going to show documentation. + */ + UMLView * m_pUMLView; + + /** + * A pointer to the Project we are going to show documentation. + */ + UMLDoc * m_pUMLDoc; + + /** + * A pointer to the UMLWidget we are going to show documentation. + */ + UMLWidget * m_pUMLWidget; + + /** + * A pointer to the association we are going to show documentation. + */ + AssociationWidget * m_pAssocWidget; + + /** + * Which type of documentation we are showing. + */ + Showing_Type m_Showing; + + //visual widgets + QMultiLineEdit * m_pDocMLE; + QGroupBox * m_pDocGB; + +}; + +#endif diff --git a/umbrello/umbrello/entity.cpp b/umbrello/umbrello/entity.cpp new file mode 100644 index 00000000..b3d842ac --- /dev/null +++ b/umbrello/umbrello/entity.cpp @@ -0,0 +1,217 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "entity.h" +// qt/kde includes +#include +#include +#include +// app includes +#include "entityattribute.h" +#include "umldoc.h" +#include "uml.h" +#include "uniqueid.h" +#include "clipboard/idchangelog.h" +#include "dialogs/umlentityattributedialog.h" + +UMLEntity::UMLEntity(const QString& name, Uml::IDType id) : UMLClassifier(name, id) { + init(); +} + +UMLEntity::~UMLEntity() { + m_List.clear(); +} + +bool UMLEntity::operator==( UMLEntity& rhs ) { + return UMLClassifier::operator==(rhs); +} + +void UMLEntity::copyInto(UMLEntity *rhs) const +{ + UMLClassifier::copyInto(rhs); +} + +UMLObject* UMLEntity::clone() const +{ + UMLEntity* clone = new UMLEntity(); + copyInto(clone); + + return clone; +} + +void UMLEntity::init() { + m_BaseType = Uml::ot_Entity; +} + +UMLAttribute* UMLEntity::createAttribute(const QString &name /*=null*/, UMLObject *type /*=NULL*/) { + Uml::IDType id = UniqueID::gen(); + QString currentName; + if (name.isNull()) { + currentName = uniqChildName(Uml::ot_EntityAttribute); + } else { + currentName = name; + } + const Settings::OptionState optionState = Settings::getOptionState(); + Uml::Visibility scope = optionState.classState.defaultAttributeScope; + UMLEntityAttribute* newAttribute = new UMLEntityAttribute(this, currentName, id, scope, type); + + int button = QDialog::Accepted; + bool goodName = false; + + //check for name.isNull() stops dialog being shown + //when creating attribute via list view + while (button==QDialog::Accepted && !goodName && name.isNull()) { + UMLEntityAttributeDialog attributedialog(0, newAttribute); + button = attributedialog.exec(); + QString name = newAttribute->getName(); + + if(name.length() == 0) { + KMessageBox::error(0, i18n("That is an invalid name."), i18n("Invalid Name")); + } else if ( findChildObject(name) != NULL ) { + KMessageBox::error(0, i18n("That name is already being used."), i18n("Not a Unique Name")); + } else { + goodName = true; + } + } + + if (button != QDialog::Accepted) { + delete newAttribute; + return NULL; + } + + addEntityAttribute(newAttribute); + + UMLDoc *umldoc = UMLApp::app()->getDocument(); + umldoc->signalUMLObjectCreated(newAttribute); + return newAttribute; +} + +UMLObject* UMLEntity::addEntityAttribute(const QString& name, Uml::IDType id) { + UMLEntityAttribute* literal = new UMLEntityAttribute(this, name, id); + m_List.append(literal); + emit entityAttributeAdded(literal); + UMLObject::emitModified(); + connect(literal,SIGNAL(modified()),this,SIGNAL(modified())); + return literal; +} + +bool UMLEntity::addEntityAttribute(UMLEntityAttribute* attribute, IDChangeLog* Log /* = 0*/) { + QString name = (QString)attribute->getName(); + if (findChildObject(name) == NULL) { + attribute->parent()->removeChild(attribute); + this->insertChild(attribute); + m_List.append(attribute); + emit entityAttributeAdded(attribute); + UMLObject::emitModified(); + connect(attribute,SIGNAL(modified()),this,SIGNAL(modified())); + return true; + } else if (Log) { + Log->removeChangeByNewID( attribute->getID() ); + delete attribute; + } + return false; +} + +bool UMLEntity::addEntityAttribute(UMLEntityAttribute* attribute, int position) { + QString name = (QString)attribute->getName(); + if (findChildObject(name) == NULL) { + attribute->parent()->removeChild(attribute); + this->insertChild(attribute); + if ( position >= 0 && position <= (int)m_List.count() ) { + m_List.insert(position,attribute); + } else { + m_List.append(attribute); + } + emit entityAttributeAdded(attribute); + UMLObject::emitModified(); + connect(attribute,SIGNAL(modified()),this,SIGNAL(modified())); + return true; + } + return false; +} + +int UMLEntity::removeEntityAttribute(UMLClassifierListItem* literal) { + if (!m_List.remove((UMLEntityAttribute*)literal)) { + kDebug() << "can't find att given in list" << endl; + return -1; + } + emit entityAttributeRemoved(literal); + UMLObject::emitModified(); + // If we are deleting the object, then we don't need to disconnect..this is done auto-magically + // for us by QObject. -b.t. + // disconnect(a,SIGNAL(modified()),this,SIGNAL(modified())); + delete literal; + return m_List.count(); +} + +int UMLEntity::entityAttributes() { + UMLClassifierListItemList entityAttributes = getFilteredList(Uml::ot_EntityAttribute); + return entityAttributes.count(); +} + +void UMLEntity::signalEntityAttributeRemoved(UMLClassifierListItem *eattr) { + emit entityAttributeRemoved(eattr); +} + +bool UMLEntity::resolveRef() { + bool success = UMLClassifier::resolveRef(); + for (UMLObjectListIt oit(m_List); oit.current(); ++oit) { + UMLObject* obj = oit.current(); + if (obj->resolveRef()) { + UMLClassifierListItem *cli = static_cast(obj); + if (cli->getBaseType() == Uml::ot_EntityAttribute) + emit entityAttributeAdded(cli); + } + } + return success; +} + +void UMLEntity::saveToXMI(QDomDocument& qDoc, QDomElement& qElement) { + QDomElement entityElement = UMLObject::save("UML:Entity", qDoc); + //save operations + UMLClassifierListItemList entityAttributes = getFilteredList(Uml::ot_EntityAttribute); + UMLClassifierListItem* pEntityAttribute = 0; + for (UMLClassifierListItemListIt it(entityAttributes); + (pEntityAttribute = it.current()) != NULL; ++it) { + pEntityAttribute->saveToXMI(qDoc, entityElement); + } + qElement.appendChild(entityElement); +} + +bool UMLEntity::load(QDomElement& element) { + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + if (node.isComment()) { + node = node.nextSibling(); + continue; + } + QDomElement tempElement = node.toElement(); + QString tag = tempElement.tagName(); + if (Uml::tagEq(tag, "EntityAttribute")) { // for backward compatibility + UMLEntityAttribute* pEntityAttribute = new UMLEntityAttribute(this); + if( !pEntityAttribute->loadFromXMI(tempElement) ) { + return false; + } + m_List.append(pEntityAttribute); + } else if (tag == "stereotype") { + kDebug() << "UMLEntity::load(" << m_Name + << "): losing old-format stereotype." << endl; + } else { + kWarning() << "unknown child type in UMLEntity::load" << endl; + } + node = node.nextSibling(); + }//end while + return true; +} + + +#include "entity.moc" diff --git a/umbrello/umbrello/entity.h b/umbrello/umbrello/entity.h new file mode 100644 index 00000000..7a4a2ce9 --- /dev/null +++ b/umbrello/umbrello/entity.h @@ -0,0 +1,155 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef ENTITY_H +#define ENTITY_H + +#include "classifier.h" + +class UMLEntityAttribute; + +/** + * This class contains the non-graphical information required for a UML + * Entity. + * This class inherits from @ref UMLClassifier which contains most of the + * information. + * + * @short Non-graphical Information for an Entity. + * @author Jonathan Riddell + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class UMLEntity : public UMLClassifier { + Q_OBJECT +public: + /** + * Sets up an entity. + * + * @param name The name of the Entity. + * @param id The unique id of the Entity. + */ + explicit UMLEntity(const QString& name = "", Uml::IDType id = Uml::id_None); + + /** + * Standard deconstructor. + */ + virtual ~UMLEntity(); + + /** + * Overloaded '==' operator. + */ + bool operator==(UMLEntity& rhs); + + /** + * Copy the internal presentation of this object into the new + * object. + */ + virtual void copyInto(UMLEntity* rhs) const; + + /** + * Make a clone of this object. + */ + virtual UMLObject* clone() const; + + /** + * Creates an entity attribute for the parent concept. + * Reimplementation of method from UMLClassifier. + * + * @param name An optional name, used by when creating through UMLListView + * @param type An optional type, used by when creating through UMLListView + * @return The UMLEntityAttribute created + */ + UMLAttribute* createAttribute(const QString &name = QString::null, + UMLObject *type = 0); + + /** + * Adds an entityAttribute to the entity. + * + * @param name The name of the entityAttribute. + * @param id The id of the entityAttribute (optional.) + * If omitted a new ID is assigned internally. + * @return Pointer to the UMLEntityAttribute created. + */ + UMLObject* addEntityAttribute(const QString &name, Uml::IDType id = Uml::id_None); + + /** + * Adds an already created entityAttribute. + * The entityAttribute object must not belong to any other concept. + * + * @param att Pointer to the UMLEntityAttribute. + * @param Log Pointer to the IDChangeLog. + * @return True if the entityAttribute was successfully added. + */ + bool addEntityAttribute(UMLEntityAttribute* att, IDChangeLog* Log = 0); + + /** + * Adds an entityAttribute to the entity, at the given position. + * If position is negative or too large, the entityAttribute is added + * to the end of the list. + * + * @param att Pointer to the UMLEntityAttribute. + * @param position Position index for the insertion. + * @return True if the entityAttribute was successfully added. + */ + //TODO: give default value -1 to position (append) - now it conflicts with the method above.. + bool addEntityAttribute(UMLEntityAttribute* att, int position ); + + /** + * Removes an entityAttribute from the class. + * + * @param a The entityAttribute to remove. + * @return Count of the remaining entityAttributes after removal. + * Returns -1 if the given entityAttribute was not found. + */ + int removeEntityAttribute(UMLClassifierListItem* a); + + /** + * Emit the entityAttributeRemoved signal. + */ + void signalEntityAttributeRemoved(UMLClassifierListItem *eattr); + + /** + * Returns the number of entityAttributes for the class. + * + * @return The number of entityAttributes for the class. + */ + int entityAttributes() ; + + /** + * Resolve the types referenced by our UMLEntityAttributes. + * Reimplements the method from UMLClassifier. + */ + virtual bool resolveRef(); + + /** + * Creates the element including its entityliterals. + */ + virtual void saveToXMI(QDomDocument& qDoc, QDomElement& qElement); + +signals: + void entityAttributeAdded(UMLClassifierListItem*); + void entityAttributeRemoved(UMLClassifierListItem*); + +protected: + /** + * Loads the element including its entityAttributes. + */ + bool load(QDomElement& element); + +private: + /** + * Initializes key variables of the class. + */ + void init(); + +}; + +#endif // ENTITY_H + diff --git a/umbrello/umbrello/entityattribute.cpp b/umbrello/umbrello/entityattribute.cpp new file mode 100644 index 00000000..8855efcb --- /dev/null +++ b/umbrello/umbrello/entityattribute.cpp @@ -0,0 +1,178 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "entityattribute.h" +// qt/kde includes +#include +#include +// app includes +#include "umlcanvasobject.h" +#include "umldoc.h" +#include "uml.h" +#include "dialogs/umlentityattributedialog.h" +#include "object_factory.h" + +UMLEntityAttribute::UMLEntityAttribute( const UMLObject *parent, const QString& name, + Uml::IDType id, Uml::Visibility s, + UMLObject *type, const QString& iv ) + : UMLAttribute(parent, name, id, s, type, iv) { + init(); + if (m_pSecondary) { + m_pSecondary->setBaseType(Uml::ot_Entity); + } +} + +UMLEntityAttribute::UMLEntityAttribute(const UMLObject *parent) : UMLAttribute(parent) { + init(); +} + +UMLEntityAttribute::~UMLEntityAttribute() { } + +void UMLEntityAttribute::init() { + m_BaseType = Uml::ot_EntityAttribute; + m_indexType = Uml::None; + m_autoIncrement = false; + m_null = false; +} + +QString UMLEntityAttribute::getAttributes() const{ + return m_attributes; +} + +void UMLEntityAttribute::setAttributes(const QString& attributes) { + m_attributes = attributes; +} + +QString UMLEntityAttribute::getValues() const{ + return m_values; +} + +void UMLEntityAttribute::setValues(const QString& values) { + m_values = values; +} + +bool UMLEntityAttribute::getAutoIncrement() const{ + return m_autoIncrement; +} + +void UMLEntityAttribute::setAutoIncrement(const bool autoIncrement) { + m_autoIncrement = autoIncrement; +} + +Uml::DBIndex_Type UMLEntityAttribute::getIndexType() const{ + return m_indexType; +} + +void UMLEntityAttribute::setIndexType(const Uml::DBIndex_Type indexType) { + m_indexType = indexType; +} + +bool UMLEntityAttribute::getNull() const{ + return m_null; +} + +void UMLEntityAttribute::setNull(const bool nullIn) { + m_null = nullIn; +} + +QString UMLEntityAttribute::toString(Uml::Signature_Type sig) { + QString s; + //FIXME + + if(sig == Uml::st_ShowSig || sig == Uml::st_NoSig) { + s=m_Vis.toString(true) + ' '; + } else + s = ""; + + if(sig == Uml::st_ShowSig || sig == Uml::st_SigNoVis) { + QString string = s + getName() + " : " + getTypeName(); + if(m_InitialValue.length() > 0) + string += " = " + m_InitialValue; + return string; + } else + return s + getName(); +} + +bool UMLEntityAttribute::operator==( UMLEntityAttribute &rhs) { + if( this == &rhs ) + return true; + + if( !UMLObject::operator==( rhs ) ) + return false; + + // The type name is the only distinguishing criterion. + // (Some programming languages might support more, but others don't.) + if (m_pSecondary != rhs.m_pSecondary) + return false; + + return true; +} + +void UMLEntityAttribute::copyInto(UMLEntityAttribute *rhs) const +{ + // call the parent first. + UMLClassifierListItem::copyInto(rhs); + + // Copy all datamembers + rhs->m_pSecondary = m_pSecondary; + rhs->m_SecondaryId = m_SecondaryId; + rhs->m_InitialValue = m_InitialValue; + rhs->m_ParmKind = m_ParmKind; +} + +UMLObject* UMLEntityAttribute::clone() const +{ + UMLEntityAttribute* clone = new UMLEntityAttribute( (UMLEntityAttribute*)parent() ); + copyInto(clone); + + return clone; +} + + +void UMLEntityAttribute::saveToXMI( QDomDocument & qDoc, QDomElement & qElement ) { + QDomElement entityattributeElement = UMLObject::save("UML:EntityAttribute", qDoc); + if (m_pSecondary == NULL) { + kDebug() << "UMLEntityAttribute::saveToXMI(" << m_Name + << "): m_pSecondary is NULL, using local name " + << m_SecondaryId << endl; + entityattributeElement.setAttribute( "type", m_SecondaryId ); + } else { + entityattributeElement.setAttribute( "type", ID2STR(m_pSecondary->getID()) ); + } + entityattributeElement.setAttribute( "initialValue", m_InitialValue ); + entityattributeElement.setAttribute( "dbindex_type", m_indexType ); + entityattributeElement.setAttribute( "values", m_values ); + entityattributeElement.setAttribute( "attributes", m_attributes ); + entityattributeElement.setAttribute( "auto_increment", m_autoIncrement ); + entityattributeElement.setAttribute( "allow_null", m_null ); + qElement.appendChild( entityattributeElement ); +} + +bool UMLEntityAttribute::load( QDomElement & element ) { + if (! UMLAttribute::load(element)) + return false; + int indexType = element.attribute( "dbindex_type", "1100" ).toInt(); + m_indexType = ( Uml::DBIndex_Type )indexType; + m_values = element.attribute( "values", "" ); + m_attributes = element.attribute( "attributes", "" ); + m_autoIncrement = ( bool )element.attribute( "auto_increment", "" ).toInt(); + m_null = ( bool )element.attribute( "allow_null", "" ).toInt(); + return true; +} + +bool UMLEntityAttribute::showPropertiesDialog(QWidget* parent) { + UMLEntityAttributeDialog dialog(parent, this); + return dialog.exec(); +} + +#include "entityattribute.moc" + diff --git a/umbrello/umbrello/entityattribute.h b/umbrello/umbrello/entityattribute.h new file mode 100644 index 00000000..76af37a7 --- /dev/null +++ b/umbrello/umbrello/entityattribute.h @@ -0,0 +1,183 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef ENTITYATTRIBUTE_H +#define ENTITYATTRIBUTE_H + +#include "attribute.h" +#include "umlnamespace.h" + +/** + * This class is used to set up information for an entityattribute. This is a database field + * It has a type, name, index type and default value. + * + * @short Sets up entityattribute information. + * @author Jonathan Riddell + * @see UMLObject + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class UMLEntityAttribute : public UMLAttribute { + Q_OBJECT +public: + /** + * Sets up an entityattribute. + * + * @param parent The parent of this UMLEntityAttribute. + * @param name The name of this UMLEntityAttribute. + * @param id The unique id given to this UMLEntityAttribute. + * @param s The visibility of the UMLEntityAttribute. + * @param type The type of this UMLEntityAttribute. + * @param iv The initial value of the entityattribute. + */ + UMLEntityAttribute(const UMLObject* parent, const QString& name, + Uml::IDType id = Uml::id_None, + Uml::Visibility s = Uml::Visibility::Private, + UMLObject *type = 0, const QString& iv = 0); + + /** + * Sets up an entityattribute. + * + * @param parent The parent of this UMLEntityAttribute. + */ + UMLEntityAttribute(const UMLObject* parent); + + /** + * Overloaded '==' operator + */ + bool operator==( UMLEntityAttribute& rhs); + + /** + * destructor. + */ + virtual ~UMLEntityAttribute(); + + /** + * Copy the internal presentation of this object into the UMLEntityAttribute + * object. + */ + virtual void copyInto(UMLEntityAttribute* rhs) const; + + /** + * Make a clone of the UMLEntityAttribute. + */ + virtual UMLObject* clone() const; + + /** + * Returns The value of the UMLEntityAttribute's attributes property. + * + * @return The value of the UMLEntityAttribute's attributes property. + */ + QString getAttributes() const; + + /** + * Sets the UMLEntityAttribute's attributes property. + * + * @param attributes The new value for the attributes property. + */ + void setAttributes(const QString& attributes); + + /** + * Returns the UMLEntityAttribute's index type property. + * + * @return The value of the UMLEntityAttribute's index type property. + */ + Uml::DBIndex_Type getIndexType() const; + + /** + * Sets the UMLEntityAttribute's index type property. + * + * @param indexType The UMLEntityAttribute's index type property. + */ + void setIndexType(const Uml::DBIndex_Type indexType); + + /** + * Returns the UMLEntityAttribute's length/values property. + * + * @return The UMLEntityAttribute's length/values property. + */ + QString getValues() const; + + /** + * Sets the UMLEntityAttribute's length/values property. + * + * @param values The new value of the length/values property. + */ + void setValues(const QString& values); + + /** + * Returns the UMLEntityAttribute's auto_increment boolean + * + * @return The UMLEntityAttribute's auto_increment boolean + */ + bool getAutoIncrement() const; + + /** + * Sets the UMLEntityAttribute's auto_increment property + * + * @param autoIncrement The UMLEntityAttribute's auto_increment property + */ + void setAutoIncrement(const bool autoIncrement); + + /** + * Returns the UMLEntityAttribute's allow null value. + * + * @return The UMLEntityAttribute's allow null value. + */ + bool getNull() const; + + /** + * Sets the UMLEntityAttribute's allow null value. + * + * @param null The UMLEntityAttribute's allow null value. + */ + void setNull(const bool null); + + /** + * Returns a string representation of the UMLEntityAttribute. + * + * @param sig If true will show the entityattribute type and + * initial value. + * @return Returns a string representation of the UMLEntityAttribute. + */ + QString toString(Uml::Signature_Type sig = Uml::st_NoSig); + + /** + * Creates the XMI element. + */ + void saveToXMI(QDomDocument& qDoc, QDomElement& qElement); + + /** + * Display the properties configuration dialog for the entityattribute. + */ + bool showPropertiesDialog(QWidget* parent); + +protected: + /** + * Initialize members of this class. + * Auxiliary method used by constructors. + */ + void init(); + + /** + * Loads the XMI element. + */ + bool load(QDomElement& element); + +private: + Uml::DBIndex_Type m_indexType; + QString m_values; + QString m_attributes; + bool m_autoIncrement; + bool m_null; +}; + +#endif + diff --git a/umbrello/umbrello/entitywidget.cpp b/umbrello/umbrello/entitywidget.cpp new file mode 100644 index 00000000..942de360 --- /dev/null +++ b/umbrello/umbrello/entitywidget.cpp @@ -0,0 +1,203 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "entitywidget.h" + +// qt/kde includes +#include +#include + +// app includes +#include "entity.h" +#include "entityattribute.h" +#include "classifier.h" +#include "umlclassifierlistitemlist.h" +#include "classifierlistitem.h" +#include "umlview.h" +#include "umldoc.h" +#include "uml.h" +#include "listpopupmenu.h" +#include "object_factory.h" + + +EntityWidget::EntityWidget(UMLView* view, UMLObject* o): UMLWidget(view, o) { + init(); +} + +void EntityWidget::init() { + UMLWidget::setBaseType(Uml::wt_Entity); + setSize(100, 30); + //set defaults from m_pView + if (m_pView) { + //check to see if correct + const Settings::OptionState& ops = m_pView->getOptionState(); + } + if (! UMLApp::app()->getDocument()->loading()) + updateComponentSize(); +} + +EntityWidget::~EntityWidget() {} + +void EntityWidget::draw(QPainter& p, int offsetX, int offsetY) { + UMLWidget::setPen(p); + if(UMLWidget::getUseFillColour()) + p.setBrush(UMLWidget::getFillColour()); + else + p.setBrush(m_pView -> viewport() -> backgroundColor()); + + const int w = width(); + const int h = height(); + + const QFontMetrics &fm = getFontMetrics(FT_NORMAL); + int fontHeight = fm.lineSpacing(); + const QString name = this->getName(); + + p.drawRect(offsetX, offsetY, w, h); + p.setPen(QPen(Qt::black)); + + QFont font = UMLWidget::getFont(); + font.setBold(true); + p.setFont(font); + int y = 0; + if ( !m_pObject->getStereotype().isEmpty() ) { + p.drawText(offsetX + ENTITY_MARGIN, offsetY, + w - ENTITY_MARGIN * 2,fontHeight, + Qt::AlignCenter, m_pObject->getStereotype(true)); + font.setItalic( m_pObject -> getAbstract() ); + p.setFont(font); + p.drawText(offsetX + ENTITY_MARGIN, offsetY + fontHeight, + w - ENTITY_MARGIN * 2, fontHeight, Qt::AlignCenter, name); + font.setBold(false); + font.setItalic(false); + p.setFont(font); + y = fontHeight * 2; + } else { + font.setItalic( m_pObject -> getAbstract() ); + p.setFont(font); + p.drawText(offsetX + ENTITY_MARGIN, offsetY, + w - ENTITY_MARGIN * 2, fontHeight, Qt::AlignCenter, name); + font.setBold(false); + font.setItalic(false); + p.setFont(font); + + y = fontHeight; + } + + UMLWidget::setPen(p); + + p.drawLine(offsetX, offsetY + y, offsetX + w - 1, offsetY + y); + + QFontMetrics fontMetrics(font); + UMLClassifier *classifier = (UMLClassifier*)m_pObject; + UMLClassifierListItem* entityattribute = 0; + UMLClassifierListItemList list = classifier->getFilteredList(Uml::ot_EntityAttribute); + for (entityattribute = list.first(); entityattribute; entityattribute = list.next()) { + QString text = entityattribute->getName(); + p.setPen( QPen(Qt::black) ); + UMLEntityAttribute* casted = dynamic_cast( entityattribute ); + if( casted && casted->getIndexType() == Uml::Primary ) + { + font.setUnderline( true ); + p.setFont( font ); + font.setUnderline( false ); + } + p.drawText(offsetX + ENTITY_MARGIN, offsetY + y, + fontMetrics.width(text), fontHeight, Qt::AlignVCenter, text); + p.setFont( font ); + y+=fontHeight; + } + + if (m_bSelected) { + drawSelected(&p, offsetX, offsetY); + } +} + +QSize EntityWidget::calculateSize() { + if (!m_pObject) { + return UMLWidget::calculateSize(); + } + + int width, height; + QFont font = UMLWidget::getFont(); + font.setItalic(false); + font.setUnderline(false); + font.setBold(false); + const QFontMetrics fm(font); + + const int fontHeight = fm.lineSpacing(); + + int lines = 1;//always have one line - for name + if ( !m_pObject->getStereotype().isEmpty() ) { + lines++; + } + + const int numberOfEntityAttributes = ((UMLEntity*)m_pObject)->entityAttributes(); + + height = width = 0; + //set the height of the entity + + lines += numberOfEntityAttributes; + if (numberOfEntityAttributes == 0) { + height += fontHeight / 2; //no entity literals, so just add a bit of space + } + + height += lines * fontHeight; + + //now set the width of the concept + //set width to name to start with + // FIXME spaces to get round beastie with font width, + // investigate UMLWidget::getFontMetrics() + width = getFontMetrics(FT_BOLD_ITALIC).boundingRect(' ' + getName() + ' ').width(); + + const int w = getFontMetrics(FT_BOLD).boundingRect(m_pObject->getStereotype(true)).width(); + + width = w > width?w:width; + + UMLClassifier* classifier = (UMLClassifier*)m_pObject; + UMLClassifierListItemList list = classifier->getFilteredList(Uml::ot_EntityAttribute); + UMLClassifierListItem* listItem = 0; + for (listItem = list.first(); listItem; listItem = list.next()) { + int w = fm.width( listItem->getName() ); + width = w > width?w:width; + } + + //allow for width margin + width += ENTITY_MARGIN * 2; + + return QSize(width, height); +} + +void EntityWidget::slotMenuSelection(int sel) { + switch(sel) { + case ListPopupMenu::mt_EntityAttribute: + if (Object_Factory::createChildObject(static_cast(m_pObject), + Uml::ot_EntityAttribute) ) { + UMLApp::app()->getDocument()->setModified(); + } + break; + } + UMLWidget::slotMenuSelection(sel); +} + +void EntityWidget::saveToXMI( QDomDocument& qDoc, QDomElement& qElement ) { + QDomElement conceptElement = qDoc.createElement("entitywidget"); + UMLWidget::saveToXMI(qDoc, conceptElement); + qElement.appendChild(conceptElement); +} + +bool EntityWidget::loadFromXMI( QDomElement & qElement ) { + if ( !UMLWidget::loadFromXMI(qElement) ) { + return false; + } + return true; +} + diff --git a/umbrello/umbrello/entitywidget.h b/umbrello/umbrello/entitywidget.h new file mode 100644 index 00000000..249f0984 --- /dev/null +++ b/umbrello/umbrello/entitywidget.h @@ -0,0 +1,82 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef ENTITYWIDGET_H +#define ENTITYWIDGET_H + +#include "umlwidget.h" + +class UMLView; + +#define ENTITY_MARGIN 5 + +/** + * Defines a graphical version of the entity. Most of the functionality + * will come from the @ref UMLWidget class from which class inherits from. + * + * @short A graphical version of an entity. + * @author Jonathan Riddell + * @see UMLWidget + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class EntityWidget : public UMLWidget { +public: + + /** + * Constructs an EntityWidget. + * + * @param view The parent of this EntityWidget. + * @param o The UMLObject this will be representing. + */ + EntityWidget(UMLView* view, UMLObject* o); + + /** + * Standard deconstructor. + */ + ~EntityWidget(); + + /** + * Initializes key variables of the class. + */ + void init(); + + /** + * Draws the entity as a rectangle with a box underneith with a list of literals + */ + void draw(QPainter& p, int offsetX, int offsetY); + + /** + * Saves to the "entitywidget" XMI element. + */ + void saveToXMI(QDomDocument& qDoc, QDomElement& qElement); + + /** + * Loads from an "entitywidget" XMI element. + */ + bool loadFromXMI(QDomElement& qElement); + +protected: + /** + * Overrides method from UMLWidget. + */ + QSize calculateSize(); + +public slots: + /** + * Will be called when a menu selection has been made from the + * popup menu. + * + * @param sel The selection id that has been selected. + */ + void slotMenuSelection(int sel); +}; + +#endif diff --git a/umbrello/umbrello/enum.cpp b/umbrello/umbrello/enum.cpp new file mode 100644 index 00000000..ba3be7b4 --- /dev/null +++ b/umbrello/umbrello/enum.cpp @@ -0,0 +1,206 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "enum.h" +// qt/kde includes +#include +#include +#include +// app includes +#include "enumliteral.h" +#include "umldoc.h" +#include "uml.h" +#include "uniqueid.h" +#include "clipboard/idchangelog.h" + +UMLEnum::UMLEnum(const QString& name, Uml::IDType id) : UMLClassifier(name, id) { + init(); +} + +UMLEnum::~UMLEnum() { + m_List.clear(); +} + +bool UMLEnum::operator==( UMLEnum & rhs ) { + return UMLClassifier::operator==(rhs); +} + +void UMLEnum::copyInto(UMLEnum *rhs) const +{ + UMLClassifier::copyInto(rhs); +} + +UMLObject* UMLEnum::clone() const +{ + UMLEnum *clone = new UMLEnum(); + copyInto(clone); + + return clone; +} + +void UMLEnum::init() { + m_BaseType = Uml::ot_Enum; + setStereotype( "enum" ); +} + +UMLObject* UMLEnum::createEnumLiteral(const QString& name) { + Uml::IDType id = UniqueID::gen(); + QString currentName; + if (name.isNull()) { + currentName = uniqChildName(Uml::ot_EnumLiteral); + } else { + currentName = name; + } + + UMLEnumLiteral* newEnumLiteral = new UMLEnumLiteral(this, currentName); + + bool ok = true; + bool goodName = false; + + //check for name.isNull() stops dialog being shown + //when creating enum literal via list view + while (ok && !goodName && name.isNull()) { + ok = newEnumLiteral->showPropertiesDialog( UMLApp::app() ); + QString name = newEnumLiteral->getName(); + + if(name.length() == 0) { + KMessageBox::error(0, i18n("That is an invalid name."), i18n("Invalid Name")); + } else { + goodName = true; + } + } + + if (!ok) { + delete newEnumLiteral; + return NULL; + } + + addEnumLiteral(newEnumLiteral); + + UMLDoc *umldoc = UMLApp::app()->getDocument(); + umldoc->signalUMLObjectCreated(newEnumLiteral); + return newEnumLiteral; +} + +UMLObject* UMLEnum::addEnumLiteral(const QString &name, Uml::IDType id) { + UMLObject *el = UMLCanvasObject::findChildObject(name); + if (el != NULL) { + kDebug() << "UMLEnum::addEnumLiteral: " << name + << " is already present" << endl; + return el; + } + UMLEnumLiteral* literal = new UMLEnumLiteral(this, name, id); + m_List.append(literal); + UMLObject::emitModified(); + emit enumLiteralAdded(literal); + connect(literal,SIGNAL(modified()),this,SIGNAL(modified())); + return literal; +} + +bool UMLEnum::addEnumLiteral(UMLEnumLiteral* literal, IDChangeLog* Log /* = 0*/) { + QString name = (QString)literal->getName(); + if (findChildObject(name) == NULL) { + literal->parent()->removeChild(literal); + this->insertChild(literal); + m_List.append(literal); + UMLObject::emitModified(); + emit enumLiteralAdded(literal); + connect(literal,SIGNAL(modified()),this,SIGNAL(modified())); + return true; + } else if (Log) { + Log->removeChangeByNewID( literal->getID() ); + delete literal; + } + return false; +} + +bool UMLEnum::addEnumLiteral(UMLEnumLiteral* literal, int position) { + QString name = (QString)literal->getName(); + if (findChildObject(name) == NULL) { + literal->parent()->removeChild(literal); + this->insertChild(literal); + if ( position >= 0 && position <= (int)m_List.count() ) { + m_List.insert(position,literal); + } else { + m_List.append(literal); + } + UMLObject::emitModified(); + emit enumLiteralAdded(literal); + connect(literal,SIGNAL(modified()),this,SIGNAL(modified())); + return true; + } + return false; +} + +int UMLEnum::removeEnumLiteral(UMLEnumLiteral* literal) { + if (!m_List.remove(literal)) { + kDebug() << "can't find att given in list" << endl; + return -1; + } + emit enumLiteralRemoved(literal); + UMLObject::emitModified(); + // If we are deleting the object, then we don't need to disconnect..this is done auto-magically + // for us by QObject. -b.t. + // disconnect(a,SIGNAL(modified()),this,SIGNAL(modified())); + delete literal; + return m_List.count(); +} + +int UMLEnum::enumLiterals() { + return m_List.count(); +} + +void UMLEnum::signalEnumLiteralRemoved(UMLClassifierListItem *elit) { + emit enumLiteralRemoved(elit); +} + +void UMLEnum::saveToXMI(QDomDocument& qDoc, QDomElement& qElement) { + QDomElement enumElement = UMLObject::save("UML:Enumeration", qDoc); + // save enum literals + UMLClassifierListItemList enumLiterals = getFilteredList(Uml::ot_EnumLiteral); + UMLClassifierListItem* pEnumLiteral = 0; + for (UMLClassifierListItemListIt it(enumLiterals); + (pEnumLiteral = it.current()) != NULL; ++it) { + pEnumLiteral->saveToXMI(qDoc, enumElement); + } + qElement.appendChild(enumElement); +} + +bool UMLEnum::load(QDomElement& element) { + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + if (node.isComment()) { + node = node.nextSibling(); + continue; + } + QDomElement tempElement = node.toElement(); + QString tag = tempElement.tagName(); + if (Uml::tagEq(tag, "EnumerationLiteral") || + Uml::tagEq(tag, "EnumLiteral")) { // for backward compatibility + UMLEnumLiteral* pEnumLiteral = new UMLEnumLiteral(this); + if( !pEnumLiteral->loadFromXMI(tempElement) ) { + return false; + } + m_List.append(pEnumLiteral); + } else if (tag == "stereotype") { + kDebug() << "UMLEnum::load(" << m_Name + << "): losing old-format stereotype." << endl; + } else { + kWarning() << "unknown child type in UMLEnum::load" << endl; + } + node = node.nextSibling(); + }//end while + return true; +} + + +#include "enum.moc" diff --git a/umbrello/umbrello/enum.h b/umbrello/umbrello/enum.h new file mode 100644 index 00000000..1845eb88 --- /dev/null +++ b/umbrello/umbrello/enum.h @@ -0,0 +1,145 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef ENUM_H +#define ENUM_H + +#include "classifier.h" + +class UMLEnumLiteral; + +/** + * This class contains the non-graphical information required for a UML + * Enum. + * This class inherits from @ref UMLClassifier which contains most of the + * information. + * + * @short Non-graphical Information for an Enum. + * @author Jonathan Riddell + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class UMLEnum : public UMLClassifier { + Q_OBJECT +public: + /** + * Sets up an enum. + * + * @param name The name of the Enum. + * @param id The unique id of the Enum. + */ + explicit UMLEnum(const QString& name = "", Uml::IDType id = Uml::id_None); + + /** + * Standard deconstructor. + */ + virtual ~UMLEnum(); + + /** + * Overloaded '==' operator. + */ + bool operator==(UMLEnum& rhs); + + /** + * Copy the internal presentation of this object into the new + * object. + */ + virtual void copyInto(UMLEnum *rhs) const; + + /** + * Make a clone of this object. + */ + virtual UMLObject* clone() const; + + /** + * Creates a literal for the enum. + * + * @return The UMLEnum created + */ + UMLObject* createEnumLiteral(const QString& name = QString()); + + /** + * Adds an enumliteral to the enum. + * + * @param name The name of the enumliteral. + * @param id The id of the enumliteral (optional.) + * If omitted a new ID is assigned internally. + * @return Pointer to the UMLEnumliteral created. + */ + UMLObject* addEnumLiteral(const QString &name, Uml::IDType id = Uml::id_None); + + /** + * Adds an already created enumliteral. + * The enumliteral object must not belong to any other concept. + * + * @param Att Pointer to the UMLEnumLiteral. + * @param Log Pointer to the IDChangeLog. + * @return True if the enumliteral was successfully added. + */ + bool addEnumLiteral(UMLEnumLiteral* Att, IDChangeLog* Log = 0); + + /** + * Adds an enumliteral to the enum, at the given position. + * If position is negative or too large, the enumliteral is added + * to the end of the list. + * + * @param Att Pointer to the UMLEnumLiteral. + * @param position Position index for the insertion. + * @return True if the enumliteral was successfully added. + */ + //TODO: give default value -1 to position (append) - now it conflicts with the method above.. + bool addEnumLiteral(UMLEnumLiteral* Att, int position ); + + /** + * Removes an enumliteral from the class. + * + * @param a The enumliteral to remove. + * @return Count of the remaining enumliterals after removal. + * Returns -1 if the given enumliteral was not found. + */ + int removeEnumLiteral(UMLEnumLiteral *a); + + /** + * Returns the number of enumliterals for the class. + * + * @return The number of enumliterals for the class. + */ + int enumLiterals(); + + /** + * Emit the enumLiteralRemoved signal. + */ + void signalEnumLiteralRemoved(UMLClassifierListItem *elit); + + /** + * Creates the element including its enumliterals. + */ + virtual void saveToXMI( QDomDocument & qDoc, QDomElement & qElement ); + +signals: + void enumLiteralAdded(UMLClassifierListItem*); + void enumLiteralRemoved(UMLClassifierListItem*); + +protected: + /** + * Loads the element including its enumliterals. + */ + bool load( QDomElement & element ); + +private: + /** + * Initializes key variables of the class. + */ + void init(); + +}; + +#endif // ENUM_H + diff --git a/umbrello/umbrello/enumliteral.cpp b/umbrello/umbrello/enumliteral.cpp new file mode 100644 index 00000000..ea6eef49 --- /dev/null +++ b/umbrello/umbrello/enumliteral.cpp @@ -0,0 +1,74 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#include "enumliteral.h" + +#include +#include +#include + +UMLEnumLiteral::UMLEnumLiteral(const UMLObject *parent, + const QString& name, Uml::IDType id) + : UMLClassifierListItem(parent, name, id) { + m_BaseType = Uml::ot_EnumLiteral; +} + +UMLEnumLiteral::UMLEnumLiteral(const UMLObject *parent) : UMLClassifierListItem(parent) { + m_BaseType = Uml::ot_EnumLiteral; +} + +UMLEnumLiteral::~UMLEnumLiteral() { } + +bool UMLEnumLiteral::operator==(UMLEnumLiteral& rhs) { + if ( this == &rhs ) { + return true; + } + if ( !UMLObject::operator==( rhs ) ) { + return false; + } + return true; +} + +void UMLEnumLiteral::copyInto(UMLEnumLiteral *rhs) const +{ + UMLClassifierListItem::copyInto(rhs); +} + +UMLObject* UMLEnumLiteral::clone() const +{ + UMLEnumLiteral *clone = new UMLEnumLiteral((UMLObject *) parent()); + copyInto(clone); + + return clone; +} + + +void UMLEnumLiteral::saveToXMI(QDomDocument& qDoc, QDomElement& qElement) { + QDomElement attributeElement = UMLObject::save("UML:EnumerationLiteral", qDoc); + qElement.appendChild( attributeElement ); +} + +bool UMLEnumLiteral::load(QDomElement& ) { + return true; +} + +bool UMLEnumLiteral::showPropertiesDialog(QWidget* parent) { + bool ok; + QString name = KInputDialog::getText(i18n("Name"), i18n("Enter name:"), getName(), &ok, parent); + if ( ok && !name.isEmpty() ) { + setName(name); + return true; + } else { + return false; + } +} + + diff --git a/umbrello/umbrello/enumliteral.h b/umbrello/umbrello/enumliteral.h new file mode 100644 index 00000000..3484012e --- /dev/null +++ b/umbrello/umbrello/enumliteral.h @@ -0,0 +1,85 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef ENUMLITERAL_H +#define ENUMLITERAL_H + +#include "classifierlistitem.h" + +/** + * This class is used to set up information for an enum literal. Enum + * literals are the values that enums can be set to. + * + * @short Sets up attribute information. + * @author Paul Hensgen + * @see UMLObject + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + +class UMLEnumLiteral : public UMLClassifierListItem { +public: + /** + * Sets up an enum literal. + * + * @param parent The parent of this UMLEnumLiteral. + * @param name The name of this UMLEnumLiteral. + * @param id The unique id given to this UMLEnumLiteral. + */ + UMLEnumLiteral(const UMLObject* parent, + const QString& name, Uml::IDType id = Uml::id_None); + + /** + * Sets up an enum literal. + * + * @param parent The parent of this UMLEnumLiteral. + */ + UMLEnumLiteral(const UMLObject* parent); + + /** + * Overloaded '==' operator + */ + bool operator==(UMLEnumLiteral &rhs); + + /** + * Copy the internal presentation of this object into the new + * object. + */ + virtual void copyInto(UMLEnumLiteral *rhs) const; + + /** + * Make a clone of this object. + */ + virtual UMLObject* clone() const; + + /** + * destructor + */ + virtual ~UMLEnumLiteral(); + + /** + * Creates the XMI element. + */ + void saveToXMI(QDomDocument& qDoc, QDomElement& qElement); + + /** + * Display the properties configuration dialog for the enum literal. + */ + bool showPropertiesDialog(QWidget* parent); + +protected: + /** + * Loads the XMI element (empty.) + */ + bool load(QDomElement& element); + +}; + +#endif diff --git a/umbrello/umbrello/enumwidget.cpp b/umbrello/umbrello/enumwidget.cpp new file mode 100644 index 00000000..2d642b31 --- /dev/null +++ b/umbrello/umbrello/enumwidget.cpp @@ -0,0 +1,218 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "enumwidget.h" + +// qt/kde includes +#include +#include + +// app includes +#include "enum.h" +#include "enumliteral.h" +#include "classifier.h" +#include "umlclassifierlistitemlist.h" +#include "classifierlistitem.h" +#include "umlview.h" +#include "umldoc.h" +#include "uml.h" +#include "listpopupmenu.h" +#include "object_factory.h" + + +EnumWidget::EnumWidget(UMLView* view, UMLObject* o) : UMLWidget(view, o) { + init(); +} + +void EnumWidget::init() { + UMLWidget::setBaseType(Uml::wt_Enum); + setSize(100, 30); + m_pMenu = 0; + //set defaults from m_pView + if (m_pView) { + //check to see if correct + const Settings::OptionState& ops = m_pView->getOptionState(); + m_bShowPackage = ops.classState.showPackage; + } else { + // For completeness only. Not supposed to happen. + m_bShowPackage = false; + } + if (! UMLApp::app()->getDocument()->loading()) + updateComponentSize(); +} + +EnumWidget::~EnumWidget() {} + +void EnumWidget::draw(QPainter& p, int offsetX, int offsetY) { + UMLWidget::setPen(p); + if(UMLWidget::getUseFillColour()) + p.setBrush(UMLWidget::getFillColour()); + else + p.setBrush(m_pView -> viewport() -> backgroundColor()); + + const int w = width(); + const int h = height(); + + const QFontMetrics &fm = getFontMetrics(FT_NORMAL); + const int fontHeight = fm.lineSpacing(); + QString name; + if ( m_bShowPackage ) { + name = m_pObject->getFullyQualifiedName(); + } else { + name = this -> getName(); + } + + p.drawRect(offsetX, offsetY, w, h); + p.setPen(QPen(Qt::black)); + + QFont font = UMLWidget::getFont(); + font.setBold(true); + p.setFont(font); + p.drawText(offsetX + ENUM_MARGIN, offsetY, + w - ENUM_MARGIN * 2,fontHeight, + Qt::AlignCenter, m_pObject->getStereotype(true)); + + font.setItalic( m_pObject -> getAbstract() ); + p.setFont(font); + p.drawText(offsetX + ENUM_MARGIN, offsetY + fontHeight, + w - ENUM_MARGIN * 2, fontHeight, Qt::AlignCenter, name); + font.setBold(false); + font.setItalic(false); + p.setFont(font); + + int y = fontHeight * 2; + + UMLWidget::setPen(p); + + p.drawLine(offsetX, offsetY + y, offsetX + w - 1, offsetY + y); + + QFontMetrics fontMetrics(font); + UMLClassifier *classifier = (UMLClassifier*)m_pObject; + UMLClassifierListItem* enumLiteral = 0; + UMLClassifierListItemList list = classifier->getFilteredList(Uml::ot_EnumLiteral); + for (enumLiteral = list.first(); enumLiteral; enumLiteral = list.next()) { + QString text = enumLiteral->getName(); + p.setPen( QPen(Qt::black) ); + p.drawText(offsetX + ENUM_MARGIN, offsetY + y, + fontMetrics.width(text), fontHeight, Qt::AlignVCenter, text); + y+=fontHeight; + } + + if (m_bSelected) { + drawSelected(&p, offsetX, offsetY); + } +} + +QSize EnumWidget::calculateSize() { + if (!m_pObject) { + return UMLWidget::calculateSize(); + } + + int width, height; + QFont font = UMLWidget::getFont(); + font.setItalic(false); + font.setUnderline(false); + font.setBold(false); + const QFontMetrics fm(font); + + const int fontHeight = fm.lineSpacing(); + + int lines = 1;//always have one line - for name + lines++; //for the stereotype + + const int numberOfEnumLiterals = ((UMLEnum*)m_pObject)->enumLiterals(); + + height = width = 0; + //set the height of the enum + + lines += numberOfEnumLiterals; + if (numberOfEnumLiterals == 0) { + height += fontHeight / 2; //no enum literals, so just add a bit of space + } + + height += lines * fontHeight; + + //now set the width of the concept + //set width to name to start with + if (m_bShowPackage) { + width = getFontMetrics(FT_BOLD_ITALIC).boundingRect(m_pObject->getFullyQualifiedName()).width(); + } else { + width = getFontMetrics(FT_BOLD_ITALIC).boundingRect(getName()).width(); + } + int w = getFontMetrics(FT_BOLD).boundingRect(m_pObject->getStereotype(true)).width(); + + + width = w > width?w:width; + + UMLClassifier *classifier = (UMLClassifier*)m_pObject; + UMLClassifierListItemList list = classifier->getFilteredList(Uml::ot_EnumLiteral); + UMLClassifierListItem* listItem = 0; + for (listItem = list.first(); listItem; listItem = list.next()) { + int w = fm.width( listItem->getName() ); + width = w > width?w:width; + } + + //allow for width margin + width += ENUM_MARGIN * 2; + + return QSize(width, height); +} + +void EnumWidget::slotMenuSelection(int sel) { + switch(sel) { + case ListPopupMenu::mt_EnumLiteral: + if (Object_Factory::createChildObject(static_cast(m_pObject), + Uml::ot_EnumLiteral) ) { + UMLApp::app()->getDocument()->setModified(); + } + break; + } + UMLWidget::slotMenuSelection(sel); +} + +void EnumWidget::setShowPackage(bool _status) { + m_bShowPackage = _status; + updateComponentSize(); + update(); +} + +bool EnumWidget::getShowPackage() const { + return m_bShowPackage; +} + +void EnumWidget::saveToXMI( QDomDocument& qDoc, QDomElement& qElement ) { + QDomElement conceptElement = qDoc.createElement("enumwidget"); + UMLWidget::saveToXMI(qDoc, conceptElement); + + conceptElement.setAttribute("showpackage", m_bShowPackage); + qElement.appendChild(conceptElement); +} + +bool EnumWidget::loadFromXMI( QDomElement & qElement ) { + if ( !UMLWidget::loadFromXMI(qElement) ) { + return false; + } + QString showpackage = qElement.attribute("showpackage", "0"); + + m_bShowPackage = (bool)showpackage.toInt(); + + return true; +} + +void EnumWidget::toggleShowPackage() { + m_bShowPackage = !m_bShowPackage; + updateComponentSize(); + update(); + + return; +} + diff --git a/umbrello/umbrello/enumwidget.h b/umbrello/umbrello/enumwidget.h new file mode 100644 index 00000000..7022ac61 --- /dev/null +++ b/umbrello/umbrello/enumwidget.h @@ -0,0 +1,109 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef ENUMWIDGET_H +#define ENUMWIDGET_H + +#include "umlwidget.h" + +class UMLView; + +#define ENUM_MARGIN 5 + +/** + * Defines a graphical version of the enum. Most of the functionality + * will come from the @ref UMLWidget class from which class inherits from. + * + * @short A graphical version of an enum. + * @author Jonathan Riddell + * @see UMLWidget + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class EnumWidget : public UMLWidget { +public: + + /** + * Constructs an EnumWidget. + * + * @param view The parent of this EnumWidget. + * @param o The UMLObject this will be representing. + */ + EnumWidget(UMLView* view, UMLObject* o); + + /** + * Standard deconstructor. + */ + ~EnumWidget(); + + /** + * Initializes key variables of the class. + */ + void init(); + + /** + * Returns the status of whether to show Package. + * + * @return True if package is shown. + */ + bool getShowPackage() const; + + /** + * Toggles the status of whether to show package. + */ + void toggleShowPackage(); + + /** + * Set the status of whether to show Package. + * + * @param _status True if package shall be shown. + */ + void setShowPackage(bool _status); + + /** + * Draws the enum as a rectangle with a box underneith with a list of literals + */ + void draw(QPainter& p, int offsetX, int offsetY); + + /** + * Saves to the "enumwidget" XMI element. + */ + void saveToXMI(QDomDocument& qDoc, QDomElement& qElement); + + /** + * Loads from an "enumwidget" XMI element. + */ + bool loadFromXMI(QDomElement& qElement); + +protected: + /** + * Overrides method from UMLWidget. + */ + QSize calculateSize(); + + bool m_bShowPackage; + +private: + /** + * The right mouse button menu. + */ + ListPopupMenu* m_pMenu; + +public slots: + /** + * Will be called when a menu selection has been made from the + * popup menu. + * + * @param sel The selection id that has been selected. + */ + void slotMenuSelection(int sel); +}; + +#endif diff --git a/umbrello/umbrello/floatingtextwidget.cpp b/umbrello/umbrello/floatingtextwidget.cpp new file mode 100644 index 00000000..44ea3373 --- /dev/null +++ b/umbrello/umbrello/floatingtextwidget.cpp @@ -0,0 +1,453 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "floatingtextwidget.h" + +// system includes +#include +#include +#include +#include +#include +#include +#include + +// local includes +#include "floatingtextwidgetcontroller.h" +#include "association.h" +#include "umlview.h" +#include "umldoc.h" +#include "uml.h" +#include "classifier.h" +#include "listpopupmenu.h" +#include "operation.h" +#include "model_utils.h" +#include "object_factory.h" +#include "messagewidget.h" +#include "dialogs/assocpropdlg.h" +#include "dialogs/selectopdlg.h" + + +FloatingTextWidget::FloatingTextWidget(UMLView * view, Uml::Text_Role role, + const QString& text, Uml::IDType id) + : UMLWidget(view, id, new FloatingTextWidgetController(this)) +{ + init(); + m_Text = text; + m_Role = role; + if ( ! UMLApp::app()->getDocument()->loading() ) { + updateComponentSize(); + setZ( 10 );//make sure always on top. + update(); + } +} + +void FloatingTextWidget::init() { + // initialize loaded/saved (i.e. persistent) data + m_PreText = ""; + m_Text = ""; + m_PostText = ""; + m_Role = Uml::tr_Floating; + m_Type = Uml::wt_Text; + // initialize non-saved (i.e. volatile) data + m_pLink = NULL; + UMLWidget::m_bResizable = false; +} + +FloatingTextWidget::~FloatingTextWidget() { +} + +void FloatingTextWidget::draw(QPainter & p, int offsetX, int offsetY) { + int w = width(); + int h = height(); + p.setFont( UMLWidget::getFont() ); + QColor textColor(50, 50, 50); + p.setPen(textColor); + p.drawText( offsetX , offsetY,w,h, Qt::AlignCenter, getDisplayText() ); + if(m_bSelected) + drawSelected(&p, offsetX, offsetY); +} + +void FloatingTextWidget::resizeEvent(QResizeEvent * /*re*/) {} + +QSize FloatingTextWidget::calculateSize() { + const QFontMetrics &fm = getFontMetrics(FT_NORMAL); + int h = fm.lineSpacing(); + int w = fm.width( getDisplayText() ); + return QSize(w + 8, h + 4); // give a small margin +} + +void FloatingTextWidget::slotMenuSelection(int sel) { + switch(sel) { + case ListPopupMenu::mt_Properties: + showProperties(); + break; + + case ListPopupMenu::mt_Delete: + m_pView -> removeWidget(this); + break; + + case ListPopupMenu::mt_Operation: + { + kDebug() << "FloatingTextWidget::slotMenuSelection(mt_Operation) is called." + << endl; + if (m_pLink == NULL) { + kDebug() << "FloatingTextWidget::slotMenuSelection(mt_Operation): " + << "m_pLink is NULL" << endl; + return; + } + UMLClassifier* c = m_pLink->getOperationOwner(); + if (c == NULL) { + bool ok = false; + QString opText = KInputDialog::getText(i18n("Name"), + i18n("Enter operation name:"), + getText(), &ok, m_pView); + if (ok) + m_pLink->setCustomOpText(opText); + return; + } + UMLClassifierListItem* umlObj = Object_Factory::createChildObject(c, Uml::ot_Operation); + if (umlObj) { + UMLOperation* newOperation = static_cast( umlObj ); + m_pLink->setOperation(newOperation); + } + } + break; + + case ListPopupMenu::mt_Select_Operation: + showOpDlg(); + break; + + case ListPopupMenu::mt_Rename: + handleRename(); + break; + + case ListPopupMenu::mt_Change_Font: + { + QFont font = getFont(); + if( KFontDialog::getFont( font, false, m_pView ) ) { + if( m_Role == Uml::tr_Floating || m_Role == Uml::tr_Seq_Message ) { + setFont( font ); + } else if (m_pLink) { + m_pLink->lwSetFont(font); + } + } + } + break; + + case ListPopupMenu::mt_Reset_Label_Positions: + if (m_pLink) + m_pLink->resetTextPositions(); + break; + + default: + UMLWidget::slotMenuSelection(sel); + break; + }//end switch +} + +void FloatingTextWidget::handleRename() { + QRegExpValidator v(QRegExp(".*"), 0); + QString t; + if( m_Role == Uml::tr_RoleAName || m_Role == Uml::tr_RoleBName ) { + t = i18n("Enter role name:"); + } else if (m_Role == Uml::tr_MultiA || m_Role == Uml::tr_MultiB) { + t = i18n("Enter multiplicity:"); + /* + // NO! shouldn't be allowed + } else if( m_Role == Uml::tr_ChangeA || m_Role == Uml::tr_ChangeB ) { + t = i18n("Enter changeability"); + */ + } else if (m_Role == Uml::tr_Name) { + t = i18n("Enter association name:"); + } else if (m_Role == Uml::tr_Floating) { + t = i18n("Enter new text:"); + } else { + t = i18n("ERROR"); + } + bool ok = false; + QString newText = KInputDialog::getText(i18n("Rename"), t, getText(), &ok, + m_pView, NULL, &v); + if (!ok || newText == getText()) + return; +} + +void FloatingTextWidget::changeName(const QString& newText) +{ + + if (m_pLink && !isTextValid(newText)) { + AssociationWidget *assoc = dynamic_cast(m_pLink); + if (assoc) { + switch (m_Role) { + case Uml::tr_MultiA: + assoc->setMulti(QString(), Uml::A); + break; + case Uml::tr_MultiB: + assoc->setMulti(QString(), Uml::B); + break; + case Uml::tr_RoleAName: + assoc->setRoleName(QString(), Uml::A); + break; + case Uml::tr_RoleBName: + assoc->setRoleName(QString(), Uml::B); + break; + case Uml::tr_ChangeA: + assoc->setChangeability(Uml::chg_Changeable, Uml::A); + break; + case Uml::tr_ChangeB: + assoc->setChangeability(Uml::chg_Changeable, Uml::B); + break; + default: + assoc->setName(QString()); + break; + } + } else { + MessageWidget *msg = dynamic_cast(m_pLink); + if (msg) { + msg->setName(QString()); + m_pView->removeWidget(this); + } + } + return; + } + if (m_pLink && m_Role != Uml::tr_Seq_Message && m_Role != Uml::tr_Seq_Message_Self) { + m_pLink->setText(this, newText); + } else { + setText( newText ); + UMLApp::app()->getDocument()->setModified(true); + } + setVisible( true ); + updateComponentSize(); + update(); +} + +void FloatingTextWidget::setText(const QString &t) { + if (m_Role == Uml::tr_Seq_Message || m_Role == Uml::tr_Seq_Message_Self) { + QString seqNum, op; + m_pLink->getSeqNumAndOp(seqNum, op); + if (seqNum.length() > 0 || op.length() > 0) { + if (! m_pView->getShowOpSig()) + op.replace( QRegExp("\\(.*\\)"), "()" ); + m_Text = seqNum.append(": ").append( op ); + } else + m_Text = t; + } else + m_Text = t; + updateComponentSize(); + update(); +} + +void FloatingTextWidget::setPreText (const QString &t) +{ + m_PreText = t; + updateComponentSize(); + update(); +} + +void FloatingTextWidget::setPostText(const QString &t) { + m_PostText = t; + updateComponentSize(); + update(); +} + +void FloatingTextWidget::changeTextDlg() { + bool ok = false; + QString newText = KInputDialog::getText(i18n("Change Text"), i18n("Enter new text:"), getText(), &ok, m_pView); + + if(ok && newText != getText() && isTextValid(newText)) { + setText( newText ); + setVisible( ( getText().length() > 0 ) ); + updateComponentSize(); + update(); + } + if(!isTextValid(newText)) + hide(); +} + +void FloatingTextWidget::showOpDlg() { + if (m_pLink == NULL) { + kError() << "FloatingTextWidget::showOpDlg: m_pLink is NULL" << endl; + return; + } + QString seqNum, opText; + UMLClassifier* c = m_pLink->getSeqNumAndOp(seqNum, opText); + if (c == NULL) { + kError() << "FloatingTextWidget::showOpDlg: " + << "m_pLink->getSeqNumAndOp() returns a NULL classifier" + << endl; + return; + } + + SelectOpDlg selectDlg(m_pView, c); + selectDlg.setSeqNumber( seqNum ); + if (m_pLink->getOperation() == NULL) { + selectDlg.setCustomOp( opText ); + } else { + selectDlg.setClassOp( opText ); + } + int result = selectDlg.exec(); + if(!result) { + return; + } + seqNum = selectDlg.getSeqNumber(); + opText = selectDlg.getOpText(); + if (selectDlg.isClassOp()) { + Model_Utils::OpDescriptor od; + Model_Utils::Parse_Status st = Model_Utils::parseOperation(opText, od, c); + if (st == Model_Utils::PS_OK) { + UMLClassifierList selfAndAncestors = c->findSuperClassConcepts(); + selfAndAncestors.prepend(c); + UMLOperation *op = NULL; + for (UMLClassifier *cl = selfAndAncestors.first(); cl; cl = selfAndAncestors.next()) { + op = cl->findOperation(od.m_name, od.m_args); + if (op != NULL) + break; + } + if (op == NULL) { + // The op does not yet exist. Create a new one. + UMLObject *o = c->createOperation(od.m_name, NULL, &od.m_args); + op = static_cast(o); + } + if (od.m_pReturnType) + op->setType(od.m_pReturnType); + m_pLink->setOperation(op); + opText = QString(); + } else { + m_pLink->setOperation(NULL); + } + } else { + m_pLink->setOperation(NULL); + } + m_pLink->setSeqNumAndOp(seqNum, opText); + setMessageText(); +} + +QString FloatingTextWidget::getPreText() const { + return m_PreText; +} + +QString FloatingTextWidget::getPostText() const { + return m_PostText; +} + +QString FloatingTextWidget::getText() const { + //test to make sure not just the ":" between the seq number + //and the actual message widget + // hmm. this section looks like it could have been avoided by using pre-, post- text + // instead of storing in the main body of the text -b.t. + if(m_Role == Uml::tr_Seq_Message || m_Role == Uml::tr_Seq_Message_Self || + m_Role == Uml::tr_Coll_Message || m_Role == Uml::tr_Coll_Message_Self) { + if( m_Text.length() <= 1 || m_Text == ": " ) + return ""; + } + return m_Text; +} + +QString FloatingTextWidget::getDisplayText() const +{ + QString displayText = m_Text; + displayText.prepend(m_PreText); + displayText.append(m_PostText); + return displayText; +} + +bool FloatingTextWidget::activate( IDChangeLog* ChangeLog /*= 0 */) { + if (! UMLWidget::activate(ChangeLog)) + return false; + update(); + return true; +} + +void FloatingTextWidget::setLink(LinkWidget * l) { + m_pLink = l; +} + +LinkWidget * FloatingTextWidget::getLink() { + return m_pLink; +} + +void FloatingTextWidget::setRole(Uml::Text_Role role) { + m_Role = role; +} + +Uml::Text_Role FloatingTextWidget::getRole() const { + return m_Role; +} + +bool FloatingTextWidget::isTextValid( const QString &text ) { + int length = text.length(); + if(length < 1) + return false; + for(int i=0;ishowDialog(); + } +} + +void FloatingTextWidget::saveToXMI( QDomDocument & qDoc, QDomElement & qElement ) { + QDomElement textElement = qDoc.createElement( "floatingtext" ); + UMLWidget::saveToXMI( qDoc, textElement ); + textElement.setAttribute( "text", m_Text ); + textElement.setAttribute( "pretext", m_PreText ); + textElement.setAttribute( "posttext", m_PostText ); + + /* No need to save these - the messagewidget already did it. + m_Operation = qElement.attribute( "operation", "" ); + m_SeqNum = qElement.attribute( "seqnum", "" ); + */ + + textElement.setAttribute( "role", m_Role ); + qElement.appendChild( textElement ); +} + +bool FloatingTextWidget::loadFromXMI( QDomElement & qElement ) { + if( !UMLWidget::loadFromXMI( qElement ) ) + return false; + + QString role = qElement.attribute( "role", "" ); + if( !role.isEmpty() ) + m_Role = (Uml::Text_Role)role.toInt(); + + m_PreText = qElement.attribute( "pretext", "" ); + m_PostText = qElement.attribute( "posttext", "" ); + m_Text = qElement.attribute( "text", "" ); + // If all texts are empty then this is a useless widget. + // In that case we return false. + // CAVEAT: The caller should not interpret the false return value + // as an indication of failure since previous umbrello versions + // saved lots of these empty FloatingTexts. + bool isDummy = (m_Text.isEmpty() && m_PreText.isEmpty() && m_PostText.isEmpty()); + return !isDummy; +} + +void FloatingTextWidget::setMessageText() { + if (m_pLink) + m_pLink->setMessageText(this); + setVisible(getText().length() > 0); + updateComponentSize(); +} + + +#include "floatingtextwidget.moc" + diff --git a/umbrello/umbrello/floatingtextwidget.h b/umbrello/umbrello/floatingtextwidget.h new file mode 100644 index 00000000..a8a1bfa8 --- /dev/null +++ b/umbrello/umbrello/floatingtextwidget.h @@ -0,0 +1,303 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef FLOATINGTEXTWIDGET_H +#define FLOATINGTEXTWIDGET_H + +#include "umlwidget.h" +#include "linkwidget.h" + +class UMLView; + +class FloatingTextWidgetController; + +/** + * This is a multipurpose class. In its simplest form it will display a + * line of text. + * It can also be setup to be the text for an operation with regard to the + * @ref MessageWidget on the sequence diagram. + * It is also used for the text required for an association. + * + * The differences between all these different uses will be the popup menu + * that is associated with it. + * + * @short Displays a line of text or an operation. + * @author Paul Hensgen + * @see UMLWidget + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + +class FloatingTextWidget : public UMLWidget { + Q_OBJECT +public: + friend class FloatingTextWidgetController; + + /** sometimes the x/y values get numbers of <0 and >10000 - which is + probably due to a bug somewhere in calculating the position. + -> workaround till problem is found: define min and max limits + => if x or y is outside of interval, the position is reset + ( e.g. by AssociationWidget::resetTextPositions() ) + */ + static const int restrictPositionMin = 0; + static const int restrictPositionMax = 3000; + + + /** + * Constructs a FloatingTextWidget instance. + * + * @param view The parent of this FloatingTextWidget. + * @param role The role this FloatingTextWidget will take up. + * @param text The main text to display. + * @param id The ID to assign (-1 will prompt a new ID.) + */ + explicit FloatingTextWidget(UMLView * view, Uml::Text_Role role = Uml::tr_Floating, + const QString& text = "", Uml::IDType id = Uml::id_None); + + /** + * destructor + */ + virtual ~FloatingTextWidget(); + + /** + * Set the main body of text to display. + * + * @param t The text to display. + */ + void setText(const QString &t); + + /** + * Set some text to be prepended to the main body of text. + * @param t The text to prepend to main body which is displayed. + */ + void setPreText(const QString &t); + + /** + * Set some text to be appended to the main body of text. + * @param t The text to append to main body which is displayed. + */ + void setPostText(const QString &t); + + /** + * Set the sequence number to display. + * + * @param sn The sequence number to display. + */ + void setSeqNum(const QString &sn); + + /** + * Return the sequence number. + * + * @return The sequence number. + */ + QString getSeqNum() const; + + /** + * Set the operation to display. + * + * @param op The operation to display. + */ + void setOperation(const QString &op); + + /** + * Return the operation that is displayed. + * + * @return The operation that is displayed. + * + QString getOperation() const; + */ + + /** + * Use to get the _main body_ of text (e.g. prepended and appended + * text is omitted) as currently displayed by the widget. + * + * @return The main text currently being displayed by the widget. + */ + QString getText() const; + + /** + * Use to get the pre-text which is prepended to the main body of + * text to be displayed. + * + * @return The pre-text currently displayed by the widget. + */ + QString getPreText() const; + + /** + * Use to get the post-text which is appended to the main body of + * text to be displayed. + * + * @return The post-text currently displayed by the widget. + */ + QString getPostText() const; + + /** + * Use to get the total text (prepended + main body + appended) + * currently displayed by the widget. + * + * @return The text currently being displayed by the widget. + */ + QString getDisplayText() const; + + /** + * Displays a dialog box to change the text. + */ + void changeTextDlg(); + + /** + * Set the LinkWidget that this FloatingTextWidget is related to. + * + * @param l The related LinkWidget. + */ + void setLink(LinkWidget * l); + + /** + * Returns the LinkWidget this floating text is related to. + * + * @return The LinkWidget this floating text is related to. + */ + LinkWidget * getLink(); + + /** + * Returns whether this is a line of text. + * Used for transparency in printing. + * + * @return Returns whether this is a line of text. + */ + bool isText() { + return true; + } + + /** + * Activate the FloatingTextWidget after the saved data has been loaded + * + * @param ChangeLog Pointer to the IDChangeLog. + * @return true for success + */ + bool activate( IDChangeLog* ChangeLog = 0 ); + + /** + * Sets the role type of this FloatingTextWidget. + * + * @param role The Text_Role of this FloatingTextWidget. + */ + void setRole(Uml::Text_Role role); + + /** + * Return the role of the text widget + * + * @return The Text_Role of this FloatingTextWidget. + */ + Uml::Text_Role getRole() const; + + /** + * For a text to be valid it must be non-empty, i.e. have a length + * larger that zero, and have at least one non whitespace character. + * + * @param text The string to analyze. + * @return True if the given text is valid. + */ + static bool isTextValid(const QString &text); + + /** + * Overrides default method + */ + void draw(QPainter & p, int offsetX, int offsetY); + + /** + * Handle the ListPopupMenu::mt_Rename case of the slotMenuSelection. + * Given an own method because it requires rather lengthy code. + */ + void handleRename(); + + /** + * Change Name + */ + void changeName(const QString& newText); + + /** + * Shows an operation dialog box. + */ + void showOpDlg(); + + /** + * Show the properties for a FloatingTextWidget. + * Depending on the role of the floating text wiget, the options dialog + * for the floating text widget, the rename dialog for floating text or + * the options dialog for the link widget are shown. + */ + void showProperties(); + + /** + * Creates the "floatingtext" XMI element. + */ + void saveToXMI( QDomDocument & qDoc, QDomElement & qElement ); + + /** + * Loads the "floatingtext" XMI element. + */ + bool loadFromXMI( QDomElement & qElement ); + +public slots: + /** + * Called when a menu selection has been made. + * This method is public due to called by @ref MessageWidget + * when this is text for a @ref MessageWidget. + * + * @param sel The selection that has been made. + */ + void slotMenuSelection(int sel); + + /** + * Sets the text for this label if it is acting as a sequence + * diagram message or a collaboration diagram message. + */ + void setMessageText(); + +protected: + /** + * Overrides method from UMLWidget. + */ + QSize calculateSize(); + +private: + /** + * Initializes key variables of the class. + */ + void init(); + + /** + * Override default method + */ + void resizeEvent(QResizeEvent* /*re*/); + + /** + * The association or message widget we may be linked to. + */ + LinkWidget * m_pLink; + + //////////////////// Data loaded/saved: + + /// Prepended text (such as for scope of association Role or method) + QString m_PreText; + /** + * Ending text (such as bracket on changability notation for + * association Role) + */ + QString m_PostText; + + /** + * The role the text widget will enact. + */ + Uml::Text_Role m_Role; + +}; + +#endif diff --git a/umbrello/umbrello/floatingtextwidgetcontroller.cpp b/umbrello/umbrello/floatingtextwidgetcontroller.cpp new file mode 100644 index 00000000..4c6a4aa8 --- /dev/null +++ b/umbrello/umbrello/floatingtextwidgetcontroller.cpp @@ -0,0 +1,119 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "floatingtextwidgetcontroller.h" +// qt/kde includes +#include +// app includes +#include "floatingtextwidget.h" +#include "messagewidget.h" +#include "objectwidget.h" + +FloatingTextWidgetController::FloatingTextWidgetController(FloatingTextWidget *floatingTextWidget): + UMLWidgetController(floatingTextWidget) { + m_floatingTextWidget = floatingTextWidget; + m_unconstrainedPositionX = 0; + m_unconstrainedPositionY = 0; + m_movementDirectionX = 0; + m_movementDirectionY = 0; +} + +FloatingTextWidgetController::~FloatingTextWidgetController() { +} + +void FloatingTextWidgetController::saveWidgetValues(QMouseEvent *me) { + UMLWidgetController::saveWidgetValues(me); + + m_unconstrainedPositionX = m_widget->getX(); + m_unconstrainedPositionY = m_widget->getY(); + m_movementDirectionX = 0; + m_movementDirectionY = 0; +} + +bool FloatingTextWidgetController::isInResizeArea(QMouseEvent* /*me*/) { + return false; +} + +void FloatingTextWidgetController::moveWidgetBy(int diffX, int diffY) { + if (m_floatingTextWidget->m_Role == Uml::tr_Seq_Message_Self) + return; + + if (m_floatingTextWidget->m_Role == Uml::tr_Seq_Message + && ((MessageWidget*)m_floatingTextWidget->m_pLink)->getSelected()) { + return; + } + + m_unconstrainedPositionX += diffX; + m_unconstrainedPositionY += diffY; + QPoint constrainedPosition = constrainPosition(diffX, diffY); + + int newX = constrainedPosition.x(); + int newY = constrainedPosition.y(); + + if (!m_movementDirectionX) { + if (m_unconstrainedPositionX != constrainedPosition.x()) { + m_movementDirectionX = (diffX > 0)? 1: -1; + } + } else if ((m_movementDirectionX < 0 && m_unconstrainedPositionX > m_floatingTextWidget->getX()) || + (m_movementDirectionX > 0 && m_unconstrainedPositionX < m_floatingTextWidget->getX()) ) { + newX = m_unconstrainedPositionX; + m_movementDirectionX = 0; + } + + if (!m_movementDirectionY) { + if (m_unconstrainedPositionY != constrainedPosition.y()) { + m_movementDirectionY = (diffY > 0)? 1: -1; + } + } else if ((m_movementDirectionY < 0 && m_unconstrainedPositionY > m_floatingTextWidget->getY()) || + (m_movementDirectionY > 0 && m_unconstrainedPositionY < m_floatingTextWidget->getY()) ) { + newY = m_unconstrainedPositionY; + m_movementDirectionY = 0; + } + + m_floatingTextWidget->setX(newX); + m_floatingTextWidget->setY(newY); + + if (m_floatingTextWidget->m_pLink) { + m_floatingTextWidget->m_pLink->calculateNameTextSegment(); + if (m_floatingTextWidget->m_Role == Uml::tr_Seq_Message) { + MessageWidget* messageWidget = (MessageWidget*)m_floatingTextWidget->m_pLink; + messageWidget->setY(newY + m_floatingTextWidget->getHeight()); + + //TODO This should be moved to somewhere in MessageWidget, refactor with messagewidgetcontroller.cpp:44 + if (messageWidget->getSequenceMessageType() == Uml::sequence_message_creation) { + const int objWidgetHalfHeight = messageWidget->getWidget(Uml::B)->getHeight() / 2; + messageWidget->getWidget(Uml::B)->UMLWidget::setY(messageWidget->getY() - objWidgetHalfHeight); + } + } + } +} + +void FloatingTextWidgetController::constrainMovementForAllWidgets(int &diffX, int &diffY) { + QPoint constrainedPosition = constrainPosition(diffX, diffY); + + diffX = constrainedPosition.x() - m_floatingTextWidget->getX(); + diffY = constrainedPosition.y() - m_floatingTextWidget->getY(); +} + +QPoint FloatingTextWidgetController::constrainPosition(int diffX, int diffY) { + int newX = m_floatingTextWidget->getX() + diffX; + int newY = m_floatingTextWidget->getY() + diffY; + + if (m_floatingTextWidget->m_pLink) { + m_floatingTextWidget->m_pLink->constrainTextPos(newX, newY, + m_floatingTextWidget->width(), m_floatingTextWidget->height(), + m_floatingTextWidget->m_Role); + } + + return QPoint(newX, newY); +} + diff --git a/umbrello/umbrello/floatingtextwidgetcontroller.h b/umbrello/umbrello/floatingtextwidgetcontroller.h new file mode 100644 index 00000000..f73b359a --- /dev/null +++ b/umbrello/umbrello/floatingtextwidgetcontroller.h @@ -0,0 +1,152 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef FLOATINGTEXTWIDGETCONTROLLER_H +#define FLOATINGTEXTWIDGETCONTROLLER_H + +#include "umlwidgetcontroller.h" + +class FloatingTextWidget; + +/** + * Controller for FloatingTextWidget. + * + * When moving a FloatingTextWidget, it is constrained using constrainTextPos + * method from the LinkWidget, if any. + * This applies both when moving as part of a selection and when constraining + * the move of the selection because it's the receiver of mouse move events. + * The only exception to this is that when moving the widget, if it's a sequence + * message and the message widget is selected, the floating text widget isn't + * moved itself, but automatically by the message widget. + * If the sequence message wasn't part of the selection, the floating text + * widget moves it. + * When moving the floating text as part of a selection, if the position of + * the floating text is constrained, it's kept at that position until it can + * be moved to another valid position. + * No resize is allowed for FloatingTextWidget. + * + * @author Umbrello UML Modeller Authors + */ +class FloatingTextWidgetController : public UMLWidgetController { +public: + + /** + * Constructor for FloatingTextWidgetController. + * + * @param floatingTextWidget The floating text widget which uses the controller. + */ + FloatingTextWidgetController(FloatingTextWidget *floatingTextWidget); + + /** + * Destructor for MessageWidgetController. + */ + ~FloatingTextWidgetController(); + +protected: + + /** + * Overridden from UMLWidgetController. + * Saves the values of the widget needed for move/resize. + * Calls parent method and then saves the value of m_unconstrainedPositionX/Y + * and m_movementDirectionX/Y. + * + * @param me The QMouseEvent to get the offset from. + */ + virtual void saveWidgetValues(QMouseEvent *me); + + /** + * Overridden from UMLWidgetController. + * FloatingTextWidgets can't be resized, so this method always returns false. + * Cursor isn't changed. + * + * @param me The QMouseEVent to check. + * @return true if the mouse is in resize area, false otherwise. + */ + virtual bool isInResizeArea(QMouseEvent *me); + + /** + * Overridden from UMLWidgetController. + * Moves the widget to a new position using the difference between the + * current position and the new position. + * If the floating text widget is part of a sequence message, and the + * message widget is selected, it does nothing: the message widget will + * update the text position when it's moved. + * In any other case, the floating text widget constrains its move using + * constrainPosition. When the position of the floating text is constrained, + * it's kept at that position until it can be moved to another valid + * position (m_unconstrainedPositionX/Y and m_movementDirectionX/Y are + * used for that). + * Moreover, if is part of a sequence message (and the message widget + * isn't selected), it updates the position of the message widget. + * @see constrainPosition + * + * @param diffX The difference between current X position and new X position. + * @param diffY The difference between current Y position and new Y position. + */ + virtual void moveWidgetBy(int diffX, int diffY); + + /** + * Overridden from UMLWidgetController. + * Modifies the value of the diffX and diffY variables used to move the + * widgets. + * The values are constrained using constrainPosition. + * @see constrainPosition + * + * @param diffX The difference between current X position and new X position. + * @param diffY The difference between current Y position and new Y position. + */ + virtual void constrainMovementForAllWidgets(int &diffX, int &diffY); + +private: + + /** + * Returns a constrained position for the widget after applying the position + * difference. + * If no link widget exists, the position returned is the current widget + * position with the difference applied. If there's a link, the position + * to be returned is constrained using constrainTextPos method from the + * LinkWidget, if any. + * + * @param diffX The difference between current X position and new X position. + * @param diffY The difference between current Y position and new Y position. + * @return A QPoint with the constrained new position. + */ + QPoint constrainPosition(int diffX, int diffY); + + /** + * The floating text widget which uses the controller. + */ + FloatingTextWidget *m_floatingTextWidget; + + /** + * The horizontal position the widget would have if its move wasn't constrained. + */ + int m_unconstrainedPositionX; + + /** + * The vertical position the widget would have if its move wasn't constrained. + */ + int m_unconstrainedPositionY; + + /** + * The X direction the widget was moved when the constrain was applied. + * -1 means left, 1 means right. + */ + int m_movementDirectionX; + + /** + * The Y direction the widget was moved when the constrain was applied. + * -1 means up, 1 means down. + */ + int m_movementDirectionY; +}; + +#endif diff --git a/umbrello/umbrello/folder.cpp b/umbrello/umbrello/folder.cpp new file mode 100644 index 00000000..8317bab1 --- /dev/null +++ b/umbrello/umbrello/folder.cpp @@ -0,0 +1,412 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "folder.h" +// qt/kde includes +#include +#include +#include +#include +// app includes +#include "uml.h" +#include "umldoc.h" +#include "umlview.h" +#include "umllistview.h" +#include "optionstate.h" +#include "object_factory.h" +#include "model_utils.h" + +UMLFolder::UMLFolder(const QString & name, Uml::IDType id) + : UMLPackage(name, id) { + init(); +} + +UMLFolder::~UMLFolder() { +} + +void UMLFolder::init() { + m_BaseType = Uml::ot_Folder; + m_diagrams.setAutoDelete(true); + UMLObject::setStereotype("folder"); +} + +UMLObject* UMLFolder::clone() const { + UMLFolder *clone = new UMLFolder(); + UMLObject::copyInto(clone); + return clone; +} + +void UMLFolder::setLocalName(const QString& localName) { + m_localName = localName; +} + +QString UMLFolder::getLocalName() { + return m_localName; +} + +void UMLFolder::addView(UMLView *view) { + m_diagrams.append(view); +} + +void UMLFolder::removeView(UMLView *view) { + // m_diagrams is set to autodelete! + m_diagrams.remove(view); +} + +void UMLFolder::appendViews(UMLViewList& viewList, bool includeNested) { + if (includeNested) { + UMLObject *o; + for (UMLObjectListIt oit(m_objects); (o = oit.current()) != NULL; ++oit) { + if (o->getBaseType() == Uml::ot_Folder) { + UMLFolder *f = static_cast(o); + f->appendViews(viewList); + } + } + } + UMLView *v; + for (UMLViewListIt vit(m_diagrams); (v = vit.current()) != NULL; ++vit) + viewList.append(v); +} + +void UMLFolder::activateViews() { + UMLObject *o; + for (UMLObjectListIt oit(m_objects); (o = oit.current()) != NULL; ++oit) { + if (o->getBaseType() == Uml::ot_Folder) { + UMLFolder *f = static_cast(o); + f->activateViews(); + } + } + UMLView *v; + for (UMLViewListIt vit(m_diagrams); (v = vit.current()) != NULL; ++vit) + v->activateAfterLoad(); + // Make sure we have a treeview item for each diagram. + // It may happen that we are missing them after switching off tabbed widgets. + Settings::OptionState optionState = Settings::getOptionState(); + if (optionState.generalState.tabdiagrams) + return; + UMLListView *lv = UMLApp::app()->getListView(); + for (UMLViewListIt it(m_diagrams); (v = it.current()) != NULL; ++it) { + if (lv->findItem(v->getID()) != NULL) + continue; + lv->createDiagramItem(v); + } +} + +UMLView *UMLFolder::findView(Uml::IDType id) { + UMLView *v = NULL; + for (UMLViewListIt vit(m_diagrams); (v = vit.current()) != NULL; ++vit) { + if (v->getID() == id) + return v; + } + UMLObject *o; + for (UMLObjectListIt oit(m_objects); (o = oit.current()) != NULL; ++oit) { + if (o->getBaseType() != Uml::ot_Folder) + continue; + UMLFolder *f = static_cast(o); + v = f->findView(id); + if (v) + break; + } + return v; +} + +UMLView *UMLFolder::findView(Uml::Diagram_Type type, const QString &name, bool searchAllScopes) { + UMLView *v = NULL; + for (UMLViewListIt vit(m_diagrams); (v = vit.current()) != NULL; ++vit) { + if (v->getType() == type && v->getName() == name) + return v; + } + if (searchAllScopes) { + UMLObject *o; + for (UMLObjectListIt oit(m_objects); (o = oit.current()) != NULL; ++oit) { + if (o->getBaseType() != Uml::ot_Folder) + continue; + UMLFolder *f = static_cast(o); + v = f->findView(type, name, searchAllScopes); + if (v) + break; + } + } + return v; +} + +void UMLFolder::setViewOptions(const Settings::OptionState& optionState) { + // for each view update settings + UMLView *v; + for (UMLViewListIt vit(m_diagrams); (v = vit.current()) != NULL; ++vit) + v->setOptionState(optionState); +} + +void UMLFolder::removeAllViews() { + UMLObject *o; + for (UMLObjectListIt oit(m_objects); (o = oit.current()) != NULL; ++oit) { + if (o->getBaseType() != Uml::ot_Folder) + continue; + UMLFolder *f = static_cast(o); + f->removeAllViews(); + } + UMLView *v = NULL; + for (UMLViewListIt vit(m_diagrams); (v = vit.current()) != NULL; ++vit) { + // TODO ------------------ check this code - bad: calling back to UMLDoc::removeView() + v->removeAllAssociations(); // note : It may not be apparent, but when we remove all associations + // from a view, it also causes any UMLAssociations that lack parent + // association widgets (but once had them) to remove themselves from + // this document. + UMLApp::app()->getDocument()->removeView(v, false); + } + m_diagrams.clear(); +} + +void UMLFolder::setFolderFile(const QString& fileName) { + m_folderFile = fileName; +} + +QString UMLFolder::getFolderFile() { + return m_folderFile; +} + +void UMLFolder::saveContents(QDomDocument& qDoc, QDomElement& qElement) { + QDomElement ownedElement = qDoc.createElement("UML:Namespace.ownedElement"); + UMLObject *obj; + // Save contained objects if any. + for (UMLObjectListIt oit(m_objects); (obj = oit.current()) != NULL; ++oit) + obj->saveToXMI (qDoc, ownedElement); + // Save asscociations if any. + for (UMLObjectListIt ait(m_List); (obj = ait.current()) != NULL; ++ait) + obj->saveToXMI (qDoc, ownedElement); + qElement.appendChild(ownedElement); + // Save diagrams to `extension'. + if (m_diagrams.count()) { + QDomElement diagramsElement = qDoc.createElement("diagrams"); + UMLView *pView; + for (UMLViewListIt vit(m_diagrams); (pView = vit.current()) != NULL; ++vit) { + pView->saveToXMI(qDoc, diagramsElement); + } + QDomElement extension = qDoc.createElement("XMI.extension"); + extension.setAttribute("xmi.extender", "umbrello"); + extension.appendChild( diagramsElement ); + qElement.appendChild(extension); + } +} + +void UMLFolder::save(QDomDocument& qDoc, QDomElement& qElement) { + UMLDoc *umldoc = UMLApp::app()->getDocument(); + QString elementName("UML:Package"); + const Uml::Model_Type mt = umldoc->rootFolderType(this); + if (mt != Uml::N_MODELTYPES) + elementName = "UML:Model"; + QDomElement folderElement = UMLObject::save(elementName, qDoc); + saveContents(qDoc, folderElement); + qElement.appendChild(folderElement); +} + +void UMLFolder::saveToXMI(QDomDocument& qDoc, QDomElement& qElement) { + if (m_folderFile.isEmpty()) { + save(qDoc, qElement); + return; + } + // See if we can create the external file. + // If not then internalize the folder. + UMLDoc *umldoc = UMLApp::app()->getDocument(); + QString fileName = umldoc->URL().directory() + '/' + m_folderFile; + QFile file(fileName); + if (!file.open(IO_WriteOnly)) { + kError() << "UMLFolder::saveToXMI(" << m_folderFile << "): " + << "cannot create file, contents will be saved in main model file" + << endl; + m_folderFile = QString::null; + save(qDoc, qElement); + return; + } + // Okay, external file is writable. Wrap up main file. + QDomElement folderElement = UMLObject::save("UML:Package", qDoc); + QDomElement extension = qDoc.createElement("XMI.extension"); + extension.setAttribute("xmi.extender", "umbrello"); + QDomElement fileElement = qDoc.createElement("external_file"); + fileElement.setAttribute("name", m_folderFile); + extension.appendChild(fileElement); + folderElement.appendChild(extension); + qElement.appendChild(folderElement); + + // Save folder to external file. + QDomDocument folderDoc; + QDomElement folderRoot; + QDomProcessingInstruction xmlHeading = + folderDoc.createProcessingInstruction("xml", + "version=\"1.0\" encoding=\"UTF-8\""); + folderDoc.appendChild(xmlHeading); + folderRoot = folderDoc.createElement("external_file"); + folderRoot.setAttribute("name", m_Name); + folderRoot.setAttribute("filename", m_folderFile); + folderRoot.setAttribute("mainModel", umldoc->URL().fileName()); + folderRoot.setAttribute("parentId", ID2STR(m_pUMLPackage->getID())); + folderRoot.setAttribute("parent", m_pUMLPackage->getFullyQualifiedName("::", true)); + saveContents(folderDoc, folderRoot); + folderDoc.appendChild(folderRoot); + QTextStream stream(&file); + stream.setEncoding(QTextStream::UnicodeUTF8); + stream << folderDoc.toString(); + file.close(); +} + +bool UMLFolder::loadDiagramsFromXMI(QDomNode& diagrams) { + const Settings::OptionState optionState = Settings::getOptionState(); + UMLDoc *umldoc = UMLApp::app()->getDocument(); + bool totalSuccess = true; + for (QDomElement diagram = diagrams.toElement(); !diagram.isNull(); + diagrams = diagrams.nextSibling(), diagram = diagrams.toElement()) { + QString tag = diagram.tagName(); + if (tag != "diagram") { + kDebug() << "UMLFolder::loadDiagramsFromXMI: ignoring " + << tag << " in " << endl; + continue; + } + UMLView * pView = new UMLView(this); + pView->setOptionState(optionState); + if (pView->loadFromXMI(diagram)) { + pView->hide(); + umldoc->addView(pView); + } else { + delete pView; + totalSuccess = false; + } + } + return totalSuccess; +} + +bool UMLFolder::loadFolderFile(const QString& path) { + QFile file(path); + if (!file.exists()) { + KMessageBox::error(0, i18n("The folderfile %1 does not exist.").arg(path), i18n("Load Error")); + return false; + } + if (!file.open(IO_ReadOnly)) { + KMessageBox::error(0, i18n("The folderfile %1 cannot be opened.").arg(path), i18n("Load Error")); + return false; + } + QTextStream stream(&file); + QString data = stream.read(); + file.close(); + QDomDocument doc; + QString error; + int line; + if (!doc.setContent( data, false, &error, &line)) { + kError() << "UMLFolder::loadFolderFile: Can't set content:" + << error << " line:" << line << endl; + return false; + } + QDomNode rootNode = doc.firstChild(); + while (rootNode.isComment() || rootNode.isProcessingInstruction()) { + rootNode = rootNode.nextSibling(); + } + if (rootNode.isNull()) { + kError() << "UMLFolder::loadFolderFile: Root node is Null" << endl; + return false; + } + QDomElement element = rootNode.toElement(); + QString type = element.tagName(); + if (type != "external_file") { + kError() << "UMLFolder::loadFolderFile: Root node has unknown type " + << type << endl; + return false; + } + return load(element); +} + +bool UMLFolder::load(QDomElement& element) { + UMLDoc *umldoc = UMLApp::app()->getDocument(); + bool totalSuccess = true; + for (QDomNode node = element.firstChild(); !node.isNull(); + node = node.nextSibling()) { + if (node.isComment()) + continue; + QDomElement tempElement = node.toElement(); + QString type = tempElement.tagName(); + if (Model_Utils::isCommonXMIAttribute(type)) + continue; + if (Uml::tagEq(type, "Namespace.ownedElement") || + Uml::tagEq(type, "Namespace.contents")) { + //CHECK: Umbrello currently assumes that nested elements + // are ownedElements anyway. + // Therefore these tags are not further interpreted. + if (! load(tempElement)) { + kDebug() << "An error happened while loading the " << type + << " of the " << m_Name << endl; + totalSuccess = false; + } + continue; + } else if (type == "XMI.extension") { + for (QDomNode xtnode = node.firstChild(); !xtnode.isNull(); + xtnode = xtnode.nextSibling()) { + QDomElement el = xtnode.toElement(); + const QString xtag = el.tagName(); + if (xtag == "diagrams") { + QDomNode diagramNode = xtnode.firstChild(); + if (!loadDiagramsFromXMI(diagramNode)) + totalSuccess = false; + } else if (xtag == "external_file") { + const QString rootDir(umldoc->URL().directory()); + QString fileName = el.attribute("name", ""); + const QString path(rootDir + '/' + fileName); + if (loadFolderFile(path)) + m_folderFile = fileName; + } else { + kDebug() << "UMLFolder::load(" << m_Name + << "): ignoring XMI.extension " << xtag << endl; + continue; + } + } + continue; + } + // Do not re-create the predefined Datatypes folder in the Logical View, + // it already exists. + UMLFolder *logicalView = umldoc->getRootFolder(Uml::mt_Logical); + if (this == logicalView && Uml::tagEq(type, "Package")) { + QString thisName = tempElement.attribute("name", ""); + if (thisName == "Datatypes") { + UMLFolder *datatypeFolder = umldoc->getDatatypeFolder(); + if (!datatypeFolder->loadFromXMI(tempElement)) + totalSuccess = false; + continue; + } + } + UMLObject *pObject = NULL; + // Avoid duplicate creation of forward declared object + QString idStr = tempElement.attribute("xmi.id", ""); + if (!idStr.isEmpty()) { + Uml::IDType id = STR2ID(idStr); + pObject = umldoc->findObjectById(id); + if (pObject) { + kDebug() << "UMLFolder::load: object " << idStr + << "already exists" << endl; + } + } + if (pObject == NULL) { + QString stereoID = tempElement.attribute("stereotype", ""); + pObject = Object_Factory::makeObjectFromXMI(type, stereoID); + if (!pObject) { + kWarning() << "UMLFolder::load: " + << "Unknown type of umlobject to create: " << type << endl; + continue; + } + } + pObject->setUMLPackage(this); + if (!pObject->loadFromXMI(tempElement)) { + removeObject(pObject); + delete pObject; + totalSuccess = false; + } + } + return totalSuccess; +} + +#include "folder.moc" diff --git a/umbrello/umbrello/folder.h b/umbrello/umbrello/folder.h new file mode 100644 index 00000000..2e4d23f6 --- /dev/null +++ b/umbrello/umbrello/folder.h @@ -0,0 +1,199 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLFOLDER_H +#define UMLFOLDER_H + +#include "package.h" +#include "umlviewlist.h" +#include "optionstate.h" + +/** + * This class manages the UMLObjects and UMLViews of a Folder. + * This class inherits from UMLPackage which contains most + * of the information. + * + * The UMLDoc class allocates a fixed instance of this class for + * each of the predefined Logical, UseCase, Component, Deployment, and + * Entity-Relationship folders. Further instances are created on demand + * for user folders. + * + * @short Non-graphical management of objects and diagrams of a Folder + * @author Oliver Kellogg + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + +class UMLFolder : public UMLPackage { + Q_OBJECT +public: + /** + * Sets up a Folder. + * + * @param name The name of the Folder. + * @param id The unique id of the Folder. A new ID will be generated + * if this argument is left away. + */ + explicit UMLFolder(const QString & name = "", Uml::IDType id = Uml::id_None); + + /** + * Empty deconstructor. + */ + virtual ~UMLFolder(); + + /** + * Initializes key variables of the class. + */ + virtual void init(); + + /** + * Make a clone of this object. + */ + virtual UMLObject* clone() const; + + /** + * Set the localized name of this folder. + * This is set for the predefined root views (Logical, + * UseCase, Component, Deployment, EntityRelationship, + * and the Datatypes folder inside the Logical View.) + */ + void setLocalName(const QString& localName); + + /** + * Return the localized name of this folder. + * Only useful for the predefined root folders. + */ + QString getLocalName(); + + /** + * Add a view to the diagram list. + */ + void addView(UMLView *view); + + /** + * Remove a view from the diagram list. + */ + void removeView(UMLView *view); + + /** + * Append the views in this folder to the given diagram list. + * + * @param viewList The UMLViewList to which to append the diagrams. + * @param includeNested Whether to include diagrams from nested folders + * (default: true.) + */ + void appendViews(UMLViewList& viewList, bool includeNested = true); + + /** + * Acivate the views in this folder. + * "Activation": Some widgets require adjustments after loading from file, + * those are done here. + */ + void activateViews(); + + /** + * Seek a view of the given ID in this folder. + * + * @param id ID of the view to find. + * @return Pointer to the view if found, NULL if no view found. + */ + UMLView *findView(Uml::IDType id); + + /** + * Seek a view by the type and name given. + * + * @param type The type of view to find. + * @param name The name of the view to find. + * @param searchAllScopes Search in all subfolders (default: true.) + * @return Pointer to the view found, or NULL if not found. + */ + UMLView * findView(Uml::Diagram_Type type, const QString &name, bool searchAllScopes = true); + + /** + * Set the options for the views in this folder. + */ + void setViewOptions(const Settings::OptionState& optionState); + + /** + * Remove all views in this folder. + */ + void removeAllViews(); + + /** + * Set the folder file name for a separate submodel. + */ + void setFolderFile(const QString& fileName); + + /** + * Get the folder file name for a separate submodel. + */ + QString getFolderFile(); + + /** + * Creates a UML:Model or UML:Package element: + * UML:Model is created for the predefined fixed folders, + * UML:Package with stereotype "folder" is created for all else. + */ + void saveToXMI( QDomDocument & qDoc, QDomElement & qElement ); + +protected: + /** + * Auxiliary to saveToXMI(): Save the contained objects and diagrams. + * Can be used regardless of whether saving to the main model file + * or to an external folder file (see m_folderFile.) + */ + void saveContents(QDomDocument& qDoc, QDomElement& qElement); + + /** + * Auxiliary to saveToXMI(): Creates a element when saving + * a predefined modelview, or a element when saving a + * user created folder. Invokes saveContents() with the newly created + * element. + */ + void save(QDomDocument& qDoc, QDomElement& qElement); + + /** + * Auxiliary to load(): + * Load the diagrams from the "diagrams" in the + */ + bool loadDiagramsFromXMI(QDomNode& diagrams); + + /** + * Folders in the listview can be marked such that their contents + * are saved to a separate file. + * This method loads the separate folder file. + * CAVEAT: This is not XMI standard compliant. + * If standard compliance is an issue then avoid folder files. + * + * @param path Fully qualified file name, i.e. absolute directory + * plus file name. + * @return True for success. + */ + bool loadFolderFile(const QString& path); + + /** + * Loads the UML:Component element. + */ + bool load(QDomElement & element); + +private: + QString m_localName; ///< i18n name, only used for predefined root folders + /** + * If m_folderFile is not empty then it contains a file name to which + * this folder is saved. + * In this case the folder file acts as a physically separate submodel. + * What is saved in the main model is not the folder contents but a + * reference to the folder file. + */ + QString m_folderFile; + UMLViewList m_diagrams; +}; + +#endif diff --git a/umbrello/umbrello/forkjoinwidget.cpp b/umbrello/umbrello/forkjoinwidget.cpp new file mode 100644 index 00000000..5a1ce3b8 --- /dev/null +++ b/umbrello/umbrello/forkjoinwidget.cpp @@ -0,0 +1,112 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2005-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "forkjoinwidget.h" +//qt includes +#include +//kde includes +#include +#include +//app includes +#include "umlview.h" +#include "listpopupmenu.h" + +ForkJoinWidget::ForkJoinWidget(UMLView * view, bool drawVertical, Uml::IDType id) + : BoxWidget(view, id), m_drawVertical(drawVertical) { + init(); +} + +void ForkJoinWidget::init() { + WidgetBase::setBaseType( Uml::wt_ForkJoin ); + UMLWidget::updateComponentSize(); +} + +ForkJoinWidget::~ForkJoinWidget() { +} + +QSize ForkJoinWidget::calculateSize() { + if (m_drawVertical) { + return QSize(4, 40); + } else { + return QSize(40, 4); + } +} + +void ForkJoinWidget::draw(QPainter& p, int offsetX, int offsetY) { + p.fillRect( offsetX, offsetY, width(), height(), QBrush( Qt::black )); + + if (m_bSelected) { + drawSelected(&p, offsetX, offsetY); + } +} + +void ForkJoinWidget::drawSelected(QPainter *, int /*offsetX*/, int /*offsetY*/) { +} + +void ForkJoinWidget::constrain(int& width, int& height) { + if (m_drawVertical) { + if (width < 4) + width = 4; + else if (width > 10) + width = 10; + if (height < 40) + height = 40; + else if (height > 100) + height = 100; + } else { + if (height < 4) + height = 4; + else if (height > 10) + height = 10; + if (width < 40) + width = 40; + else if (width > 100) + width = 100; + } +} + +void ForkJoinWidget::slotMenuSelection(int sel) { + switch (sel) { + case ListPopupMenu::mt_Flip: + setDrawVertical(!m_drawVertical); + break; + default: + break; + } +} + +void ForkJoinWidget::setDrawVertical(bool to) { + m_drawVertical = to; + updateComponentSize(); + UMLWidget::adjustAssocs( getX(), getY() ); +} + +bool ForkJoinWidget::getDrawVertical() const { + return m_drawVertical; +} + +void ForkJoinWidget::saveToXMI(QDomDocument& qDoc, QDomElement& qElement) { + QDomElement fjElement = qDoc.createElement("forkjoin"); + UMLWidget::saveToXMI(qDoc, fjElement); + fjElement.setAttribute("drawvertical", m_drawVertical); + qElement.appendChild(fjElement); +} + +bool ForkJoinWidget::loadFromXMI(QDomElement& qElement) { + if ( !UMLWidget::loadFromXMI(qElement) ) { + return false; + } + QString drawVertical = qElement.attribute("drawvertical", "0"); + setDrawVertical( (bool)drawVertical.toInt() ); + return true; +} + diff --git a/umbrello/umbrello/forkjoinwidget.h b/umbrello/umbrello/forkjoinwidget.h new file mode 100644 index 00000000..bf8c47c8 --- /dev/null +++ b/umbrello/umbrello/forkjoinwidget.h @@ -0,0 +1,103 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2005-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef FORKJOINWIDGET_H +#define FORKJOINWIDGET_H +//qt includes +#include +//app includes +#include "boxwidget.h" + +// fwd decl. +class UMLView; + +/** + * @short Displays a fork/join plate in a state diagram. + * @author Oliver Kellogg + * @see UMLWidget + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class ForkJoinWidget : public BoxWidget { +public: + + /** + * Constructs a ForkJoinWidget. + * + * @param view The parent to this widget. + * @param drawVertical Whether to draw the plate horizontally or vertically. + * @param id The ID to assign (-1 will prompt a new ID.) + */ + explicit ForkJoinWidget(UMLView * view, bool drawVertical = false, Uml::IDType id = Uml::id_None); + + /** + * destructor + */ + virtual ~ForkJoinWidget(); + + /** + * Set whether to draw the plate vertically. + */ + void setDrawVertical(bool to); + /** + * Get whether to draw the plate vertically. + */ + bool getDrawVertical() const; + + /** + * Overrides the function from UMLWidget. + * + * @param sel The command to be executed. + */ + void slotMenuSelection(int sel); + + /** + * Draws a slim solid black rectangle. + */ + void draw(QPainter & p, int offsetX, int offsetY); + + /** + * Saves the widget to the "forkjoinwidget" XMI element. + */ + void saveToXMI(QDomDocument& qDoc, QDomElement& qElement); + + /** + * Loads the widget from the "forkjoinwidget" XMI element. + */ + bool loadFromXMI(QDomElement & qElement); + +protected: + /** + * Reimplement method from UMLWidget to suppress the resize corner. + * Although the ForkJoinWidget supports resizing, we suppress the + * resize corner because it is too large for this very slim widget. + */ + void drawSelected(QPainter * p, int offsetX, int offsetY); + + /** + * Overrides the function from UMLWidget. + */ + QSize calculateSize(); + + /** + * Reimplement method from UMLWidget. + */ + void constrain(int& width, int& height); + +private: + /** + * Initializes key variables for the class. + */ + void init(); + + bool m_drawVertical; ///< whether to draw the plate horizontally or vertically +}; + +#endif diff --git a/umbrello/umbrello/headings/Makefile.am b/umbrello/umbrello/headings/Makefile.am new file mode 100644 index 00000000..334e9435 --- /dev/null +++ b/umbrello/umbrello/headings/Makefile.am @@ -0,0 +1,17 @@ +mydir = $(kde_datadir)/umbrello/headings +my_DATA = heading.adb \ +heading.ads \ +heading.as \ +heading.cpp \ +heading.cs \ +heading.d \ +heading.h \ +heading.idl \ +heading.java \ +heading.js \ +heading.php \ +heading.pm \ +heading.py \ +heading.rb \ +heading.sql \ +heading.xsd diff --git a/umbrello/umbrello/headings/heading.adb b/umbrello/umbrello/headings/heading.adb new file mode 100644 index 00000000..87dc70a8 --- /dev/null +++ b/umbrello/umbrello/headings/heading.adb @@ -0,0 +1,29 @@ +-- %filename% - Copyright %author% +-- +-- Here you can write a license for your code, some comments or any other +-- information you want to have in your generated code. To to this simply +-- configure the "headings" directory in uml to point to a directory +-- where you have your heading files. +-- +-- or you can just replace the contents of this file with your own. +-- If you want to do this, this file is located at +-- +-- %headingpath% +-- +-- -->Code Generators searches for heading files based on the file +-- extension i.e. it will look for a file name ending in ".h" to +-- include in C++ header files, and for a file name ending in +-- ".java" to include in all generated java code. If you name +-- the file "heading.", Code Generator will always +-- choose this file even if there are other files with the same +-- extension in the directory. If you name the file something +-- else, it must be the only one with that extension in the +-- directory to guarantee that Code Generator will choose it. +-- +-- you can use variables in your heading files which are replaced at +-- generation time. possible variables are : author, date, time, +-- filename and filepath. Just write %variable_name% +-- +-- This file was generated on %date% at %time% +-- The original location of this file is %filepath% + diff --git a/umbrello/umbrello/headings/heading.ads b/umbrello/umbrello/headings/heading.ads new file mode 100644 index 00000000..1e4a582c --- /dev/null +++ b/umbrello/umbrello/headings/heading.ads @@ -0,0 +1,30 @@ +-- %filename% - Copyright %author% +-- +-- Here you can write a license for your code, some comments or any other +-- information you want to have in your generated code. To to this simply +-- configure the "headings" directory in uml to point to a directory +-- where you have your heading files. +-- +-- or you can just replace the contents of this file with your own. +-- If you want to do this, this file is located at +-- +-- %headingpath% +-- +-- -->Code Generators searches for heading files based on the file +-- extension i.e. it will look for a file name ending in ".h" to +-- include in C++ header files, and for a file name ending in +-- ".java" to include in all generated java code. If you name +-- the file "heading.", Code Generator will always +-- choose this file even if there are other files with the same +-- extension in the directory. If you name the file something +-- else, it must be the only one with that extension in the +-- directory to guarantee that Code Generator will choose it. +-- +-- you can use variables in your heading files which are replaced at +-- generation time. possible variables are : author, date, time, +-- filename and filepath. Just write %variable_name% +-- +-- This file was generated on %date% at %time% +-- The original location of this file is %filepath% + + diff --git a/umbrello/umbrello/headings/heading.as b/umbrello/umbrello/headings/heading.as new file mode 100644 index 00000000..6e3f61cd --- /dev/null +++ b/umbrello/umbrello/headings/heading.as @@ -0,0 +1,29 @@ +/************************************************************************ + %filename% - Copyright %author% + +Here you can write a license for your code, some comments or any other +information you want to have in your generated code. To to this simply +configure the "headings" directory in uml to point to a directory +where you have your heading files. + +or you can just replace the contents of this file with your own. +If you want to do this, this file is located at + +%headingpath% + +-->Code Generators searches for heading files based on the file extension + i.e. it will look for a file name ending in ".h" to include in C++ header + files, and for a file name ending in ".java" to include in all generated + java code. + If you name the file "heading.", Code Generator will always + choose this file even if there are other files with the same extension in the + directory. If you name the file something else, it must be the only one with that + extension in the directory to guarantee that Code Generator will choose it. + +you can use variables in your heading files which are replaced at generation +time. possible variables are : author, date, time, filename and filepath. +just write %variable_name% + +This file was generated on %date% at %time% +The original location of this file is %filepath% +**************************************************************************/ diff --git a/umbrello/umbrello/headings/heading.cpp b/umbrello/umbrello/headings/heading.cpp new file mode 100644 index 00000000..d33911bf --- /dev/null +++ b/umbrello/umbrello/headings/heading.cpp @@ -0,0 +1,29 @@ +/************************************************************************ + %filename% - Copyright %author% + +Here you can write a license for your code, some comments or any other +information you want to have in your generated code. To to this simply +configure the "headings" directory in uml to point to a directory +where you have your heading files. + +or you can just replace the contents of this file with your own. +If you want to do this, this file is located at + +%headingpath% + +-->Code Generators searches for heading files based on the file extension + i.e. it will look for a file name ending in ".h" to include in C++ header + files, and for a file name ending in ".java" to include in all generated + java code. + If you name the file "heading.", Code Generator will always + choose this file even if there are other files with the same extension in the + directory. If you name the file something else, it must be the only one with that + extension in the directory to guarantee that Code Generator will choose it. + +you can use variables in your heading files which are replaced at generation +time. possible variables are : author, date, time, filename and filepath. +just write %variable_name% + +This file was generated on %date% at %time% +The original location of this file is %filepath% +**************************************************************************/ diff --git a/umbrello/umbrello/headings/heading.cs b/umbrello/umbrello/headings/heading.cs new file mode 100644 index 00000000..6e3f61cd --- /dev/null +++ b/umbrello/umbrello/headings/heading.cs @@ -0,0 +1,29 @@ +/************************************************************************ + %filename% - Copyright %author% + +Here you can write a license for your code, some comments or any other +information you want to have in your generated code. To to this simply +configure the "headings" directory in uml to point to a directory +where you have your heading files. + +or you can just replace the contents of this file with your own. +If you want to do this, this file is located at + +%headingpath% + +-->Code Generators searches for heading files based on the file extension + i.e. it will look for a file name ending in ".h" to include in C++ header + files, and for a file name ending in ".java" to include in all generated + java code. + If you name the file "heading.", Code Generator will always + choose this file even if there are other files with the same extension in the + directory. If you name the file something else, it must be the only one with that + extension in the directory to guarantee that Code Generator will choose it. + +you can use variables in your heading files which are replaced at generation +time. possible variables are : author, date, time, filename and filepath. +just write %variable_name% + +This file was generated on %date% at %time% +The original location of this file is %filepath% +**************************************************************************/ diff --git a/umbrello/umbrello/headings/heading.d b/umbrello/umbrello/headings/heading.d new file mode 100644 index 00000000..d33911bf --- /dev/null +++ b/umbrello/umbrello/headings/heading.d @@ -0,0 +1,29 @@ +/************************************************************************ + %filename% - Copyright %author% + +Here you can write a license for your code, some comments or any other +information you want to have in your generated code. To to this simply +configure the "headings" directory in uml to point to a directory +where you have your heading files. + +or you can just replace the contents of this file with your own. +If you want to do this, this file is located at + +%headingpath% + +-->Code Generators searches for heading files based on the file extension + i.e. it will look for a file name ending in ".h" to include in C++ header + files, and for a file name ending in ".java" to include in all generated + java code. + If you name the file "heading.", Code Generator will always + choose this file even if there are other files with the same extension in the + directory. If you name the file something else, it must be the only one with that + extension in the directory to guarantee that Code Generator will choose it. + +you can use variables in your heading files which are replaced at generation +time. possible variables are : author, date, time, filename and filepath. +just write %variable_name% + +This file was generated on %date% at %time% +The original location of this file is %filepath% +**************************************************************************/ diff --git a/umbrello/umbrello/headings/heading.h b/umbrello/umbrello/headings/heading.h new file mode 100644 index 00000000..d33911bf --- /dev/null +++ b/umbrello/umbrello/headings/heading.h @@ -0,0 +1,29 @@ +/************************************************************************ + %filename% - Copyright %author% + +Here you can write a license for your code, some comments or any other +information you want to have in your generated code. To to this simply +configure the "headings" directory in uml to point to a directory +where you have your heading files. + +or you can just replace the contents of this file with your own. +If you want to do this, this file is located at + +%headingpath% + +-->Code Generators searches for heading files based on the file extension + i.e. it will look for a file name ending in ".h" to include in C++ header + files, and for a file name ending in ".java" to include in all generated + java code. + If you name the file "heading.", Code Generator will always + choose this file even if there are other files with the same extension in the + directory. If you name the file something else, it must be the only one with that + extension in the directory to guarantee that Code Generator will choose it. + +you can use variables in your heading files which are replaced at generation +time. possible variables are : author, date, time, filename and filepath. +just write %variable_name% + +This file was generated on %date% at %time% +The original location of this file is %filepath% +**************************************************************************/ diff --git a/umbrello/umbrello/headings/heading.idl b/umbrello/umbrello/headings/heading.idl new file mode 100644 index 00000000..6e3f61cd --- /dev/null +++ b/umbrello/umbrello/headings/heading.idl @@ -0,0 +1,29 @@ +/************************************************************************ + %filename% - Copyright %author% + +Here you can write a license for your code, some comments or any other +information you want to have in your generated code. To to this simply +configure the "headings" directory in uml to point to a directory +where you have your heading files. + +or you can just replace the contents of this file with your own. +If you want to do this, this file is located at + +%headingpath% + +-->Code Generators searches for heading files based on the file extension + i.e. it will look for a file name ending in ".h" to include in C++ header + files, and for a file name ending in ".java" to include in all generated + java code. + If you name the file "heading.", Code Generator will always + choose this file even if there are other files with the same extension in the + directory. If you name the file something else, it must be the only one with that + extension in the directory to guarantee that Code Generator will choose it. + +you can use variables in your heading files which are replaced at generation +time. possible variables are : author, date, time, filename and filepath. +just write %variable_name% + +This file was generated on %date% at %time% +The original location of this file is %filepath% +**************************************************************************/ diff --git a/umbrello/umbrello/headings/heading.java b/umbrello/umbrello/headings/heading.java new file mode 100644 index 00000000..6e3f61cd --- /dev/null +++ b/umbrello/umbrello/headings/heading.java @@ -0,0 +1,29 @@ +/************************************************************************ + %filename% - Copyright %author% + +Here you can write a license for your code, some comments or any other +information you want to have in your generated code. To to this simply +configure the "headings" directory in uml to point to a directory +where you have your heading files. + +or you can just replace the contents of this file with your own. +If you want to do this, this file is located at + +%headingpath% + +-->Code Generators searches for heading files based on the file extension + i.e. it will look for a file name ending in ".h" to include in C++ header + files, and for a file name ending in ".java" to include in all generated + java code. + If you name the file "heading.", Code Generator will always + choose this file even if there are other files with the same extension in the + directory. If you name the file something else, it must be the only one with that + extension in the directory to guarantee that Code Generator will choose it. + +you can use variables in your heading files which are replaced at generation +time. possible variables are : author, date, time, filename and filepath. +just write %variable_name% + +This file was generated on %date% at %time% +The original location of this file is %filepath% +**************************************************************************/ diff --git a/umbrello/umbrello/headings/heading.js b/umbrello/umbrello/headings/heading.js new file mode 100644 index 00000000..6e3f61cd --- /dev/null +++ b/umbrello/umbrello/headings/heading.js @@ -0,0 +1,29 @@ +/************************************************************************ + %filename% - Copyright %author% + +Here you can write a license for your code, some comments or any other +information you want to have in your generated code. To to this simply +configure the "headings" directory in uml to point to a directory +where you have your heading files. + +or you can just replace the contents of this file with your own. +If you want to do this, this file is located at + +%headingpath% + +-->Code Generators searches for heading files based on the file extension + i.e. it will look for a file name ending in ".h" to include in C++ header + files, and for a file name ending in ".java" to include in all generated + java code. + If you name the file "heading.", Code Generator will always + choose this file even if there are other files with the same extension in the + directory. If you name the file something else, it must be the only one with that + extension in the directory to guarantee that Code Generator will choose it. + +you can use variables in your heading files which are replaced at generation +time. possible variables are : author, date, time, filename and filepath. +just write %variable_name% + +This file was generated on %date% at %time% +The original location of this file is %filepath% +**************************************************************************/ diff --git a/umbrello/umbrello/headings/heading.php b/umbrello/umbrello/headings/heading.php new file mode 100644 index 00000000..7ff129ac --- /dev/null +++ b/umbrello/umbrello/headings/heading.php @@ -0,0 +1,30 @@ +Code Generators searches for heading files based on the file extension + i.e. it will look for a file name ending in ".h" to include in C++ header + files, and for a file name ending in ".java" to include in all generated + java code. + If you name the file "heading.", Code Generator will always + choose this file even if there are other files with the same extension in the + directory. If you name the file something else, it must be the only one with that + extension in the directory to guarantee that Code Generator will choose it. + +you can use variables in your heading files which are replaced at generation +time. possible variables are : author, date, time, filename and filepath. +just write %variable_name% + +This file was generated on %date% at %time% +The original location of this file is %filepath% +**************************************************************************/ diff --git a/umbrello/umbrello/headings/heading.pm b/umbrello/umbrello/headings/heading.pm new file mode 100644 index 00000000..cc02c171 --- /dev/null +++ b/umbrello/umbrello/headings/heading.pm @@ -0,0 +1,29 @@ +#!/usr/bin/env perl +# %filename% - Copyright %author% +# +# Here you can write a license for your code, some comments or any other +# information you want to have in your generated code. To to this simply +# configure the "headings" directory in uml to point to a directory +# where you have your heading files. +# +# or you can just replace the contents of this file with your own. +# If you want to do this, this file is located at +# +# %headingpath% +# +# -->Code Generators searches for heading files based on the file extension +# i.e. it will look for a file name ending in ".h" to include in C++ header +# files, and for a file name ending in ".java" to include in all generated +# java code. +# If you name the file "heading.", Code Generator will always +# choose this file even if there are other files with the same extension in the +# directory. If you name the file something else, it must be the only one with that +# extension in the directory to guarantee that Code Generator will choose it. +# +# you can use variables in your heading files which are replaced at generation +# time. possible variables are : author, date, time, filename and filepath. +# just write %variable_name% +# +# This file was generated on %date% at %time% +# The original location of this file is %filepath% + diff --git a/umbrello/umbrello/headings/heading.py b/umbrello/umbrello/headings/heading.py new file mode 100644 index 00000000..61016aef --- /dev/null +++ b/umbrello/umbrello/headings/heading.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# %filename% - Copyright %author% +# +# Here you can write a license for your code, some comments or any other +# information you want to have in your generated code. To to this simply +# configure the "headings" directory in uml to point to a directory +# where you have your heading files. +# +# or you can just replace the contents of this file with your own. +# If you want to do this, this file is located at +# +# %headingpath% +# +# -->Code Generators searches for heading files based on the file extension +# i.e. it will look for a file name ending in ".h" to include in C++ header +# files, and for a file name ending in ".java" to include in all generated +# java code. +# If you name the file "heading.", Code Generator will always +# choose this file even if there are other files with the same extension in the +# directory. If you name the file something else, it must be the only one with that +# extension in the directory to guarantee that Code Generator will choose it. +# +# you can use variables in your heading files which are replaced at generation +# time. possible variables are : author, date, time, filename and filepath. +# just write %variable_name% +# +# This file was generated on %date% at %time% +# The original location of this file is %filepath% + diff --git a/umbrello/umbrello/headings/heading.rb b/umbrello/umbrello/headings/heading.rb new file mode 100644 index 00000000..b4bbc9b7 --- /dev/null +++ b/umbrello/umbrello/headings/heading.rb @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +# %filename% - Copyright %author% +# +# Here you can write a license for your code, some comments or any other +# information you want to have in your generated code. To to this simply +# configure the "headings" directory in uml to point to a directory +# where you have your heading files. +# +# or you can just replace the contents of this file with your own. +# If you want to do this, this file is located at +# +# %headingpath% +# +# -->Code Generators searches for heading files based on the file extension +# i.e. it will look for a file name ending in ".h" to include in C++ header +# files, and for a file name ending in ".java" to include in all generated +# java code. +# If you name the file "heading.", Code Generator will always +# choose this file even if there are other files with the same extension in the +# directory. If you name the file something else, it must be the only one with that +# extension in the directory to guarantee that Code Generator will choose it. +# +# you can use variables in your heading files which are replaced at generation +# time. possible variables are : author, date, time, filename and filepath. +# just write %variable_name% +# +# This file was generated on %date% at %time% +# The original location of this file is %filepath% + diff --git a/umbrello/umbrello/headings/heading.sql b/umbrello/umbrello/headings/heading.sql new file mode 100644 index 00000000..f4797f56 --- /dev/null +++ b/umbrello/umbrello/headings/heading.sql @@ -0,0 +1,28 @@ +-- %filename% - Copyright %author% +-- +-- Here you can write a license for your code, some comments or any other +-- information you want to have in your generated code. To to this simply +-- configure the "headings" directory in uml to point to a directory +-- where you have your heading files. +-- +-- or you can just replace the contents of this file with your own. +-- If you want to do this, this file is located at +-- +-- %headingpath% +-- +-- -->Code Generators searches for heading files based on the file extension +-- i.e. it will look for a file name ending in ".h" to include in C++ header +-- files, and for a file name ending in ".java" to include in all generated +-- java code. +-- If you name the file "heading.", Code Generator will always +-- choose this file even if there are other files with the same extension in the +-- directory. If you name the file something else, it must be the only one with that +-- extension in the directory to guarantee that Code Generator will choose it. +-- +-- you can use variables in your heading files which are replaced at generation +-- time. possible variables are : author, date, time, filename and filepath. +-- just write %variable_name% +-- +-- This file was generated on %date% at %time% +-- The original location of this file is %filepath% + diff --git a/umbrello/umbrello/headings/heading.xsd b/umbrello/umbrello/headings/heading.xsd new file mode 100644 index 00000000..cd50b9f5 --- /dev/null +++ b/umbrello/umbrello/headings/heading.xsd @@ -0,0 +1,4 @@ + diff --git a/umbrello/umbrello/hi128-app-umbrello.png b/umbrello/umbrello/hi128-app-umbrello.png new file mode 100644 index 00000000..ce14d9f7 Binary files /dev/null and b/umbrello/umbrello/hi128-app-umbrello.png differ diff --git a/umbrello/umbrello/hi16-app-umbrello.png b/umbrello/umbrello/hi16-app-umbrello.png new file mode 100644 index 00000000..6005f241 Binary files /dev/null and b/umbrello/umbrello/hi16-app-umbrello.png differ diff --git a/umbrello/umbrello/hi16-mime-umbrellofile.png b/umbrello/umbrello/hi16-mime-umbrellofile.png new file mode 100644 index 00000000..86b364ac Binary files /dev/null and b/umbrello/umbrello/hi16-mime-umbrellofile.png differ diff --git a/umbrello/umbrello/hi22-app-umbrello.png b/umbrello/umbrello/hi22-app-umbrello.png new file mode 100644 index 00000000..128317d4 Binary files /dev/null and b/umbrello/umbrello/hi22-app-umbrello.png differ diff --git a/umbrello/umbrello/hi32-app-umbrello.png b/umbrello/umbrello/hi32-app-umbrello.png new file mode 100644 index 00000000..255e592c Binary files /dev/null and b/umbrello/umbrello/hi32-app-umbrello.png differ diff --git a/umbrello/umbrello/hi32-mime-umbrellofile.png b/umbrello/umbrello/hi32-mime-umbrellofile.png new file mode 100644 index 00000000..2a298ffc Binary files /dev/null and b/umbrello/umbrello/hi32-mime-umbrellofile.png differ diff --git a/umbrello/umbrello/hi48-app-umbrello.png b/umbrello/umbrello/hi48-app-umbrello.png new file mode 100644 index 00000000..98777102 Binary files /dev/null and b/umbrello/umbrello/hi48-app-umbrello.png differ diff --git a/umbrello/umbrello/hi64-app-umbrello.png b/umbrello/umbrello/hi64-app-umbrello.png new file mode 100644 index 00000000..cfc7062a Binary files /dev/null and b/umbrello/umbrello/hi64-app-umbrello.png differ diff --git a/umbrello/umbrello/hierarchicalcodeblock.cpp b/umbrello/umbrello/hierarchicalcodeblock.cpp new file mode 100644 index 00000000..7c04a960 --- /dev/null +++ b/umbrello/umbrello/hierarchicalcodeblock.cpp @@ -0,0 +1,386 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +/* This code generated by: + * Author : thomas + * Date : Wed Jun 18 2003 + */ + +// own header +#include "hierarchicalcodeblock.h" + +// qt/kde includes +#include + +// local includes +#include "codedocument.h" +#include "classifiercodedocument.h" +#include "codeclassfield.h" +#include "codegenerationpolicy.h" +#include "codegenerators/codegenfactory.h" +#include "uml.h" + +// Constructors/Destructors +// + +HierarchicalCodeBlock::HierarchicalCodeBlock ( CodeDocument * doc , const QString &start, const QString &endString, const QString &comment ) + : CodeBlockWithComments (doc, start, comment), CodeGenObjectWithTextBlocks(doc) +{ + setEndText(endString); + initAttributes(); +} + +HierarchicalCodeBlock::~HierarchicalCodeBlock ( ) { } + +// +// Methods +// + +// Accessor methods +// + +/** + * Set the value of m_endText + * @param new_var the new value of m_endText + */ +void HierarchicalCodeBlock::setEndText ( const QString &new_var ) { + m_endText = new_var; +} + +/** + * Get the value of m_endText + * @return the value of m_endText + */ +QString HierarchicalCodeBlock::getEndText ( ) { + return m_endText; +} + +QString HierarchicalCodeBlock::getUniqueTag() +{ + return getUniqueTag("hblock_tag"); +} + +QString HierarchicalCodeBlock::getUniqueTag( const QString& prefix ) +{ + return getParentDocument()->getUniqueTag(prefix); +} + +// other methods + +CodeBlock * HierarchicalCodeBlock::newCodeBlock() { + return getParentDocument()->newCodeBlock(); +} + +CodeBlockWithComments * HierarchicalCodeBlock::newCodeBlockWithComments() { + return getParentDocument()->newCodeBlockWithComments(); +} + +HierarchicalCodeBlock * HierarchicalCodeBlock::newHierarchicalCodeBlock() { + HierarchicalCodeBlock *hb = new HierarchicalCodeBlock(getParentDocument()); + //hb->update(); + return hb; +} + +/** + * Add a CodeBlock object to the m_textblockVector List + */ +bool HierarchicalCodeBlock::addTextBlock(TextBlock* add_object ) +{ + + if(CodeGenObjectWithTextBlocks::addTextBlock(add_object)) + { + getParentDocument()->addChildTagToMap(add_object->getTag(), add_object); + return true; + } + return false; +} + +/** + * Insert a new text block before the existing text block. Returns + * false if it cannot insert the textblock. + */ +bool HierarchicalCodeBlock::insertTextBlock(TextBlock * newBlock, TextBlock * existingBlock, bool after) +{ + + if(!newBlock || !existingBlock) + return false; + + QString tag = existingBlock->getTag(); + // FIX: just do a quick check if the parent DOCUMENT has this. + // IF it does, then the lack of an index will force us into + // a search of any child hierarchical codeblocks we may have + // Its not efficient, but works. I don't think speed is a problem + // right now for the current implementation, but in the future + // when code import/roundtripping is done, it *may* be. -b.t. + if(!getParentDocument()->findTextBlockByTag(tag, true)) + return false; + + int index = m_textblockVector.findRef(existingBlock); + if(index < 0) + { + // may be hiding in child hierarchical codeblock + for(TextBlock * tb = m_textblockVector.first(); tb ; tb = m_textblockVector.next()) + { + HierarchicalCodeBlock * hb = dynamic_cast(tb); + if(hb && hb->insertTextBlock(newBlock, existingBlock, after)) + return true; // found, and inserted, otherwise keep going + } + kWarning()<<" Warning: couldnt insert text block (tag:"<getTag()<<"). Reference text block (tag:"<getTag()<<") not found."<getTag(); + + // assign a tag if one doesn't already exist + if(new_tag.isEmpty()) + { + new_tag = getUniqueTag(); + newBlock->setTag(new_tag); + } + + if(m_textBlockTagMap.contains(new_tag)) + return false; // return false, we already have some object with this tag in the list + else { + m_textBlockTagMap.insert(new_tag, newBlock); + getParentDocument()->addChildTagToMap(new_tag, newBlock); + } + + + if(after) + index++; + + m_textblockVector.insert(index,newBlock); + + return true; +} + +/** + * Remove a CodeBlock object from m_textblockVector List + */ +bool HierarchicalCodeBlock::removeTextBlock ( TextBlock * remove_object ) { + + // try to remove from the list in this object + if(!m_textblockVector.removeRef(remove_object)) + { + // may be hiding in child hierarchical codeblock + for(TextBlock * tb = m_textblockVector.first(); tb ; tb = m_textblockVector.next()) + { + HierarchicalCodeBlock * hb = dynamic_cast(tb); + if(hb && hb->removeTextBlock(remove_object)) + return true; // because we got in child hb; + } + return false; + } + + // IF we get here, the text block was in THIS object (and not a child).. + QString tag = remove_object->getTag(); + if(!(tag.isEmpty())) + { + m_textBlockTagMap.erase(tag); + getParentDocument()->removeChildTagFromMap(tag); + } + return true; + +} + +/** + * @param text + */ +void HierarchicalCodeBlock::setStartText ( const QString &text ) { + m_startText = text; +} + +/** + * @return QString + */ +QString HierarchicalCodeBlock::getStartText ( ) { + return m_startText; +} + +// Other methods +// + +void HierarchicalCodeBlock::addCodeClassFieldMethods(CodeClassFieldList &list ) +{ + + for (CodeClassFieldListIt ccflit(list); ccflit.current(); ++ccflit) + { + CodeClassField * field = ccflit.current(); + CodeAccessorMethodList list = field->getMethodList(); + CodeAccessorMethod * method; + for (CodeAccessorMethodListIt it(list); (method = it.current()) != NULL; ++it) + { + QString tag = method->getTag(); + if(tag.isEmpty()) + { + tag = getUniqueTag(); + method->setTag(tag); + } + + addTextBlock(method); // wont add if already exists in object; + + } + + } + +} + +/** + * Save the XMI representation of this object + */ +void HierarchicalCodeBlock::saveToXMI ( QDomDocument & doc, QDomElement & root ) { + QDomElement blockElement = doc.createElement( "hierarchicalcodeblock" ); + + setAttributesOnNode(doc, blockElement); + + root.appendChild( blockElement ); +} + +void HierarchicalCodeBlock::setAttributesOnNode (QDomDocument & doc, QDomElement & elem ) { + + // set super-class attributes + CodeBlockWithComments::setAttributesOnNode(doc, elem); + CodeGenObjectWithTextBlocks::setAttributesOnNode(doc, elem); + + // set local class attributes + if(getContentType() != CodeBlock::AutoGenerated) + { + QString endLine = UMLApp::app()->getCommonPolicy()->getNewLineEndingChars(); + elem.setAttribute("startText",encodeText(getStartText(),endLine)); + elem.setAttribute("endText",encodeText(getEndText(),endLine)); + } +} + +/** + * load params from the appropriate XMI element node. + */ +void HierarchicalCodeBlock::loadFromXMI ( QDomElement & root ) { + setAttributesFromNode(root); +} + + +/** set the class attributes of this object from + * the passed element node. + */ +void HierarchicalCodeBlock::setAttributesFromNode ( QDomElement & root) +{ + + // set attributes from the XMI + CodeBlockWithComments::setAttributesFromNode(root); // superclass load + + if(getContentType() != CodeBlock::AutoGenerated) + { + QString endLine = UMLApp::app()->getCommonPolicy()->getNewLineEndingChars(); + setStartText(decodeText(root.attribute("startText",""),endLine)); + setEndText(decodeText(root.attribute("endText",""),endLine)); + } + + // do this *after* all other attributes saved + CodeGenObjectWithTextBlocks::setAttributesFromNode(root); + +} + +/** set the class attributes from a passed object + */ +void HierarchicalCodeBlock::setAttributesFromObject (TextBlock * obj) { + + CodeBlockWithComments::setAttributesFromObject(obj); + + HierarchicalCodeBlock * hb = dynamic_cast(obj); + if(hb) + { + setStartText(hb->getStartText()); + setEndText(hb->getEndText()); + CodeGenObjectWithTextBlocks *cgowtb = dynamic_cast(obj); + CodeGenObjectWithTextBlocks::setAttributesFromObject(cgowtb); + } + +} + + +/** + * @return QString + */ +QString HierarchicalCodeBlock::toString ( ) { + + QString string = QString(); + + if(getWriteOutText()) { + QString indent = getIndentationString(); + QString endLine = UMLApp::app()->getCommonPolicy()->getNewLineEndingChars(); + QString startText = ""; + QString endText = ""; + if (!getStartText().isEmpty()) + startText = formatMultiLineText (getStartText(), indent, endLine); + if (!getEndText().isEmpty()) + endText = formatMultiLineText (getEndText(), indent, endLine); + + QString body = childTextBlocksToString(); + QString comment = getComment()->toString(); + + // tack in text, if there is something there.. + if(!comment.isEmpty() && getComment()->getWriteOutText()) + string.append(comment); + + if (!startText.isEmpty()) + string.append(startText); + + if (!body.isEmpty()) + string.append(body); + + if (!endText.isEmpty()) + string.append(endText); + } + + return string; +} + +QString HierarchicalCodeBlock::childTextBlocksToString() { + TextBlockList* list = getTextBlockList(); + QString retString = ""; + for(TextBlock *block = list->first() ; block; block=list->next()) + { + QString blockValue = block->toString(); + if(!blockValue.isEmpty()) + retString.append(blockValue); + } + return retString; +} + +TextBlock * HierarchicalCodeBlock::findCodeClassFieldTextBlockByTag ( const QString &tag ) +{ + + ClassifierCodeDocument * cdoc = dynamic_cast(getParentDocument()); + if(cdoc) + return cdoc->findCodeClassFieldTextBlockByTag(tag); + else + kError()<<" HierarchicalCodeBlock: findCodeClassFieldTextBlockByTag() finds NO parent document! Badly constructed textblock?!?"< * + ***************************************************************************/ + +/* This code generated by: + * Author : thomas + * Date : Wed Jun 18 2003 + */ + + +#ifndef HIERARCHICALCODEBLOCK_H +#define HIERARCHICALCODEBLOCK_H + +#include +#include + +#include "codegenobjectwithtextblocks.h" +#include "codeblockwithcomments.h" +#include "codeclassfieldlist.h" + +class HierarchicalCodeBlock : public CodeBlockWithComments, public CodeGenObjectWithTextBlocks +{ + Q_OBJECT + friend class CodeGenObjectWithTextBlocks; +public: + + // Constructors/Destructors + // + + /** + * Constructor + */ + explicit HierarchicalCodeBlock ( CodeDocument * doc , const QString &startString = "", const QString &endString = "", const QString &comment = ""); + + /** + * Empty Destructor + */ + virtual ~HierarchicalCodeBlock ( ); + + // Public attributes + // + + // Public attribute accessor methods + // + + /** + * Set the value of m_endText + * @param new_var the new value of m_endText + */ + void setEndText ( const QString &new_var ); + + /** + * Get the value of m_endText + * @return the value of m_endText + */ + QString getEndText ( ); + + /** + * Add a TextBlock object to the m_textblockVector List + */ + bool addTextBlock ( TextBlock * add_object ); + + /** + * Insert a new text block before/after the existing text block. Returns + * false if it cannot insert the textblock. + */ + bool insertTextBlock (TextBlock * newBlock, TextBlock * existingBlock, bool after = true); + + /** + * Remove a TextBlock object from m_textblockVector List + * returns boolean - true if successful + */ + bool removeTextBlock ( TextBlock * remove_object ); + + /** + * @param text + */ + void setStartText ( const QString &text ); + + /** + * @return QString + */ + QString getStartText ( ); + + /** + * Save the XMI representation of this object + */ + virtual void saveToXMI ( QDomDocument & doc, QDomElement & root ); + + /** + * load params from the appropriate XMI element node. + */ + virtual void loadFromXMI ( QDomElement & root ); + + /** + * @return QString + */ + virtual QString toString ( ); + + // return a unique, and currently unallocated, text block tag for this hblock + QString getUniqueTag(); + QString getUniqueTag( const QString& prefix ); + + /** + * Utility method to add accessormethods in this object + */ + void addCodeClassFieldMethods ( CodeClassFieldList &list ); + + virtual CodeBlock * newCodeBlock(); + virtual CodeBlockWithComments * newCodeBlockWithComments(); + virtual HierarchicalCodeBlock * newHierarchicalCodeBlock(); + +protected: + + /** causes the text block to release all of its connections + * and any other text blocks that it 'owns'. + * needed to be called prior to deletion of the textblock. + */ + virtual void release (); + + /** set attributes of the node that represents this class + * in the XMI document. + */ + virtual void setAttributesOnNode (QDomDocument & doc, QDomElement & elem ); + + /** set the class attributes of this object from + * the passed element node. + */ + virtual void setAttributesFromNode ( QDomElement & element); + + /** set the class attributes from a passed object + */ + virtual void setAttributesFromObject (TextBlock * obj); + + // look for specific text blocks which belong to code classfields + TextBlock * findCodeClassFieldTextBlockByTag ( const QString &tag ); + +private: + + QString m_startText; + QString m_endText; + + QString childTextBlocksToString(); + void initAttributes ( ) ; + +}; + +#endif // HIERARCHICALCODEBLOCK_H diff --git a/umbrello/umbrello/hisc-app-umbrello.svgz b/umbrello/umbrello/hisc-app-umbrello.svgz new file mode 100644 index 00000000..27b89716 Binary files /dev/null and b/umbrello/umbrello/hisc-app-umbrello.svgz differ diff --git a/umbrello/umbrello/import_rose.cpp b/umbrello/umbrello/import_rose.cpp new file mode 100644 index 00000000..aeaeb7fb --- /dev/null +++ b/umbrello/umbrello/import_rose.cpp @@ -0,0 +1,391 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "import_rose.h" + +// qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +// app includes +#include "petalnode.h" +#include "petaltree2uml.h" +#include "umlnamespace.h" // only for the KDE4 compatibility macros + +namespace Import_Rose { + +typedef QPtrList PetalNodeList; + +uint nClosures; // Multiple closing parentheses may appear on a single + // line. The parsing is done line-by-line and using + // recursive descent. This means that we can only handle + // _one_ closing parenthesis at a time, i.e. the closing + // of the currently parsed node. Since we may see more + // closing parentheses than we can handle, we need a + // counter indicating how many additional node closings + // have been seen. + +uint linum; // line number +QString g_methodName; +void methodName(const QString& m) { + g_methodName = m; +} +/** + * Auxiliary function for diagnostics: Return current location. + */ +QString loc() { + return "Import_Rose::" + g_methodName + " line " + QString::number(linum) + ": "; +} + +/** + * Split a line into lexemes. + */ +QStringList scan(const QString& lin) { + QStringList result; + QString line = lin.stripWhiteSpace(); + if (line.isEmpty()) + return result; // empty + QString lexeme; + const uint len = line.length(); + bool inString = false; + for (uint i = 0; i < len; i++) { + QChar c = line[i]; + if (c == '"') { + lexeme += c; + if (inString) { + result.append(lexeme); + lexeme = QString(); + } + inString = !inString; + } else if (inString || + c.isLetterOrNumber() || c == '_' || c == '@') { + lexeme += c; + } else { + if (!lexeme.isEmpty()) { + result.append(lexeme); + lexeme = QString(); + } + if (! c.isSpace()) { + result.append(QString(c)); + } + } + } + if (!lexeme.isEmpty()) + result.append(lexeme); + return result; +} + +/** + * Emulate perl shift(). + */ +QString shift(QStringList& l) { + QString first = l.first(); + l.pop_front(); + return first; +} + +/** + * Check for closing of one or more scopes. + */ +bool checkClosing(QStringList& tokens) { + if (tokens.count() == 0) + return false; + if (tokens.last() == ")") { + // For a single closing parenthesis, we just return true. + // But if there are more closing parentheses, we need to increment + // nClosures for each scope. + tokens.pop_back(); + while (tokens.count() && tokens.last() == ")") { + nClosures++; + tokens.pop_back(); + } + return true; + } + return false; +} + +/** + * Immediate values are numbers or quoted strings. + * @return True if the given text is a natural or negative number + * or a quoted string. + */ +bool isImmediateValue(QString s) { + return s.contains(QRegExp("^[\\d\\-\"]")); +} + +/** + * Extract immediate values out of `l'. + * Examples of immediate value lists: + * number list: ( 123 , 456 ) + * string list: ( "SomeText" 888 ) + * Any enclosing parentheses are removed. + * All extracted items are also removed from `l'. + * For the example given above the following is returned: + * "123 456" + * or + * "\"SomeText\" 888" + */ +QString extractImmediateValues(QStringList& l) { + if (l.count() == 0) + return QString(); + if (l.first() == "(") + l.pop_front(); + QString result; + bool start = true; + while (l.count() && isImmediateValue(l.first())) { + if (start) + start = false; + else + result += ' '; + result += shift(l); + if (l.first() == ",") + l.pop_front(); + } + if (l.first() == ")") + l.pop_front(); + while (l.count() && l.first() == ")") { + nClosures++; + l.pop_front(); + } + return result; +} + +QString collectVerbatimText(QTextStream& stream) { + QString result; + QString line; + methodName("collectVerbatimText"); + while (!(line = stream.readLine()).isNull()) { + linum++; + line = line.stripWhiteSpace(); + if (line.isEmpty() || line.startsWith(")")) + break; + if (line[0] != '|') { + kError() << loc() << "expecting '|' at start of verbatim text" << endl; + return QString(); + } else { + result += line.mid(1) + '\n'; + } + } + if (line.isNull()) { + kError() << loc() << "premature EOF" << endl; + return QString(); + } + if (! line.isEmpty()) { + for (uint i = 0; i < line.length(); i++) { + const QChar& clParenth = line[i]; + if (clParenth != ')') { + kError() << loc() << "expected ')', found: " << clParenth << endl; + return QString(); + } + nClosures++; + } + } + return result; +} + +/** + * Extract the stripped down value from a (value ...) element. + * Example: for the input + * (value Text "This is some text") + * the following is extracted: + * "This is some text" + * Extracted elements and syntactic sugar of the value element are + * removed from the input list. + * The stream is passed into this method because it may be necessary + * to read new lines - in the case of verbatim text. + * The format of verbatim text in petal files is as follows: + * + * (value Text + * |This is the first line of verbatim text. + * |This is another line of verbatim text. + * ) + * (The '|' character is supposed to be in the first column of the line) + * In this case the two lines are extracted without the leading '|'. + * The line ending '\n' of each line is preserved. + */ +QString extractValue(QStringList& l, QTextStream& stream) { + methodName("extractValue"); + if (l.count() == 0) + return QString(); + if (l.first() == "(") + l.pop_front(); + if (l.first() != "value") + return QString(); + l.pop_front(); // remove "value" + l.pop_front(); // remove the value type: could be e.g. "Text" or "cardinality" + QString result; + if (l.count() == 0) { // expect verbatim text to follow on subsequent lines + QString text = collectVerbatimText(stream); + nClosures--; // expect own closure + return text; + } else { + result = shift(l); + if (l.first() != ")") { + kError() << loc() << "expecting closing parenthesis" << endl; + return result; + } + l.pop_front(); + } + while (l.count() && l.first() == ")") { + nClosures++; + l.pop_front(); + } + return result; +} + +/** + * Read attributes of a node. + * @param initialArgs Tokens on the line of the opening "(" of the node + * but with leading whitespace and the opening "(" removed. + * @param stream The QTextStream from which to read following lines. + * @return Pointer to the created PetalNode or NULL on error. + */ +PetalNode *readAttributes(QStringList initialArgs, QTextStream& stream) { + methodName("readAttributes"); + if (initialArgs.count() == 0) { + kError() << loc() << "initialArgs is empty" << endl; + return NULL; + } + PetalNode::NodeType nt; + QString type = shift(initialArgs); + if (type == "object") + nt = PetalNode::nt_object; + else if (type == "list") + nt = PetalNode::nt_list; + else { + kError() << loc() << "unknown node type " << type << endl; + return NULL; + } + PetalNode *node = new PetalNode(nt); + bool seenClosing = checkClosing(initialArgs); + node->setInitialArgs(initialArgs); + if (seenClosing) + return node; + PetalNode::NameValueList attrs; + QString line; + while (!(line = stream.readLine()).isNull()) { + linum++; + line = line.stripWhiteSpace(); + if (line.isEmpty()) + continue; + QStringList tokens = scan(line); + QString stringOrNodeOpener = shift(tokens); + QString name; + if (nt == PetalNode::nt_object && !stringOrNodeOpener.contains(QRegExp("^[A-Za-z]"))) { + kError() << loc() << "unexpected line " << line << endl; + return NULL; + } + PetalNode::StringOrNode value; + if (nt == PetalNode::nt_object) { + name = stringOrNodeOpener; + if (tokens.count() == 0) { // expect verbatim text to follow on subsequent lines + value.string = collectVerbatimText(stream); + PetalNode::NameValue attr(name, value); + attrs.append(attr); + if (nClosures) { + // Decrement nClosures exactly once, namely for the own scope. + // Each recursion of readAttributes() is only responsible for + // its own scope. I.e. each further scope closing is handled by + // an outer recursion in case of multiple closing parentheses. + nClosures--; + break; + } + continue; + } + stringOrNodeOpener = shift(tokens); + } else if (stringOrNodeOpener != "(") { + value.string = stringOrNodeOpener; + PetalNode::NameValue attr; + attr.second = value; + attrs.append(attr); + if (tokens.count() && tokens.first() != ")") { + kDebug() << loc() + << "NYI - immediate list entry with more than one item" << endl; + } + if (checkClosing(tokens)) + break; + continue; + } + if (stringOrNodeOpener == "(") { + QString nxt = tokens.first(); + if (isImmediateValue(nxt)) { + value.string = extractImmediateValues(tokens); + } else if (nxt == "value" || nxt.startsWith("\"")) { + value.string = extractValue(tokens, stream); + } else { + kapp->processEvents(); + value.node = readAttributes(tokens, stream); + if (value.node == NULL) + return NULL; + } + PetalNode::NameValue attr(name, value); + attrs.append(attr); + if (nClosures) { + // Decrement nClosures exactly once, namely for the own scope. + // Each recursion of readAttributes() is only responsible for + // its own scope. I.e. each further scope closing is handled by + // an outer recursion in case of multiple closing parentheses. + nClosures--; + break; + } + } else { + value.string = stringOrNodeOpener; + bool seenClosing = checkClosing(tokens); + PetalNode::NameValue attr(name, value); + attrs.append(attr); + if (seenClosing) { + break; + } + } + } + node->setAttributes(attrs); + return node; +} + +bool loadFromMDL(QIODevice& file) { + QTextStream stream(&file); + stream.setEncoding(QTextStream::Latin1); + QString line; + PetalNode *root = NULL; + linum = 0; + while (!(line = stream.readLine()).isNull()) { + linum++; + if (line.contains( QRegExp("^\\s*\\(object Petal") )) { + while (!(line = stream.readLine()).isNull() && !line.contains(')')) { + linum++; // CHECK: do we need petal version info? + } + if (line.isNull()) + break; + } else { + QRegExp objectRx("^\\s*\\(object "); + if (line.contains(objectRx)) { + nClosures = 0; + QStringList initialArgs = scan(line); + initialArgs.pop_front(); // remove opening parenthesis + root = readAttributes(initialArgs, stream); + } + } + } + file.close(); + if (root == NULL) + return false; + return petalTree2Uml(root); +} + +} + diff --git a/umbrello/umbrello/import_rose.h b/umbrello/umbrello/import_rose.h new file mode 100644 index 00000000..b5090db3 --- /dev/null +++ b/umbrello/umbrello/import_rose.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef IMPORT_ROSE__H +#define IMPORT_ROSE__H + +#include + +/** + * Rose model import + * + * @author Oliver Kellogg + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +namespace Import_Rose { + + /** + * Parse a file into the PetalNode internal tree representation + * and then create Umbrello objects by traversing the tree. + * + * @return True for success, false in case of error. + */ + bool loadFromMDL(QIODevice & file); + +} + +#endif diff --git a/umbrello/umbrello/kplayerslideraction.cpp b/umbrello/umbrello/kplayerslideraction.cpp new file mode 100644 index 00000000..ae5c3f97 --- /dev/null +++ b/umbrello/umbrello/kplayerslideraction.cpp @@ -0,0 +1,412 @@ +/*************************************************************************** + begin : Sat Jan 11 2003 + copyright : (C) 2003 by kiriuja + email : kplayer-dev@en-directo.net + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +/* Taken from kplayer CVS 2003-09-21 (kplayer > 0.3.1) by Jonathan Riddell + * Changes from kplayer original marked by CHANGED + */ + +#include +#include +#include +#include +#include + +#include + +//CHANGED #include "kplayersettings.h" +#include "kplayerslideraction.h" +#include "kplayerslideraction.moc" + +void KPlayerPopupFrame::keyPressEvent (QKeyEvent* ev) +{ + switch ( ev -> key() ) + { + case Qt::Key_Alt: + case Qt::Key_Tab: + case Qt::Key_Escape: + case Qt::Key_Return: + case Qt::Key_Enter: + close(); + } +} + +/*void KPlayerPopupFrame::closeEvent (QCloseEvent* ev) +{ + QFrame::closeEvent (ev); +} + +void KPlayerPopupFrame::mousePressEvent (QMouseEvent* ev) +{ + QFrame::mousePressEvent (ev); +//if ( ! rect().contains (ev -> pos()) ) +// m_outside_mouse_press = true; +} + +void KPlayerPopupFrame::mouseReleaseEvent (QMouseEvent* ev) +{ + QFrame::mouseReleaseEvent (ev); + if ( m_outside_mouse_press ) + { + m_outside_mouse_press = false; + if ( ! rect().contains (ev -> pos()) ) + close(); + } +}*/ + +KPlayerPopupSliderAction::KPlayerPopupSliderAction (const QString& text, + const QString& pix, const KShortcut& shortcut, const QObject* receiver, + const char* slot, QObject* parent, const char* name) + : KAction (text, pix, shortcut, parent, name) +{ + m_frame = new KPlayerPopupFrame; + m_frame -> setFrameStyle (QFrame::PopupPanel | QFrame::Raised); + m_frame -> setLineWidth (2); + m_slider = new KPlayerSlider (Qt::Vertical, m_frame); + m_frame -> resize (36, m_slider -> sizeHint().height() + 4); + m_slider -> setGeometry (m_frame -> contentsRect()); + //CHANGED kdDebug() << "Popup slider size " << m_slider -> width() << "x" << m_slider -> height() << "\n"; + connect (m_slider, SIGNAL (changed (int)), receiver, slot); +} + +KPlayerPopupSliderAction::~KPlayerPopupSliderAction() +{ + delete m_frame; + m_frame = 0; +} + +/*int KPlayerPopupSliderAction::plug (QWidget* widget, int index) +{ + Q_ASSERT (m_slider); + Q_ASSERT (widget); + Q_ASSERT (! isPlugged()); + if ( ! m_slider || ! widget || isPlugged() ) + return -1; + Q_ASSERT (widget -> inherits ("KToolBar")); + if ( ! widget -> inherits ("KToolBar") ) + return -1; + int retval = KAction::plug (widget, index); +// if ( retval >= 0 ) +// m_slider -> reparent (widget, QPoint()); + return retval; +} + +void KPlayerPopupSliderAction::unplug (QWidget* widget) +{ + Q_ASSERT (m_slider); + Q_ASSERT (widget); + Q_ASSERT (isPlugged()); + Q_ASSERT (widget -> inherits ("KToolBar")); + if ( ! m_slider || ! widget || ! isPlugged() || ! widget -> inherits ("KToolBar") ) + return; +//m_slider -> reparent (0, QPoint()); + KAction::unplug (widget); +}*/ + +void KPlayerPopupSliderAction::slotActivated (void) +{ + KAction::slotActivated(); + QWidget* button = 0; + if ( sender() ) + { + //CHANGED kdDebug() << "Sender class name: " << sender() -> className() << "\n"; + if ( sender() -> inherits ("KToolBarButton") ) + button = (QWidget*) sender(); + else if ( sender() -> inherits ("KToolBar") ) + { + KToolBar* toolbar = (KToolBar*) sender(); + int index = findContainer (toolbar); + if ( index >= 0 ) + button = toolbar -> getButton (itemId (index)); + } + } + QPoint point; + if ( button ) + point = button -> mapToGlobal (QPoint (0, button -> height())); + else + { + point = QCursor::pos() - QPoint (m_frame -> width() / 2, m_frame -> height() / 2); + if ( point.x() + m_frame -> width() > QApplication::desktop() -> width() ) + point.setX (QApplication::desktop() -> width() - m_frame -> width()); + if ( point.y() + m_frame -> height() > QApplication::desktop() -> height() ) + point.setY (QApplication::desktop() -> height() - m_frame -> height()); + if ( point.x() < 0 ) + point.setX (0); + if ( point.y() < 0 ) + point.setY (0); + } + //CHANGED kdDebug() << "Point: " << point.x() << "x" << point.y() << "\n"; + m_frame -> move (point); + /*if ( kapp && kapp -> activeWindow() ) + { + QMouseEvent me (QEvent::MouseButtonRelease, QPoint(0,0), QPoint(0,0), QMouseEvent::LeftButton, QMouseEvent::NoButton); + QApplication::sendEvent (kapp -> activeWindow(), &me); + }*/ + m_frame -> show(); + m_slider -> setFocus(); +} + +KPlayerSliderAction::KPlayerSliderAction (const QString& text, const KShortcut& cut, + const QObject* receiver, const char* slot, KActionCollection* parent, const char* name) + : KWidgetAction (new KPlayerSlider (Qt::Horizontal, 0, name), text, cut, receiver, slot, parent, name) + //: KAction (text, 0, parent, name) +{ + setAutoSized (true); + connect (slider(), SIGNAL (changed (int)), receiver, slot); +} + +KPlayerSliderAction::~KPlayerSliderAction() +{ +} + +int KPlayerSliderAction::plug (QWidget* widget, int index) +{ + //Q_ASSERT (widget); + //Q_ASSERT (! isPlugged()); + //Q_ASSERT (slider()); + //if ( ! slider() || ! widget || isPlugged() ) + // return -1; + //Q_ASSERT (widget -> inherits ("KToolBar")); + //if ( ! widget -> inherits ("KToolBar") ) + // return -1; + //if ( kapp && ! kapp -> authorizeKAction (name()) ) + // return -1; + int result = KWidgetAction::plug (widget, index); + if ( result < 0 ) + return result; + KToolBar* toolbar = (KToolBar*) widget; + //int id = getToolButtonID(); + //kdDebug() << "Orientation: " << toolbar -> orientation() << "\n"; + //m_slider -> reparent (toolbar, QPoint()); + //toolbar -> insertWidget (id, 0, m_slider, index); + //toolbar -> setItemAutoSized (id, true); + //QWhatsThis::remove (m_slider); + //if ( ! whatsThis().isEmpty() ) + // QWhatsThis::add (m_slider, whatsThis()); + //if ( ! text().isEmpty() ) + // QToolTip::add (m_slider, text()); + //addContainer (toolbar, id); + //setupToolbar (toolbar -> orientation(), toolbar); + orientationChanged (toolbar -> orientation()); + connect (toolbar, SIGNAL (orientationChanged (Orientation)), this, SLOT (orientationChanged (Orientation))); + //connect (toolbar, SIGNAL (destroyed()), this, SLOT (toolbarDestroyed())); + //if ( parentCollection() ) + // parentCollection() -> connectHighlight (toolbar, this); + //return containerCount() - 1; + return result; +} + +void KPlayerSliderAction::unplug (QWidget* widget) +{ + //Q_ASSERT (m_slider); + //Q_ASSERT (isPlugged()); + //Q_ASSERT (widget -> inherits ("KToolBar")); + KWidgetAction::unplug (widget); + if ( ! slider() || ! isPlugged() || widget != slider() -> parent() ) + return; + //KToolBar* toolbar = (KToolBar*) widget; + disconnect (widget, SIGNAL (orientationChanged (Orientation)), this, SLOT (orientationChanged (Orientation))); + //disconnect (toolbar, SIGNAL (destroyed()), this, SLOT (toolbarDestroyed())); + //m_slider -> reparent (0, QPoint()); + /*int index = findContainer (toolbar); + if ( index == -1 ) + return; + bar -> removeItem (menuId (index)); + removeContainer (index);*/ +} + +/*void KPlayerSliderAction::setupToolbar (Orientation orientation, KToolBar* toolbar) +{ + if ( orientation == Qt::Horizontal ) + { +// toolbar -> setMinimumWidth (300); +// toolbar -> setMinimumHeight (0); + toolbar -> setFixedExtentWidth (300); + toolbar -> setFixedExtentHeight (-1); +// toolbar -> setHorizontallyStretchable (true); +// toolbar -> setVerticallyStretchable (false); + } + else + { +// toolbar -> setMinimumWidth (0); +// toolbar -> setMinimumHeight (300); + toolbar -> setFixedExtentWidth (-1); + toolbar -> setFixedExtentHeight (300); +// toolbar -> setHorizontallyStretchable (false); +// toolbar -> setVerticallyStretchable (true); + } +}*/ + +void KPlayerSliderAction::orientationChanged (Qt::Orientation orientation) +{ + //if ( sender() && sender() -> inherits ("KToolBar") ) + // setupToolbar (orientation, (KToolBar*) sender()); + //Q_ASSERT (m_slider); + //Q_ASSERT (isPlugged()); + if ( slider() ) + slider() -> setOrientation (orientation); +} + +/*void KPlayerSliderAction::toolbarDestroyed (void) +{ + if ( m_slider ) + m_slider -> reparent (0, QPoint()); +}*/ + +KPlayerSlider::KPlayerSlider (Qt::Orientation orientation, QWidget* parent, const char* name) +//CHANGED : QSlider (orientation, parent, name) + : QSlider (300, 2200, 400, 1000, orientation, parent, name) +{ + m_changing_orientation = false; + setTickmarks (QSlider::Both); + connect (this, SIGNAL (valueChanged (int)), this, SLOT (sliderValueChanged (int))); +} + +KPlayerSlider::~KPlayerSlider() +{ + //CHANGED kdDebug() << "KPlayerSlider destroyed\n"; +} + +QSize KPlayerSlider::sizeHint() const +{ + QSize hint = QSlider::sizeHint(); + //CHANGED int length = kPlayerSettings() -> preferredSliderLength(); + int length = 200; + if ( orientation() == Qt::Horizontal ) + { + if ( hint.width() < length ) + hint.setWidth (length); + } + else + { + if ( hint.height() < length ) + hint.setHeight (length); + } + return hint; +} + +QSize KPlayerSlider::minimumSizeHint() const +{ + //kdDebug() << "KPlayerSlider minimum size hint\n"; + QSize hint = QSlider::minimumSizeHint(); + //CHANGED int length = kPlayerSettings() -> minimumSliderLength(); + int length = 200; + if ( orientation() == Qt::Horizontal ) + { + if ( hint.width() < length ) + hint.setWidth (length); + } + else + { + if ( hint.height() < length ) + hint.setHeight (length); + } + return hint; +} + +void KPlayerSlider::setOrientation (Qt::Orientation o) +{ + if ( o == orientation() ) + return; + m_changing_orientation = true; + int minValue = QSlider::minValue(); + int maxValue = QSlider::maxValue(); + int value = QSlider::value(); + QSlider::setOrientation (o); + QSlider::setMinValue (- maxValue); + QSlider::setMaxValue (- minValue); + QSlider::setValue (- value); + m_changing_orientation = false; +} + +int KPlayerSlider::minValue (void) const +{ + if ( orientation() == Qt::Horizontal ) + return QSlider::minValue(); + return - QSlider::maxValue(); +} + +void KPlayerSlider::setMinValue (int minValue) +{ + if ( orientation() == Qt::Horizontal ) + QSlider::setMinValue (minValue); + else + QSlider::setMaxValue (- minValue); +} + +int KPlayerSlider::maxValue (void) const +{ + if ( orientation() == Qt::Horizontal ) + return QSlider::maxValue(); + return - QSlider::minValue(); +} + +void KPlayerSlider::setMaxValue (int maxValue) +{ + if ( orientation() == Qt::Horizontal ) + QSlider::setMaxValue (maxValue); + else + QSlider::setMinValue (- maxValue); +} + +int KPlayerSlider::lineStep (void) const +{ + return QSlider::lineStep(); +} + +void KPlayerSlider::setLineStep (int lineStep) +{ + QSlider::setLineStep (lineStep); +} + +int KPlayerSlider::pageStep (void) const +{ + return QSlider::pageStep(); +} + +void KPlayerSlider::setPageStep (int pageStep) +{ + QSlider::setPageStep (pageStep); + setTickInterval (pageStep); +} + +int KPlayerSlider::value (void) const +{ + if ( orientation() == Qt::Horizontal ) + return QSlider::value(); + return - QSlider::value(); +} + +void KPlayerSlider::setValue (int value, int) +{ + if ( orientation() == Qt::Horizontal ) + QSlider::setValue (value); + else + QSlider::setValue (- value); +} + +void KPlayerSlider::setup (int minValue, int maxValue, int value, int pageStep, int lineStep) +{ + setMinValue (minValue); + setMaxValue (maxValue); + setLineStep (lineStep); + setPageStep (pageStep); + setValue (value); +} + +void KPlayerSlider::sliderValueChanged (int) +{ + if ( ! m_changing_orientation ) + emit changed (value()); +} diff --git a/umbrello/umbrello/kplayerslideraction.h b/umbrello/umbrello/kplayerslideraction.h new file mode 100644 index 00000000..e8bb6ff9 --- /dev/null +++ b/umbrello/umbrello/kplayerslideraction.h @@ -0,0 +1,202 @@ +/*************************************************************************** + kplayerslideraction.h + --------------------- + begin : Sat Jan 11 2003 + copyright : (C) 2003 by kiriuja + email : kplayer-dev@en-directo.net + ***************************************************************************/ + +/*************************************************************************** + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifndef KPLAYERSLIDERACTION_H +#define KPLAYERSLIDERACTION_H + +#include +#include + +/**KPlayer's slider widget. Works around the Qt upside-down slider bug. + * Taken from kplayer CVS 2003-09-21 (kplayer > 0.3.1) by Jonathan Riddell + *@author kiriuja + */ +class KPlayerSlider : public QSlider +{ + Q_OBJECT + +public: + /** The KPlayerSlider constructor. Parameters are passed on to QSlider. + */ + explicit KPlayerSlider (Qt::Orientation, QWidget* parent = 0, const char* name = 0); + /** The KPlayerSlider destructor. Does nothing. + */ + virtual ~KPlayerSlider(); + + /** The size hint. + */ + virtual QSize sizeHint() const; + /** The minimum size hint. + */ + virtual QSize minimumSizeHint() const; + + /** The minimum value. + */ + int minValue (void) const; + /** Sets the minimum value. + */ + void setMinValue (int); + /** The maximum value. + */ + int maxValue (void) const; + /** Sets the maximum value. + */ + void setMaxValue (int); + /** The line step. + */ + int lineStep (void) const; + /** Sets the line step. + */ + void setLineStep (int); + /** The page step. + */ + int pageStep (void) const; + /** Sets the page step. + */ + void setPageStep (int); + /** The current value. + */ + int value (void) const; + /** Sets the current value. The extra parameter prevents overriding of the virtual QSlider::setValue. + */ + void setValue (int, int = 0); // do not override the virtual setValue + + /** Sets up the slider by setting five options in one go. + */ + void setup (int minValue, int maxValue, int value, int pageStep, int lineStep = 1); + /** Sets the slider orientation. + */ + virtual void setOrientation (Qt::Orientation); + +signals: + /** Emitted when the slider value changes. + */ + void changed (int); + +protected slots: + /** Receives the valueChanged signal from QSlider. + */ + void sliderValueChanged (int); + +protected: + // Recursion prevention. Should be private. + bool m_changing_orientation; + + friend class KPlayerSliderAction; + friend class KPlayerPopupSliderAction; +}; + +/**KPlayer popup frame. + *@author kiriuja + */ +class KPlayerPopupFrame : public QFrame +{ + Q_OBJECT + +public: + /** The KPlayerPopupFrame constructor. Parameters are passed on to QFrame. + */ + KPlayerPopupFrame (QWidget* parent = 0, const char* name = 0) + : QFrame (parent, name, Qt::WType_Popup) { } + /** The KPlayerPopupFrame destructor. Does nothing. + */ + virtual ~KPlayerPopupFrame() { } + +protected: + /** Closes the popup frame when Alt, Tab, Esc, Enter or Return is pressed. + */ + virtual void keyPressEvent (QKeyEvent*); +}; + +/**Action representing a popup slider activated by a toolbar button. + *@author kiriuja + */ +class KPlayerPopupSliderAction : public KAction +{ + Q_OBJECT + +public: + /** The KPlayerPopupSliderAction constructor. Parameters are passed on to KAction. + */ + KPlayerPopupSliderAction (const QString& text, const QString& pix, const KShortcut& shortcut, + const QObject* receiver, const char* slot, QObject* parent = 0, const char* name = 0); + /** The KPlayerPopupSliderAction destructor. Deletes the KPlayerPopupFrame. + */ + virtual ~KPlayerPopupSliderAction(); + + /** Returns a pointer to the KPlayerSlider object. + */ + KPlayerSlider* slider (void) + { return m_slider; } + + /** Plugs the action into the toolbar. Reparents the slider into the toolbar. */ + //virtual int plug (QWidget*, int = -1); + /** Unplugs the action from the toolbar. Reparents the slider out of the toolbar. */ + //virtual void unplug (QWidget*); + +protected slots: + /** Pops up the slider. + */ + virtual void slotActivated (void); + +protected: + /** The slider. + */ + KPlayerSlider* m_slider; + /** The popup frame. + */ + KPlayerPopupFrame* m_frame; +}; + +/**Slider action suitable for insertion into a toolbar. + *@author kiriuja + */ +class KPlayerSliderAction : public KWidgetAction +{ + Q_OBJECT + +public: + /** The KPlayerSliderAction constructor. Parameters are passed on to KAction. + */ + KPlayerSliderAction (const QString& text, const KShortcut&, const QObject* receiver, + const char* slot, KActionCollection* parent = 0, const char* name = 0); + /** The KPlayerSliderAction destructor. Does nothing. + */ + virtual ~KPlayerSliderAction(); + + /** Returns a pointer to the KPlayerSlider object. + */ + KPlayerSlider* slider (void) + { return (KPlayerSlider*) widget(); } + + /** Plugs the slider into the toolbar. + */ + virtual int plug (QWidget* widget, int index = -1); + /** Unplugs the slider from the toolbar. + */ + virtual void unplug (QWidget* widget); + +protected slots: + /** Changes the slider orientation when the toolbar orientation changes. + */ + void orientationChanged (Qt::Orientation); + +protected: + /** The slider. + */ + //KPlayerSlider* m_slider; +}; + +#endif diff --git a/umbrello/umbrello/kstartuplogo.cpp b/umbrello/umbrello/kstartuplogo.cpp new file mode 100644 index 00000000..5cefe463 --- /dev/null +++ b/umbrello/umbrello/kstartuplogo.cpp @@ -0,0 +1,55 @@ +/* + * copyright (C) 2000 + * Michael Edwardes + */ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kstartuplogo.h" +#include +#include +#include + +KStartupLogo::KStartupLogo(QWidget * parent, const char *name) + : QWidget(parent,name, Qt::WStyle_NoBorder | Qt::WStyle_Customize | Qt::WDestructiveClose ) +,m_bReadyToHide(false) { + //pm.load(locate("appdata", "pics/startlogo.png")); + KStandardDirs * dirs = KGlobal::dirs(); + QString dataDir = dirs -> findResourceDir("data", "umbrello/pics/object.png"); + dataDir += "/umbrello/pics/"; + QPixmap pm(dataDir + "startlogo.png"); + setBackgroundPixmap(pm); + setGeometry(QApplication::desktop()->width()/2-pm.width()/2, + QApplication::desktop()->height()/2-pm.height()/2, + pm.width(),pm.height()); + + timer = new QTimer(this); + connect( timer, SIGNAL(timeout()), this, SLOT(timerDone()) ); + timer->start(2000, true); +} + +KStartupLogo::~KStartupLogo() { + delete timer; +} + +void KStartupLogo::mousePressEvent( QMouseEvent*) { + // for the haters of raising startlogos + if (m_bReadyToHide) + hide(); +} + +void KStartupLogo::timerDone() { + this->hide(); +} + +void KStartupLogo::setHideEnabled(bool bEnabled) { + m_bReadyToHide = bEnabled; +} +#include "kstartuplogo.moc" diff --git a/umbrello/umbrello/kstartuplogo.h b/umbrello/umbrello/kstartuplogo.h new file mode 100644 index 00000000..d8f4af7a --- /dev/null +++ b/umbrello/umbrello/kstartuplogo.h @@ -0,0 +1,49 @@ +/* + * copyright (C) 2000 + * Michael Edwardes + */ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + + +#ifndef KSTARTUPLOGO_H +#define KSTARTUPLOGO_H + +#include + + +/** + * Displays a startup splash screen. + * This class is mostly borrowed from another project, probably KMyMoney2. + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class KStartupLogo : public QWidget { + Q_OBJECT +public: + KStartupLogo(QWidget *parent=0, const char *name=0); + ~KStartupLogo(); + void setHideEnabled(bool bEnabled); + +protected: + virtual void mousePressEvent( QMouseEvent*); + bool m_bReadyToHide; + QTimer* timer; + +public slots: + void timerDone(); + +}; + +#endif + + + + + diff --git a/umbrello/umbrello/linepath.cpp b/umbrello/umbrello/linepath.cpp new file mode 100644 index 00000000..59cf105f --- /dev/null +++ b/umbrello/umbrello/linepath.cpp @@ -0,0 +1,957 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "linepath.h" + +// system includes +#include +#include + +// qt includes +#include +#include +#include + +// kde includes +#include + +// application includes +#include "associationwidget.h" +#include "activitywidget.h" +#include "widget_utils.h" +#include "umlview.h" +#include "umldoc.h" +#include "uml.h" + +LinePath::Circle::Circle(QCanvas * canvas, int radius /* = 0 */) + : QCanvasEllipse(radius * 2, radius * 2, canvas) { +} + +void LinePath::Circle::setX(int x) { + QCanvasItem::setX( (double) x ); +} + +void LinePath::Circle::setY(int y) { + QCanvasItem::setY( (double) y ); +} + +void LinePath::Circle::setRadius(int radius) { + QCanvasEllipse::setSize(radius * 2, radius * 2); +} + +int LinePath::Circle::getRadius() const { + return (QCanvasEllipse::height() / 2); +} + +void LinePath::Circle::drawShape(QPainter& p) { + int diameter = height(); + int radius = diameter / 2; + p.drawEllipse( (int)x() - radius, (int)y() - radius, diameter, diameter); +} + +LinePath::LinePath() { + m_RectList.setAutoDelete( true ); + m_LineList.setAutoDelete( true ); + m_HeadList.setAutoDelete( true ); + m_ParallelList.setAutoDelete( true ); + m_bSelected = false; + m_pClearPoly = 0; + m_pCircle = 0; + m_PointArray.resize( 4 ); + m_ParallelLines.resize( 4 ); + m_pAssociation = 0; + m_bHeadCreated = false; + m_bParallelLineCreated = false; + m_DockRegion = TopBottom; +} + +LinePath::~LinePath() {} + +void LinePath::setAssociation(AssociationWidget * association ) { + if( !association ) + return; + cleanup(); + m_pAssociation = association; + createHeadLines(); + if( getAssocType() == Uml::at_Coll_Message ) + setupParallelLine(); + UMLView * view = (UMLView *)m_pAssociation -> parent(); + connect( view, SIGNAL( sigColorChanged( Uml::IDType ) ), this, SLOT( slotLineColorChanged( Uml::IDType ) ) ); + connect( view, SIGNAL( sigLineWidthChanged( Uml::IDType ) ), this, SLOT( slotLineWidthChanged( Uml::IDType ) ) ); +} + +QPoint LinePath::getPoint( int pointIndex ) { + int count = m_LineList.count(); + if( count == 0 || pointIndex > count || pointIndex < 0) + return QPoint( -1, -1 ); + + if( pointIndex == count ) { + QCanvasLine * line = m_LineList.last(); + return line -> endPoint(); + } + QCanvasLine * line = m_LineList.at( pointIndex ); + return line -> startPoint(); +} + +bool LinePath::setPoint( int pointIndex, const QPoint &point ) { + int count = m_LineList.count(); + if( count == 0 || pointIndex > count || pointIndex < 0) + return false; + if (point.x() == 0 && point.y() == 0) { + kError() << "LinePath::setPoint:ignoring request for (0,0)" << endl; + return false; + } + + if( pointIndex == count) { + QCanvasLine * line = m_LineList.last(); + QPoint p = line -> startPoint(); + line -> setPoints( p.x(), p.y(), point.x(), point.y() ); + moveSelected( pointIndex ); + update(); + return true; + } + if( pointIndex == 0 ) { + QCanvasLine * line = m_LineList.first(); + QPoint p = line -> endPoint(); + line -> setPoints( point.x(), point.y(), p.x(), p.y() ); + moveSelected( pointIndex ); + update(); + return true; + } + QCanvasLine * line = m_LineList.at( pointIndex ); + QPoint p = line -> endPoint(); + line -> setPoints( point.x(), point.y(), p.x(), p.y() ); + line = m_LineList.at( pointIndex - 1 ); + p = line -> startPoint(); + line -> setPoints( p.x(), p.y(), point.x(), point.y() ); + moveSelected( pointIndex ); + update(); + return true; +} + +bool LinePath::isPoint( int pointIndex, const QPoint &point, unsigned short delta) { + int count = m_LineList.count(); + if ( pointIndex >= count ) + return false; + + QCanvasLine * line = m_LineList.at( pointIndex ); + + /* check if the given point is the start or end point of the line */ + if ( ( + abs( line -> endPoint().x() - point.x() ) <= delta + && + abs( line -> endPoint().y() - point.y() ) <= delta + ) || ( + abs( line -> startPoint().x() - point.x() ) <= delta + && + abs( line -> startPoint().y() - point.y() ) <= delta + ) ) + return true; + + /* check if the given point is the start or end point of the line */ + return false; +} + +bool LinePath::insertPoint( int pointIndex, const QPoint &point ) { + int count = m_LineList.count(); + if( count == 0 ) + return false; + const bool bLoading = UMLApp::app()->getDocument()->loading(); + + if( count == 1 || pointIndex == 1) { + QCanvasLine * first = m_LineList.first(); + QPoint sp = first -> startPoint(); + QPoint ep = first -> endPoint(); + first -> setPoints( sp.x(), sp.y(), point.x(), point.y() ); + QCanvasLine * line = new QCanvasLine( getCanvas() ); + line -> setZ( -2 ); + line -> setPoints( point.x(), point.y(), ep.x(), ep.y() ); + line -> setPen( getPen() ); + line -> setVisible( true ); + m_LineList.insert( 1, line ); + if (!bLoading) + setupSelected(); + return true; + } + if( count + 1 == pointIndex ) { + QCanvasLine * before = m_LineList.last(); + QPoint sp = before -> startPoint(); + QPoint ep = before -> endPoint(); + before -> setPoints( sp.x(), sp.y(), point.x(), point.y() ); + QCanvasLine * line = new QCanvasLine( getCanvas() ); + line -> setPoints( point.x(), point.y(), ep.x(), ep.y() ); + line -> setZ( -2 ); + line -> setPen( getPen() ); + line -> setVisible( true ); + m_LineList.append( line ); + if (!bLoading) + setupSelected(); + return true; + } + QCanvasLine * before = m_LineList.at( pointIndex - 1 ); + QPoint sp = before -> startPoint(); + QPoint ep = before -> endPoint(); + before -> setPoints( sp.x(), sp.y(), point.x(), point.y() ); + QCanvasLine * line = new QCanvasLine(getCanvas() ); + line -> setPoints( point.x(), point.y(), ep.x(), ep.y() ); + line -> setZ( -2 ); + line -> setPen( getPen() ); + line -> setVisible( true ); + m_LineList.insert( pointIndex, line ); + if (!bLoading) + setupSelected(); + return true; +} + +bool LinePath::removePoint( int pointIndex, const QPoint &point, unsigned short delta ) +{ + /* get the number of line segments */ + int count = m_LineList.count(); + if ( pointIndex >= count ) + return false; + + /* we don't know if the user clicked on the start- or endpoint of a + * line segment */ + QCanvasLine * current_line = m_LineList.at( pointIndex ); + if (abs( current_line -> endPoint().x() - point.x() ) <= delta + && + abs( current_line -> endPoint().y() - point.y() ) <= delta) + { + /* the user clicked on the end point of the line; + * we have to make sure that this isn't the last line segment */ + if (pointIndex >= count - 1) + return false; + + /* the next segment will get the starting point from the current one, + * which is going to be removed */ + QCanvasLine * next_line = m_LineList.at( pointIndex + 1 ); + QPoint startPoint = current_line -> startPoint(); + QPoint endPoint = next_line -> endPoint(); + next_line -> setPoints(startPoint.x(), startPoint.y(), + endPoint.x(), endPoint.y()); + + } else + if (abs( current_line -> startPoint().x() - point.x() ) <= delta + && + abs( current_line -> startPoint().y() - point.y() ) <= delta) + { + /* the user clicked on the start point of the line; + * we have to make sure that this isn't the first line segment */ + if (pointIndex < 1) + return false; + + /* the previous segment will get the end point from the current one, + * which is going to be removed */ + QCanvasLine * previous_line = m_LineList.at( pointIndex - 1 ); + QPoint startPoint = previous_line -> startPoint(); + QPoint endPoint = current_line -> endPoint(); + previous_line -> setPoints(startPoint.x(), startPoint.y(), + endPoint.x(), endPoint.y()); + } else { + /* the user clicked neither on the start- nor on the end point of + * the line; this really shouldn't happen, but just make sure */ + return false; + } + + + /* remove the segment from the list */ + m_LineList.remove( pointIndex ); + + return true; +} + +bool LinePath::setStartEndPoints( const QPoint &start, const QPoint &end ) { + int count = m_LineList.count(); + + if( count == 0 ) { + QCanvasLine * line = new QCanvasLine(getCanvas() ); + line -> setPoints( start.x(), start.y(), end.x(), end.y() ); + line -> setZ( -2 ); + line -> setPen( getPen() ); + line -> setVisible( true ); + m_LineList.append( line ); + return true; + } + bool status = setPoint( 0, start ); + if( status) + return setPoint( count , end ); + return false; +} + +int LinePath::count() { + return m_LineList.count() + 1; +} + +int LinePath::onLinePath( const QPoint &position ) { + QCanvasItemList list = getCanvas() -> collisions( position ); + int index = -1; + + QCanvasItemList::iterator end(list.end()); + for(QCanvasItemList::iterator item_it(list.begin()); item_it != end; ++item_it ) { + if( ( index = m_LineList.findRef( (QCanvasLine*)*item_it ) ) != -1 ) + break; + }//end for + return index; +} + +void LinePath::setSelected( bool select ) { + if(select) + setupSelected(); + else if( m_RectList.count() > 0 ) + m_RectList.clear(); +} + +void LinePath::setAssocType( Uml::Association_Type type ) { + LineListIt it( m_LineList ); + QCanvasLine * line = 0; + while( ( line = it.current() ) ) { + line -> setPen( getPen() ); + ++it; + } + if( m_pClearPoly ) { + delete m_pClearPoly; + m_pClearPoly = 0; + } + if( type == Uml::at_Coll_Message ) + setupParallelLine(); + else + createHeadLines(); + update(); +} + +void LinePath::update() { + if (getAssocType() == Uml::at_Coll_Message) { + if (m_bParallelLineCreated) { + calculateParallelLine(); + updateParallelLine(); + } else + setupParallelLine(); + } else if (m_bHeadCreated) { + calculateHead(); + updateHead(); + } else { + createHeadLines(); + } +} + +void LinePath::slotLineColorChanged( Uml::IDType viewID ) { + if(m_pAssociation->getUMLView()->getID() != viewID) { + return; + } + setLineColor( m_pAssociation->getUMLView()->getLineColor() ); +} + + +void LinePath::setLineColor( const QColor &color ) { + QCanvasLine * line = 0; + uint linewidth = 0; + LineListIt it( m_LineList ); + while( ( line = it.current() ) ) { + linewidth = line->pen().width(); + line -> setPen( QPen( color, linewidth ) ); + ++it; + } + LineListIt hit( m_HeadList ); + while( ( line = hit.current() ) ) { + linewidth = line->pen().width(); + line -> setPen( QPen( color, linewidth ) ); + ++hit; + } + LineListIt pit( m_ParallelList ); + while( ( line = pit.current() ) ) { + linewidth = line->pen().width(); + line -> setPen( QPen( color, linewidth ) ); + ++pit; + } + + if( getAssocType() == Uml::at_Aggregation ) + if (m_pClearPoly) m_pClearPoly -> setBrush( QBrush( Qt::white ) ); + else if( getAssocType() == Uml::at_Composition ) + if (m_pClearPoly) m_pClearPoly -> setBrush( QBrush( color ) ); + + if( m_pCircle ) { + linewidth = m_pCircle->pen().width(); + m_pCircle->setPen( QPen(color, linewidth) ); + } +} + +void LinePath::slotLineWidthChanged( Uml::IDType viewID ) { + if(m_pAssociation->getUMLView()->getID() != viewID) { + return; + } + setLineWidth( m_pAssociation->getUMLView()->getLineWidth() ); +} + +void LinePath::setLineWidth( uint width ) { + QCanvasLine * line = 0; + QColor linecolor; + LineListIt it( m_LineList ); + while( ( line = it.current() ) ) { + linecolor = line->pen().color(); + line -> setPen( QPen( linecolor, width ) ); + ++it; + } + LineListIt hit( m_HeadList ); + while( ( line = hit.current() ) ) { + linecolor = line->pen().color(); + line -> setPen( QPen( linecolor, width ) ); + ++hit; + } + LineListIt pit( m_ParallelList ); + while( ( line = pit.current() ) ) { + linecolor = line->pen().color(); + line -> setPen( QPen( linecolor, width ) ); + ++pit; + } + + if( m_pCircle ) { + linecolor = m_pCircle->pen().color(); + m_pCircle->setPen( QPen(linecolor, width) ); + } +} + +void LinePath::moveSelected( int pointIndex ) { + int lineCount = m_LineList.count(); + if( !m_bSelected ) { + m_RectList.clear(); + return; + } + if( (int)m_RectList.count() + 1 != lineCount ) + setupSelected(); + QCanvasRectangle * rect = 0; + QCanvasLine * line = 0; + if( pointIndex == lineCount || lineCount == 1) { + line = m_LineList.last(); + QPoint p = line -> endPoint(); + rect = m_RectList.last(); + rect -> setX( p.x() ); + rect -> setY( p.y() ); + rect -> setZ( 4 ); + return; + } + line = m_LineList.at( pointIndex ); + QPoint p = line -> startPoint(); + rect = m_RectList.at( pointIndex ); + rect -> setX( p.x() ); + rect -> setY( p.y() ); + rect -> setZ( 4 ); +} + +void LinePath::setupSelected() { + m_RectList.clear(); + QCanvasLine * line = 0; + LineListIt it( m_LineList ); + while( ( line = it.current() ) ) { + QPoint sp = line -> startPoint(); + QCanvasRectangle *rect = Widget_Utils::decoratePoint(sp); + m_RectList.append( rect ); + ++it; + } + //special case for last point + line = m_LineList.last(); + QPoint p = line -> endPoint(); + QCanvasRectangle *rect = Widget_Utils::decoratePoint(p); + m_RectList.append( rect ); + update(); +} + +QPen LinePath::getPen() { + Uml::Association_Type type = getAssocType(); + if( type == Uml::at_Dependency || type == Uml::at_Realization || type == Uml::at_Anchor ) + return QPen( getLineColor(), getLineWidth(), Qt::DashLine ); + return QPen( getLineColor(), getLineWidth() ); +} + +void LinePath::calculateHead() { + uint size = m_LineList.count(); + QPoint farPoint; + int halfLength = 10; + double arrowAngle = 0.2618; // 0.5 * atan(sqrt(3.0) / 3.0) = 0.2618 + Uml::Association_Type at = getAssocType(); + bool diamond = (at == Uml::at_Aggregation || at == Uml::at_Composition); + if (diamond || at == Uml::at_Containment) { + farPoint = getPoint(1); + m_EgdePoint = getPoint(0); + if (diamond) { + arrowAngle *= 1.5; // wider + halfLength += 1; // longer + } else { + // Containment has a circle-plus symbol at the + // containing object. What we are tweaking here + // is the perpendicular line through the circle + // (i.e. the horizontal line of the plus sign if + // the objects are oriented north/south) + arrowAngle *= 2.5; // wider + halfLength -= 4; // shorter + } + } else { + farPoint = getPoint(size - 1); + m_EgdePoint = getPoint(size); + // We have an arrow. + arrowAngle *= 2.0; // wider + halfLength += 3; // longer + } + int xa = farPoint.x(); + int ya = farPoint.y(); + int xb = m_EgdePoint.x(); + int yb = m_EgdePoint.y(); + double deltaX = xb - xa; + double deltaY = yb - ya; + double hypotenuse = sqrt(deltaX*deltaX + deltaY*deltaY); // the length + double slope = atan2(deltaY, deltaX); //slope of line + double arrowSlope = slope + arrowAngle; + double cosx, siny; + if (hypotenuse < 1.0e-6) { + cosx = 1.0; + siny = 0.0; + } else { + cosx = halfLength * deltaX/hypotenuse; + siny = halfLength * deltaY/hypotenuse; + } + + m_ArrowPointA.setX( (int)rint(xb - halfLength * cos(arrowSlope)) ); + m_ArrowPointA.setY( (int)rint(yb - halfLength * sin(arrowSlope)) ); + arrowSlope = slope - arrowAngle; + m_ArrowPointB.setX( (int)rint(xb - halfLength * cos(arrowSlope)) ); + m_ArrowPointB.setY( (int)rint(yb - halfLength * sin(arrowSlope)) ); + + if(xa > xb) + cosx = cosx > 0 ? cosx : cosx * -1; + else + cosx = cosx > 0 ? cosx * -1: cosx; + + if(ya > yb) + siny = siny > 0 ? siny : siny * -1; + else + siny = siny > 0 ? siny * -1 : siny; + + m_MidPoint.setX( (int)rint(xb + cosx) ); + m_MidPoint.setY( (int)rint(yb + siny) ); + + m_PointArray.setPoint(0, m_EgdePoint); + m_PointArray.setPoint(1, m_ArrowPointA); + if( getAssocType() == Uml::at_Realization || + getAssocType() == Uml::at_Generalization ) { + m_PointArray.setPoint( 2, m_ArrowPointB ); + m_PointArray.setPoint( 3, m_EgdePoint ); + } else { + QPoint diamondFarPoint; + diamondFarPoint.setX( (int)rint(xb + cosx * 2) ); + diamondFarPoint.setY( (int)rint(yb + siny * 2) ); + m_PointArray.setPoint(2, diamondFarPoint); + m_PointArray.setPoint(3, m_ArrowPointB); + } + +} + +void LinePath::updateHead() { + int count = m_HeadList.count(); + QCanvasLine * line = 0; + + switch( getAssocType() ) { + case Uml::at_State: + case Uml::at_Activity: + case Uml::at_UniAssociation: + case Uml::at_Dependency: + if( count < 2) + return; + line = m_HeadList.at( 0 ); + line -> setPoints( m_EgdePoint.x(), m_EgdePoint.y(), m_ArrowPointA.x(), m_ArrowPointA.y() ); + + line = m_HeadList.at( 1 ); + line -> setPoints( m_EgdePoint.x(), m_EgdePoint.y(), m_ArrowPointB.x(), m_ArrowPointB.y() ); + break; + + case Uml::at_Relationship: + if (count < 2) { + return; + } + { + int xoffset = 0; + int yoffset = 0; + if( m_DockRegion == TopBottom ) + xoffset = 8; + else + yoffset = 8; + line = m_HeadList.at( 0 ); + line->setPoints( m_PointArray[2].x(), m_PointArray[2].y(), + m_PointArray[0].x()-xoffset, m_PointArray[0].y()-yoffset ); + + line = m_HeadList.at( 1 ); + line->setPoints( m_PointArray[2].x(), m_PointArray[2].y(), + m_PointArray[0].x()+xoffset, m_PointArray[0].y()+yoffset ); + } + + case Uml::at_Generalization: + case Uml::at_Realization: + if( count < 3) + return; + line = m_HeadList.at( 0 ); + line -> setPoints( m_EgdePoint.x(), m_EgdePoint.y(), m_ArrowPointA.x(), m_ArrowPointA.y() ); + + line = m_HeadList.at( 1 ); + line -> setPoints( m_EgdePoint.x(), m_EgdePoint.y(), m_ArrowPointB.x(), m_ArrowPointB.y() ); + + line = m_HeadList.at( 2 ); + line -> setPoints( m_ArrowPointA.x(), m_ArrowPointA.y(), m_ArrowPointB.x(), m_ArrowPointB.y() ); + m_pClearPoly -> setPoints( m_PointArray ); + break; + + case Uml::at_Composition: + case Uml::at_Aggregation: + if( count < 4) + return; + line = m_HeadList.at( 0 ); + line -> setPoints( m_PointArray[ 0 ].x(), m_PointArray[ 0 ].y(), m_PointArray[ 1 ].x(), m_PointArray[ 1 ].y() ); + + line = m_HeadList.at( 1 ); + line -> setPoints( m_PointArray[ 1 ].x(), m_PointArray[ 1 ].y(), m_PointArray[ 2 ].x(), m_PointArray[ 2 ].y() ); + + line = m_HeadList.at( 2 ); + line -> setPoints( m_PointArray[ 2 ].x(), m_PointArray[ 2 ].y(), m_PointArray[ 3 ].x(), m_PointArray[ 3 ].y() ); + + line = m_HeadList.at( 3 ); + line -> setPoints( m_PointArray[ 3 ].x(), m_PointArray[ 3 ].y(), m_PointArray[ 0 ].x(), m_PointArray[ 0 ].y() ); + m_pClearPoly -> setPoints( m_PointArray ); + break; + + case Uml::at_Containment: + if (count < 1) + return; + line = m_HeadList.at( 0 ); + line->setPoints( m_PointArray[ 1 ].x(), m_PointArray[ 1 ].y(), + m_PointArray[ 3 ].x(), m_PointArray[ 3 ].y() ); + m_pCircle -> setX( m_MidPoint.x() ); + m_pCircle -> setY( m_MidPoint.y() ); + break; + default: + break; + } +} + +void LinePath::growList(LineList &list, int by) { + QPen pen( getLineColor(), getLineWidth() ); + for (int i = 0; i < by; i++) { + QCanvasLine * line = new QCanvasLine( getCanvas() ); + line -> setZ( 0 ); + line -> setPen( pen ); + line -> setVisible( true ); + list.append( line ); + } +} + +void LinePath::createHeadLines() { + m_HeadList.clear(); + QCanvas * canvas = getCanvas(); + switch( getAssocType() ) { + case Uml::at_Activity: + case Uml::at_State: + case Uml::at_Dependency: + case Uml::at_UniAssociation: + case Uml::at_Relationship: + growList(m_HeadList, 2); + break; + + case Uml::at_Generalization: + case Uml::at_Realization: + growList(m_HeadList, 3); + m_pClearPoly = new QCanvasPolygon( canvas ); + m_pClearPoly -> setVisible( true ); + m_pClearPoly -> setBrush( QBrush( Qt::white ) ); + m_pClearPoly -> setZ( -1 ); + break; + + case Uml::at_Composition: + case Uml::at_Aggregation: + growList(m_HeadList, 4); + m_pClearPoly = new QCanvasPolygon( canvas ); + m_pClearPoly -> setVisible( true ); + if( getAssocType() == Uml::at_Aggregation ) + m_pClearPoly -> setBrush( QBrush( Qt::white ) ); + else + m_pClearPoly -> setBrush( QBrush( getLineColor() ) ); + m_pClearPoly -> setZ( -1 ); + break; + + case Uml::at_Containment: + growList(m_HeadList, 1); + if (!m_pCircle) { + m_pCircle = new Circle( canvas, 6 ); + m_pCircle->show(); + m_pCircle->setPen( QPen( getLineColor(), getLineWidth() ) ); + } + break; + default: + break; + } + m_bHeadCreated = true; +} + +void LinePath::calculateParallelLine() { + int midCount = count() / 2; + double ATAN = atan(1.0); + int lineDist = 10; + //get 1/8(M) and 7/8(T) point + QPoint a = getPoint( midCount - 1 ); + QPoint b = getPoint( midCount ); + int mx = ( a.x() + b.x() ) / 2; + int my = ( a.y() + b.y() ) / 2; + int tx = ( mx + b.x() ) / 2; + int ty = ( my + b.y() ) / 2; + //find dist between M and T points + int distX = ( mx - tx ); + distX *= distX; + int distY = ( my - ty ); + distY *= distY; + double dist = sqrt( double(distX + distY) ); + double angle = atan2( double(ty - my), double(tx - mx) ) + ( ATAN * 2 ); + //find point from M to start line from. + double cosx = cos( angle ) * lineDist; + double siny = sin( angle ) * lineDist; + QPoint pointM( mx + (int)cosx, my + (int)siny ); + //find dist between P(xb, yb) + distX = ( tx - b.x() ); + distX *= distX; + distY = ( ty - b.y() ); + distY *= distY; + dist = sqrt( double(distX + distY) ); + //find point from T to end line + cosx = cos( angle ) * lineDist; + siny = sin( angle ) * lineDist; + QPoint pointT( tx + (int)cosx, ty + (int)siny ); + m_ParallelLines[ 1 ] = pointM; + m_ParallelLines[ 0 ] = pointT; + + int arrowDist = 5; + angle = atan2( double(pointT.y() - pointM.y()), + double(pointT.x() - pointM.x()) ); + double arrowSlope = angle + ATAN; + cosx = ( cos( arrowSlope ) ) * arrowDist; + siny = ( sin( arrowSlope ) ) * arrowDist; + m_ParallelLines[ 2 ] = QPoint( pointT.x() - (int)cosx, pointT.y() - (int)siny ); + arrowSlope = angle - ATAN; + cosx = ( cos( arrowSlope ) ) * arrowDist; + siny = ( sin( arrowSlope ) ) * arrowDist; + m_ParallelLines[ 3 ] = QPoint( pointT.x() - (int)cosx, pointT.y() - (int)siny ); +} + +void LinePath::setupParallelLine() { + m_ParallelList.clear(); + growList(m_ParallelList, 3); + m_bParallelLineCreated = true; +} + +void LinePath::updateParallelLine() { + if( !m_bParallelLineCreated ) + return; + QCanvasLine * line = 0; + QPoint common = m_ParallelLines.at( 0 ); + QPoint p = m_ParallelLines.at( 1 ); + line = m_ParallelList.at( 0 ); + line -> setPoints( common.x(), common.y(), p.x(), p.y() ); + + p = m_ParallelLines.at( 2 ); + line = m_ParallelList.at( 1 ); + line -> setPoints( common.x(), common.y(), p.x(), p.y() ); + + p = m_ParallelLines.at( 3 ); + line = m_ParallelList.at( 2 ); + line -> setPoints( common.x(), common.y(), p.x(), p.y() ); +} + +bool LinePath::operator==( LinePath & rhs ) { + if( this -> m_LineList.count() != rhs.m_LineList.count() ) + return false; + + //Check to see if all points at the same position + for( int i = 0; i< rhs.count() ; i++ ) { + if( this -> getPoint( i ) != rhs.getPoint( i ) ) + return false; + } + return true; +} + +LinePath & LinePath::operator=( LinePath & rhs ) { + if( this == &rhs ) + return *this; + //clear out the old canvas objects + this -> m_LineList.clear(); + this -> m_ParallelList.clear(); + this -> m_RectList.clear(); + this -> m_HeadList.clear(); + int count = rhs.m_LineList.count(); + //setup start end points + this -> setStartEndPoints( rhs.getPoint( 0 ), rhs.getPoint( count) ); + //now insert the rest + for( int i = 1; i < count ; i++ ) { + this -> insertPoint( i, rhs.getPoint ( i ) ); + } + this -> setAssocType( rhs.getAssocType() ); + + return *this; +} + +QCanvas * LinePath::getCanvas() { + if( !m_pAssociation ) + return 0; + const UMLView * view = m_pAssociation->getUMLView(); + return view -> canvas(); +} + +Uml::Association_Type LinePath::getAssocType() { + if( m_pAssociation ) + return m_pAssociation -> getAssocType(); + return Uml::at_Association; +} + +QColor LinePath::getLineColor() { + if( !m_pAssociation ) + return Qt::black; + return m_pAssociation -> getLineColor(); +} + +uint LinePath::getLineWidth() { + if( !m_pAssociation ) + return 0; + int viewLineWidth = m_pAssociation -> getLineWidth(); + if ( viewLineWidth >= 0 && viewLineWidth <= 10 ) + return viewLineWidth; + else { + kWarning() << "Ignore wrong LineWidth of " << viewLineWidth + << " in LinePath::getLineWidth" << endl; + return 0; + } +} + +void LinePath::cleanup() { + if (m_pAssociation) + m_LineList.clear(); + m_HeadList.clear(); + m_RectList.clear(); + m_ParallelList.clear(); + + if( m_pClearPoly ) + delete m_pClearPoly; + if( m_pCircle ) + delete m_pCircle; + m_pCircle = 0; + m_pClearPoly = 0; + m_bHeadCreated = m_bParallelLineCreated = false; + if( m_pAssociation ) { + UMLView * view = (UMLView *)m_pAssociation -> parent(); + if(view) { + disconnect( view, SIGNAL( sigColorChanged( Uml::IDType ) ), this, SLOT( slotLineColorChanged( Uml::IDType ) ) ); + disconnect( view, SIGNAL( sigLineWidthChanged( Uml::IDType ) ), this, SLOT( slotLineWidthChanged( Uml::IDType ) ) ); + } + m_pAssociation = NULL; + } +} + +void LinePath::setDockRegion( Region region ) { + m_DockRegion = region; +} + +bool LinePath::hasPoints () { + int count = m_LineList.count(); + if (count>1) + return true; + return false; +} +void LinePath::dumpPoints () { + int count = m_LineList.count(); + for( int i = 1; i < count; i++ ) { + QPoint point = getPoint( i ); + kDebug()<<" * point x:"< setCanvas( canvas ); + line -> setPen( getPen() ); + } +} + + + +#include "linepath.moc" diff --git a/umbrello/umbrello/linepath.h b/umbrello/umbrello/linepath.h new file mode 100644 index 00000000..434749e3 --- /dev/null +++ b/umbrello/umbrello/linepath.h @@ -0,0 +1,383 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef LINEPATH_H +#define LINEPATH_H +#include +#include +#include +#include +#include +#include +#include "umlnamespace.h" + +/* how many pixels a user could click around a point */ +#define POINT_DELTA 5 + +class AssociationWidget; +class UMLView; + +// Qt forward declarations +class QDataStream; +class QDomDocument; +class QDomElement; + +// typedefs +typedef QPtrList LineList; +typedef QPtrListIterator LineListIt; + +typedef QPtrList RectList; +typedef QPtrListIterator RectListIt; + + +/** + *@author Paul Hensgen + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class LinePath : public QObject { + Q_OBJECT +public: + /** + * Constructor + */ + LinePath(); + + /** + * Deconstructor + */ + ~LinePath(); + + /** + * equal to (==) operator + */ + bool operator==( LinePath & rhs ); + + /** + * copy ( = ) operator + */ + LinePath & operator=( LinePath & rhs ); + + /** + * Enum to tell whether the line docks top/bottom or left/right + */ + enum Region { + TopBottom, LeftRight + }; + + /** + * Tell the line where the line docks + */ + void setDockRegion( Region region ); + + bool hasPoints (); + void dumpPoints (); + + /** + * Returns the point at the point index. + */ + QPoint getPoint( int pointIndex ); + + /** + * Sets the position of an already set point. + */ + bool setPoint( int pointIndex, const QPoint &point ); + + /** + * Checks, if we are at an end of the segment or somewhere in the middle. + * We use the delta, because with the mouse it is hard to find the + * exactly point. + */ + bool isPoint( int pointIndex, const QPoint &point, unsigned short delta = 0 ); + + /** + * Inserts a point at the given index. + */ + bool insertPoint( int pointIndex, const QPoint &point ); + + /** + * Removes the point on the line given by the index, at the coordinates + * given by point with a fuzzy of delta + */ + bool removePoint( int pointIndex, const QPoint &point, unsigned short delta = 0 ); + + /** + * Sets the start and end points. + */ + bool setStartEndPoints( const QPoint &start, const QPoint &end ); + + /** + * Returns the amount of POINTS on the line. + * Includes start and end points. + */ + int count(); + + /** + * Returns -1 if the given point is not on the line. + * else returns the line segment the point is on. + * Use the value to insert points at the point position. + */ + int onLinePath( const QPoint &position ); + + /** + * Sets the canvas to be used. + */ + void setCanvas( QCanvas * canvas ); + + /** + * Sets the Association type. + */ + void setAssocType( Uml::Association_Type type ); + + /** + * Calls a group of methods to update the line. Used to save you calling multiple methods. + */ + void update(); + + /** + * This will setup the class ready to display the line correctly. + * This MUST be called before you can use this class. + */ + void setAssociation( AssociationWidget * association ); + + /** + * Returns the Association this class is linked to. + */ + AssociationWidget * getAssociation() { + return m_pAssociation; + } + + /** + * Sets the status of whether the line is selected or not. + */ + void setSelected( bool select ); + + void saveToXMI( QDomDocument & qDoc, QDomElement & qElement ); + + bool loadFromXMI( QDomElement & qElement ); + + /** + * Activates the line list. + * This is needed because the m_pAssociation does not yet + * exist at the time of the LinePath::loadFromXMI call. + * However, this means that the points in the m_LineList + * do not have a parent when they are loaded. + * They need to be reparented by calling LinePath::activate() + * once the m_pAssociation exists. + */ + void activate(); + + /** + * Removes and item created that are no longer needed. + */ + void cleanup(); + + /** + * Returns the type of pen to use depending on the type of Association. + */ + QPen getPen(); + + /** + * Sets the line color used by the line. + */ + void setLineColor( const QColor &color ); + /** + * Sets the line width used by the line. + */ + void setLineWidth( uint width ); + +protected: + + /** + * Draw a (hollow) circle. + * We can't use QCanvasEllipse directly for this because it doesn't + * use the pen, i.e. QCanvasEllipse only draws filled ellipses. + */ + class Circle : public QCanvasEllipse { + public: + explicit Circle(QCanvas * canvas, int radius = 0); + void setRadius(int radius); + int getRadius() const; + void setX(int x); + void setY(int y); + /** + * The beef: Override method from QCanvasEllipse. + */ + void drawShape(QPainter& p); + }; + + /** + * Returns the canvas being used. + * Will return zero if the Association hasn't been set. + * + * This class doesn't hold this information but is a wrapper + * method to stop calls to undefined variable like m_pAssociation. + */ + QCanvas * getCanvas(); + + /** + * Returns the Association type. + * Returns Uml::at_Association if association hasn't been set. + * + * This class doesn't hold this information but is a wrapper + * method to stop calls to undefined variable like m_pAssociation. + */ + Uml::Association_Type getAssocType(); + + /** + * Returns the Line Color to use. + * Returns black if association not set. + * + * This class doesn't hold this information but is a wrapper + * method to stop calls to undefined variable like m_pAssociation. + */ + QColor getLineColor(); + /** + * Returns the Line Width to use. + * Returns 0 if association not set. + * + * This class doesn't hold this information but is a wrapper + * method to stop calls to undefined variable like m_pAssociation. + */ + uint getLineWidth(); + + /** + * Moves the selected canvas widgets. + */ + void moveSelected( int pointIndex ); + + /** + * Sets up the selected canvases needed. + */ + void setupSelected(); + + /** + * Calculates the head points. + */ + void calculateHead(); + + /** + * Creates the head lines to display the head. + */ + void createHeadLines(); + + /** + * Create a number of new lines and append them to the given list. + * + * @param list The list into which to append lines. + * @param by The number of lines to insert into the given list. + */ + void growList(LineList &list, int by); + + /** + * Updates the head lines. Call after calculating the new points. + */ + void updateHead(); + + /** + * Creates the line objects to display the parallel line. + */ + void setupParallelLine(); + + /** + * Calculates the position of the parallel line. + */ + void calculateParallelLine(); + + /** + * Updates the parallel line. + * Call after calculating the new position. + */ + void updateParallelLine(); + + /********Attributes*************/ + + /** + * The association we are representing. + */ + AssociationWidget * m_pAssociation; + + /** + * Contains all the lines of the association. + */ + LineList m_LineList; + + /** + * Selected boxes list. + */ + RectList m_RectList; + + /** + * Head lines. + */ + LineList m_HeadList; + + /** + * The parallel line. + */ + LineList m_ParallelList; + + /** + * Selected status. + */ + bool m_bSelected; + + /** + * Contains calculated points used to draw the line head. + */ + QPointArray m_PointArray; + + /** + * Contains calculated points used to draw the line head. + */ + QPoint m_ArrowPointA, m_ArrowPointB, m_MidPoint, m_EgdePoint; + + /** + * A polygon object to blank out any lines we don't want to see. + */ + QCanvasPolygon * m_pClearPoly; + + /** + * The transparent circle required by containment associations. + */ + Circle * m_pCircle; + + /** + * Contains the calculated points for the parallel line + * on a collaboration message to use. + */ + QPointArray m_ParallelLines; + + /** + * Region where the line docks + */ + Region m_DockRegion; + + bool m_bHeadCreated; + + bool m_bParallelLineCreated; + +public slots: + + /** + * Sets the line color used by the line. + * + * @param viewID The id of the object behind the widget. + */ + void slotLineColorChanged( Uml::IDType viewID ); + /** + * Sets the line width used by the line. + * + * @param viewID The id of the object behind the widget. + */ + void slotLineWidthChanged( Uml::IDType viewID ); +}; + +#endif diff --git a/umbrello/umbrello/linkwidget.cpp b/umbrello/umbrello/linkwidget.cpp new file mode 100644 index 00000000..b1b176de --- /dev/null +++ b/umbrello/umbrello/linkwidget.cpp @@ -0,0 +1,61 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "linkwidget.h" +// qt/kde includes +#include +// app includes +#include "umlview.h" +#include "umlobject.h" +#include "classifier.h" +#include "operation.h" +#include "uml.h" + +LinkWidget::LinkWidget() { +} + +LinkWidget::~LinkWidget() { +} + +UMLClassifier *LinkWidget::getOperationOwner() { + UMLOperation *op = getOperation(); + if (op == NULL) + return NULL; + return static_cast(op->parent()); +} + +QString LinkWidget::getOperationText(UMLView *view /* = NULL */) { + UMLOperation *op = getOperation(); + if (op == NULL) + return getCustomOpText(); + if (view == NULL) + view = UMLApp::app()->getCurrentView(); + Uml::Signature_Type sigType; + if (view && view->getShowOpSig()) + sigType = Uml::st_SigNoVis; + else + sigType = Uml::st_NoSigNoVis; + QString opText = op->toString(sigType); + return opText; +} + +void LinkWidget::resetTextPositions() { +} + +bool LinkWidget::showDialog() { + return true; +} + +void LinkWidget::calculateNameTextSegment() { +} + + diff --git a/umbrello/umbrello/linkwidget.h b/umbrello/umbrello/linkwidget.h new file mode 100644 index 00000000..0e85d615 --- /dev/null +++ b/umbrello/umbrello/linkwidget.h @@ -0,0 +1,128 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef LINKWIDGET_H +#define LINKWIDGET_H + +#include + +#include "umlnamespace.h" + +// forward declarations +class UMLClassifier; +class UMLOperation; +class FloatingTextWidget; +class UMLView; + +/** + * This is an interface realized by AssociationWidget and MessageWidget. + * The design of this interface was driven by the requirements of + * class FloatingTextWidget. As the architecture of Umbrello evolves (for + * example, if the class FloatingTextWidget is redesigned), it can be + * cleaned up. + * + * @short Interface to FloatingTextWidget for AssociationWidget and MessageWidget. + * @author Oliver Kellogg + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class LinkWidget { +public: + LinkWidget(); + virtual ~LinkWidget(); + + /** + * Sets the font the widget is to use. + * Abstract operation implemented by inheriting classes. + * Motivated by FloatingTextWidget::slotMenuSelection(mt_Operation) + * + * @param font Font to be set. + */ + virtual void lwSetFont(QFont font) = 0; + + /** + * Motivated by FloatingTextWidget::slotMenuSelection(mt_Operation) + */ + virtual UMLClassifier *getOperationOwner(); + + /** + * Motivated by FloatingTextWidget::slotMenuSelection(mt_Operation) + */ + virtual UMLOperation *getOperation() = 0; + + /** + * Motivated by FloatingTextWidget::slotMenuSelection(mt_Operation) + */ + virtual void setOperation(UMLOperation *op) = 0; + + /** + * Motivated by getOperationText() + */ + virtual QString getCustomOpText() = 0; + + /** + * Motivated by FloatingTextWidget::slotMenuSelection(mt_Operation) + */ + virtual void setCustomOpText(const QString &opText) = 0; + + /** + * Uses getOperation() if set, else calls getCustomOpText(). + */ + QString getOperationText(UMLView *view = NULL); + + /** + * Motivated by FloatingTextWidget::slotMenuSelection(mt_Reset_Label_Positions) + * Only applies to AssociationWidget. + */ + virtual void resetTextPositions(); + + /** + * Motivated by FloatingTextWidget::setMessageText() + */ + virtual void setMessageText(FloatingTextWidget *ft) = 0; + + /** + * Motivated by FloatingTextWidget::handleRename() + */ + virtual void setText(FloatingTextWidget *ft, const QString &newText) = 0; + + /** + * Motivated by FloatingTextWidget::mouseDoubleClickEvent() + * Only applies to AssociationWidget. + */ + virtual bool showDialog(); + + /** + * Motivated by FloatingTextWidget::showOpDlg() + */ + virtual UMLClassifier *getSeqNumAndOp(QString& seqNum, QString& op) = 0; + + /** + * Motivated by FloatingTextWidget::showOpDlg() + */ + virtual void setSeqNumAndOp(const QString &seqNum, const QString &op) = 0; + + /** + * Abstract operation implemented by inheriting classes. + * Motivated by FloatingTextWidget::mouseMoveEvent() + */ + virtual void constrainTextPos(int &textX, int &textY, + int textWidth, int textHeight, + Uml::Text_Role tr) = 0; + + /** + * Motivated by FloatingTextWidget::setLink(). + * Only applies to AssociationWidget. + */ + virtual void calculateNameTextSegment(); + +}; + +#endif diff --git a/umbrello/umbrello/listpopupmenu.cpp b/umbrello/umbrello/listpopupmenu.cpp new file mode 100644 index 00000000..816d661b --- /dev/null +++ b/umbrello/umbrello/listpopupmenu.cpp @@ -0,0 +1,1348 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "listpopupmenu.h" + +// qt/kde includes +#include +#include +#include +#include + +// app includes +#include "umlwidget.h" +#include "umldoc.h" +#include "umllistview.h" +#include "umllistviewitem.h" +#include "classifierwidget.h" +#include "classifier.h" +#include "floatingtextwidget.h" +#include "uml.h" +#include "model_utils.h" +#include "widget_utils.h" +#include "folder.h" +#include "umlview.h" +#include "statewidget.h" +#include "activitywidget.h" +#include "forkjoinwidget.h" +#include "objectwidget.h" + +//ListPopupMenu for a UMLView (diagram) +ListPopupMenu::ListPopupMenu(QWidget *parent, Menu_Type type, UMLView * view) + : KPopupMenu(parent) { + init(); + setupMenu(type, view); +} + +//ListPopupMenu for the tree list view +ListPopupMenu::ListPopupMenu(QWidget *parent, Uml::ListView_Type type) + : KPopupMenu(parent) { + init(); + Menu_Type mt = mt_Undefined; + switch(type) + { + case Uml::lvt_Logical_View: + mt = mt_Logical_View; + break; + + case Uml::lvt_UseCase_View: + mt = mt_UseCase_View; + break; + + case Uml::lvt_Component_View: + mt = mt_Component_View; + break; + + case Uml::lvt_EntityRelationship_Model: + mt = mt_EntityRelationship_Model; + break; + + case Uml::lvt_Deployment_View: + mt = mt_Deployment_View; + break; + + case Uml::lvt_Logical_Folder: + mt = mt_Logical_Folder; + break; + + case Uml::lvt_UseCase_Folder: + mt = mt_UseCase_Folder; + break; + + case Uml::lvt_Component_Folder: + mt = mt_Component_Folder; + break; + + case Uml::lvt_Deployment_Folder: + mt = mt_Deployment_Folder; + break; + + case Uml::lvt_EntityRelationship_Folder: + mt = mt_EntityRelationship_Folder; + break; + + case Uml::lvt_UseCase_Diagram: + mt = mt_UseCase_Diagram; + break; + + case Uml::lvt_Class_Diagram: + mt = mt_Class_Diagram; + break; + + case Uml::lvt_Collaboration_Diagram: + mt = mt_Collaboration_Diagram; + break; + + case Uml::lvt_Sequence_Diagram: + mt = mt_Sequence_Diagram; + break; + + case Uml::lvt_State_Diagram: + mt = mt_State_Diagram; + break; + + case Uml::lvt_Activity_Diagram: + mt = mt_Activity_Diagram; + break; + + case Uml::lvt_Component_Diagram: + mt = mt_Component_Diagram; + break; + + case Uml::lvt_Deployment_Diagram: + mt = mt_Deployment_Diagram; + break; + + case Uml::lvt_EntityRelationship_Diagram: + mt = mt_EntityRelationship_Diagram; + break; + + case Uml::lvt_Actor: + mt = mt_Actor; + break; + + case Uml::lvt_UseCase: + mt = mt_UseCase; + break; + + case Uml::lvt_Class: + mt = mt_Class; + break; + + case Uml::lvt_Package: + mt = mt_Package; + break; + + case Uml::lvt_Subsystem: + mt = mt_Subsystem; + break; + + case Uml::lvt_Component: + mt = mt_Component; + break; + + case Uml::lvt_Node: + mt = mt_Node; + break; + + case Uml::lvt_Artifact: + mt = mt_Artifact; + break; + + case Uml::lvt_Interface: + mt = mt_Interface; + break; + + case Uml::lvt_Enum: + mt = mt_Enum; + break; + + case Uml::lvt_EnumLiteral: + mt = mt_EnumLiteral; + break; + + case Uml::lvt_Datatype: + mt = mt_Datatype; + break; + + case Uml::lvt_Attribute: + mt = mt_Attribute; + break; + + case Uml::lvt_Operation: + mt = mt_Operation; + break; + + case Uml::lvt_Template: + mt = mt_Template; + break; + + case Uml::lvt_Entity: + mt = mt_Entity; + break; + + case Uml::lvt_EntityAttribute: + mt = mt_EntityAttribute; + break; + + case Uml::lvt_Model: + mt = mt_Model; + break; + + default: ; + //break; + } + setupMenu(mt); +} + +//ListPopupMenu for a canvas widget +ListPopupMenu::ListPopupMenu(QWidget * parent, UMLWidget * object, + bool multi, bool unique) + : KPopupMenu(parent) +{ + init(); + //make the right menu for the type + //make menu for logical view + if(!object) + return; + Uml::Widget_Type type = object -> getBaseType(); + + if(multi) { + ClassifierWidget *c = NULL; + if (unique && (type == Uml::wt_Class || type == Uml::wt_Interface)) { + c = static_cast( object ); + makeMultiClassifierPopup(c); + } + setupColorSelection(object -> getUseFillColour()); + insertSeparator(); + insertStdItem(mt_Cut); + insertStdItem(mt_Copy); + insertStdItem(mt_Paste); + insertSeparator(); + insertItem(SmallIcon( "fonts" ), i18n( "Change Font..." ), + mt_Change_Font_Selection ); + insertItem(SmallIcon( "editdelete" ), i18n("Delete Selected Items"), + mt_Delete_Selection); + + // add this here and not above with the other stuff of the interface + // user might expect it at this position of the context menu + if (unique) { + if (type == Uml::wt_Interface) { + insertItem(i18n("Draw as Circle"), mt_DrawAsCircle_Selection); + setItemChecked( mt_DrawAsCircle_Selection, + c->getDrawAsCircle() ); + insertItem(i18n("Change into Class"), mt_ChangeToClass_Selection); + } else if (type == Uml::wt_Class) { + UMLClassifier *umlc = c->getClassifier(); + if (umlc->getAbstract() && umlc->attributes() == 0) + insertItem(i18n("Change into Interface"), mt_ChangeToInterface_Selection); + } + } + + if(m_pInsert) + connect(m_pInsert, SIGNAL(activated(int)), this, SIGNAL(activated(int))); + if(m_pShow) + connect(m_pShow, SIGNAL(activated(int)), this, SIGNAL(activated(int))); + if(m_pColor) + connect(m_pColor, SIGNAL(activated(int)), this, SIGNAL(activated(int))); + return; + } + + StateWidget *pState; + ActivityWidget *pActivity; + UMLView * pView = static_cast( parent ); + + switch(type) { + case Uml::wt_Actor: + case Uml::wt_UseCase: + setupColor(object -> getUseFillColour()); + insertStdItems(true, type); + insertStdItem(mt_Rename); + insertStdItem(mt_Change_Font); + insertStdItem(mt_Properties); + break; + + case Uml::wt_Class: + case Uml::wt_Interface: + makeClassifierPopup(static_cast(object)); + break; + + case Uml::wt_Enum: + m_pInsert = new KPopupMenu(this); + m_pInsert->insertItem(SmallIcon("source"), i18n("Enum Literal..."), mt_EnumLiteral); + insertFileNew(); + setupColor(object->getUseFillColour()); + insertStdItems(true, type); + insertStdItem(mt_Rename); + insertStdItem(mt_Change_Font); + insertStdItem(mt_Properties); + break; + + case Uml::wt_Entity: + m_pInsert = new KPopupMenu(this); + m_pInsert->insertItem(SmallIcon("source"), i18n("Entity Attribute..."), mt_EntityAttribute); + insertFileNew(); + setupColor(object->getUseFillColour()); + insertStdItems(true, type); + insertStdItem(mt_Rename); + insertStdItem(mt_Change_Font); + insertStdItem(mt_Properties); + break; + + case Uml::wt_Datatype: + case Uml::wt_Package: + case Uml::wt_Component: + case Uml::wt_Node: + case Uml::wt_Artifact: + setupColor(object->getUseFillColour()); + insertStdItems(false, type); + insertStdItem(mt_Rename); + insertStdItem(mt_Change_Font); + insertStdItem(mt_Properties); + break; + + case Uml::wt_Object: + //Used for sequence diagram and collaboration diagram widgets + setupColor( object->getUseFillColour() ); + if( pView->getType() == Uml::dt_Sequence ) { + insertSeparator(); + int tabUp = insertItem( SmallIcon( "1uparrow"), i18n("Move Up"), mt_Up); + insertItem( SmallIcon( "1downarrow"), i18n("Move Down"), mt_Down); + if ( !(static_cast(object))->canTabUp() ) { + setItemEnabled(tabUp, false); + } + } + insertStdItems(true, type); + insertItem(i18n("Rename Class..."), mt_Rename); + insertItem(i18n("Rename Object..."), mt_Rename_Object); + insertStdItem(mt_Change_Font); + insertStdItem(mt_Properties); + break; + + case Uml::wt_Message: + insertStdItems(false, type); + insertStdItem(mt_Change_Font); + insertItem(SmallIcon( "filenew"), i18n("New Operation..."), mt_Operation); + insertItem(i18n("Select Operation..."), mt_Select_Operation); + break; + + case Uml::wt_Note: + setupColor(object -> getUseFillColour()); + insertSeparator(); + insertStdItem(mt_Cut); + insertStdItem(mt_Copy); + insertStdItem(mt_Paste); + insertItem(SmallIcon( "editdelete"), i18n("Clear"), mt_Clear); + insertSeparator(); + insertItem(i18n("Change Text..."), mt_Rename); + insertStdItem(mt_Delete); + insertStdItem(mt_Change_Font); + break; + + case Uml::wt_Box: + insertStdItems(false, type); + insertStdItem(mt_Line_Color); + break; + + case Uml::wt_State: + pState = static_cast< StateWidget *>( object ); + if( pState -> getStateType() == StateWidget::Normal ) { + m_pInsert = new KPopupMenu(this); + m_pInsert -> insertItem(SmallIcon( "filenew"), i18n("Activity..."), mt_New_Activity); + insertFileNew(); + } + setupColor( object -> getUseFillColour() ); + insertStdItems(false, type); + if( pState -> getStateType() == StateWidget::Normal ) { + insertItem(i18n("Change State Name..."), mt_Rename); + insertStdItem(mt_Change_Font); + insertStdItem(mt_Properties); + } + break; + + case Uml::wt_ForkJoin: + { + ForkJoinWidget *pForkJoin = static_cast(object); + if (pForkJoin->getDrawVertical()) + insertItem(i18n("Flip Horizontal"), mt_Flip); + else + insertItem(i18n("Flip Vertical"), mt_Flip); + } + break; + + case Uml::wt_Activity: + pActivity = static_cast( object ); + if( pActivity -> getActivityType() == ActivityWidget::Normal ) + setupColor( object -> getUseFillColour() ); + insertStdItems(false, type); + if( pActivity -> getActivityType() == ActivityWidget::Normal ) { + insertItem(i18n("Change Activity Name..."), mt_Rename); + insertStdItem(mt_Change_Font); + insertStdItem(mt_Properties); + } + break; + + case Uml::wt_Text: + switch( (static_cast(object))->getRole() ) { + case Uml::tr_MultiB: + insertAssocItem(i18n("Change Multiplicity..."), mt_Rename_MultiB); + break; + case Uml::tr_MultiA: + insertAssocItem(i18n("Change Multiplicity..."), mt_Rename_MultiA); + break; + case Uml::tr_Name: + insertAssocItem(i18n("Change Name"), mt_Rename_Name); + break; + case Uml::tr_RoleAName: + insertAssocItem(i18n("Change Role A Name..."), mt_Rename_RoleAName); + break; + case Uml::tr_RoleBName: + insertAssocItem(i18n("Change Role B Name..."), mt_Rename_RoleBName); + break; + case Uml::tr_ChangeA: + case Uml::tr_ChangeB: + insertStdItem(mt_Change_Font); + insertStdItem(mt_Reset_Label_Positions); + insertStdItem(mt_Properties); + break; + + case Uml::tr_Coll_Message_Self: + case Uml::tr_Coll_Message: + case Uml::tr_Seq_Message_Self: + case Uml::tr_Seq_Message: + insertStdItem(mt_Change_Font); + insertItem(SmallIcon( "filenew"), i18n("New Operation..."), mt_Operation); + insertItem(i18n("Select Operation..."), mt_Select_Operation); + break; + + case Uml::tr_Floating: + default: + insertStdItems(false, type); + insertItem(i18n("Change Text..."), mt_Rename); + insertStdItem(mt_Change_Font); + break; + } + break; + default: + break; + }//end switch + + if(m_pInsert) + connect(m_pInsert, SIGNAL(activated(int)), this, SIGNAL(activated(int))); + if(m_pShow) + connect(m_pShow, SIGNAL(activated(int)), this, SIGNAL(activated(int))); + if(m_pColor) + connect(m_pColor, SIGNAL(activated(int)), this, SIGNAL(activated(int))); + + bool bCutState = UMLApp::app() -> getCutCopyState(); + setItemEnabled( mt_Cut, bCutState ); + setItemEnabled( mt_Copy, bCutState ); + setItemEnabled( mt_Paste, false ); +} + +ListPopupMenu::~ListPopupMenu() {} + +void ListPopupMenu::init() { + m_pInsert = 0; + m_pShow = 0; + m_pColor = 0; +} + +void ListPopupMenu::insertFileNew() { + insertItem(SmallIcon("filenew"), i18n("New"), m_pInsert); +} + +void ListPopupMenu::insertStdItem(Menu_Type m) +{ + switch (m) { + case mt_Properties: + insertItem(SmallIcon("info"), i18n("Properties"), mt_Properties); + break; + case mt_Rename: + insertItem(i18n("Rename..."), mt_Rename); + break; + case mt_Delete: + insertItem(SmallIcon("editdelete"), i18n("Delete"), mt_Delete); + break; + case mt_Cut: + insertItem(SmallIcon("editcut"), i18n("Cut"), mt_Cut); + break; + case mt_Copy: + insertItem(SmallIcon("editcopy"), i18n("Copy"), mt_Copy); + break; + case mt_Paste: + insertItem(SmallIcon("editpaste"), i18n("Paste"), mt_Paste); + break; + case mt_Change_Font: + insertItem(SmallIcon("fonts"), i18n("Change Font..."), mt_Change_Font); + break; + case mt_Line_Color: + insertItem(SmallIcon("color_line"), i18n("Line Color..."), mt_Line_Color); + break; + case mt_Expand_All: + insertItem(i18n("Expand All"), mt_Expand_All); + break; + case mt_Collapse_All: + insertItem(i18n("Collapse All"), mt_Collapse_All); + break; + case mt_Clone: + insertItem(i18n("Duplicate"), mt_Clone); + break; + case mt_Externalize_Folder: + insertItem(i18n("Externalize Folder..."), mt_Externalize_Folder); + break; + case mt_Internalize_Folder: + insertItem(i18n("Internalize Folder"), mt_Internalize_Folder); + break; + case mt_Import_Classes: + insertItem(BarIcon("source_cpp"), i18n("Import Classes..."), mt_Import_Classes); + break; + case mt_Package: + m_pInsert->insertItem(m_pixmap[pm_Package], i18n("Package"), mt_Package); + case mt_Subsystem: + m_pInsert->insertItem(m_pixmap[pm_Subsystem], i18n("Subsystem"), mt_Subsystem); + break; + case mt_Component: + m_pInsert->insertItem(m_pixmap[pm_Component], i18n("Component"), mt_Component); + break; + case mt_Artifact: + m_pInsert->insertItem(m_pixmap[pm_Artifact], i18n("Artifact"), mt_Artifact); + break; + case mt_Component_Diagram: + m_pInsert->insertItem(BarIcon("umbrello_diagram_component"), i18n("Component Diagram..."), + mt_Component_Diagram); + break; + case mt_Node: + m_pInsert->insertItem(m_pixmap[pm_Node], i18n("Node"), mt_Node); + break; + case mt_Deployment_Diagram: + m_pInsert->insertItem(Widget_Utils::iconSet(Uml::dt_Deployment), i18n("Deployment Diagram..."), + mt_Deployment_Diagram); + break; + case mt_Deployment_Folder: + case mt_Component_Folder: + case mt_UseCase_Folder: + case mt_EntityRelationship_Folder: + m_pInsert->insertItem(BarIcon("folder_new"), i18n("Folder"), m); + break; + case mt_Entity: + m_pInsert->insertItem(m_pixmap[pm_Entity], i18n("Entity"), mt_Entity); + break; + case mt_EntityRelationship_Diagram: + m_pInsert->insertItem(Widget_Utils::iconSet(Uml::dt_EntityRelationship), i18n("Entity Relationship Diagram..."), + mt_EntityRelationship_Diagram); + break; + case mt_Actor: + m_pInsert->insertItem(m_pixmap[pm_Actor], i18n("Actor"), mt_Actor); + break; + case mt_UseCase: + m_pInsert->insertItem(m_pixmap[pm_Usecase], i18n("Use Case"), mt_UseCase); + break; + case mt_UseCase_Diagram: + m_pInsert->insertItem(Widget_Utils::iconSet(Uml::dt_UseCase), i18n("Use Case Diagram..."), + mt_UseCase_Diagram); + break; + case mt_FloatText: + m_pInsert->insertItem(m_pixmap[pm_Text], i18n("Text Line..." ), mt_FloatText); + break; + case mt_Reset_Label_Positions: + insertItem(i18n("Reset Label Positions"), mt_Reset_Label_Positions); + break; + case mt_New_Parameter: + insertItem(SmallIcon("source"), i18n("New Parameter..."), mt_New_Parameter); + break; + case mt_New_Operation: + insertItem(SmallIcon("CVpublic_meth"),i18n("New Operation..."), mt_New_Operation); + break; + case mt_New_Attribute: + insertItem(SmallIcon("CVpublic_var"), i18n("New Attribute..."), mt_New_Attribute); + break; + case mt_New_Template: + insertItem(SmallIcon("source"), i18n("New Template..."), mt_New_Template); + break; + case mt_New_EnumLiteral: + insertItem(SmallIcon("source"), i18n("New Literal..."), mt_New_EnumLiteral); + break; + case mt_New_EntityAttribute: + insertItem(SmallIcon("source"), i18n("New Entity Attribute..."), mt_New_EntityAttribute); + break; + case mt_New_Activity: + m_pInsert->insertItem(SmallIcon("source"), i18n("Activity..."), mt_New_Activity); + break; + case mt_Export_Image: + insertItem(SmallIcon("image"), i18n("Export as Picture..."), mt_Export_Image); + break; + default: + kWarning() << "ListPopupMenu::insertStdItem called on unimplemented Menu_Type " << m << endl; + break; + } +} + +void ListPopupMenu::insertStdItems(bool insertLeadingSeparator /* = true */, + Uml::Widget_Type type /* = wt_UMLWidget */) +{ + if (insertLeadingSeparator) + insertSeparator(); + insertStdItem(mt_Cut); + insertStdItem(mt_Copy); + insertStdItem(mt_Paste); + insertSeparator(); + if (type == Uml::wt_UMLWidget) + insertStdItem(mt_Rename); + else if (Model_Utils::isCloneable(type)) + insertStdItem(mt_Clone); + insertStdItem(mt_Delete); +} + +void ListPopupMenu::insertContainerItems(bool folderAndDiagrams) { + if (folderAndDiagrams) + m_pInsert -> insertItem(BarIcon("folder_new"), i18n("Folder"), mt_Logical_Folder); + m_pInsert -> insertItem(m_pixmap[pm_Class], i18n("Class"), mt_Class); + m_pInsert -> insertItem(m_pixmap[pm_Interface], i18n("Interface"), mt_Interface); + m_pInsert -> insertItem(m_pixmap[pm_Datatype], i18n("Datatype"), mt_Datatype); + m_pInsert -> insertItem(m_pixmap[pm_Enum], i18n("Enum"), mt_Enum); + insertStdItem(mt_Package); + if (folderAndDiagrams) { + m_pInsert->insertItem(Widget_Utils::iconSet(Uml::dt_Class), i18n("Class Diagram..."), mt_Class_Diagram); + m_pInsert->insertItem(Widget_Utils::iconSet(Uml::dt_State), i18n("State Diagram..."), mt_State_Diagram); + m_pInsert->insertItem(Widget_Utils::iconSet(Uml::dt_Activity), i18n("Activity Diagram..."), mt_Activity_Diagram); + m_pInsert->insertItem(Widget_Utils::iconSet(Uml::dt_Sequence), i18n("Sequence Diagram..."), mt_Sequence_Diagram); + m_pInsert->insertItem(Widget_Utils::iconSet(Uml::dt_Collaboration), i18n("Collaboration Diagram..."), mt_Collaboration_Diagram); + } + insertFileNew(); +} + +void ListPopupMenu::insertAssocItem(const QString &label, Menu_Type mt) { + insertItem(label, mt); + insertStdItem(mt_Change_Font); + insertStdItem(mt_Reset_Label_Positions); + insertStdItem(mt_Properties); +} + +void ListPopupMenu::insertSubmodelAction() { + const Settings::OptionState& ostat = Settings::getOptionState(); + if (ostat.generalState.tabdiagrams) { + // Umbrello currently does not support External Folders + // in combination with Tabbed Diagrams. + // If you need external folders then disable the tabbed diagrams + // in the General Settings. + return; + } + UMLListView *listView = UMLApp::app()->getListView(); + UMLListViewItem *current = static_cast(listView->currentItem()); + UMLObject *o = current->getUMLObject(); + if (o == NULL) { + kError() << "ListPopupMenu::insertSubmodelAction: " + << current->getText() << " getUMLObject() returns NULL" << endl; + return; + } + UMLFolder *f = dynamic_cast(o); + if (f == NULL) { + kError() << "ListPopupMenu::insertSubmodelAction: " + << "current->getUMLObject (" << o->getName() << ") is not a Folder" << endl; + return; + } + QString submodelFile = f->getFolderFile(); + if (submodelFile.isEmpty()) + insertStdItem(mt_Externalize_Folder); + else + insertStdItem(mt_Internalize_Folder); +} + +void ListPopupMenu::makeMultiClassifierPopup(ClassifierWidget *c) +{ + Uml::Widget_Type type = c->getBaseType(); + ClassifierWidget *cls = NULL; + + m_pShow = new KPopupMenu(this); + m_pShow->setCheckable(true); + if (type == Uml::wt_Class) { + cls = static_cast(c); + m_pShow->insertItem( i18n("Attributes"), mt_Show_Attributes_Selection); + m_pShow->setItemChecked(mt_Show_Attributes_Selection, + cls->getShowAtts()); + } + m_pShow->insertItem(i18n("Operations"), mt_Show_Operations_Selection); + m_pShow->setItemChecked(mt_Show_Operations_Selection, c->getShowOps()); + m_pShow->insertItem(i18n("Public Only"), mt_Show_Public_Only_Selection); + m_pShow->setItemChecked(mt_Show_Public_Only_Selection, c->getShowPublicOnly()); + m_pShow->insertItem(i18n("Visibility"), mt_Visibility_Selection); + m_pShow->setItemChecked(mt_Visibility_Selection, c->getShowVisibility()); + m_pShow->insertItem(i18n("Operation Signature"), + mt_Show_Operation_Signature_Selection); + bool sig = (c->getShowOpSigs() == Uml::st_SigNoVis || + c->getShowOpSigs() == Uml::st_ShowSig); + m_pShow->setItemChecked(mt_Show_Operation_Signature_Selection, sig); + if (type == Uml::wt_Class) { + m_pShow->insertItem(i18n("Attribute Signature"), + mt_Show_Attribute_Signature_Selection); + sig = (cls->getShowAttSigs() == Uml::st_SigNoVis || + cls->getShowAttSigs() == Uml::st_ShowSig); + m_pShow->setItemChecked(mt_Show_Attribute_Signature_Selection, sig); + } + m_pShow->insertItem(i18n("Package"), mt_Show_Packages_Selection); + m_pShow->setItemChecked(mt_Show_Packages_Selection, c->getShowPackage()); + if (type == Uml::wt_Class) { + m_pShow->insertItem(i18n("Stereotype"), mt_Show_Stereotypes_Selection); + m_pShow->setItemChecked(mt_Show_Stereotypes_Selection, + cls->getShowStereotype()); + } + insertItem(i18n("Show"), m_pShow); +} + +void ListPopupMenu::makeClassifierPopup(ClassifierWidget *c) +{ + Uml::Widget_Type type = c->getBaseType(); + m_pInsert = new KPopupMenu(this); + if (type == Uml::wt_Class) + m_pInsert->insertItem(SmallIcon( "CVpublic_var" ), i18n("Attribute..."), mt_Attribute); + m_pInsert->insertItem( SmallIcon( "CVpublic_meth"), i18n("Operation..."), mt_Operation); + insertFileNew(); + + makeMultiClassifierPopup(c); + + setupColor(c->getUseFillColour()); + insertStdItems(true, type); + insertStdItem(mt_Rename); + insertStdItem(mt_Change_Font); + if (type == Uml::wt_Interface) { + insertItem(i18n("Draw as Circle"), mt_DrawAsCircle); + setItemChecked( mt_DrawAsCircle, c->getDrawAsCircle() ); + insertItem(i18n("Change into Class"), mt_ChangeToClass); + } else { + insertItem(i18n("Refactor"), mt_Refactoring); + insertItem(i18n("View Code"), mt_ViewCode); + UMLClassifier *umlc = c->getClassifier(); + if (umlc->getAbstract() && umlc->attributes() == 0) + insertItem(i18n("Change into Interface"), mt_ChangeToInterface); + } + insertStdItem(mt_Properties); +} + +void ListPopupMenu::setupColor(bool fc) +{ + m_pColor = new KPopupMenu(this); + m_pColor -> insertItem(SmallIcon( "color_line"), i18n("Line Color..."), mt_Line_Color); + m_pColor -> insertItem(SmallIcon( "color_fill"), i18n("Fill Color..."), mt_Fill_Color); + m_pColor -> insertItem( i18n("Use Fill Color"), mt_Use_Fill_Color); + + m_pColor -> setItemChecked(mt_Use_Fill_Color, fc); + insertItem(SmallIcon( "colorize"), i18n("Color"), m_pColor); +} + +void ListPopupMenu::setupColorSelection(bool fc) +{ + m_pColor = new KPopupMenu(this); + m_pColor -> insertItem(SmallIcon( "color_line"), i18n("Line Color..."), mt_Line_Color_Selection); + m_pColor -> insertItem(SmallIcon( "color_fill"), i18n("Fill Color..."), mt_Fill_Color_Selection); + m_pColor -> insertItem( i18n("Use Fill Color"), mt_Use_Fill_Color); + + m_pColor -> setItemChecked(mt_Use_Fill_Color, fc); + insertItem(SmallIcon( "colorize"), i18n("Color"), m_pColor); +} + +Uml::Diagram_Type ListPopupMenu::convert_MT_DT(Menu_Type mt) { + Uml::Diagram_Type type = Uml::dt_Undefined; + + switch(mt) { + case mt_UseCase_Diagram: + type = Uml::dt_UseCase; + break; + case mt_Class_Diagram: + type = Uml::dt_Class; + break; + case mt_Sequence_Diagram: + type = Uml::dt_Sequence; + break; + case mt_Collaboration_Diagram: + type = Uml::dt_Collaboration; + break; + case mt_State_Diagram: + type = Uml::dt_State; + break; + case mt_Activity_Diagram: + type = Uml::dt_Activity; + break; + case mt_Component_Diagram: + type = Uml::dt_Component; + break; + case mt_Deployment_Diagram: + type = Uml::dt_Deployment; + break; + case mt_EntityRelationship_Diagram: + type = Uml::dt_EntityRelationship; + break; + default: + break; + } + return type; +} + +Uml::Object_Type ListPopupMenu::convert_MT_OT(Menu_Type mt) { + Uml::Object_Type type = Uml::ot_UMLObject; + + switch(mt) { + case mt_UseCase: + type = Uml::ot_UseCase; + break; + case mt_Actor: + type = Uml::ot_Actor; + break; + case mt_Class: + type = Uml::ot_Class; + break; + case mt_Attribute: + type = Uml::ot_Attribute; + break; + case mt_EnumLiteral: + type = Uml::ot_EnumLiteral; + break; + case mt_EntityAttribute: + type = Uml::ot_EntityAttribute; + break; + case mt_Operation: + type = Uml::ot_Operation; + break; + default: + break; + } + return type; +} + +void ListPopupMenu::setupMenu(Menu_Type type, UMLView* view) { + //make the right menu for the type + //make menu for logical view + m_pInsert = 0; + + m_pShow = 0; + m_pColor = 0; + + KStandardDirs* dirs = KGlobal::dirs(); + QString dataDir = dirs->findResourceDir("data", "umbrello/pics/object.png"); + dataDir += "/umbrello/pics/"; + m_pixmap[pm_Class] .load(dataDir+"class.png", "PNG"); + m_pixmap[pm_Package] .load(dataDir+"package.png", "PNG"); + m_pixmap[pm_Interface] .load(dataDir+"interface.png", "PNG"); + m_pixmap[pm_Datatype] .load(dataDir+"datatype.png", "PNG"); + m_pixmap[pm_Enum] .load(dataDir+"enum.png", "PNG"); + m_pixmap[pm_Actor] .load(dataDir+"actor.png", "PNG"); + m_pixmap[pm_Usecase] .load(dataDir+"usecase.png", "PNG"); + m_pixmap[pm_InitialState].load(dataDir+"initial_state.png", "PNG"); + m_pixmap[pm_EndState] .load(dataDir+"end_state.png", "PNG"); + m_pixmap[pm_Branch] .load(dataDir+"branch.png", "PNG"); + m_pixmap[pm_Object] .load(dataDir+"object.png", "PNG"); + m_pixmap[pm_Component] .load(dataDir+"component.png", "PNG"); + m_pixmap[pm_Node] .load(dataDir+"node.png", "PNG"); + m_pixmap[pm_Entity] .load(dataDir+"entity.png", "PNG"); + m_pixmap[pm_Artifact] .load(dataDir+"artifact.png", "PNG"); + m_pixmap[pm_Text] .load(dataDir+"text.png", "PNG"); + m_pixmap[pm_Subsystem] .load(dataDir+"subsystem.png", "PNG"); + + switch(type) { + case mt_Logical_View: + m_pInsert = new KPopupMenu(this); + insertContainerItems(true); + insertSeparator(); + insertStdItem(mt_Paste); + insertSeparator(); + insertStdItem(mt_Import_Classes); + insertSeparator(); + insertStdItem(mt_Expand_All); + insertStdItem(mt_Collapse_All); + break; + + case mt_Component_View: + m_pInsert = new KPopupMenu(this); + insertStdItem(mt_Component_Folder); + insertStdItem(mt_Subsystem); + insertStdItem(mt_Component); + insertStdItem(mt_Artifact); + insertStdItem(mt_Component_Diagram); + insertFileNew(); + insertSeparator(); + insertStdItem(mt_Paste); + insertSeparator(); + insertStdItem(mt_Expand_All); + insertStdItem(mt_Collapse_All); + break; + + case mt_Deployment_View: + m_pInsert = new KPopupMenu(this); + insertStdItem(mt_Deployment_Folder); + insertStdItem(mt_Node); + insertStdItem(mt_Deployment_Diagram); + insertFileNew(); + insertSeparator(); + insertStdItem(mt_Paste); + insertSeparator(); + insertStdItem(mt_Expand_All); + insertStdItem(mt_Collapse_All); + break; + + case mt_EntityRelationship_Model: + m_pInsert = new KPopupMenu(this); + insertStdItem(mt_EntityRelationship_Folder); + insertStdItem(mt_Entity); + insertStdItem(mt_EntityRelationship_Diagram); + insertFileNew(); + insertSeparator(); + insertStdItem(mt_Paste); + insertSeparator(); + insertStdItem(mt_Expand_All); + insertStdItem(mt_Collapse_All); + break; + + case mt_UseCase_View: + m_pInsert = new KPopupMenu(this); + insertStdItem(mt_UseCase_Folder); + insertStdItem(mt_Actor); + insertStdItem(mt_UseCase); + insertStdItem(mt_UseCase_Diagram); + insertFileNew(); + insertSeparator(); + // insertStdItem(mt_Cut); + // insertStdItem(mt_Copy); + insertStdItem(mt_Paste); + insertSeparator(); + insertStdItem(mt_Expand_All); + insertStdItem(mt_Collapse_All); + break; + + case mt_Logical_Folder: + m_pInsert = new KPopupMenu(this); + insertContainerItems(true); + insertStdItems(); + insertStdItem(mt_Import_Classes); + insertSubmodelAction(); + insertSeparator(); + insertStdItem(mt_Expand_All); + insertStdItem(mt_Collapse_All); + break; + + case mt_Component_Folder: + m_pInsert = new KPopupMenu(this); + insertStdItem(mt_Component_Folder); + insertStdItem(mt_Subsystem); + insertStdItem(mt_Component); + insertStdItem(mt_Artifact); + insertStdItem(mt_Component_Diagram); + insertFileNew(); + insertStdItems(); + insertSubmodelAction(); + insertSeparator(); + insertStdItem(mt_Expand_All); + insertStdItem(mt_Collapse_All); + break; + + case mt_Deployment_Folder: + m_pInsert = new KPopupMenu(this); + insertStdItem(mt_Deployment_Folder); + insertStdItem(mt_Node); + insertStdItem(mt_Deployment_Diagram); + insertFileNew(); + insertStdItems(); + insertSubmodelAction(); + insertSeparator(); + insertStdItem(mt_Expand_All); + insertStdItem(mt_Collapse_All); + break; + + case mt_UseCase_Folder: + m_pInsert = new KPopupMenu(this); + insertStdItem(mt_UseCase_Folder); + insertStdItem(mt_Actor); + insertStdItem(mt_UseCase); + insertStdItem(mt_UseCase_Diagram); + insertFileNew(); + insertStdItems(); + insertSubmodelAction(); + insertSeparator(); + insertStdItem(mt_Expand_All); + insertStdItem(mt_Collapse_All); + break; + + case mt_EntityRelationship_Folder: + m_pInsert = new KPopupMenu(this); + insertStdItem(mt_EntityRelationship_Folder); + insertStdItem(mt_Entity); + insertStdItem(mt_EntityRelationship_Diagram); + insertFileNew(); + insertStdItems(); + insertSubmodelAction(); + insertSeparator(); + insertStdItem(mt_Expand_All); + insertStdItem(mt_Collapse_All); + break; + + case mt_UseCase_Diagram: + case mt_Sequence_Diagram: + case mt_Class_Diagram: + case mt_Collaboration_Diagram: + case mt_State_Diagram: + case mt_Activity_Diagram: + case mt_Component_Diagram: + case mt_Deployment_Diagram: + case mt_EntityRelationship_Diagram: + //don't insert standard items because cut/copy not currently + // possible with tabbed diagrams (it didn't work anyway) + //insertStdItems(false); + insertStdItem(mt_Rename); + insertStdItem(mt_Delete); + insertStdItem(mt_Export_Image); + insertStdItem(mt_Properties); + break; + + //FIXME a lot of these insertItem()s could be insertStandardItem()s + case mt_On_UseCase_Diagram: + m_pInsert = new KPopupMenu( this ); + m_pInsert -> insertItem(m_pixmap[pm_Actor], i18n( "Actor..." ), mt_Actor ); + m_pInsert -> insertItem(m_pixmap[pm_Usecase], i18n( "Use Case..."), mt_UseCase ); + insertStdItem(mt_FloatText ); + insertFileNew(); + insertSeparator(); + setupDiagramMenu(view); + break; + + case mt_On_Class_Diagram: + m_pInsert = new KPopupMenu( this ); + m_pInsert -> insertItem(m_pixmap[pm_Class], i18n("Class..."), mt_Class); + m_pInsert->insertItem(m_pixmap[pm_Interface], i18n("Interface..."), mt_Interface); + m_pInsert->insertItem(m_pixmap[pm_Datatype], i18n("Datatype..."), mt_Datatype); + m_pInsert->insertItem(m_pixmap[pm_Enum], i18n("Enum..."), mt_Enum); + m_pInsert -> insertItem(m_pixmap[pm_Package], i18n("Package..."), mt_Package); + insertStdItem(mt_FloatText); + insertFileNew(); + insertSeparator(); + setupDiagramMenu(view); + break; + + case mt_On_State_Diagram: + m_pInsert = new KPopupMenu( this ); + m_pInsert -> insertItem(m_pixmap[pm_InitialState], i18n("Initial State"), mt_Initial_State ); + m_pInsert -> insertItem(m_pixmap[pm_EndState], i18n("End State"), mt_End_State ); + m_pInsert -> insertItem(m_pixmap[pm_Usecase], i18n("State..."), mt_State ); + insertStdItem(mt_FloatText); + insertFileNew(); + insertSeparator(); + setupDiagramMenu(view); + break; + + case mt_On_Activity_Diagram: + m_pInsert = new KPopupMenu( this ); + m_pInsert -> insertItem(m_pixmap[pm_InitialState], i18n("Initial Activity"), mt_Initial_Activity ); + m_pInsert -> insertItem(m_pixmap[pm_EndState], i18n("End Activity"), mt_End_Activity ); + m_pInsert -> insertItem(m_pixmap[pm_Usecase], i18n("Activity..."), mt_Activity ); + m_pInsert -> insertItem(m_pixmap[pm_Branch], i18n("Branch/Merge"), mt_Branch ); + insertStdItem(mt_FloatText); + insertFileNew(); + insertSeparator(); + setupDiagramMenu(view); + break; + + case mt_On_Component_Diagram: + m_pInsert = new KPopupMenu(this); + m_pInsert->insertItem(m_pixmap[pm_Subsystem], i18n("Subsystem..."), mt_Subsystem); + m_pInsert->insertItem(m_pixmap[pm_Component], i18n("Component..."), mt_Component); + m_pInsert->insertItem(m_pixmap[pm_Artifact], i18n("Artifact..."), mt_Artifact); + insertFileNew(); + insertSeparator(); + setupDiagramMenu(view); + break; + + case mt_On_Deployment_Diagram: + m_pInsert = new KPopupMenu(this); + m_pInsert->insertItem(m_pixmap[pm_Node], i18n("Node..."), mt_Node); + insertFileNew(); + insertSeparator(); + setupDiagramMenu(view); + break; + + case mt_On_EntityRelationship_Diagram: + m_pInsert = new KPopupMenu(this); + m_pInsert->insertItem(m_pixmap[pm_Entity], i18n("Entity..."), mt_Entity); + insertFileNew(); + insertSeparator(); + setupDiagramMenu(view); + break; + + case mt_On_Sequence_Diagram: + case mt_On_Collaboration_Diagram: + m_pInsert = new KPopupMenu( this ); + m_pInsert -> insertItem(m_pixmap[pm_Object], i18n("Object..."), mt_Object); + insertStdItem(mt_FloatText); + insertFileNew(); + insertSeparator(); + setupDiagramMenu(view); + break; + + case mt_Class: + m_pInsert = new KPopupMenu(this); + m_pInsert -> insertItem(SmallIcon( "CVpublic_var"), i18n("Attribute"), mt_Attribute); + m_pInsert -> insertItem(SmallIcon( "CVpublic_meth"), i18n("Operation"), mt_Operation); + m_pInsert -> insertItem(SmallIcon("source"), i18n("Template"), mt_Template); + insertFileNew(); + insertStdItems(); + insertStdItem(mt_Properties); + break; + + case mt_Interface: + m_pInsert = new KPopupMenu(this); + m_pInsert->insertItem(SmallIcon("CVpublic_meth"), i18n("Operation"), mt_Operation); + m_pInsert -> insertItem(SmallIcon("source"), i18n("Template"), mt_Template); + insertFileNew(); + insertStdItems(); + insertStdItem(mt_Properties); + break; + + case mt_Package: + m_pInsert = new KPopupMenu(this); + insertContainerItems(false); + insertStdItems(); + insertStdItem(mt_Properties); + insertSeparator(); + insertStdItem(mt_Expand_All); + insertStdItem(mt_Collapse_All); + break; + + case mt_Subsystem: + m_pInsert = new KPopupMenu(this); + insertStdItem(mt_Subsystem); + insertStdItem(mt_Component); + insertStdItem(mt_Artifact); + insertFileNew(); + insertStdItems(); + insertStdItem(mt_Properties); + insertSeparator(); + insertStdItem(mt_Expand_All); + insertStdItem(mt_Collapse_All); + break; + + case mt_Component: + m_pInsert = new KPopupMenu(this); + insertStdItem(mt_Component); + insertStdItem(mt_Artifact); + insertFileNew(); + insertStdItems(); + insertStdItem(mt_Properties); + insertSeparator(); + insertStdItem(mt_Expand_All); + insertStdItem(mt_Collapse_All); + break; + + case mt_Entity: + m_pInsert = new KPopupMenu(this); + m_pInsert->insertItem(SmallIcon("source"), i18n("Entity Attribute..."), mt_EntityAttribute); + insertFileNew(); + insertStdItems(); + insertStdItem(mt_Properties); + break; + + case mt_EnumLiteral: + insertStdItems(false); + break; + + case mt_Enum: + m_pInsert = new KPopupMenu(this); + m_pInsert->insertItem(SmallIcon("source"), i18n("Enum Literal..."), mt_EnumLiteral); + insertFileNew(); + insertStdItems(); + insertStdItem(mt_Properties); + break; + + case mt_Datatype: + case mt_Node: + case mt_Artifact: + case mt_Actor: + case mt_UseCase: + case mt_Attribute: + case mt_EntityAttribute: + case mt_Operation: + case mt_Template: + insertStdItems(false); + insertStdItem(mt_Properties); + break; + + case mt_New_Parameter: + insertStdItem(mt_New_Parameter); + break; + + case mt_New_Operation: + insertStdItem(mt_New_Operation); + break; + + case mt_New_Attribute: + insertStdItem(mt_New_Attribute); + break; + + case mt_New_Template: + insertStdItem(mt_New_Template); + break; + + case mt_New_EnumLiteral: + insertStdItem(mt_New_EnumLiteral); + break; + + case mt_New_EntityAttribute: + insertStdItem(mt_New_EntityAttribute); + break; + + case mt_New_Activity: + m_pInsert = new KPopupMenu(this); + insertStdItem(mt_New_Activity); + insertFileNew(); + break; + + case mt_Activity_Selected: + m_pInsert = new KPopupMenu(this); + insertStdItem(mt_New_Activity); + insertFileNew(); + insertStdItem(mt_Rename); + insertStdItem(mt_Delete); + break; + + case mt_Parameter_Selected: + insertStdItem(mt_New_Parameter); + insertStdItem(mt_Rename); + insertStdItem(mt_Delete); + insertStdItem(mt_Properties); + break; + + case mt_Operation_Selected: + insertStdItem(mt_New_Operation); + insertStdItem(mt_Delete); + insertStdItem(mt_Properties); + break; + + case mt_Attribute_Selected: + insertStdItem(mt_New_Attribute); + insertStdItem(mt_Delete); + insertStdItem(mt_Properties); + break; + + case mt_Template_Selected: + insertItem(SmallIcon("source"),i18n("New Template..."), mt_New_Attribute); + insertStdItem(mt_Delete); + insertStdItem(mt_Properties); + break; + + case mt_EnumLiteral_Selected: + insertStdItem(mt_New_EnumLiteral); + insertStdItem(mt_Delete); + insertStdItem(mt_Properties); + break; + + case mt_EntityAttribute_Selected: + insertStdItem(mt_New_EntityAttribute); + insertStdItem(mt_Delete); + insertStdItem(mt_Properties); + break; + + case mt_Association_Selected: + insertStdItem(mt_Delete); + insertStdItem(mt_Line_Color); + insertStdItem(mt_Properties); + break; + + case mt_Anchor: + insertItem(SmallIcon( "editdelete"),i18n("Delete Anchor"), mt_Delete); + break; + + case mt_RoleNameA: + insertAssocItem(i18n("Change Role A Name..."), mt_Rename_RoleAName); + break; + + case mt_RoleNameB: + insertAssocItem(i18n("Change Role B Name..."), mt_Rename_RoleBName); + break; + + case mt_MultiB: + insertAssocItem(i18n("Change Multiplicity..."), mt_Rename_MultiB); + break; + + case mt_MultiA: + insertAssocItem(i18n("Change Multiplicity..."), mt_Rename_MultiA); + break; + + case mt_Name: + insertAssocItem(i18n("Change Name"), mt_Rename_Name); + break; + + case mt_FullAssociation: + insertStdItem(mt_Delete); + insertItem(i18n("Change Association Name..."), mt_Rename_Name); + insertItem(i18n("Change Role A Name..."), mt_Rename_RoleAName); + insertItem(i18n("Change Role B Name..."), mt_Rename_RoleBName); + insertStdItem(mt_Change_Font); + insertStdItem(mt_Reset_Label_Positions); + insertStdItem(mt_Line_Color); + insertStdItem(mt_Properties); + break; + + case mt_AttributeAssociation: + insertStdItem(mt_Delete); // @todo add more items + break; + + case mt_Collaboration_Message: + // insertStdItem(mt_Cut); + // insertStdItem(mt_Copy); + // insertStdItem(mt_Paste); + // insertSeparator(); + insertStdItem(mt_Delete); + insertStdItem(mt_Change_Font); + insertStdItem(mt_New_Operation); + insertItem(i18n("Select Operation..."), mt_Select_Operation); + break; + + case mt_Model: + insertItem(i18n("Rename..."), mt_Model); + break; + + default: + insertStdItem(mt_Expand_All); + insertStdItem(mt_Collapse_All); + break; + }//end switch + + if( view ) { + bool bCutState = UMLApp::app() -> getCutCopyState(); + setItemEnabled( mt_Undo, UMLApp::app()->getUndoEnabled() ); + setItemEnabled( mt_Redo, UMLApp::app()->getRedoEnabled() ); + setItemEnabled( mt_Cut, bCutState ); + setItemEnabled( mt_Copy, bCutState ); + setItemEnabled( mt_Paste, UMLApp::app() -> getPasteState() ); + } + if(m_pInsert) + connect(m_pInsert, SIGNAL(activated(int)), this, SIGNAL(activated(int))); + if(m_pShow) + connect(m_pShow, SIGNAL(activated(int)), this, SIGNAL(activated(int))); + if(m_pColor) + connect(m_pColor, SIGNAL(activated(int)), this, SIGNAL(activated(int))); +} + +void ListPopupMenu::setupDiagramMenu(UMLView* view) { + insertItem(SmallIcon("undo"), i18n("Undo"), mt_Undo); + insertItem(SmallIcon("redo"), i18n("Redo"), mt_Redo); + insertSeparator(); + insertStdItem(mt_Cut); + insertStdItem(mt_Copy); + insertStdItem(mt_Paste); + insertSeparator(); + insertItem(SmallIcon("editclear"), i18n("Clear Diagram"), mt_Clear); + insertStdItem(mt_Export_Image); + insertSeparator(); + insertItem(i18n("Snap to Grid"), mt_SnapToGrid); + setItemChecked(mt_SnapToGrid, view->getSnapToGrid() ); + insertItem(i18n("Show Grid"), mt_ShowSnapGrid ); + setItemChecked(mt_ShowSnapGrid, view->getShowSnapGrid() ); + insertStdItem(mt_Properties); +} + diff --git a/umbrello/umbrello/listpopupmenu.h b/umbrello/umbrello/listpopupmenu.h new file mode 100644 index 00000000..58d779ab --- /dev/null +++ b/umbrello/umbrello/listpopupmenu.h @@ -0,0 +1,330 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef LISTPOPUPMENU_H +#define LISTPOPUPMENU_H + +#include +#include "umlnamespace.h" + +class UMLView; +class UMLWidget; +class ClassifierWidget; + +/** + * A popup menu that depending on what type it is set to will + * display a different menu. + * + * @short Displays a popup menu. + * @author Paul Hensgen + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + +class ListPopupMenu : public KPopupMenu { +public: + + /// This type hosts all possible menu types. + enum Menu_Type + { + mt_Model, + mt_Logical_View, + mt_UseCase_View, + mt_Component_View, + mt_Deployment_View, + mt_EntityRelationship_Model, + mt_UseCase_Diagram, + mt_Sequence_Diagram, + mt_Class_Diagram, + mt_Collaboration_Diagram, + mt_State_Diagram, + mt_Activity_Diagram, + mt_Component_Diagram, + mt_Deployment_Diagram, + mt_EntityRelationship_Diagram, + mt_On_UseCase_Diagram, + mt_On_Sequence_Diagram, + mt_On_Class_Diagram, + mt_On_Collaboration_Diagram, + mt_On_State_Diagram, + mt_On_Activity_Diagram, + mt_On_Component_Diagram, + mt_On_Deployment_Diagram, + mt_On_EntityRelationship_Diagram, + mt_Logical_Folder, + mt_UseCase_Folder, + mt_Component_Folder, + mt_Deployment_Folder, + mt_EntityRelationship_Folder, + mt_Class, + mt_Package, + mt_Subsystem, + mt_Component, + mt_Node, + mt_Artifact, + mt_Interface, + mt_Enum, + mt_Entity, + mt_Datatype, + mt_Actor, + mt_UseCase, + mt_Attribute, + mt_EntityAttribute, + mt_EnumLiteral, + mt_Object, + mt_Initial_State, + mt_End_State, + mt_State, + mt_Activity, + mt_Initial_Activity, + mt_End_Activity, + mt_Operation, + mt_Template, + mt_New_Parameter, + mt_New_Operation, + mt_New_Attribute, + mt_New_Template, + mt_New_EnumLiteral, + mt_New_EntityAttribute, + mt_Parameter_Selected, + mt_Operation_Selected, + mt_Attribute_Selected, + mt_Template_Selected, + mt_EnumLiteral_Selected, + mt_EntityAttribute_Selected, + mt_Association_Selected, // Association without role names + mt_Show_Attributes, + mt_Show_Attributes_Selection, //SHOWATTS, multiple items + mt_Show_Operations, + mt_Show_Operations_Selection, //SHOWOPS, multiple items + mt_Show_Packages, + mt_Show_Packages_Selection, //SHOWPACKAGE, multiple items + mt_Show_Stereotypes, + mt_Show_Stereotypes_Selection, //SHOWSTEREOTYPE, multiple items + mt_Visibility, + mt_Visibility_Selection, //SCOPE, multiple items + mt_DrawAsCircle, + mt_DrawAsCircle_Selection, //DRAWASCIRCLE, multiple items + mt_ChangeToClass, + mt_ChangeToClass_Selection, + mt_ChangeToInterface, + mt_ChangeToInterface_Selection, + mt_Rename_Object, + mt_Select_Operation, + mt_Anchor, + mt_Properties, + mt_Rename, + mt_Delete, + mt_Export_Image, + mt_Import_Classes, + mt_Sequence_Number, + mt_Cut, + mt_Copy, + mt_Paste, + mt_Clear, + mt_Redo, + mt_Undo, + mt_Link_Docs, + mt_Show_Operation_Signature, + mt_Show_Operation_Signature_Selection, //SHOWOPSIG, multiple items + mt_Show_Attribute_Signature, + mt_Show_Attribute_Signature_Selection, //SHOWATTSIG, multiple items + mt_Message_Text, + mt_Collaboration_Message, + mt_FloatText, + mt_MultiA, + mt_MultiB, + mt_Name, //Association name + mt_FullAssociation, // Association with role names + mt_AttributeAssociation, // Rendering of an attribute as an association + mt_RoleNameA, + mt_RoleNameB, + mt_Delete_Selection, + mt_Reset_Label_Positions, + mt_Line_Color, + mt_Line_Color_Selection, //LINECOLOR, multiple items + mt_Fill_Color, + mt_Fill_Color_Selection, //FILLCOLOR, multiple items + mt_Use_Fill_Color, + mt_Default_Properties, + mt_Rename_MultiA, + mt_Rename_MultiB, + mt_Rename_Name, + mt_Rename_RoleAName, + mt_Rename_RoleBName, + mt_Change_Font, + mt_Change_Font_Selection, + mt_SnapToGrid, + mt_ShowSnapGrid, + mt_Activity_Selected, + mt_New_Activity, + mt_Up, + mt_Down, + mt_Branch, + mt_Flip, + + mt_Expand_All, //Expand all items in the list + mt_Collapse_All, //Collapse all items in the list + + mt_Refactoring, + mt_ViewCode, // view code document contents + mt_Clone, // Create a deep copy of the object. + mt_Show_Public_Only, // (not currently used) + mt_Show_Public_Only_Selection, // Show public operations/attributes only. + mt_Externalize_Folder, // Mark folder for saving as separate submodel + mt_Internalize_Folder, // Reintegrate separate submodel into main model + + mt_Undefined = - 1 + }; + + /** + * Constructs the popup menu for a diagram + * + * @param parent The parent to ListPopupMenu. + * @param type The type of menu to display. + * @param view The UMLView in which this ListPopupMenu is going to be displayed + */ + explicit ListPopupMenu(QWidget* parent, Menu_Type type = mt_Undefined, UMLView* view = 0); + + /** + * Constructs the popup menu for a list view item. + * + * @param parent The parent to ListPopupMenu. + * @param type The type of menu to display. + */ + ListPopupMenu(QWidget* parent, Uml::ListView_Type type); + + /** + * Constructs the popup menu for a canvas widget. + * + * @param parent The parent to ListPopupMenu. + * @param object The UMLWidget to represent a menu for. + * @param multi True if multiple items are selected. + * @param unique True if multiple selected items all have + * the same type (e.g. Class, Interface) + */ + ListPopupMenu(QWidget* parent, UMLWidget* object, bool multi = false, bool unique = false); + + /** + * Standard deconstructor. + */ + ~ListPopupMenu(); + + /** + * Utility: Convert a Menu_Type value to an Object_Type value. + */ + static Uml::Object_Type convert_MT_OT(Menu_Type mt); + + /** + * Utility: Convert a Menu_Type value to a Diagram_Type value. + */ + static Uml::Diagram_Type convert_MT_DT(Menu_Type mt); + +private: + /** + * Basic initialization - common to all constructors. + */ + void init(); + + /** + * Shortcut for inserting a "File->New" choice. + */ + void insertFileNew(); + + /** + * Shortcut for the most frequently used insertItem() calls. + * + * @param m The Menu_Type for which to insert a menu item. + */ + void insertStdItem(Menu_Type m); + + /** + * Shortcut for the most frequently used insertStdItem() calls. + * + * @param insertLeadingSeparator Set this true if the group shall + * start with a separator. + * @param type The Widget_Type for which to insert the menu items. + * If no argument is supplied then a Rename item will be + * included. + */ + void insertStdItems(bool insertLeadingSeparator = true, + Uml::Widget_Type type = Uml::wt_UMLWidget); + + /** + * Shortcut for inserting standard model items (Class, Interface, + * Datatype, Enum, Package) as well as diagram choices. + * + * @param folderAndDiagrams Set this true if folders and diagram + * types shall be included as choices. + */ + void insertContainerItems(bool folderAndDiagrams); + + /** + * Inserts a menu item for an association related text + * (such as name, role, multiplicity etc.) + * + * @param label The menu text. + * @param mt The menu type. + */ + void insertAssocItem(const QString &label, Menu_Type mt); + + /** + * Inserts a menu item for externalization/de-externalization + * of a folder. + */ + void insertSubmodelAction(); + + /** + * Creates a popup menu for a multiple selection of class and + * interface widgets. + */ + void makeMultiClassifierPopup(ClassifierWidget *c); + + /** + * Creates a popup menu for a single class or interface widgets. + */ + void makeClassifierPopup(ClassifierWidget *c); + + /** + * Shortcut for commonly used menu initializations. + * + * @param type The Menu_Type for which to set up the menu. + * @param view The UMLView parent of the menu. + */ + void setupMenu(Menu_Type type, UMLView * view = 0); + + enum PixMap_Type { + pm_Class, + pm_Package, + pm_Interface, + pm_Datatype, + pm_Enum, + pm_Actor, + pm_Usecase, + pm_InitialState, + pm_EndState, + pm_Branch, + pm_Object, + pm_Component, + pm_Node, + pm_Artifact, + pm_Text, + pm_Entity, + pm_Subsystem, + pm_NUMBER_OF_PIXMAPS + }; + QPixmap m_pixmap[pm_NUMBER_OF_PIXMAPS]; + KPopupMenu * m_pInsert, * m_pShow, * m_pColor; + void setupColor(bool fc); + void setupColorSelection(bool fc); + void setupDiagramMenu(UMLView* view); +}; + +#endif diff --git a/umbrello/umbrello/main.cpp b/umbrello/umbrello/main.cpp new file mode 100644 index 00000000..faba82d3 --- /dev/null +++ b/umbrello/umbrello/main.cpp @@ -0,0 +1,208 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#include + +// kde includes +#include +#include +#include +#include +#include +#include +#include +#include + +// app includes +#include "uml.h" +#include "version.h" +#include "umldoc.h" +#include "cmdlineexportallviewsevent.h" +#include "kstartuplogo.h" + +static const char description[] = + I18N_NOOP("Umbrello UML Modeller"); +// INSERT A DESCRIPTION FOR YOUR APPLICATION HERE + +/** + * @todo Add options to use the documentation generators from command line. + */ +static KCmdLineOptions options[] = + { + { "+[File]", I18N_NOOP("File to open"), 0 }, + { "export ", I18N_NOOP("export diagrams to extension and exit"), 0}, + { "directory ", I18N_NOOP("the local directory to save the exported diagrams in"), I18N_NOOP("the directory of the file")}, + { "use-folders", I18N_NOOP("keep the tree structure used to store the views in the document in the target directory"), 0}, + // INSERT YOUR COMMANDLINE OPTIONS HERE + KCmdLineLastOption + }; + +/** + * Determines if the application GUI should be shown based on command line arguments. + * + * @param args The command line arguments given. + * @return True if the GUI should be shown, false otherwise. + */ +bool getShowGUI(KCmdLineArgs *args); + +/** + * Creates, shows and returns the startup logo for the application if it should be shown, + * else returns null pointer. + * The startup logo is shown if it is configured to be shown and also the GUI must be shown. + * + * @param cfg The application configuration. + * @param showGUI If the GUI should be shown. + * @return The startup logo for the application, or a null pointer if it shouldn't be shown. + */ +KStartupLogo* showStartupLogo(KConfig* cfg, bool showGUI); + +/** + * Initializes the document used by the application. + * If a file was specified in command line arguments, opens that file. Else, it + * opens the last opened file, or a new file if there isn't any "last file used" + * in the configuration. + * + * @param args The command line arguments given. + * @param cfg The application configuration. + */ +void initDocument(KCmdLineArgs *args, KConfig* cfg); + +/** + * Export all the views in the document using the command line args set by the user. + * Errors that occurred while exporting, if any, are shown using kError(). + * + * @param args The command line arguments given. + * @param exportOpt A list containing all the "export" arguments given. + */ +void exportAllViews(KCmdLineArgs *args, const QCStringList &exportOpt); + +extern "C" int flushEvents() { + kapp->processEvents(); + return 0; +} + +int main(int argc, char *argv[]) { + KAboutData aboutData( "umbrello", I18N_NOOP("Umbrello UML Modeller"), + UMBRELLO_VERSION, description, KAboutData::License_GPL, + I18N_NOOP("(c) 2001 Paul Hensgen, (c) 2002-2006 Umbrello UML Modeller Authors"), 0, + "http://uml.sf.net/"); + aboutData.addAuthor("Paul Hensgen",0, "phensgen@users.sourceforge.net"); + aboutData.addAuthor(I18N_NOOP("Umbrello UML Modeller Authors"), 0, "uml-devel@lists.sourceforge.net"); + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions( options ); // Add our own options. + + KApplication app; + if( app.isRestored() ) { + RESTORE( UMLApp ); + } else { + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + bool showGUI = getShowGUI(args); + + UMLApp *uml = new UMLApp(); + flushEvents(); + KConfig * cfg = app.config(); + + KStartupLogo* startLogo = showStartupLogo(cfg, showGUI); + + if (showGUI) { + uml->show(); + } + uml->initGenerator(); + + //show tips if wanted + if (showGUI) { + KTipDialog::showTip(); + } + + initDocument(args, cfg); + + // export option + QCStringList exportOpt = args->getOptionList("export"); + if (exportOpt.size() > 0) { + exportAllViews(args, exportOpt); + } + + if ( showGUI && (startLogo != 0L) && !startLogo->isHidden() ) { + startLogo->raise(); + } + } + return app.exec(); +} + +bool getShowGUI(KCmdLineArgs *args) { + if (args->getOptionList("export").size() > 0) { + return false; + } + + return true; +} + +KStartupLogo* showStartupLogo(KConfig* cfg, bool showGUI) { + KStartupLogo* startLogo = 0L; + + cfg->setGroup( "General Options" ); + bool showLogo = cfg->readBoolEntry( "logo", true ); + if (showGUI && showLogo) { +#if KDE_IS_VERSION(3,3,90) + startLogo = new KStartupLogo(0); + startLogo->setHideEnabled(true); + KWin::setMainWindow(startLogo, UMLApp::app()->winId()); +#else + startLogo = new KStartupLogo(UMLApp::app()); + startLogo->setHideEnabled(true); +#endif + KWin::setState(startLogo->winId(), NET::KeepAbove); + startLogo->show(); + QApplication::flushX(); + } + + return startLogo; +} + +void initDocument(KCmdLineArgs *args, KConfig* cfg) { + if ( args -> count() ) { + UMLApp::app()->openDocumentFile( args->url( 0 ) ); + } else { + cfg->setGroup( "General Options" ); + bool last = cfg->readBoolEntry( "loadlast", false ); + QString file = cfg->readPathEntry( "lastFile" ); + if( last && !file.isEmpty() ) { + UMLApp::app()->openDocumentFile( KURL( file ) ); + } else { + UMLApp::app()->newDocument(); + } + } +} + +void exportAllViews(KCmdLineArgs *args, const QCStringList &exportOpt) { + QString extension(exportOpt.last()); + kDebug() << "extension: " << extension << endl; + + // export to the specified directory, or the directory where the file is saved + // if no directory was specified + KURL directory; + QCStringList directoryOpt = args->getOptionList("directory"); + if (directoryOpt.size() > 0) { + directory = KCmdLineArgs::makeURL(directoryOpt.last()); + } else { + directory = KURL(UMLApp::app()->getDocument()->URL().directory()); + } + + bool useFolders = args->isSet("use-folders"); + + kDebug() << "directory: " << directory.prettyURL() << endl; + + // the event is posted so when the QT loop begins it's processed. UMLApp process this event executing + // the method it provides for exporting the views. Once all the views were exported, a quit event + // is sent and the app finishes without user interaction + kapp->postEvent(UMLApp::app(), new CmdLineExportAllViewsEvent(extension, directory, useFolders)); +} + diff --git a/umbrello/umbrello/messagewidget.cpp b/umbrello/umbrello/messagewidget.cpp new file mode 100644 index 00000000..3cefc99d --- /dev/null +++ b/umbrello/umbrello/messagewidget.cpp @@ -0,0 +1,792 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// onw header +#include "messagewidget.h" + +//qt includes +#include +//kde includes +#include +#include +//app includes +#include "messagewidgetcontroller.h" +#include "floatingtextwidget.h" +#include "objectwidget.h" +#include "classifier.h" +#include "operation.h" +#include "umlview.h" +#include "umldoc.h" +#include "uml.h" +#include "uniqueid.h" +#include "listpopupmenu.h" + +MessageWidget::MessageWidget(UMLView * view, ObjectWidget* a, ObjectWidget* b, + int y, Uml::Sequence_Message_Type sequenceMessageType, + Uml::IDType id /* = Uml::id_None */) + : UMLWidget(view, id, new MessageWidgetController(this)) { + init(); + m_pOw[Uml::A] = a; + m_pOw[Uml::B] = b; + m_nY = y; + m_sequenceMessageType = sequenceMessageType; + if (m_sequenceMessageType == Uml::sequence_message_creation) { + y -= m_pOw[Uml::B]->getHeight() / 2; + m_pOw[Uml::B]->setY(y); + } + updateResizability(); + calculateWidget(); + y = y < getMinY() ? getMinY() : y; + y = y > getMaxY() ? getMaxY() : y; + m_nY = y; + + this->activate(); +} + +MessageWidget::MessageWidget(UMLView * view, Uml::Sequence_Message_Type seqMsgType, Uml::IDType id) + : UMLWidget(view, id, new MessageWidgetController(this)) { + init(); + m_sequenceMessageType = seqMsgType; +} + +void MessageWidget::init() { + UMLWidget::setBaseType(Uml::wt_Message); + m_bIgnoreSnapToGrid = true; + m_bIgnoreSnapComponentSizeToGrid = true; + m_pOw[Uml::A] = m_pOw[Uml::B] = NULL; + m_pFText = NULL; + m_nY = 0; + setVisible(true); +} + +MessageWidget::~MessageWidget() { +} + +void MessageWidget::updateResizability() { + if (m_sequenceMessageType == Uml::sequence_message_synchronous || + m_pOw[Uml::A] == m_pOw[Uml::B]) + UMLWidget::m_bResizable = true; + else + UMLWidget::m_bResizable = false; +} + +void MessageWidget::draw(QPainter& p, int offsetX, int offsetY) { + if(!m_pOw[Uml::A] || !m_pOw[Uml::B]) { + return; + } + UMLWidget::setPen(p); + if (m_sequenceMessageType == Uml::sequence_message_synchronous) { + drawSynchronous(p, offsetX, offsetY); + } else if (m_sequenceMessageType == Uml::sequence_message_asynchronous) { + drawAsynchronous(p, offsetX, offsetY); + } else if (m_sequenceMessageType == Uml::sequence_message_creation) { + drawCreation(p, offsetX, offsetY); + } else { + kWarning() << "Unknown message type" << endl; + } +} + +void MessageWidget::drawSolidArrowhead(QPainter& p, int x, int y, Qt::ArrowType direction) { + int arrowheadExtentX = 4; + if (direction == Qt::RightArrow) { + arrowheadExtentX = -arrowheadExtentX; + } + QPointArray points; + points.putPoints(0, 3, x, y, x + arrowheadExtentX, y - 3, x + arrowheadExtentX, y + 3); + p.setBrush( QBrush(p.pen().color()) ); + p.drawPolygon(points); +} + +void MessageWidget::drawArrow(QPainter& p, int x, int y, int w, + Qt::ArrowType direction, bool useDottedLine /* = false */) { + int arrowheadStartX = x; + int arrowheadExtentX = 4; + if (direction == Qt::RightArrow) { + arrowheadStartX += w; + arrowheadExtentX = -arrowheadExtentX; + } + // draw upper half of arrowhead + p.drawLine(arrowheadStartX, y, arrowheadStartX + arrowheadExtentX, y - 3); + // draw lower half of arrowhead + p.drawLine(arrowheadStartX, y, arrowheadStartX + arrowheadExtentX, y + 3); + // draw arrow line + if (useDottedLine) { + QPen pen = p.pen(); + pen.setStyle(Qt::DotLine); + p.setPen(pen); + } + p.drawLine(x, y, x + w, y); +} + +void MessageWidget::drawSynchronous(QPainter& p, int offsetX, int offsetY) { + int x1 = m_pOw[Uml::A]->getX(); + int x2 = m_pOw[Uml::B]->getX(); + int w = getWidth() - 1; + int h = getHeight(); + + bool messageOverlaps = m_pOw[Uml::A] -> messageOverlap( getY(), this ); + + if(m_pOw[Uml::A] == m_pOw[Uml::B]) { + p.fillRect( offsetX, offsetY, 17, h, QBrush(Qt::white) ); //box + p.drawRect(offsetX, offsetY, 17, h); //box + offsetX += 17; + w -= 17; + offsetY += 3; + const int lowerLineY = offsetY + h - 6; + // draw upper line segment (leaving the life line) + p.drawLine(offsetX, offsetY, offsetX + w, offsetY); + // draw line segment parallel to (and at the right of) the life line + p.drawLine(offsetX + w, offsetY, offsetX + w, lowerLineY); + // draw lower line segment (back to the life line) + drawArrow(p, offsetX, lowerLineY, w, Qt::LeftArrow); + offsetX -= 17; + offsetY -= 3; + } else if(x1 < x2) { + if (messageOverlaps) { + offsetX += 8; + w -= 8; + } + QPen pen = p.pen(); + int startX = offsetX + w - 16; + p.fillRect(startX, offsetY, 17, h, QBrush(Qt::white)); //box + p.drawRect(startX, offsetY, 17, h); //box + p.drawLine(offsetX, offsetY + 4, startX, offsetY + 4); //arrow line + drawSolidArrowhead(p, startX - 1, offsetY + 4, Qt::RightArrow); + drawArrow(p, offsetX, offsetY + h - 3, w - 16, Qt::LeftArrow, true); // return arrow + if (messageOverlaps) { + offsetX -= 8; //reset for drawSelected() + } + } else { + if (messageOverlaps) { + w -=8; + } + QPen pen = p.pen(); + p.fillRect( offsetX, offsetY, 17, h, QBrush(Qt::white) ); //box + p.drawRect(offsetX, offsetY, 17, h); //box + p.drawLine(offsetX + 18, offsetY + 4, offsetX + w, offsetY + 4); //arrow line + drawSolidArrowhead(p, offsetX + 17, offsetY + 4, Qt::LeftArrow); + drawArrow(p, offsetX + 18, offsetY + h - 3, w - 18, Qt::RightArrow, true); // return arrow + } + + if(m_bSelected) { + drawSelected(&p, offsetX, offsetY); + } +} + +void MessageWidget::drawAsynchronous(QPainter& p, int offsetX, int offsetY) { + int x1 = m_pOw[Uml::A]->getX(); + int x2 = m_pOw[Uml::B]->getX(); + int w = getWidth() - 1; + int h = getHeight() - 1; + bool messageOverlapsA = m_pOw[Uml::A] -> messageOverlap( getY(), this ); + //bool messageOverlapsB = m_pOw[Uml::B] -> messageOverlap( getY(), this ); + + if(m_pOw[Uml::A] == m_pOw[Uml::B]) { + if (messageOverlapsA) { + offsetX += 7; + w -= 7; + } + const int lowerLineY = offsetY + h - 3; + // draw upper line segment (leaving the life line) + p.drawLine(offsetX, offsetY, offsetX + w, offsetY); + // draw line segment parallel to (and at the right of) the life line + p.drawLine(offsetX + w, offsetY, offsetX + w, lowerLineY); + // draw lower line segment (back to the life line) + drawArrow(p, offsetX, lowerLineY, w, Qt::LeftArrow); + if (messageOverlapsA) { + offsetX -= 7; //reset for drawSelected() + } + } else if(x1 < x2) { + if (messageOverlapsA) { + offsetX += 7; + w -= 7; + } + drawArrow(p, offsetX, offsetY + 4, w, Qt::RightArrow); + if (messageOverlapsA) { + offsetX -= 7; + } + } else { + if (messageOverlapsA) { + w -= 7; + } + drawArrow(p, offsetX, offsetY + 4, w, Qt::LeftArrow); + } + + if (m_bSelected) + drawSelected(&p, offsetX, offsetY); +} + +void MessageWidget::drawCreation(QPainter& p, int offsetX, int offsetY) { + int x1 = m_pOw[Uml::A]->getX(); + int x2 = m_pOw[Uml::B]->getX(); + int w = getWidth() - 1; + //int h = getHeight() - 1; + bool messageOverlapsA = m_pOw[Uml::A] -> messageOverlap( getY(), this ); + //bool messageOverlapsB = m_pOw[Uml::B] -> messageOverlap( getY(), this ); + + const int lineY = offsetY + 4; + if (x1 < x2) { + if (messageOverlapsA) { + offsetX += 7; + w -= 7; + } + drawArrow(p, offsetX, lineY, w, Qt::RightArrow); + if (messageOverlapsA) { + offsetX -= 7; + } + } else { + if (messageOverlapsA) { + w -= 7; + } + drawArrow(p, offsetX, lineY, w, Qt::LeftArrow); + } + + if (m_bSelected) + drawSelected(&p, offsetX, offsetY); +} + +int MessageWidget::onWidget(const QPoint & p) { + if (m_sequenceMessageType != Uml::sequence_message_synchronous) { + return UMLWidget::onWidget(p); + } + // Synchronous message: + // Consists of top arrow (call) and bottom arrow (return.) + if (p.x() < getX() || p.x() > getX() + getWidth()) + return 0; + const int tolerance = 5; // pixels + const int pY = p.y(); + const int topArrowY = getY() + 3; + const int bottomArrowY = getY() + getHeight() - 3; + if (pY < topArrowY - tolerance || pY > bottomArrowY + tolerance) + return 0; + if (getHeight() <= 2 * tolerance) + return 1; + if (pY > topArrowY + tolerance && pY < bottomArrowY - tolerance) + return 0; + return 1; +} + +void MessageWidget::setTextPosition() { + if (m_pFText == NULL) { + kDebug() << "MessageWidget::setTextPosition: m_pFText is NULL" + << endl; + return; + } + if (m_pFText->getDisplayText().isEmpty()) { + return; + } + m_pFText->updateComponentSize(); + int ftX = constrainX(m_pFText->getX(), m_pFText->getWidth(), m_pFText->getRole()); + int ftY = getY() - m_pFText->getHeight(); + m_pFText->setX( ftX ); + m_pFText->setY( ftY ); +} + +int MessageWidget::constrainX(int textX, int textWidth, Uml::Text_Role tr) { + int result = textX; + const int minTextX = getX() + 5; + if (textX < minTextX || tr == Uml::tr_Seq_Message_Self) { + result = minTextX; + } else { + ObjectWidget *objectAtRight = NULL; + if (m_pOw[Uml::B]->getX() > m_pOw[Uml::A]->getX()) + objectAtRight = m_pOw[Uml::B]; + else + objectAtRight = m_pOw[Uml::A]; + const int objRight_seqLineX = objectAtRight->getX() + objectAtRight->getWidth() / 2; + const int maxTextX = objRight_seqLineX - textWidth - 5; + if (maxTextX <= minTextX) + result = minTextX; + else if (textX > maxTextX) + result = maxTextX; + } + return result; +} + +void MessageWidget::constrainTextPos(int &textX, int &textY, int textWidth, int textHeight, + Uml::Text_Role tr) { + textX = constrainX(textX, textWidth, tr); + // Constrain Y. + const int minTextY = getMinY(); + const int maxTextY = getMaxY() - textHeight - 5; + if (textY < minTextY) + textY = minTextY; + else if (textY > maxTextY) + textY = maxTextY; +// setY( textY + textHeight ); // NB: side effect +} + +void MessageWidget::setLinkAndTextPos() { + if (m_pFText == NULL) + return; + m_pFText->setLink(this); + setTextPosition(); +} + +void MessageWidget::moveEvent(QMoveEvent* /*m*/) { + //kDebug() << "MessageWidget::moveEvent: m_pFText is " << m_pFText << endl; + if (!m_pFText) { + return; + } + //TODO why this condition? +/* if (m_pView->getSelectCount() > 2) { + return; + }*/ + + setTextPosition(); + + emit sigMessageMoved(); +} + +void MessageWidget::resizeEvent(QResizeEvent* /*re*/) { +} + +void MessageWidget::calculateWidget() { + setMessageText(m_pFText); + calculateDimensions(); + + setVisible(true); + + setX(m_nPosX); + setY(m_nY); +} + +void MessageWidget::slotWidgetMoved(Uml::IDType id) { + const Uml::IDType idA = m_pOw[Uml::A]->getLocalID(); + const Uml::IDType idB = m_pOw[Uml::B]->getLocalID(); + if (idA != id && idB != id) { + kDebug() << "MessageWidget::slotWidgetMoved(" << ID2STR(id) + << "): ignoring for idA=" << ID2STR(idA) + << ", idB=" << ID2STR(idB) << endl; + return; + } + m_nY = getY(); + if (m_nY < getMinY()) + m_nY = getMinY(); + if (m_nY > getMaxY()) + m_nY = getMaxY(); + calculateWidget(); + if( !m_pFText ) + return; + if (m_pView->getSelectCount(true) > 1) + return; + setTextPosition(); +} + +bool MessageWidget::contains(ObjectWidget * w) { + if(m_pOw[Uml::A] == w || m_pOw[Uml::B] == w) + return true; + else + return false; +} + +void MessageWidget::slotMenuSelection(int sel) { + if(sel == ListPopupMenu::mt_Delete) { + // This will clean up this widget and the text widget: + m_pView -> removeWidget(this); + } else { + if (m_pFText == NULL) { + Uml::Text_Role tr = Uml::tr_Seq_Message; + if (m_pOw[Uml::A] == m_pOw[Uml::B]) + tr = Uml::tr_Seq_Message_Self; + m_pFText = new FloatingTextWidget( m_pView, tr ); + m_pFText->setFont(UMLWidget::getFont()); + setLinkAndTextPos(); + m_pView->getWidgetList().append(m_pFText); + } + m_pFText -> slotMenuSelection(sel); + } +} + +bool MessageWidget::activate(IDChangeLog * Log /*= 0*/) { + m_pView->resetPastePoint(); + // UMLWidget::activate(Log); CHECK: I don't think we need this ? + if (m_pOw[Uml::A] == NULL) { + UMLWidget *pWA = m_pView->findWidget(m_widgetAId); + if (pWA == NULL) { + kDebug() << "MessageWidget::activate: role A object " + << ID2STR(m_widgetAId) << " not found" << endl; + return false; + } + m_pOw[Uml::A] = dynamic_cast(pWA); + if (m_pOw[Uml::A] == NULL) { + kDebug() << "MessageWidget::activate: role A widget " + << ID2STR(m_widgetAId) << " is not an ObjectWidget" << endl; + return false; + } + } + if (m_pOw[Uml::B] == NULL) { + UMLWidget *pWB = m_pView->findWidget(m_widgetBId); + if (pWB == NULL) { + kDebug() << "MessageWidget::activate: role B object " + << ID2STR(m_widgetBId) << " not found" << endl; + return false; + } + m_pOw[Uml::B] = dynamic_cast(pWB); + if (m_pOw[Uml::B] == NULL) { + kDebug() << "MessageWidget::activate: role B widget " + << ID2STR(m_widgetBId) << " is not an ObjectWidget" << endl; + return false; + } + } + updateResizability(); + + UMLClassifier *c = dynamic_cast(m_pOw[Uml::B]->getUMLObject()); + UMLOperation *op = NULL; + if (c && !m_CustomOp.isEmpty()) { + Uml::IDType opId = STR2ID(m_CustomOp); + op = dynamic_cast( c->findChildObjectById(opId, true) ); + if (op) { + // If the UMLOperation is set, m_CustomOp isn't used anyway. + // Just setting it empty for the sake of sanity. + m_CustomOp = QString::null; + } + } + + if( !m_pFText ) { + Uml::Text_Role tr = Uml::tr_Seq_Message; + if (m_pOw[Uml::A] == m_pOw[Uml::B]) + tr = Uml::tr_Seq_Message_Self; + m_pFText = new FloatingTextWidget( m_pView, tr, "" ); + m_pFText->setFont(UMLWidget::getFont()); + } + if (op) + setOperation(op); // This requires a valid m_pFText. + setLinkAndTextPos(); + m_pFText -> setText(""); + m_pFText->setActivated(); + QString messageText = m_pFText->getText(); + m_pFText->setVisible( messageText.length() > 1 ); + + connect(m_pOw[Uml::A], SIGNAL(sigWidgetMoved(Uml::IDType)), this, SLOT(slotWidgetMoved(Uml::IDType))); + connect(m_pOw[Uml::B], SIGNAL(sigWidgetMoved(Uml::IDType)), this, SLOT(slotWidgetMoved(Uml::IDType))); + + connect(this, SIGNAL(sigMessageMoved()), m_pOw[Uml::A], SLOT(slotMessageMoved()) ); + connect(this, SIGNAL(sigMessageMoved()), m_pOw[Uml::B], SLOT(slotMessageMoved()) ); + m_pOw[Uml::A] -> messageAdded(this); + m_pOw[Uml::B] -> messageAdded(this); + calculateDimensions(); + + emit sigMessageMoved(); + return true; +} + +void MessageWidget::setMessageText(FloatingTextWidget *ft) { + if (ft == NULL) + return; + QString displayText = m_SequenceNumber + ": " + getOperationText(m_pView); + ft->setText(displayText); + setTextPosition(); +} + +void MessageWidget::setText(FloatingTextWidget *ft, const QString &newText) { + ft->setText(newText); + UMLApp::app()->getDocument()->setModified(true); +} + +void MessageWidget::setSeqNumAndOp(const QString &seqNum, const QString &op) { + setSequenceNumber( seqNum ); + m_CustomOp = op; ///FIXME m_pOperation +} + +void MessageWidget::setSequenceNumber( const QString &sequenceNumber ) { + m_SequenceNumber = sequenceNumber; +} + +QString MessageWidget::getSequenceNumber() const { + return m_SequenceNumber; +} + +void MessageWidget::lwSetFont (QFont font) { + UMLWidget::setFont( font ); +} + +UMLClassifier *MessageWidget::getOperationOwner() { + UMLObject *pObject = m_pOw[Uml::B]->getUMLObject(); + if (pObject == NULL) + return NULL; + UMLClassifier *c = dynamic_cast(pObject); + return c; +} + +UMLOperation *MessageWidget::getOperation() { + return static_cast(m_pObject); +} + +void MessageWidget::setOperation(UMLOperation *op) { + if (m_pObject && m_pFText) + disconnect(m_pObject, SIGNAL(modified()), m_pFText, SLOT(setMessageText())); + m_pObject = op; + if (m_pObject && m_pFText) + connect(m_pObject, SIGNAL(modified()), m_pFText, SLOT(setMessageText())); +} + +QString MessageWidget::getCustomOpText() { + return m_CustomOp; +} + +void MessageWidget::setCustomOpText(const QString &opText) { + m_CustomOp = opText; + m_pFText->setMessageText(); +} + +UMLClassifier * MessageWidget::getSeqNumAndOp(QString& seqNum, QString& op) { + seqNum = m_SequenceNumber; + UMLOperation *pOperation = getOperation(); + if (pOperation != NULL) { + op = pOperation->toString(Uml::st_SigNoVis); + } else { + op = m_CustomOp; + } + UMLObject *o = m_pOw[Uml::B]->getUMLObject(); + UMLClassifier *c = dynamic_cast(o); + return c; +} + +void MessageWidget::calculateDimensions() { + if (m_sequenceMessageType == Uml::sequence_message_synchronous) { + calculateDimensionsSynchronous(); + } else if (m_sequenceMessageType == Uml::sequence_message_asynchronous) { + calculateDimensionsAsynchronous(); + } else if (m_sequenceMessageType == Uml::sequence_message_creation) { + calculateDimensionsCreation(); + } else { + kWarning() << "Unknown message type" << endl; + } + if (! UMLApp::app()->getDocument()->loading()) { + adjustAssocs( getX(), getY() ); // adjust assoc lines + } +} + +void MessageWidget::calculateDimensionsSynchronous() { + int x = 0; + + int x1 = m_pOw[Uml::A]->getX(); + int x2 = m_pOw[Uml::B]->getX(); + int w1 = m_pOw[Uml::A]->getWidth() / 2; + int w2 = m_pOw[Uml::B]->getWidth() / 2; + x1 += w1; + x2 += w2; + + int widgetWidth = 0; + int widgetHeight = 0; + if( m_pOw[Uml::A] == m_pOw[Uml::B] ) { + widgetWidth = 50; + x = x1 - 2; + } else if( x1 < x2 ) { + x = x1; + widgetWidth = x2 - x1 + 8; + } else { + x = x2 - 8; + widgetWidth = x1 - x2 + 8; + } + + if ( height() < 20 ) { + widgetHeight = 20; + } else { + widgetHeight = height(); + } + + m_nPosX = x; + setSize(widgetWidth, widgetHeight); +} + +void MessageWidget::calculateDimensionsAsynchronous() { + int x = 0; + + int x1 = m_pOw[Uml::A]->getX(); + int x2 = m_pOw[Uml::B]->getX(); + int w1 = m_pOw[Uml::A]->getWidth() / 2; + int w2 = m_pOw[Uml::B]->getWidth() / 2; + x1 += w1; + x2 += w2; + + int widgetWidth = 0; + int widgetHeight = 8; + if( m_pOw[Uml::A] == m_pOw[Uml::B] ) { + widgetWidth = 50; + x = x1; + if( height() < 20 ) { + widgetHeight = 20; + } else { + widgetHeight = height(); + } + } else if( x1 < x2 ) { + x = x1; + widgetWidth = x2 - x1; + } else { + x = x2; + widgetWidth = x1 - x2; + } + x += 1; + widgetWidth -= 2; + m_nPosX = x; + setSize(widgetWidth, widgetHeight); +} + +void MessageWidget::calculateDimensionsCreation() { + int x = 0; + + int x1 = m_pOw[Uml::A]->getX(); + int x2 = m_pOw[Uml::B]->getX(); + int w1 = m_pOw[Uml::A]->getWidth() / 2; + int w2 = m_pOw[Uml::B]->getWidth(); + x1 += w1; + if (x1 > x2) + x2 += w2; + + int widgetWidth = 0; + int widgetHeight = 8; + if ( x1 < x2 ) { + x = x1; + widgetWidth = x2 - x1; + } else { + x = x2; + widgetWidth = x1 - x2; + } + x += 1; + widgetWidth -= 2; + m_nPosX = x; + m_nY = m_pOw[Uml::B]->getY() + m_pOw[Uml::B]->getHeight() / 2; + setSize(widgetWidth, widgetHeight); +} + +void MessageWidget::cleanup() { + if (m_pOw[Uml::A]) { + disconnect(this, SIGNAL(sigMessageMoved()), m_pOw[Uml::A], SLOT(slotMessageMoved()) ); + m_pOw[Uml::A]->messageRemoved(this); + } + if (m_pOw[Uml::B]) { + disconnect(this, SIGNAL(sigMessageMoved()), m_pOw[Uml::B], SLOT(slotMessageMoved()) ); + m_pOw[Uml::B]->messageRemoved(this); + } + + UMLWidget::cleanup(); + if (m_pFText) { + m_pView->removeWidget(m_pFText); + m_pFText = NULL; + } +} + +void MessageWidget::setSelected(bool _select) { + UMLWidget::setSelected( _select ); + if( !m_pFText || m_pFText->getDisplayText().isEmpty()) + return; + if( m_bSelected && m_pFText -> getSelected() ) + return; + if( !m_bSelected && !m_pFText -> getSelected() ) + return; + + m_pView -> setSelected( m_pFText, 0 ); + m_pFText -> setSelected( m_bSelected ); +} + +int MessageWidget::getMinY() { + if (!m_pOw[Uml::A] || !m_pOw[Uml::B]) { + return 0; + } + if (m_sequenceMessageType == Uml::sequence_message_creation) { + return m_pOw[Uml::A]->getY() + m_pOw[Uml::A]->getHeight(); + } + int heightA = m_pOw[Uml::A]->getY() + m_pOw[Uml::A]->getHeight(); + int heightB = m_pOw[Uml::B]->getY() + m_pOw[Uml::B]->getHeight(); + int height = heightA; + if( heightA < heightB ) { + height = heightB; + } + return height; +} + +int MessageWidget::getMaxY() { + if( !m_pOw[Uml::A] || !m_pOw[Uml::B] ) { + return 0; + } + int heightA = (int)((ObjectWidget*)m_pOw[Uml::A])->getEndLineY(); + int heightB = (int)((ObjectWidget*)m_pOw[Uml::B])->getEndLineY(); + int height = heightA; + if( heightA > heightB ) { + height = heightB; + } + return (height - this->height()); +} + +void MessageWidget::setWidget(ObjectWidget * ow, Uml::Role_Type role) { + m_pOw[role] = ow; + updateResizability(); +} + +ObjectWidget* MessageWidget::getWidget(Uml::Role_Type role) { + return m_pOw[role]; +} + +void MessageWidget::saveToXMI( QDomDocument & qDoc, QDomElement & qElement ) { + QDomElement messageElement = qDoc.createElement( "messagewidget" ); + UMLWidget::saveToXMI( qDoc, messageElement ); + messageElement.setAttribute( "widgetaid", ID2STR(m_pOw[Uml::A]->getLocalID()) ); + messageElement.setAttribute( "widgetbid", ID2STR(m_pOw[Uml::B]->getLocalID()) ); + UMLOperation *pOperation = getOperation(); + if (pOperation) + messageElement.setAttribute( "operation", ID2STR(pOperation->getID()) ); + else + messageElement.setAttribute( "operation", m_CustomOp ); + messageElement.setAttribute( "seqnum", m_SequenceNumber ); + messageElement.setAttribute( "sequencemessagetype", m_sequenceMessageType ); + + // save the corresponding message text + if (m_pFText && !m_pFText->getText().isEmpty()) { + messageElement.setAttribute( "textid", ID2STR(m_pFText->getID()) ); + m_pFText -> saveToXMI( qDoc, messageElement ); + } + + qElement.appendChild( messageElement ); +} + +bool MessageWidget::loadFromXMI(QDomElement& qElement) { + if ( !UMLWidget::loadFromXMI(qElement) ) { + return false; + } + QString textid = qElement.attribute( "textid", "-1" ); + QString widgetaid = qElement.attribute( "widgetaid", "-1" ); + QString widgetbid = qElement.attribute( "widgetbid", "-1" ); + m_CustomOp = qElement.attribute( "operation", "" ); + m_SequenceNumber = qElement.attribute( "seqnum", "" ); + QString sequenceMessageType = qElement.attribute( "sequencemessagetype", "1001" ); + m_sequenceMessageType = (Uml::Sequence_Message_Type)sequenceMessageType.toInt(); + + m_widgetAId = STR2ID(widgetaid); + m_widgetBId = STR2ID(widgetbid); + m_textId = STR2ID(textid); + + Uml::Text_Role tr = Uml::tr_Seq_Message; + if (m_widgetAId == m_widgetBId) + tr = Uml::tr_Seq_Message_Self; + + //now load child elements + QDomNode node = qElement.firstChild(); + QDomElement element = node.toElement(); + if ( !element.isNull() ) { + QString tag = element.tagName(); + if (tag == "floatingtext") { + m_pFText = new FloatingTextWidget( m_pView, tr, getOperationText(m_pView), m_textId ); + if( ! m_pFText->loadFromXMI(element) ) { + // Most likely cause: The FloatingTextWidget is empty. + delete m_pFText; + m_pFText = NULL; + } + } else { + kError() << "MessageWidget::loadFromXMI: unknown tag " + << tag << endl; + } + } + return true; +} + +#include "messagewidget.moc" diff --git a/umbrello/umbrello/messagewidget.h b/umbrello/umbrello/messagewidget.h new file mode 100644 index 00000000..66f2f639 --- /dev/null +++ b/umbrello/umbrello/messagewidget.h @@ -0,0 +1,401 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef MESSAGEWIDGET_H +#define MESSAGEWIDGET_H + +#include "umlwidget.h" +#include "linkwidget.h" + +// forward declarations +class FloatingTextWidget; +class ObjectWidget; +class UMLOperation; +class MessageWidgetController; + +/** + * Used to display a message on a sequence diagram. The message + * could be between two objects or a message that calls itself on + * an object. This class will only display the line that is + * required and the text will be setup by the @ref FloatingTextWidget + * widget that is passed in the constructor. A message can be + * synchronous (calls a method and gains control back on return, + * as happens in most programming languages) or asynchronous + * (calls a method and gains back control immediately). + * + * @short Displays a message. + * @author Paul Hensgen + * @see UMLWidget + * @see ObjectWidget + * @see FloatingTextWidget + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class MessageWidget : public UMLWidget, public LinkWidget { + Q_OBJECT +public: + friend class MessageWidgetController; + + /** + * Constructs a MessageWidget. + * + * @param view The parent to this class. + * @param a The role A widget for this message. + * @param b The role B widget for this message. + * @param y The vertical position to display this message. + * @param sequenceMessageType Whether synchronous or asynchronous + * @param id A unique id used for deleting this object cleanly. + * The default (-1) will prompt generation of a new ID. + */ + MessageWidget(UMLView * view, ObjectWidget* a, ObjectWidget* b, + int y, Uml::Sequence_Message_Type sequenceMessageType, + Uml::IDType id = Uml::id_None); + + /** + * Constructs a MessageWidget. + * + * @param view The parent to this class. + * @param sequenceMessageType The Uml::Sequence_Message_Type of this message widget + * @param id The ID to assign (-1 will prompt a new ID.) + */ + MessageWidget(UMLView * view, Uml::Sequence_Message_Type sequenceMessageType, Uml::IDType id = Uml::id_None); + + /** + * Initializes key variables of the class. + */ + void init(); + + /** + * Standard deconstructor. + */ + virtual ~MessageWidget(); + + /** + * Write property of QString m_SequenceNumber. + */ + void setSequenceNumber( const QString &sequenceNumber ); + + /** + * Read property of QString m_SequenceNumber. + */ + QString getSequenceNumber() const; + + /** + * Returns whether the message is synchronous or asynchronous + */ + Uml::Sequence_Message_Type getSequenceMessageType() const { + return m_sequenceMessageType; + } + + /** + * Check to see if the given ObjectWidget is involved in the message. + * + * @param w The ObjectWidget to check for. + * @return true - if is contained, false - not contained. + */ + bool contains(ObjectWidget * w); + + /** + * Returns the related widget on the given side. + * + * @return The ObjectWidget we are related to. + */ + ObjectWidget* getWidget(Uml::Role_Type role); + + /** + * Sets the related widget on the given side. + * + * @param ow The ObjectWidget we are related to. + * @param role The Uml::Role_Type to be set for the ObjectWidget + */ + void setWidget(ObjectWidget * ow, Uml::Role_Type role) ; + + /** + * Returns the text widget it is related to. + * + * @return The text widget we are related to. + */ + FloatingTextWidget * getFloatingTextWidget() { + return m_pFText; + } + + /** + * Sets the text widget it is related to. + * + * @param f The text widget we are related to. + */ + void setFloatingTextWidget(FloatingTextWidget * f) { + m_pFText = f; + } + + /** + * Implements operation from LinkWidget. + * Required by FloatingTextWidget. + */ + void lwSetFont (QFont font); + + /** + * Overrides operation from LinkWidget. + * Required by FloatingTextWidget. + * @todo Move to LinkWidget. + */ + UMLClassifier *getOperationOwner(); + + /** + * Implements operation from LinkWidget. + * Motivated by FloatingTextWidget. + */ + UMLOperation *getOperation(); + + /** + * Implements operation from LinkWidget. + * Motivated by FloatingTextWidget. + */ + void setOperation(UMLOperation *op); + + /** + * Overrides operation from LinkWidget. + * Required by FloatingTextWidget. + */ + QString getCustomOpText(); + + /** + * Overrides operation from LinkWidget. + * Required by FloatingTextWidget. + */ + void setCustomOpText(const QString &opText); + + /** + * Overrides operation from LinkWidget. + * Required by FloatingTextWidget. + * + * @param ft The text widget which to update. + */ + void setMessageText(FloatingTextWidget *ft); + + /** + * Overrides operation from LinkWidget. + * Required by FloatingTextWidget. + * + * @param ft The text widget which to update. + * @param newText The new text to set. + */ + void setText(FloatingTextWidget *ft, const QString &newText); + + /** + * Overrides operation from LinkWidget. + * Required by FloatingTextWidget. + * + * @param seqNum The new sequence number string to set. + * @param op The new operation string to set. + */ + void setSeqNumAndOp(const QString &seqNum, const QString &op); + + /** + * Overrides operation from LinkWidget. + * Required by FloatingTextWidget. + * + * @param seqNum Return this MessageWidget's sequence number string. + * @param op Return this MessageWidget's operation string. + */ + UMLClassifier * getSeqNumAndOp(QString& seqNum, QString& op); + + /** + * Calculate the geometry of the widget. + */ + void calculateWidget(); + + /** + * Activates a MessageWidget. Connects its m_pOw[] pointers + * to UMLObjects and also send signals about its FloatingTextWidget. + */ + bool activate(IDChangeLog * Log = 0); + + /** + * Calculates the size of the widget by calling + * calculateDimenstionsSynchronous(), + * calculateDimenstionsAsynchronous(), or + * calculateDimensionsCreation() + */ + void calculateDimensions(); + + /** + * Calculates and sets the size of the widget for a synchronous message + */ + void calculateDimensionsSynchronous(); + + /** + * Calculates and sets the size of the widget for an asynchronous message + */ + void calculateDimensionsAsynchronous(); + + /** + * Calculates and sets the size of the widget for a creation message + */ + void calculateDimensionsCreation(); + + /** + * Calls drawSynchronous() or drawAsynchronous() + */ + void draw(QPainter& p, int offsetX, int offsetY); + + /** + * Draws the calling arrow with filled in arrowhead, the + * timeline box and the returning arrow with a dashed line and + * stick arrowhead. + */ + void drawSynchronous(QPainter& p, int offsetX, int offsetY); + + /** + * Draws a solid arrow line and a stick arrow head. + */ + void drawAsynchronous(QPainter& p, int offsetX, int offsetY); + + /** + * Draws a solid arrow line and a stick arrow head to the + * edge of the target object widget instead of to the + * sequence line. + */ + void drawCreation(QPainter& p, int offsetX, int offsetY); + + /** + * Sets the text position relative to the sequence message. + */ + void setTextPosition(); + + /** + * Constrains the FloatingTextWidget X and Y values supplied. + * Overrides operation from LinkWidget. + * + * @param textX Candidate X value (may be modified by the constraint.) + * @param textY Candidate Y value (may be modified by the constraint.) + * @param textWidth Width of the text. + * @param textHeight Height of the text. + * @param tr Uml::Text_Role of the text. + */ + void constrainTextPos(int &textX, int &textY, int textWidth, int textHeight, + Uml::Text_Role tr); + + /** + * Used to cleanup any other widget it may need to delete. + */ + void cleanup(); + + /** + * Sets the state of whether the widget is selected. + * + * @param _select True if the widget is selected. + */ + void setSelected(bool _select); + + /** + * Returns the minimum height this widget should be set at on + * a sequence diagrams. Takes into account the widget positions + * it is related to. + */ + int getMinY(); + + /** + * Returns the maximum height this widget should be set at on + * a sequence diagrams. Takes into account the widget positions + * it is related to. + */ + int getMaxY(); + + /** + * Overrides operation from UMLWidget. + * + * @param p Point to be checked. + * + * @return Non-zero if the point is on a part of the MessageWidget. + * NB In case of a synchronous message, the empty space + * between call line and return line does not count, i.e. if + * the point is located in that space the function returns 0. + */ + int onWidget(const QPoint & p); + + /** + * Saves to the "messagewidget" XMI element. + */ + void saveToXMI( QDomDocument & qDoc, QDomElement & qElement ); + + /** + * Loads from the "messagewidget" XMI element. + */ + bool loadFromXMI( QDomElement & qElement ); + +protected: + /** + * Shortcut for calling m_pFText->setLink() followed by + * this->setTextPosition(). + */ + void setLinkAndTextPos(); + + /** + * Returns the textX arg with constraints applied. + * Auxiliary to setTextPosition() and constrainTextPos(). + */ + int constrainX(int textX, int textWidth, Uml::Text_Role tr); + + /** + * Draw an arrow pointing in the given direction. + * The arrow head is not solid, i.e. it is made up of two lines + * like so: ---> + * The direction can be either Qt::LeftArrow or Qt::RightArrow. + */ + static void drawArrow( QPainter& p, int x, int y, int w, + Qt::ArrowType direction, bool useDottedLine = false ); + + /** + * Draw a solid (triangular) arrowhead pointing in the given direction. + * The direction can be either Qt::LeftArrow or Qt::RightArrow. + */ + static void drawSolidArrowhead(QPainter& p, int x, int y, Qt::ArrowType direction); + + /** + * Update the UMLWidget::m_bResizable flag according to the + * charactersitics of this message. + */ + void updateResizability(); + + // Data loaded/saved + QString m_SequenceNumber; + QString m_CustomOp; + /** + * Whether the message is synchronous or asynchronous + */ + Uml::Sequence_Message_Type m_sequenceMessageType; + +private: + void moveEvent(QMoveEvent */*m*/); + void resizeEvent(QResizeEvent */*re*/); + + ObjectWidget * m_pOw[2]; + FloatingTextWidget * m_pFText; + int m_nY; + /** + * The following variables are used by loadFromXMI() as an intermediate + * store. activate() resolves the IDs, i.e. after activate() the variables + * m_pOw[] and m_pFText can be used. + */ + Uml::IDType m_widgetAId, m_widgetBId, m_textId; + +public slots: + void slotWidgetMoved(Uml::IDType id); + void slotMenuSelection(int sel); +signals: + /** + * emitted when the message widget is moved up or down + * slots into ObjectWidget::slotMessageMoved() + */ + void sigMessageMoved(); +}; + +#endif diff --git a/umbrello/umbrello/messagewidgetcontroller.cpp b/umbrello/umbrello/messagewidgetcontroller.cpp new file mode 100644 index 00000000..8ffac822 --- /dev/null +++ b/umbrello/umbrello/messagewidgetcontroller.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "messagewidgetcontroller.h" + +// kde includes +#include +#include + +// app includes +#include "messagewidget.h" +#include "floatingtextwidget.h" +#include "objectwidget.h" +#include "listpopupmenu.h" + +MessageWidgetController::MessageWidgetController(MessageWidget* messageWidget): + UMLWidgetController(messageWidget) { + m_messageWidget = messageWidget; + m_unconstrainedPositionY = 0; +} + +MessageWidgetController::~MessageWidgetController() { +} + +void MessageWidgetController::saveWidgetValues(QMouseEvent *me) { + UMLWidgetController::saveWidgetValues(me); + + m_unconstrainedPositionY = m_widget->getY(); +} + +QCursor MessageWidgetController::getResizeCursor() { + return KCursor::sizeVerCursor(); +} + +void MessageWidgetController::resizeWidget(int newW, int newH) { + m_messageWidget->setSize(m_messageWidget->width(), newH); + emit m_messageWidget->sigMessageMoved(); +} + +void MessageWidgetController::moveWidgetBy(int diffX, int diffY) { + m_unconstrainedPositionY += diffY; + int newY = constrainPositionY(diffY); + + if (m_unconstrainedPositionY != newY) { + if (m_unconstrainedPositionY > m_messageWidget->getY()) { + newY = m_unconstrainedPositionY; + } else { + return; + } + } + + m_messageWidget->setY(newY); + + if (m_messageWidget->m_sequenceMessageType == Uml::sequence_message_creation) { + const int objWidgetHalfHeight = m_messageWidget->m_pOw[Uml::B]->getHeight() / 2; + m_messageWidget->m_pOw[Uml::B]->UMLWidget::setY(newY - objWidgetHalfHeight); + } + + m_messageWidget->moveEvent(0); +} + +void MessageWidgetController::constrainMovementForAllWidgets(int &diffX, int &diffY) { + diffX = 0; + diffY = constrainPositionY(diffY) - m_widget->getY(); +} + +void MessageWidgetController::doMouseDoubleClick(QMouseEvent *me) { + if (m_messageWidget->m_pFText != NULL) { + m_messageWidget->m_pFText->slotMenuSelection(ListPopupMenu::mt_Select_Operation); + } +} + +int MessageWidgetController::constrainPositionY(int diffY) { + int newY = m_widget->getY() + diffY; + + int minY = m_messageWidget->getMinY(); + if (m_messageWidget->m_pFText && !m_messageWidget->m_pFText->getDisplayText().isEmpty()) { + minY += m_messageWidget->m_pFText->getHeight(); + } + + if (newY < minY) { + newY = minY; + } + + return newY; +} + diff --git a/umbrello/umbrello/messagewidgetcontroller.h b/umbrello/umbrello/messagewidgetcontroller.h new file mode 100644 index 00000000..921fd2bf --- /dev/null +++ b/umbrello/umbrello/messagewidgetcontroller.h @@ -0,0 +1,154 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef MESSAGEWIDGETCONTROLLER_H +#define MESSAGEWIDGETCONTROLLER_H + +#include "umlwidgetcontroller.h" + +class MessageWidget; + +/** + * Controller for MessageWidget. + * + * When moving a MessageWidget, it is only moved along Y axis. X axis movement + * is always ignored. + * So, if the MessageWidget is being moved as part of a selection and that + * selection is moved in X and/or Y axis, the MessageWidget will only move in + * Y axis. Another constrain is applied in Y axis, so the message doesn't pass + * over the related object widgets. Due to this constrain, the vertical + * position the message would have if it wasn't constrained is calculated, so + * when the widget lowers the position where it was constrained it begins to + * move again. + * Also, when constraining the move of the selection because the receiver of + * mouse move events is a MessageWidget, all the widgets are moved only in Y + * axis. Another constrain is applied in Y axis, so the message doesn't pass + * over the related object widgets. The unconstrained position isn't need here, + * because the message widget is the receiver of the events, so when the cursor + * goes lower than where it was constrained it begins to lower automatically. + * + * Creation messages take care of moving the object created when they're moved. + * + * Only vertical resize is allowed for MessageWidget. Cursor is set to reflect + * this. + * + * Double click shows the dialog to select the operation of the message. + * + * @author Umbrello UML Modeller Authors + */ +class MessageWidgetController : public UMLWidgetController { +public: + + /** + * Constructor for MessageWidgetController. + * + * @param messageWidget The message widget which uses the controller. + */ + MessageWidgetController(MessageWidget* messageWidget); + + /** + * Destructor for MessageWidgetController. + */ + ~MessageWidgetController(); + +protected: + + /** + * Overriden from UMLWidgetController. + * Saves the values of the widget needed for move/resize. + * Calls parent method and then saves the value of m_unconstrainedPositionY + * + * @param me The QMouseEvent to get the offset from. + */ + virtual void saveWidgetValues(QMouseEvent *me); + + /** + * Overriden from UMLWidgetController. + * Returns the cursor to be shown when resizing the widget. + * The cursor shown is KCursor::sizeVerCursor(). + * + * @return The cursor to be shown when resizing the widget. + */ + virtual QCursor getResizeCursor(); + + /** + * Overriden from UMLWidgetController. + * Resizes the height of the message widget and emits the message moved signal. + * Message widgets can only be resized vertically, so width isn't modified. + * + * @param newW The new width for the widget (isn't used). + * @param newH The new height for the widget. + */ + virtual void resizeWidget(int newW, int newH); + + /** + * Overriden from UMLWidgetController. + * Moves the widget to a new position using the difference between the + * current position and the new position. X position is ignored, and widget + * is only moved along Y axis. If message goes upper than the object, it's + * kept at this position until it should be lowered again (the unconstrained + * Y position is saved to know when it's the time to lower it again). + * If the message is a creation message, the object created is also moved to + * the new vertical position. + * @see constrainPositionY + * + * @param diffX The difference between current X position and new X position + * (isn't used). + * @param diffY The difference between current Y position and new Y position. + */ + virtual void moveWidgetBy(int diffX, int diffY); + + /** + * Overriden from UMLWidgetController. + * Modifies the value of the diffX and diffY variables used to move the widgets. + * All the widgets are constrained to be moved only in Y axis (diffX is set to 0). + * @see constrainPositionY + * + * @param diffX The difference between current X position and new X position. + * @param diffY The difference between current Y position and new Y position. + */ + virtual void constrainMovementForAllWidgets(int &diffX, int &diffY); + + /** + * Overriden from UMLWidgetController. + * Executes the action for double click in the widget. + * Shows the dialog to select the operation of the message. + * + * @param me The QMouseEvent which triggered the double click event. + */ + virtual void doMouseDoubleClick(QMouseEvent *me); + +private: + + /** + * Constrains the vertical position of the message widget so it doesn't go + * upper than the bottom side of the lower object. + * The height of the floating text widget in the message is taken in account + * if there is any and isn't empty. + * + * @param diffY The difference between current Y position and new Y position. + * @return The new Y position, constrained. + */ + int constrainPositionY(int diffY); + + /** + * The message widget which uses the controller. + */ + MessageWidget *m_messageWidget; + + /** + * The vertical position the widget would have if its move wasn't constrained. + */ + int m_unconstrainedPositionY; + +}; + +#endif diff --git a/umbrello/umbrello/messagewidgetlist.h b/umbrello/umbrello/messagewidgetlist.h new file mode 100644 index 00000000..559a4cf7 --- /dev/null +++ b/umbrello/umbrello/messagewidgetlist.h @@ -0,0 +1,22 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef MESSAGEWIDGETLIST_H +#define MESSAGEWIDGETLIST_H + +#include + +class MessageWidget; + +typedef QPtrList MessageWidgetList; +typedef QPtrListIterator MessageWidgetListIt; + +#endif diff --git a/umbrello/umbrello/model_utils.cpp b/umbrello/umbrello/model_utils.cpp new file mode 100644 index 00000000..89e1cf33 --- /dev/null +++ b/umbrello/umbrello/model_utils.cpp @@ -0,0 +1,1221 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "model_utils.h" + +// qt/kde includes +#include +#include +#include +#include +#include + +// app includes +#include "umlobject.h" +#include "umlpackagelist.h" +#include "package.h" +#include "folder.h" +#include "classifier.h" +#include "enum.h" +#include "entity.h" +#include "template.h" +#include "operation.h" +#include "attribute.h" +#include "association.h" +#include "umlrole.h" +#include "umldoc.h" +#include "uml.h" +#include "codegenerator.h" + +namespace Model_Utils { + +bool isCloneable(Uml::Widget_Type type) { + switch (type) { + case Uml::wt_Actor: + case Uml::wt_UseCase: + case Uml::wt_Class: + case Uml::wt_Interface: + case Uml::wt_Enum: + case Uml::wt_Datatype: + case Uml::wt_Package: + case Uml::wt_Component: + case Uml::wt_Node: + case Uml::wt_Artifact: + return true; + default: + return false; + } +} + +UMLObject * findObjectInList(Uml::IDType id, const UMLObjectList& inList) { + for (UMLObjectListIt oit(inList); oit.current(); ++oit) { + UMLObject *obj = oit.current(); + if (obj->getID() == id) + return obj; + UMLObject *o; + Uml::Object_Type t = obj->getBaseType(); + switch (t) { + case Uml::ot_Folder: + case Uml::ot_Package: + case Uml::ot_Component: + o = static_cast(obj)->findObjectById(id); + if (o) + return o; + break; + case Uml::ot_Interface: + case Uml::ot_Class: + case Uml::ot_Enum: + case Uml::ot_Entity: + o = static_cast(obj)->findChildObjectById(id); + if (o == NULL && + (t == Uml::ot_Interface || t == Uml::ot_Class)) + o = ((UMLPackage*)obj)->findObjectById(id); + if (o) + return o; + break; + case Uml::ot_Association: + { + UMLAssociation *assoc = static_cast(obj); + UMLRole *rA = assoc->getUMLRole(Uml::A); + if (rA->getID() == id) + return rA; + UMLRole *rB = assoc->getUMLRole(Uml::B); + if (rB->getID() == id) + return rB; + } + break; + default: + break; + } + } + return NULL; +} + +UMLObject* findUMLObject(const UMLObjectList& inList, + const QString& inName, + Uml::Object_Type type /* = ot_UMLObject */, + UMLObject *currentObj /* = NULL */) { + const bool caseSensitive = UMLApp::app()->activeLanguageIsCaseSensitive(); + QString name = inName; + QStringList components; +#ifdef TRY_BUGFIX_120682 + // If we have a pointer or a reference in cpp we need to remove + // the asterisks and ampersands in order to find the appropriate object + if (UMLApp::app()->getActiveLanguage() == Uml::pl_Cpp) { + if (name.endsWith("*")) + name.remove("*"); + else if (name.contains("&")) + name.remove("&"); + } +#endif + QString nameWithoutFirstPrefix; + if (name.contains("::")) + components = QStringList::split("::", name); + else if (name.contains(".")) + components = QStringList::split(".", name); + if (components.size() > 1) { + name = components.front(); + components.pop_front(); + nameWithoutFirstPrefix = components.join("::"); + } + if (currentObj) { + UMLPackage *pkg = NULL; + if (dynamic_cast(currentObj)) { + currentObj = static_cast(currentObj->parent()); + } + pkg = dynamic_cast(currentObj); + if (pkg == NULL) + pkg = currentObj->getUMLPackage(); + // Remember packages that we've seen - for avoiding cycles. + UMLPackageList seenPkgs; + for (; pkg; pkg = currentObj->getUMLPackage()) { + if (nameWithoutFirstPrefix.isEmpty()) { + if (caseSensitive) { + if (pkg->getName() == name) + return pkg; + } else if (pkg->getName().lower() == name.lower()) { + return pkg; + } + } + if (seenPkgs.findRef(pkg) != -1) { + kError() << "findUMLObject(" << name << "): " + << "breaking out of cycle involving " + << pkg->getName() << endl; + break; + } + seenPkgs.append(pkg); + UMLObjectList objectsInCurrentScope = pkg->containedObjects(); + for (UMLObjectListIt oit(objectsInCurrentScope); oit.current(); ++oit) { + UMLObject *obj = oit.current(); + if (caseSensitive) { + if (obj->getName() != name) + continue; + } else if (obj->getName().lower() != name.lower()) { + continue; + } + Uml::Object_Type foundType = obj->getBaseType(); + if (nameWithoutFirstPrefix.isEmpty()) { + if (type != Uml::ot_UMLObject && type != foundType) { + kDebug() << "findUMLObject: type mismatch for " + << name << " (seeking type: " + << type << ", found type: " + << foundType << ")" << endl; + // Class, Interface, and Datatype are all Classifiers + // and are considered equivalent. + // The caller must be prepared to handle possible mismatches. + if ((type == Uml::ot_Class || + type == Uml::ot_Interface || + type == Uml::ot_Datatype) && + (foundType == Uml::ot_Class || + foundType == Uml::ot_Interface || + foundType == Uml::ot_Datatype)) { + return obj; + } + continue; + } + return obj; + } + if (foundType != Uml::ot_Package && + foundType != Uml::ot_Folder && + foundType != Uml::ot_Class && + foundType != Uml::ot_Interface && + foundType != Uml::ot_Component) { + kDebug() << "findUMLObject: found \"" << name + << "\" is not a package (?)" << endl; + continue; + } + UMLPackage *pkg = static_cast(obj); + return findUMLObject( pkg->containedObjects(), + nameWithoutFirstPrefix, type ); + } + currentObj = pkg; + } + } + for (UMLObjectListIt oit(inList); oit.current(); ++oit) { + UMLObject *obj = oit.current(); + if (caseSensitive) { + if (obj->getName() != name) + continue; + } else if (obj->getName().lower() != name.lower()) { + continue; + } + Uml::Object_Type foundType = obj->getBaseType(); + if (nameWithoutFirstPrefix.isEmpty()) { + if (type != Uml::ot_UMLObject && type != foundType) { + kDebug() << "findUMLObject: type mismatch for " + << name << " (seeking type: " + << type << ", found type: " + << foundType << ")" << endl; + continue; + } + return obj; + } + if (foundType != Uml::ot_Package && + foundType != Uml::ot_Folder && + foundType != Uml::ot_Class && + foundType != Uml::ot_Interface && + foundType != Uml::ot_Component) { + kDebug() << "findUMLObject: found \"" << name + << "\" is not a package (?)" << endl; + continue; + } + UMLPackage *pkg = static_cast(obj); + return findUMLObject( pkg->containedObjects(), + nameWithoutFirstPrefix, type ); + } + return NULL; +} + +QString uniqObjectName(Uml::Object_Type type, UMLPackage *parentPkg, QString prefix) { + QString currentName = prefix; + if (currentName.isEmpty()) { + if(type == Uml::ot_Class) + currentName = i18n("new_class"); + else if(type == Uml::ot_Actor) + currentName = i18n("new_actor"); + else if(type == Uml::ot_UseCase) + currentName = i18n("new_usecase"); + else if(type == Uml::ot_Package) + currentName = i18n("new_package"); + else if(type == Uml::ot_Component) + currentName = i18n("new_component"); + else if(type == Uml::ot_Node) + currentName = i18n("new_node"); + else if(type == Uml::ot_Artifact) + currentName = i18n("new_artifact"); + else if(type == Uml::ot_Interface) + currentName = i18n("new_interface"); + else if(type == Uml::ot_Datatype) + currentName = i18n("new_datatype"); + else if(type == Uml::ot_Enum) + currentName = i18n("new_enum"); + else if(type == Uml::ot_Entity) + currentName = i18n("new_entity"); + else if(type == Uml::ot_Folder) + currentName = i18n("new_folder"); + else if(type == Uml::ot_Association) + currentName = i18n("new_association"); + else { + currentName = i18n("new_object"); + kWarning() << "unknown object type in umldoc::uniqObjectName()" << endl; + } + } + UMLDoc *doc = UMLApp::app()->getDocument(); + QString name = currentName; + for (int number = 1; !doc->isUnique(name, parentPkg); number++) { + name = currentName + '_' + QString::number(number); + } + return name; +} + +bool isCommonXMIAttribute( const QString &tag ) { + bool retval = (Uml::tagEq(tag, "name") || + Uml::tagEq(tag, "visibility") || + Uml::tagEq(tag, "isRoot") || + Uml::tagEq(tag, "isLeaf") || + Uml::tagEq(tag, "isAbstract") || + Uml::tagEq(tag, "isSpecification") || + Uml::tagEq(tag, "isActive") || + Uml::tagEq(tag, "namespace") || + Uml::tagEq(tag, "ownerScope") || + Uml::tagEq(tag, "ModelElement.stereotype") || + Uml::tagEq(tag, "GeneralizableElement.generalization") || + Uml::tagEq(tag, "specialization") || //NYI + Uml::tagEq(tag, "clientDependency") || //NYI + Uml::tagEq(tag, "supplierDependency") //NYI + ); + return retval; +} + +bool isCommonDataType(QString type) { + CodeGenerator *gen = UMLApp::app()->getGenerator(); + if (gen == NULL) + return false; + const bool caseSensitive = UMLApp::app()->activeLanguageIsCaseSensitive(); + QStringList dataTypes = gen->defaultDatatypes(); + QStringList::Iterator end(dataTypes.end()); + for (QStringList::Iterator it = dataTypes.begin(); it != end; ++it) { + if (caseSensitive) { + if (type == *it) + return true; + } else if (type.lower() == (*it).lower()) { + return true; + } + } + return false; +} + +bool isClassifierListitem(Uml::Object_Type type) { + if (type == Uml::ot_Attribute || + type == Uml::ot_Operation || + type == Uml::ot_Template || + type == Uml::ot_EntityAttribute || + type == Uml::ot_EnumLiteral) { + return true; + } else { + return false; + } +} + +Uml::Model_Type guessContainer(UMLObject *o) { + Uml::Object_Type ot = o->getBaseType(); + if (ot == Uml::ot_Package && o->getStereotype() == "subsystem") + return Uml::mt_Component; + Uml::Model_Type mt = Uml::N_MODELTYPES; + switch (ot) { + case Uml::ot_Package: // CHECK: packages may appear in other views? + case Uml::ot_Interface: + case Uml::ot_Datatype: + case Uml::ot_Enum: + case Uml::ot_Class: + case Uml::ot_Attribute: + case Uml::ot_Operation: + case Uml::ot_EnumLiteral: + case Uml::ot_Template: + mt = Uml::mt_Logical; + break; + case Uml::ot_Actor: + case Uml::ot_UseCase: + mt = Uml::mt_UseCase; + break; + case Uml::ot_Component: + case Uml::ot_Artifact: // trouble: artifact can also appear at Deployment + mt = Uml::mt_Component; + break; + case Uml::ot_Node: + mt = Uml::mt_Deployment; + break; + case Uml::ot_Entity: + case Uml::ot_EntityAttribute: + mt = Uml::mt_EntityRelationship; + break; + case Uml::ot_Association: + { + UMLAssociation *assoc = static_cast(o); + UMLDoc *umldoc = UMLApp::app()->getDocument(); + for (int r = Uml::A; r <= Uml::B; r++) { + UMLObject *roleObj = assoc->getObject((Uml::Role_Type)r); + if (roleObj == NULL) { + // Ouch! we have been called while types are not yet resolved + return Uml::N_MODELTYPES; + } + UMLPackage *pkg = roleObj->getUMLPackage(); + if (pkg) { + while (pkg->getUMLPackage()) { // wind back to root + pkg = pkg->getUMLPackage(); + } + const Uml::Model_Type m = umldoc->rootFolderType(pkg); + if (m != Uml::N_MODELTYPES) + return m; + } + mt = guessContainer(roleObj); + if (mt != Uml::mt_Logical) + break; + } + } + break; + default: + break; + } + return mt; +} + +int stringToDirection(QString input, Uml::Parameter_Direction & result) { + QRegExp dirx("^(in|out|inout)"); + int pos = dirx.search(input); + if (pos == -1) + return 0; + const QString& dirStr = dirx.capturedTexts().first(); + uint dirLen = dirStr.length(); + if (input.length() > dirLen && !input[dirLen].isSpace()) + return 0; // no match after all. + if (dirStr == "out") + result = Uml::pd_Out; + else if (dirStr == "inout") + result = Uml::pd_InOut; + else + result = Uml::pd_In; + return dirLen; +} + +Parse_Status parseTemplate(QString t, NameAndType& nmTp, UMLClassifier *owningScope) { + + UMLDoc *pDoc = UMLApp::app()->getDocument(); + + t = t.stripWhiteSpace(); + if (t.isEmpty()) + return PS_Empty; + + QStringList nameAndType = QStringList::split( QRegExp("\\s*:\\s*"), t); + if (nameAndType.count() == 2) { + UMLObject *pType = NULL; + if (nameAndType[1] != "class") { + pType = pDoc->findUMLObject(nameAndType[1], Uml::ot_UMLObject, owningScope); + if (pType == NULL) + return PS_Unknown_ArgType; + } + nmTp = NameAndType(nameAndType[0], pType); + } else { + nmTp = NameAndType(t, NULL); + } + return PS_OK; +} + +Parse_Status parseAttribute(QString a, NameAndType& nmTp, UMLClassifier *owningScope, + Uml::Visibility *vis /* = 0 */) { + UMLDoc *pDoc = UMLApp::app()->getDocument(); + + a = a.simplifyWhiteSpace(); + if (a.isEmpty()) + return PS_Empty; + + int colonPos = a.find(':'); + if (colonPos < 0) { + nmTp = NameAndType(a, NULL); + return PS_OK; + } + QString name = a.left(colonPos).stripWhiteSpace(); + if (vis) { + QRegExp mnemonicVis("^([\\+\\#\\-\\~] *)"); + int pos = mnemonicVis.search(name); + if (pos == -1) { + *vis = Uml::Visibility::Private; // default value + } else { + QString caption = mnemonicVis.cap(1); + QString strVis = caption.left(1); + if (strVis == "+") + *vis = Uml::Visibility::Public; + else if (strVis == "#") + *vis = Uml::Visibility::Protected; + else if (strVis == "-") + *vis = Uml::Visibility::Private; + else + *vis = Uml::Visibility::Implementation; + } + name.remove(mnemonicVis); + } + Uml::Parameter_Direction pd = Uml::pd_In; + if (name.startsWith("in ")) { + pd = Uml::pd_In; + name = name.mid(3); + } else if (name.startsWith("inout ")) { + pd = Uml::pd_InOut; + name = name.mid(6); + } else if (name.startsWith("out ")) { + pd = Uml::pd_Out; + name = name.mid(4); + } + a = a.mid(colonPos + 1).stripWhiteSpace(); + if (a.isEmpty()) { + nmTp = NameAndType(name, NULL, pd); + return PS_OK; + } + QStringList typeAndInitialValue = QStringList::split( QRegExp("\\s*=\\s*"), a ); + const QString &type = typeAndInitialValue[0]; + UMLObject *pType = pDoc->findUMLObject(type, Uml::ot_UMLObject, owningScope); + if (pType == NULL) { + nmTp = NameAndType(name, NULL, pd); + return PS_Unknown_ArgType; + } + QString initialValue; + if (typeAndInitialValue.count() == 2) { + initialValue = typeAndInitialValue[1]; + } + nmTp = NameAndType(name, pType, pd, initialValue); + return PS_OK; +} + +Parse_Status parseOperation(QString m, OpDescriptor& desc, UMLClassifier *owningScope) { + UMLDoc *pDoc = UMLApp::app()->getDocument(); + + m = m.simplifyWhiteSpace(); + if (m.isEmpty()) + return PS_Empty; + if (m.contains(QRegExp("operator *()"))) { + // C++ special case: two sets of parentheses + desc.m_name = "operator()"; + m.remove(QRegExp("operator *()")); + } else { + /** + * The search pattern includes everything up to the opening parenthesis + * because UML also permits non programming-language oriented designs + * using narrative names, for example "check water temperature". + */ + QRegExp beginningUpToOpenParenth( "^([^\\(]+)" ); + int pos = beginningUpToOpenParenth.search(m); + if (pos == -1) + return PS_Illegal_MethodName; + desc.m_name = beginningUpToOpenParenth.cap(1); + } + desc.m_pReturnType = NULL; + QRegExp pat = QRegExp("\\) *:(.*)$"); + int pos = pat.search(m); + if (pos != -1) { // return type is optional + QString retType = pat.cap(1); + retType = retType.stripWhiteSpace(); + if (retType != "void") { + UMLObject *pRetType = owningScope->findTemplate(retType); + if (pRetType == NULL) { + pRetType = pDoc->findUMLObject(retType, Uml::ot_UMLObject, owningScope); + if (pRetType == NULL) + return PS_Unknown_ReturnType; + } + desc.m_pReturnType = pRetType; + } + } + // Remove possible empty parentheses () + m.remove( QRegExp("\\s*\\(\\s*\\)") ); + desc.m_args.clear(); + pat = QRegExp( "\\((.*)\\)" ); + pos = pat.search(m); + if (pos == -1) // argument list is optional + return PS_OK; + QString arglist = pat.cap(1); + arglist = arglist.stripWhiteSpace(); + if (arglist.isEmpty()) + return PS_OK; + QStringList args = QStringList::split( QRegExp("\\s*,\\s*"), arglist); + for (QStringList::Iterator lit = args.begin(); lit != args.end(); ++lit) { + NameAndType nmTp; + Parse_Status ps = parseAttribute(*lit, nmTp, owningScope); + if (ps) + return ps; + desc.m_args.append(nmTp); + } + return PS_OK; +} + +QString psText(Parse_Status value) { + const QString text[] = { + i18n("OK"), i18n("Empty"), i18n("Malformed argument"), + i18n("Unknown argument type"), i18n("Illegal method name"), + i18n("Unknown return type"), i18n("Unspecified error") + }; + return text[(unsigned) value]; +} + +QString progLangToString(Uml::Programming_Language pl) { + switch (pl) { + case Uml::pl_ActionScript: + return "ActionScript"; + case Uml::pl_Ada: + return "Ada"; + case Uml::pl_Cpp: + return "C++"; + case Uml::pl_CSharp: + return "C#"; + case Uml::pl_D: + return "D"; + case Uml::pl_IDL: + return "IDL"; + case Uml::pl_Java: + return "Java"; + case Uml::pl_JavaScript: + return "JavaScript"; + case Uml::pl_Pascal: + return "Pascal"; + case Uml::pl_Perl: + return "Perl"; + case Uml::pl_PHP: + return "PHP"; + case Uml::pl_PHP5: + return "PHP5"; + case Uml::pl_Python: + return "Python"; + case Uml::pl_Ruby: + return "Ruby"; + case Uml::pl_SQL: + return "SQL"; + case Uml::pl_Tcl: + return "Tcl"; + case Uml::pl_XMLSchema: + return "XMLSchema"; + default: + break; + } + return QString::null; +} + +Uml::Programming_Language stringToProgLang(QString str) { + if (str == "ActionScript") + return Uml::pl_ActionScript; + if (str == "Ada") + return Uml::pl_Ada; + if (str == "C++" || str == "Cpp") // "Cpp" only for bkwd compatibility + return Uml::pl_Cpp; + if (str == "C#") + return Uml::pl_CSharp; + if (str == "D") + return Uml::pl_D; + if (str == "IDL") + return Uml::pl_IDL; + if (str == "Java") + return Uml::pl_Java; + if (str == "JavaScript") + return Uml::pl_JavaScript; + if (str == "Pascal") + return Uml::pl_Pascal; + if (str == "Perl") + return Uml::pl_Perl; + if (str == "PHP") + return Uml::pl_PHP; + if (str == "PHP5") + return Uml::pl_PHP5; + if (str == "Python") + return Uml::pl_Python; + if (str == "Ruby") + return Uml::pl_Ruby; + if (str == "SQL") + return Uml::pl_SQL; + if (str == "Tcl") + return Uml::pl_Tcl; + if (str == "XMLSchema") + return Uml::pl_XMLSchema; + return Uml::pl_Reserved; +} + +bool typeIsRootView(Uml::ListView_Type type) { + switch (type) { + case Uml::lvt_View: + case Uml::lvt_Logical_View: + case Uml::lvt_UseCase_View: + case Uml::lvt_Component_View: + case Uml::lvt_Deployment_View: + case Uml::lvt_EntityRelationship_Model: + return true; + break; + default: + break; + } + return false; +} + +bool typeIsCanvasWidget(Uml::ListView_Type type) { + switch (type) { + case Uml::lvt_Actor: + case Uml::lvt_UseCase: + case Uml::lvt_Class: + case Uml::lvt_Package: + case Uml::lvt_Logical_Folder: + case Uml::lvt_UseCase_Folder: + case Uml::lvt_Component_Folder: + case Uml::lvt_Deployment_Folder: + case Uml::lvt_EntityRelationship_Folder: + case Uml::lvt_Subsystem: + case Uml::lvt_Component: + case Uml::lvt_Node: + case Uml::lvt_Artifact: + case Uml::lvt_Interface: + case Uml::lvt_Datatype: + case Uml::lvt_Enum: + case Uml::lvt_Entity: + return true; + break; + default: + break; + } + return false; +} + +bool typeIsFolder(Uml::ListView_Type type) { + if (typeIsRootView(type) || + type == Uml::lvt_Datatype_Folder || + type == Uml::lvt_Logical_Folder || + type == Uml::lvt_UseCase_Folder || + type == Uml::lvt_Component_Folder || + type == Uml::lvt_Deployment_Folder || + type == Uml::lvt_EntityRelationship_Folder) { + return true; + } else { + return false; + } +} + +bool typeIsContainer(Uml::ListView_Type type) { + if (typeIsFolder(type)) + return true; + return (type == Uml::lvt_Package || + type == Uml::lvt_Subsystem || + type == Uml::lvt_Component); +} + +bool typeIsClassifierList(Uml::ListView_Type type) { + if (type == Uml::lvt_Attribute || + type == Uml::lvt_Operation || + type == Uml::lvt_Template || + type == Uml::lvt_EntityAttribute || + type == Uml::lvt_EnumLiteral) { + return true; + } else { + return false; + } +} + +bool typeIsDiagram(Uml::ListView_Type type) { + if (type == Uml::lvt_Class_Diagram || + type == Uml::lvt_Collaboration_Diagram || + type == Uml::lvt_State_Diagram || + type == Uml::lvt_Activity_Diagram || + type == Uml::lvt_Sequence_Diagram || + type == Uml::lvt_UseCase_Diagram || + type == Uml::lvt_Component_Diagram || + type == Uml::lvt_Deployment_Diagram || + type == Uml::lvt_EntityRelationship_Diagram) { + return true; + } else { + return false; + } +} + +Uml::Model_Type convert_DT_MT(Uml::Diagram_Type dt) { + Uml::Model_Type mt; + switch (dt) { + case Uml::dt_UseCase: + mt = Uml::mt_UseCase; + break; + case Uml::dt_Collaboration: + case Uml::dt_Class: + case Uml::dt_Sequence: + case Uml::dt_State: + case Uml::dt_Activity: + mt = Uml::mt_Logical; + break; + case Uml::dt_Component: + mt = Uml::mt_Component; + break; + case Uml::dt_Deployment: + mt = Uml::mt_Deployment; + break; + case Uml::dt_EntityRelationship: + mt = Uml::mt_EntityRelationship; + break; + default: + kError() << "Model_Utils::convert_DT_MT: illegal input value " << dt << endl; + mt = Uml::N_MODELTYPES; + break; + } + return mt; +} + +Uml::ListView_Type convert_MT_LVT(Uml::Model_Type mt) { + Uml::ListView_Type lvt = Uml::lvt_Unknown; + switch (mt) { + case Uml::mt_Logical: + lvt = Uml::lvt_Logical_View; + break; + case Uml::mt_UseCase: + lvt = Uml::lvt_UseCase_View; + break; + case Uml::mt_Component: + lvt = Uml::lvt_Component_View; + break; + case Uml::mt_Deployment: + lvt = Uml::lvt_Deployment_View; + break; + case Uml::mt_EntityRelationship: + lvt = Uml::lvt_EntityRelationship_Model; + break; + default: + break; + } + return lvt; +} + +Uml::Model_Type convert_LVT_MT(Uml::ListView_Type lvt) { + Uml::Model_Type mt = Uml::N_MODELTYPES; + switch (lvt) { + case Uml::lvt_Logical_View: + mt = Uml::mt_Logical; + break; + case Uml::lvt_UseCase_View: + mt = Uml::mt_UseCase; + break; + case Uml::lvt_Component_View: + mt = Uml::mt_Component; + break; + case Uml::lvt_Deployment_View: + mt = Uml::mt_Deployment; + break; + case Uml::lvt_EntityRelationship_Model: + mt = Uml::mt_EntityRelationship; + break; + default: + break; + } + return mt; +} + +Uml::ListView_Type convert_DT_LVT(Uml::Diagram_Type dt) { + Uml::ListView_Type type = Uml::lvt_Unknown; + switch(dt) { + case Uml::dt_UseCase: + type = Uml::lvt_UseCase_Diagram; + break; + + case Uml::dt_Class: + type = Uml::lvt_Class_Diagram; + break; + + case Uml::dt_Sequence: + type = Uml::lvt_Sequence_Diagram; + break; + + case Uml::dt_Collaboration: + type = Uml::lvt_Collaboration_Diagram; + break; + + case Uml::dt_State: + type = Uml::lvt_State_Diagram; + break; + + case Uml::dt_Activity: + type = Uml::lvt_Activity_Diagram; + break; + + case Uml::dt_Component: + type = Uml::lvt_Component_Diagram; + break; + + case Uml::dt_Deployment: + type = Uml::lvt_Deployment_Diagram; + break; + + case Uml::dt_EntityRelationship: + type = Uml::lvt_EntityRelationship_Diagram; + break; + + default: + kWarning() << "convert_DT_LVT() called on unknown diagram type" << endl; + } + return type; +} + +Uml::ListView_Type convert_OT_LVT(UMLObject *o) { + Uml::Object_Type ot = o->getBaseType(); + Uml::ListView_Type type = Uml::lvt_Unknown; + switch(ot) { + case Uml::ot_UseCase: + type = Uml::lvt_UseCase; + break; + + case Uml::ot_Actor: + type = Uml::lvt_Actor; + break; + + case Uml::ot_Class: + type = Uml::lvt_Class; + break; + + case Uml::ot_Package: + type = Uml::lvt_Package; + break; + + case Uml::ot_Folder: + { + UMLDoc *umldoc = UMLApp::app()->getDocument(); + UMLFolder *f = static_cast(o); + do { + const Uml::Model_Type mt = umldoc->rootFolderType(f); + if (mt != Uml::N_MODELTYPES) { + switch (mt) { + case Uml::mt_Logical: + type = Uml::lvt_Logical_Folder; + break; + case Uml::mt_UseCase: + type = Uml::lvt_UseCase_Folder; + break; + case Uml::mt_Component: + type = Uml::lvt_Component_Folder; + break; + case Uml::mt_Deployment: + type = Uml::lvt_Deployment_Folder; + break; + case Uml::mt_EntityRelationship: + type = Uml::lvt_EntityRelationship_Folder; + break; + default: + break; + } + return type; + } + } while ((f = static_cast(f->getUMLPackage())) != NULL); + kError() << "convert_OT_LVT(" << o->getName() + << "): internal error - object is not properly nested in folder" + << endl; + } + break; + + case Uml::ot_Component: + type = Uml::lvt_Component; + break; + + case Uml::ot_Node: + type = Uml::lvt_Node; + break; + + case Uml::ot_Artifact: + type = Uml::lvt_Artifact; + break; + + case Uml::ot_Interface: + type = Uml::lvt_Interface; + break; + + case Uml::ot_Datatype: + type = Uml::lvt_Datatype; + break; + + case Uml::ot_Enum: + type = Uml::lvt_Enum; + break; + + case Uml::ot_EnumLiteral: + type = Uml::lvt_EnumLiteral; + break; + + case Uml::ot_Entity: + type = Uml::lvt_Entity; + break; + + case Uml::ot_EntityAttribute: + type = Uml::lvt_EntityAttribute; + break; + + case Uml::ot_Attribute: + type = Uml::lvt_Attribute; + break; + + case Uml::ot_Operation: + type = Uml::lvt_Operation; + break; + + case Uml::ot_Template: + type = Uml::lvt_Template; + break; + default: + break; + } + return type; +} + +Uml::Object_Type convert_LVT_OT(Uml::ListView_Type lvt) { + Uml::Object_Type ot = (Uml::Object_Type)0; + switch (lvt) { + case Uml::lvt_UseCase: + ot = Uml::ot_UseCase; + break; + + case Uml::lvt_Actor: + ot = Uml::ot_Actor; + break; + + case Uml::lvt_Class: + ot = Uml::ot_Class; + break; + + case Uml::lvt_Package: + case Uml::lvt_Subsystem: + ot = Uml::ot_Package; + break; + + case Uml::lvt_Component: + ot = Uml::ot_Component; + break; + + case Uml::lvt_Node: + ot = Uml::ot_Node; + break; + + case Uml::lvt_Artifact: + ot = Uml::ot_Artifact; + break; + + case Uml::lvt_Interface: + ot = Uml::ot_Interface; + break; + + case Uml::lvt_Datatype: + ot = Uml::ot_Datatype; + break; + + case Uml::lvt_Enum: + ot = Uml::ot_Enum; + break; + + case Uml::lvt_Entity: + ot = Uml::ot_Entity; + break; + + case Uml::lvt_EntityAttribute: + ot = Uml::ot_EntityAttribute; + break; + + case Uml::lvt_Attribute: + ot = Uml::ot_Attribute; + break; + + case Uml::lvt_Operation: + ot = Uml::ot_Operation; + break; + + case Uml::lvt_Template: + ot = Uml::ot_Template; + break; + + case Uml::lvt_EnumLiteral: + ot = Uml::ot_EnumLiteral; + break; + + default: + if (typeIsFolder(lvt)) + ot = Uml::ot_Folder; + break; + } + return ot; +} + +Uml::Icon_Type convert_LVT_IT(Uml::ListView_Type lvt) { + Uml::Icon_Type icon = Uml::it_Home; + switch (lvt) { + case Uml::lvt_UseCase_View: + case Uml::lvt_UseCase_Folder: + icon = Uml::it_Folder_Grey; + break; + case Uml::lvt_Logical_View: + case Uml::lvt_Logical_Folder: + icon = Uml::it_Folder_Green; + break; + case Uml::lvt_Datatype_Folder: + icon = Uml::it_Folder_Orange; + break; + case Uml::lvt_Component_View: + case Uml::lvt_Component_Folder: + icon = Uml::it_Folder_Red; + break; + case Uml::lvt_Deployment_View: + case Uml::lvt_Deployment_Folder: + icon = Uml::it_Folder_Violet; + break; + case Uml::lvt_EntityRelationship_Model: + case Uml::lvt_EntityRelationship_Folder: + icon = Uml::it_Folder_Cyan; + break; + + case Uml::lvt_Actor: + icon = Uml::it_Actor; + break; + case Uml::lvt_UseCase: + icon = Uml::it_UseCase; + break; + case Uml::lvt_Class: + icon = Uml::it_Class; + break; + case Uml::lvt_Package: + icon = Uml::it_Package; + break; + case Uml::lvt_Subsystem: + icon = Uml::it_Subsystem; + break; + case Uml::lvt_Component: + icon = Uml::it_Component; + break; + case Uml::lvt_Node: + icon = Uml::it_Node; + break; + case Uml::lvt_Artifact: + icon = Uml::it_Artifact; + break; + case Uml::lvt_Interface: + icon = Uml::it_Interface; + break; + case Uml::lvt_Datatype: + icon = Uml::it_Datatype; + break; + case Uml::lvt_Enum: + icon = Uml::it_Enum; + break; + case Uml::lvt_Entity: + icon = Uml::it_Entity; + break; + case Uml::lvt_Template: + icon = Uml::it_Template; + break; + case Uml::lvt_Attribute: + icon = Uml::it_Private_Attribute; + break; + case Uml::lvt_EntityAttribute: + icon = Uml::it_Private_Attribute; + break; + case Uml::lvt_EnumLiteral: + icon = Uml::it_Public_Attribute; + break; + case Uml::lvt_Operation: + icon = Uml::it_Public_Method; + break; + + case Uml::lvt_Class_Diagram: + icon = Uml::it_Diagram_Class; + break; + case Uml::lvt_UseCase_Diagram: + icon = Uml::it_Diagram_Usecase; + break; + case Uml::lvt_Sequence_Diagram: + icon = Uml::it_Diagram_Sequence; + break; + case Uml::lvt_Collaboration_Diagram: + icon = Uml::it_Diagram_Collaboration; + break; + case Uml::lvt_State_Diagram: + icon = Uml::it_Diagram_State; + break; + case Uml::lvt_Activity_Diagram: + icon = Uml::it_Diagram_Activity; + break; + case Uml::lvt_Component_Diagram: + icon = Uml::it_Diagram_Component; + break; + case Uml::lvt_Deployment_Diagram: + icon = Uml::it_Diagram_Deployment; + break; + case Uml::lvt_EntityRelationship_Diagram: + icon = Uml::it_Diagram_EntityRelationship; + break; + + default: + break; + } + return icon; +} + +Uml::Diagram_Type convert_LVT_DT(Uml::ListView_Type lvt) { + Uml::Diagram_Type dt = Uml::dt_Undefined; + switch (lvt) { + case Uml::lvt_Class_Diagram: + dt = Uml::dt_Class; + break; + case Uml::lvt_UseCase_Diagram: + dt = Uml::dt_UseCase; + break; + case Uml::lvt_Sequence_Diagram: + dt = Uml::dt_Sequence; + break; + case Uml::lvt_Collaboration_Diagram: + dt = Uml::dt_Collaboration; + break; + case Uml::lvt_State_Diagram: + dt = Uml::dt_State; + break; + case Uml::lvt_Activity_Diagram: + dt = Uml::dt_Activity; + break; + case Uml::lvt_Component_Diagram: + dt = Uml::dt_Component; + break; + case Uml::lvt_Deployment_Diagram: + dt = Uml::dt_Deployment; + break; + case Uml::lvt_EntityRelationship_Diagram: + dt = Uml::dt_EntityRelationship; + break; + default: + break; + } + return dt; +} + +Uml::Model_Type convert_OT_MT(Uml::Object_Type ot) { + Uml::Model_Type mt = Uml::N_MODELTYPES; + switch (ot) { + case Uml::ot_Actor: + case Uml::ot_UseCase: + mt = Uml::mt_UseCase; + break; + case Uml::ot_Component: + case Uml::ot_Artifact: + mt = Uml::mt_Component; + break; + case Uml::ot_Node: + mt = Uml::mt_Deployment; + break; + case Uml::ot_Entity: + case Uml::ot_EntityAttribute: + mt = Uml::mt_EntityRelationship; + break; + default: + mt = Uml::mt_Logical; + break; + } + return mt; +} + +} // namespace Model_Utils + diff --git a/umbrello/umbrello/model_utils.h b/umbrello/umbrello/model_utils.h new file mode 100644 index 00000000..3fa19afb --- /dev/null +++ b/umbrello/umbrello/model_utils.h @@ -0,0 +1,328 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef MODEL_UTILS_H +#define MODEL_UTILS_H + +#include +#include + +#include "umlnamespace.h" +#include "umlobjectlist.h" + +/** + * General purpose model utilities. + * @author Oliver Kellogg + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + + +// forward declarations +class UMLClassifier; +class UMLPackage; + +namespace Model_Utils { + +/** + * Determines whether the given widget type is cloneable. + * + * @param type The input Widget_Type. + * @return True if the given type is cloneable. + */ +bool isCloneable(Uml::Widget_Type type); + +/** + * Seek the given id in the given list of objects. + * Each list element may itself contain other objects + * and the search is done recursively. + * + * @param id The unique ID to seek. + * @param inList The UMLObjectList in which to search. + * @return Pointer to the UMLObject that matches the ID + * (NULL if none matches.) + */ +UMLObject * findObjectInList(Uml::IDType id, const UMLObjectList& inList); + +/** + * Find the UML object of the given type and name in the passed-in list. + * + * @param inList List in which to seek the object. + * @param name Name of the object to find. + * @param type Object_Type of the object to find (optional.) + * When the given type is ot_UMLObject the type is + * disregarded, i.e. the given name is the only + * search criterion. + * @param currentObj Object relative to which to search (optional.) + * If given then the enclosing scope(s) of this + * object are searched before the global scope. + * @return Pointer to the UMLObject found, or NULL if not found. + */ +UMLObject* findUMLObject( const UMLObjectList& inList, + const QString& name, + Uml::Object_Type type = Uml::ot_UMLObject, + UMLObject *currentObj = NULL); + +/** + * Returns a name for the new object, appended with a number + * if the default name is taken e.g. new_actor, new_actor_1 + * etc. + * @param type The object type. + * @param parentPkg The package in which to compare the name. + * @param prefix The prefix to use (optional.) + * If no prefix is given then a type related + * prefix will be chosen internally. + */ +QString uniqObjectName(Uml::Object_Type type, + UMLPackage *parentPkg, + QString prefix = QString::null); + +/** + * Return true if the given tag is a one of the common XMI + * attributes, such as: + * "name" | "visibility" | "isRoot" | "isLeaf" | "isAbstract" | + * "isActive" | "ownerScope" + */ +bool isCommonXMIAttribute(const QString &tag); + +/** + * Return true if the given type is common among the majority + * of programming languages, such as "bool" or "boolean". + * TODO: Make this depend on the active programming language. + */ +bool isCommonDataType(QString type); + +/** + * Return true if the given object type is a classifier list item type. + */ +bool isClassifierListitem(Uml::Object_Type ot); + +/** + * Return true if the listview type also has a widget representation in diagrams. + */ +bool typeIsCanvasWidget(Uml::ListView_Type type); + +/** + * Return true if the listview type is one of the predefined root views + * (root, logical, usecase, component, deployment, datatype, or entity- + * relationship view.) + */ +bool typeIsRootView(Uml::ListView_Type type); + +/** + * Return true if the listview type is a logical, usecase or component folder. + */ +bool typeIsFolder(Uml::ListView_Type type); + +/** + * Return true if the listview type may act as a container for other objects, + * i.e. if it is a folder, package, subsystem, or component. + */ +bool typeIsContainer(Uml::ListView_Type type); + +/** + * Return true if the listview type is a diagram. + */ +bool typeIsDiagram(Uml::ListView_Type type); + +/** + * Return true if the listview type is an attribute, operation, or template. + */ +bool typeIsClassifierList(Uml::ListView_Type type); + +/** + * Return the Model_Type which corresponds to the given Diagram_Type. + */ +Uml::Model_Type convert_DT_MT(Uml::Diagram_Type dt); + +/** + * Return the ListView_Type which corresponds to the given Model_Type. + */ +Uml::ListView_Type convert_MT_LVT(Uml::Model_Type mt); + +/** + * Return the Model_Type which corresponds to the given ListView_Type. + * Returns Uml::N_MODELTYPES if the list view type given does not map + * to a Model_Type. + */ +Uml::Model_Type convert_LVT_MT(Uml::ListView_Type lvt); + +/** + * Convert a diagram type enum to the equivalent list view type. + */ +Uml::ListView_Type convert_DT_LVT(Uml::Diagram_Type dt); + +/** + * Converts a list view type enum to the equivalent object type. + * + * @param lvt The ListView_Type to convert. + * @return The converted Object_Type if the listview type + * has a Uml::Object_Type representation, else 0. + */ +Uml::Object_Type convert_LVT_OT(Uml::ListView_Type lvt); + +/** + * Convert an object's type to the equivalent list view type + * + * @param o Pointer to the UMLObject whose type shall be converted + * to the equivalent Uml::ListView_Type. We cannot just + * pass in a Uml::Object_Type because a UMLFolder is mapped + * to different Uml::ListView_Type values, depending on its + * location in one of the predefined modelviews (Logical/ + * UseCase/etc.) + * @return The equivalent Uml::ListView_Type. + */ +Uml::ListView_Type convert_OT_LVT(UMLObject *o); + +/** + * Return the Icon_Type which corresponds to the given listview type. + * + * @param lvt ListView_Type to convert. + * @return The Uml::Icon_Type corresponding to the lvt. + * Returns it_Home in case no mapping to Uml::Icon_Type exists. + */ +Uml::Icon_Type convert_LVT_IT(Uml::ListView_Type lvt); + +/** + * Return the Diagram_Type which corresponds to the given listview type. + * + * @param lvt ListView_Type to convert. + * @return The Uml::Diagram_Type corresponding to the lvt. + * Returns dt_Undefined in case no mapping to Diagram_Type exists. + */ +Uml::Diagram_Type convert_LVT_DT(Uml::ListView_Type lvt); + +/** + * Return the Model_Type which corresponds to the given Object_Type. + */ +Uml::Model_Type convert_OT_MT(Uml::Object_Type ot); + +/** + * Try to guess the correct container folder type of an UMLObject. + * Object types that can't be guessed are mapped to Uml::mt_Logical. + * NOTE: This function exists mainly for handling pre-1.5.5 files + * and should not be used for new code. + */ +Uml::Model_Type guessContainer(UMLObject *o); + +/** + * Parse a direction string into the Uml::Parameter_Direction. + * + * @param input The string to parse: "in", "out", or "inout" + * optionally followed by whitespace. + * @param result The corresponding Uml::Parameter_Direction. + * @return Length of the string matched, excluding the optional + * whitespace. + */ +int stringToDirection(QString input, Uml::Parameter_Direction & result); + +/** + * Return string corresponding to the given Uml::Programming_Language. + */ +QString progLangToString(Uml::Programming_Language pl); + +/** + * Return Uml::Programming_Language corresponding to the given string. + */ +Uml::Programming_Language stringToProgLang(QString str); + +/** + * Return type of parseOperation() + */ +enum Parse_Status { + PS_OK, PS_Empty, PS_Malformed_Arg, PS_Unknown_ArgType, + PS_Illegal_MethodName, PS_Unknown_ReturnType, PS_Unspecified_Error +}; + +/** + * Data structure filled by parseAttribute() + */ +struct NameAndType { + QString m_name; + UMLObject *m_type; + Uml::Parameter_Direction m_direction; + QString m_initialValue; + NameAndType() : m_type(0), m_direction(Uml::pd_In) { + } + NameAndType(QString name, UMLObject *type, + Uml::Parameter_Direction direction = Uml::pd_In, + QString initialValue = QString::null) + : m_name(name), m_type(type), + m_direction(direction), m_initialValue(initialValue) { + } +}; + +/** + * Auxiliary type for OpDescriptor + */ +typedef QValueList NameAndType_List; +typedef QValueListIterator NameAndType_ListIt; + +/** + * Data structure filled by parseOperation() + */ +struct OpDescriptor { + QString m_name; + NameAndType_List m_args; + UMLObject *m_pReturnType; +}; + +/** + * Parses a template parameter given in UML syntax. + * + * @param t Input text of the template parameter. + * Example: parname : partype + * or just: parname (for class type) + * @param nmTp NameAndType returned by this method. + * @param owningScope Pointer to the owning scope of the template param. + * @return Error status of the parse, PS_OK for success. + */ +Parse_Status parseTemplate(QString t, NameAndType& nmTp, UMLClassifier *owningScope); + +/** + * Parses an attribute given in UML syntax. + * + * @param a Input text of the attribute in UML syntax. + * Example: argname : argtype + * @param nmTp NameAndType returned by this method. + * @param owningScope Pointer to the owning scope of the attribute. + * @param vis Optional pointer to visibility (return value.) + * The visibility may be given at the beginning of the + * attribute text in mnemonic form as follows: + * "+" stands for public + * "#" stands for protected + * "-" stands for private + * "~" stands for implementation level visibility + * + * @return Error status of the parse, PS_OK for success. + */ +Parse_Status parseAttribute(QString a, NameAndType& nmTp, UMLClassifier *owningScope, + Uml::Visibility *vis = 0); + +/** + * Parses an operation given in UML syntax. + * + * @param m Input text of the operation in UML syntax. + * Example of a two-argument operation returning "void": + * methodname (arg1name : arg1type, arg2name : arg2type) : void + * @param desc OpDescriptor returned by this method. + * @param owningScope Pointer to the owning scope of the operation. + * @return Error status of the parse, PS_OK for success. + */ +Parse_Status parseOperation(QString m, OpDescriptor& desc, UMLClassifier *owningScope); + +/** + * Returns the Parse_Status as a text. + */ +QString psText(Parse_Status value); + +} + +#endif diff --git a/umbrello/umbrello/node.cpp b/umbrello/umbrello/node.cpp new file mode 100644 index 00000000..0070f481 --- /dev/null +++ b/umbrello/umbrello/node.cpp @@ -0,0 +1,43 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#include "node.h" +#include +#include + +UMLNode::UMLNode(const QString & name, Uml::IDType id) + : UMLCanvasObject(name, id) { + init(); +} + +UMLNode::~UMLNode() { +} + +void UMLNode::init() { + m_BaseType = Uml::ot_Node; +} + +UMLObject* UMLNode::clone() const { + UMLNode *clone = new UMLNode(); + UMLObject::copyInto(clone); + return clone; +} + +void UMLNode::saveToXMI(QDomDocument& qDoc, QDomElement& qElement) { + QDomElement nodeElement = UMLObject::save("UML:Node", qDoc); + qElement.appendChild(nodeElement); +} + +bool UMLNode::load(QDomElement& ) { + return true; +} + +#include "node.moc" diff --git a/umbrello/umbrello/node.h b/umbrello/umbrello/node.h new file mode 100644 index 00000000..057ea219 --- /dev/null +++ b/umbrello/umbrello/node.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef NODE_H +#define NODE_H + +#include "umlcanvasobject.h" + + +/** + * This class contains the non-graphical information required for a UML Node. + * This class inherits from @ref UMLCanvasObject which contains most of the + * information. + * + * @short Non-graphical information for a Node. + * @author Jonathan Riddell + * @see UMLCanvasObject + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class UMLNode : public UMLCanvasObject { + Q_OBJECT +public: + /** + * Sets up a Node. + * + * @param name The name of the Concept. + * @param id The unique id of the Concept. + */ + explicit UMLNode(const QString & name = "", Uml::IDType id = Uml::id_None); + + /** + * Empty deconstructor. + */ + virtual ~UMLNode(); + + /** + * Initializes key variables of the class. + */ + virtual void init(); + + /** + * Make a clone of this object. + */ + virtual UMLObject* clone() const; + + /** + * Creates the XMI element. + */ + void saveToXMI( QDomDocument & qDoc, QDomElement & qElement ); + +protected: + /** + * Loads the XMI element (empty.) + */ + bool load( QDomElement & element ); + +}; + +#endif diff --git a/umbrello/umbrello/nodewidget.cpp b/umbrello/umbrello/nodewidget.cpp new file mode 100644 index 00000000..2bd78a93 --- /dev/null +++ b/umbrello/umbrello/nodewidget.cpp @@ -0,0 +1,132 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "nodewidget.h" + +// qt/kde includes +#include +#include + +// app includes +#include "node.h" +#include "uml.h" +#include "umldoc.h" +#include "umlview.h" + +NodeWidget::NodeWidget(UMLView * view, UMLNode *n ) + : UMLWidget(view, n) { + UMLWidget::setBaseType(Uml::wt_Node); + setZ(m_origZ = 1); // above box but below UMLWidget because may embed widgets + setSize(100, 30); + if (n && !UMLApp::app()->getDocument()->loading()) + updateComponentSize(); +} + +NodeWidget::~NodeWidget() {} + +void NodeWidget::draw(QPainter & p, int offsetX, int offsetY) { + UMLWidget::setPen(p); + if ( UMLWidget::getUseFillColour() ) { + p.setBrush( UMLWidget::getFillColour() ); + } else { + p.setBrush( m_pView->viewport()->backgroundColor() ); + } + const int w = width(); + const int h = height(); + const int wDepth = (w/3 > DEPTH ? DEPTH : w/3); + const int hDepth = (h/3 > DEPTH ? DEPTH : h/3); + const int bodyOffsetY = offsetY + hDepth; + const int bodyWidth = w - wDepth; + const int bodyHeight = h - hDepth; + QFont font = UMLWidget::getFont(); + font.setBold(true); + const QFontMetrics &fm = getFontMetrics(FT_BOLD); + const int fontHeight = fm.lineSpacing(); + QString name = getName(); + + QPointArray pointArray(5); + pointArray.setPoint(0, offsetX, bodyOffsetY); + pointArray.setPoint(1, offsetX + wDepth, offsetY); + pointArray.setPoint(2, offsetX + w - 1, offsetY); + pointArray.setPoint(3, offsetX + w - 1, offsetY + bodyHeight ); + pointArray.setPoint(4, offsetX + bodyWidth, offsetY + h - 1); + p.drawPolygon(pointArray); + p.drawRect(offsetX, bodyOffsetY, bodyWidth, bodyHeight); + p.drawLine(offsetX + w - 1, offsetY, offsetX + bodyWidth - 2, bodyOffsetY + 1); + + p.setPen( QPen(Qt::black) ); + p.setFont(font); + + int lines = 1; + if (m_pObject) { + QString stereotype = m_pObject->getStereotype(); + if (!stereotype.isEmpty()) { + p.drawText(offsetX, bodyOffsetY + (bodyHeight/2) - fontHeight, + bodyWidth, fontHeight, Qt::AlignCenter, m_pObject->getStereotype(true)); + lines = 2; + } + } + + if ( UMLWidget::getIsInstance() ) { + font.setUnderline(true); + p.setFont(font); + name = UMLWidget::getInstanceName() + " : " + name; + } + + if (lines == 1) { + p.drawText(offsetX, bodyOffsetY + (bodyHeight/2) - (fontHeight/2), + bodyWidth, fontHeight, Qt::AlignCenter, name); + } else { + p.drawText(offsetX, bodyOffsetY + (bodyHeight/2), + bodyWidth, fontHeight, Qt::AlignCenter, name); + } + + if(m_bSelected) { + drawSelected(&p, offsetX, offsetY); + } +} + +QSize NodeWidget::calculateSize() { + if (m_pObject == NULL) { + kDebug() << "NodeWidget::calculateSize: m_pObject is NULL" << endl; + return UMLWidget::calculateSize(); + } + + const QFontMetrics &fm = getFontMetrics(FT_BOLD_ITALIC); + const int fontHeight = fm.lineSpacing(); + + QString name = m_pObject->getName(); + if ( UMLWidget::getIsInstance() ) { + name = UMLWidget::getInstanceName() + " : " + name; + } + + int width = fm.width(name); + + int tempWidth = 0; + if (!m_pObject->getStereotype().isEmpty()) { + tempWidth = fm.width(m_pObject->getStereotype(true)); + } + if (tempWidth > width) + width = tempWidth; + width += DEPTH; + + int height = (2*fontHeight) + DEPTH; + + return QSize(width, height); +} + +void NodeWidget::saveToXMI(QDomDocument& qDoc, QDomElement& qElement) { + QDomElement conceptElement = qDoc.createElement("nodewidget"); + UMLWidget::saveToXMI(qDoc, conceptElement); + qElement.appendChild(conceptElement); +} + diff --git a/umbrello/umbrello/nodewidget.h b/umbrello/umbrello/nodewidget.h new file mode 100644 index 00000000..90d28f63 --- /dev/null +++ b/umbrello/umbrello/nodewidget.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef NODEWIDGET_H +#define NODEWIDGET_H + +#include "umlwidget.h" + +class UMLNode; + +/** + * Defines a graphical version of the Node. Most of the functionality + * will come from the @ref UMLNode class. + * + * @short A graphical version of a Node. + * @author Jonathan Riddell + * @see UMLWidget + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class NodeWidget : public UMLWidget { +public: + + /** + * Constructs a NodeWidget. + * + * @param view The parent of this NodeWidget. + * @param n The UMLNode this will be representing. + */ + NodeWidget(UMLView * view, UMLNode *n ); + + /** + * destructor + */ + virtual ~NodeWidget(); + + /** + * Overrides standard method. + */ + void draw(QPainter& p, int offsetX, int offsetY); + + /** + * Saves to the "nodewidget" XMI element. + * Note: For loading we use the method inherited from UMLWidget. + */ + void saveToXMI(QDomDocument& qDoc, QDomElement& qElement); + +protected: + /** + * Overrides method from UMLWidget + */ + QSize calculateSize(); + + static const int DEPTH = 30; ///< pixels on Z axis +}; + +#endif diff --git a/umbrello/umbrello/notewidget.cpp b/umbrello/umbrello/notewidget.cpp new file mode 100644 index 00000000..0cc7d079 --- /dev/null +++ b/umbrello/umbrello/notewidget.cpp @@ -0,0 +1,316 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "notewidget.h" +//qt includes +#include +#include +#include +#include +// kde includes +#include +#include +// app includes +#include "notewidgetcontroller.h" +#include "dialogs/notedialog.h" +#include "clipboard/umldrag.h" +#include "umldoc.h" +#include "umlview.h" +#include "uml.h" +#include "listpopupmenu.h" + +#define NOTEMARGIN 10 + +NoteWidget::NoteWidget(UMLView * view, Uml::IDType id) + : UMLWidget(view, id, new NoteWidgetController(this)) { + init(); + setSize(100,80); + setZ( 20 ); //make sure always on top. +#ifdef NOTEWIDGET_EMBED_EDITOR + // NB: This code is currently deactivated because + // Zoom does not yet work with the embedded text editor. + m_pEditor = new QTextEdit(view); + m_pEditor->setFrameStyle(QFrame::NoFrame | QFrame::Plain); + m_pEditor->setHScrollBarMode(QScrollView::AlwaysOff); + m_pEditor->setVScrollBarMode(QScrollView::AlwaysOff); + m_pEditor->setTextFormat(Qt::RichText); + m_pEditor->setShown(true); + setEditorGeometry(); + connect(m_pView, SIGNAL(contentsMoving(int, int)), + this, SLOT(slotViewScrolled(int, int))); +#endif +} + +void NoteWidget::init() { + UMLWidget::setBaseType(Uml::wt_Note); + m_DiagramLink = Uml::id_None; +} + +NoteWidget::~NoteWidget() { +#ifdef NOTEWIDGET_EMBED_EDITOR + delete m_pEditor; +#endif +} + +void NoteWidget::setDiagramLink(Uml::IDType viewID) { + UMLDoc *umldoc = UMLApp::app()->getDocument(); + UMLView *view = umldoc->findView(viewID); + if (view == NULL) { + kError() << "NoteWidget::setDiagramLink(" << ID2STR(viewID) + << "): no view found for this ID." << endl; + return; + } + QString linkText("Diagram: " + view->getName()); +#if defined (NOTEWIDGET_EMBED_EDITOR) + m_pEditor->setUnderline(true); + m_pEditor->insert(linkText); + m_pEditor->setUnderline(false); +#else + setDoc(linkText); + update(); +#endif + m_DiagramLink = viewID; +} + +Uml::IDType NoteWidget::getDiagramLink() const { + return m_DiagramLink; +} + +void NoteWidget::slotViewScrolled(int x, int y) { + setEditorGeometry(x, y); +} + +void NoteWidget::setFont(QFont font) { + UMLWidget::setFont(font); +#ifdef NOTEWIDGET_EMBED_EDITOR + m_pEditor->setFont(font); +#endif +} + +void NoteWidget::setEditorGeometry(int dx /*=0*/, int dy /*=0*/) { +#if defined (NOTEWIDGET_EMBED_EDITOR) + const QRect editorGeometry( UMLWidget::getX() - dx + 6, + UMLWidget::getY() - dy + 10, + UMLWidget::getWidth() - 16, + UMLWidget::getHeight() - 16); + m_pEditor->setGeometry( editorGeometry ); + drawText(); +#else + dx=0; dy=0; // avoid "unused arg" warnings +#endif +} + +void NoteWidget::setX( int x ) { + UMLWidget::setX(x); + setEditorGeometry(); +} + +void NoteWidget::setY( int y ) { + UMLWidget::setY(y); + setEditorGeometry(); +} + +QString NoteWidget::getDoc() const { +#if defined (NOTEWIDGET_EMBED_EDITOR) + return m_pEditor->text(); +#else + return m_Text; +#endif +} + +void NoteWidget::setDoc(const QString &newText) { +#if defined (NOTEWIDGET_EMBED_EDITOR) + m_pEditor->setText(newText); +#else + m_Text = newText; +#endif +} + +void NoteWidget::draw(QPainter & p, int offsetX, int offsetY) { + int margin = 10; + int w = width()-1; + + int h= height()-1; + QPointArray poly(6); + poly.setPoint(0, offsetX, offsetY); + poly.setPoint(1, offsetX, offsetY + h); + poly.setPoint(2, offsetX + w, offsetY + h); + poly.setPoint(3, offsetX + w, offsetY + margin); + poly.setPoint(4, offsetX + w - margin, offsetY); + poly.setPoint(5, offsetX, offsetY); + UMLWidget::setPen(p); + if ( UMLWidget::getUseFillColour() ) { + QBrush brush( UMLWidget::getFillColour() ); + p.setBrush(brush); + p.drawPolygon(poly); +#if defined (NOTEWIDGET_EMBED_EDITOR) + m_pEditor->setPaper(brush); +#endif + } else + p.drawPolyline(poly); + p.drawLine(offsetX + w - margin, offsetY, offsetX + w - margin, offsetY + margin); + p.drawLine(offsetX + w - margin, offsetY + margin, offsetX + w, offsetY + margin); + if(m_bSelected) { + drawSelected(&p, offsetX, offsetY); + } + + drawText(&p, offsetX, offsetY); +} + +QSize NoteWidget::calculateSize() { + return QSize(50, 50); +} + +void NoteWidget::slotMenuSelection(int sel) { + NoteDialog * dlg = 0; + UMLDoc *doc = UMLApp::app()->getDocument(); + switch(sel) { + ///OBSOLETE - remove ListPopupMenu::mt_Link_Docs + // case ListPopupMenu::mt_Link_Docs: + // m_pView->updateNoteWidgets(); + // doc -> setModified(true); + // break; + + case ListPopupMenu::mt_Rename: + m_pView -> updateDocumentation( false ); + dlg = new NoteDialog( m_pView, this ); + if( dlg -> exec() ) { + m_pView -> showDocumentation( this, true ); + doc -> setModified(true); + update(); + } + delete dlg; + break; + + default: + UMLWidget::slotMenuSelection(sel); + break; + } +} + +void NoteWidget::drawText(QPainter * p /*=NULL*/, int offsetX /*=0*/, int offsetY /*=0*/) { +#if defined (NOTEWIDGET_EMBED_EDITOR) + m_pEditor->setText( getDoc() ); + m_pEditor->setShown(true); + m_pEditor->repaint(); +#else + if (p == NULL) + return; + /* + Implement word wrap for text as follows: + wrap at width on whole words. + if word is wider than width then clip word + if reach height exit and don't print anymore + start new line on \n character + */ + p->setPen( Qt::black ); + QFont font = UMLWidget::getFont(); + p->setFont( font ); + const QFontMetrics &fm = getFontMetrics(FT_NORMAL); + const int fontHeight = fm.lineSpacing(); + QString text = getDoc(); + if( text.length() == 0 ) + return; + QString word = ""; + QString fullLine = ""; + QString testCombineLine = ""; + const int margin = fm.width( "W" ); + int textY = fontHeight / 2; + int textX = margin; + const int width = this -> width() - margin * 2; + const int height = this -> height() - fontHeight; + QChar returnChar('\n'); + QChar c; + for (uint i = 0; i <= text.length(); i++) { + if (i < text.length()) { + c = text[i]; + } else { + // all chars of text have been handled already -> + // perform this last run to spool current content of "word" + c = returnChar; + } + if (c == returnChar || c.isSpace()) { + // new word delimiter found -> its time to decide on word wrap + testCombineLine = fullLine + ' ' + word; + int textWidth = fm.width( testCombineLine ); + if (textX + textWidth > width) { + // combination of "fullLine" and "word" doesn't fit into one line -> + // print "fullLine" in current line, update write position to next line + // and decide then on following actions + p->drawText(offsetX + textX, offsetY + textY, + textWidth, fontHeight, Qt::AlignLeft, fullLine ); + fullLine = word; + word = ""; + // update write position + textX = margin; + textY += fontHeight; + if (textY > height) + return; + // in case of c==newline -> + // print "word" and set write position one additional line lower + if (c == returnChar) { + // print "word" - which is now "fullLine" and set to next line + p->drawText(offsetX + textX, offsetY + textY, + textWidth, fontHeight, Qt::AlignLeft, fullLine); + fullLine = ""; + textX = margin; + textY += fontHeight; + if( textY > height ) return; + } + } + else if ( c == returnChar ) { + // newline found and combination of "fullLine" and "word" fits + // in one line + p->drawText(offsetX + textX, offsetY + textY, + textWidth, fontHeight, Qt::AlignLeft, testCombineLine); + fullLine = word = ""; + textX = margin; + textY += fontHeight; + if (textY > height) + return; + } else { + // word delimiter found, and combination of "fullLine", space and "word" fits into one line + fullLine = testCombineLine; + word = ""; + } + } else { + // no word delimiter found --> add current char to "word" + if (c != '\0') + word += c; + } + }//end for +#endif +} + +void NoteWidget::saveToXMI( QDomDocument & qDoc, QDomElement & qElement ) { + QDomElement noteElement = qDoc.createElement( "notewidget" ); + UMLWidget::saveToXMI( qDoc, noteElement ); + noteElement.setAttribute( "text", getDoc() ); + if (m_DiagramLink != Uml::id_None) + noteElement.setAttribute( "diagramlink", ID2STR(m_DiagramLink) ); + qElement.appendChild( noteElement ); +} + +bool NoteWidget::loadFromXMI( QDomElement & qElement ) { + if( !UMLWidget::loadFromXMI( qElement ) ) + return false; + setZ( 20 ); //make sure always on top. + setDoc( qElement.attribute("text", "") ); + QString diagramlink = qElement.attribute("diagramlink", ""); + if (!diagramlink.isEmpty()) + m_DiagramLink = STR2ID(diagramlink); + return true; +} + + +#include "notewidget.moc" + diff --git a/umbrello/umbrello/notewidget.h b/umbrello/umbrello/notewidget.h new file mode 100644 index 00000000..298839cb --- /dev/null +++ b/umbrello/umbrello/notewidget.h @@ -0,0 +1,146 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef NOTEWIDGET_H +#define NOTEWIDGET_H + +//app includes +#include "umlwidget.h" + +// forward declarations +class NoteWidgetController; + +// Qt forward declarations +class QPainter; +class QTextEdit; + +/** + * Displays a note box to allow multiple lines of text to be displayed. + * These widgets are diagram specific. They will still need a unique id + * from the @ref UMLDoc class for deletion and other purposes. + * + * @short Displays a note box. + * @author Paul Hensgen + * @see UMLWidget + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class NoteWidget : public UMLWidget { + Q_OBJECT +public: + friend class NoteWidgetController; + + /** + * Constructs a NoteWidget. + * + * @param view The parent to this widget. + * @param noteType The NoteWidget::NoteType of this NoteWidget + * @param id The unique id of the widget. + * The default (-1) will prompt a new ID. + */ + explicit NoteWidget(UMLView * view, Uml::IDType id = Uml::id_None ); + + /** + * destructor + */ + virtual ~NoteWidget(); + + /** + * Overrides method from UMLWidget. + */ + QSize calculateSize(); + + /** + * Returns the text in the box. + * + * @return The text in the box. + */ + QString getDoc() const; + + /** + * Sets the note documentation. + * + * @param newText The text to set the documentation to. + */ + void setDoc(const QString &newText); + + /** + * Set the ID of the diagram hyperlinked to this note. + * To switch off the hyperlink, set this to Uml::id_None. + * + * @param viewID ID of an UMLView. + */ + void setDiagramLink(Uml::IDType viewID); + + /** + * Return the ID of the diagram hyperlinked to this note. + * + * @return ID of an UMLView, or Uml::id_None if no + * hyperlink is set. + */ + Uml::IDType getDiagramLink() const; + + /** + * Override default method. + */ + void draw(QPainter & p, int offsetX, int offsetY); + + /** + * Override method from UMLWidget. + */ + void setFont(QFont font); + + /** + * Override method from UMLWidget. + */ + void setX(int x); + + /** + * Override method from UMLWidget. + */ + void setY(int y); + + /** + * Saves to the "notewidget" XMI element. + */ + void saveToXMI( QDomDocument & qDoc, QDomElement & qElement ); + + /** + * Loads a "notewidget" XMI element. + */ + bool loadFromXMI( QDomElement & qElement ); + +public slots: + void slotMenuSelection(int sel); + void slotViewScrolled(int x, int y); + +protected: + // Data loaded/saved + Uml::IDType m_DiagramLink; + + /** + * Draws the text. Auxiliary to draw(). + */ + void drawText(QPainter * p = NULL, int offsetX = 0, int offsetY = 0); +private: + /** + * Initializes key variables for the class. + */ + void init(); + + void setEditorGeometry(int dx = 0, int dy = 0); +#if defined (NOTEWIDGET_EMBED_EDITOR) + QTextEdit *m_pEditor; +#else + QString m_Text; +#endif +}; + +#endif diff --git a/umbrello/umbrello/notewidgetcontroller.cpp b/umbrello/umbrello/notewidgetcontroller.cpp new file mode 100644 index 00000000..e61a7d76 --- /dev/null +++ b/umbrello/umbrello/notewidgetcontroller.cpp @@ -0,0 +1,49 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// app includes +#include "notewidgetcontroller.h" +#include "notewidget.h" +#include "uml.h" +#include "umldoc.h" +#include "listpopupmenu.h" + +NoteWidgetController::NoteWidgetController(NoteWidget *noteWidget): + UMLWidgetController(noteWidget) { + m_noteWidget = noteWidget; +} + +NoteWidgetController::~NoteWidgetController() { +} + +void NoteWidgetController::mouseMoveEvent(QMouseEvent *me) { + UMLWidgetController::mouseMoveEvent(me); + m_noteWidget->setEditorGeometry(); +} + +void NoteWidgetController::mouseReleaseEvent(QMouseEvent *me) { + UMLWidgetController::mouseReleaseEvent(me); + //TODO why is it needed? drawText is already called in draw, + //and draw is (well, I think that is) called when the canvas rectangle is resized + if (m_resized) { + m_noteWidget->drawText(); + } +} + +void NoteWidgetController::doMouseDoubleClick(QMouseEvent *me) { + //TODO Copied from old code. What it does? + if (m_noteWidget->m_DiagramLink == Uml::id_None) { + m_noteWidget->slotMenuSelection(ListPopupMenu::mt_Rename); + } else { + UMLDoc *umldoc = UMLApp::app()->getDocument(); + umldoc->changeCurrentView(m_noteWidget->m_DiagramLink); + } +} diff --git a/umbrello/umbrello/notewidgetcontroller.h b/umbrello/umbrello/notewidgetcontroller.h new file mode 100644 index 00000000..f3295ce7 --- /dev/null +++ b/umbrello/umbrello/notewidgetcontroller.h @@ -0,0 +1,81 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef NOTEWIDGETCONTROLLER_H +#define NOTEWIDGETCONTROLLER_H + +#include "umlwidgetcontroller.h" + +class NoteWidget; + +/** + * Controller for NoteWidget. + * + * MouseMove and MouseRelease execute the base code and then specific code + * for note widget. + * + * Double click behaviour is edit the text of the note. + * + * @author Umbrello UML Modeller Authors + */ +class NoteWidgetController : public UMLWidgetController { +public: + + /** + * Constructor for NoteWidgetController. + * + * @param noteWidget The NoteWidget which uses the controller. + */ + NoteWidgetController(NoteWidget* noteWidget); + + /** + * Destructor for NoteWidgetController. + */ + virtual ~NoteWidgetController(); + + /** + * Overriden from UMLWidgetController. + * Handles a mouse move event. + * Executes base code and then sets the geometry of the editor. + * + * @param me The QMouseEvent event. + */ + virtual void mouseMoveEvent(QMouseEvent* me); + + /** + * Overriden from UMLWidgetController. + * Handles a mouse release event. + * Executes base code and then draws the text in the note. + * + * @param me The QMouseEvent event. + */ + virtual void mouseReleaseEvent(QMouseEvent * me); + +protected: + + /** + * Overriden from UMLWidgetController. + * Executes the action for double click in the widget. + * Shows the dialog to change the text of the note. + * + * @param me The QMouseEvent which triggered the double click event. + */ + virtual void doMouseDoubleClick(QMouseEvent *me); + +private: + + /** + * The note widget which uses the controller. + */ + NoteWidget* m_noteWidget; +}; + +#endif diff --git a/umbrello/umbrello/object_factory.cpp b/umbrello/umbrello/object_factory.cpp new file mode 100644 index 00000000..f6fcbe41 --- /dev/null +++ b/umbrello/umbrello/object_factory.cpp @@ -0,0 +1,283 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "object_factory.h" + +// qt/kde includes +#include +#include +#include +#include +#include +#include +#include + +// app includes +#include "umlobject.h" +#include "umlpackagelist.h" +#include "package.h" +#include "folder.h" +#include "classifier.h" +#include "attribute.h" +#include "operation.h" +#include "enum.h" +#include "entity.h" +#include "actor.h" +#include "usecase.h" +#include "component.h" +#include "node.h" +#include "artifact.h" +#include "stereotype.h" +#include "association.h" +#include "umldoc.h" +#include "uml.h" +#include "codegenerator.h" +#include "model_utils.h" +#include "uniqueid.h" + +namespace Object_Factory { + +Uml::IDType g_predefinedId = Uml::id_None; + +void assignUniqueIdOnCreation(bool yesno) { + if (yesno) + g_predefinedId = Uml::id_None; + else + g_predefinedId = Uml::id_Reserved; +} + +bool assignUniqueIdOnCreation() { + return (g_predefinedId == Uml::id_None); +} + +UMLObject* createNewUMLObject(Uml::Object_Type type, const QString &name, + UMLPackage *parentPkg) { + if (parentPkg == NULL) { + kError() << "Object_Factory::createNewUMLObject(" << name + << "): parentPkg is NULL" << endl; + return NULL; + } + UMLObject *o = NULL; + switch (type) { + case Uml::ot_Actor: + o = new UMLActor(name, g_predefinedId); + break; + case Uml::ot_UseCase: + o = new UMLUseCase(name, g_predefinedId); + break; + case Uml::ot_Class: + o = new UMLClassifier(name, g_predefinedId); + break; + case Uml::ot_Package: + o = new UMLPackage(name, g_predefinedId); + break; + case Uml::ot_Component: + o = new UMLComponent(name, g_predefinedId); + break; + case Uml::ot_Node: + o = new UMLNode(name, g_predefinedId); + break; + case Uml::ot_Artifact: + o = new UMLArtifact(name, g_predefinedId); + break; + case Uml::ot_Interface: { + UMLClassifier *c = new UMLClassifier(name, g_predefinedId); + c->setBaseType(Uml::ot_Interface); + o = c; + break; + } + case Uml::ot_Datatype: { + UMLClassifier *c = new UMLClassifier(name, g_predefinedId); + c->setBaseType(Uml::ot_Datatype); + o = c; + break; + } + case Uml::ot_Enum: + o = new UMLEnum(name, g_predefinedId); + break; + case Uml::ot_Entity: + o = new UMLEntity(name, g_predefinedId); + break; + case Uml::ot_Folder: + o = new UMLFolder(name, g_predefinedId); + break; + default: + kWarning() << "createNewUMLObject error unknown type: " << type << endl; + return NULL; + } + o->setUMLPackage(parentPkg); + UMLDoc *doc = UMLApp::app()->getDocument(); + parentPkg->addObject(o); + doc->signalUMLObjectCreated(o); + kapp->processEvents(); + return o; +} + +UMLObject* createUMLObject(Uml::Object_Type type, const QString &n, + UMLPackage *parentPkg /* = NULL */, + bool solicitNewName /* = true */) { + UMLDoc *doc = UMLApp::app()->getDocument(); + if (parentPkg == NULL) { + if (type == Uml::ot_Datatype) { + parentPkg = doc->getDatatypeFolder(); + } else { + Uml::Model_Type mt = Model_Utils::convert_OT_MT(type); + kDebug() << "Object_Factory::createUMLObject(" << n << "): " + << "parentPkg is not set, assuming Model_Type " << mt << endl; + parentPkg = doc->getRootFolder(mt); + } + } + if (!n.isEmpty()) { + UMLObject *o = doc->findUMLObject(n, type, parentPkg); + if (o) { + if (!solicitNewName) + return o; + } else { + o = createNewUMLObject(type, n, parentPkg); + return o; + } + } + bool ok = false; + QString name = Model_Utils::uniqObjectName(type, parentPkg, n); + bool bValidNameEntered = false; + do { + name = KInputDialog::getText(i18n("Name"), i18n("Enter name:"), name, &ok, (QWidget*)UMLApp::app()); + if (!ok) { + return 0; + } + if (name.length() == 0) { + KMessageBox::error(0, i18n("That is an invalid name."), + i18n("Invalid Name")); + continue; + } + CodeGenerator *codegen = UMLApp::app()->getGenerator(); + if (codegen != NULL && codegen->isReservedKeyword(name)) { + KMessageBox::error(0, i18n("This is a reserved keyword for the language of the configured code generator."), + i18n("Reserved Keyword")); + continue; + } + if (! doc->isUnique(name, parentPkg)) { + KMessageBox::error(0, i18n("That name is already being used."), + i18n("Not a Unique Name")); + continue; + } + bValidNameEntered = true; + } while (bValidNameEntered == false); + UMLObject *o = createNewUMLObject(type, name, parentPkg); + return o; +} + +UMLAttribute *createAttribute(UMLObject *parent, const QString& name, UMLObject *type) { + UMLAttribute *attr = new UMLAttribute(parent); + attr->setName(name); + attr->setType(type); + if (g_predefinedId == Uml::id_None) + attr->setID(UniqueID::gen()); + return attr; +} + +UMLOperation *createOperation(UMLClassifier *parent, const QString& name) { + UMLOperation *op = new UMLOperation(parent, name, g_predefinedId); + return op; +} + +UMLClassifierListItem* createChildObject(UMLClassifier* parent, Uml::Object_Type type) { + UMLObject* returnObject = NULL; + switch (type) { + case Uml::ot_Attribute: + case Uml::ot_EntityAttribute: { + UMLClassifier *c = dynamic_cast(parent); + if (c && !c->isInterface()) + returnObject = c->createAttribute(); + break; + } + case Uml::ot_Operation: { + UMLClassifier *c = dynamic_cast(parent); + if (c) + returnObject = c->createOperation(); + break; + } + case Uml::ot_Template: { + UMLClassifier *c = dynamic_cast(parent); + if (c) + returnObject = c->createTemplate(); + break; + } + case Uml::ot_EnumLiteral: { + UMLEnum* umlenum = dynamic_cast(parent); + if (umlenum) { + returnObject = umlenum->createEnumLiteral(); + } + break; + } + default: + kDebug() << "ERROR UMLDoc::createChildObject type:" << type << endl; + } + return static_cast(returnObject); +} + +UMLObject* makeObjectFromXMI(const QString& xmiTag, + const QString& stereoID /* = QString::null */) { + UMLObject* pObject = 0; + if (Uml::tagEq(xmiTag, "UseCase")) { + pObject = new UMLUseCase(); + } else if (Uml::tagEq(xmiTag, "Actor")) { + pObject = new UMLActor(); + } else if (Uml::tagEq(xmiTag, "Class")) { + pObject = new UMLClassifier(); + } else if (Uml::tagEq(xmiTag, "Package")) { + if (!stereoID.isEmpty()) { + UMLDoc *doc = UMLApp::app()->getDocument(); + UMLObject *stereo = doc->findStereotypeById(STR2ID(stereoID)); + if (stereo && stereo->getName() == "folder") + pObject = new UMLFolder(); + } + if (pObject == NULL) + pObject = new UMLPackage(); + } else if (Uml::tagEq(xmiTag, "Component")) { + pObject = new UMLComponent(); + } else if (Uml::tagEq(xmiTag, "Node")) { + pObject = new UMLNode(); + } else if (Uml::tagEq(xmiTag, "Artifact")) { + pObject = new UMLArtifact(); + } else if (Uml::tagEq(xmiTag, "Interface")) { + UMLClassifier *c = new UMLClassifier(); + c->setBaseType(Uml::ot_Interface); + pObject = c; + } else if (Uml::tagEq(xmiTag, "DataType") || Uml::tagEq(xmiTag, "Primitive") + || Uml::tagEq(xmiTag, "Datatype")) { // for bkwd compat. + UMLClassifier *c = new UMLClassifier(); + c->setBaseType(Uml::ot_Datatype); + pObject = c; + } else if (Uml::tagEq(xmiTag, "Enumeration") || + Uml::tagEq(xmiTag, "Enum")) { // for bkwd compat. + pObject = new UMLEnum(); + } else if (Uml::tagEq(xmiTag, "Entity")) { + pObject = new UMLEntity(); + } else if (Uml::tagEq(xmiTag, "Stereotype")) { + pObject = new UMLStereotype(); + } else if (Uml::tagEq(xmiTag, "Association") || + Uml::tagEq(xmiTag, "AssociationClass")) { + pObject = new UMLAssociation(); + } else if (Uml::tagEq(xmiTag, "Generalization")) { + pObject = new UMLAssociation(Uml::at_Generalization); + } else if (Uml::tagEq(xmiTag, "Realization") || + Uml::tagEq(xmiTag, "Abstraction")) { + pObject = new UMLAssociation(Uml::at_Realization); + } else if (Uml::tagEq(xmiTag, "Dependency")) { + pObject = new UMLAssociation(Uml::at_Dependency); + } + return pObject; +} + +} // end namespace Object_Factory + diff --git a/umbrello/umbrello/object_factory.h b/umbrello/umbrello/object_factory.h new file mode 100644 index 00000000..c7bf71d9 --- /dev/null +++ b/umbrello/umbrello/object_factory.h @@ -0,0 +1,84 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef OBJECT_FACTORY__H +#define OBJECT_FACTORY__H + +#include +#include "umlnamespace.h" + +class UMLObject; +class UMLPackage; +class UMLClassifier; +class UMLClassifierListItem; +class UMLAttribute; +class UMLOperation; + +namespace Object_Factory { + +/** + * Creates a UMLObject of the given type. + * + * @param type The type of @ref UMLObject to create. + * @param n A name to give to the object (optional.) + * If not given then an input dialog prompts + * the user to supply a name. + * @param parentPkg The object's parent package. + * @param solicitNewName Ask user for a different name if an object + * of the given name already exists. + * If set to false and the name already exists + * then the existing object is returned. + * The default is to ask for the new name. + */ +UMLObject* createUMLObject(Uml::Object_Type type, + const QString &n = QString::null, + UMLPackage *parentPkg = 0, + bool solicitNewName = true); + +/** + * Creates an operation, attribute, template, or enum literal + * for the parent classifier. + * + * @param parent The parent concept + * @param type The type to create + * @return Pointer to the UMLClassifierListItem created + */ +UMLClassifierListItem* createChildObject(UMLClassifier *parent, Uml::Object_Type type); + +UMLAttribute *createAttribute(UMLObject *parent, const QString& name, + UMLObject *type = 0); + +UMLOperation *createOperation(UMLClassifier *parent, const QString& name); + +/** + * Control whether the createUMLObject() solicits a new unique ID for the + * created object. + * By default, unique ID generation is turned on. + * + * @param yesno False turns UID generation off, true turns it on. + */ +void assignUniqueIdOnCreation(bool yesno); + +/** + * Return whether unique ID generation is on or off. + */ +bool assignUniqueIdOnCreation(); + +/** + * Make a new UMLObject according to the given XMI tag. + * Used by loadFromXMI and clipboard paste. + */ +UMLObject* makeObjectFromXMI(const QString& xmiTag, + const QString& stereoID = QString::null); + +} + +#endif diff --git a/umbrello/umbrello/objectwidget.cpp b/umbrello/umbrello/objectwidget.cpp new file mode 100644 index 00000000..35e7bdde --- /dev/null +++ b/umbrello/umbrello/objectwidget.cpp @@ -0,0 +1,403 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header file +#include "objectwidget.h" + +// system includes +#include +#include +#include +#include +#include +#include + +// local includes +#include "objectwidgetcontroller.h" +#include "seqlinewidget.h" +#include "umlview.h" +#include "umldoc.h" +#include "uml.h" +#include "umlobject.h" +#include "listpopupmenu.h" +#include "docwindow.h" +#include "dialogs/classpropdlg.h" + +/** + * The number of pixels margin between the lowest message + * and the bottom of the vertical line + */ +static const int sequenceLineMargin = 20; + +ObjectWidget::ObjectWidget(UMLView * view, UMLObject *o, Uml::IDType lid) + : UMLWidget(view, o) { + init(); + if( lid != Uml::id_None ) + m_nLocalID = lid; + //updateComponentSize(); + // Doing this during loadFromXMI() gives futile updates. + // Instead, it is done afterwards by UMLWidget::activate() +} + +void ObjectWidget::init() { + UMLWidget::setBaseType(Uml::wt_Object); + m_nLocalID = Uml::id_None; + m_InstanceName = ""; + m_bMultipleInstance = false; + m_bDrawAsActor = false; + m_bShowDestruction = false; + messageWidgetList.setAutoDelete(false); + if( m_pView != NULL && m_pView -> getType() == Uml::dt_Sequence ) { + m_pLine = new SeqLineWidget( m_pView, this ); + + //Sets specific widget controller for sequence diagrams + delete m_widgetController; + m_widgetController = new ObjectWidgetController(this); + } else { + m_pLine = NULL; + } +} + +ObjectWidget::~ObjectWidget() {} + +void ObjectWidget::draw(QPainter & p , int offsetX, int offsetY) { + if ( m_bDrawAsActor ) + drawActor( p, offsetX, offsetY ); + else + drawObject( p, offsetX, offsetY ); + + UMLWidget::setPen(p); + if(m_bSelected) + drawSelected(&p, offsetX, offsetY); +} + +void ObjectWidget::slotMenuSelection(int sel) { + QString name = ""; + switch(sel) { + case ListPopupMenu::mt_Rename_Object: + { + bool ok; + QRegExpValidator* validator = new QRegExpValidator(QRegExp(".*"), 0); + name = KInputDialog::getText + (i18n("Rename Object"), + i18n("Enter object name:"), + m_InstanceName, + &ok, + m_pView, + "renameobject", + validator); + if (ok) { + m_InstanceName = name; + updateComponentSize(); + moveEvent( 0 ); + update(); + UMLApp::app()->getDocument()->setModified(true); + } + delete validator; + break; + } + case ListPopupMenu::mt_Properties: + showProperties(); + updateComponentSize(); + moveEvent( 0 ); + update(); + break; + + case ListPopupMenu::mt_Up: + tabUp(); + break; + + case ListPopupMenu::mt_Down: + tabDown(); + break; + + default: + UMLWidget::slotMenuSelection(sel); + break; + } +} + +QSize ObjectWidget::calculateSize() { + int width, height; + const QFontMetrics &fm = getFontMetrics(FT_UNDERLINE); + const int fontHeight = fm.lineSpacing(); + const QString t = m_InstanceName + " : " + m_pObject->getName(); + const int textWidth = fm.width(t); + if ( m_bDrawAsActor ) { + width = textWidth > A_WIDTH?textWidth:A_WIDTH; + height = A_HEIGHT + fontHeight + A_MARGIN; + width += A_MARGIN * 2; + } else { + width = textWidth > O_WIDTH?textWidth:O_WIDTH; + height = fontHeight + O_MARGIN * 2; + width += O_MARGIN * 2; + if (m_bMultipleInstance) { + width += 10; + height += 10; + } + }//end else drawasactor + + return QSize(width, height); +} + +void ObjectWidget::setDrawAsActor( bool drawAsActor ) { + m_bDrawAsActor = drawAsActor; + updateComponentSize(); +} + +void ObjectWidget::setMultipleInstance(bool multiple) { + //make sure only calling this in relation to an object on a collab. diagram + if(m_pView -> getType() != Uml::dt_Collaboration) + return; + m_bMultipleInstance = multiple; + updateComponentSize(); + update(); +} + +bool ObjectWidget::activate(IDChangeLog* ChangeLog /*= 0*/) { + if (! UMLWidget::activate(ChangeLog)) + return false; + if (m_bShowDestruction && m_pLine) + m_pLine->setupDestructionBox(); + moveEvent(0); + return true; +} + +void ObjectWidget::setX( int x ) { + UMLWidget::setX(x); + moveEvent(0); +} + +void ObjectWidget::setY( int y ) { + UMLWidget::setY(y); + moveEvent(0); +} + +void ObjectWidget::moveEvent(QMoveEvent */*m*/) { + emit sigWidgetMoved( m_nLocalID ); + if (m_pLine) { + const int x = getX(); // for debugging: gdb has a problem evaluating getX() etc + const int w = width(); + const int y = getY(); + const int h = height(); + m_pLine->setStartPoint(x + w / 2, y + h); + } +} + +void ObjectWidget::slotColorChanged(Uml::IDType /*viewID*/) { + UMLWidget::setFillColour( m_pView->getFillColor() ); + UMLWidget::setLineColor( m_pView->getLineColor() ); + + if( m_pLine) + m_pLine -> setPen( QPen( UMLWidget::getLineColor(), UMLWidget::getLineWidth(), Qt::DashLine ) ); +} + +void ObjectWidget::cleanup() { + + UMLWidget::cleanup(); + if( m_pLine ) { + m_pLine -> cleanup(); + delete m_pLine; + } +} + +void ObjectWidget::showProperties() { + DocWindow *docwindow = UMLApp::app()->getDocWindow(); + docwindow->updateDocumentation(false); + ClassPropDlg *dlg = new ClassPropDlg((QWidget*)UMLApp::app(), this); + if (dlg->exec()) { + docwindow->showDocumentation(this, true); + UMLApp::app()->getDocument()->setModified(true); + } + dlg->close(true);//wipe from memory +} + +void ObjectWidget::drawObject(QPainter & p, int offsetX, int offsetY) { + + QFont oldFont = p.font(); + QFont font = UMLWidget::getFont(); + font.setUnderline( true ); + p.setFont( font ); + + UMLWidget::setPen(p); + if(UMLWidget::getUseFillColour()) + p.setBrush(UMLWidget::getFillColour()); + else + p.setBrush(m_pView -> viewport() -> backgroundColor()); + const int w = width(); + const int h = height(); + + const QString t = m_InstanceName + " : " + m_pObject -> getName(); + int multiInstOfst = 0; + if ( m_bMultipleInstance ) { + p.drawRect(offsetX + 10, offsetY + 10, w - 10, h - 10); + p.drawRect(offsetX + 5, offsetY + 5, w - 10, h - 10); + multiInstOfst = 10; + } + p.drawRect(offsetX, offsetY, w - multiInstOfst, h - multiInstOfst); + p.setPen(QPen(Qt::black)); + p.drawText(offsetX + O_MARGIN, offsetY + O_MARGIN, + w - O_MARGIN * 2 - multiInstOfst, h - O_MARGIN * 2 - multiInstOfst, + Qt::AlignCenter, t); + + p.setFont( oldFont ); +} + +void ObjectWidget::drawActor(QPainter & p, int offsetX, int offsetY) { + const QFontMetrics &fm = getFontMetrics(FT_UNDERLINE); + + UMLWidget::setPen(p); + if ( UMLWidget::getUseFillColour() ) + p.setBrush( UMLWidget::getFillColour() ); + const int w = width(); + const int textStartY = A_HEIGHT + A_MARGIN; + const int fontHeight = fm.lineSpacing(); + + const int middleX = offsetX + w / 2; + const int thirdH = A_HEIGHT / 3; + + //draw actor + p.drawEllipse(middleX - A_WIDTH / 2, offsetY, A_WIDTH, thirdH);//head + p.drawLine(middleX, offsetY + thirdH, middleX, offsetY + thirdH * 2);//body + p.drawLine(middleX, offsetY + 2 * thirdH, + middleX - A_WIDTH / 2, offsetY + A_HEIGHT);//left leg + p.drawLine(middleX, offsetY + 2 * thirdH, + middleX + A_WIDTH / 2, offsetY + A_HEIGHT);//right leg + p.drawLine(middleX - A_WIDTH / 2, offsetY + thirdH + thirdH / 2, + middleX + A_WIDTH / 2, offsetY + thirdH + thirdH / 2);//arms + //draw text + p.setPen(QPen(Qt::black)); + QString t = m_InstanceName + " : " + m_pObject -> getName(); + p.drawText(offsetX + A_MARGIN, offsetY + textStartY, + w - A_MARGIN * 2, fontHeight, Qt::AlignCenter, t); +} + +void ObjectWidget::tabUp() { + int newY = getY() - height(); + if (newY < topMargin()) + newY = topMargin(); + setY( newY ); + moveEvent( 0 ); + adjustAssocs( getX(), newY); +} + +void ObjectWidget::tabDown() { + int newY = getY() + height(); + setY( newY ); + moveEvent( 0 ); + adjustAssocs( getX(), newY); +} + +int ObjectWidget::topMargin() { + return 80 - height(); +} + +bool ObjectWidget::canTabUp() { + int y = getY(); + //kDebug() << "ObjectWidget::canTabUp: y is " << y << endl; + return (y > topMargin()); +} + +void ObjectWidget::setShowDestruction( bool bShow ) { + m_bShowDestruction = bShow; + if( m_pLine ) + m_pLine -> setupDestructionBox(); +} + +int ObjectWidget::getEndLineY() { + int y = this -> getY() + getHeight(); + if( m_pLine) + y += m_pLine -> getLineLength(); + if ( m_bShowDestruction ) + y += 10; + return y; +} + +void ObjectWidget::messageAdded(MessageWidget* message) { + if (messageWidgetList.containsRef(message) ) { + kError() << "ObjectWidget::messageAdded(" + << message->getName() << ") : duplicate entry !" + << endl; + return ; + } + messageWidgetList.append(message); +} + +void ObjectWidget::messageRemoved(MessageWidget* message) { + if ( messageWidgetList.remove(message) == false ) { + kError() << "ObjectWidget::messageRemoved(" + << message->getName() << ") : missing entry !" + << endl; + return ; + } +} + +void ObjectWidget::slotMessageMoved() { + MessageWidgetListIt iterator(messageWidgetList); + MessageWidget* message; + int lowestMessage = 0; + while ( (message = iterator.current()) != 0 ) { + ++iterator; + int messageHeight = message->getY() + message->getHeight(); + if (lowestMessage < messageHeight) { + lowestMessage = messageHeight; + } + } + m_pLine->setEndOfLine(lowestMessage + sequenceLineMargin); +} + +bool ObjectWidget::messageOverlap(int y, MessageWidget* messageWidget) { + MessageWidgetListIt iterator(messageWidgetList); + MessageWidget* message; + while ( (message = iterator.current()) != 0 ) { + ++iterator; + const int msgY = message->getY(); + const int msgHeight = msgY + message->getHeight(); + if (y >= msgY && y <= msgHeight && message != messageWidget) { + return true; + } + } + return false; +} + +SeqLineWidget *ObjectWidget::getSeqLine() { + return m_pLine; +} + +void ObjectWidget::saveToXMI( QDomDocument & qDoc, QDomElement & qElement ) { + QDomElement objectElement = qDoc.createElement( "objectwidget" ); + UMLWidget::saveToXMI( qDoc, objectElement ); + objectElement.setAttribute( "instancename", m_InstanceName ); + objectElement.setAttribute( "drawasactor", m_bDrawAsActor ); + objectElement.setAttribute( "multipleinstance", m_bMultipleInstance ); + objectElement.setAttribute( "localid", ID2STR(m_nLocalID) ); + objectElement.setAttribute( "decon", m_bShowDestruction ); + qElement.appendChild( objectElement ); +} + +bool ObjectWidget::loadFromXMI( QDomElement & qElement ) { + if( !UMLWidget::loadFromXMI( qElement ) ) + return false; + m_InstanceName = qElement.attribute( "instancename", "" ); + QString draw = qElement.attribute( "drawasactor", "0" ); + QString multi = qElement.attribute( "multipleinstance", "0" ); + QString localid = qElement.attribute( "localid", "0" ); + QString decon = qElement.attribute( "decon", "0" ); + + m_bDrawAsActor = (bool)draw.toInt(); + m_bMultipleInstance = (bool)multi.toInt(); + m_nLocalID = STR2ID(localid); + m_bShowDestruction = (bool)decon.toInt(); + return true; + +} + +#include "objectwidget.moc" diff --git a/umbrello/umbrello/objectwidget.h b/umbrello/umbrello/objectwidget.h new file mode 100644 index 00000000..bbf63053 --- /dev/null +++ b/umbrello/umbrello/objectwidget.h @@ -0,0 +1,331 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef OBJECTWIDGET_H +#define OBJECTWIDGET_H + +#define O_MARGIN 5 +#define O_WIDTH 40 +#define A_WIDTH 20 +#define A_HEIGHT 40 +#define A_MARGIN 5 + +#include "messagewidgetlist.h" +#include "messagewidget.h" + + +class SeqLineWidget; + +/** + * Displays an instance UMLObject of a concept. + * + * @short Displays an instance of a Concept. + * @author Paul Hensgen + * @see UMLWidget + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class ObjectWidget : public UMLWidget { + Q_OBJECT +public: + /** + * Creates an ObjectWidget. + * + * @param view The parent to this object. + * @param o The object it will be representing. + * @param lid The local id for the object. + */ + ObjectWidget(UMLView * view, UMLObject *o, Uml::IDType lid = Uml::id_None ); + + /** + * destructor + */ + virtual ~ObjectWidget(); + + /** + * Sets the x-coordinate. + * Reimplements the method from UMLWidget. + * + * @param x The x-coordinate to be set. + */ + virtual void setX( int x ); + + /** + * Sets the y-coordinate. + * Reimplements the method from UMLWidget. + * + * @param y The y-coordinate to be set. + */ + virtual void setY( int y ); + + /** + * Returns the local ID for this object. This ID is used so that + * many objects of the same @ref UMLObject instance can be on the + * same diagram. + * + * @return The local ID. + */ + Uml::IDType getLocalID() const { + return m_nLocalID; + } + + /** + * Returns the instance name. + * + * @return The instance name. + */ + QString getInstanceName() const { + return m_InstanceName; + } + + /** + * Sets the instance name. + * + * @param name The name to set the instance name to. + */ + void setInstanceName(const QString &name) { + m_InstanceName = name; + } + + /** + * Returns whether object is representing a multi-object. + * + * @return True if object is representing a multi-object. + */ + bool getMultipleInstance() const { + return m_bMultipleInstance; + } + + /** + * Sets whether representing a multi-instance object. + * + * @param multiple Object state. true- multi, false - single. + */ + void setMultipleInstance(bool multiple); + + /** + * Sets the local id of the object. + * + * @param id The local id of the object. + */ + void setLocalID(Uml::IDType id) { + m_nLocalID = id; + } + + /** + * Activate the object after serializing it from a QDataStream + */ + bool activate(IDChangeLog* ChangeLog = 0); + + /** + * Override default method. + */ + void draw(QPainter & p, int offsetX, int offsetY); + + /** + * Overrides the standard operation. + */ + virtual void moveEvent(QMoveEvent */*m*/); + + /** + * Used to cleanup any other widget it may need to delete. + */ + void cleanup(); + + /** + * Show a properties dialog for an ObjectWidget. + */ + void showProperties(); + + /** + * Returns whether to draw as an Actor or not. + * + * @return True if widget is drawn as an actor. + */ + bool getDrawAsActor() const { + return m_bDrawAsActor; + } + + /** + * Sets whether to draw as an Actor. + * + * @param drawAsActor True if widget shall be drawn as an actor. + */ + void setDrawAsActor( bool drawAsActor ); + + /** + * Sets whether to show deconstruction on sequence line. + * + * @param bShow True if destruction on line shall be shown. + */ + void setShowDestruction( bool bShow ); + + /** + * Returns whether to show deconstruction on sequence line. + * + * @return True if destruction on sequence line is shown. + */ + bool getShowDestruction() const { + return m_bShowDestruction; + } + + /** + * Returns the top margin constant (Y axis value) + * + * @return Y coordinate of the space between the diagram top + * and the upper edge of the ObjectWidget. + */ + int topMargin(); + + /** + * Returns the end Y co-ord of the seq. line. + * + * @return Y coordinate of the endpoint of the sequence line. + */ + int getEndLineY(); + + /** + * Add a message widget to the list. + * + * @param message Pointer to the MessageWidget to add. + */ + void messageAdded(MessageWidget* message); + + /** + * Remove a message widget from the list. + * + * @param message Pointer to the MessageWidget to remove. + */ + void messageRemoved(MessageWidget* message); + + /** + * Returns whether or not the widget can be moved vertically up. + * + * @return True if widget can be moved upwards vertically. + */ + bool canTabUp(); + + /** + * Returns whether a message is overlapping with another message. + * Used by MessageWidget::draw() methods. + * + * @param y The top of your message. + * @param messageWidget A pointer to your message so it doesn't + * check against itself. + */ + bool messageOverlap(int y, MessageWidget* messageWidget); + + /** + * Return the SeqLineWidget. + * Returns a non NULL pointer if this ObjectWidget is part of a + * sequence diagram. + */ + SeqLineWidget *getSeqLine(); + + /** + * Saves to the "objectwidget" XMI element. + */ + void saveToXMI( QDomDocument & qDoc, QDomElement & qElement ); + + /** + * Loads from a "objectwidget" XMI element. + */ + bool loadFromXMI( QDomElement & qElement ); + +public slots: + /** + * Handles a popup menu selection. + */ + void slotMenuSelection(int sel); + + /** + * Handles a color change signal. + */ + virtual void slotColorChanged(Uml::IDType viewID); + + /** + * Called when a message widget with an end on this object has + * moved up or down. + * Sets the bottom of the line to a nice position. + */ + void slotMessageMoved(); + +protected: + SeqLineWidget * m_pLine; + + /** + * Overrides method from UMLWidget + */ + QSize calculateSize(); + + /** + * Draw the object as an actor. + */ + void drawActor(QPainter & p, int offsetX, int offsetY); + + /** + * Draw the object as an object (default). + */ + void drawObject(QPainter & p, int offsetX, int offsetY); + + /** + * Move the object up on a sequence diagram. + */ + void tabUp(); + + /** + * Move the object down on a sequence diagram. + */ + void tabDown(); + + // Data loaded/saved: + + /** + * Instance name of object. + */ + QString m_InstanceName; + + /** + * Local ID used on views. Needed as a it can represent a class + * that has many objects representing it. + */ + Uml::IDType m_nLocalID; + + /** + * Determines whether to draw an object as a multiple object + * instance. + */ + bool m_bMultipleInstance; + + /** + * Determines whether the object should be drawn as an Actor or + * an Object. + */ + bool m_bDrawAsActor; + + /** + * Determines whether to show object destruction on sequence + * diagram line. + */ + bool m_bShowDestruction; + +private: + /** + * Initializes the key attributes of the class. + */ + void init(); + + /** + * A list of the message widgets with an end on this widget. + */ + MessageWidgetList messageWidgetList; +}; + +#endif diff --git a/umbrello/umbrello/objectwidgetcontroller.cpp b/umbrello/umbrello/objectwidgetcontroller.cpp new file mode 100644 index 00000000..1594b3fa --- /dev/null +++ b/umbrello/umbrello/objectwidgetcontroller.cpp @@ -0,0 +1,44 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "objectwidgetcontroller.h" + +// kde includes +#include + +// app includes +#include "objectwidget.h" +#include "listpopupmenu.h" + + +ObjectWidgetController::ObjectWidgetController(ObjectWidget* objectWidget): + UMLWidgetController(objectWidget) { +} + +ObjectWidgetController::~ObjectWidgetController() { +} + +QCursor ObjectWidgetController::getResizeCursor() { + return KCursor::sizeHorCursor(); +} + +void ObjectWidgetController::resizeWidget(int newW, int newH) { + m_widget->setSize(newW, m_widget->getHeight()); +} + +void ObjectWidgetController::moveWidgetBy(int diffX, int diffY) { + m_widget->setX(m_widget->getX() + diffX); +} + +void ObjectWidgetController::constrainMovementForAllWidgets(int &diffX, int &diffY) { + diffY = 0; +} diff --git a/umbrello/umbrello/objectwidgetcontroller.h b/umbrello/umbrello/objectwidgetcontroller.h new file mode 100644 index 00000000..1d8d699a --- /dev/null +++ b/umbrello/umbrello/objectwidgetcontroller.h @@ -0,0 +1,92 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef OBJECTWIDGETCONTROLLER_H +#define OBJECTWIDGETCONTROLLER_H + +#include "umlwidgetcontroller.h" + +class ObjectWidget; + +/** + * Controller for ObjectWidget. + * + * When moving an ObjectWidget, it is only moved along X axis. Y axis movement + * is always ignored. + * So, if the ObjectWidget is being moved as part of a selection and that + * selection is moved in X and/or Y axis, the ObjectWidget will only move in X axis. + * Also, when constraining the move of the selection because the receiver of + * mouse move events is an ObjectWidget, all the widgets are moved only in X axis. + * + * Only horizontal resize is allowed for ObjectWidget. Cursor is set to reflect this. + * + * @author Umbrello UML Modeller Authors + */ +class ObjectWidgetController : public UMLWidgetController { +public: + + /** + * Constructor for ObjectWidgetController. + * + * @param objectWidget The object widget which uses the controller. + */ + ObjectWidgetController(ObjectWidget *objectWidget); + + /** + * Destructor for ObjectWidgetController. + */ + virtual ~ObjectWidgetController(); + +protected: + + /** + * Overriden from UMLWidgetController. + * Returns the cursor to be shown when resizing the widget. + * The cursor shown is KCursor::sizeHorCursor(). + * + * @return The cursor to be shown when resizing the widget. + */ + virtual QCursor getResizeCursor(); + + /** + * Overriden from UMLWidgetController. + * Resizes the width of the object widget. + * Object widgets can only be resized horizontally, so height isn't modified. + * + * @param newW The new width for the widget. + * @param newH The new height for the widget (isn't used). + */ + virtual void resizeWidget(int newW, int newH); + + /** + * Overriden from UMLWidgetController. + * Moves the widget to a new position using the difference between the + * current position and the new position. + * Y position is ignored, and widget is only moved along X axis. + * + * @param diffX The difference between current X position and new X position. + * @param diffY The difference between current Y position and new Y position + * (isn't used). + */ + virtual void moveWidgetBy(int diffX, int diffY); + + /** + * Overriden from UMLWidgetController. + * Modifies the value of the diffX and diffY variables used to move the widgets. + * All the widgets are constrained to be moved only in X axis (diffY is set to 0). + * + * @param diffX The difference between current X position and new X position. + * @param diffY The difference between current Y position and new Y position. + */ + virtual void constrainMovementForAllWidgets(int &diffX, int &diffY); +}; + +#endif diff --git a/umbrello/umbrello/operation.cpp b/umbrello/umbrello/operation.cpp new file mode 100644 index 00000000..1052c469 --- /dev/null +++ b/umbrello/umbrello/operation.cpp @@ -0,0 +1,431 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "operation.h" + +// qt/kde includes +#include +#include +#include + +// app includes +#include "attribute.h" +#include "classifier.h" +#include "uml.h" +#include "umldoc.h" +#include "uniqueid.h" +#include "dialogs/umloperationdialog.h" + +UMLOperation::UMLOperation(const UMLClassifier *parent, const QString& name, + Uml::IDType id, Uml::Visibility s, UMLObject *rt) + : UMLClassifierListItem(parent, name, id) +{ + if (rt) + m_returnId = UniqueID::gen(); + else + m_returnId = Uml::id_None; + m_pSecondary = rt; + m_Vis = s; + m_BaseType = Uml::ot_Operation; + m_bConst = false; +} + +UMLOperation::UMLOperation(const UMLClassifier * parent) + : UMLClassifierListItem (parent) +{ + m_BaseType = Uml::ot_Operation; + m_bConst = false; +} + +UMLOperation::~UMLOperation() { +} + +void UMLOperation::setType(UMLObject *type) { + UMLClassifierListItem::setType(type); + if (m_returnId == Uml::id_None) + m_returnId = UniqueID::gen(); +} + +void UMLOperation::moveParmLeft(UMLAttribute * a) { + if (a == NULL) { + kDebug() << "UMLOperation::moveParmLeft called on NULL attribute" + << endl; + return; + } + kDebug() << "UMLOperation::moveParmLeft(" << a->getName() << ") called" + << endl; + disconnect(a,SIGNAL(modified()),this,SIGNAL(modified())); + int idx; + if ( (idx=m_List.find( a )) == -1 ) { + kDebug() << "Error move parm left " << a->getName() << endl; + return; + } + if ( idx == 0 ) + return; + m_List.remove( a ); + m_List.insert( idx-1, a ); +} + +void UMLOperation::moveParmRight(UMLAttribute * a) { + if (a == NULL) { + kDebug() << "UMLOperation::moveParmRight called on NULL attribute" + << endl; + return; + } + kDebug() << "UMLOperation::moveParmRight(" << a->getName() << ") called" + << endl; + disconnect(a,SIGNAL(modified()),this,SIGNAL(modified())); + int idx; + if ( (idx=m_List.find( a )) == -1 ) { + kDebug() << "Error move parm right " << a->getName() << endl; + return; + } + int count = m_List.count(); + if ( idx == count-1 ) + return; + m_List.remove( a ); + m_List.insert( idx+1, a ); +} + +void UMLOperation::removeParm(UMLAttribute * a, bool emitModifiedSignal /* =true */) { + if (a == NULL) { + kDebug() << "UMLOperation::removeParm called on NULL attribute" + << endl; + return; + } + kDebug() << "UMLOperation::removeParm(" << a->getName() << ") called" + << endl; + disconnect(a,SIGNAL(modified()),this,SIGNAL(modified())); + if(!m_List.remove(a)) + kDebug() << "Error removing parm " << a->getName() << endl; + + if (emitModifiedSignal) + emit modified(); +} + +UMLAttribute* UMLOperation::findParm(const QString &name) { + UMLAttribute * obj=0; + for (obj = m_List.first(); obj; obj = m_List.next()) { + if (obj->getName() == name) + return obj; + } + return 0; +} + +QString UMLOperation::toString(Uml::Signature_Type sig) { + QString s = ""; + + if(sig == Uml::st_ShowSig || sig == Uml::st_NoSig) + s = m_Vis.toString(true) + ' '; + + s += getName(); + Uml::Programming_Language pl = UMLApp::app()->getActiveLanguage(); + bool parameterlessOpNeedsParentheses = (pl != Uml::pl_Pascal && pl != Uml::pl_Ada); + + if (sig == Uml::st_NoSig || sig == Uml::st_NoSigNoVis) { + if (parameterlessOpNeedsParentheses) + s.append("()"); + return s; + } + int last = m_List.count(); + if (last) { + s.append("("); + int i = 0; + for (UMLAttribute *param = m_List.first(); param; param = m_List.next()) { + i++; + s.append(param->toString(Uml::st_SigNoVis)); + if (i < last) + s.append(", "); + } + s.append(")"); + } else if (parameterlessOpNeedsParentheses) { + s.append("()"); + } + UMLClassifier *ownParent = static_cast(parent()); + QString returnType; + UMLClassifier *retType = UMLClassifierListItem::getType(); + if (retType) { + UMLPackage *retVisibility = retType->getUMLPackage(); + if (retVisibility != ownParent && retVisibility != ownParent->getUMLPackage()) + returnType = retType->getFullyQualifiedName(); + else + returnType = retType->getName(); + } + if (returnType.length() > 0 && returnType != "void") { + s.append(" : "); + + if (returnType.startsWith("virtual ")) { + s += returnType.mid(8); + } else { + s += returnType; + } + } + return s; +} + +void UMLOperation::addParm(UMLAttribute *parameter, int position) { + if( position >= 0 && position <= (int)m_List.count() ) + m_List.insert(position,parameter); + else + m_List.append( parameter ); + UMLObject::emitModified(); + connect(parameter,SIGNAL(modified()),this,SIGNAL(modified())); +} + +QString UMLOperation::getUniqueParameterName() { + QString currentName = i18n("new_parameter"); + QString name = currentName; + for (int number = 1; findParm(name); number++) { + name = currentName + '_' + QString::number(number); + } + return name; +} + +bool UMLOperation::operator==( UMLOperation & rhs ) { + if( this == &rhs ) + return true; + + if( !UMLObject::operator==( rhs ) ) + return false; + + if( getTypeName() != rhs.getTypeName() ) + return false; + + if( m_List.count() != rhs.m_List.count() ) + return false; + + if(!(m_List == rhs.m_List)) + return false; + + return true; +} + +void UMLOperation::copyInto(UMLOperation *rhs) const +{ + UMLClassifierListItem::copyInto(rhs); + + m_List.copyInto(&(rhs->m_List)); +} + +UMLObject* UMLOperation::clone() const +{ + //FIXME: The new operation should be slaved to the NEW parent not the old. + UMLOperation *clone = new UMLOperation( static_cast(parent()) ); + copyInto(clone); + + return clone; +} + +bool UMLOperation::resolveRef() { + bool overallSuccess = UMLObject::resolveRef(); + // See remark on iteration style in UMLClassifier::resolveRef() + for (UMLAttributeListIt ait(m_List); ait.current(); ++ait) { + UMLAttribute *pAtt = ait.current(); + if (! pAtt->resolveRef()) + overallSuccess = false; + } + return overallSuccess; +} + +bool UMLOperation::isConstructorOperation() { + // if an operation has the stereotype constructor + // return true + QString strConstructor ("constructor"); + if (getStereotype() == strConstructor) + return true; + + UMLClassifier * c = static_cast(this->parent()); + QString cName = c->getName(); + QString opName = getName(); + // It's a constructor operation if the operation name + // matches that of the parent classifier. + return (cName == opName); +} + +bool UMLOperation::isDestructorOperation() { + if (getStereotype() == "destructor") + return true; + UMLClassifier * c = static_cast(this->parent()); + + QString cName = c->getName(); + QString opName = getName(); + // Special support for C++ syntax: + // It's a destructor operation if the operation name begins + // with "~" followed by the name of the parent classifier. + if (! opName.startsWith("~")) + return false; + opName.remove( QRegExp("^~\\s*") ); + return (cName == opName); +} + +bool UMLOperation::isLifeOperation() { + return (isConstructorOperation() || isDestructorOperation()); +} + +void UMLOperation::setConst(bool b) { + m_bConst = b; +} + +bool UMLOperation::getConst() const { + return m_bConst; +} + +bool UMLOperation::showPropertiesDialog(QWidget* parent) { + UMLOperationDialog dialog(parent, this); + return dialog.exec(); +} + +void UMLOperation::saveToXMI( QDomDocument & qDoc, QDomElement & qElement ) { + QDomElement operationElement = UMLObject::save("UML:Operation", qDoc); + operationElement.setAttribute( "isQuery", m_bConst ? "true" : "false" ); + QDomElement featureElement = qDoc.createElement( "UML:BehavioralFeature.parameter" ); + if (m_pSecondary) { + QDomElement retElement = qDoc.createElement("UML:Parameter"); + if (m_returnId == Uml::id_None) { + kDebug() << "UMLOperation::saveToXMI(" << m_Name + << "): m_returnId is not set, setting it now." << endl; + m_returnId = UniqueID::gen(); + } + retElement.setAttribute( "xmi.id", ID2STR(m_returnId) ); + retElement.setAttribute( "type", ID2STR(m_pSecondary->getID()) ); + retElement.setAttribute( "kind", "return" ); + featureElement.appendChild( retElement ); + } else { + kDebug() << "UMLOperation::saveToXMI: m_SecondaryId is " + << m_SecondaryId << endl; + } + //save each attribute here, type different + UMLAttribute* pAtt = 0; + for( pAtt = m_List.first(); pAtt != 0; pAtt = m_List.next() ) { + QDomElement attElement = pAtt->UMLObject::save("UML:Parameter", qDoc); + UMLClassifier *attrType = pAtt->getType(); + if (attrType) { + attElement.setAttribute( "type", ID2STR(attrType->getID()) ); + } else { + attElement.setAttribute( "type", pAtt -> getTypeName() ); + } + attElement.setAttribute( "value", pAtt -> getInitialValue() ); + + Uml::Parameter_Direction kind = pAtt->getParmKind(); + if (kind == Uml::pd_Out) + attElement.setAttribute("kind", "out"); + else if (kind == Uml::pd_InOut) + attElement.setAttribute("kind", "inout"); + // The default for the parameter kind is "in". + + featureElement.appendChild( attElement ); + } + if (featureElement.hasChildNodes()) + operationElement.appendChild( featureElement ); + qElement.appendChild( operationElement ); +} + +bool UMLOperation::load( QDomElement & element ) { + m_SecondaryId = element.attribute( "type", "" ); + QString isQuery = element.attribute( "isQuery", "" ); + if (!isQuery.isEmpty()) { + // We need this extra test for isEmpty() because load() might have been + // called again by the processing for BehavioralFeature.parameter (see below) + m_bConst = (isQuery == "true"); + } + QDomNode node = element.firstChild(); + if (node.isComment()) + node = node.nextSibling(); + QDomElement attElement = node.toElement(); + while( !attElement.isNull() ) { + QString tag = attElement.tagName(); + if (Uml::tagEq(tag, "BehavioralFeature.parameter")) { + if (! load(attElement)) + return false; + } else if (Uml::tagEq(tag, "Parameter")) { + QString kind = attElement.attribute("kind", ""); + if (kind.isEmpty()) { + // Perhaps the kind is stored in a child node: + for (QDomNode n = attElement.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (n.isComment()) + continue; + QDomElement tempElement = n.toElement(); + QString tag = tempElement.tagName(); + if (!Uml::tagEq(tag, "kind")) + continue; + kind = tempElement.attribute( "xmi.value", "" ); + break; + } + if (kind.isEmpty()) { + // kDebug() << "UMLOperation::load(" << m_Name << "): " + // << "cannot find kind, using default \"in\"." << endl; + kind = "in"; + } + } + if (kind == "return") { + QString returnId = attElement.attribute("xmi.id", ""); + if (!returnId.isEmpty()) + m_returnId = STR2ID(returnId); + m_SecondaryId = attElement.attribute( "type", "" ); + if (m_SecondaryId.isEmpty()) { + // Perhaps the type is stored in a child node: + QDomNode node = attElement.firstChild(); + while (!node.isNull()) { + if (node.isComment()) { + node = node.nextSibling(); + continue; + } + QDomElement tempElement = node.toElement(); + QString tag = tempElement.tagName(); + if (!Uml::tagEq(tag, "type")) { + node = node.nextSibling(); + continue; + } + m_SecondaryId = tempElement.attribute( "xmi.id", "" ); + if (m_SecondaryId.isEmpty()) + m_SecondaryId = tempElement.attribute( "xmi.idref", "" ); + if (m_SecondaryId.isEmpty()) { + QDomNode inner = node.firstChild(); + QDomElement tmpElem = inner.toElement(); + m_SecondaryId = tmpElem.attribute( "xmi.id", "" ); + if (m_SecondaryId.isEmpty()) + m_SecondaryId = tmpElem.attribute( "xmi.idref", "" ); + } + break; + } + if (m_SecondaryId.isEmpty()) { + kError() << "UMLOperation::load(" << m_Name << "): " + << "cannot find return type." << endl; + } + } + // Use deferred xmi.id resolution. + m_pSecondary = NULL; + } else { + UMLAttribute * pAtt = new UMLAttribute( this ); + if( !pAtt->loadFromXMI(attElement) ) { + delete pAtt; + return false; + } + if (kind == "out") + pAtt->setParmKind(Uml::pd_Out); + else if (kind == "inout") + pAtt->setParmKind(Uml::pd_InOut); + else + pAtt->setParmKind(Uml::pd_In); + m_List.append( pAtt ); + } + } + node = node.nextSibling(); + if (node.isComment()) + node = node.nextSibling(); + attElement = node.toElement(); + }//end while + return true; +} + + +#include "operation.moc" diff --git a/umbrello/umbrello/operation.h b/umbrello/umbrello/operation.h new file mode 100644 index 00000000..eb94576e --- /dev/null +++ b/umbrello/umbrello/operation.h @@ -0,0 +1,209 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef OPERATION_H +#define OPERATION_H + +#include "umlattributelist.h" +#include "classifierlistitem.h" + +class UMLClassifier; + +/** + * This class represents an operation in the UML model. + * + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class UMLOperation : public UMLClassifierListItem { + Q_OBJECT +public: + /** + * Constructs an UMLOperation. + * Not intended for general use: The operation is not tied in with + * umbrello's Qt signalling for object creation. + * If you want to create an Operation use the method in UMLDoc instead. + * + * @param parent The parent to this operation. + * @param name The name of the operation. + * @param id The id of the operation. + * @param s The visibility of the operation. + * @param rt The return type of the operation. + */ + UMLOperation(const UMLClassifier * parent, const QString& name, + Uml::IDType id = Uml::id_None, + Uml::Visibility s = Uml::Visibility::Public, + UMLObject *rt = 0); + + /** + * Constructs an UMLOperation. + * Not intended for general use: The operation is not tied in with + * umbrello's Qt signalling for object creation. + * If you want to create an Operation use the method in UMLDoc instead. + * + * @param parent The parent to this operation. + */ + UMLOperation(const UMLClassifier * parent); +public: + + /** + * destructor + */ + virtual ~UMLOperation(); + + /** + * Overloaded '==' operator. + */ + bool operator==( UMLOperation & rhs ); + + /** + * Copy the internal presentation of this object into the new + * object. + */ + virtual void copyInto(UMLOperation *rhs) const; + + /** + * Make a clone of this object. + */ + virtual UMLObject* clone() const; + + /** + * Reimplement method from UMLClassifierListItem. + * + * @param type Pointer to the type object. + */ + void setType(UMLObject *type); + + /** + * Move a parameter one position to the left. + * + * @param a The parameter to move. + */ + void moveParmLeft(UMLAttribute *a); + + /** + *Move a parameter one position to the right. + * + * @param a The parameter to move. + */ + void moveParmRight(UMLAttribute *a); + + /** + * Remove a parameter from the operation. + * + * @param a The parameter to remove. + * @param emitModifiedSignal Whether to emit the "modified" signal + * which creates an entry in the Undo stack for the + * removal. Default: true. + */ + void removeParm(UMLAttribute *a, bool emitModifiedSignal = true); + + /** + * Returns a list of parameters. + * + * @return A list of the parameters in the operation. + */ + UMLAttributeList getParmList() { + return m_List; + } + + /** + * Finds a parameter of the operation. + * + * @param name The parameter name to search for. + * @return The found parameter, 0 if not found. + */ + UMLAttribute * findParm(const QString &name); + + /** + * Returns a string representation of the operation. + * + * @param sig What type of operation string to show. + * @return The string representation of the operation. + */ + QString toString(Uml::Signature_Type sig = Uml::st_NoSig); + + /** + * Add a parameter to the operation. + * + * @param parameter The parameter to add. + * @param position The position in the parameter list. + * If position = -1 the parameter will be + * appended to the list. + */ + void addParm(UMLAttribute *parameter, int position = -1); + + /** + * Calls resolveRef() on all parameters. + * Needs to be called after all UML objects are loaded from file. + * + * @return True for success. + */ + bool resolveRef(); + + /** + * Returns an unused parameter name for a new parameter. + */ + QString getUniqueParameterName(); + + /** + * Display the properties configuration dialog for the template. + */ + bool showPropertiesDialog(QWidget* parent); + + /** + * Returns whether this operation is a constructor. + * + * @return True if this operation is a constructor. + */ + bool isConstructorOperation(); + + /** + * Returns whether this operation is a destructor. + * + * @return True if this operation is a destructor. + */ + bool isDestructorOperation(); + + /** + * Shortcut for (isConstructorOperation() || isDestructorOperation()) + * + * @return True if this operation is a constructor or destructor. + */ + bool isLifeOperation(); + + /** + * Sets whether this operation is a query (C++ "const".) + */ + void setConst(bool b); + + /** + * Returns whether this operation is a query (C++ "const".) + */ + bool getConst() const; + + /** + * Saves to the XMI element. + */ + void saveToXMI( QDomDocument & qDoc, QDomElement & qElement ); + +protected: + /** + * Loads a XMI element. + */ + bool load( QDomElement & element ); + +private: + Uml::IDType m_returnId; ///< Holds the xmi.id of the + UMLAttributeList m_List; /// Parameter list + bool m_bConst; ///< Holds the isQuery attribute of the +}; + +#endif diff --git a/umbrello/umbrello/optionstate.cpp b/umbrello/umbrello/optionstate.cpp new file mode 100644 index 00000000..16daef83 --- /dev/null +++ b/umbrello/umbrello/optionstate.cpp @@ -0,0 +1,27 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#include "optionstate.h" + +namespace Settings{ + +OptionState pd_optionState; + +OptionState& getOptionState() { + return pd_optionState; +} + +void setOptionState(const OptionState& optstate) { + pd_optionState = optstate; +} + +} // namespace Settings + diff --git a/umbrello/umbrello/optionstate.h b/umbrello/umbrello/optionstate.h new file mode 100644 index 00000000..b8429f62 --- /dev/null +++ b/umbrello/umbrello/optionstate.h @@ -0,0 +1,81 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef OPTIONSTATE_H +#define OPTIONSTATE_H + + +#include "umlnamespace.h" +#include "codeviewerstate.h" + +namespace Settings { + +enum Page +{ + page_general = 0, + page_font, + page_UI, + page_class, + page_codegen, + page_codeview +}; + +//public structs +struct GeneralState { + bool undo; + bool tabdiagrams; + bool newcodegen; + bool angularlines; + bool autosave; + int time; //old autosave time, kept for compatibility + int autosavetime; + QString autosavesuffix; ///< Text input field for suffix of autosave + bool logo; + bool tip; + bool loadlast; + Uml::Diagram_Type diagram; + QString lastFile; +}; + +struct UIState { + bool useFillColor; + QColor fillColor; + QColor lineColor; + uint lineWidth; + QFont font; +}; + +struct ClassState { + bool showVisibility; + bool showAtts; + bool showOps; + bool showStereoType; + bool showAttSig; + bool showOpSig; + bool showPackage; + Uml::Visibility defaultAttributeScope; + Uml::Visibility defaultOperationScope; +}; + +struct OptionState { + GeneralState generalState; + UIState uiState; + ClassState classState; + CodeViewerState codeViewerState; +}; + + +OptionState& getOptionState(); +void setOptionState(const OptionState& optstate); + +} // namespace Settings + +#endif diff --git a/umbrello/umbrello/ownedcodeblock.cpp b/umbrello/umbrello/ownedcodeblock.cpp new file mode 100644 index 00000000..99b3c865 --- /dev/null +++ b/umbrello/umbrello/ownedcodeblock.cpp @@ -0,0 +1,180 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +/* This code generated by: + * Author : thomas + * Date : Tue Aug 19 2003 + */ + +// own header +#include "ownedcodeblock.h" + +// qt/kde includes +#include + +// local includes +#include "association.h" +#include "classifier.h" +#include "umldoc.h" +#include "umlobject.h" +#include "umlrole.h" +#include "uml.h" +#include "codedocument.h" +#include "codegenerator.h" + + +// Constructors/Destructors +// + +OwnedCodeBlock::OwnedCodeBlock ( UMLObject * parent ) + : QObject ( (QObject*)parent, "anOwnedCodeBlock" ) +{ + initFields(parent); +} + +OwnedCodeBlock::~OwnedCodeBlock ( ) { + /* + if(m_parentObject) + m_parentObject->disconnect(this); + */ +} + +// +// Methods +// + +void OwnedCodeBlock::release () { + if(m_parentObject) + m_parentObject->disconnect(this); + m_parentObject = 0; +} + +/** + * Get the value of m_parentObject + * @return the value of m_parentObject + */ +UMLObject * OwnedCodeBlock::getParentObject () { + return m_parentObject; +} + +// Other methods +// + +void OwnedCodeBlock::setAttributesFromObject (TextBlock * obj) { + + OwnedCodeBlock * oc = dynamic_cast(obj); + if(oc) + { + m_parentObject->disconnect(this); + initFields(oc->getParentObject()); + } +} + +/** set attributes of the node that represents this class + * in the XMI document. + */ +void OwnedCodeBlock::setAttributesOnNode(QDomDocument& /*doc*/, QDomElement& elem) { + + // set local class attributes + // setting ID's takes special treatment + // as UMLRoles arent properly stored in the XMI right now. + // (change would break the XMI format..save for big version change ) + UMLRole * role = dynamic_cast(m_parentObject); + if(role) + { + elem.setAttribute("parent_id",ID2STR(role->getParentAssociation()->getID())); + // CAUTION: role_id here is numerically inverted wrt Uml::Role_Type, + // i.e. role A is 1 and role B is 0. + // I'll resist the temptation to change this - + // in order to maintain backward compatibility. + elem.setAttribute("role_id", (role->getRole() == Uml::A)); + } + else + { + elem.setAttribute("parent_id",ID2STR(m_parentObject->getID())); + //elem.setAttribute("role_id","-1"); + } + +} + +/** set the class attributes of this object from + * the passed element node. + */ +void OwnedCodeBlock::setAttributesFromNode ( QDomElement & elem) { + + // set local attributes, parent object first + QString idStr = elem.attribute("parent_id","-1"); + Uml::IDType id = STR2ID(idStr); + + // always disconnect from current parent + getParentObject()->disconnect(this); + + // now, what is the new object we want to set? + UMLObject * obj = UMLApp::app()->getDocument()->findObjectById(id); + if(obj) + { + + // FIX..one day. + // Ugh. This is UGLY, but we have to do it this way because UMLRoles + // don't go into the document list of UMLobjects, and have the same + // ID as their parent UMLAssociations. So..the drill is then special + // for Associations..in that case we need to find out which role will + // serve as the parametger here. The REAL fix, of course, would be to + // treat UMLRoles on a more even footing, but im not sure how that change + // might ripple throughout the code and cause problems. Thus, since the + // change appears to be needed for only this part, I'll do this crappy + // change instead. -b.t. + UMLAssociation * assoc = dynamic_cast(obj); + if(assoc) { + // In this case we init with indicated role child obj. + UMLRole * role = 0; + int role_id = elem.attribute("role_id","-1").toInt(); + // see comment on role_id at setAttributesOnNode() + if(role_id == 1) + role = assoc->getUMLRole(Uml::A); + else if(role_id == 0) + role = assoc->getUMLRole(Uml::B); + else // this will cause a crash + kError() << "corrupt save file? " + << "cant get proper UMLRole for ownedcodeblock uml id:" + << ID2STR(id) << " w/role_id:" << role_id << endl; + + // init using UMLRole obj + initFields ( role ); + } else + initFields ( obj); // just the regular approach + + } + else + kError() << "ERROR: can't load ownedcodeblock: parentUMLObject w/id:" + << ID2STR(id) << " not found, corrupt save file?" << endl; + +} + +void OwnedCodeBlock::initFields(UMLObject * parent ) +{ + + m_parentObject = parent; + + // one reason for being: set up the connection between + // this code block and the parent UMLObject..when the parent + // signals a change has been made, we automatically update + // ourselves + connect(m_parentObject, SIGNAL(modified()), this, SLOT(syncToParent())); +} + +/** + */ +void OwnedCodeBlock::syncToParent ( ) { + updateContent(); +} + +#include "ownedcodeblock.moc" diff --git a/umbrello/umbrello/ownedcodeblock.h b/umbrello/umbrello/ownedcodeblock.h new file mode 100644 index 00000000..b089e50e --- /dev/null +++ b/umbrello/umbrello/ownedcodeblock.h @@ -0,0 +1,100 @@ + + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +/* This code generated by: + * Author : thomas + * Date : Tue Aug 19 2003 + */ + +#ifndef OWNEDCODEBLOCK_H +#define OWNEDCODEBLOCK_H + +#include +#include +#include + +class TextBlock; +class CodeDocument; +// class CodeGenObjectWithTextBlocks; +class UMLObject; + +/** + * class OwnedCodeBlock + * Describes any codeblock which is 'owned' by a UMLobject of some sort and should + * be in sync with that parent. + */ + +class OwnedCodeBlock : virtual public QObject +{ + Q_OBJECT +public: + + // Constructors/Destructors + // + + /** + * Constructor + */ + OwnedCodeBlock ( UMLObject * parent ); + + /** + * Empty Destructor + */ + virtual ~OwnedCodeBlock ( ); + + /** + * @return UMLObject + */ + UMLObject * getParentObject ( ); + + // get the parent code document of this code block + virtual CodeDocument * getParentDocument ( ) = 0; + +protected: + + /** causes the text block to release all of its connections + * and any other text blocks that it 'owns'. + * needed to be called prior to deletion of the textblock. + */ + virtual void release (); + + /** set attributes of the node that represents this class + * in the XMI document. + */ + virtual void setAttributesOnNode ( QDomDocument & doc, QDomElement & blockElement); + + /** set the class attributes of this object from + * the passed element node. + */ + virtual void setAttributesFromNode ( QDomElement & element); + + /** set the class attributes from a passed object + */ + virtual void setAttributesFromObject (TextBlock * obj); + + /** + * This is the method called from within syncToParent + */ + virtual void updateContent ( ) = 0; + +private: + + void initFields ( UMLObject * parent ); + + UMLObject * m_parentObject; + +public slots: + + virtual void syncToParent ( ); + +}; + +#endif // OWNEDCODEBLOCK_H diff --git a/umbrello/umbrello/ownedhierarchicalcodeblock.cpp b/umbrello/umbrello/ownedhierarchicalcodeblock.cpp new file mode 100644 index 00000000..bb2b38ad --- /dev/null +++ b/umbrello/umbrello/ownedhierarchicalcodeblock.cpp @@ -0,0 +1,113 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +/* This code generated by: + * Author : thomas + * Date : Fri Aug 07 2003 + */ + +// own header +#include "ownedhierarchicalcodeblock.h" + +// qt/kde includes +#include + +// local includes +#include "association.h" +#include "umldoc.h" +#include "umlobject.h" +#include "umlrole.h" +#include "codedocument.h" +#include "codegenerator.h" + +// Constructors/Destructors +// + +OwnedHierarchicalCodeBlock::OwnedHierarchicalCodeBlock ( UMLObject *parent, CodeDocument * doc, const QString &start, const QString &end, const QString &comment) + : HierarchicalCodeBlock ( doc, start, end, comment), OwnedCodeBlock(parent) +{ + +} + +OwnedHierarchicalCodeBlock::~OwnedHierarchicalCodeBlock ( ) { } + +// +// Methods +// + + +// Accessor methods +// + +// Other methods +// + +void OwnedHierarchicalCodeBlock::release () { + OwnedCodeBlock::release(); + HierarchicalCodeBlock::release(); +} + +void OwnedHierarchicalCodeBlock::setAttributesFromObject (TextBlock * obj) { + + HierarchicalCodeBlock::setAttributesFromObject(obj); + OwnedCodeBlock::setAttributesFromObject(obj); +} + +void OwnedHierarchicalCodeBlock::setAttributesOnNode (QDomDocument & doc, QDomElement & elem ) { + + // set super-class attributes + HierarchicalCodeBlock::setAttributesOnNode(doc, elem); + OwnedCodeBlock::setAttributesOnNode(doc, elem); + + // set local class attributes + elem.setAttribute("parent_id",ID2STR(getParentObject()->getID())); + + // setting ID's takes special treatment + // as UMLRoles arent properly stored in the XMI right now. + // (change would break the XMI format..save for big version change ) + UMLRole * role = dynamic_cast(getParentObject()); + if(role) { + // see comment on role_id at OwnedCodeBlock::setAttributesOnNode() + elem.setAttribute("role_id", (role->getRole() == Uml::A)); + } + /* else + elem.setAttribute("role_id","-1"); + */ +} + +/** set the class attributes of this object from + * the passed element node. + */ +void OwnedHierarchicalCodeBlock::setAttributesFromNode ( QDomElement & root) +{ + + // set attributes from the XMI + HierarchicalCodeBlock::setAttributesFromNode(root); // superclass load + OwnedCodeBlock::setAttributesFromNode(root); // superclass load + +} + +CodeDocument * OwnedHierarchicalCodeBlock::getParentDocument() { + return TextBlock::getParentDocument(); +} + +/** + */ +void OwnedHierarchicalCodeBlock::syncToParent ( ) { + + if(getContentType() != CodeBlock::AutoGenerated) + return; + + updateContent(); +} + + +#include "ownedhierarchicalcodeblock.moc" diff --git a/umbrello/umbrello/ownedhierarchicalcodeblock.h b/umbrello/umbrello/ownedhierarchicalcodeblock.h new file mode 100644 index 00000000..af5ade94 --- /dev/null +++ b/umbrello/umbrello/ownedhierarchicalcodeblock.h @@ -0,0 +1,98 @@ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +/* This code generated by: + * Author : thomas + * Date : Fri Aug 07 2003 + */ + +#ifndef OWNEDHIERARCHICALCODEBLOCK_H +#define OWNEDHIERARCHICALCODEBLOCK_H + +#include "hierarchicalcodeblock.h" + +class UMLObject; +class CodeDocument; + +/** + * class OwnedHierarchicalCodeBlock + * A "chunk" of code within the code document that is "owned" by some + * umlobject. This is an abstract class that should be inherited. + */ + +class OwnedHierarchicalCodeBlock : public HierarchicalCodeBlock, public OwnedCodeBlock +{ + Q_OBJECT +public: + + // Constructors/Destructors + // + + /** constructor with QString so we can create & populate it in + * one step. + */ + OwnedHierarchicalCodeBlock ( UMLObject * parent, CodeDocument * parentDoc, const QString &start="", const QString &end="", const QString &comment="" ); + + /** + * Empty Destructor + */ + virtual ~OwnedHierarchicalCodeBlock ( ); + + // Public attributes + // + + // Return the parent code document + CodeDocument * getParentDocument(); + + // these next 2 methods should be supplied by inheriting classes + /** + * Save the XMI representation of this object + */ + virtual void saveToXMI ( QDomDocument & doc, QDomElement & root ) = 0; + + /** + * load params from the appropriate XMI element node. + */ + virtual void loadFromXMI ( QDomElement & root ) = 0; + +protected: + + /** causes the text block to release all of its connections + * and any other text blocks that it 'owns'. + * needed to be called prior to deletion of the textblock. + */ + virtual void release (); + + /** set attributes of the node that represents this class + * in the XMI document. + */ + virtual void setAttributesOnNode ( QDomDocument & doc, QDomElement & blockElement); + + /** set the class attributes of this object from + * the passed element node. + */ + virtual void setAttributesFromNode ( QDomElement & element); + + /** set the class attributes from a passed object + */ + virtual void setAttributesFromObject (TextBlock * obj); + + virtual void updateContent ( ) = 0; + +private: + +public slots: + + void syncToParent ( ); + + +}; + +#endif // OWNEDHIERARCHICALCODEBLOCK_H diff --git a/umbrello/umbrello/package.cpp b/umbrello/umbrello/package.cpp new file mode 100644 index 00000000..54dc18a5 --- /dev/null +++ b/umbrello/umbrello/package.cpp @@ -0,0 +1,298 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header file +#include "package.h" + +// system includes +#include +#include + +// local includes +#include "uml.h" +#include "umldoc.h" +#include "classifier.h" +#include "association.h" +#include "object_factory.h" +#include "model_utils.h" +#include "umllistview.h" +#include "umllistviewitem.h" + +using namespace Uml; + +UMLPackage::UMLPackage(const QString & name, Uml::IDType id) + : UMLCanvasObject(name, id) { + init(); +} + +UMLPackage::~UMLPackage() { +} + +void UMLPackage::init() { + m_BaseType = ot_Package; +} + +void UMLPackage::copyInto(UMLPackage *rhs) const +{ + UMLCanvasObject::copyInto(rhs); + + m_objects.copyInto(&(rhs->m_objects)); +} + +UMLObject* UMLPackage::clone() const +{ + UMLPackage *clone = new UMLPackage(); + copyInto(clone); + + return clone; +} + +void UMLPackage::addAssocToConcepts(UMLAssociation* a) { + if (! UMLAssociation::assocTypeHasUMLRepresentation(a->getAssocType()) ) + return; + Uml::IDType AId = a->getObjectId(Uml::A); + Uml::IDType BId = a->getObjectId(Uml::B); + UMLObject *o; + for (UMLObjectListIt it(m_objects); (o = it.current()) != NULL; ++it) { + UMLCanvasObject *c = dynamic_cast(o); + if (c == NULL) + continue; + if (AId == c->getID() || (BId == c->getID())) { + if (c->hasAssociation(a)) + kDebug() << "UMLPackage::addAssocToConcepts: " << c->getName() + << " already has association id=" << ID2STR(a->getID()) + << endl; + else + c->addAssociationEnd(a); + } + UMLPackage *pkg = dynamic_cast(c); + if (pkg) + pkg->addAssocToConcepts(a); + } +} + +void UMLPackage::removeAssocFromConcepts(UMLAssociation *assoc) +{ + UMLObject *o; + for (UMLObjectListIt it(m_objects); (o = it.current()) != NULL; ++it) { + UMLCanvasObject *c = dynamic_cast(o); + if (c == NULL) + continue; + if (c->hasAssociation(assoc)) + c->removeAssociationEnd(assoc); + UMLPackage *pkg = dynamic_cast(c); + if (pkg) + pkg->removeAssocFromConcepts(assoc); + } +} + +bool UMLPackage::addObject(UMLObject *pObject) { + if (pObject == NULL) { + kError() << "UMLPackage::addObject is called with a NULL object" + << endl; + return false; + } + if (m_objects.find(pObject) != -1) { + kDebug() << "UMLPackage::addObject: " << pObject->getName() + << " is already there" << endl; + return false; + } + if (pObject->getBaseType() == Uml::ot_Association) { + UMLAssociation *assoc = static_cast(pObject); + // Adding the UMLAssociation at the participating concepts is done + // again later (in UMLAssociation::resolveRef()) if they are not yet + // known right here. + if (assoc->getObject(Uml::A) && assoc->getObject(Uml::B)) { + UMLPackage *pkg = pObject->getUMLPackage(); + if (pkg != this) { + kError() << "UMLPackage " << m_Name << " addObject: " + << "assoc's UMLPackage is " << pkg->getName() << endl; + } + addAssocToConcepts(assoc); + } + } + m_objects.append( pObject ); + return true; +} + +void UMLPackage::removeObject(UMLObject *pObject) { + if (pObject->getBaseType() == Uml::ot_Association) { + UMLObject *o = const_cast(pObject); + UMLAssociation *assoc = static_cast(o); + removeAssocFromConcepts(assoc); + } + if (m_objects.findRef(pObject) == -1) + kDebug() << m_Name << " removeObject: object with id=" + << ID2STR(pObject->getID()) << "not found." << endl; + else + m_objects.remove(pObject); +} + +void UMLPackage::removeAllObjects() { + UMLCanvasObject::removeAllChildObjects(); + UMLObject *o; + while ((o = m_objects.first()) != NULL) { + UMLPackage *pkg = dynamic_cast(o); + if (pkg) + pkg->removeAllObjects(); + removeObject(o); + //delete o; + // CHECK: Direct usage of the destructor crashes on associations. + o->deleteLater(); + } +} + +UMLObjectList UMLPackage::containedObjects() { + return m_objects; +} + +UMLObject * UMLPackage::findObject(const QString &name) { + const bool caseSensitive = UMLApp::app()->activeLanguageIsCaseSensitive(); + for (UMLObjectListIt oit(m_objects); oit.current(); ++oit) { + UMLObject *obj = oit.current(); + if (caseSensitive) { + if (obj->getName() == name) + return obj; + } else if (obj->getName().lower() == name.lower()) { + return obj; + } + } + return NULL; +} + +UMLObject * UMLPackage::findObjectById(Uml::IDType id) { + return Model_Utils::findObjectInList(id, m_objects); +} + +void UMLPackage::appendClassifiers(UMLClassifierList& classifiers, + bool includeNested /* = true */) { + for (UMLObjectListIt oit(m_objects); oit.current(); ++oit) { + UMLObject *o = oit.current(); + Object_Type ot = o->getBaseType(); + if (ot == ot_Class || ot == ot_Interface || + ot == ot_Datatype || ot == ot_Enum || ot == ot_Entity) { + classifiers.append((UMLClassifier *)o); + } else if (includeNested && (ot == ot_Package || ot == ot_Folder)) { + UMLPackage *inner = static_cast(o); + inner->appendClassifiers(classifiers); + } + } +} + +void UMLPackage::appendClasses(UMLClassifierList& classes, + bool includeNested /* = true */) { + for (UMLObjectListIt oit(m_objects); oit.current(); ++oit) { + UMLObject *o = oit.current(); + Object_Type ot = o->getBaseType(); + if (ot == ot_Class) { + UMLClassifier *c = static_cast(o); + classes.append(c); + } else if (includeNested && (ot == ot_Package || ot == ot_Folder)) { + UMLPackage *inner = static_cast(o); + inner->appendClasses(classes); + } + } +} + +void UMLPackage::appendClassesAndInterfaces(UMLClassifierList& classifiers, + bool includeNested /* = true */) { + for (UMLObjectListIt oit(m_objects); oit.current(); ++oit) { + UMLObject *o = oit.current(); + Object_Type ot = o->getBaseType(); + if (ot == ot_Class || ot == ot_Interface) { + UMLClassifier *c = static_cast(o); + classifiers.append(c); + } else if (includeNested && (ot == ot_Package || ot == ot_Folder)) { + UMLPackage *inner = static_cast(o); + inner->appendClassesAndInterfaces(classifiers); + } + } +} + +void UMLPackage::appendInterfaces( UMLClassifierList& interfaces, + bool includeNested /* = true */) { + for (UMLObjectListIt oit(m_objects); oit.current(); ++oit) { + UMLObject *o = oit.current(); + Object_Type ot = o->getBaseType(); + if (ot == ot_Interface) { + UMLClassifier *c = static_cast(o); + interfaces.append(c); + } else if (includeNested && (ot == ot_Package || ot == ot_Folder)) { + UMLPackage *inner = static_cast(o); + inner->appendInterfaces(interfaces); + } + } +} + +bool UMLPackage::resolveRef() { + bool overallSuccess = UMLCanvasObject::resolveRef(); + for (UMLObjectListIt oit(m_objects); oit.current(); ++oit) { + UMLObject *obj = oit.current(); + if (! obj->resolveRef()) { + Uml::Object_Type ot = obj->getBaseType(); + if (ot != Uml::ot_Package && ot != Uml::ot_Folder) + m_objects.remove(obj); + overallSuccess = false; + } + } + return overallSuccess; +} + +void UMLPackage::saveToXMI(QDomDocument& qDoc, QDomElement& qElement) { + QDomElement packageElement = UMLObject::save("UML:Package", qDoc); + QDomElement ownedElement = qDoc.createElement("UML:Namespace.ownedElement"); + UMLObject *obj; + // save classifiers etc. + for (UMLObjectListIt oit(m_objects); (obj = oit.current()) != NULL; ++oit) + obj->saveToXMI (qDoc, ownedElement); + // save associations + for (UMLObjectListIt ait(m_List); (obj = ait.current()) != NULL; ++ait) + obj->saveToXMI (qDoc, ownedElement); + + packageElement.appendChild(ownedElement); + qElement.appendChild(packageElement); +} + +bool UMLPackage::load(QDomElement& element) { + for (QDomNode node = element.firstChild(); !node.isNull(); + node = node.nextSibling()) { + if (node.isComment()) + continue; + QDomElement tempElement = node.toElement(); + QString type = tempElement.tagName(); + if (Model_Utils::isCommonXMIAttribute(type)) + continue; + if (tagEq(type, "Namespace.ownedElement") || + tagEq(type, "Namespace.contents")) { + //CHECK: Umbrello currently assumes that nested elements + // are ownedElements anyway. + // Therefore these tags are not further interpreted. + if (! load(tempElement)) + return false; + continue; + } + UMLObject *pObject = Object_Factory::makeObjectFromXMI(type); + if( !pObject ) { + kWarning() << "UMLPackage::load: " + << "Unknown type of umlobject to create: " + << type << endl; + continue; + } + pObject->setUMLPackage(this); + if (!pObject->loadFromXMI(tempElement)) { + removeObject(pObject); + delete pObject; + } + } + return true; +} + +#include "package.moc" diff --git a/umbrello/umbrello/package.h b/umbrello/umbrello/package.h new file mode 100644 index 00000000..b7d51000 --- /dev/null +++ b/umbrello/umbrello/package.h @@ -0,0 +1,198 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef PACKAGE_H +#define PACKAGE_H + +#include "umlcanvasobject.h" +#include "umlclassifierlist.h" + +// forward declarations +class UMLAssociation; + + +/** + * This class contains the non-graphical information required for a UML + * Package. + * This class inherits from @ref UMLCanvasObject which contains most of the + * information. + * + * @short Non-graphical information for a Package. + * @author Jonathan Riddell + * @see UMLCanvasObject + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + +class UMLPackage : public UMLCanvasObject { + Q_OBJECT +public: + /** + * Sets up a Package. + * + * @param name The name of the Concept. + * @param id The unique id of the Concept. + */ + explicit UMLPackage(const QString & name = "", Uml::IDType id = Uml::id_None); + + /** + * Empty deconstructor. + */ + virtual ~UMLPackage(); + + /** + * Copy the internal presentation of this object into the new + * object. + */ + virtual void copyInto(UMLPackage *rhs) const; + + /** + * Make a clone of this object. + */ + virtual UMLObject* clone() const; + + /** + * Initializes key variables of the class. + */ + void init(); + + /** + * Adds an object in this package. + * + * @param pObject Pointer to the UMLObject to add. + * @return True if the object was actually added. + */ + bool addObject(UMLObject *pObject); + + /** + * Removes an object from this package. + * Does not physically delete the object. + * + * @param pObject Pointer to the UMLObject to be removed. + */ + void removeObject(UMLObject *pObject); + + /** + * Removes all objects from this package. + * Inner containers (e.g. nested packages) are removed recursively. + */ + virtual void removeAllObjects(); + + /** + * Returns the list of objects contained in this package. + */ + UMLObjectList containedObjects(); + + /** + * Adds an existing association to the matching concept in the list of concepts. + * The selection of the matching concept depends on the association type: + * For generalizations, the assoc is added to the concept that matches role A. + * For aggregations and compositions , the assoc is added to the concept + * that matches role B. + * + * @param assoc The association to add + */ + void addAssocToConcepts(UMLAssociation* assoc); + + /** + * Remove the association from the participating concepts. + */ + void removeAssocFromConcepts(UMLAssociation *assoc); + + /** + * Find the object of the given name in the list of contained objects. + * + * @param name The name to seek. + * @return Pointer to the UMLObject found or NULL if not found. + */ + UMLObject * findObject(const QString &name); + + /** + * Find the object of the given ID in the list of contained objects. + * + * @param id The ID to seek. + * @return Pointer to the UMLObject found or NULL if not found. + */ + UMLObject * findObjectById(Uml::IDType id); + + /** + * Append all classifiers from this package (and those from + * nested packages) to the given UMLClassifierList. + * + * @param classifiers The list to append to. + * @param includeNested Whether to include the classifiers from + * nested packages (default: true.) + */ + void appendClassifiers( UMLClassifierList& classifiers, + bool includeNested = true ); + + /** + * Append all classes from this package (and those from + * nested packages) to the given UMLClassifierList. + * + * @param classes The list to append to. + * @param includeNested Whether to include the classes from + * nested packages (default: true.) + */ + void appendClasses( UMLClassifierList& classes, bool includeNested = true ); + + /** + * Append all classes and interfaces from this package (and those + * from nested packages) to the given UMLClassifierList. + * + * @param classifiers The list to append to. + * @param includeNested Whether to include the classifiers from + * nested packages (default: true.) + */ + void appendClassesAndInterfaces(UMLClassifierList& classifiers, + bool includeNested = true); + + /** + * Append all interfaces from this package (and those from + * nested packages) to the given UMLClassifierList. + * + * @param interfaces The list to append to. + * @param includeNested Whether to include the interfaces from + * nested packages (default: true.) + */ + void appendInterfaces(UMLClassifierList& interfaces, + bool includeNested = true ); + + /** + * Resolve types. Required when dealing with foreign XMI files. + * Needs to be called after all UML objects are loaded from file. + * Overrides the method from UMLObject. + * Calls resolveRef() on each contained object. + * + * @return True for overall success. + */ + virtual bool resolveRef(); + + /** + * Creates the XMI element. + */ + virtual void saveToXMI(QDomDocument& qDoc, QDomElement& qElement); + +protected: + /** + * Loads the XMI element. + * Auxiliary to UMLObject::loadFromXMI. + */ + virtual bool load(QDomElement& element); + + /** + * References to the objects contained in this package. + * The UMLPackage is the owner of the objects. + */ + UMLObjectList m_objects; + +}; + +#endif diff --git a/umbrello/umbrello/packagewidget.cpp b/umbrello/umbrello/packagewidget.cpp new file mode 100644 index 00000000..002f723e --- /dev/null +++ b/umbrello/umbrello/packagewidget.cpp @@ -0,0 +1,133 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "packagewidget.h" + +// qt/kde includes +#include +#include + +// app includes +#include "package.h" +#include "uml.h" +#include "umldoc.h" +#include "umlview.h" +#include "umlobject.h" + + +PackageWidget::PackageWidget(UMLView * view, UMLPackage *o) + : UMLWidget(view, o) { + init(); +} + +void PackageWidget::init() { + UMLWidget::setBaseType(Uml::wt_Package); + setSize(100, 30); + setZ(m_origZ = 1); // above box but below UMLWidget because may embed widgets + m_pMenu = 0; + //set defaults from m_pView + if (m_pView) { + //check to see if correct + const Settings::OptionState& ops = m_pView->getOptionState(); + m_bShowStereotype = ops.classState.showStereoType; + } + //maybe loading and this may not be set. + if (m_pObject && !UMLApp::app()->getDocument()->loading()) + updateComponentSize(); +} + +PackageWidget::~PackageWidget() {} + +void PackageWidget::draw(QPainter & p, int offsetX, int offsetY) { + UMLWidget::setPen(p); + if ( UMLWidget::getUseFillColour() ) + p.setBrush( UMLWidget::getFillColour() ); + else + p.setBrush(m_pView -> viewport() -> backgroundColor()); + + int w = width(); + int h = height(); + QFont font = UMLWidget::getFont(); + font.setBold(true); + //FIXME italic is true when a package is first created until you click elsewhere, not sure why + font.setItalic(false); + const QFontMetrics &fm = getFontMetrics(FT_BOLD); + const int fontHeight = fm.lineSpacing(); + QString name = getName(); + + p.drawRect(offsetX, offsetY, 50, fontHeight); + if (m_pObject->getStereotype() == "subsystem") { + const int fHalf = fontHeight / 2; + const int symY = offsetY + fHalf; + const int symX = offsetX + 38; + p.drawLine(symX, symY, symX, symY + fHalf - 2); // left leg + p.drawLine(symX + 8, symY, symX + 8, symY + fHalf - 2); // right leg + p.drawLine(symX, symY, symX + 8, symY); // waist + p.drawLine(symX + 4, symY, symX + 4, symY - fHalf + 2); // head + } + p.drawRect(offsetX, offsetY + fontHeight - 1, w, h - fontHeight); + + p.setPen( QPen(Qt::black) ); + p.setFont(font); + + int lines = 1; + if (m_pObject != NULL) { + QString stereotype = m_pObject->getStereotype(); + if (!stereotype.isEmpty()) { + p.drawText(offsetX, offsetY + fontHeight + PACKAGE_MARGIN, + w, fontHeight, Qt::AlignCenter, m_pObject->getStereotype(true)); + lines = 2; + } + } + + p.drawText(offsetX, offsetY + (fontHeight*lines) + PACKAGE_MARGIN, + w, fontHeight, Qt::AlignCenter, name ); + + if(m_bSelected) { + drawSelected(&p, offsetX, offsetY); + } +} + +QSize PackageWidget::calculateSize() { + if ( !m_pObject ) { + return UMLWidget::calculateSize(); + } + + const QFontMetrics &fm = getFontMetrics(FT_BOLD_ITALIC); + const int fontHeight = fm.lineSpacing(); + + int lines = 1; + + int width = fm.width( m_pObject->getName() ); + + int tempWidth = 0; + if (!m_pObject->getStereotype().isEmpty()) { + tempWidth = fm.width(m_pObject->getStereotype(true)); + lines = 2; + } + if (tempWidth > width) + width = tempWidth; + width += PACKAGE_MARGIN * 2; + if (width < 70) + width = 70; // minumin width of 70 + + int height = (lines*fontHeight) + fontHeight + (PACKAGE_MARGIN * 2); + + return QSize(width, height); +} + +void PackageWidget::saveToXMI(QDomDocument& qDoc, QDomElement& qElement) { + QDomElement conceptElement = qDoc.createElement("packagewidget"); + UMLWidget::saveToXMI(qDoc, conceptElement); + qElement.appendChild(conceptElement); +} + diff --git a/umbrello/umbrello/packagewidget.h b/umbrello/umbrello/packagewidget.h new file mode 100644 index 00000000..5acb63d1 --- /dev/null +++ b/umbrello/umbrello/packagewidget.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef PACKAGEWIDGET_H +#define PACKAGEWIDGET_H + +#include "umlwidget.h" + +class UMLPackage; + +#define PACKAGE_MARGIN 5 + +/** + * Defines a graphical version of the Package. Most of the functionality + * will come from the @ref UMLPackage class. + * + * @short A graphical version of a Package. + * @author Jonathan Riddell + * @see UMLWidget + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class PackageWidget : public UMLWidget { +public: + + /** + * Constructs a PackageWidget. + * + * @param view The parent of this PackageWidget. + * @param o The UMLObject this will be representing. + */ + PackageWidget(UMLView * view, UMLPackage * o); + + /** + * destructor + */ + virtual ~PackageWidget(); + + /** + * Overrides standard method. + */ + void draw(QPainter& p, int offsetX, int offsetY); + + /** + * Saves to the "packagewidget" XMI element. + */ + void saveToXMI(QDomDocument& qDoc, QDomElement& qElement); + +protected: + /** + * Overrides method from UMLWidget + */ + QSize calculateSize(); + +private: + /** + * Initializes key variables of the class. + */ + void init(); + + /** + * The right mouse button menu. + */ + ListPopupMenu* m_pMenu; +}; + +#endif diff --git a/umbrello/umbrello/petalnode.cpp b/umbrello/umbrello/petalnode.cpp new file mode 100644 index 00000000..9d9aa0ad --- /dev/null +++ b/umbrello/umbrello/petalnode.cpp @@ -0,0 +1,61 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "petalnode.h" + +PetalNode::PetalNode(NodeType nt) { + m_type = nt; +} + +PetalNode::~PetalNode() { +} + +PetalNode::NodeType PetalNode::type() const { + return m_type; +} + +QStringList PetalNode::initialArgs() const { + return m_initialArgs; +} + +QString PetalNode::name() const { + if (m_initialArgs.count() == 0) + return QString(); + return m_initialArgs.first(); +} + +PetalNode::NameValueList PetalNode::attributes() const { + return m_attributes; +} + +/* +void PetalNode::setType(PetalNode::NodeType t) { + m_type = t; +} + */ + +void PetalNode::setInitialArgs(const QStringList& args) { + m_initialArgs = args; +} + +void PetalNode::setAttributes(PetalNode::NameValueList vl) { + m_attributes = vl; +} + +PetalNode::StringOrNode PetalNode::findAttribute(const QString& name) const { + for (uint i = 0; i < m_attributes.count(); i++) { + if (m_attributes[i].first == name) + return m_attributes[i].second; + } + return StringOrNode(); +} + diff --git a/umbrello/umbrello/petalnode.h b/umbrello/umbrello/petalnode.h new file mode 100644 index 00000000..705de417 --- /dev/null +++ b/umbrello/umbrello/petalnode.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef PETALNODE__H +#define PETALNODE__H + +#include +#include +#include +#include + +/** + * Rose petal node - parse tree for model import + * + * A Rose petal node can be of type: + * + object - initialArgs() contains the object type name and further + * initial arguments which depend on the exact object type + * - subordinate attributes are contained in attributes() + * + list - initialArgs() contains the list type name + * - list elements are contained in attributes() but the name + * of each NameValue is empty. + * + value - not represented as a node, instead the stripped down value + * is saved in the value string of the NameValue. + * Example: for the input + * (value Text "This is some text") + * the following is saved in the value string of the NameValue: + * "This is some text" + * + * @author Oliver Kellogg + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class PetalNode { +public: + /** + * Use `string' if it is not empty. + * Use `node' if it is not NULL. + * Either `string' is set, or `node', but never both. + * (Perhaps this should be a union but that is ugly.) + */ + struct StringOrNode { + QString string; + PetalNode *node; + StringOrNode() { node = 0; } + virtual ~StringOrNode() { } + bool isEmpty() { return (string.isEmpty() && node == 0); } + }; + typedef QPair NameValue; + typedef QValueList NameValueList; + + enum NodeType { nt_object, nt_list }; + + PetalNode(NodeType nt); + virtual ~PetalNode(); + + // getters + NodeType type() const; + QStringList initialArgs() const; // name and other initial args + QString name() const; // convenience function: equal to initialArgs().first() + NameValueList attributes() const; + // setters + //void setType(NodeType nt); see constructor + void setInitialArgs(const QStringList& args); + void setAttributes(NameValueList vl); + // utilities + /** + * Find an attribute by name. + * @return The value of the attribute. StringOrNode::isEmpty() returns true + * if the name could not be found. + */ + StringOrNode findAttribute(const QString& name) const; +private: + NodeType m_type; + QStringList m_initialArgs; + NameValueList m_attributes; +}; + +#endif + diff --git a/umbrello/umbrello/petaltree2uml.cpp b/umbrello/umbrello/petaltree2uml.cpp new file mode 100644 index 00000000..41563785 --- /dev/null +++ b/umbrello/umbrello/petaltree2uml.cpp @@ -0,0 +1,631 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "petaltree2uml.h" +// qt/kde includes +#include +#include +// app includes +#include "petalnode.h" +#include "codeimport/import_utils.h" +#include "package.h" +#include "classifier.h" +#include "attribute.h" +#include "operation.h" +#include "association.h" +#include "umlrole.h" +#include "actor.h" +#include "usecase.h" +#include "component.h" +#include "node.h" +#include "uml.h" +#include "umldoc.h" +#include "umllistview.h" +#include "umllistviewitem.h" + +namespace Import_Rose { + +/** + * Return the given string without surrounding quotation marks. + * Also remove a possible prefix "Logical View::", it is not modeled in Umbrello. + */ +QString clean(const QString& s) { + if (s.isNull()) + return QString(); + QString str = s; + str.remove("\""); + str.remove(QRegExp("^Logical View::")); + return str; +} + +/** + * Extract the quid attribute from a petal node and return it as a Uml::IDType. + */ +Uml::IDType quid(const PetalNode *node) { + QString quidStr = node->findAttribute("quid").string; + if (quidStr.isEmpty()) + return Uml::id_None; + quidStr.remove("\""); + return STR2ID(quidStr); +} + +/** + * Extract the quidu attribute from a petal node. + */ +QString quidu(const PetalNode *node) { + QString quiduStr = node->findAttribute("quidu").string; + if (quiduStr.isEmpty()) + return QString(); + quiduStr.remove("\""); + return quiduStr; +} + +/** + * Determine the model type corresponding to a name. + * If the given name consists only of letters, digits, underscores, and + * scope separators, then return Uml::ot_Class, else return Uml::ot_Datatype. + */ +Uml::Object_Type typeToCreate(const QString& name) { + QString n = name; + n.remove(QRegExp("^.*::")); // don't consider the scope prefix, it may contain spaces + Uml::Object_Type t = (n.contains(QRegExp("\\W")) ? Uml::ot_Datatype : Uml::ot_Class); + return t; +} + +/** + * Transfer the Rose attribute "exportControl" to the Umbrello object given. + * + * @param from Pointer to PetalNode from which to read the "exportControl" attribute + * @param to Pointer to UMLObject in which to set the Uml::Visibility + */ +void transferVisibility(const PetalNode *from, UMLObject *to) { + QString vis = from->findAttribute("exportControl").string; + if (!vis.isEmpty()) { + Uml::Visibility v = Uml::Visibility::fromString(clean(vis.lower())); + to->setVisibility(v); + } +} + +/** + * ClassifierListReader factors the common processing for attributes, operations, + * and operation parameters. + */ +class ClassifierListReader { +public: + /// constructor + ClassifierListReader(const char* attributeTag, + const char* elementName, + const char* itemTypeDesignator) : + m_attributeTag(attributeTag), + m_elementName(elementName), + m_itemTypeDesignator(itemTypeDesignator) { + } + /// destructor + virtual ~ClassifierListReader() {} + + /** + * Return a UMLClassifierListItem of the specific type desired. + * Abstract method to be implemented by inheriting classes. + */ + virtual UMLObject *createListItem() = 0; + + virtual void setTypeReferences(UMLObject *item, + const QString& quid, const QString& type) { + if (!quid.isEmpty()) { + item->setSecondaryId(quid); + } + if (!type.isEmpty()) { + item->setSecondaryFallback(type); + } + } + + /** + * Insert the given UMLClassifierListItem at the parent Umbrello object. + * Abstract method to be implemented by inheriting classes. + * NB the parent Umbrello object is not included in the ClassifierListReader + * class - it must be added at inheriting classes. + * + * @param node The PetalNode which corresponds to the parent Umbrello object. + * @param o The UMLObject to insert. + */ + virtual void insertAtParent(const PetalNode *node, UMLObject *o) = 0; + + /** + * Iterate over the attributes of the given PetalNode and for each recognized + * attribute do the following: + * - invoke createListItem() + * - fill common properties such as name, unique ID, visibility, etc. into + * the new UMLClassifierListItem + * - invoke insertAtParent() with the new classifier list item as the argument + * This is the user entry point. + */ + void read(const PetalNode *node, const QString& name) { + PetalNode *attributes = node->findAttribute(m_attributeTag).node; + if (attributes == NULL) { +#ifdef VERBOSE_DEBUGGING + kDebug() << "read(" << name << "): no " << m_attributeTag << " found" + << endl; +#endif + return; + } + PetalNode::NameValueList attributeList = attributes->attributes(); + for (uint i = 0; i < attributeList.count(); i++) { + PetalNode *attNode = attributeList[i].second.node; + QStringList initialArgs = attNode->initialArgs(); + if (attNode->name() != m_elementName) { + kDebug() << "read(" << name << "): expecting " << m_elementName + << ", " << "found " << initialArgs[0] << endl; + continue; + } + UMLObject *item = createListItem(); + if (initialArgs.count() > 1) + item->setName(clean(initialArgs[1])); + item->setID(quid(attNode)); + QString quidref = quidu(attNode); + QString type = clean(attNode->findAttribute(m_itemTypeDesignator).string); + setTypeReferences(item, quidref, type); + transferVisibility(attNode, item); + QString doc = attNode->findAttribute("documentation").string; + if (! doc.isEmpty()) + item->setDoc(doc); + insertAtParent(attNode, item); + } + } +protected: + const QString m_attributeTag, m_elementName, m_itemTypeDesignator; +}; + +class AttributesReader : public ClassifierListReader { +public: + AttributesReader(UMLClassifier *c) + : ClassifierListReader("class_attributes", "ClassAttribute", "type") { + m_classifier = c; + } + virtual ~AttributesReader() {} + UMLObject *createListItem() { + return new UMLAttribute(m_classifier); + } + void insertAtParent(const PetalNode *, UMLObject *item) { + m_classifier->addAttribute(static_cast(item)); + } +protected: + UMLClassifier *m_classifier; +}; + +class ParametersReader : public ClassifierListReader { +public: + ParametersReader(UMLOperation *op) + : ClassifierListReader("parameters", "Parameter", "type") { + m_operation = op; + } + virtual ~ParametersReader() {} + UMLObject *createListItem() { + return new UMLAttribute(m_operation); + } + void insertAtParent(const PetalNode *, UMLObject *item) { + m_operation->addParm(static_cast(item)); + } +protected: + UMLOperation *m_operation; +}; + +class OperationsReader : public ClassifierListReader { +public: + OperationsReader(UMLClassifier *c) + : ClassifierListReader("operations", "Operation", "result") { + m_classifier = c; + } + virtual ~OperationsReader() {} + UMLObject *createListItem() { + return new UMLOperation(m_classifier); + } + void insertAtParent(const PetalNode *node, UMLObject *item) { + UMLOperation *op = static_cast(item); + ParametersReader parmReader(op); + parmReader.read(node, m_classifier->getName()); + m_classifier->addOperation(op); + } +protected: + UMLClassifier *m_classifier; +}; + +class SuperclassesReader : public ClassifierListReader { +public: + SuperclassesReader(UMLClassifier *c) + : ClassifierListReader("superclasses", "Inheritance_Relationship", "supplier") { + m_classifier = c; + } + virtual ~SuperclassesReader() {} + UMLObject *createListItem() { + return new UMLAssociation(Uml::at_Generalization); + } + /** + * Override parent implementation: The secondary data is not for the + * UMLAssociation itself but for its role B object. + */ + void setTypeReferences(UMLObject *item, + const QString& quid, const QString& type) { + UMLAssociation *assoc = static_cast(item); + if (!quid.isEmpty()) { + assoc->getUMLRole(Uml::B)->setSecondaryId(quid); + } + if (!type.isEmpty()) { + assoc->getUMLRole(Uml::B)->setSecondaryFallback(type); + } + } + void insertAtParent(const PetalNode *, UMLObject *item) { + UMLAssociation *assoc = static_cast(item); + assoc->setObject(m_classifier, Uml::A); + UMLApp::app()->getDocument()->addAssociation(assoc); + } +protected: + UMLClassifier *m_classifier; +}; + +class RealizationsReader : public ClassifierListReader { +public: + RealizationsReader(UMLClassifier *c) + : ClassifierListReader("realized_interfaces", "Realize_Relationship", "supplier") { + m_classifier = c; + } + virtual ~RealizationsReader() {} + UMLObject *createListItem() { + return new UMLAssociation(Uml::at_Realization); + } + /** + * Override parent implementation: The secondary data is not for the + * UMLAssociation itself but for its role B object. + */ + void setTypeReferences(UMLObject *item, + const QString& quid, const QString& type) { + UMLAssociation *assoc = static_cast(item); + if (!quid.isEmpty()) { + assoc->getUMLRole(Uml::B)->setSecondaryId(quid); + } + if (!type.isEmpty()) { + assoc->getUMLRole(Uml::B)->setSecondaryFallback(type); + } + } + void insertAtParent(const PetalNode *, UMLObject *item) { + UMLAssociation *assoc = static_cast(item); + assoc->setObject(m_classifier, Uml::A); + UMLApp::app()->getDocument()->addAssociation(assoc); + } +protected: + UMLClassifier *m_classifier; +}; + +/** + * Handle a controlled unit. + * + * @param node Pointer to the PetalNode which may contain a controlled unit + * @param name Name of the current node + * @param id QUID of the current node + * @param parentPkg Pointer to the current parent UMLPackage. + * @return True if the node actually contained a controlled unit. + */ +bool handleControlledUnit(PetalNode *node, const QString& name, Uml::IDType id, UMLPackage *parentPkg) { + if (node->findAttribute("is_unit").string != "TRUE") + return false; + bool is_loaded = (node->findAttribute("is_loaded").string != "FALSE"); + QString file_name = node->findAttribute("file_name").string; + if (file_name.isEmpty()) { + kError() << "handleControlledUnit(" << name + << "): attribute file_name not found (?)" << endl; + return true; + } + // To Be Continued. + + return true; +} + +/** + * Create an Umbrello object from a PetalNode of the Logical View. + * + * @return True for success. + * Given a PetalNode for which the mapping to Umbrello is not yet + * implemented umbrellify() is a no-op but also returns true. + */ +bool umbrellify(PetalNode *node, UMLPackage *parentPkg = NULL) { + if (node == NULL) { + kError() << "umbrellify: node is NULL" << endl; + return false; + } + QStringList args = node->initialArgs(); + QString objType = args[0]; + QString name = clean(args[1]); + Uml::IDType id = quid(node); + + if (objType == "Class_Category") { + UMLObject *o = Import_Utils::createUMLObject(Uml::ot_Package, name, parentPkg); + o->setID(id); + PetalNode *logical_models = node->findAttribute("logical_models").node; + if (logical_models) { + UMLPackage *localParent = static_cast(o); + PetalNode::NameValueList atts = logical_models->attributes(); + for (uint i = 0; i < atts.count(); i++) { + umbrellify(atts[i].second.node, localParent); + } + } else if (!handleControlledUnit(node, name, id, parentPkg)) { + kDebug() << "umbrellify: handling of " << objType << " " << name + << " is not yet implemented" << endl; + } + + } else if (objType == "Class") { + UMLObject *o = Import_Utils::createUMLObject(Uml::ot_Class, name, parentPkg); + o->setID(id); + UMLClassifier *c = static_cast(o); + // set stereotype + QString stereotype = clean(node->findAttribute("stereotype").string); + if (!stereotype.isEmpty()) { + if (stereotype.lower() == "interface") + c->setBaseType(Uml::ot_Interface); + else + c->setStereotype(stereotype); + } + // insert attributes + AttributesReader attReader(c); + attReader.read(node, c->getName()); + // insert operations + OperationsReader opReader(c); + opReader.read(node, c->getName()); + // insert generalizations + SuperclassesReader superReader(c); + superReader.read(node, c->getName()); + // insert realizations + RealizationsReader realReader(c); + realReader.read(node, c->getName()); + + } else if (objType == "Association") { + PetalNode *roles = node->findAttribute("roles").node; + if (node == NULL) { + kError() << "umbrellify: cannot find roles of Association" << endl; + return false; + } + UMLAssociation *assoc = new UMLAssociation(Uml::at_UniAssociation); + PetalNode::NameValueList roleList = roles->attributes(); + for (uint i = 0; i <= 1; i++) { + PetalNode *roleNode = roleList[i].second.node; + if (roleNode == NULL) { + kError() << "umbrellify: roleNode of Association is NULL" << endl; + return false; + } + if (roleNode->name() != "Role") { + kDebug() << "umbrellify(" << name << "): expecting Role, found \"" + << roleNode->name() << endl; + continue; + } + // index 0 corresponds to Umbrello roleB + // index 1 corresponds to Umbrello roleA + UMLRole *role = assoc->getUMLRole((Uml::Role_Type) !i); + QStringList initialArgs = roleNode->initialArgs(); + if (initialArgs.count() > 1) { + QString roleName = clean(initialArgs[1]); + if (! roleName.startsWith("$UNNAMED")) + role->setName(roleName); + } + role->setID(quid(roleNode)); + QString quidref = quidu(roleNode); + QString type = clean(roleNode->findAttribute("supplier").string); + if (!quidref.isEmpty()) { + role->setSecondaryId(quidref); + } + if (!type.isEmpty()) { + role->setSecondaryFallback(type); + } + QString label = clean(roleNode->findAttribute("label").string); + if (!label.isEmpty()) { + role->setName(label); + } + QString client_cardinality = clean(roleNode->findAttribute("client_cardinality").string); + if (!client_cardinality.isEmpty()) { + role->setMultiplicity(client_cardinality); + } + QString is_navigable = clean(roleNode->findAttribute("is_navigable").string); + if (is_navigable == "FALSE") { + assoc->setAssocType(Uml::at_Association); + } + QString is_aggregate = clean(roleNode->findAttribute("is_aggregate").string); + if (is_aggregate == "TRUE") { + assoc->setAssocType(Uml::at_Aggregation); + } + QString containment = clean(roleNode->findAttribute("Containment").string); + if (containment == "By Value") { + assoc->setAssocType(Uml::at_Composition); + } + QString doc = roleNode->findAttribute("documentation").string; + if (! doc.isEmpty()) + role->setDoc(doc); + } + UMLApp::app()->getDocument()->addAssociation(assoc); + + } else { + kDebug() << "umbrellify: object type " << objType + << " is not yet implemented" << endl; + } + return true; +} + +Uml::ListView_Type folderType(UMLListViewItem *parent) { + Uml::ListView_Type type = Uml::lvt_Unknown; + switch (parent->getType()) { + case Uml::lvt_Logical_View: + case Uml::lvt_Logical_Folder: + type = Uml::lvt_Logical_Folder; + break; + case Uml::lvt_UseCase_View: + case Uml::lvt_UseCase_Folder: + type = Uml::lvt_UseCase_Folder; + break; + case Uml::lvt_Component_View: + case Uml::lvt_Component_Folder: + type = Uml::lvt_Component_Folder; + break; + case Uml::lvt_Deployment_View: + case Uml::lvt_Deployment_Folder: + type = Uml::lvt_Deployment_Folder; + break; + default: + break; + } + return type; +} + +/** + * Create an Umbrello object from a PetalNode of the UseCase, Component, + * or Deployment View. + * + * @return True for success. + * Given a PetalNode for which the mapping to Umbrello is not yet + * implemented umbrellify() is a no-op but also returns true. + */ +bool umbrellify(PetalNode *node, const QString& modelsName, UMLListViewItem *parent) { + if (node == NULL) { + kError() << "umbrellify(" << modelsName << "): node is NULL" << endl; + return false; + } + QStringList args = node->initialArgs(); + QString objType = args[0]; + QString name = clean(args[1]); + Uml::IDType id = quid(node); + UMLObject *obj = NULL; + UMLListViewItem *item = NULL; + + if (objType == "Class_Category") { + Uml::ListView_Type lvType = folderType(parent); + item = new UMLListViewItem( parent, name, lvType, id ); + } else if (objType == "Class") { + QString stereotype = clean(node->findAttribute("stereotype").string); + if (stereotype == "Actor") { + UMLActor *act = new UMLActor(name, id); + item = new UMLListViewItem(parent, name, Uml::lvt_Actor, act); + obj = act; + } else { + kDebug() << "umbrellify(" << name << "): handling of Class stereotype " + << stereotype << " is not yet implemented" << endl; + } + } else if (objType == "UseCase") { + UMLUseCase *uc = new UMLUseCase(name, id); + item = new UMLListViewItem(parent, name, Uml::lvt_UseCase, uc); + obj = uc; + } else if (objType == "SubSystem") { + UMLComponent *comp = new UMLComponent(name, id); + item = new UMLListViewItem(parent, name, Uml::lvt_Component, comp); + obj = comp; + } else if (objType == "Processor" || objType == "Device") { + UMLNode *un = new UMLNode(name, id); + un->setStereotype(objType.lower()); + item = new UMLListViewItem(parent, name, Uml::lvt_Node, un); + obj = un; + } else { + kDebug() << "umbrellify: object type " << objType + << " is not yet implemented" << endl; + return true; + } + PetalNode *models = node->findAttribute(modelsName).node; + if (models) { + PetalNode::NameValueList atts = models->attributes(); + for (uint i = 0; i < atts.count(); i++) { + if (! umbrellify(atts[i].second.node, modelsName, item)) + return false; + } + } + if (obj) { + QString doc = node->findAttribute("documentation").string; + if (! doc.isEmpty()) + obj->setDoc(doc); + UMLDoc *theDocument = UMLApp::app()->getDocument(); + theDocument->addUMLObject(obj); + } + return true; +} + +/** + * Auxiliary function for UseCase/Component/Deployment view import + */ +bool importView(PetalNode *root, const QString& rootName, + const QString& modelsName, UMLListViewItem *lvParent) { + PetalNode *viewRoot = root->findAttribute(rootName).node; + if (viewRoot == NULL) { + kDebug() << "importView: cannot find " << rootName << endl; + return false; + } + PetalNode *models = viewRoot->findAttribute(modelsName).node; + if (models == NULL) { + kError() << "importView: cannot find " << modelsName + << " of " << rootName << endl; + return false; + } + PetalNode::NameValueList atts = models->attributes(); + for (uint i = 0; i < atts.count(); i++) { + umbrellify(atts[i].second.node, modelsName, lvParent); + } + return true; +} + +bool petalTree2Uml(PetalNode *root) { + if (root == NULL) { + kError() << "petalTree2Uml: root is NULL" << endl; + return false; + } + if (root->name() != "Design") { + kError() << "petalTree2Uml: expecting root name Design" << endl; + return false; + } + /*************************** import Logical View ********************************/ + PetalNode *root_category = root->findAttribute("root_category").node; + if (root_category == NULL) { + kError() << "petalTree2Uml: cannot find root_category" << endl; + return false; + } + if (root_category->name() != "Class_Category") { + kError() << "petalTree2Uml: expecting root_category object Class_Category" + << endl; + return false; + } + PetalNode *logical_models = root_category->findAttribute("logical_models").node; + if (logical_models == NULL) { + kError() << "petalTree2Uml: cannot find logical_models" << endl; + return false; + } + UMLDoc *umldoc = UMLApp::app()->getDocument(); + umldoc->setCurrentRoot(Uml::mt_Logical); + Import_Utils::assignUniqueIdOnCreation(false); + PetalNode::NameValueList atts = logical_models->attributes(); + for (uint i = 0; i < atts.count(); i++) { + umbrellify(atts[i].second.node); + } + + /** Shorthand for UMLApp::app()->getListView() **/ + UMLListView *lv = UMLApp::app()->getListView(); + + /*************************** import Use Case View ********************************/ + umldoc->setCurrentRoot(Uml::mt_UseCase); + importView(root, "root_usecase_package", "logical_models", lv->theUseCaseView()); + + /*************************** import Component View *******************************/ + umldoc->setCurrentRoot(Uml::mt_Component); + importView(root, "root_subsystem", "physical_models", lv->theComponentView()); + + /*************************** import Deployment View ******************************/ + umldoc->setCurrentRoot(Uml::mt_Deployment); + importView(root, "process_structure", "ProcsNDevs", lv->theDeploymentView()); + + /*************************** wrap up ********************************/ + umldoc->setCurrentRoot(Uml::mt_Logical); + Import_Utils::assignUniqueIdOnCreation(true); + umldoc->resolveTypes(); + return true; +} + +} // namespace Import_Rose + diff --git a/umbrello/umbrello/petaltree2uml.h b/umbrello/umbrello/petaltree2uml.h new file mode 100644 index 00000000..a60f3cbc --- /dev/null +++ b/umbrello/umbrello/petaltree2uml.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef PETALTREE2UML_H +#define PETALTREE2UML_H + +// fwd decl +class PetalNode; + +/** + * Traverse the PetalNode tree and create corresponding Umbrello objects + * for the PetalNodes encountered. + * + * @author Oliver Kellogg + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +namespace Import_Rose { + + /** + * This is really an auxiliary method for loadFromMDL() but is kept in a + * separate file to reflect that it is not coupled with the parser + * (other than by the PetalNode.) + * + * @return true for success. + */ + bool petalTree2Uml(PetalNode *root); + +} + +#endif + diff --git a/umbrello/umbrello/pics/COPYING b/umbrello/umbrello/pics/COPYING new file mode 100644 index 00000000..5e09cc93 --- /dev/null +++ b/umbrello/umbrello/pics/COPYING @@ -0,0 +1,3 @@ +Images under this directory as well as the Umbrello and Umbrello +mimetype icons may be copied under the terms of the LGPL plus add-on as +found in kdelibs/pics/LICENSE.crystalsvg diff --git a/umbrello/umbrello/pics/CVglobal_meth.png b/umbrello/umbrello/pics/CVglobal_meth.png new file mode 100644 index 00000000..a7bb6659 Binary files /dev/null and b/umbrello/umbrello/pics/CVglobal_meth.png differ diff --git a/umbrello/umbrello/pics/CVglobal_var.png b/umbrello/umbrello/pics/CVglobal_var.png new file mode 100644 index 00000000..f7f85455 Binary files /dev/null and b/umbrello/umbrello/pics/CVglobal_var.png differ diff --git a/umbrello/umbrello/pics/CVimplementation_meth.png b/umbrello/umbrello/pics/CVimplementation_meth.png new file mode 100644 index 00000000..97b23df4 Binary files /dev/null and b/umbrello/umbrello/pics/CVimplementation_meth.png differ diff --git a/umbrello/umbrello/pics/CVimplementation_signal.png b/umbrello/umbrello/pics/CVimplementation_signal.png new file mode 100644 index 00000000..c89e1e11 Binary files /dev/null and b/umbrello/umbrello/pics/CVimplementation_signal.png differ diff --git a/umbrello/umbrello/pics/CVimplementation_slot.png b/umbrello/umbrello/pics/CVimplementation_slot.png new file mode 100644 index 00000000..c1a77f5f Binary files /dev/null and b/umbrello/umbrello/pics/CVimplementation_slot.png differ diff --git a/umbrello/umbrello/pics/CVimplementation_var.png b/umbrello/umbrello/pics/CVimplementation_var.png new file mode 100644 index 00000000..026df532 Binary files /dev/null and b/umbrello/umbrello/pics/CVimplementation_var.png differ diff --git a/umbrello/umbrello/pics/CVnamespace.png b/umbrello/umbrello/pics/CVnamespace.png new file mode 100644 index 00000000..bc40283d Binary files /dev/null and b/umbrello/umbrello/pics/CVnamespace.png differ diff --git a/umbrello/umbrello/pics/CVprivate_meth.png b/umbrello/umbrello/pics/CVprivate_meth.png new file mode 100644 index 00000000..45c7b3e1 Binary files /dev/null and b/umbrello/umbrello/pics/CVprivate_meth.png differ diff --git a/umbrello/umbrello/pics/CVprivate_signal.png b/umbrello/umbrello/pics/CVprivate_signal.png new file mode 100644 index 00000000..114a7148 Binary files /dev/null and b/umbrello/umbrello/pics/CVprivate_signal.png differ diff --git a/umbrello/umbrello/pics/CVprivate_slot.png b/umbrello/umbrello/pics/CVprivate_slot.png new file mode 100644 index 00000000..e9b8ecd6 Binary files /dev/null and b/umbrello/umbrello/pics/CVprivate_slot.png differ diff --git a/umbrello/umbrello/pics/CVprivate_var.png b/umbrello/umbrello/pics/CVprivate_var.png new file mode 100644 index 00000000..4bf6180c Binary files /dev/null and b/umbrello/umbrello/pics/CVprivate_var.png differ diff --git a/umbrello/umbrello/pics/CVprotected_meth.png b/umbrello/umbrello/pics/CVprotected_meth.png new file mode 100644 index 00000000..fe96d4db Binary files /dev/null and b/umbrello/umbrello/pics/CVprotected_meth.png differ diff --git a/umbrello/umbrello/pics/CVprotected_signal.png b/umbrello/umbrello/pics/CVprotected_signal.png new file mode 100644 index 00000000..07aef0b3 Binary files /dev/null and b/umbrello/umbrello/pics/CVprotected_signal.png differ diff --git a/umbrello/umbrello/pics/CVprotected_slot.png b/umbrello/umbrello/pics/CVprotected_slot.png new file mode 100644 index 00000000..db084899 Binary files /dev/null and b/umbrello/umbrello/pics/CVprotected_slot.png differ diff --git a/umbrello/umbrello/pics/CVprotected_var.png b/umbrello/umbrello/pics/CVprotected_var.png new file mode 100644 index 00000000..f97903f0 Binary files /dev/null and b/umbrello/umbrello/pics/CVprotected_var.png differ diff --git a/umbrello/umbrello/pics/CVpublic_meth.png b/umbrello/umbrello/pics/CVpublic_meth.png new file mode 100644 index 00000000..56063b02 Binary files /dev/null and b/umbrello/umbrello/pics/CVpublic_meth.png differ diff --git a/umbrello/umbrello/pics/CVpublic_signal.png b/umbrello/umbrello/pics/CVpublic_signal.png new file mode 100644 index 00000000..5a101850 Binary files /dev/null and b/umbrello/umbrello/pics/CVpublic_signal.png differ diff --git a/umbrello/umbrello/pics/CVpublic_slot.png b/umbrello/umbrello/pics/CVpublic_slot.png new file mode 100644 index 00000000..b04b49de Binary files /dev/null and b/umbrello/umbrello/pics/CVpublic_slot.png differ diff --git a/umbrello/umbrello/pics/CVpublic_var.png b/umbrello/umbrello/pics/CVpublic_var.png new file mode 100644 index 00000000..d17730e7 Binary files /dev/null and b/umbrello/umbrello/pics/CVpublic_var.png differ diff --git a/umbrello/umbrello/pics/CVstruct.png b/umbrello/umbrello/pics/CVstruct.png new file mode 100644 index 00000000..ec953add Binary files /dev/null and b/umbrello/umbrello/pics/CVstruct.png differ diff --git a/umbrello/umbrello/pics/Makefile.am b/umbrello/umbrello/pics/Makefile.am new file mode 100644 index 00000000..43292145 --- /dev/null +++ b/umbrello/umbrello/pics/Makefile.am @@ -0,0 +1,112 @@ +picsdir = $(kde_datadir)/umbrello/pics + +pics_DATA = actor.png \ +aggregation.png \ +align_left.png \ +align_right.png \ +align_top.png \ +align_bottom.png \ +align_vert_middle.png \ +align_hori_middle.png \ +align_vert_distribute.png \ +align_hori_distribute.png \ +anchor.png \ +andline.png \ +arrow.png \ +association.png \ +choice-rhomb.png \ +choice-round.png \ +component.png \ +composition.png \ +containment.png \ +CVglobal_meth.png \ +CVglobal_var.png \ +CVimplementation_meth.png \ +CVimplementation_signal.png \ +CVimplementation_slot.png \ +CVimplementation_var.png \ +CVnamespace.png \ +CVprivate_meth.png \ +CVprivate_signal.png \ +CVprivate_slot.png \ +CVprivate_var.png \ +CVprotected_meth.png \ +CVprotected_signal.png \ +CVprotected_slot.png \ +CVprotected_var.png \ +CVpublic_meth.png \ +CVpublic_signal.png \ +CVpublic_slot.png \ +CVpublic_var.png \ +CVstruct.png \ +deep-history.png \ +shallow-history.png \ +dependency.png \ +end_state.png \ +generalisation.png \ +initial_state.png \ +interface.png \ +join.png \ +junction.png \ +datatype.png \ +enum.png \ +message-synchronous.png \ +message-asynchronous.png \ +note.png \ +object.png \ +startlogo.png \ +template.png \ +text.png \ +class.png \ +uniassociation.png \ +usecase.png \ +branch.png \ +fork.png \ +state-fork.png \ +package.png \ +subsystem.png \ +artifact.png \ +box.png \ +node.png \ +entity.png \ +relationship.png \ +cursor-actor.png \ +cursor-andline.png \ +cursor-choice-rhomb.png \ +cursor-choice-round.png \ +cursor-deep-history.png \ +cursor-join.png \ +cursor-junction.png \ +cursor-shallow-history.png \ +cursor-state-fork.png \ +cursor-usecase.png \ +cursor-initial_state.png \ +cursor-package.png \ +cursor-aggregation.png \ +cursor-component.png \ +cursor-containment.png \ +cursor-interface.png \ +cursor-datatype.png \ +cursor-enum.png \ +cursor-text.png \ +cursor-anchor.png \ +cursor-composition.png \ +cursor-message-asynchronous.png \ +cursor-class.png \ +cursor-artifact.png \ +cursor-dependency.png \ +cursor-message-synchronous.png \ +cursor-uniassociation.png \ +cursor-association.png \ +cursor-end_state.png \ +cursor-node.png \ +cursor-box.png \ +cursor-fork.png \ +cursor-note.png \ +cursor-branch.png \ +cursor-generalisation.png \ +cursor-object.png \ +cursor-entity.png \ +cursor-relationship.png + +KDE_ICON = umbrello_diagram_activity umbrello_diagram_class umbrello_diagram_collaboration umbrello_diagram_component umbrello_diagram_deployment umbrello_diagram_sequence umbrello_diagram_state umbrello_diagram_usecase umbrello_diagram_entityrelationship diff --git a/umbrello/umbrello/pics/actor.png b/umbrello/umbrello/pics/actor.png new file mode 100644 index 00000000..728be176 Binary files /dev/null and b/umbrello/umbrello/pics/actor.png differ diff --git a/umbrello/umbrello/pics/aggregation.png b/umbrello/umbrello/pics/aggregation.png new file mode 100644 index 00000000..cb9ce67d Binary files /dev/null and b/umbrello/umbrello/pics/aggregation.png differ diff --git a/umbrello/umbrello/pics/align_bottom.png b/umbrello/umbrello/pics/align_bottom.png new file mode 100644 index 00000000..555239ec Binary files /dev/null and b/umbrello/umbrello/pics/align_bottom.png differ diff --git a/umbrello/umbrello/pics/align_hori_distribute.png b/umbrello/umbrello/pics/align_hori_distribute.png new file mode 100644 index 00000000..03215af6 Binary files /dev/null and b/umbrello/umbrello/pics/align_hori_distribute.png differ diff --git a/umbrello/umbrello/pics/align_hori_middle.png b/umbrello/umbrello/pics/align_hori_middle.png new file mode 100644 index 00000000..62956a49 Binary files /dev/null and b/umbrello/umbrello/pics/align_hori_middle.png differ diff --git a/umbrello/umbrello/pics/align_left.png b/umbrello/umbrello/pics/align_left.png new file mode 100644 index 00000000..9bf21f99 Binary files /dev/null and b/umbrello/umbrello/pics/align_left.png differ diff --git a/umbrello/umbrello/pics/align_right.png b/umbrello/umbrello/pics/align_right.png new file mode 100644 index 00000000..80b5c0c5 Binary files /dev/null and b/umbrello/umbrello/pics/align_right.png differ diff --git a/umbrello/umbrello/pics/align_top.png b/umbrello/umbrello/pics/align_top.png new file mode 100644 index 00000000..b168cd5a Binary files /dev/null and b/umbrello/umbrello/pics/align_top.png differ diff --git a/umbrello/umbrello/pics/align_vert_distribute.png b/umbrello/umbrello/pics/align_vert_distribute.png new file mode 100644 index 00000000..7da67730 Binary files /dev/null and b/umbrello/umbrello/pics/align_vert_distribute.png differ diff --git a/umbrello/umbrello/pics/align_vert_middle.png b/umbrello/umbrello/pics/align_vert_middle.png new file mode 100644 index 00000000..096782fc Binary files /dev/null and b/umbrello/umbrello/pics/align_vert_middle.png differ diff --git a/umbrello/umbrello/pics/anchor.png b/umbrello/umbrello/pics/anchor.png new file mode 100644 index 00000000..9fc2bc06 Binary files /dev/null and b/umbrello/umbrello/pics/anchor.png differ diff --git a/umbrello/umbrello/pics/andline.png b/umbrello/umbrello/pics/andline.png new file mode 100644 index 00000000..7fe6081b Binary files /dev/null and b/umbrello/umbrello/pics/andline.png differ diff --git a/umbrello/umbrello/pics/arrow.png b/umbrello/umbrello/pics/arrow.png new file mode 100644 index 00000000..6f48d945 Binary files /dev/null and b/umbrello/umbrello/pics/arrow.png differ diff --git a/umbrello/umbrello/pics/artifact.png b/umbrello/umbrello/pics/artifact.png new file mode 100644 index 00000000..41e3ccd9 Binary files /dev/null and b/umbrello/umbrello/pics/artifact.png differ diff --git a/umbrello/umbrello/pics/association.png b/umbrello/umbrello/pics/association.png new file mode 100644 index 00000000..60f742bb Binary files /dev/null and b/umbrello/umbrello/pics/association.png differ diff --git a/umbrello/umbrello/pics/box.png b/umbrello/umbrello/pics/box.png new file mode 100644 index 00000000..03e2b41b Binary files /dev/null and b/umbrello/umbrello/pics/box.png differ diff --git a/umbrello/umbrello/pics/branch.png b/umbrello/umbrello/pics/branch.png new file mode 100644 index 00000000..36bea8f5 Binary files /dev/null and b/umbrello/umbrello/pics/branch.png differ diff --git a/umbrello/umbrello/pics/choice-rhomb.png b/umbrello/umbrello/pics/choice-rhomb.png new file mode 100644 index 00000000..d067c3ee Binary files /dev/null and b/umbrello/umbrello/pics/choice-rhomb.png differ diff --git a/umbrello/umbrello/pics/choice-round.png b/umbrello/umbrello/pics/choice-round.png new file mode 100644 index 00000000..423748d8 Binary files /dev/null and b/umbrello/umbrello/pics/choice-round.png differ diff --git a/umbrello/umbrello/pics/class.png b/umbrello/umbrello/pics/class.png new file mode 100644 index 00000000..8b47d5b0 Binary files /dev/null and b/umbrello/umbrello/pics/class.png differ diff --git a/umbrello/umbrello/pics/component.png b/umbrello/umbrello/pics/component.png new file mode 100644 index 00000000..1ba3d604 Binary files /dev/null and b/umbrello/umbrello/pics/component.png differ diff --git a/umbrello/umbrello/pics/composition.png b/umbrello/umbrello/pics/composition.png new file mode 100644 index 00000000..3d53ea48 Binary files /dev/null and b/umbrello/umbrello/pics/composition.png differ diff --git a/umbrello/umbrello/pics/containment.png b/umbrello/umbrello/pics/containment.png new file mode 100644 index 00000000..1972fda5 Binary files /dev/null and b/umbrello/umbrello/pics/containment.png differ diff --git a/umbrello/umbrello/pics/cr16-action-umbrello_diagram_activity.png b/umbrello/umbrello/pics/cr16-action-umbrello_diagram_activity.png new file mode 100644 index 00000000..0295b8b6 Binary files /dev/null and b/umbrello/umbrello/pics/cr16-action-umbrello_diagram_activity.png differ diff --git a/umbrello/umbrello/pics/cr16-action-umbrello_diagram_class.png b/umbrello/umbrello/pics/cr16-action-umbrello_diagram_class.png new file mode 100644 index 00000000..0e8c6060 Binary files /dev/null and b/umbrello/umbrello/pics/cr16-action-umbrello_diagram_class.png differ diff --git a/umbrello/umbrello/pics/cr16-action-umbrello_diagram_collaboration.png b/umbrello/umbrello/pics/cr16-action-umbrello_diagram_collaboration.png new file mode 100644 index 00000000..76207be2 Binary files /dev/null and b/umbrello/umbrello/pics/cr16-action-umbrello_diagram_collaboration.png differ diff --git a/umbrello/umbrello/pics/cr16-action-umbrello_diagram_component.png b/umbrello/umbrello/pics/cr16-action-umbrello_diagram_component.png new file mode 100644 index 00000000..f80cc31f Binary files /dev/null and b/umbrello/umbrello/pics/cr16-action-umbrello_diagram_component.png differ diff --git a/umbrello/umbrello/pics/cr16-action-umbrello_diagram_deployment.png b/umbrello/umbrello/pics/cr16-action-umbrello_diagram_deployment.png new file mode 100644 index 00000000..fe515150 Binary files /dev/null and b/umbrello/umbrello/pics/cr16-action-umbrello_diagram_deployment.png differ diff --git a/umbrello/umbrello/pics/cr16-action-umbrello_diagram_sequence.png b/umbrello/umbrello/pics/cr16-action-umbrello_diagram_sequence.png new file mode 100644 index 00000000..e60ca4c6 Binary files /dev/null and b/umbrello/umbrello/pics/cr16-action-umbrello_diagram_sequence.png differ diff --git a/umbrello/umbrello/pics/cr16-action-umbrello_diagram_state.png b/umbrello/umbrello/pics/cr16-action-umbrello_diagram_state.png new file mode 100644 index 00000000..4fe826df Binary files /dev/null and b/umbrello/umbrello/pics/cr16-action-umbrello_diagram_state.png differ diff --git a/umbrello/umbrello/pics/cr16-action-umbrello_diagram_usecase.png b/umbrello/umbrello/pics/cr16-action-umbrello_diagram_usecase.png new file mode 100644 index 00000000..3259c733 Binary files /dev/null and b/umbrello/umbrello/pics/cr16-action-umbrello_diagram_usecase.png differ diff --git a/umbrello/umbrello/pics/cr22-action-umbrello_diagram_activity.png b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_activity.png new file mode 100644 index 00000000..053af9e5 Binary files /dev/null and b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_activity.png differ diff --git a/umbrello/umbrello/pics/cr22-action-umbrello_diagram_class.png b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_class.png new file mode 100644 index 00000000..cf9e168d Binary files /dev/null and b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_class.png differ diff --git a/umbrello/umbrello/pics/cr22-action-umbrello_diagram_collaboration.png b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_collaboration.png new file mode 100644 index 00000000..323947e8 Binary files /dev/null and b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_collaboration.png differ diff --git a/umbrello/umbrello/pics/cr22-action-umbrello_diagram_component.png b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_component.png new file mode 100644 index 00000000..48202c8f Binary files /dev/null and b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_component.png differ diff --git a/umbrello/umbrello/pics/cr22-action-umbrello_diagram_deployment.png b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_deployment.png new file mode 100644 index 00000000..5343363c Binary files /dev/null and b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_deployment.png differ diff --git a/umbrello/umbrello/pics/cr22-action-umbrello_diagram_entityrelationship.png b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_entityrelationship.png new file mode 100644 index 00000000..570b8ac1 Binary files /dev/null and b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_entityrelationship.png differ diff --git a/umbrello/umbrello/pics/cr22-action-umbrello_diagram_sequence.png b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_sequence.png new file mode 100644 index 00000000..b2cfd3c2 Binary files /dev/null and b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_sequence.png differ diff --git a/umbrello/umbrello/pics/cr22-action-umbrello_diagram_state.png b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_state.png new file mode 100644 index 00000000..f812a17c Binary files /dev/null and b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_state.png differ diff --git a/umbrello/umbrello/pics/cr22-action-umbrello_diagram_usecase.png b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_usecase.png new file mode 100644 index 00000000..bc07439d Binary files /dev/null and b/umbrello/umbrello/pics/cr22-action-umbrello_diagram_usecase.png differ diff --git a/umbrello/umbrello/pics/cursor-actor.png b/umbrello/umbrello/pics/cursor-actor.png new file mode 100644 index 00000000..97e05a98 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-actor.png differ diff --git a/umbrello/umbrello/pics/cursor-aggregation.png b/umbrello/umbrello/pics/cursor-aggregation.png new file mode 100644 index 00000000..f75e323d Binary files /dev/null and b/umbrello/umbrello/pics/cursor-aggregation.png differ diff --git a/umbrello/umbrello/pics/cursor-anchor.png b/umbrello/umbrello/pics/cursor-anchor.png new file mode 100644 index 00000000..6facf249 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-anchor.png differ diff --git a/umbrello/umbrello/pics/cursor-andline.png b/umbrello/umbrello/pics/cursor-andline.png new file mode 100644 index 00000000..ca66655d Binary files /dev/null and b/umbrello/umbrello/pics/cursor-andline.png differ diff --git a/umbrello/umbrello/pics/cursor-artifact.png b/umbrello/umbrello/pics/cursor-artifact.png new file mode 100644 index 00000000..47965eed Binary files /dev/null and b/umbrello/umbrello/pics/cursor-artifact.png differ diff --git a/umbrello/umbrello/pics/cursor-association.png b/umbrello/umbrello/pics/cursor-association.png new file mode 100644 index 00000000..543a857d Binary files /dev/null and b/umbrello/umbrello/pics/cursor-association.png differ diff --git a/umbrello/umbrello/pics/cursor-box.png b/umbrello/umbrello/pics/cursor-box.png new file mode 100644 index 00000000..c4fea53c Binary files /dev/null and b/umbrello/umbrello/pics/cursor-box.png differ diff --git a/umbrello/umbrello/pics/cursor-branch.png b/umbrello/umbrello/pics/cursor-branch.png new file mode 100644 index 00000000..a7f04705 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-branch.png differ diff --git a/umbrello/umbrello/pics/cursor-choice-rhomb.png b/umbrello/umbrello/pics/cursor-choice-rhomb.png new file mode 100644 index 00000000..84db46bd Binary files /dev/null and b/umbrello/umbrello/pics/cursor-choice-rhomb.png differ diff --git a/umbrello/umbrello/pics/cursor-choice-round.png b/umbrello/umbrello/pics/cursor-choice-round.png new file mode 100644 index 00000000..5cad8062 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-choice-round.png differ diff --git a/umbrello/umbrello/pics/cursor-class.png b/umbrello/umbrello/pics/cursor-class.png new file mode 100644 index 00000000..4be74cee Binary files /dev/null and b/umbrello/umbrello/pics/cursor-class.png differ diff --git a/umbrello/umbrello/pics/cursor-component.png b/umbrello/umbrello/pics/cursor-component.png new file mode 100644 index 00000000..6b4a814e Binary files /dev/null and b/umbrello/umbrello/pics/cursor-component.png differ diff --git a/umbrello/umbrello/pics/cursor-composition.png b/umbrello/umbrello/pics/cursor-composition.png new file mode 100644 index 00000000..f2eabb9c Binary files /dev/null and b/umbrello/umbrello/pics/cursor-composition.png differ diff --git a/umbrello/umbrello/pics/cursor-containment.png b/umbrello/umbrello/pics/cursor-containment.png new file mode 100644 index 00000000..824074b6 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-containment.png differ diff --git a/umbrello/umbrello/pics/cursor-datatype.png b/umbrello/umbrello/pics/cursor-datatype.png new file mode 100644 index 00000000..82f20c16 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-datatype.png differ diff --git a/umbrello/umbrello/pics/cursor-deep-history.png b/umbrello/umbrello/pics/cursor-deep-history.png new file mode 100644 index 00000000..9d8550d7 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-deep-history.png differ diff --git a/umbrello/umbrello/pics/cursor-dependency.png b/umbrello/umbrello/pics/cursor-dependency.png new file mode 100644 index 00000000..621ddb6e Binary files /dev/null and b/umbrello/umbrello/pics/cursor-dependency.png differ diff --git a/umbrello/umbrello/pics/cursor-end_state.png b/umbrello/umbrello/pics/cursor-end_state.png new file mode 100644 index 00000000..a282e503 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-end_state.png differ diff --git a/umbrello/umbrello/pics/cursor-entity.png b/umbrello/umbrello/pics/cursor-entity.png new file mode 100644 index 00000000..dd103eb0 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-entity.png differ diff --git a/umbrello/umbrello/pics/cursor-enum.png b/umbrello/umbrello/pics/cursor-enum.png new file mode 100644 index 00000000..d4cc0525 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-enum.png differ diff --git a/umbrello/umbrello/pics/cursor-fork.png b/umbrello/umbrello/pics/cursor-fork.png new file mode 100644 index 00000000..40edc63c Binary files /dev/null and b/umbrello/umbrello/pics/cursor-fork.png differ diff --git a/umbrello/umbrello/pics/cursor-generalisation.png b/umbrello/umbrello/pics/cursor-generalisation.png new file mode 100644 index 00000000..fa7a71ab Binary files /dev/null and b/umbrello/umbrello/pics/cursor-generalisation.png differ diff --git a/umbrello/umbrello/pics/cursor-initial_state.png b/umbrello/umbrello/pics/cursor-initial_state.png new file mode 100644 index 00000000..91ae1b3e Binary files /dev/null and b/umbrello/umbrello/pics/cursor-initial_state.png differ diff --git a/umbrello/umbrello/pics/cursor-interface.png b/umbrello/umbrello/pics/cursor-interface.png new file mode 100644 index 00000000..ad8277d5 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-interface.png differ diff --git a/umbrello/umbrello/pics/cursor-join.png b/umbrello/umbrello/pics/cursor-join.png new file mode 100644 index 00000000..a485f4a8 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-join.png differ diff --git a/umbrello/umbrello/pics/cursor-junction.png b/umbrello/umbrello/pics/cursor-junction.png new file mode 100644 index 00000000..be0ddee0 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-junction.png differ diff --git a/umbrello/umbrello/pics/cursor-message-asynchronous.png b/umbrello/umbrello/pics/cursor-message-asynchronous.png new file mode 100644 index 00000000..549afe0e Binary files /dev/null and b/umbrello/umbrello/pics/cursor-message-asynchronous.png differ diff --git a/umbrello/umbrello/pics/cursor-message-synchronous.png b/umbrello/umbrello/pics/cursor-message-synchronous.png new file mode 100644 index 00000000..a6535c12 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-message-synchronous.png differ diff --git a/umbrello/umbrello/pics/cursor-node.png b/umbrello/umbrello/pics/cursor-node.png new file mode 100644 index 00000000..c4a14312 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-node.png differ diff --git a/umbrello/umbrello/pics/cursor-note.png b/umbrello/umbrello/pics/cursor-note.png new file mode 100644 index 00000000..6b656a2e Binary files /dev/null and b/umbrello/umbrello/pics/cursor-note.png differ diff --git a/umbrello/umbrello/pics/cursor-object.png b/umbrello/umbrello/pics/cursor-object.png new file mode 100644 index 00000000..97e0f7bf Binary files /dev/null and b/umbrello/umbrello/pics/cursor-object.png differ diff --git a/umbrello/umbrello/pics/cursor-package.png b/umbrello/umbrello/pics/cursor-package.png new file mode 100644 index 00000000..afdbdbbe Binary files /dev/null and b/umbrello/umbrello/pics/cursor-package.png differ diff --git a/umbrello/umbrello/pics/cursor-relationship.png b/umbrello/umbrello/pics/cursor-relationship.png new file mode 100644 index 00000000..5c4d27a3 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-relationship.png differ diff --git a/umbrello/umbrello/pics/cursor-shallow-history.png b/umbrello/umbrello/pics/cursor-shallow-history.png new file mode 100644 index 00000000..8e3a83b2 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-shallow-history.png differ diff --git a/umbrello/umbrello/pics/cursor-state-fork.png b/umbrello/umbrello/pics/cursor-state-fork.png new file mode 100644 index 00000000..b1028ef7 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-state-fork.png differ diff --git a/umbrello/umbrello/pics/cursor-text.png b/umbrello/umbrello/pics/cursor-text.png new file mode 100644 index 00000000..91058cb0 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-text.png differ diff --git a/umbrello/umbrello/pics/cursor-uniassociation.png b/umbrello/umbrello/pics/cursor-uniassociation.png new file mode 100644 index 00000000..b8980073 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-uniassociation.png differ diff --git a/umbrello/umbrello/pics/cursor-usecase.png b/umbrello/umbrello/pics/cursor-usecase.png new file mode 100644 index 00000000..d91d5b40 Binary files /dev/null and b/umbrello/umbrello/pics/cursor-usecase.png differ diff --git a/umbrello/umbrello/pics/datatype.png b/umbrello/umbrello/pics/datatype.png new file mode 100644 index 00000000..cce13889 Binary files /dev/null and b/umbrello/umbrello/pics/datatype.png differ diff --git a/umbrello/umbrello/pics/deep-history.png b/umbrello/umbrello/pics/deep-history.png new file mode 100644 index 00000000..01b9d64d Binary files /dev/null and b/umbrello/umbrello/pics/deep-history.png differ diff --git a/umbrello/umbrello/pics/dependency.png b/umbrello/umbrello/pics/dependency.png new file mode 100644 index 00000000..0cd1a323 Binary files /dev/null and b/umbrello/umbrello/pics/dependency.png differ diff --git a/umbrello/umbrello/pics/end_state.png b/umbrello/umbrello/pics/end_state.png new file mode 100644 index 00000000..ac78110c Binary files /dev/null and b/umbrello/umbrello/pics/end_state.png differ diff --git a/umbrello/umbrello/pics/entity.png b/umbrello/umbrello/pics/entity.png new file mode 100644 index 00000000..2983c1db Binary files /dev/null and b/umbrello/umbrello/pics/entity.png differ diff --git a/umbrello/umbrello/pics/enum.png b/umbrello/umbrello/pics/enum.png new file mode 100644 index 00000000..d4b9fcd8 Binary files /dev/null and b/umbrello/umbrello/pics/enum.png differ diff --git a/umbrello/umbrello/pics/fork.png b/umbrello/umbrello/pics/fork.png new file mode 100644 index 00000000..abb0649b Binary files /dev/null and b/umbrello/umbrello/pics/fork.png differ diff --git a/umbrello/umbrello/pics/generalisation.png b/umbrello/umbrello/pics/generalisation.png new file mode 100644 index 00000000..2ea9d66d Binary files /dev/null and b/umbrello/umbrello/pics/generalisation.png differ diff --git a/umbrello/umbrello/pics/initial_state.png b/umbrello/umbrello/pics/initial_state.png new file mode 100644 index 00000000..d5d6f4f3 Binary files /dev/null and b/umbrello/umbrello/pics/initial_state.png differ diff --git a/umbrello/umbrello/pics/interface.png b/umbrello/umbrello/pics/interface.png new file mode 100644 index 00000000..66d52761 Binary files /dev/null and b/umbrello/umbrello/pics/interface.png differ diff --git a/umbrello/umbrello/pics/join.png b/umbrello/umbrello/pics/join.png new file mode 100644 index 00000000..0582a7dc Binary files /dev/null and b/umbrello/umbrello/pics/join.png differ diff --git a/umbrello/umbrello/pics/junction.png b/umbrello/umbrello/pics/junction.png new file mode 100644 index 00000000..ee131fa2 Binary files /dev/null and b/umbrello/umbrello/pics/junction.png differ diff --git a/umbrello/umbrello/pics/message-asynchronous.png b/umbrello/umbrello/pics/message-asynchronous.png new file mode 100644 index 00000000..21eb52f7 Binary files /dev/null and b/umbrello/umbrello/pics/message-asynchronous.png differ diff --git a/umbrello/umbrello/pics/message-synchronous.png b/umbrello/umbrello/pics/message-synchronous.png new file mode 100644 index 00000000..0567346d Binary files /dev/null and b/umbrello/umbrello/pics/message-synchronous.png differ diff --git a/umbrello/umbrello/pics/node.png b/umbrello/umbrello/pics/node.png new file mode 100644 index 00000000..6a12d3a1 Binary files /dev/null and b/umbrello/umbrello/pics/node.png differ diff --git a/umbrello/umbrello/pics/note.png b/umbrello/umbrello/pics/note.png new file mode 100644 index 00000000..11bc3dd9 Binary files /dev/null and b/umbrello/umbrello/pics/note.png differ diff --git a/umbrello/umbrello/pics/object.png b/umbrello/umbrello/pics/object.png new file mode 100644 index 00000000..eb246070 Binary files /dev/null and b/umbrello/umbrello/pics/object.png differ diff --git a/umbrello/umbrello/pics/package.png b/umbrello/umbrello/pics/package.png new file mode 100644 index 00000000..8f35b7c2 Binary files /dev/null and b/umbrello/umbrello/pics/package.png differ diff --git a/umbrello/umbrello/pics/relationship.png b/umbrello/umbrello/pics/relationship.png new file mode 100644 index 00000000..8154868e Binary files /dev/null and b/umbrello/umbrello/pics/relationship.png differ diff --git a/umbrello/umbrello/pics/shallow-history.png b/umbrello/umbrello/pics/shallow-history.png new file mode 100644 index 00000000..7d871cc1 Binary files /dev/null and b/umbrello/umbrello/pics/shallow-history.png differ diff --git a/umbrello/umbrello/pics/sources/actor.svg b/umbrello/umbrello/pics/sources/actor.svg new file mode 100644 index 00000000..ae4a2d81 --- /dev/null +++ b/umbrello/umbrello/pics/sources/actor.svg @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/aggregation.svg b/umbrello/umbrello/pics/sources/aggregation.svg new file mode 100644 index 00000000..7d9fe672 --- /dev/null +++ b/umbrello/umbrello/pics/sources/aggregation.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/align_bottom.svg b/umbrello/umbrello/pics/sources/align_bottom.svg new file mode 100644 index 00000000..0f292766 --- /dev/null +++ b/umbrello/umbrello/pics/sources/align_bottom.svg @@ -0,0 +1,261 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/align_hori_distribute.svg b/umbrello/umbrello/pics/sources/align_hori_distribute.svg new file mode 100644 index 00000000..f6174a83 --- /dev/null +++ b/umbrello/umbrello/pics/sources/align_hori_distribute.svg @@ -0,0 +1,273 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/align_hori_middle.svg b/umbrello/umbrello/pics/sources/align_hori_middle.svg new file mode 100644 index 00000000..4c7b1234 --- /dev/null +++ b/umbrello/umbrello/pics/sources/align_hori_middle.svg @@ -0,0 +1,257 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/align_left.svg b/umbrello/umbrello/pics/sources/align_left.svg new file mode 100644 index 00000000..362e4e34 --- /dev/null +++ b/umbrello/umbrello/pics/sources/align_left.svg @@ -0,0 +1,260 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/align_right.svg b/umbrello/umbrello/pics/sources/align_right.svg new file mode 100644 index 00000000..8fa3af6e --- /dev/null +++ b/umbrello/umbrello/pics/sources/align_right.svg @@ -0,0 +1,260 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/align_top.svg b/umbrello/umbrello/pics/sources/align_top.svg new file mode 100644 index 00000000..e7cec488 --- /dev/null +++ b/umbrello/umbrello/pics/sources/align_top.svg @@ -0,0 +1,251 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/align_vert_distribute.svg b/umbrello/umbrello/pics/sources/align_vert_distribute.svg new file mode 100644 index 00000000..135eda26 --- /dev/null +++ b/umbrello/umbrello/pics/sources/align_vert_distribute.svg @@ -0,0 +1,272 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/align_vert_middle.svg b/umbrello/umbrello/pics/sources/align_vert_middle.svg new file mode 100644 index 00000000..b7b62def --- /dev/null +++ b/umbrello/umbrello/pics/sources/align_vert_middle.svg @@ -0,0 +1,254 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/anchor.svg b/umbrello/umbrello/pics/sources/anchor.svg new file mode 100644 index 00000000..27fd26b9 --- /dev/null +++ b/umbrello/umbrello/pics/sources/anchor.svg @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XYZ + + + ABC + + + + + + diff --git a/umbrello/umbrello/pics/sources/andline.svg b/umbrello/umbrello/pics/sources/andline.svg new file mode 100644 index 00000000..20948588 --- /dev/null +++ b/umbrello/umbrello/pics/sources/andline.svg @@ -0,0 +1,102 @@ + + + + + + + image/svg+xml + + And Line + + 2005-02-28 + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/artifact.svg b/umbrello/umbrello/pics/sources/artifact.svg new file mode 100644 index 00000000..4f78d3c6 --- /dev/null +++ b/umbrello/umbrello/pics/sources/artifact.svg @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/association.svg b/umbrello/umbrello/pics/sources/association.svg new file mode 100644 index 00000000..a51cb414 --- /dev/null +++ b/umbrello/umbrello/pics/sources/association.svg @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/asynchro.svg b/umbrello/umbrello/pics/sources/asynchro.svg new file mode 100644 index 00000000..7bb8054e --- /dev/null +++ b/umbrello/umbrello/pics/sources/asynchro.svg @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + f(x) + + + diff --git a/umbrello/umbrello/pics/sources/box.svg b/umbrello/umbrello/pics/sources/box.svg new file mode 100644 index 00000000..9fd9bebd --- /dev/null +++ b/umbrello/umbrello/pics/sources/box.svg @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/branch.svg b/umbrello/umbrello/pics/sources/branch.svg new file mode 100644 index 00000000..939c12dc --- /dev/null +++ b/umbrello/umbrello/pics/sources/branch.svg @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/choice-rhomb.svg b/umbrello/umbrello/pics/sources/choice-rhomb.svg new file mode 100644 index 00000000..9f3014f8 --- /dev/null +++ b/umbrello/umbrello/pics/sources/choice-rhomb.svg @@ -0,0 +1,160 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/choice-round.svg b/umbrello/umbrello/pics/sources/choice-round.svg new file mode 100644 index 00000000..ecb862e1 --- /dev/null +++ b/umbrello/umbrello/pics/sources/choice-round.svg @@ -0,0 +1,178 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + C + + diff --git a/umbrello/umbrello/pics/sources/class.svg b/umbrello/umbrello/pics/sources/class.svg new file mode 100644 index 00000000..03124a31 --- /dev/null +++ b/umbrello/umbrello/pics/sources/class.svg @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + ABC + + + + + + diff --git a/umbrello/umbrello/pics/sources/component.svg b/umbrello/umbrello/pics/sources/component.svg new file mode 100644 index 00000000..514cc765 --- /dev/null +++ b/umbrello/umbrello/pics/sources/component.svg @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/composition.svg b/umbrello/umbrello/pics/sources/composition.svg new file mode 100644 index 00000000..04bec885 --- /dev/null +++ b/umbrello/umbrello/pics/sources/composition.svg @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/containment.svg b/umbrello/umbrello/pics/sources/containment.svg new file mode 100644 index 00000000..7d33113b --- /dev/null +++ b/umbrello/umbrello/pics/sources/containment.svg @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/cursor-andline.svg b/umbrello/umbrello/pics/sources/cursor-andline.svg new file mode 100644 index 00000000..fc315835 --- /dev/null +++ b/umbrello/umbrello/pics/sources/cursor-andline.svg @@ -0,0 +1,496 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/cursor-choice-rhomb.svg b/umbrello/umbrello/pics/sources/cursor-choice-rhomb.svg new file mode 100644 index 00000000..81277a0c --- /dev/null +++ b/umbrello/umbrello/pics/sources/cursor-choice-rhomb.svg @@ -0,0 +1,439 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/cursor-choice-round.svg b/umbrello/umbrello/pics/sources/cursor-choice-round.svg new file mode 100644 index 00000000..747a511f --- /dev/null +++ b/umbrello/umbrello/pics/sources/cursor-choice-round.svg @@ -0,0 +1,356 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + C + + + + diff --git a/umbrello/umbrello/pics/sources/cursor-deep-history.svg b/umbrello/umbrello/pics/sources/cursor-deep-history.svg new file mode 100644 index 00000000..2cb89831 --- /dev/null +++ b/umbrello/umbrello/pics/sources/cursor-deep-history.svg @@ -0,0 +1,206 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + H + * + + + diff --git a/umbrello/umbrello/pics/sources/cursor-join.svg b/umbrello/umbrello/pics/sources/cursor-join.svg new file mode 100644 index 00000000..f84032bb --- /dev/null +++ b/umbrello/umbrello/pics/sources/cursor-join.svg @@ -0,0 +1,289 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/cursor-junction.svg b/umbrello/umbrello/pics/sources/cursor-junction.svg new file mode 100644 index 00000000..73154f52 --- /dev/null +++ b/umbrello/umbrello/pics/sources/cursor-junction.svg @@ -0,0 +1,279 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/cursor-shallow-history.svg b/umbrello/umbrello/pics/sources/cursor-shallow-history.svg new file mode 100644 index 00000000..8f840682 --- /dev/null +++ b/umbrello/umbrello/pics/sources/cursor-shallow-history.svg @@ -0,0 +1,195 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + H + + + + diff --git a/umbrello/umbrello/pics/sources/cursor-state-fork.svg b/umbrello/umbrello/pics/sources/cursor-state-fork.svg new file mode 100644 index 00000000..92f6fa29 --- /dev/null +++ b/umbrello/umbrello/pics/sources/cursor-state-fork.svg @@ -0,0 +1,289 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/datatype.svg b/umbrello/umbrello/pics/sources/datatype.svg new file mode 100644 index 00000000..b6c1f36c --- /dev/null +++ b/umbrello/umbrello/pics/sources/datatype.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + «xy»ABC + + diff --git a/umbrello/umbrello/pics/sources/deep-history.svg b/umbrello/umbrello/pics/sources/deep-history.svg new file mode 100644 index 00000000..913cb3f4 --- /dev/null +++ b/umbrello/umbrello/pics/sources/deep-history.svg @@ -0,0 +1,84 @@ + + + + + + + image/svg+xml + + + + + + + + + H + * + + diff --git a/umbrello/umbrello/pics/sources/dependency.svg b/umbrello/umbrello/pics/sources/dependency.svg new file mode 100644 index 00000000..8a98444a --- /dev/null +++ b/umbrello/umbrello/pics/sources/dependency.svg @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/diag_activity.svg b/umbrello/umbrello/pics/sources/diag_activity.svg new file mode 100644 index 00000000..d5c42951 --- /dev/null +++ b/umbrello/umbrello/pics/sources/diag_activity.svg @@ -0,0 +1,571 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ABC + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/diag_class.svg b/umbrello/umbrello/pics/sources/diag_class.svg new file mode 100644 index 00000000..ef84ebe6 --- /dev/null +++ b/umbrello/umbrello/pics/sources/diag_class.svg @@ -0,0 +1,360 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ABC + + + + + + + + + ABC + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/diag_collaboration.svg b/umbrello/umbrello/pics/sources/diag_collaboration.svg new file mode 100644 index 00000000..6d84d6e3 --- /dev/null +++ b/umbrello/umbrello/pics/sources/diag_collaboration.svg @@ -0,0 +1,1111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A:B + + + + + + A:B + + diff --git a/umbrello/umbrello/pics/sources/diag_component.svg b/umbrello/umbrello/pics/sources/diag_component.svg new file mode 100644 index 00000000..e8859441 --- /dev/null +++ b/umbrello/umbrello/pics/sources/diag_component.svg @@ -0,0 +1,732 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/diag_deployment.svg b/umbrello/umbrello/pics/sources/diag_deployment.svg new file mode 100644 index 00000000..940cce17 --- /dev/null +++ b/umbrello/umbrello/pics/sources/diag_deployment.svg @@ -0,0 +1,676 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XY + + + + + + + XY + + + + + + diff --git a/umbrello/umbrello/pics/sources/diag_entityrelationship.svg b/umbrello/umbrello/pics/sources/diag_entityrelationship.svg new file mode 100644 index 00000000..586f8e2e --- /dev/null +++ b/umbrello/umbrello/pics/sources/diag_entityrelationship.svg @@ -0,0 +1,715 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ABC + + + + + + + ABC + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/diag_sequence.svg b/umbrello/umbrello/pics/sources/diag_sequence.svg new file mode 100644 index 00000000..654c9847 --- /dev/null +++ b/umbrello/umbrello/pics/sources/diag_sequence.svg @@ -0,0 +1,729 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A:B + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + f(x) + + diff --git a/umbrello/umbrello/pics/sources/diag_state.svg b/umbrello/umbrello/pics/sources/diag_state.svg new file mode 100644 index 00000000..3d428ff4 --- /dev/null +++ b/umbrello/umbrello/pics/sources/diag_state.svg @@ -0,0 +1,629 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ABC + + + + + + + ABC + + + diff --git a/umbrello/umbrello/pics/sources/diag_usecase.svg b/umbrello/umbrello/pics/sources/diag_usecase.svg new file mode 100644 index 00000000..9f1d6400 --- /dev/null +++ b/umbrello/umbrello/pics/sources/diag_usecase.svg @@ -0,0 +1,489 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ABC + + + diff --git a/umbrello/umbrello/pics/sources/diagbase.svg b/umbrello/umbrello/pics/sources/diagbase.svg new file mode 100644 index 00000000..44774402 --- /dev/null +++ b/umbrello/umbrello/pics/sources/diagbase.svg @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/end_state.svg b/umbrello/umbrello/pics/sources/end_state.svg new file mode 100644 index 00000000..65d22943 --- /dev/null +++ b/umbrello/umbrello/pics/sources/end_state.svg @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/entity.svg b/umbrello/umbrello/pics/sources/entity.svg new file mode 100644 index 00000000..a40611eb --- /dev/null +++ b/umbrello/umbrello/pics/sources/entity.svg @@ -0,0 +1,151 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + ABC + + + + diff --git a/umbrello/umbrello/pics/sources/enum.svg b/umbrello/umbrello/pics/sources/enum.svg new file mode 100644 index 00000000..ca09e448 --- /dev/null +++ b/umbrello/umbrello/pics/sources/enum.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + - ABC- DEF- IJK + diff --git a/umbrello/umbrello/pics/sources/fork.svg b/umbrello/umbrello/pics/sources/fork.svg new file mode 100644 index 00000000..c9e6c53b --- /dev/null +++ b/umbrello/umbrello/pics/sources/fork.svg @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/generalise.svg b/umbrello/umbrello/pics/sources/generalise.svg new file mode 100644 index 00000000..d43ed6c2 --- /dev/null +++ b/umbrello/umbrello/pics/sources/generalise.svg @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/initial.svg b/umbrello/umbrello/pics/sources/initial.svg new file mode 100644 index 00000000..2ab111b5 --- /dev/null +++ b/umbrello/umbrello/pics/sources/initial.svg @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/interface.svg b/umbrello/umbrello/pics/sources/interface.svg new file mode 100644 index 00000000..00c0a274 --- /dev/null +++ b/umbrello/umbrello/pics/sources/interface.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + «xy»ABC + + + + diff --git a/umbrello/umbrello/pics/sources/join.svg b/umbrello/umbrello/pics/sources/join.svg new file mode 100644 index 00000000..9bdd1f88 --- /dev/null +++ b/umbrello/umbrello/pics/sources/join.svg @@ -0,0 +1,175 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/junction.svg b/umbrello/umbrello/pics/sources/junction.svg new file mode 100644 index 00000000..98ef166d --- /dev/null +++ b/umbrello/umbrello/pics/sources/junction.svg @@ -0,0 +1,256 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/message-asynchronous.svg b/umbrello/umbrello/pics/sources/message-asynchronous.svg new file mode 100644 index 00000000..7bb8054e --- /dev/null +++ b/umbrello/umbrello/pics/sources/message-asynchronous.svg @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + f(x) + + + diff --git a/umbrello/umbrello/pics/sources/message-synchronous.svg b/umbrello/umbrello/pics/sources/message-synchronous.svg new file mode 100644 index 00000000..098a56b5 --- /dev/null +++ b/umbrello/umbrello/pics/sources/message-synchronous.svg @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + f(x) + + + + + + diff --git a/umbrello/umbrello/pics/sources/node.svg b/umbrello/umbrello/pics/sources/node.svg new file mode 100644 index 00000000..ee76129a --- /dev/null +++ b/umbrello/umbrello/pics/sources/node.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + XY + diff --git a/umbrello/umbrello/pics/sources/note.svg b/umbrello/umbrello/pics/sources/note.svg new file mode 100644 index 00000000..314d3026 --- /dev/null +++ b/umbrello/umbrello/pics/sources/note.svg @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XYZ + diff --git a/umbrello/umbrello/pics/sources/object.svg b/umbrello/umbrello/pics/sources/object.svg new file mode 100644 index 00000000..184f6228 --- /dev/null +++ b/umbrello/umbrello/pics/sources/object.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + A:B + + diff --git a/umbrello/umbrello/pics/sources/package.svg b/umbrello/umbrello/pics/sources/package.svg new file mode 100644 index 00000000..bbfe4d43 --- /dev/null +++ b/umbrello/umbrello/pics/sources/package.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + XYZ + + + diff --git a/umbrello/umbrello/pics/sources/relationship.svg b/umbrello/umbrello/pics/sources/relationship.svg new file mode 100644 index 00000000..89981396 --- /dev/null +++ b/umbrello/umbrello/pics/sources/relationship.svg @@ -0,0 +1,243 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/shallow-history.svg b/umbrello/umbrello/pics/sources/shallow-history.svg new file mode 100644 index 00000000..d5ce2653 --- /dev/null +++ b/umbrello/umbrello/pics/sources/shallow-history.svg @@ -0,0 +1,73 @@ + + + + + + + image/svg+xml + + + + + + + + + H + + diff --git a/umbrello/umbrello/pics/sources/state-fork.svg b/umbrello/umbrello/pics/sources/state-fork.svg new file mode 100644 index 00000000..9e12691a --- /dev/null +++ b/umbrello/umbrello/pics/sources/state-fork.svg @@ -0,0 +1,172 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/subsystem.svg b/umbrello/umbrello/pics/sources/subsystem.svg new file mode 100644 index 00000000..ca5f1ce7 --- /dev/null +++ b/umbrello/umbrello/pics/sources/subsystem.svg @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/template.svg b/umbrello/umbrello/pics/sources/template.svg new file mode 100644 index 00000000..5c299757 --- /dev/null +++ b/umbrello/umbrello/pics/sources/template.svg @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + ABC + + + + + + XYZ + diff --git a/umbrello/umbrello/pics/sources/text.svg b/umbrello/umbrello/pics/sources/text.svg new file mode 100644 index 00000000..24eba425 --- /dev/null +++ b/umbrello/umbrello/pics/sources/text.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + XYZ + diff --git a/umbrello/umbrello/pics/sources/uniassociation.svg b/umbrello/umbrello/pics/sources/uniassociation.svg new file mode 100644 index 00000000..4fce2c33 --- /dev/null +++ b/umbrello/umbrello/pics/sources/uniassociation.svg @@ -0,0 +1,221 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/umbrello/pics/sources/usecase.svg b/umbrello/umbrello/pics/sources/usecase.svg new file mode 100644 index 00000000..cd0851c5 --- /dev/null +++ b/umbrello/umbrello/pics/sources/usecase.svg @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ABC + diff --git a/umbrello/umbrello/pics/startlogo.png b/umbrello/umbrello/pics/startlogo.png new file mode 100644 index 00000000..29d6e15d Binary files /dev/null and b/umbrello/umbrello/pics/startlogo.png differ diff --git a/umbrello/umbrello/pics/state-fork.png b/umbrello/umbrello/pics/state-fork.png new file mode 100644 index 00000000..5a3108a1 Binary files /dev/null and b/umbrello/umbrello/pics/state-fork.png differ diff --git a/umbrello/umbrello/pics/subsystem.png b/umbrello/umbrello/pics/subsystem.png new file mode 100644 index 00000000..a002b73e Binary files /dev/null and b/umbrello/umbrello/pics/subsystem.png differ diff --git a/umbrello/umbrello/pics/template.png b/umbrello/umbrello/pics/template.png new file mode 100644 index 00000000..0993de43 Binary files /dev/null and b/umbrello/umbrello/pics/template.png differ diff --git a/umbrello/umbrello/pics/text.png b/umbrello/umbrello/pics/text.png new file mode 100644 index 00000000..77893ded Binary files /dev/null and b/umbrello/umbrello/pics/text.png differ diff --git a/umbrello/umbrello/pics/uniassociation.png b/umbrello/umbrello/pics/uniassociation.png new file mode 100644 index 00000000..f4be9352 Binary files /dev/null and b/umbrello/umbrello/pics/uniassociation.png differ diff --git a/umbrello/umbrello/pics/usecase.png b/umbrello/umbrello/pics/usecase.png new file mode 100644 index 00000000..83201d44 Binary files /dev/null and b/umbrello/umbrello/pics/usecase.png differ diff --git a/umbrello/umbrello/plugin.cpp b/umbrello/umbrello/plugin.cpp new file mode 100644 index 00000000..1447afcd --- /dev/null +++ b/umbrello/umbrello/plugin.cpp @@ -0,0 +1,167 @@ +/*************************************************************************** + plugin.h + ------------------- + begin : Mon Jan 13 2003 + copyright : (C) 2003 by Andrew Sutton + email : ansutton@kent.edu +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +// own header +#include "plugin.h" + +// KDE includes +#include +#include +#include + +// app includes +#include "pluginloader.h" + +using namespace Umbrello; + +Plugin::Plugin(QObject *parent, + const char *name, + const QStringList & /* args */) : + QObject(parent, name), + Configurable(), + _ref(0), + _instanceName(name), + _config(NULL) +{ +} + +Plugin::~Plugin() +{ +} + +void +Plugin::ref() +{ + _ref++; +} + +void +Plugin::unload() +{ + _ref--; + if(_ref == 0) { + // save the name + QString pluginName = _instanceName; + + // shutdown and delete + shutdown(); + delete this; + + // once the object is destroyed, we can have the plugin loader unload + // the library. + PluginLoader::instance()->unloadPlugin(pluginName); + } +} + +bool +Plugin::init() +{ + bool ret = true; + + // initialize this plugin first - then load other plugins + ret = onInit(); + if(!ret) { + kdError() << "failed to initialize " << instanceName() << endl; + } + + // configure on load plugins + if(ret) { + ret = configure(); + if(!ret) { + kdError() << "failed configuration " << instanceName() << endl; + } + } + + return true; +} + +bool +Plugin::shutdown() +{ + bool ret = true; + + // always unload plugins, even if things are failing + unloadPlugins(); + + // shutdown this plugin + ret = onShutdown(); + if(!ret) { + kdError() << "failed to shutdown " << instanceName() << endl; + } + + return true; +} + +QCString +Plugin::instanceName() const +{ + return _instanceName; +} + +KConfig * +Plugin::config() +{ + return _config; +} + +bool +Plugin::onInit() +{ + return true; +} + +bool +Plugin::onShutdown() +{ + return true; +} + +bool +Plugin::configure() +{ + bool ret = true; + + // grab the OnStartup map + KConfig *conf = config(); + if(!conf) { + kdDebug() << "no configuration for " << instanceName() << endl; + ret = false; + } + + if(ret) { + // set the config group to Load Actions + conf->setGroup("Load Actions"); + + // load standard plugins by default + loadPlugins(conf, "Load"); + + // only load GUI plugins if this is not a terminal app + if(KApplication::kApplication()->type() != QApplication::Tty) { + loadPlugins(conf, "LoadGUI"); + } + } + + return ret; +} + +QString +Plugin::category() +{ + return QString("miscellaneous"); +} + +#include "plugin.moc" diff --git a/umbrello/umbrello/plugin.h b/umbrello/umbrello/plugin.h new file mode 100644 index 00000000..8d1eacc8 --- /dev/null +++ b/umbrello/umbrello/plugin.h @@ -0,0 +1,163 @@ +/*************************************************************************** + plugin.h + ------------------- + begin : Mon Jan 13 2003 + copyright : (C) 2003 by Andrew Sutton + email : ansutton@kent.edu + Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef UMBRELLO_PLUGIN_H +#define UMBRELLO_PLUGIN_H + +// Qt includes +#include + +// KDE includes +#include + +// local includes +#include "configurable.h" + +// forward declarations +class QStringList; +class KConfig; + +/** + * This macro replaces the K_EXPORT_COMPONENT_FACTORY macro because of + * a simple defficiency for this application - the construction of the + * factory with a default instance name. This macro must be used in + * the .cpp file implementing the plugin. + * + * + * @param libname The name of the plugin. This corresponds to + * the name of the shared object without the ".so" + * extension. + * @param factory The type of factory. Typically, this will be + * KGenericFactory<> with the name of the plugin + * as the parameter. + */ +#define UMBRELLO_EXPORT_PLUGIN_FACTORY(libname, factory) \ + extern "C" { KDE_EXPORT void *init_##libname() { return new factory(#libname); } } + +namespace Umbrello +{ +// forward declarations +class PluginLoader; + +/** + * @ingroup U2_Lib + * + * The Plugin class is the base class for all modular functionality in + * the core Umbrello library. Because Umbrello is a plugin architecture, + * this class is derived from many times. Plugins are created via the + * KLibFactory of the encapsulating shared library and created from some + * other functional object (application, tool or plugin). After the plugin + * has been created, the init method is called. Before unloading, the + * shutdown method is called. Derived plugins can implement specific + * startup/shutdown behavior by overloading the onInit and onShutdown + * methods respectively. + * + * By default, plugins use a configuration group called [LoadActions] in + * the config file. Entries in this group define any dependant or on-demand + * plugins that should be loaded in conjunction with this plugin. Known + * entries (actions) are "Load" and "LoadGUI". Because plugins can be used + * by both GUI and command line tools, they must be selective about some + * functionality. Specifically, during configuration, a plugin for a tool + * must not load GUI plugins. + * + * In order to provide application-like functionality, this class offers + * support for accessing the configuration records of the KInstance object + * corresponding to the shared library. Because the KInstance object is + * only available within the scope of the shared library, the configuration + * records must be set in the constructor of the derived plugin class. However, + * because the construction name is passed to this constructor (as are the + * parent object and args), we can simply capture the name when the object + * is constructed. + */ +class Plugin : + public QObject, + public Configurable +{ + Q_OBJECT + friend class PluginLoader; +public: + /** Destroy a plugin.*/ + virtual ~Plugin(); + + /** Return the instance name of the plugin */ + QCString instanceName() const; + + /** Return the configuration record for the plugin */ + KConfig *config(); + + /** Return the category descriptor string */ + virtual QString category(); + + /** + * Unload the plugin. This method actually only decrements + * the reference count. When the refcount is 0, the object + * calls shutdown and deletes itself. + */ + void unload(); + +protected: + /** Construct a plugin */ + Plugin(QObject *parent, const char *name, const QStringList &args); + + /** Can be reimplemented to define plugin specific startup behavior */ + virtual bool onInit(); + + /** Can be reimplemented to define plugin specific shutdown behavior */ + virtual bool onShutdown(); + +private: + /** + * This method is called by the loader to initialize and configure the + * plugin. During initialization, any configured plugins are loaded. + * Before loading plugins, onInit is called to perform plugin specific + * initialization. This allows dependencies in the plugin chain. + * + * @return True on success, false on failure. + */ + bool init(); + + /** + * This method is called by the loader to shutdown the plugin. During + * shutdown, any configured plugins are unloaded this occurs before + * plugin specific shutdown so as to reduce dependency errors. + * + * @return True on success, false on failure. + */ + bool shutdown(); + + /** + * The configure method is called by init to parse the configuration + * file and load any plugins. Note that the libraries loaded depends + * on the GUI state of the application. If the application is type + * Qt::Tty, then we don't use the "loadGUI" action. + * + * @return True on success, false on failure. + */ + virtual bool configure(); + + /** Add to the reference count */ + void ref(); + +protected: + uint _ref; ///< Reference counter + QCString _instanceName; ///< Instance name of the plugin + KConfig *_config; ///< Configuration record +}; +} + +#endif diff --git a/umbrello/umbrello/pluginloader.cpp b/umbrello/umbrello/pluginloader.cpp new file mode 100644 index 00000000..db79bca1 --- /dev/null +++ b/umbrello/umbrello/pluginloader.cpp @@ -0,0 +1,177 @@ +/*************************************************************************** + begin : Mon Jan 13 2003 + copyright : (C) 2003 by Andrew Sutton + email : ansutton@kent.edu +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +// own header +#include "pluginloader.h" + +// Qt includes +#include + +// KDE includes +#include +#include + +// u2 includes +#include "plugin.h" + +using namespace Umbrello; + +// static data +PluginLoader *PluginLoader::_instance = NULL; + +PluginLoader::PluginLoader() : + _plugins(), + _categories() +{ + // preseed the categories + _categories["metamodel"] = PluginList(); + _categories["storage"] = PluginList(); + _categories["visual"] = PluginList(); +} + +PluginLoader::~PluginLoader() +{ +} + +PluginLoader * +PluginLoader::instance() +{ + if(!_instance) _instance = new PluginLoader; + return _instance; +} + +Plugin * +PluginLoader::loadPlugin(const QString &name) +{ + KLibrary *lib = NULL; + KLibFactory *factory = NULL; + Plugin *plugin = NULL; + PluginMap::iterator it; + bool success = true; + + // if the plugin has already been loaded, increment + // its reference and return. + if((it = _plugins.find(name)) != _plugins.end()) { + plugin = it.data(); + plugin->ref(); + return plugin; + } + + // use KLibLoader to get a reference to the library + lib = KLibLoader::self()->library(name.latin1()); + if(!lib) { + kdError() << "failed loading plugin library " << name << endl; + success = false; + } + + // get the factory from the library + if(success) { + factory = lib->factory(); + if(!factory) { + kdError() << "failed to find factory for " << name << endl; + success = false; + } + } + + // use the factory to create the plugin + if(success) { + plugin = dynamic_cast(factory->create((QObject*)0, name.latin1())); + if(!plugin) { + kdError() << "failed to create a plugin object for " << name << endl; + success = false; + } + else { + // we have to register the plugin here, otherwise, we can get + // recursive loads + _plugins[name] = plugin; + _categories[plugin->category()].append(plugin); + } + } + + // initialize the plugin + if(success && plugin) { + success = plugin->init(); + if(!success) { + // on failure, delete the plugin. this should cause the + // library to unload. + kdError() << "failure initializing " << name << endl; + _categories[plugin->category()].remove(plugin); + _plugins.remove(name); + delete plugin; + } + } + + // finally, finally connect to the destroyed signal and keep a + // reference to it + if(success) { + plugin->ref(); + connect(plugin, SIGNAL(destroyed(QObject *)), SLOT(slotDestroyed(QObject *))); + } + + return plugin; +} + +Plugin * +PluginLoader::findPlugin(const QString &name) +{ + Plugin *ret = NULL; + PluginMap::iterator it = _plugins.find(name); + if(it != _plugins.end()) { + ret = it.data(); + } + return ret; +} + +void +PluginLoader::unloadPlugin(const QString &name) +{ + KLibLoader::self()->unloadLibrary(name.latin1()); +} + +const PluginLoader::PluginMap & +PluginLoader::plugins() const +{ + return _plugins; +} + +const PluginLoader::CategoryMap & +PluginLoader::categories() const +{ + return _categories; +} + +void +PluginLoader::slotDestroyed(QObject *obj) +{ + Plugin *plugin = static_cast(obj); + + // we can't just use the name because its already been destroyed + // at this point. we have to iterate thru and find the reference + // by hand. + + PluginMap::iterator end(_plugins.end()); + for(PluginMap::iterator i = _plugins.begin(); i != end; ++i) { + Plugin *p = i.data(); + if(p == plugin) { + kdDebug() << "unloading plugin " << i.key() << endl; + + // remove it from the mapping + _plugins.remove(i); + break; + } + } +} + +#include "pluginloader.moc" diff --git a/umbrello/umbrello/pluginloader.h b/umbrello/umbrello/pluginloader.h new file mode 100644 index 00000000..64ebed6f --- /dev/null +++ b/umbrello/umbrello/pluginloader.h @@ -0,0 +1,132 @@ +/*************************************************************************** + pluginloader.h + ------------------- + begin : Mon Jan 13 2003 + copyright : (C) 2003 by Andrew Sutton + email : ansutton@kent.edu + Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef UMBRELLO_PLUGINLOADER_H +#define UMBRELLO_PLUGINLOADER_H + +// Qt includes +#include +#include +#include + +// forward declarations +class QString; + +namespace Umbrello +{ +// forward declarations +class Plugin; + +/** + * @ingroup U2_Lib + * + * The plugin loader is an abstraction that sits on top of KLibLoader. + * Whereas plugins are specialized shared objects, the plugin must + * specialize the loading of those objects. Essentially, the plugin + * loader provides a single unit of functionality - loading plugins. + * In order to load a plugin, we must first load a shared library + * and then use the libraries factory to create the plugin. However, + * because a plugin is required to be a singleton, we must ensure + * that no plugin is ever created more than once. To that end, the + * loader must also retain a map of loaded plugins. When a loaded + * plugin is requested, we can increase its reference count. + * + * On the subject of unloading, we actually have very little to do. + * The unload method on a plugin is simply a reference decrementer. + * When it reaches 0, the object destroys itself. Being a QObject, + * it will emit the destroyed signal just before deletion, allowing + * the plugin loader to respond to the event and remove the plugin + * from its mapping. + * + * The PluginLoader also manages categories of plugins. The runtime + * categories actually reflect the directory structure of the build + * environment with each category represented by the name of a + * directory. The categories are "pre-seeded" at startup. + * + * @bug Plugins are not removed from their respective categories + * when they are destroyed. It may be acceptable to call + * Plugin::category() from slotDestroyed because the category() + * method doesn't reference any local variables - it just returns + * a string. + */ +class PluginLoader : public QObject +{ + Q_OBJECT +public: + /** Destry the plugin loader */ + ~PluginLoader(); + + + /** Just a container of plugins */ + typedef QValueList PluginList; + + /** The containment type for mapping plugins */ + typedef QMap PluginMap; + + /** Container of plugin categories */ + typedef QMap CategoryMap; + + /** Singleton accessor */ + static PluginLoader *instance(); + + /** + * Load a plugin. Test to see if the plugin already exists. If it + * does, just add a reference to it and continue on. + */ + Plugin *loadPlugin(const QString &name); + + /** Find a plugin */ + Plugin *findPlugin(const QString &name); + + /** + * Unload a plugin. Never use this method. It is only used by the deref + * method of a plugin to cause this class to unload the corresponding + * library. In fact, there is actually no corresponding plugin to unload, + * we just unload the library. + */ + void unloadPlugin(const QString &name); + + /** + * Get a reference to the plugin mapping. This method wraps everything + * in consts with the express purpose that no changes are made to the + * plugin map after using this method. + */ + const PluginMap &plugins() const; + + /** Get a reference to the plugin category mapping. */ + const CategoryMap &categories() const; + +private slots: + /** + * This is used to connect to the destroyed signal emitted by plugins + * when they are finally deleted. The plugin loader uses this signal + * to remove the plugin from the plugin map. + */ + void slotDestroyed(QObject *obj); + +private: + /** Private constructor - This must be created through the instance method */ + PluginLoader(); + + static PluginLoader *_instance; ///< Singleton instance + PluginMap _plugins; ///< The plugin mapping + CategoryMap _categories; ///< Categories of plugins +}; +} + +#endif diff --git a/umbrello/umbrello/refactoring/Makefile.am b/umbrello/umbrello/refactoring/Makefile.am new file mode 100644 index 00000000..4ab82c59 --- /dev/null +++ b/umbrello/umbrello/refactoring/Makefile.am @@ -0,0 +1,8 @@ +noinst_LTLIBRARIES = librefactoring.la + +INCLUDES = -I$(top_srcdir) -I$(top_builddir)/umbrello/umbrello/dialogs $(all_includes) + +librefactoring_la_METASOURCES = AUTO + +librefactoring_la_SOURCES = refactoringassistant.cpp + diff --git a/umbrello/umbrello/refactoring/refactoringassistant.cpp b/umbrello/umbrello/refactoring/refactoringassistant.cpp new file mode 100644 index 00000000..0cbcf6aa --- /dev/null +++ b/umbrello/umbrello/refactoring/refactoringassistant.cpp @@ -0,0 +1,701 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003 Luis De la Parra * + * copyright (C) 2004-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#include "refactoringassistant.h" + +#include "../umlnamespace.h" +#include "../umldoc.h" +#include "../classifier.h" +#include "../attribute.h" +#include "../operation.h" +#include "../dialogs/classpropdlg.h" +#include "../dialogs/umloperationdialog.h" +#include "../dialogs/umlattributedialog.h" +#include "../object_factory.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +using std::type_info; + + +RefactoringAssistant::RefactoringAssistant( UMLDoc *doc, UMLClassifier *obj, QWidget *parent, const char *name ): + KListView( parent, name ), m_doc( doc ) +{ + loadPixmaps(); + + setRootIsDecorated( true ); + setAcceptDrops( true ); + setDropVisualizer( false ); + setItemsMovable( true ); + setSelectionModeExt( Single ); + setShowToolTips( true ); + setTooltipColumn( 0 ); + setDragEnabled( true ); + setDropHighlighter( true ); + setFullWidth( true ); + setSorting( -1 ); + + addColumn("Name "); + + m_menu = new QPopupMenu(this); + + connect(this,SIGNAL(doubleClicked(QListViewItem*)),this,SLOT(itemExecuted(QListViewItem*))); + connect(this,SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)), + this,SLOT(showContextMenu(KListView*,QListViewItem*,const QPoint&))); + + resize(300,400); + + refactor( obj ); +} + +RefactoringAssistant::~RefactoringAssistant() +{ + m_umlObjectMap.clear(); + clear(); +} + +void RefactoringAssistant::refactor( UMLClassifier *obj ) +{ + clear(); + m_umlObjectMap.clear(); + m_umlObject = obj; + if (! m_umlObject ) + { + return; + } + + addClassifier( obj, 0, true, true, true ); + QListViewItem *item = firstChild(); + item->setOpen(true); + for( item = item->firstChild(); item ; item = item->nextSibling() ) + item->setOpen(true); +} + + +UMLObject* RefactoringAssistant::findUMLObject( const QListViewItem *item ) +{ + QListViewItem *i = const_cast(item); + if( m_umlObjectMap.find(i) == m_umlObjectMap.end() ) + { + kWarning()<<"RefactoringAssistant::findUMLObject( QListViewItem *item )" + <<"item with text "<text(0)<<"not found in uml map!"<getID()) + << "does not have a ListItem" << endl; + return 0L; +} + + +void RefactoringAssistant::itemExecuted( QListViewItem *item ) +{ + UMLObject *o = findUMLObject( item ); + if(o) editProperties( ); +} + +void RefactoringAssistant::setVisibilityIcon( QListViewItem *item , const UMLObject *obj ) +{ + switch(obj->getVisibility()) + { + case Uml::Visibility::Public: + item->setPixmap(0,m_pixmaps.Public); + break; + case Uml::Visibility::Protected: + item->setPixmap(0,m_pixmaps.Protected); + break; + case Uml::Visibility::Private: + item->setPixmap(0,m_pixmaps.Private); + break; + case Uml::Visibility::Implementation: + item->setPixmap(0,m_pixmaps.Implementation); + break; + break; + } +} + +void RefactoringAssistant::umlObjectModified( const UMLObject *obj ) +{ + if( !obj ) + obj = dynamic_cast(sender()); + QListViewItem *item = findListViewItem( obj ); + if( !item ) + return; + item->setText( 0, obj->getName() ); + if( typeid(*obj) == typeid(UMLOperation) || + typeid(*obj) == typeid(UMLAttribute) ) + { + setVisibilityIcon( item, obj ); + } +} + +void RefactoringAssistant::operationAdded( UMLClassifierListItem *o ) +{ + UMLOperation *op = static_cast(o); + UMLClassifier *c = dynamic_cast(op->parent()); + if(!c) + { + kWarning() << "RefactoringAssistant::operationAdded(" << op->getName() + << ") - Parent of operation is not a classifier!" << endl; + return; + } + QListViewItem *item = findListViewItem( c ); + if( !item ) + { + return; + } + for( QListViewItem *folder = item->firstChild(); folder; folder = folder->nextSibling() ) + { + if( folder->text(1) == "operations" ) + { + item = new KListViewItem( folder, op->getName() ); + m_umlObjectMap[item] = op; + connect( op, SIGNAL( modified() ), this, SLOT( umlObjectModified() ) ); + setVisibilityIcon( item, op ); + break; + } + } +} + +void RefactoringAssistant::operationRemoved( UMLClassifierListItem *o ) +{ + UMLOperation *op = static_cast(o); + QListViewItem *item = findListViewItem( op ); + if( !item ) + { + return; + } + disconnect( op, SIGNAL( modified() ), this, SLOT( umlObjectModified() ) ); + m_umlObjectMap.erase(item); + delete item; +} + + +void RefactoringAssistant::attributeAdded( UMLClassifierListItem *a ) +{ + UMLAttribute *att = static_cast(a); + UMLClassifier *c = dynamic_cast(att->parent()); + if(!c) + { + kWarning() << "RefactoringAssistant::attributeAdded(" << att->getName() + << ") - Parent is not a class!" << endl; + return; + } + QListViewItem *item = findListViewItem( c ); + if( !item ) + { + return; + } + for( QListViewItem *folder = item->firstChild(); folder; folder = folder->nextSibling() ) + { + if( folder->text(1) == "attributes" ) + { + item = new KListViewItem( folder, att->getName() ); + m_umlObjectMap[item] = att; + connect( att, SIGNAL( modified() ), this, SLOT( umlObjectModified() ) ); + setVisibilityIcon( item, att ); + break; + } + } +} + +void RefactoringAssistant::attributeRemoved( UMLClassifierListItem *a ) +{ + UMLAttribute *att = static_cast(a); + QListViewItem *item = findListViewItem( att ); + if( !item ) + { + return; + } + disconnect( att, SIGNAL( modified() ), this, SLOT( umlObjectModified() ) ); + m_umlObjectMap.erase(item); + delete item; +} + +void RefactoringAssistant::editProperties( ) +{ + QListViewItem *item = selectedItem(); + if( item ) + { + UMLObject *o = findUMLObject( item ); + if( o ) editProperties( o ); + } +} + +void RefactoringAssistant::editProperties( UMLObject *obj ) +{ + KDialogBase *dia(0); + Uml::Object_Type t = obj->getBaseType(); + if (t == Uml::ot_Class || t == Uml::ot_Interface) + { + dia = new ClassPropDlg(this,obj,0,true); + } + else if (t == Uml::ot_Operation) + { + dia = new UMLOperationDialog(this,static_cast(obj)); + } + else if (t == Uml::ot_Attribute) + { + dia = new UMLAttributeDialog(this,static_cast(obj)); + } + else + { + kWarning()<<"RefactoringAssistant::editProperties( UMLObject *o ) caled for unknown type "<exec() ) + { + // need to update something? + } + delete dia; +} + +void RefactoringAssistant::showContextMenu(KListView* ,QListViewItem *item, const QPoint &p) +{ + m_menu->clear(); + UMLObject *obj = findUMLObject( item ); + if(obj) + {// Menu for UMLObjects + Uml::Object_Type t = obj->getBaseType(); + if (t == Uml::ot_Class) + { + m_menu->insertItem(i18n("Add Base Class"),this,SLOT(addBaseClassifier())); + m_menu->insertItem(i18n("Add Derived Class"),this,SLOT(addDerivedClassifier())); + // m_menu->insertItem(i18n("Add Interface Implementation"),this,SLOT(addInterfaceImplementation())); + m_menu->insertItem(i18n("Add Operation"),this,SLOT(createOperation())); + m_menu->insertItem(i18n("Add Attribute"),this,SLOT(createAttribute())); + } + else if (t == Uml::ot_Interface) + { + m_menu->insertItem(i18n("Add Base Interface"),this,SLOT(addSuperClassifier())); + m_menu->insertItem(i18n("Add Derived Interface"),this,SLOT(addDerivedClassifier())); + m_menu->insertItem(i18n("Add Operation"),this,SLOT(createOperation())); + } + // else + // { + // kDebug()<<"No context menu for objects of type "<insertSeparator(); + m_menu->insertItem(i18n("Properties"),this,SLOT(editProperties())); + } + else + {//menu for other ViewItems + if( item->text(1) == "operations" ) + { + m_menu->insertItem(i18n("Add Operation"),this,SLOT(createOperation())); + } + else if( item->text(1) == "attributes" ) + { + m_menu->insertItem(i18n("Add Attribute"),this,SLOT(createAttribute())); + } + else + { + kWarning()<<"RefactoringAssistant::showContextMenu() " + <<"called for extraneous item"<exec(p); +} + +void RefactoringAssistant::addBaseClassifier() +{ + QListViewItem *item = selectedItem(); + if(!item) + { + kWarning()<<"RefactoringAssistant::addBaseClassifier() " + <<"called with no item selected"<(obj) ) + { + kWarning()<<"RefactoringAssistant::addBaseClassifier() " + <<"called for a non-classifier object"<getBaseType(); + UMLClassifier *super = static_cast(Object_Factory::createUMLObject(t)); + if(!super) + return; + m_doc->createUMLAssociation( obj, super, Uml::at_Generalization ); + ////////////////////// Manually add the classifier to the assitant - would be nicer to do it with + ///////////////////// a signal, like operations and attributes + QListViewItem *baseFolder = item->firstChild(); + while( baseFolder->text(0) != i18n("Base Classifiers") ) + baseFolder = baseFolder->nextSibling(); + if(!baseFolder) + { + kWarning()<<"Cannot find Base Folder"<getName() ); + item->setPixmap(0,m_pixmaps.Generalization); + item->setExpandable( true ); + m_umlObjectMap[item] = super; + addClassifier( super, item, true, false, true); + ///////////////////////// +} + +void RefactoringAssistant::addDerivedClassifier() +{ + QListViewItem *item = selectedItem(); + if(!item) + { + kWarning()<<"RefactoringAssistant::addDerivedClassifier() " + <<"called with no item selected"<(obj) ) + { + kWarning()<<"RefactoringAssistant::addDerivedClassifier() " + <<"called for a non-classifier object"<getBaseType(); + UMLClassifier *derived = static_cast(Object_Factory::createUMLObject(t)); + if(!derived) + return; + m_doc->createUMLAssociation( derived, obj, Uml::at_Generalization ); + + ////////////////////// Manually add the classifier to the assitant - would be nicer to do it with + ///////////////////// a signal, like operations and attributes + QListViewItem *derivedFolder = item->firstChild(); + while( derivedFolder->text(0) != i18n("Derived Classifiers") ) + derivedFolder = derivedFolder->nextSibling(); + if(!derivedFolder) + { + kWarning()<<"Cannot find Derived Folder"<getName() ); + item->setPixmap(0,m_pixmaps.Subclass); + item->setExpandable( true ); + m_umlObjectMap[item] = derived; + addClassifier( derived, item, false, true, true); + ///////////////////////// +} + +void RefactoringAssistant::addInterfaceImplementation() +{ + kWarning()<<"RefactoringAssistant::addInterfaceImplementation()" + <<"not implemented... finish addSuperClassifier() first!!"<(obj) ) + // return; + // UMLObject *n = Object_Factory::createUMLObject( Uml::ot_Interface) ); + // if(!n) + // return; + // m_doc->createUMLAssociation( n, obj, Uml::at_Realization ); + // //refresh, add classifier to assistant +} + +void RefactoringAssistant::createOperation() +{ + QListViewItem *item = selectedItem(); + if(!item) + { + kWarning()<<"RefactoringAssistant::createOperation() " + <<"called with no item selected"<(findUMLObject( item )); + if( !c ) + return; + c->createOperation(); +} + +void RefactoringAssistant::createAttribute() +{ + QListViewItem *item = selectedItem(); + if(!item) + { + kWarning()<<"RefactoringAssistant::createAttribute() " + <<"called with no item selected"<(findUMLObject( item )); + if( !c ) + return; + c->createAttribute(); +} + + +void RefactoringAssistant::addClassifier( UMLClassifier *classifier, QListViewItem *parent, bool addSuper, bool addSub, bool recurse) +{ + QListViewItem *classifierItem, *item; + if( parent ) + { + classifierItem = parent; + } + else + { + classifierItem= new KListViewItem( this, classifier->getName() ); + m_umlObjectMap[classifierItem] = classifier; + } + + connect( classifier, SIGNAL( modified() ), this, SLOT( umlObjectModified() ) ); + + UMLClassifier *klass = dynamic_cast(classifier); + if( klass ) + {// only Classes have attributes... + connect( classifier, SIGNAL(attributeAdded(UMLClassifierListItem*)), + this, SLOT(attributeAdded(UMLClassifierListItem*))); + connect( classifier, SIGNAL(attributeRemoved(UMLClassifierListItem*)), + this, SLOT(attributeRemoved(UMLClassifierListItem*))); + + QListViewItem *attsFolder = new KListViewItem( classifierItem, i18n("Attributes"), "attributes" ); + attsFolder->setPixmap(0,SmallIcon("folder_green_open")); + attsFolder->setExpandable( true ); + UMLAttributeList atts = klass->getAttributeList(); + for( UMLAttribute *att = atts.first(); att; att = atts.next() ) + { + attributeAdded( att ); + } + + } + + // add operations + connect( classifier, SIGNAL(operationAdded(UMLClassifierListItem*)), + this, SLOT(operationAdded(UMLClassifierListItem*))); + connect( classifier, SIGNAL(operationRemoved(UMLClassifierListItem*)), + this, SLOT(operationRemoved(UMLClassifierListItem*))); + + QListViewItem *opsFolder = new KListViewItem( classifierItem, i18n("Operations"), "operations" ); + opsFolder->setPixmap(0,SmallIcon("folder_blue_open")); + opsFolder->setExpandable( true ); + UMLOperationList ops(classifier->getOpList()); + for( UMLOperation *op = ops.first(); op ; op = ops.next() ) + { + operationAdded( op ); + } + + //if add parents + if(addSuper) + { + QListViewItem *superFolder = new KListViewItem( classifierItem, i18n("Base Classifiers") ); + superFolder->setExpandable( true ); + UMLClassifierList super = classifier->findSuperClassConcepts(); + for( UMLClassifier *cl = super.first(); cl ; cl = super.next() ) + { + item = new KListViewItem( superFolder, cl->getName() ); + item->setPixmap(0,m_pixmaps.Generalization); + item->setExpandable( true ); + m_umlObjectMap[item] = cl; + if( recurse ) + { + addClassifier( cl, item, true, false, true); + } + + } + } + if(addSub) + { + //add derived classifiers + QListViewItem *derivedFolder = new KListViewItem( classifierItem, i18n("Derived Classifiers") ); + derivedFolder->setExpandable( true ); + UMLClassifierList derived = classifier->findSubClassConcepts(); + for( UMLClassifier *d = derived.first(); d ; d = derived.next() ) + { + item = new KListViewItem( derivedFolder, d->getName() ); + item->setPixmap(0,m_pixmaps.Subclass); + item->setExpandable( true ); + m_umlObjectMap[item] = d; + if( recurse ) + { + addClassifier( d, item, false, true, true); + } + + } + } +} + + +bool RefactoringAssistant::acceptDrag(QDropEvent *event) const +{ + //first check if we can accept drops at all, and if the operation + // is a move within the list itself + if( !acceptDrops() || !itemsMovable() || (event->source()!=viewport())) + { + return false; + } + + RefactoringAssistant *me = const_cast(this); + + //ok, check if the move is valid + QListViewItem *movingItem = 0, *afterme = 0, *parentItem = 0; + me->findDrop(event->pos(), parentItem, afterme); + for( movingItem = firstChild(); movingItem != 0; movingItem = movingItem->itemBelow() ) + { + if( movingItem->isSelected() ) + break; + } + if(!movingItem || !parentItem) + { kDebug()<<"moving/parent items not found - can't accept drag!"<findUMLObject(movingItem)) ) + { + kDebug()<<"Moving object not found in uml map!"<text(0)<getBaseType(); + if (t != Uml::ot_Attribute && t != Uml::ot_Operation) + { + kDebug()<<"only operations and attributes are movable! - return false"<text(0)<findUMLObject(parentItem); + if( parentObject && dynamic_cast(parentObject) ) + { + //droping to a classifier, ok + } + else + {//parent is not a classifier, so maybe it's a folder.. check types + if( (parentItem->text(1) == "operations" && t == Uml::ot_Operation) + || (parentItem->text(1) == "attributes" && t == Uml::ot_Attribute)) + { + parentObject = me->findUMLObject( parentItem->parent() ); + } + else + { + kDebug()<<"moving to item "<text(0)<<" -- "<text(1)<<" not valid"<(parentObject) && + (t == Uml::ot_Attribute || t == Uml::ot_Operation)) + { + return true; + } + + kDebug()<<"how did I get here? return false!!"<itemBelow() ) + { + if( movingItem->isSelected() ) + break; + } + if( !movingItem || (movingItem == afterme) || !(movingObject = findUMLObject(movingItem)) ) + { + kWarning()<<"Moving item not found or dropping after itself or item not found in uml obj map. aborting. (drop had already been accepted)"<getBaseType(); + newClassifier = dynamic_cast( findUMLObject( parentItem ) ); + if(!newClassifier) + { + if ((parentItem->text(1) == "operations" && t == Uml::ot_Operation) + || (parentItem->text(1) == "attributes" && t == Uml::ot_Attribute)) + { + newClassifier = dynamic_cast( findUMLObject( parentItem->parent() ) ); + } + if(!newClassifier) + { + kWarning()<<"New parent of object is not a Classifier - Drop had already been accepted - check!"<(movingObject); + if(newClassifier->checkOperationSignature(op->getName(), op->getParmList())) + { + QString msg = QString(i18n("An operation with that signature already exists in %1.\n")).arg(newClassifier->getName()) + + + QString(i18n("Choose a different name or parameter list." )); + KMessageBox::error(this, msg, i18n("Operation Name Invalid"), false); + return; + } + UMLClassifier *oldClassifier = dynamic_cast(op->parent()); + if(oldClassifier) + oldClassifier->removeOperation( op ); + newClassifier->addOperation( op ); + } + else if (t == Uml::ot_Attribute) + {kDebug()<<"moving attribute - not implemented"<(movingObject); + // if(!newClassifier->checkAttributeSignature(att)) + // { + // QString msg = QString(i18n("An attribute with that signature already exists in %1.\n")).arg(newClassifier->getName()) + // + + // QString(i18n("Choose a different name or parameter list." )); + // KMessageBox::error(this, msg, i18n("Operation Name Invalid"), false); + // return; + // } + // oldClassifier->removeAttribute( att ); + // newClassifier->addAttribute( att ); + } + //emit moved(moving, afterFirst, afterme); + emit moved(); +} + +void RefactoringAssistant::loadPixmaps() +{ + KStandardDirs *dirs = KGlobal::dirs(); + QString dataDir = dirs -> findResourceDir( "data", "umbrello/pics/object.png" ); + dataDir += "/umbrello/pics/"; + + m_pixmaps.Public.load( dataDir + "CVpublic_var.png" ); + m_pixmaps.Protected.load( dataDir + "CVprotected_var.png" ); + m_pixmaps.Private.load( dataDir + "CVprivate_var.png" ); + m_pixmaps.Implementation.load( dataDir + "CVimplementation_var.png" ); + m_pixmaps.Generalization.load( dataDir + "generalisation.png" ); + m_pixmaps.Subclass.load( dataDir + "uniassociation.png" ); + + +} + + + + +#include "refactoringassistant.moc" diff --git a/umbrello/umbrello/refactoring/refactoringassistant.h b/umbrello/umbrello/refactoring/refactoringassistant.h new file mode 100644 index 00000000..5d8bd79f --- /dev/null +++ b/umbrello/umbrello/refactoring/refactoringassistant.h @@ -0,0 +1,89 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003 Luis De la Parra * + * copyright (C) 2004-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + + +#ifndef REFACTORING_ASSISTANT +#define REFACTORING_ASSISTANT + + +#include +#include +#include + + +class UMLObject; +class UMLClassifier; +class UMLClassifierListItem; +class UMLDoc; + +class QPopupMenu; +class QPoint; + +class RefactoringAssistant : public KListView +{ + Q_OBJECT +public: + typedef std::map UMLObjectMap; + + explicit RefactoringAssistant( UMLDoc *doc, UMLClassifier *obj = 0, QWidget *parent = 0, const char *name = 0 ); + virtual ~RefactoringAssistant(); + + void refactor( UMLClassifier *obj ); + +public slots: + + void addBaseClassifier(); + void addDerivedClassifier(); + void addInterfaceImplementation(); + void createOperation( ); + void createAttribute( ); + void editProperties( ); + + void umlObjectModified( const UMLObject *obj = 0 ); + + void operationAdded( UMLClassifierListItem *o ); + void operationRemoved( UMLClassifierListItem *o ); + + void attributeAdded( UMLClassifierListItem *a ); + void attributeRemoved( UMLClassifierListItem *a ); + + void itemExecuted( QListViewItem *item ); + void showContextMenu( KListView*, QListViewItem*, const QPoint&); + +protected: + struct { QPixmap Public, + Protected, + Private, + Implementation, + Generalization, + Subclass; + } m_pixmaps; + + UMLObject* findUMLObject( const QListViewItem* ); + QListViewItem* findListViewItem( const UMLObject *obj ); + void editProperties( UMLObject *obj ); + void addClassifier( UMLClassifier *classifier, QListViewItem *parent = 0, bool addSuper = true, bool addSub = true, bool recurse = false ); + void loadPixmaps(); + virtual bool acceptDrag(QDropEvent *event) const; + virtual void movableDropEvent (QListViewItem* parent, QListViewItem* afterme); + void setVisibilityIcon( QListViewItem *item , const UMLObject *obj ); + UMLClassifier *m_umlObject; + UMLDoc *m_doc; + QPopupMenu *m_menu; + UMLObjectMap m_umlObjectMap; + + +}; + + +#endif + diff --git a/umbrello/umbrello/seqlinewidget.cpp b/umbrello/umbrello/seqlinewidget.cpp new file mode 100644 index 00000000..a4be0216 --- /dev/null +++ b/umbrello/umbrello/seqlinewidget.cpp @@ -0,0 +1,124 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "seqlinewidget.h" + +//kde includes +#include +#include +//app includes +#include "umlview.h" +#include "objectwidget.h" +#include "messagewidget.h" + +// class members +int const SeqLineWidget::m_nMouseDownEpsilonX = 20; + +SeqLineWidget::SeqLineWidget( UMLView * pView, ObjectWidget * pObject ) : QCanvasLine( pView -> canvas() ) { + m_pView = pView; + m_pObject = pObject; + setPen( QPen( m_pObject->getLineColor(), 0, Qt::DashLine ) ); + setZ( 0 ); + setVisible( true ); + m_DestructionBox.line1 = 0; + m_nLengthY = 250; + setupDestructionBox(); +} + +SeqLineWidget::~SeqLineWidget() {} + +int SeqLineWidget::onWidget( const QPoint & p ) { + int nOnWidget = 0; + QPoint sp = startPoint(); + QPoint ep = endPoint(); + //see if on widget ( for message creation ) + if( sp.x() - m_nMouseDownEpsilonX < p.x() + && ep.x() + m_nMouseDownEpsilonX > p.x() + && sp.y() < p.y() && ep.y() + 3 > p.y() ) + { + nOnWidget = 1; + } + return nOnWidget; +} + +void SeqLineWidget::cleanup() { + cleanupDestructionBox(); +} + +void SeqLineWidget::setStartPoint( int startX, int startY ) { + int endX = startX; + int endY = startY + m_nLengthY; + QCanvasLine::setPoints( startX, startY, endX, endY ); + moveDestructionBox(); +} + +void SeqLineWidget::cleanupDestructionBox() { + if ( m_DestructionBox.line1 ) { + delete m_DestructionBox.line1; + m_DestructionBox.line1 = 0; + delete m_DestructionBox.line2; + m_DestructionBox.line2 = 0; + } +} + +void SeqLineWidget::setupDestructionBox() { + cleanupDestructionBox(); + if( !m_pObject->getShowDestruction() ) { + return; + } + QRect rect; + rect.setX( m_pObject->getX() + m_pObject->getWidth() / 2 - 10 ); + rect.setY( m_pObject->getY() + m_pObject->getHeight() + m_nLengthY ); + rect.setWidth( 14 ); + rect.setHeight( 14 ); + + m_DestructionBox.line1 = new QCanvasLine( m_pView->canvas() ); + m_DestructionBox.setLine1Points(rect); + m_DestructionBox.line1->setVisible( true ); + m_DestructionBox.line1->setPen( QPen(m_pObject->getLineColor(), 2) ); + m_DestructionBox.line1->setZ( 3 ); + + m_DestructionBox.line2 = new QCanvasLine( m_pView -> canvas() ); + m_DestructionBox.setLine2Points(rect); + m_DestructionBox.line2->setVisible( true ); + m_DestructionBox.line2->setPen( QPen(m_pObject->getLineColor(), 2) ); + m_DestructionBox.line2->setZ( 3 ); +} + +void SeqLineWidget::moveDestructionBox() { + if( !m_DestructionBox.line1 ) { + return; + } + QRect rect; + rect.setX( m_pObject->getX() + m_pObject->getWidth() / 2 - 7 ); + rect.setY( m_pObject->getY() + m_pObject->getHeight() + m_nLengthY - 7 ); + rect.setWidth( 14 ); + rect.setHeight( 14 ); + m_DestructionBox.setLine1Points(rect); + m_DestructionBox.setLine2Points(rect); +} + +void SeqLineWidget::setEndOfLine(int yPosition) { + QPoint sp = startPoint(); + int newY = yPosition; + m_nLengthY = yPosition - m_pObject->getY() - m_pObject->getHeight(); + // normally the managing Objectwidget is responsible for the call of this function + // but to be sure - make a double check _against current position_ + if ( m_nLengthY < 0 ) { + m_nLengthY = 0; + newY = m_pObject->getY() + m_pObject->getHeight(); + } + setPoints( sp.x(), sp.y(), sp.x(), newY ); + moveDestructionBox(); + m_pView->resizeCanvasToItems(); +} + diff --git a/umbrello/umbrello/seqlinewidget.h b/umbrello/umbrello/seqlinewidget.h new file mode 100644 index 00000000..1978fafd --- /dev/null +++ b/umbrello/umbrello/seqlinewidget.h @@ -0,0 +1,135 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef SEQLINEWIDGET_H +#define SEQLINEWIDGET_H + +#include + +class UMLView; +class ObjectWidget; + +/** + * @short Widget class for graphical representation of sequence lines + * @author Paul Hensgen + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class SeqLineWidget : public QCanvasLine { +public: + /** + * Constructor. + */ + SeqLineWidget( UMLView * pView, ObjectWidget * pObject ); + + /** + * Destructor. + */ + ~SeqLineWidget(); + + /** + * Return whether on seq. line. + * Takes into account destruction box if shown. + * + * @param p The point to investigate. + * @return Non-zero if point is on this sequence line. + */ + int onWidget(const QPoint & p); + + /** + * Clean up anything before deletion. + */ + void cleanup(); + + /** + * Set up destruction box. + */ + void setupDestructionBox(); + + /** + * Set the start point of the line. + * + * @param startX X coordinate of the start point. + * @param startY Y coordinate of the start point. + */ + void setStartPoint( int startX, int startY ); + + /** + * Gets the length of the line. + * + * @return Length of the line. + */ + int getLineLength() { + return m_nLengthY; + } + + /** + * Returns the @ref ObjectWidget associated with this sequence line. + * + * @return Pointer to the associated ObjectWidget. + */ + ObjectWidget * getObjectWidget() { + return m_pObject; + } + + /** + * Sets the y position of the bottom of the vertical line. + * + * @param yPosition The y coordinate for the bottom of the line. + */ + void setEndOfLine(int yPosition); + +protected: + /** + * Clean up destruction box. + */ + void cleanupDestructionBox(); + + /** + * Move destruction box. + */ + void moveDestructionBox(); + + /** + * ObjectWidget associated with this sequence line. + */ + ObjectWidget * m_pObject; + + /** + * View displayed on. + */ + UMLView * m_pView; + + /// The destruction box. + struct DestructionBox { + QCanvasLine * line1; + QCanvasLine * line2; + void setLine1Points(QRect rect) { + line1->setPoints( rect.x(), rect.y(), + rect.x() + rect.width(), rect.y() + rect.height() ); + } + void setLine2Points(QRect rect) { + line2->setPoints( rect.x(), rect.y() + rect.height(), + rect.x() + rect.width(), rect.y() ); + } + } m_DestructionBox; + + /** + * The length of the line. + */ + int m_nLengthY; + + /** + * Margin used for mouse clicks. + */ + static int const m_nMouseDownEpsilonX; +}; + +#endif diff --git a/umbrello/umbrello/statewidget.cpp b/umbrello/umbrello/statewidget.cpp new file mode 100644 index 00000000..9d104234 --- /dev/null +++ b/umbrello/umbrello/statewidget.cpp @@ -0,0 +1,297 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "statewidget.h" + +// qt includes +#include + +// kde includes +#include +#include +#include + +// app includes +#include "uml.h" +#include "umldoc.h" +#include "docwindow.h" +#include "umlwidget.h" +#include "umlview.h" +#include "dialogs/statedialog.h" +#include "listpopupmenu.h" + +StateWidget::StateWidget(UMLView * view, StateType stateType, Uml::IDType id) + : UMLWidget(view, id) { + UMLWidget::setBaseType(Uml::wt_State); + m_StateType = stateType; + m_Text = "State"; + updateComponentSize(); +} + +StateWidget::~StateWidget() {} + +void StateWidget::draw(QPainter & p, int offsetX, int offsetY) { + UMLWidget::setPen(p); + const int w = width(); + const int h = height(); + switch (m_StateType) + { + case Normal : + if(UMLWidget::getUseFillColour()) + p.setBrush(UMLWidget::getFillColour()); + { + const QFontMetrics &fm = getFontMetrics(FT_NORMAL); + const int fontHeight = fm.lineSpacing(); + int textStartY = (h / 2) - (fontHeight / 2); + const int count = m_Activities.count(); + if( count == 0 ) { + p.drawRoundRect(offsetX, offsetY, w, h, (h*40)/w, (w*40)/h); + p.setPen(Qt::black); + QFont font = UMLWidget::getFont(); + font.setBold( false ); + p.setFont( font ); + p.drawText(offsetX + STATE_MARGIN, offsetY + textStartY, + w - STATE_MARGIN * 2, fontHeight, + Qt::AlignCenter, getName()); + UMLWidget::setPen(p); + } else { + p.drawRoundRect(offsetX, offsetY, w, h, (h*40)/w, (w*40)/h); + textStartY = offsetY + STATE_MARGIN; + p.setPen(Qt::black); + QFont font = UMLWidget::getFont(); + font.setBold( true ); + p.setFont( font ); + p.drawText(offsetX + STATE_MARGIN, textStartY, w - STATE_MARGIN * 2, + fontHeight, Qt::AlignCenter, getName()); + font.setBold( false ); + p.setFont( font ); + UMLWidget::setPen(p); + int linePosY = textStartY + fontHeight; + + QStringList::Iterator end(m_Activities.end()); + for( QStringList::Iterator it(m_Activities.begin()); it != end; ++it ) { + textStartY += fontHeight; + p.drawLine( offsetX, linePosY, offsetX + w - 1, linePosY ); + p.setPen(Qt::black); + p.drawText(offsetX + STATE_MARGIN, textStartY, w - STATE_MARGIN * 2 - 1, + fontHeight, Qt::AlignCenter, *it); + UMLWidget::setPen(p); + linePosY += fontHeight; + }//end for + }//end else + } + break; + case Initial : + p.setBrush( WidgetBase::getLineColor() ); + p.drawEllipse( offsetX, offsetY, w, h ); + break; + case End : + p.setBrush( WidgetBase::getLineColor() ); + p.drawEllipse( offsetX, offsetY, w, h ); + p.setBrush( Qt::white ); + p.drawEllipse( offsetX + 1, offsetY + 1, w - 2, h - 2 ); + p.setBrush( WidgetBase::getLineColor() ); + p.drawEllipse( offsetX + 3, offsetY + 3, w - 6, h - 6 ); + break; + default: + kWarning() << "Unknown state type:" << m_StateType << endl; + break; + } + if(m_bSelected) + drawSelected(&p, offsetX, offsetY); +} + +QSize StateWidget::calculateSize() { + int width = 10, height = 10; + if ( m_StateType == Normal ) { + const QFontMetrics &fm = getFontMetrics(FT_BOLD); + const int fontHeight = fm.lineSpacing(); + int textWidth = fm.width(getName()); + const int count = m_Activities.count(); + height = fontHeight; + if( count > 0 ) { + height = fontHeight * ( count + 1); + + QStringList::Iterator end(m_Activities.end()); + for( QStringList::Iterator it(m_Activities.begin()); it != end; ++it ) { + int w = fm.width( *it ); + if( w > textWidth ) + textWidth = w; + }//end for + }//end if + width = textWidth > STATE_WIDTH?textWidth:STATE_WIDTH; + height = height > STATE_HEIGHT?height:STATE_HEIGHT; + width += STATE_MARGIN * 2; + height += STATE_MARGIN * 2; + } + + return QSize(width, height); +} + +void StateWidget::setName(const QString &strName) { + m_Text = strName; + updateComponentSize(); + adjustAssocs( getX(), getY() ); +} + +QString StateWidget::getName() const { + return m_Text; +} + +StateWidget::StateType StateWidget::getStateType() const { + return m_StateType; +} + +void StateWidget::setStateType( StateType stateType ) { + m_StateType = stateType; +} + +void StateWidget::slotMenuSelection(int sel) { + bool done = false; + bool ok = false; + QString name = getName(); + + switch( sel ) { + case ListPopupMenu::mt_Rename: + name = KInputDialog::getText( i18n("Enter State Name"), i18n("Enter the name of the new state:"), getName(), &ok ); + if( ok && name.length() > 0 ) + setName( name ); + done = true; + break; + + case ListPopupMenu::mt_Properties: + showProperties(); + done = true; + break; + case ListPopupMenu::mt_New_Activity: + name = KInputDialog::getText( i18n("Enter Activity"), i18n("Enter the name of the new activity:"), i18n("new activity"), &ok ); + if( ok && name.length() > 0 ) + addActivity( name ); + done = true; + break; + } + + if( !done ) + UMLWidget::slotMenuSelection( sel ); +} + +bool StateWidget::addActivity( const QString &activity ) { + m_Activities.append( activity ); + updateComponentSize(); + return true; +} + +bool StateWidget::removeActivity( const QString &activity ) { + int index = - 1; + if( ( index = m_Activities.findIndex( activity ) ) == -1 ) + return false; + m_Activities.remove( m_Activities.at( index ) ); + updateComponentSize(); + return true; +} + +void StateWidget::setActivities( QStringList & list ) { + m_Activities = list; + updateComponentSize(); +} + +QStringList & StateWidget::getActivityList() { + return m_Activities; +} + +bool StateWidget::renameActivity( const QString &activity, const QString &newName ) { + int index = - 1; + if( ( index = m_Activities.findIndex( activity ) ) == -1 ) + return false; + m_Activities[ index ] = newName; + return true; +} + +void StateWidget::showProperties() { + DocWindow *docwindow = UMLApp::app()->getDocWindow(); + docwindow->updateDocumentation(false); + + StateDialog dialog(m_pView, this); + if (dialog.exec() && dialog.getChangesMade()) { + docwindow->showDocumentation(this, true); + UMLApp::app()->getDocument()->setModified(true); + } +} + +bool StateWidget::isState(WorkToolBar::ToolBar_Buttons tbb, StateType& resultType) +{ + bool status = true; + switch (tbb) { + case WorkToolBar::tbb_Initial_State: + resultType = Initial; + break; + case WorkToolBar::tbb_State: + resultType = Normal; + break; + case WorkToolBar::tbb_End_State: + resultType = End; + break; + default: + status = false; + break; + } + return status; +} + +void StateWidget::saveToXMI( QDomDocument & qDoc, QDomElement & qElement ) { + QDomElement stateElement = qDoc.createElement( "statewidget" ); + UMLWidget::saveToXMI( qDoc, stateElement ); + stateElement.setAttribute( "statename", m_Text ); + stateElement.setAttribute( "documentation", m_Doc ); + stateElement.setAttribute( "statetype", m_StateType ); + //save states activities + QDomElement activitiesElement = qDoc.createElement( "Activities" ); + + QStringList::Iterator end(m_Activities.end()); + for( QStringList::Iterator it(m_Activities.begin()); it != end; ++it ) { + QDomElement tempElement = qDoc.createElement( "Activity" ); + tempElement.setAttribute( "name", *it ); + activitiesElement.appendChild( tempElement ); + }//end for + stateElement.appendChild( activitiesElement ); + qElement.appendChild( stateElement ); +} + +bool StateWidget::loadFromXMI( QDomElement & qElement ) { + if( !UMLWidget::loadFromXMI( qElement ) ) + return false; + m_Text = qElement.attribute( "statename", "" ); + m_Doc = qElement.attribute( "documentation", "" ); + QString type = qElement.attribute( "statetype", "1" ); + m_StateType = (StateType)type.toInt(); + //load states activities + QDomNode node = qElement.firstChild(); + QDomElement tempElement = node.toElement(); + if( !tempElement.isNull() && tempElement.tagName() == "Activities" ) { + QDomNode node = tempElement.firstChild(); + QDomElement activityElement = node.toElement(); + while( !activityElement.isNull() ) { + if( activityElement.tagName() == "Activity" ) { + QString name = activityElement.attribute( "name", "" ); + if( !name.isEmpty() ) + m_Activities.append( name ); + }//end if + node = node.nextSibling(); + activityElement = node.toElement(); + }//end while + }//end if + return true; +} + + +#include "statewidget.moc" + diff --git a/umbrello/umbrello/statewidget.h b/umbrello/umbrello/statewidget.h new file mode 100644 index 00000000..d214980b --- /dev/null +++ b/umbrello/umbrello/statewidget.h @@ -0,0 +1,163 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * +***************************************************************************/ + +#ifndef STATEWIDGET_H +#define STATEWIDGET_H +#include +#include +#include "umlwidget.h" +#include "worktoolbar.h" + +#define STATE_MARGIN 5 +#define STATE_WIDTH 30 +#define STATE_HEIGHT 10 + +/** + * This class is the graphical version of a UML State. + * + * A StateWidget is created by a @ref UMLView. A StateWidget belongs to + * only one @ref UMLView instance. + * When the @ref UMLView instance that this class belongs to is destroyed, + * it will be automatically deleted. + * + * The StateWidget class inherits from the @ref UMLWidget class which adds + * most of the functionality to this class. + * + * @short A graphical version of a UML State. + * @author Paul Hensgen + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class StateWidget : public UMLWidget { + Q_OBJECT +public: + + /// Enumeration that codes the different types of state. + enum StateType + { + Initial = 0, + Normal, + End + }; + + /** + * Creates a State widget. + * + * @param view The parent of the widget. + * @param stateType The type of state. + * @param id The ID to assign (-1 will prompt a new ID.) + */ + explicit StateWidget( UMLView * view, StateType stateType = Normal, Uml::IDType id = Uml::id_None ); + + /** + * destructor + */ + virtual ~StateWidget(); + + /** + * Overrides the standard paint event. + */ + void draw(QPainter & p, int offsetX, int offsetY); + + /** + * Sets the name of the State. + */ + virtual void setName(const QString &strName); + + /** + * Returns the name of the State. + */ + virtual QString getName() const; + + /** + * Returns the type of state. + */ + StateType getStateType() const; + + /** + * Sets the type of state. + */ + void setStateType( StateType stateType ); + + /** + * Adds the given activity to the state. + */ + bool addActivity( const QString &activity ); + + /** + * Removes the given activity from the state. + */ + bool removeActivity( const QString &activity ); + + /** + * Renames the given activity. + */ + bool renameActivity( const QString &activity, const QString &newName ); + + /** + * Sets the states activities to the ones given. + */ + void setActivities( QStringList & list ); + + /** + * Returns the list of activities. + */ + QStringList & getActivityList(); + + /** + * Show a properties dialog for a StateWidget. + */ + void showProperties(); + + /** + * Returns true if the given toolbar button represents a State. + * + * @param tbb Input value of type WorkToolBar::ToolBar_Buttons. + * @param resultType Output value, the StateType that corresponds to tbb. + * Only set if the method returns true. + */ + static bool isState( WorkToolBar::ToolBar_Buttons tbb, + StateType& resultType ); + + /** + * Creates the "statewidget" XMI element. + */ + void saveToXMI( QDomDocument & qDoc, QDomElement & qElement ); + + /** + * Loads a "statewidget" XMI element. + */ + bool loadFromXMI( QDomElement & qElement ); + +protected: + /** + * Overrides method from UMLWidget + */ + QSize calculateSize(); + + /** + * Type of state. + */ + StateType m_StateType; + + /** + * List of activities for the state. + */ + QStringList m_Activities; + +public slots: + + /** + * Captures any popup menu signals for menus it created. + */ + void slotMenuSelection(int sel); +}; + +#endif diff --git a/umbrello/umbrello/stereotype.cpp b/umbrello/umbrello/stereotype.cpp new file mode 100644 index 00000000..5cd68f11 --- /dev/null +++ b/umbrello/umbrello/stereotype.cpp @@ -0,0 +1,94 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "stereotype.h" + +// qt/kde includes +#include +#include +#include + +// local includes +#include "umldoc.h" +#include "uml.h" + +UMLStereotype::UMLStereotype(const QString &name, Uml::IDType id /* = Uml::id_None */) + : UMLObject( name, id ) { + m_BaseType = Uml::ot_Stereotype; + UMLStereotype * existing = UMLApp::app()->getDocument()->findStereotype(name); + if (existing) { + kError() << "UMLStereotype constructor: " << name << " already exists" + << kdBacktrace(25) << endl; + } + m_refCount = 0; +} + +UMLStereotype::UMLStereotype() : UMLObject() { + m_BaseType = Uml::ot_Stereotype; + m_refCount = 0; +} + +UMLStereotype::~UMLStereotype() {} + +bool UMLStereotype::operator==( UMLStereotype &rhs) { + if (this == &rhs) { + return true; + } + + if ( !UMLObject::operator==( rhs ) ) { + return false; + } + + return true; +} + +void UMLStereotype::copyInto(UMLStereotype *rhs) const +{ + UMLObject::copyInto(rhs); +} + +UMLObject* UMLStereotype::clone() const +{ + UMLStereotype *clone = new UMLStereotype(); + copyInto(clone); + + return clone; +} + + +void UMLStereotype::saveToXMI(QDomDocument& qDoc, QDomElement& qElement) { + //FIXME: uml13.dtd compliance + QDomElement stereotypeElement = UMLObject::save("UML:Stereotype", qDoc); + qElement.appendChild( stereotypeElement ); +} + +bool UMLStereotype::showPropertiesDialog(QWidget* parent) { + bool ok; + QString name = KInputDialog::getText(i18n("Stereotype"), i18n("Enter name:"), getName(),&ok, parent); + if (ok) { + setName(name); + } + return ok; +} + +void UMLStereotype::incrRefCount() { + m_refCount++; +} + +void UMLStereotype::decrRefCount() { + m_refCount--; +} + +int UMLStereotype::refCount() const { + return m_refCount; +} + diff --git a/umbrello/umbrello/stereotype.h b/umbrello/umbrello/stereotype.h new file mode 100644 index 00000000..a836952f --- /dev/null +++ b/umbrello/umbrello/stereotype.h @@ -0,0 +1,103 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef STEREOTYPE_H +#define STEREOTYPE_H + +#include "umlobject.h" + +/** + * This class is used to set up information for a stereotype. + * Stereotypes are used essentially as properties of + * attributes and operations etc. + * + * @short Sets up stereotype information. + * @author Jonathan Riddell + * @author Oliver Kellogg + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + +class UMLStereotype : public UMLObject { +public: + /** + * Sets up a stereotype. + * + * @param name The name of this UMLStereotype. + * @param id The unique id given to this UMLStereotype. + */ + explicit UMLStereotype(const QString &name, Uml::IDType id = Uml::id_None); + + /** + * Sets up a stereotype. + */ + UMLStereotype(); + + /** + * Overloaded '==' operator + */ + bool operator==(UMLStereotype &rhs); + + /** + * destructor + */ + virtual ~UMLStereotype(); + + /** + * Copy the internal presentation of this object into the new + * object. + */ + virtual void copyInto(UMLStereotype *rhs) const; + + /** + * Make a clone of this object. + */ + virtual UMLObject* clone() const; + + /** + * Increments the reference count for this stereotype. + */ + void incrRefCount(); + + /** + * Decrements the reference count for this stereotype. + */ + void decrRefCount(); + + /** + * Returns the reference count for this stereotype. + */ + int refCount() const; + + /** + * Saves to the XMI element. + */ + void saveToXMI(QDomDocument& qDoc, QDomElement& qElement); + + /** + * Display the properties configuration dialog for the stereotype + * (just a line edit). + */ + bool showPropertiesDialog(QWidget* parent); + +protected: + /** + * Each stereotype object is reference counted, i.e. client code + * manages it such that it comes into existence as soon as there is + * at least one user, and ceases existing when the number of users + * drops to 0. + * m_refCount reflects the number of users. It is externally managed, + * i.e. client code must take care to call incrRefCount() and + * decrRefCount() as appropriate. + */ + int m_refCount; +}; + +#endif diff --git a/umbrello/umbrello/template.cpp b/umbrello/umbrello/template.cpp new file mode 100644 index 00000000..12d08fab --- /dev/null +++ b/umbrello/umbrello/template.cpp @@ -0,0 +1,95 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "template.h" + +// qt/kde includes +#include +#include + +// app includes +#include "uml.h" +#include "umldoc.h" +#include "dialogs/umltemplatedialog.h" + +UMLTemplate::UMLTemplate(const UMLObject *parent, const QString& name, + Uml::IDType id, const QString& type) + : UMLClassifierListItem( parent, name, id ) { + setTypeName( type ); + m_BaseType = Uml::ot_Template; +} + +UMLTemplate::UMLTemplate(const UMLObject *parent) + : UMLClassifierListItem( parent ) { + m_BaseType = Uml::ot_Template; +} + +UMLTemplate::~UMLTemplate() {} + +QString UMLTemplate::toString(Uml::Signature_Type /*sig = st_NoSig*/) { + if (m_pSecondary == NULL || m_pSecondary->getName() == "class") { + return getName(); + } else { + return getName() + " : " + m_pSecondary->getName(); + } +} + +QString UMLTemplate::getTypeName() { + if (m_pSecondary == NULL) + return "class"; + return m_pSecondary->getName(); +} + +bool UMLTemplate::operator==(UMLTemplate &rhs) { + if (this == &rhs) { + return true; + } + if ( !UMLObject::operator==( rhs ) ) { + return false; + } + if (m_pSecondary != rhs.m_pSecondary) { + return false; + } + return true; +} + +void UMLTemplate::copyInto(UMLTemplate *rhs) const +{ + UMLClassifierListItem::copyInto(rhs); +} + +UMLObject* UMLTemplate::clone() const +{ + UMLTemplate *clone = new UMLTemplate( (UMLTemplate*) parent()); + copyInto(clone); + + return clone; +} + + +void UMLTemplate::saveToXMI(QDomDocument& qDoc, QDomElement& qElement) { + //FIXME: uml13.dtd compliance + QDomElement attributeElement = UMLObject::save("UML:TemplateParameter", qDoc); + if (m_pSecondary) + attributeElement.setAttribute("type", ID2STR(m_pSecondary->getID())); + qElement.appendChild(attributeElement); +} + +bool UMLTemplate::load(QDomElement& element) { + m_SecondaryId = element.attribute("type", ""); + return true; +} + +bool UMLTemplate::showPropertiesDialog(QWidget* parent) { + UMLTemplateDialog dialog(parent, this); + return dialog.exec(); +} diff --git a/umbrello/umbrello/template.h b/umbrello/umbrello/template.h new file mode 100644 index 00000000..1109411a --- /dev/null +++ b/umbrello/umbrello/template.h @@ -0,0 +1,107 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef TEMPLATE_H +#define TEMPLATE_H + +#include "classifierlistitem.h" + +/** + * This class holds information used by template classes, called + * paramaterised class in UML and a generic in Java. It has a + * type (usually just "class") and name. + * + * @short Sets up template information. + * @author Jonathan Riddell + * @see UMLObject + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + +class UMLTemplate : public UMLClassifierListItem { +public: + /** + * Sets up a template. + * + * @param parent The parent of this UMLTemplate (i.e. its concept). + * @param name The name of this UMLTemplate. + * @param id The unique id given to this UMLTemplate. + * @param type The type of this UMLTemplate. + */ + UMLTemplate(const UMLObject *parent, const QString& name, + Uml::IDType id = Uml::id_None, const QString& type = "class"); + + /** + * Sets up a template. + * + * @param parent The parent of this UMLTemplate (i.e. its concept). + */ + UMLTemplate(const UMLObject *parent); + + /** + * Overloaded '==' operator + */ + bool operator==(UMLTemplate &rhs); + + /** + * Copy the internal presentation of this object into the new + * object. + */ + virtual void copyInto(UMLTemplate *rhs) const; + + /** + * Make a clone of this object. + */ + virtual UMLObject* clone() const; + + /** + * destructor + */ + virtual ~UMLTemplate(); + + /** + * Returns a string representation of the UMLTemplate. + * + * @param sig Currently unused. + * @return Returns a string representation of the UMLTemplate. + */ + QString toString(Uml::Signature_Type sig = Uml::st_NoSig); + + /** + * Overrides method from UMLClassifierListItem. + * Returns the type name of the UMLTemplate. + * If the template parameter is a class, there is no separate + * type object. In this case, getTypeName() returns "class". + * + * @return The type name of the UMLClassifierListItem. + */ + virtual QString getTypeName(); + + /** + * Display the properties configuration dialog for the template. + * + * @return Success status. + */ + bool showPropertiesDialog(QWidget* parent); + + /** + * Writes the XMI element. + */ + void saveToXMI(QDomDocument & qDoc, QDomElement & qElement); + +protected: + /** + * Loads the XMI element. + */ + bool load(QDomElement & element); + +}; + +#endif diff --git a/umbrello/umbrello/textblock.cpp b/umbrello/umbrello/textblock.cpp new file mode 100644 index 00000000..e9779875 --- /dev/null +++ b/umbrello/umbrello/textblock.cpp @@ -0,0 +1,320 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +/* This code generated by: + * Author : thomas + * Date : Wed Jun 18 2003 + */ + + +// own header +#include "textblock.h" + +// qt/kde includes +#include + +// local includes +#include "codedocument.h" +#include "codegenerator.h" +#include "codegenerationpolicy.h" +#include "uml.h" + +// Constructors/Destructors +// + +TextBlock::TextBlock ( CodeDocument * parent, const QString & text ) + : QObject ( (QObject *)parent, "textBlock") +{ + initFields(parent); + setText(text); +} + +TextBlock::~TextBlock ( ) { } + +// +// Methods +// + + +// Accessor methods +// + + +/** + * Set the value of the parent code document + * @param new_var the new value of m_parentDocument + */ +void TextBlock::setParentDocument ( CodeDocument * new_var ) { + m_parentDocument = new_var; +} + +bool TextBlock::canDelete ( ) { + return m_canDelete; +} + +/** + * Get the value of m_parentDocument + * @return the value of m_parentDocument + */ +CodeDocument * TextBlock::getParentDocument ( ) { + return m_parentDocument; +} + +/** + * Set the value of m_text + * The actual text of this code block. + * @param new_var the new value of m_text + */ +void TextBlock::setText ( const QString &new_var ) { + m_text = new_var; +} + +/** + * Add text to this object. + * + */ +void TextBlock::appendText ( const QString &new_text ) { + m_text = m_text + new_text; +} + +/** + * Get the value of m_text + * The actual text of this code block. + * @return the value of m_text + */ +QString TextBlock::getText ( ) const { + return m_text; +} + +/** + * Get the tag of this text block. This tag + * may be used to find this text block in the code document + * to which it belongs. + */ +QString TextBlock::getTag( ) const { + return m_tag; +} + +/** + * Set the tag of this text block. This tag + * may be used to find this text block in the code document + * to which it belongs. + */ +void TextBlock::setTag ( const QString &value ) { + m_tag = value; +} + +/** + * Set the value of m_writeOutText + * Whether or not to include the text of this TextBlock into a file. + * @param new_var the new value of m_writeOutText + */ +void TextBlock::setWriteOutText ( bool new_var ) { + m_writeOutText = new_var; +} + +/** + * Get the value of m_writeOutText + * Whether or not to include the text of this TextBlock into a file. + * @return the value of m_writeOutText + */ +bool TextBlock::getWriteOutText ( ) { + return m_writeOutText; +} + +/** Set how many times to indent this text block. + */ +void TextBlock::setIndentationLevel ( int level ) { + m_indentationLevel = level; +} + +/** Get how many times to indent this text block. + * The amount of each indenatation is determined from the parent + * codedocument codegeneration policy. + */ +int TextBlock::getIndentationLevel ( ) { + return m_indentationLevel; +} + +QString TextBlock::getNewLineEndingChars ( ) { + CodeGenerationPolicy * policy = UMLApp::app()->getCommonPolicy(); + return policy->getNewLineEndingChars(); +} + +QString TextBlock::getIndentation() { + CodeGenerationPolicy * policy = UMLApp::app()->getCommonPolicy(); + return policy->getIndentation(); +} + +QString TextBlock::getIndentationString ( int level ) { + if (!level) + level = m_indentationLevel; + QString indentAmount = getIndentation(); + QString indentation = ""; + for(int i=0; idisconnect(); + //this->deleteLater(); +} + +QString TextBlock::formatMultiLineText ( const QString &work, const QString &linePrefix, + const QString& breakStr, bool addBreak, bool lastLineHasBreak ) { + QString output = ""; + QString text = work; + QString endLine = getNewLineEndingChars(); + int matches = text.contains(QRegExp(breakStr)); + if(matches) + { + // check that last part of string matches, if not, then + // we have to tack on extra match + if(!text.contains(QRegExp(breakStr+"\\$"))) + matches++; + + for(int i=0; i < matches; i++) + { + QString line = text.section(QRegExp(breakStr),i,i); + output += linePrefix + line; + if((i != matches-1) || lastLineHasBreak) + output += endLine; // add break to line + } + } else { + output = linePrefix + text; + if(addBreak) + output += breakStr; + } + + return output; +} + +void TextBlock::setAttributesOnNode ( QDomDocument & doc, QDomElement & blockElement) +{ + + QString endLine = UMLApp::app()->getCommonPolicy()->getNewLineEndingChars(); + + if (&doc != 0 ) { + + blockElement.setAttribute("tag",getTag()); + + // only write these if different from defaults + if(getIndentationLevel()) + blockElement.setAttribute("indentLevel",QString::number(getIndentationLevel())); + if(!m_text.isEmpty()) + blockElement.setAttribute("text",encodeText(m_text,endLine)); + if(!getWriteOutText()) + blockElement.setAttribute("writeOutText",getWriteOutText()?"true":"false"); + if(!canDelete()) + blockElement.setAttribute("canDelete",canDelete()?"true":"false"); + + } + +} + +void TextBlock::setAttributesFromObject(TextBlock * obj) +{ + + // DONT set tag here. + setIndentationLevel(obj->getIndentationLevel()); + setText(obj->getText()); + setWriteOutText(obj->getWriteOutText()); + m_canDelete = obj->canDelete(); + +} + +void TextBlock::setAttributesFromNode (QDomElement & root ) { + + QString endLine = UMLApp::app()->getCommonPolicy()->getNewLineEndingChars(); + + setIndentationLevel(root.attribute("indentLevel","0").toInt()); + setTag(root.attribute("tag","")); + setText(decodeText(root.attribute("text",""),endLine)); + setWriteOutText(root.attribute("writeOutText","true") == "true" ? true : false); + m_canDelete = root.attribute("canDelete","true") == "true" ? true : false; + +} + +// encode text for XML storage +// we simply convert all types of newLines to the "\n" or +// entity. +QString TextBlock::encodeText(const QString& text, const QString &endLine) { + QString encoded = text; + encoded.replace(QRegExp(endLine)," "); + return encoded; +} + +// encode text for XML storage +// we simply convert all types of newLines to the "\n" or +// entity. +QString TextBlock::decodeText(const QString& text, const QString &endLine) { + QString decoded = text; + decoded.replace(QRegExp(" "),endLine); + return decoded; +} + +/** + * @return QString + */ +QString TextBlock::toString ( ) +{ + + // simple output method + if(m_writeOutText && !m_text.isEmpty()) + { + QString endLine = UMLApp::app()->getCommonPolicy()->getNewLineEndingChars(); + return formatMultiLineText(m_text, getIndentationString(), endLine); + } else + return ""; +} + +void TextBlock::initFields ( CodeDocument * parent ) { + m_canDelete = true; + m_writeOutText = true; + m_parentDocument = parent; + m_text = ""; + m_tag = ""; + m_indentationLevel = 0; +} + +#include "textblock.moc" diff --git a/umbrello/umbrello/textblock.h b/umbrello/umbrello/textblock.h new file mode 100644 index 00000000..16dcd910 --- /dev/null +++ b/umbrello/umbrello/textblock.h @@ -0,0 +1,241 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +/* This code generated by: + * Author : thomas + * Date : Wed Jun 18 2003 + */ + + + +#ifndef TEXTBLOCK_H +#define TEXTBLOCK_H + +#include +#include + +class CodeDocument; + +/** + * class TextBlock + * The fundemental unit of text within an output file containing code. + */ + +class TextBlock : virtual public QObject { + friend class CodeGenObjectWithTextBlocks; + friend class ClassifierCodeDocument; + Q_OBJECT +public: + + // Constructors/Destructors + // + + /** + * Constructors + */ + explicit TextBlock ( CodeDocument * parent, const QString & text = ""); + + // destructor + ~TextBlock ( ); + + // Public attributes + // + + // Public attribute accessor methods + // + + /** + * Set the value of m_text + * The actual text of this code block. + * @param new_var the new value of m_text + */ + void setText ( const QString &new_var ); + + /** + * Add text to this object. + * + */ + void appendText ( const QString &new_text ); + + /** + * Get the value of m_text + * The actual text of this code block. + * @return the value of m_text + */ + QString getText ( ) const; + + /** + * Get the tag of this text block. This tag + * may be used to find this text block in the code document + * to which it belongs. + */ + QString getTag( ) const; + + /** + * Set the tag of this text block. This tag + * may be used to find this text block in the code document + * to which it belongs. + */ + void setTag( const QString &value ); + + /** + * Get the value of m_parentDoc + * @return the value of m_parentDoc + */ + CodeDocument * getParentDocument ( ); + + /** + * Set the value of m_writeOutText + * Whether or not to include the text of this TextBlock into a file. + * @param new_var the new value of m_writeOutText + */ + void setWriteOutText ( bool new_var ); + + /** + * Get the value of m_writeOutText + * Whether or not to include the text of this TextBlock into a file. + * @return the value of m_writeOutText + */ + bool getWriteOutText ( ); + + /** Set how many times to indent this text block. + * The amount of each indenatation is determined from the parent + * codedocument codegeneration policy. + */ + void setIndentationLevel ( int level ); + + /** Get how many times to indent this text block. + * The amount of each indenatation is determined from the parent + * codedocument codegeneration policy. + */ + int getIndentationLevel ( ); + + /** Get the actual amount of indentation for a given level of indentation. + */ + QString getIndentationString ( int level = 0); + + /** Get how much a single "level" of indentation will actually indent. + */ + QString getIndentation(); + + QString getNewLineEndingChars ( ); + + /** Format a long text string to be more readable. + */ + // should be static + QString formatMultiLineText ( const QString &text, const QString &linePrefix, + const QString& breakStr, + bool alwaysAddBreak = true, bool lastLineHasBreak = true); + + /** UnFormat a long text string. Typically, this means removing + * the indentaion (linePrefix) and/or newline chars from each line. + * If an indentation isnt specified, then the current indentation is used. + */ + virtual QString unformatText ( const QString & text, const QString & indent = ""); + + /** + * @return QString + */ + virtual QString toString ( ); + + /** encode text for XML storage + * we simply convert all types of newLines to the "\n" or + * entity. + */ + static QString encodeText(const QString& text , const QString &endChars); + + + /** decode text from XML storage + * We simply convert all newLine entity to chosen line ending. + */ + static QString decodeText(const QString& text, const QString &endChars); + + /** + * Save the XMI representation of this object + */ + virtual void saveToXMI ( QDomDocument & doc, QDomElement & root ) = 0; + + /** + * load params from the appropriate XMI element node. + */ + virtual void loadFromXMI ( QDomElement & root ) = 0; + + /** Determine if its OK to delete this textblock from the document. + * Used by the text editor to know if deletion could cause a crash of + * the program. + */ + bool canDelete (); + + /** set the class attributes from a passed object + */ + virtual void setAttributesFromObject (TextBlock * obj); + + /** Used by the CodeEditor. It provides it with an appropriate + * starting string for a new line of text within the given textblock + * (for example a string with the proper indentation). + * If the indentation amount is '0' the current indentationString will + * be used. + */ + virtual QString getNewEditorLine( int indentAmount = 0 ); + + /** Ush. These are terrifically bad and must one day go away. + * Both methods indicate the range of lines in this textblock + * which may be edited by the codeeditor (assuming that any are + * actually editable). The default case is no lines are editable. + * The line numbering starts with '0' and a '-1' means no line + * qualifies. + */ + virtual int firstEditableLine(); + virtual int lastEditableLine(); + +protected: + + /** causes the text block to release all of its connections + * and any other text blocks that it 'owns'. + * needed to be called prior to deletion of the textblock. + */ + virtual void release (); + + /** + * Set the value of m_parentDocument + * @param new_var the new value of m_parentDoc + */ + void setParentDocument ( CodeDocument * new_var ); + + /** set attributes of the node that represents this class + * in the XMI document. + */ + virtual void setAttributesOnNode ( QDomDocument & doc, QDomElement & blockElement); + + /** set the class attributes of this object from + * the passed element node. + */ + virtual void setAttributesFromNode ( QDomElement & element); + + bool m_canDelete; + +private: + + // The actual text of this code block. + QString m_text; + QString m_tag; + + // Whether or not to include the text of this TextBlock into a file. + bool m_writeOutText; + + int m_indentationLevel; + CodeDocument * m_parentDocument; + + void initFields ( CodeDocument * doc); + +}; + +#endif // TEXTBLOCK_H diff --git a/umbrello/umbrello/textblocklist.h b/umbrello/umbrello/textblocklist.h new file mode 100644 index 00000000..48048b8b --- /dev/null +++ b/umbrello/umbrello/textblocklist.h @@ -0,0 +1,23 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef _TEXTBLOCKLIST_H +#define _TEXTBLOCKLIST_H + +#include + +// forward declarations +class TextBlock; + +typedef QPtrList TextBlockList; +typedef QPtrListIterator TextBlockListIt; + +#endif diff --git a/umbrello/umbrello/tips b/umbrello/umbrello/tips new file mode 100644 index 00000000..504090f9 --- /dev/null +++ b/umbrello/umbrello/tips @@ -0,0 +1,129 @@ + + +

    Welcome to Umbrello.

    + +

    UML diagrams let you design and document object oriented software. The Umbrello Handbook is a good introduction to using UML.

    + +
    + + + +

    Welcome to Umbrello 1.5. New in this version are association classes, Ruby code generation, externalizable folders, ability to change interfaces into classes, and more.

    + +
    + + + +

    Tabbed Diagrams and Externalized Folders are mutually exclusive. If you need External Folders then deselect "Use tabbed diagrams" in the General Settings.

    + +
    + + + +

    Most diagram items can not be resized, they will resize themselves to fit to their contents. +Boxes, notes and sequence diagram messages can be resized, just click and drag on the red square.

    + +
    + + + +

    If you want to add an already existing class to a diagram just drag its entry from the tree view.

    + +
    + + + +

    Umbrello's refactoring agent lets you move operations between a class and its derived and base +classes. +Right click a class to open the refactoring agent.

    + +
    + + + +

    Sequence diagram objects can have a destructor box and be drawn as actors. Double click one for +the Properties dialogue.

    + +
    + + + +

    Sequence diagram messages can act as constructors. Click on the object box (rather than the vertical line) to make it a constructor.

    + +
    + + + +

    Sequence diagrams support messages to self. Click on the same vertical line again to create an automessage.

    + +
    + + + +

    If on loading a foreign file nothing is displayed in the list view, try saving the model under a different name, +closing, and reloading the saved file. Usually the list view is then properly populated. +

    + +
    + + + +

    Cut and Copy will also export the image to a PNG clipboard which can be pasted into KWord +and other applications.

    + +
    + + + +

    Associations do not have to be in straight lines, double clicking on one will create a movable point.

    + +
    + + + +

    You can turn on autosaving in the Configure Umbrello dialog.

    + +
    + + + +

    Is a feature missing that you need in Umbrello? Please let us know. +Either add it to the bugs database with Report Bug from the Help menu +or send it to the uml-devel mailing list.

    + + +
    + + + +

    You can delete all selected objects by pressing Del or Backspace.

    + +
    + + + +

    If you've found a bug in Umbrello, please let us know. +You can submit bugs with the Report Bug tool in the Help menu.

    + +
    + + + +

    Pressing the Escape key sets the current tool to the select tool. +Backspace jumps to the previously used tool.

    + +
    + + + +

    You can select all objects by pressing Ctrl-A.

    + +
    + + + +

    You can create and setup a new class using the New Class Wizard in the Code menu.

    + +
    diff --git a/umbrello/umbrello/toolbarstate.cpp b/umbrello/umbrello/toolbarstate.cpp new file mode 100644 index 00000000..339214a8 --- /dev/null +++ b/umbrello/umbrello/toolbarstate.cpp @@ -0,0 +1,260 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "toolbarstate.h" + +// qt includes +#include // need for inverseWorldMatrix.map + +// app includes +#include "associationwidget.h" +#include "messagewidget.h" +#include "uml.h" +#include "umlview.h" +#include "umlwidget.h" + +ToolBarState::~ToolBarState() { + delete m_pMouseEvent; +} + +void ToolBarState::init() { + m_pUMLView->viewport()->setMouseTracking(false); + m_pMouseEvent = 0; + m_currentWidget = 0; + m_currentAssociation = 0; + + connect(m_pUMLView, SIGNAL(sigAssociationRemoved(AssociationWidget*)), + this, SLOT(slotAssociationRemoved(AssociationWidget*))); + connect(m_pUMLView, SIGNAL(sigWidgetRemoved(UMLWidget*)), + this, SLOT(slotWidgetRemoved(UMLWidget*))); +} + +void ToolBarState::cleanBeforeChange() { + disconnect(m_pUMLView, SIGNAL(sigAssociationRemoved(AssociationWidget*)), + this, SLOT(slotAssociationRemoved(AssociationWidget*))); + disconnect(m_pUMLView, SIGNAL(sigWidgetRemoved(UMLWidget*)), + this, SLOT(slotWidgetRemoved(UMLWidget*))); +} + +void ToolBarState::mousePress(QMouseEvent* ome) { + setMouseEvent(ome, QEvent::MouseButtonPress); + + m_pUMLView->viewport()->setMouseTracking(true); + + //TODO Doesn't another way of emiting the signal exist? A method only for + //that seems a bit dirty. + m_pUMLView->emitRemovePopupMenu(); + + // TODO: Check who needs this. + m_pUMLView->setPos(m_pMouseEvent->pos()); + + //TODO check why + m_pUMLView->setPaste(false); + + setCurrentElement(); + + if (getCurrentWidget()) { + mousePressWidget(); + } else if (getCurrentAssociation()) { + mousePressAssociation(); + } else { + mousePressEmpty(); + } +} + +void ToolBarState::mouseRelease(QMouseEvent* ome) { + setMouseEvent(ome, QEvent::MouseButtonRelease); + + // Set the position of the mouse + // TODO, should only be available in this state? + m_pUMLView->setPos(m_pMouseEvent->pos()); + + m_pUMLView->viewport()->setMouseTracking(false); + + if (getCurrentWidget()) { + mouseReleaseWidget(); + setCurrentWidget(0); + } else if (getCurrentAssociation()) { + mouseReleaseAssociation(); + setCurrentAssociation(0); + } else { + mouseReleaseEmpty(); + } + + // Default, rightbutton changes the tool. + // The arrow tool overrides the changeTool() function. + changeTool(); +} + +void ToolBarState::mouseDoubleClick(QMouseEvent* ome) { + setMouseEvent(ome, QEvent::MouseButtonDblClick); + + UMLWidget* currentWidget = m_pUMLView->getWidgetAt(m_pMouseEvent->pos()); + AssociationWidget* currentAssociation = getAssociationAt(m_pMouseEvent->pos()); + if (currentWidget) { + setCurrentWidget(currentWidget); + mouseDoubleClickWidget(); + setCurrentWidget(0); + } else if (currentAssociation) { + setCurrentAssociation(currentAssociation); + mouseDoubleClickAssociation(); + setCurrentAssociation(0); + } else { + mouseDoubleClickEmpty(); + } +} + +void ToolBarState::mouseMove(QMouseEvent* ome) { + setMouseEvent(ome, QEvent::MouseMove); + + if (getCurrentWidget()) { + mouseMoveWidget(); + } else if (getCurrentAssociation()) { + mouseMoveAssociation(); + } else { + mouseMoveEmpty(); + } + + //Scrolls the view + int vx = ome->x(); + int vy = ome->y(); + int contsX = m_pUMLView->contentsX(); + int contsY = m_pUMLView->contentsY(); + int visw = m_pUMLView->visibleWidth(); + int vish = m_pUMLView->visibleHeight(); + int dtr = visw - (vx-contsX); + int dtb = vish - (vy-contsY); + int dtt = (vy-contsY); + int dtl = (vx-contsX); + if (dtr < 30) m_pUMLView->scrollBy(30-dtr,0); + if (dtb < 30) m_pUMLView->scrollBy(0,30-dtb); + if (dtl < 30) m_pUMLView->scrollBy(-(30-dtl),0); + if (dtt < 30) m_pUMLView->scrollBy(0,-(30-dtt)); +} + +void ToolBarState::slotAssociationRemoved(AssociationWidget* association) { + if (association == getCurrentAssociation()) { + setCurrentAssociation(0); + } +} + +void ToolBarState::slotWidgetRemoved(UMLWidget* widget) { + if (widget == getCurrentWidget()) { + setCurrentWidget(0); + } +} + +ToolBarState::ToolBarState(UMLView *umlView) : QObject(umlView), m_pUMLView(umlView) { + m_pMouseEvent = NULL; + init(); +} + +void ToolBarState::setCurrentElement() { + // Check associations. + AssociationWidget* association = getAssociationAt(m_pMouseEvent->pos()); + if (association) { + setCurrentAssociation(association); + return; + } + + // Check messages. + //TODO check why message widgets are treated different + MessageWidget* message = getMessageAt(m_pMouseEvent->pos()); + if (message) { + setCurrentWidget(message); + return; + } + + // Check widgets. + UMLWidget *widget = m_pUMLView->getWidgetAt(m_pMouseEvent->pos()); + if (widget) { + setCurrentWidget(widget); + return; + } +} + +void ToolBarState::mousePressAssociation() { +} + +void ToolBarState::mousePressWidget() { +} + +void ToolBarState::mousePressEmpty() { + m_pUMLView->clearSelected(); +} + +void ToolBarState::mouseReleaseAssociation() { +} + +void ToolBarState::mouseReleaseWidget() { +} + +void ToolBarState::mouseReleaseEmpty() { +} + +void ToolBarState::mouseDoubleClickAssociation() { +} + +void ToolBarState::mouseDoubleClickWidget() { +} + +void ToolBarState::mouseDoubleClickEmpty() { + m_pUMLView->clearSelected(); +} + +void ToolBarState::mouseMoveAssociation() { +} + +void ToolBarState::mouseMoveWidget() { +} + +void ToolBarState::mouseMoveEmpty() { +} + +void ToolBarState::changeTool() { + if (m_pMouseEvent->state() == Qt::RightButton) { + UMLApp::app()->getWorkToolBar()->setDefaultTool(); + } +} + +void ToolBarState::setMouseEvent(QMouseEvent* ome, const QEvent::Type &type) { + if (m_pMouseEvent) delete m_pMouseEvent; + + m_pMouseEvent = new QMouseEvent(type, m_pUMLView->inverseWorldMatrix().map(ome->pos()), + ome->button(),ome->state()); +} + +MessageWidget* ToolBarState::getMessageAt(const QPoint& pos) { + MessageWidget* message = 0; + for (MessageWidgetListIt it(m_pUMLView->getMessageList()); + (message = it.current()) != 0; ++it) { + if (message->isVisible() && message->onWidget(pos)) { + return message; + } + } + + return message; +} + +AssociationWidget* ToolBarState::getAssociationAt(const QPoint& pos) { + AssociationWidget* association = 0; + for (AssociationWidgetListIt it(m_pUMLView->getAssociationList()); + (association = it.current()) != 0; ++it) { + if (association->onAssociation(pos)) { + return association; + } + } + + return association; +} + +#include "toolbarstate.moc" diff --git a/umbrello/umbrello/toolbarstate.h b/umbrello/umbrello/toolbarstate.h new file mode 100644 index 00000000..e005c021 --- /dev/null +++ b/umbrello/umbrello/toolbarstate.h @@ -0,0 +1,365 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef TOOLBARSTATE_H +#define TOOLBARSTATE_H + +#include +#include + +class QEvent; +class QMouseEvent; + +class AssociationWidget; +class MessageWidget; +class UMLView; +class UMLWidget; + + +/** + * Base class for toolbar states. + * All toolbar states inherit directly or indirectly from this class. Toolbar + * states represent tools that work with the diagram (for example, to create + * widgets, make associations...). All the mouse events received in the diagram + * are delivered to the toolbar state currently active. The events are handled + * in the tool and it executes the needed actions. + * + * All the mouse event handlers can be overridden in subclasses. However, the + * behaviour of the main handlers shouldn't be modified (apart from extend it, + * that is, call the base implementation before any other actions in the derived + * method). + * + * In order to handle the events, each main handler has three protected + * "sub-handlers" named like the main handler with the suffixes "Association", + * "Wdiget" and "Empty". The events received in the main handlers are delivered + * to the suitable handler, depending on if the event happened on an association, + * on a widget or on an empty space of the diagram. Those methods are the ones to + * override or extend to specify the behaviour of the toolbar state. + * + * The mouse events received in main handlers are tweaked to use the inverse + * position. The modified event is saved in m_pMouseEvent. This is the event that + * must be used everywhere. + * + * The association or widget that will receive the events is set in press event. + * How they are set can be tweaked in subclasses overriding setCurrentElement(). + * Once a press event happens, all the mouse events and the release event are sent + * to the same widget or association. Mouse events are delivered only when mouse + * tracking is enabled. It is enabled in press event, and disabled in release + * event. Also, it is disabled in the toolbar state initialization. Additionally, + * it can be enabled or disabled in other situations by subclasses if needed. + * + * After handling a release event, the tool is changed if needed. Default + * implementation sets the default tool if the button released was the right + * button. Subclasses can override this behaviour if needed. + * + * When a toolbar state is selected, method init is called to revert its state + * to the initial. Subclasses should extend that method as needed. Also, method + * cleanBeforeChange() is called before changing it to the new tool. Subclasses + * should extend that method as needed. + * + * @todo Handle, for example, left press, right press, left release, right + * release and other similar strange combinations? + */ +class ToolBarState: public QObject { + Q_OBJECT +public: + + /** + * Destroys this ToolBarState. + * Frees m_pMouseEvent. + */ + virtual ~ToolBarState(); + + /** + * Goes back to the initial state. + * Subclasses can extend, but not override, this method as needed. + */ + virtual void init(); + + /** + * Called when the current tool is changed to use another tool. + * Subclasses can extend, but not override, this method as needed. + * Default implementation does nothing. + */ + virtual void cleanBeforeChange(); + + /** + * Handler for mouse press events. + * Mouse tracking is enabled, any pop up menu removed, the position of the + * cursor set and paste state disabled. + * Then, the current association or widget are set (if any), and events are + * delivered to the specific methods, depending on where the cursor was + * pressed. + * + * @param ome The received event. + * @see setCurrentElement() + */ + virtual void mousePress(QMouseEvent *ome); + + /** + * Handler for mouse release events. + * Mouse tracking is disabled and the position of the cursor set. + * The events are delivered to the specific methods, depending on where the + * cursor was released, and the current association or widget cleaned. + * Finally, the current tool is changed if needed. + * + * @param ome The received event. + */ + virtual void mouseRelease(QMouseEvent* ome); + + /** + * Handler for mouse double click events. + * The current association or widget is set (if any), and events are + * delivered to the specific methods, depending on where the cursor was pressed. + * After delivering the events, the current association or widget is cleaned. + * + * @param ome The received event. + */ + virtual void mouseDoubleClick(QMouseEvent* ome); + + /** + * Handler for mouse double click events. + * Events are delivered to the specific methods, depending on where the cursor + * was pressed. It uses the current widget or association set in press event, + * if any. + * Then, the view is scrolled if needed (if the cursor is moved in any of the + * 30 pixels width area from left, top, right or bottom sides, and there is + * more diagram currently not being shown in that direction). + * This method is only called when mouse tracking is enabled and the mouse + * is moved. + * + * @param ome The received event. + */ + virtual void mouseMove(QMouseEvent* ome); + +public slots: + + /** + * An association was removed from the UMLView. + * If the association removed was the current association, the current + * association is set to 0. + * It can be extended in subclasses if needed. + */ + virtual void slotAssociationRemoved(AssociationWidget* association); + + /** + * A widget was removed from the UMLView. + * If the widget removed was the current widget, the current widget is set + * to 0. + * It can be extended in subclasses if needed. + */ + virtual void slotWidgetRemoved(UMLWidget* widget); + +protected: + + /** + * Creates a new ToolBarState. + * UMLView is set as parent of this QObject, and name is left empty. + * Protected to avoid classes other than derived to create objects of this + * class. + * + * @param umlView The UMLView to use. + */ + ToolBarState(UMLView *umlView); + + /** + * Sets the current association or widget. + * It sets the current element when a press event happened. The element will + * be used until the next release event. + * Default implementation first checks for associations, then message widgets + * and then any other widgets. + * It can be overridden in subclasses if needed. + */ + virtual void setCurrentElement(); + + /** + * Called when the press event happened on an association. + * Default implementation does nothing. + */ + virtual void mousePressAssociation(); + + /** + * Called when the press event happened on a widget. + * Default implementation does nothing. + */ + virtual void mousePressWidget(); + + /** + * Called when the press event happened on an empty space. + * Default implementation cleans the selection. + */ + virtual void mousePressEmpty(); + + /** + * Called when the release event happened on an association. + * Default implementation does nothing. + */ + virtual void mouseReleaseAssociation(); + + /** + * Called when the release event happened on a widget. + * Default implementation does nothing. + */ + virtual void mouseReleaseWidget(); + + /** + * Called when the release event happened on an empty space. + * Default implementation does nothing. + */ + virtual void mouseReleaseEmpty(); + + /** + * Called when the double click event happened on an association. + * Default implementation does nothing. + */ + virtual void mouseDoubleClickAssociation(); + + /** + * Called when the double click event happened on a widget. + * Default implementation does nothing. + */ + virtual void mouseDoubleClickWidget(); + + /** + * Called when the double click event happened on an empty space. + * Default implementation cleans the selection. + */ + virtual void mouseDoubleClickEmpty(); + + /** + * Called when the move event happened when an association is + * currently available. + * Default implementation does nothing. + */ + virtual void mouseMoveAssociation(); + + /** + * Called when the move event happened when a widget is + * currently available. + * Default implementation does nothing. + */ + virtual void mouseMoveWidget(); + + /** + * Called when the move event happened when no association nor + * widget are currently available. + * Default implementation does nothing. + */ + virtual void mouseMoveEmpty(); + + /** + * Changes the current tool to the default one if the right button was released. + * It can be overridden in subclasses if needed. + */ + virtual void changeTool(); + + /** + * Returns the widget currently in use. + * + * @return The widget currently in use. + */ + virtual UMLWidget* getCurrentWidget() { + return m_currentWidget; + } + + /** + * Sets the widget currently in use. + * This method is called in main press events handler just before calling + * the press event for widgets handler. + * Default implementation is set the specified widget, although this + * behaviour can be overridden in subclasses if needed. + * + * @param currentWidget The widget to be set. + */ + virtual void setCurrentWidget(UMLWidget* currentWidget) { + m_currentWidget = currentWidget; + } + + /** + * Returns the association currently in use. + * + * @return The association currently in use. + */ + virtual AssociationWidget* getCurrentAssociation() { + return m_currentAssociation; + } + + /** + * Sets the association currently in use. + * This method is called in main press events handler just before calling + * the press event for associations handler. + * Default implementation is set the specified association, although this + * behaviour can be overridden in subclasses if needed. + * + * @param currentAssociation The association to be set. + */ + virtual void setCurrentAssociation(AssociationWidget* currentAssociation) { + m_currentAssociation = currentAssociation; + } + + /** + * Sets m_pMouseEvent as the equivalent of the received event after transforming it + * using the inverse world matrix in the UMLView. + * This method is called at the beginning of the main event handler methods. + * + * @param ome The mouse event to transform. + * @param type The type of the event. + */ + void setMouseEvent(QMouseEvent* ome, const QEvent::Type &type); + + /** + * Returns the AssociationWidget at the specified position, or null if there is none. + * If there are more than one association at this point, it returns the first found. + * + * @param pos The position to get the association. + * @return The AssociationWidget at the specified position, or null if there is none. + * @todo Better handling for associations at the same point + */ + AssociationWidget* getAssociationAt(const QPoint& pos); + + /** + * Returns the MessageWidget at the specified position, or null if there is none. + * The message is only returned if it is visible. + * If there are more than one message at this point, it returns the first found. + * + * @param pos The position to get the message. + * @return The MessageWidget at the specified position, or null if there is none. + * @todo Better handling for messages at the same point + */ + MessageWidget* getMessageAt(const QPoint& pos); + + /** + * The UMLView. + */ + UMLView* m_pUMLView; + + /** + * The mouse event currently in use. + * This event is the equivalent of the received event after transforming it + * using the inverse world matrix in the UMLView. + */ + QMouseEvent* m_pMouseEvent; + +private: + + /** + * The widget currently in use, if any. + */ + UMLWidget* m_currentWidget; + + /** + * The association currently in use, if any. + */ + AssociationWidget* m_currentAssociation; + +}; + +#endif //TOOLBARSTATE_H diff --git a/umbrello/umbrello/toolbarstatearrow.cpp b/umbrello/umbrello/toolbarstatearrow.cpp new file mode 100644 index 00000000..d8205350 --- /dev/null +++ b/umbrello/umbrello/toolbarstatearrow.cpp @@ -0,0 +1,134 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "toolbarstatearrow.h" + +// app includes +#include "associationwidget.h" +#include "uml.h" +#include "umlview.h" +#include "umlwidget.h" + +ToolBarStateArrow::ToolBarStateArrow(UMLView *umlView): ToolBarState(umlView) { + m_selectionRect.setAutoDelete(true); + + init(); +} + +ToolBarStateArrow::~ToolBarStateArrow() { +} + +void ToolBarStateArrow::init() { + ToolBarState::init(); + + m_selectionRect.clear(); +} + +void ToolBarStateArrow::mousePressAssociation() { + getCurrentAssociation()->mousePressEvent(m_pMouseEvent); +} + +void ToolBarStateArrow::mousePressWidget() { + getCurrentWidget()->mousePressEvent(m_pMouseEvent); +} + +void ToolBarStateArrow::mousePressEmpty() { + if (m_pMouseEvent->button() != Qt::LeftButton) { + // Leave widgets selected upon RMB press on empty diagram area. + // The popup menu is activated upon RMB release. + return; + } + ToolBarState::mousePressEmpty(); + + // Starts the selection rectangle + if (m_selectionRect.count() == 0) { + m_startPosition = m_pMouseEvent->pos(); + + for (int i = 0; i < 4; i++) { + QCanvasLine* line = new QCanvasLine(m_pUMLView->canvas()); + line->setPoints(m_pMouseEvent->x(), m_pMouseEvent->y(), + m_pMouseEvent->x(), m_pMouseEvent->y()); + line->setPen(QPen(QColor("grey"), 0, Qt::DotLine)); + line->setVisible(true); + line->setZ(100); + m_selectionRect.append(line); + } + } +} + +void ToolBarStateArrow::mouseReleaseAssociation() { + getCurrentAssociation()->mouseReleaseEvent(m_pMouseEvent); +} + +void ToolBarStateArrow::mouseReleaseWidget() { + getCurrentWidget()->mouseReleaseEvent(m_pMouseEvent); +} + +void ToolBarStateArrow::mouseReleaseEmpty() { + if (m_selectionRect.count() == 4) { + m_selectionRect.clear(); + } else if (m_pMouseEvent->button() == Qt::RightButton) { + m_pUMLView->setMenu(); + } +} + +void ToolBarStateArrow::mouseDoubleClickAssociation() { + getCurrentAssociation()->mouseDoubleClickEvent(m_pMouseEvent); +} + +void ToolBarStateArrow::mouseDoubleClickWidget() { + getCurrentWidget()->mouseDoubleClickEvent(m_pMouseEvent); +} + +void ToolBarStateArrow::mouseMoveAssociation() { + getCurrentAssociation()->mouseMoveEvent(m_pMouseEvent); +} + +void ToolBarStateArrow::mouseMoveWidget() { + getCurrentWidget()->mouseMoveEvent(m_pMouseEvent); +} + +void ToolBarStateArrow::mouseMoveEmpty() { + if (m_selectionRect.count() == 4) { + QCanvasLine* line = m_selectionRect.at(0); + line->setPoints(m_startPosition.x(), m_startPosition.y(), + m_pMouseEvent->x(), m_startPosition.y()); + + line = m_selectionRect.at(1); + line->setPoints(m_pMouseEvent->x(), m_startPosition.y(), + m_pMouseEvent->x(), m_pMouseEvent->y()); + + line = m_selectionRect.at(2); + line->setPoints(m_pMouseEvent->x(), m_pMouseEvent->y(), + m_startPosition.x(), m_pMouseEvent->y()); + + line = m_selectionRect.at(3); + line->setPoints(m_startPosition.x(), m_pMouseEvent->y(), + m_startPosition.x(), m_startPosition.y()); + + m_pUMLView->selectWidgets(m_startPosition.x(), m_startPosition.y(), + m_pMouseEvent->x(), m_pMouseEvent->y()); + } +} + +void ToolBarStateArrow::changeTool() { +} + +void ToolBarStateArrow::setCurrentWidget(UMLWidget* currentWidget) { + if (currentWidget != 0 && getCurrentWidget() != 0) { + return; + } + + ToolBarState::setCurrentWidget(currentWidget); +} + +#include "toolbarstatearrow.moc" diff --git a/umbrello/umbrello/toolbarstatearrow.h b/umbrello/umbrello/toolbarstatearrow.h new file mode 100644 index 00000000..a2fab59f --- /dev/null +++ b/umbrello/umbrello/toolbarstatearrow.h @@ -0,0 +1,158 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef TOOLBARSTATEARROW_H +#define TOOLBARSTATEARROW_H + + +#include "toolbarstate.h" + +#include "worktoolbar.h" + +class QMouseEvent; +class UMLView; + +class QCanvasLine; + +/** + * Arrow tool for select, move and resize widgets and associations. + * Arrow tool delegates the event handling in the widgets and associations. When + * no widget nor association is being used,the arrow tool acts as a selecting + * tool that selects all the elements in the rectangle created when dragging the + * mouse. + * + * This is the default tool. + */ +class ToolBarStateArrow : public ToolBarState { + Q_OBJECT +public: + + /** + * Creates a new ToolBarStateArrow. + * + * @param umlView The UMLView to use. + */ + ToolBarStateArrow(UMLView *umlView); + + /** + * Destroys this ToolBarStateArrow. + */ + virtual ~ToolBarStateArrow(); + + /** + * Goes back to the inital state. + */ + virtual void init(); + +protected: + + /** + * Called when the press event happened on an association. + * Delivers the event to the association. + */ + virtual void mousePressAssociation(); + + /** + * Called when the press event happened on a widget. + * Delivers the event to the widget. + */ + virtual void mousePressWidget(); + + /** + * Called when the press event happened on an empty space. + * Calls base method and, if left button was pressed, prepares the selection + * rectangle. + */ + virtual void mousePressEmpty(); + + /** + * Called when the release event happened on an association. + * Delivers the event to the association. + */ + virtual void mouseReleaseAssociation(); + + /** + * Called when the release event happened on a widget. + * Delivers the event to the widget. + */ + virtual void mouseReleaseWidget(); + + /** + * Called when the release event happened on an empty space. + * If selection rectangle is active, it is cleared. Else, if the right + * button was released, it shows the pop up menu for the diagram. + */ + virtual void mouseReleaseEmpty(); + + /** + * Called when the double click event happened on an association. + * Delivers the event to the association. + */ + virtual void mouseDoubleClickAssociation(); + + /** + * Called when the double click event happened on a widget. + * Delivers the event to the widget. + */ + virtual void mouseDoubleClickWidget(); + + /** + * Called when the move event happened when an association is + * currently available. + * Delivers the event to the association. + */ + virtual void mouseMoveAssociation(); + + /** + * Called when the move event happened when a widget is + * currently available. + * Delivers the event to the widget. + */ + virtual void mouseMoveWidget(); + + /** + * Called when the move event happened when no association nor + * widget are currently available. + * Updates the selection rectangle to the new position and selectes all the + * widgets in the rectangle. + * + * @todo Fix selection + */ + virtual void mouseMoveEmpty(); + + /** + * Sets the widget currently in use. + * It ensures that the widget is only set if there is no other widget set + * already. + * It avoids things like moving a big widget over a little one, clicking + * right button to cancel the movement and the little widget getting the + * event, thus not cancelling the movement in the big widget. + */ + virtual void setCurrentWidget(UMLWidget* currentWidget); + + /** + * Overriden from base class to do nothing, as arrow is the default tool. + */ + virtual void changeTool(); + + /** + * The selection rectangle that contains the four lines of its borders. + */ + QPtrList m_selectionRect; + + /** + * The start position of the selection rectangle. + */ + QPoint m_startPosition; + +}; + +#endif //TOOLBARSTATEARROW_H diff --git a/umbrello/umbrello/toolbarstateassociation.cpp b/umbrello/umbrello/toolbarstateassociation.cpp new file mode 100644 index 00000000..d36647dd --- /dev/null +++ b/umbrello/umbrello/toolbarstateassociation.cpp @@ -0,0 +1,232 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "toolbarstateassociation.h" + +// kde includes +#include +#include +#include + +// app includes +#include "assocrules.h" +#include "association.h" +#include "associationwidget.h" +#include "classifierwidget.h" +#include "folder.h" +#include "model_utils.h" +#include "uml.h" +#include "umlobject.h" +#include "umlview.h" +#include "umllistview.h" +#include "umldoc.h" +#include "umlwidget.h" + +using namespace Uml; + +ToolBarStateAssociation::ToolBarStateAssociation(UMLView *umlView) : ToolBarStatePool(umlView) { + m_firstWidget = 0; + m_associationLine = 0; +} + +ToolBarStateAssociation::~ToolBarStateAssociation() { + delete m_associationLine; +} + +void ToolBarStateAssociation::init() { + ToolBarStatePool::init(); + + cleanAssociation(); +} + +void ToolBarStateAssociation::cleanBeforeChange() { + ToolBarStatePool::cleanBeforeChange(); + + cleanAssociation(); +} + +void ToolBarStateAssociation::mouseMove(QMouseEvent* ome) { + ToolBarStatePool::mouseMove(ome); + + if (m_associationLine) { + QPoint sp = m_associationLine->startPoint(); + m_associationLine->setPoints(sp.x(), sp.y(), m_pMouseEvent->x(), m_pMouseEvent->y()); + } +} + +void ToolBarStateAssociation::slotWidgetRemoved(UMLWidget* widget) { + ToolBarState::slotWidgetRemoved(widget); + + if (widget == m_firstWidget) { + cleanAssociation(); + } +} + +void ToolBarStateAssociation::mouseReleaseAssociation() { + if (m_pMouseEvent->button() != Qt::LeftButton || + !m_firstWidget || m_firstWidget->getBaseType() != Uml::wt_Class) { + cleanAssociation(); + return; + } + + getCurrentAssociation()->createAssocClassLine( + static_cast(m_firstWidget), + getCurrentAssociation()->getLinePath()->onLinePath(m_pMouseEvent->pos())); + cleanAssociation(); +} + +void ToolBarStateAssociation::mouseReleaseWidget() { + if (m_pMouseEvent->button() != Qt::LeftButton) { + cleanAssociation(); + return; + } + + // TODO In old code in ToolBarState there was a TODO that said: Should not + //be called by a Sequence message Association. Here's the check for that, + //although I don't know why it is needed, but it seems that it's not needed, + //as the old code worked fine without it... + if (getAssociationType() == at_Seq_Message) { + return; + } + + if (!m_firstWidget) { + setFirstWidget(); + } else { + setSecondWidget(); + } +} + +void ToolBarStateAssociation::mouseReleaseEmpty() { + cleanAssociation(); +} + +void ToolBarStateAssociation::setFirstWidget() { + UMLWidget* widget = getCurrentWidget(); + Association_Type type = getAssociationType(); + + if (!AssocRules::allowAssociation(type, widget)) { + //TODO improve error feedback: tell the user what are the valid type of associations for + //that widget + KMessageBox::error(0, i18n("Incorrect use of associations."), i18n("Association Error")); + return; + } + //set up position + QPoint pos; + pos.setX(widget->getX() + (widget->getWidth() / 2)); + pos.setY(widget->getY() + (widget->getHeight() / 2)); + //TODO why is this needed? + m_pUMLView->setPos(pos); + + m_firstWidget = widget; + + m_associationLine = new QCanvasLine(m_pUMLView->canvas()); + m_associationLine->setPoints(pos.x(), pos.y(), pos.x(), pos.y()); + m_associationLine->setPen(QPen(m_pUMLView->getLineColor(), m_pUMLView->getLineWidth(), Qt::DashLine)); + + m_associationLine->setVisible(true); + + m_pUMLView->viewport()->setMouseTracking(true); +} + +void ToolBarStateAssociation::setSecondWidget() { + Association_Type type = getAssociationType(); + UMLWidget* widgetA = m_firstWidget; + UMLWidget* widgetB = getCurrentWidget(); + Widget_Type at = widgetA->getBaseType(); + bool valid = true; + if (type == at_Generalization) { + type = AssocRules::isGeneralisationOrRealisation(widgetA, widgetB); + } + if (widgetA == widgetB) { + valid = AssocRules::allowSelf(type, at); + if (valid && type == at_Association) { + type = at_Association_Self; + } + } else { + valid = AssocRules::allowAssociation(type, widgetA, widgetB); + } + if (valid) { + AssociationWidget *temp = new AssociationWidget(m_pUMLView, widgetA, type, widgetB); + addAssociationInViewAndDoc(temp); + if (type == at_Containment) { + UMLListView *lv = UMLApp::app()->getListView(); + UMLObject *newContainer = widgetA->getUMLObject(); + UMLObject *objToBeMoved = widgetB->getUMLObject(); + if (newContainer && objToBeMoved) { + UMLListViewItem *newLVParent = lv->findUMLObject(newContainer); + lv->moveObject(objToBeMoved->getID(), + Model_Utils::convert_OT_LVT(objToBeMoved), + newLVParent); + } + } + UMLApp::app()->getDocument()->setModified(); + } else { + //TODO improve error feedback: tell the user what are the valid type of associations for + //the second widget using the first widget + KMessageBox::error(0, i18n("Incorrect use of associations."), i18n("Association Error")); + } + + cleanAssociation(); +} + +Association_Type ToolBarStateAssociation::getAssociationType() { + Association_Type at; + + switch(getButton()) { + case WorkToolBar::tbb_Anchor: at = at_Anchor; break; + case WorkToolBar::tbb_Association: at = at_Association; break; + case WorkToolBar::tbb_UniAssociation: at = at_UniAssociation; break; + case WorkToolBar::tbb_Generalization: at = at_Generalization; break; + case WorkToolBar::tbb_Composition: at = at_Composition; break; + case WorkToolBar::tbb_Aggregation: at = at_Aggregation; break; + case WorkToolBar::tbb_Relationship: at = at_Relationship; break; + case WorkToolBar::tbb_Dependency: at = at_Dependency; break; + case WorkToolBar::tbb_Containment: at = at_Containment; break; + case WorkToolBar::tbb_Seq_Message_Synchronous: + case WorkToolBar::tbb_Seq_Message_Asynchronous: at = at_Seq_Message; break; + case WorkToolBar::tbb_Coll_Message: at = at_Coll_Message; break; + case WorkToolBar::tbb_State_Transition: at = at_State; break; + case WorkToolBar::tbb_Activity_Transition: at = at_Activity; break; + + default: at = at_Unknown; break; + } + + return at; +} + +void ToolBarStateAssociation::addAssociationInViewAndDoc(AssociationWidget* a) { + // append in view + if (m_pUMLView->addAssociation(a, false)) { + // if view went ok, then append in document + UMLAssociation *umla = a->getAssociation(); + if (umla == NULL) { + // association without model representation in UMLDoc + return; + } + Uml::Model_Type m = Model_Utils::convert_DT_MT(m_pUMLView->getType()); + UMLDoc *umldoc = UMLApp::app()->getDocument(); + umla->setUMLPackage(umldoc->getRootFolder(m)); + UMLApp::app()->getDocument()->addAssociation(umla); + } else { + kError() << "cannot addAssocInViewAndDoc(), deleting" << endl; + delete a; + } +} + +void ToolBarStateAssociation::cleanAssociation() { + m_firstWidget = 0; + + delete m_associationLine; + m_associationLine = 0; +} + +#include "toolbarstateassociation.moc" diff --git a/umbrello/umbrello/toolbarstateassociation.h b/umbrello/umbrello/toolbarstateassociation.h new file mode 100644 index 00000000..1d6c5174 --- /dev/null +++ b/umbrello/umbrello/toolbarstateassociation.h @@ -0,0 +1,168 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef TOOLBARSTATEASSOCIATION_H +#define TOOLBARSTATEASSOCIATION_H + +#include "toolbarstatepool.h" + +class QCanvasLine; + +/** + * Association tool to create associations between widgets. + * With association tool, two widgets are selected clicking with left button on + * them and an association of the needed type (depending on the association + * button selected) is created between the widgets. When the first widget is + * selected, a temporal visual association that follows the cursor movement is + * created until the second widget is selected or the association cancelled. + * + * Also, association tool can create association class: a classifier widget + * which is linked to an association. To do this, the classifier must be + * selected first and then the association must be selected. The association + * can't be selected first. + * + * An association can be cancelled using right button, which also returns to + * default tool, or with middle button, which only cancels the association + * without changing the tool being used. + * + * @todo refactor with common code in ToolBarStateMessages? + */ +class ToolBarStateAssociation : public ToolBarStatePool { + Q_OBJECT +public: + + /** + * Creates a new ToolBarStateAssociation. + * + * @param umlView The UMLView to use. + */ + ToolBarStateAssociation(UMLView *umlView); + + /** + * Destroys this ToolBarStateAssociation. + * Deletes the association line. + */ + virtual ~ToolBarStateAssociation(); + + /** + * Goes back to the initial state. + */ + virtual void init(); + + /** + * Called when the current tool is changed to use another tool. + * Executes base method and cleans the association. + */ + virtual void cleanBeforeChange(); + + /** + * Called when a mouse event happened. + * It executes the base method and then updates the position of the + * association line, if any. + */ + virtual void mouseMove(QMouseEvent* ome); + +public slots: + + /** + * A widget was removed from the UMLView. + * If the widget removed was the current widget, the current widget is set + * to 0. + * Also, if it was the first widget, the association is cleaned. + */ + virtual void slotWidgetRemoved(UMLWidget* widget); + +protected: + + /** + * Called when the release event happened on an association. + * If the button pressed isn't left button, the association being created is + * cleaned. If it is left button, and the first widget is set and is a + * classifier widget, it creates an association class. Otherwise, the + * association being created is cleaned. + */ + virtual void mouseReleaseAssociation(); + + /** + * Called when the release event happened on a widget. + * If the button pressed isn't left button, the association is cleaned. If + * it is left button, sets the first widget or the second, depending on + * whether the first widget is already set or not. + */ + virtual void mouseReleaseWidget(); + + /** + * Called when the release event happened on an empty space. + * Cleans the association. + */ + virtual void mouseReleaseEmpty(); + +private: + + /** + * Sets the first widget in the association using the current widget. + * If the widget can't be associated using the current type of association, + * an error is shown and the widget isn't set. + * Otherwise, the temporal visual association is created and the mouse + * tracking is enabled, so move events will be delivered. + */ + void setFirstWidget(); + + /** + * Sets the second widget in the association using the current widget and + * creates the association. + * If the association between the two widgets using the current type of + * association, an error is shown and the association cancelled. + * Otherwise, the association is created and added to the view, and the tool + * is changed to the default tool. + * + * @todo Why change to the default tool? Shouldn't it better to stay on + * association and let the user change with a right click? The tool to + * create widgets doesn't change to default after creating a widget + */ + void setSecondWidget(); + + /** + * Returns the association type of this tool. + * + * @return The association type of this tool. + */ + Uml::Association_Type getAssociationType(); + + /** + * Adds an AssociationWidget to the association list and creates the + * corresponding UMLAssociation in the current UMLDoc. + * If the association can't be added, is deleted. + * + * @param association The AssociationWidget to add. + */ + void addAssociationInViewAndDoc(AssociationWidget* association); + + /** + * Cleans the first widget and the temporal association line, if any. + * Both are set to null, and the association line is also deleted. + */ + void cleanAssociation(); + + /** + * The first widget in the association. + */ + UMLWidget* m_firstWidget; + + /** + * The association line shown while the first widget is selected and the + * second one wasn't selected yet. + */ + QCanvasLine* m_associationLine; + +}; + +#endif //TOOLBARSTATEASSOCIATION_H diff --git a/umbrello/umbrello/toolbarstatefactory.cpp b/umbrello/umbrello/toolbarstatefactory.cpp new file mode 100644 index 00000000..66cf89bb --- /dev/null +++ b/umbrello/umbrello/toolbarstatefactory.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ +#include "toolbarstatefactory.h" + +#include "toolbarstate.h" +#include "toolbarstatepool.h" +#include "toolbarstateother.h" +#include "toolbarstatearrow.h" +#include "toolbarstatemessages.h" +#include "toolbarstateassociation.h" + +#include "umlview.h" + +ToolBarStateFactory::ToolBarStateFactory(UMLView *umlView) +{ + m_pUMLView = umlView; + + for (int i = 0; i < NR_OF_TOOLBAR_STATES; i++) + { + states[i] = NULL; + } +} + +ToolBarStateFactory::~ToolBarStateFactory() +{ + for (int i = 0; i < NR_OF_TOOLBAR_STATES; i++) + { + if (states[i] != NULL) delete states[i]; + } +} + + +ToolBarState* ToolBarStateFactory::getState(const WorkToolBar::ToolBar_Buttons &toolbarButton) +{ + int key = getKey(toolbarButton); + + if (states[key] == NULL) + { + switch (key) + { + // When you add a new state, make sure you also increase the + // NR_OF_TOOLBAR_STATES + case 0: states[0] = new ToolBarStateOther(m_pUMLView); break; + case 1: states[1] = new ToolBarStateAssociation(m_pUMLView); break; + case 2: states[2] = new ToolBarStateMessages(m_pUMLView); break; + + // This case has no pool. + case 3: states[3] = new ToolBarStateArrow(m_pUMLView); break; + } + } + + // Make explicit the selected button. This is only necessary for states with a pool. + if (key <= 2) ((ToolBarStatePool *) states[key])->setButton(toolbarButton); + + return states[key]; +} + + +int ToolBarStateFactory::getKey(const WorkToolBar::ToolBar_Buttons &toolbarButton) const +{ + switch (toolbarButton) + { + // Associations + case WorkToolBar::tbb_Dependency: return 1; + case WorkToolBar::tbb_Aggregation: return 1; + case WorkToolBar::tbb_Relationship: return 1; + case WorkToolBar::tbb_Generalization: return 1; + case WorkToolBar::tbb_Association: return 1; + case WorkToolBar::tbb_UniAssociation: return 1; + case WorkToolBar::tbb_Composition: return 1; + case WorkToolBar::tbb_Containment: return 1; + case WorkToolBar::tbb_Anchor: return 1; + case WorkToolBar::tbb_Coll_Message: return 1; + case WorkToolBar::tbb_State_Transition: return 1; + case WorkToolBar::tbb_Activity_Transition: return 1; + + // Messages + case WorkToolBar::tbb_Seq_Message_Synchronous: return 2; + case WorkToolBar::tbb_Seq_Message_Asynchronous: return 2; + + // Arrow pointer + case WorkToolBar::tbb_Arrow: return 3; + + // Other. + default: return 0; + } + +} diff --git a/umbrello/umbrello/toolbarstatefactory.h b/umbrello/umbrello/toolbarstatefactory.h new file mode 100644 index 00000000..b5c1ee4c --- /dev/null +++ b/umbrello/umbrello/toolbarstatefactory.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef TOOLBARSTATEFACTORY_H +#define TOOLBARSTATEFACTORY_H + +#include "toolbarstate.h" + +#include "worktoolbar.h" + +#define NR_OF_TOOLBAR_STATES 4 + +class UMLView; + + +/** + * The ToolBarStateFactory keeps track of all the toolbar states. For the first + * request, the factory creates a new state object. The next requests to this + * object, this factory will return the existing object. + * + * States that inherit from the ToolBarStatePool share multiple toolbar states. + * Therefore the setButton function is called. Internally the shared state object + * determines the exact behavior by itself. + */ +class ToolBarStateFactory +{ +public: + // constructor. + ToolBarStateFactory(UMLView* umlView); + + // Destructor + virtual ~ToolBarStateFactory(); + + ToolBarState* getState(const WorkToolBar::ToolBar_Buttons &toolbarButton); + +protected: + int getKey(const WorkToolBar::ToolBar_Buttons &toolbarButton) const; + +protected: + ToolBarState* states[NR_OF_TOOLBAR_STATES]; + + UMLView* m_pUMLView; +}; + +#endif //TOOLBARSTATEFACTORY_H diff --git a/umbrello/umbrello/toolbarstatemessages.cpp b/umbrello/umbrello/toolbarstatemessages.cpp new file mode 100644 index 00000000..36ffb005 --- /dev/null +++ b/umbrello/umbrello/toolbarstatemessages.cpp @@ -0,0 +1,169 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "toolbarstatemessages.h" + +// kde includes +#include + +// local includes +#include "floatingtextwidget.h" +#include "messagewidget.h" +#include "objectwidget.h" +#include "uml.h" +#include "umldoc.h" +#include "umlview.h" + +ToolBarStateMessages::ToolBarStateMessages(UMLView *umlView) : ToolBarStatePool(umlView) { + m_firstObject = 0; + m_messageLine = 0; +} + +ToolBarStateMessages::~ToolBarStateMessages() { + delete m_messageLine; +} + +void ToolBarStateMessages::init() { + ToolBarStatePool::init(); + + cleanMessage(); +} + +void ToolBarStateMessages::cleanBeforeChange() { + ToolBarStatePool::cleanBeforeChange(); + + cleanMessage(); +} + +void ToolBarStateMessages::mouseMove(QMouseEvent* ome) { + ToolBarStatePool::mouseMove(ome); + + if (m_messageLine) { + QPoint sp = m_messageLine->startPoint(); + m_messageLine->setPoints(sp.x(), sp.y(), m_pMouseEvent->x(), m_pMouseEvent->y()); + } +} + +void ToolBarStateMessages::slotWidgetRemoved(UMLWidget* widget) { + ToolBarState::slotWidgetRemoved(widget); + + if (widget == m_firstObject) { + cleanMessage(); + } +} + +void ToolBarStateMessages::setCurrentElement() { + m_isObjectWidgetLine = false; + + ObjectWidget* objectWidgetLine = m_pUMLView->onWidgetLine(m_pMouseEvent->pos()); + if (objectWidgetLine) { + setCurrentWidget(objectWidgetLine); + m_isObjectWidgetLine = true; + return; + } + + //commit 515177 fixed a setting creation messages only working properly at 100% zoom + //However, the applied patch doesn't seem to be necessary no more, so it was removed + //The widgets weren't got from UMLView, but from a method in this class similarto the + //one in UMLView but containing special code to handle the zoom + UMLWidget *widget = m_pUMLView->getWidgetAt(m_pMouseEvent->pos()); + if (widget) { + setCurrentWidget(widget); + return; + } +} + +void ToolBarStateMessages::mouseReleaseWidget() { + //TODO When an association between UMLObjects of invalid types is made, an error message + //is shown. Shouldn't also a message be used here? + if (m_pMouseEvent->button() != Qt::LeftButton || + getCurrentWidget()->getBaseType() != Uml::wt_Object) { + cleanMessage(); + return; + } + + if (!m_isObjectWidgetLine && !m_firstObject) { + return; + } + + if (!m_isObjectWidgetLine) { + setSecondWidget(static_cast(getCurrentWidget()), CreationMessage); + return; + } + + if (!m_firstObject) { + setFirstWidget(static_cast(getCurrentWidget())); + } else { + setSecondWidget(static_cast(getCurrentWidget()), NormalMessage); + } +} + +void ToolBarStateMessages::mouseReleaseEmpty() { + cleanMessage(); +} + +void ToolBarStateMessages::setFirstWidget(ObjectWidget* firstObject) { + m_firstObject = firstObject; + + m_messageLine = new QCanvasLine(m_pUMLView->canvas()); + m_messageLine->setPoints(m_pMouseEvent->x(), m_pMouseEvent->y(), m_pMouseEvent->x(), m_pMouseEvent->y()); + m_messageLine->setPen(QPen(m_pUMLView->getLineColor(), m_pUMLView->getLineWidth(), Qt::DashLine)); + + m_messageLine->setVisible(true); + + m_pUMLView->viewport()->setMouseTracking(true); +} + +void ToolBarStateMessages::setSecondWidget(ObjectWidget* secondObject, MessageType messageType) { + Uml::Sequence_Message_Type msgType = getMessageType(); + + //TODO shouldn't start position in the first widget be used also for normal messages + //and not only for creation? + int y = m_pMouseEvent->y(); + if (messageType == CreationMessage) { + msgType = Uml::sequence_message_creation; + y = m_messageLine->startPoint().y(); + } + + MessageWidget* message = new MessageWidget(m_pUMLView, m_firstObject, + secondObject, y, msgType); + + cleanMessage(); + + m_pUMLView->getMessageList().append(message); + + FloatingTextWidget *ft = message->getFloatingTextWidget(); + //TODO cancel doesn't cancel the creation of the message, only cancels setting an operation. + //Shouldn't it cancel also the whole creation? + ft->showOpDlg(); + message->setTextPosition(); + m_pUMLView->getWidgetList().append(ft); + + UMLApp::app()->getDocument()->setModified(); +} + +Uml::Sequence_Message_Type ToolBarStateMessages::getMessageType() { + if (getButton() == WorkToolBar::tbb_Seq_Message_Synchronous) { + return Uml::sequence_message_synchronous; + } + + return Uml::sequence_message_asynchronous; +} + +void ToolBarStateMessages::cleanMessage() { + m_firstObject = 0; + + delete m_messageLine; + m_messageLine = 0; +} + +#include "toolbarstatemessages.moc" diff --git a/umbrello/umbrello/toolbarstatemessages.h b/umbrello/umbrello/toolbarstatemessages.h new file mode 100644 index 00000000..f6b2cb79 --- /dev/null +++ b/umbrello/umbrello/toolbarstatemessages.h @@ -0,0 +1,187 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef TOOLBARSTATEMESSAGES_H +#define TOOLBARSTATEMESSAGES_H + +#include "toolbarstatepool.h" +#include + +class QCanvasLine; +class ObjectWidget; + +/** + * Messages tool to create messages between objects in sequence diagrams. + * With messages tool, two objects are selected clicking with left button on + * them and a message of the needed type (depending on the message button + * selected) is created between the objects. When the first object is selected, + * a temporal visual message that follows the cursor movement is created until + * the second object is selected or the message cancelled. + * + * A message can be cancelled using right button, which also returns to default + * tool, or with middle button, which only cancels the message without changing + * the tool being used. + * + * The messages to create can be normal messages or creation messages. Normal + * messages are created clicking on the line of the two objects. Creation + * messages are created clicking in the line of the first object, and on the + * second object itself (not in its line). + * + * Associations aren't taken into account, and are treated as empty spaces. + * Moreover, widgets other than objects aren't neither taken into account. + * + * @todo refactor with common code in ToolBarStateAssociation? + * @todo sequence message lines should be handled by object widgets. Right now, + * they aren't taken into account in testOnWidget and an explicit check is + * needed. However, if onWidget in object widgets is changed to also check for + * the line, a way to make them prioritaries over other widgets in testOnWidget + * will be needed. For example, when creating a message clicking on an already + * created message,the message line must be got instead of the message, even if + * the message is smaller than the line. + */ +class ToolBarStateMessages : public ToolBarStatePool { + Q_OBJECT +public: + + /** + * Creates a new ToolBarStateMessages. + * + * @param umlView The UMLView to use. + */ + ToolBarStateMessages(UMLView *umlView); + + /** + * Destroys this ToolBarStateMessages. + */ + virtual ~ToolBarStateMessages(); + + /** + * Goes back to the initial state. + */ + virtual void init(); + + /** + * Called when the current tool is changed to use another tool. + * Executes base method and cleans the message. + */ + virtual void cleanBeforeChange(); + + /** + * Called when a mouse event happened. + * It executes the base method and then updates the position of the + * message line, if any. + */ + virtual void mouseMove(QMouseEvent* ome); + +public slots: + + /** + * A widget was removed from the UMLView. + * If the widget removed was the current widget, the current widget is set + * to 0. + * Also, if it was the first object, the message is cleaned. + */ + virtual void slotWidgetRemoved(UMLWidget* widget); + +protected: + + /** + * Selects only widgets, but no associations. + * Overrides base class method. + * If the press event happened on the line of an object, the object is set + * as current widget. If the press event happened on a widget, the widget is + * set as current widget. + */ + virtual void setCurrentElement(); + + /** + * Called when the release event happened on a widget. + * If the button pressed isn't left button or the widget isn't an object + * widget, the message is cleaned. + * If the release event didn't happen on the line of an object and the first + * object wasn't selected, nothing is done. If the first object was already + * selected, a creation message is made. + * If the event happened on the line of an object, the first object or the + * second are set, depending on whether the first object was already set or + * not. + */ + virtual void mouseReleaseWidget(); + + /** + * Called when the release event happened on an empty space. + * Cleans the message. + * Empty spaces are not only actual empty spaces, but also associations. + */ + virtual void mouseReleaseEmpty(); + +protected: + + /** + * The type of the message to create. + */ + enum MessageType { + NormalMessage, + CreationMessage + }; + + /** + * Sets the first object of the message using the specified object. + * The temporal visual message is created and mouse tracking enabled, so + * mouse events will be delivered. + * + * @param firstObject The first object of the message. + */ + void setFirstWidget(ObjectWidget* firstObject); + + /** + * Sets the second object of the message using the specified widget and + * creates the message. + * The association is created and added to the view. The dialog to select + * the operation of the message is shown. + * + * @param secondObject The second object of the message. + * @param messageType The type of the message to create. + */ + void setSecondWidget(ObjectWidget* secondObject, MessageType messageType); + + /** + * Returns the message type of this tool. + * + * @return The message type of this tool. + */ + Uml::Sequence_Message_Type getMessageType(); + + /** + * Cleans the first widget and the temporal message line, if any. + * Both are set to null, and the message line is also deleted. + */ + void cleanMessage(); + + /** + * The first object in the message. + */ + ObjectWidget* m_firstObject; + + /** + * The message line shown while the first widget is selected and the + * second one wasn't selected yet. + */ + QCanvasLine* m_messageLine; + + /** + * If there is a current widget, it is true if the press event happened on + * the line of an object, or false if it happened on a normal UMLWidget. + */ + bool m_isObjectWidgetLine; + +}; + +#endif //TOOLBARSTATEMESSAGES_H diff --git a/umbrello/umbrello/toolbarstateother.cpp b/umbrello/umbrello/toolbarstateother.cpp new file mode 100644 index 00000000..f6161dbd --- /dev/null +++ b/umbrello/umbrello/toolbarstateother.cpp @@ -0,0 +1,163 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "toolbarstateother.h" + +// kde includes +#include +#include +#include + +// app includes +#include "activitywidget.h" +#include "boxwidget.h" +#include "dialog_utils.h" +#include "floatingtextwidget.h" +#include "forkjoinwidget.h" +#include "notewidget.h" +#include "object_factory.h" +#include "statewidget.h" +#include "uml.h" +#include "umlview.h" +#include "umldoc.h" + +using namespace Uml; + +ToolBarStateOther::ToolBarStateOther(UMLView *umlView) : ToolBarStatePool(umlView) { +} + +ToolBarStateOther::~ToolBarStateOther() { +} + +void ToolBarStateOther::setCurrentElement() { +} + +void ToolBarStateOther::mouseReleaseEmpty() { + if (m_pMouseEvent->button() == Qt::LeftButton) { + if (!newWidget()) { + // Is UMLObject? + + m_pUMLView->setCreateObject(true); + Object_Factory::createUMLObject(getObjectType()); + } + + m_pUMLView->resizeCanvasToItems(); + } +} + +Uml::Object_Type ToolBarStateOther::getObjectType() { + Object_Type ot; + + switch(getButton()) { + case WorkToolBar::tbb_Actor: ot = ot_Actor; break; + case WorkToolBar::tbb_UseCase: ot = ot_UseCase; break; + case WorkToolBar::tbb_Class: ot = ot_Class; break; + case WorkToolBar::tbb_Object: ot = ot_Class; break; // Object is a class. + case WorkToolBar::tbb_Package: ot = ot_Package; break; + case WorkToolBar::tbb_Component: ot = ot_Component; break; + case WorkToolBar::tbb_Node: ot = ot_Node; break; + case WorkToolBar::tbb_Artifact: ot = ot_Artifact; break; + case WorkToolBar::tbb_Interface: ot = ot_Interface; break; + case WorkToolBar::tbb_Enum: ot = ot_Enum; break; + case WorkToolBar::tbb_Entity: ot = ot_Entity; break; + case WorkToolBar::tbb_Datatype: ot = ot_Datatype; break; + + default: ot = ot_UMLObject; break; + } + + return ot; +} + +// TODO: The name is a bit confusing. +bool ToolBarStateOther::newWidget() { + UMLWidget* umlWidget = NULL; + + switch (getButton()) { + case WorkToolBar::tbb_Note: + umlWidget = new NoteWidget(m_pUMLView); + break; + + case WorkToolBar::tbb_Box: + umlWidget = new BoxWidget(m_pUMLView); + break; + + case WorkToolBar::tbb_Text: + umlWidget = new FloatingTextWidget(m_pUMLView, tr_Floating, ""); + break; + + // Activity buttons + case WorkToolBar::tbb_Initial_Activity: + umlWidget = new ActivityWidget(m_pUMLView, ActivityWidget::Initial); + break; + + case WorkToolBar::tbb_Activity: + umlWidget = new ActivityWidget(m_pUMLView, ActivityWidget::Normal); + break; + + case WorkToolBar::tbb_End_Activity: + umlWidget = new ActivityWidget(m_pUMLView, ActivityWidget::End); + break; + + case WorkToolBar::tbb_Branch: + umlWidget = new ActivityWidget(m_pUMLView, ActivityWidget::Branch); + break; + + case WorkToolBar::tbb_Fork: + case WorkToolBar::tbb_StateFork: + umlWidget = new ForkJoinWidget(m_pUMLView); + break; + + case WorkToolBar::tbb_Initial_State: + umlWidget = new StateWidget(m_pUMLView, StateWidget::Initial); + break; + + case WorkToolBar::tbb_State: + umlWidget = new StateWidget(m_pUMLView, StateWidget::Normal); + break; + + case WorkToolBar::tbb_End_State: + umlWidget = new StateWidget(m_pUMLView, StateWidget::End); + break; + + default: + break; + } + + // Return false if we didn't find a suitable widget. + if (umlWidget == NULL) { + return false; + } + + // Special treatment for some buttons + if (getButton() == WorkToolBar::tbb_Activity) { + Dialog_Utils::askNameForWidget( + umlWidget, i18n("Enter Activity Name"), + i18n("Enter the name of the new activity:"), i18n("new activity")); + } else if (getButton() == WorkToolBar::tbb_State) { + Dialog_Utils::askNameForWidget( + umlWidget, i18n("Enter State Name"), + i18n("Enter the name of the new state:"), i18n("new state")); + } else if (getButton() == WorkToolBar::tbb_Text) { + // It is pretty invisible otherwise. + FloatingTextWidget* ft = (FloatingTextWidget*) umlWidget; + ft->changeTextDlg(); + } + + // Create the widget. Some setup functions can remove the widget. + if (umlWidget != NULL) { + m_pUMLView->setupNewWidget(umlWidget); + } + + return true; +} + +#include "toolbarstateother.moc" diff --git a/umbrello/umbrello/toolbarstateother.h b/umbrello/umbrello/toolbarstateother.h new file mode 100644 index 00000000..d5c6f412 --- /dev/null +++ b/umbrello/umbrello/toolbarstateother.h @@ -0,0 +1,83 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef TOOLBARSTATEOTHER_H +#define TOOLBARSTATEOTHER_H + +#include "toolbarstatepool.h" + +/** + * Other tool creates almost all the objects (except associations and messages). + * Objects are created when left button is released, no matter if it was + * released on an association, on a widget or on an empty space. + * + * Associations and widgets aren't taken into account, and are treated as empty + * spaces. + */ +class ToolBarStateOther : public ToolBarStatePool { + Q_OBJECT +public: + + /** + * Creates a new ToolBarStateOther. + * + * @param umlView The UMLView to use. + */ + ToolBarStateOther(UMLView *umlView); + + /** + * Destroys this ToolBarStateOther. + */ + virtual ~ToolBarStateOther(); + +private: + + /** + * Sets nothing. + * Overriden from base class to ignore associations and widgets and treat + * them as empty spaces to create widgets on it. + */ + virtual void setCurrentElement(); + + /** + * Called when the release event happened on an empty space. + * Associations, widgets and actual empty spaces are all treated as empty + * spaces. It creates a new widget if the left button was released. + * The widget to create depends on the type of the toolbar button selected. + * If the widget is the visual representation of an UMLObject, the object + * factory handles its creation. Otherwise, the widget is created using + * newWidget(). + * The UMLView is resized to fit on all the items. + */ + virtual void mouseReleaseEmpty(); + + /** + * Returns the object type of this tool. + * + * @return The object type of this tool. + */ + Uml::Object_Type getObjectType(); + + /** + * Creates and adds a new widget to the UMLView (if widgets of that type + * don't have an associated UMLObject). + * If the type of the widget doesn't use an UMLObject (for example, a note + * or a box), it creates the widget, adds it to the view and returns true. + * Otherwise, it returns false. + * + * @return True if the widget was created, false otherwise. + * @todo rename to something more clear + */ + bool newWidget(); + +}; + +#endif //TOOLBARSTATEOTHER_H diff --git a/umbrello/umbrello/toolbarstatepool.cpp b/umbrello/umbrello/toolbarstatepool.cpp new file mode 100644 index 00000000..3bb2d04a --- /dev/null +++ b/umbrello/umbrello/toolbarstatepool.cpp @@ -0,0 +1,30 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "toolbarstatepool.h" + +ToolBarStatePool::~ToolBarStatePool() { +} + +void ToolBarStatePool::setButton(const WorkToolBar::ToolBar_Buttons &button) { + if (button != m_ToolBarButton) { + m_ToolBarButton = button; + + init(); // Go back to the initial state. + } +} + +ToolBarStatePool::ToolBarStatePool(UMLView *umlView): ToolBarState(umlView) { + m_ToolBarButton = WorkToolBar::tbb_Arrow; +} + +#include "toolbarstatepool.moc" diff --git a/umbrello/umbrello/toolbarstatepool.h b/umbrello/umbrello/toolbarstatepool.h new file mode 100644 index 00000000..6b33f9ea --- /dev/null +++ b/umbrello/umbrello/toolbarstatepool.h @@ -0,0 +1,71 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef TOOLBARSTATEPOOL_H +#define TOOLBARSTATEPOOL_H + +#include "toolbarstate.h" + +#include "worktoolbar.h" + +/** + * Base class for tools that can use the same state but with different button. + * This class only adds support to specify the button currently in use for a + * tool bar state. + */ +class ToolBarStatePool : public ToolBarState { + Q_OBJECT +public: + + /** + * Destroys this ToolBarStatePool. + */ + virtual ~ToolBarStatePool(); + + /** + * Sets the current button and inits the tool. + * If the current button is the same to the button to set, the tool isn't + * initialized. + * + * @param button The button to set. + */ + void setButton(const WorkToolBar::ToolBar_Buttons &button); + + /** + * Returns the current button. + * + * @return The current button. + */ + WorkToolBar::ToolBar_Buttons getButton() const { + return m_ToolBarButton; + } + +protected: + + /** + * Creates a new ToolBarStatePool. + * Protected to avoid classes other than derived to create objects of this + * class. + * + * @param umlView The UMLView to use. + */ + ToolBarStatePool(UMLView *umlView); + +private: + + /** + * The current button of the tool. + */ + WorkToolBar::ToolBar_Buttons m_ToolBarButton; + +}; + +#endif //TOOLBARSTATEPOOL_H diff --git a/umbrello/umbrello/umbrello.desktop b/umbrello/umbrello/umbrello.desktop new file mode 100644 index 00000000..e414a8da --- /dev/null +++ b/umbrello/umbrello/umbrello.desktop @@ -0,0 +1,57 @@ +# KDE Config File +[Desktop Entry] +Type=Application +Exec=umbrello -caption "%c" %i %m +Icon=umbrello +DocPath=umbrello/index.html +Terminal=false +Name=Umbrello +Name[hi]=अम्बरैलो +Name[ne]=अम्ब्रेलो +Name[pa]=ਉਮਬਰਿੱਲਓ +Name[ta]= அம்ரல்லோ +GenericName=UML Modeller +GenericName[bs]=UML modeler +GenericName[ca]=Modelador UML +GenericName[cs]=UML modelář +GenericName[cy]=Modelydd UML +GenericName[da]=UML Modellering +GenericName[de]=UML-Modellierer +GenericName[el]=Μοντελοποιητής UML +GenericName[eo]=UML-Modelilo +GenericName[es]=Modelador de UML +GenericName[et]=UML-i modelleerimise rakendus +GenericName[eu]=UML modelatzailea +GenericName[fa]=مدل‌ساز UML +GenericName[fi]=UML-mallintaja +GenericName[fr]=Modeleur UML +GenericName[ga]=Múnlóir UML +GenericName[gl]=Modelador de UML +GenericName[hi]=यूएमएल मॉडलर +GenericName[hu]=UML-modellező +GenericName[it]=Modellatore UML +GenericName[ja]=UML モデラー +GenericName[ka]=UML მოდელერი +GenericName[kk]=UML үлгілегіші +GenericName[ms]=Pemodel UML +GenericName[nb]=UML-modelleringsprogram +GenericName[nds]=UML-Modellmaker +GenericName[ne]=यूएमएल मोडेलर +GenericName[nl]=UML-modeller +GenericName[nn]=UML-modelleringsprogram +GenericName[pl]=Program do modelowania UML +GenericName[pt]=Modelador de UML +GenericName[pt_BR]=Modelador UML +GenericName[ru]=Средство UML моделирования +GenericName[sk]=Modelár UML +GenericName[sl]=Mofdelirnik UML +GenericName[sr]=UML моделар +GenericName[sr@Latn]=UML modelar +GenericName[sv]=UML-modellering +GenericName[ta]=UML மோடெல்லார் +GenericName[tg]=Воситаи UML моделкунонӣ +GenericName[tr]=UML Modelleyici +GenericName[uk]=Засіб моделювання UML +GenericName[zh_CN]=UML 建模工具 +MimeType=application/x-uml; +Categories=Qt;KDE;Development; diff --git a/umbrello/umbrello/umbrelloui.rc b/umbrello/umbrello/umbrelloui.rc new file mode 100644 index 00000000..f14efddd --- /dev/null +++ b/umbrello/umbrello/umbrelloui.rc @@ -0,0 +1,58 @@ + + + + &File + &Export + + + + + &Edit + + + &Diagram + &New + + + + + + + + + + + + + + + + + Sh&ow + + + + &Zoom + + + + + + + + &Code + + + + Active &Language + + + + + + + + + + + diff --git a/umbrello/umbrello/uml.cpp b/umbrello/umbrello/uml.cpp new file mode 100644 index 00000000..11a5b32a --- /dev/null +++ b/umbrello/umbrello/uml.cpp @@ -0,0 +1,1708 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "uml.h" + +// qt includes +#include +#include +#include +#include +#include +#include +#include + +// kde includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// app includes +#include "aligntoolbar.h" +#include "codeimport/classimport.h" +#include "docwindow.h" +#include "codegenerator.h" +#include "codegenerationpolicy.h" +#include "codegenerators/codegenfactory.h" +#include "codegenerators/codegenpolicyext.h" +#include "optionstate.h" +#include "widget_utils.h" +#include "umldoc.h" +#include "umllistview.h" +#include "umlviewlist.h" +#include "worktoolbar.h" +#ifdef HAVE_DOT +# include "autolayout/autolayoutdlg.h" //dimitri +#endif +#include "model_utils.h" +#include "clipboard/umlclipboard.h" +#include "dialogs/classwizard.h" +#include "dialogs/codegenerationwizard.h" +#include "dialogs/codeviewerdialog.h" +#include "dialogs/diagramprintpage.h" + +#include "refactoring/refactoringassistant.h" +#include "codegenerators/simplecodegenerator.h" +#include "umlviewimageexporter.h" +#include "umlviewimageexporterall.h" + +#include "kplayerslideraction.h" + +#include "configurable.h" + +#include "cmdlineexportallviewsevent.h" + +#include "docgenerators/docbookgenerator.h" +#include "docgenerators/xhtmlgenerator.h" + +UMLApp::UMLApp(QWidget* , const char* name):KDockMainWindow(0, name) { + s_instance = this; + m_pDocWindow = 0; + m_config = kapp->config(); + m_listView = 0; + m_langSelect = NULL; + m_zoomSelect = NULL; + m_loading = false; + m_clipTimer = 0; + m_copyTimer = 0; + m_codegen = 0; + m_policyext = 0; + m_commoncodegenpolicy = 0; + m_xhtmlGenerator = 0; + m_activeLanguage = Uml::pl_Reserved; + /////////////////////////////////////////////////////////////////// + // call inits to invoke all other construction parts + readOptionState(); + m_doc = new UMLDoc(); + m_doc->init(); + initActions(); //now calls initStatusBar() because it is affected by setupGUI() + initView(); + initClip(); + readOptions(); + /////////////////////////////////////////////////////////////////// + // disable actions at startup + fileSave->setEnabled(true); + fileSaveAs->setEnabled(true); + enablePrint(false); + editCut->setEnabled(false); + editCopy->setEnabled(false); + editPaste->setEnabled(false); + editUndo->setEnabled(false); + editRedo->setEnabled(false); + + //get a reference to the Code->Active Language and to the Diagram->Zoom menu + QPopupMenu* menu = findMenu( menuBar(), QString("code") ); + m_langSelect = findMenu( menu, QString("active_lang_menu") ); + + //in case langSelect hasn't been initialized we create the Popup menu. + //it will be hidden, but at least we wont crash if someone takes the entry away from the ui.rc file + if (m_langSelect == NULL) { + m_langSelect = new QPopupMenu(this); + } + + menu = findMenu( menuBar(), QString("views") ); + m_zoomSelect = findMenu( menu, QString("zoom_menu") ); + + //in case zoomSelect hasn't been initialized we create the Popup menu. + //it will be hidden, but at least we wont crash if some one takes the entry away from the ui.rc file + if (m_zoomSelect == NULL) { + m_zoomSelect = new QPopupMenu(this); + } + + //connect zoomSelect menu + m_zoomSelect->setCheckable(true); + connect(m_zoomSelect,SIGNAL(aboutToShow()),this,SLOT(setupZoomMenu())); + connect(m_zoomSelect,SIGNAL(activated(int)),this,SLOT(setZoom(int))); + + m_refactoringAssist = 0L; + + m_commoncodegenpolicy = new CodeGenerationPolicy(m_config); + + m_imageExporterAll = new UMLViewImageExporterAll(); +} + +UMLApp::~UMLApp() { + delete m_imageExporterAll; + + delete m_clipTimer; + delete m_copyTimer; + + delete m_statusLabel; + delete m_refactoringAssist; +} + +UMLApp* UMLApp::app() +{ + return s_instance; +} + +void UMLApp::initActions() { + fileNew = KStdAction::openNew(this, SLOT(slotFileNew()), actionCollection()); + fileOpen = KStdAction::open(this, SLOT(slotFileOpen()), actionCollection()); + fileOpenRecent = KStdAction::openRecent(this, SLOT(slotFileOpenRecent(const KURL&)), actionCollection()); + fileSave = KStdAction::save(this, SLOT(slotFileSave()), actionCollection()); + fileSaveAs = KStdAction::saveAs(this, SLOT(slotFileSaveAs()), actionCollection()); + fileClose = KStdAction::close(this, SLOT(slotFileClose()), actionCollection()); + filePrint = KStdAction::print(this, SLOT(slotFilePrint()), actionCollection()); + fileQuit = KStdAction::quit(this, SLOT(slotFileQuit()), actionCollection()); + editUndo = KStdAction::undo(this, SLOT(slotEditUndo()), actionCollection()); + editRedo = KStdAction::redo(this, SLOT(slotEditRedo()), actionCollection()); + editCut = KStdAction::cut(this, SLOT(slotEditCut()), actionCollection()); + editCopy = KStdAction::copy(this, SLOT(slotEditCopy()), actionCollection()); + editPaste = KStdAction::paste(this, SLOT(slotEditPaste()), actionCollection()); + createStandardStatusBarAction(); + setStandardToolBarMenuEnabled(true); + selectAll = KStdAction::selectAll(this, SLOT( slotSelectAll() ), actionCollection()); + fileExportDocbook = new KAction(i18n("&Export model to DocBook"), 0, + this, SLOT( slotFileExportDocbook() ), + actionCollection(), "file_export_docbook"); + fileExportXhtml = new KAction(i18n("&Export model to XHTML"), 0, + this, SLOT( slotFileExportXhtml() ), + actionCollection(), "file_export_xhtml"); + + classWizard = new KAction(i18n("&New Class Wizard..."),0,this,SLOT(slotClassWizard()), + actionCollection(),"class_wizard"); + new KAction(i18n("&Add Default Datatypes for Active Language"), 0, this, + SLOT(slotAddDefaultDatatypes()), actionCollection(), "create_default_datatypes"); + + preferences = KStdAction::preferences(this, SLOT( slotPrefs() ), actionCollection()); + + genWizard = new KAction(i18n("&Code Generation Wizard..."),0,this,SLOT(generationWizard()), + actionCollection(),"generation_wizard"); + genAll = new KAction(i18n("&Generate All Code"),0,this,SLOT(generateAllCode()), + actionCollection(),"generate_all"); + + importClasses = new KAction(i18n("&Import Classes..."), SmallIconSet("source_cpp"), 0, + this,SLOT(slotImportClasses()), actionCollection(),"import_class"); + + fileNew->setToolTip(i18n("Creates a new document")); + fileOpen->setToolTip(i18n("Opens an existing document")); + fileOpenRecent->setToolTip(i18n("Opens a recently used file")); + fileSave->setToolTip(i18n("Saves the document")); + fileSaveAs->setToolTip(i18n("Saves the document as...")); + fileClose->setToolTip(i18n("Closes the document")); + filePrint ->setToolTip(i18n("Prints out the document")); + fileQuit->setToolTip(i18n("Quits the application")); + fileExportDocbook->setToolTip(i18n("Exports the model to the docbook format")); + fileExportXhtml->setToolTip(i18n("Exports the model to the XHTML format")); + editCut->setToolTip(i18n("Cuts the selected section and puts it to the clipboard")); + editCopy->setToolTip(i18n("Copies the selected section to the clipboard")); + editPaste->setToolTip(i18n("Pastes the contents of the clipboard")); + preferences->setToolTip( i18n( "Set the default program preferences") ); + + deleteSelectedWidget = new KAction( i18n("Delete &Selected"), + SmallIconSet("editdelete"), + KShortcut(Qt::Key_Delete), this, + SLOT( slotDeleteSelectedWidget() ), actionCollection(), + "delete_selected" ); + + // The different views + newDiagram = new KActionMenu(0, SmallIconSet("filenew"), actionCollection(), "new_view"); + classDiagram = new KAction( i18n( "&Class Diagram..." ), SmallIconSet("umbrello_diagram_class"), 0, + this, SLOT( slotClassDiagram() ), actionCollection(), "new_class_diagram" ); + +#if defined (HAVE_DOT) + autolayout = new KAction(i18n("&Autolayout..."),0,0,this,SLOT(slotAutolayout()), + actionCollection(),"autolayout"); +#endif + sequenceDiagram= new KAction( i18n( "&Sequence Diagram..." ), SmallIconSet("umbrello_diagram_sequence"), 0, + this, SLOT( slotSequenceDiagram() ), actionCollection(), "new_sequence_diagram" ); + + collaborationDiagram = new KAction( i18n( "C&ollaboration Diagram..." ), SmallIconSet("umbrello_diagram_collaboration"), 0, + this, SLOT( slotCollaborationDiagram() ), actionCollection(), "new_collaboration_diagram" ); + + useCaseDiagram= new KAction( i18n( "&Use Case Diagram..." ), SmallIconSet("umbrello_diagram_usecase"), 0, + this, SLOT( slotUseCaseDiagram() ), actionCollection(), "new_use_case_diagram" ); + + stateDiagram= new KAction( i18n( "S&tate Diagram..." ), SmallIconSet("umbrello_diagram_state"), 0, + this, SLOT( slotStateDiagram() ), actionCollection(), "new_state_diagram" ); + + activityDiagram= new KAction( i18n( "&Activity Diagram..." ), SmallIconSet("umbrello_diagram_activity"), 0, + this, SLOT( slotActivityDiagram() ), actionCollection(), "new_activity_diagram" ); + + componentDiagram = new KAction( i18n("Co&mponent Diagram..."), SmallIconSet("umbrello_diagram_component"), 0, + this, SLOT( slotComponentDiagram() ), actionCollection(), + "new_component_diagram" ); + + deploymentDiagram = new KAction( i18n("&Deployment Diagram..."), SmallIconSet("umbrello_diagram_deployment"), 0, + this, SLOT( slotDeploymentDiagram() ), actionCollection(), + "new_deployment_diagram" ); + + entityRelationshipDiagram = new KAction( i18n("&Entity Relationship Diagram..."), SmallIconSet("umbrello_diagram_entityrelationship"), 0, + this, SLOT( slotEntityRelationshipDiagram() ), actionCollection(), + "new_entityrelationship_diagram" ); + + viewClearDiagram = new KAction(i18n("&Clear Diagram"), SmallIconSet("editclear"), 0, + this, SLOT( slotCurrentViewClearDiagram() ), actionCollection(), "view_clear_diagram"); + viewSnapToGrid = new KToggleAction(i18n("&Snap to Grid"), 0, + this, SLOT( slotCurrentViewToggleSnapToGrid() ), actionCollection(), "view_snap_to_grid"); + viewShowGrid = new KToggleAction(i18n("S&how Grid"), 0, + this, SLOT( slotCurrentViewToggleShowGrid() ), actionCollection(), "view_show_grid"); +#if (KDE_VERSION_MINOR>=3) && (KDE_VERSION_MAJOR>=3) + viewShowGrid->setCheckedState(i18n("&Hide Grid")); +#endif + deleteDiagram = new KAction(i18n("&Delete"), SmallIconSet("editdelete"), 0, + this, SLOT( slotDeleteDiagram() ), actionCollection(), "view_delete"); + viewExportImage = new KAction(i18n("&Export as Picture..."), SmallIconSet("image"), 0, + this, SLOT( slotCurrentViewExportImage() ), actionCollection(), "view_export_image"); + viewExportImageAll = new KAction(i18n("Export &All Diagrams as Pictures..."), SmallIconSet("image"), 0, + this, SLOT( slotAllViewsExportImage() ), actionCollection(), "view_export_image_all"); + viewProperties = new KAction(i18n("&Properties"), SmallIconSet("info"), 0, + this, SLOT( slotCurrentViewProperties() ), actionCollection(), "view_properties"); + + viewSnapToGrid->setChecked(false); + viewShowGrid->setChecked(false); + + viewClearDiagram->setEnabled(false); + viewSnapToGrid->setEnabled(false); + viewShowGrid->setEnabled(false); + deleteDiagram->setEnabled(false); + viewExportImage->setEnabled(false); + viewProperties->setEnabled(false); + + zoomAction = new KPlayerPopupSliderAction(i18n("&Zoom Slider"), "viewmag", Key_F9, + this, SLOT(slotZoomSliderMoved(int)), + actionCollection(), "popup_zoom"); + zoom100Action = new KAction(i18n( "Z&oom to 100%" ), "viewmag1", 0, + this, SLOT( slotZoom100() ), actionCollection(), + "zoom100"); + + KStdAction::tipOfDay( this, SLOT( tipOfTheDay() ), actionCollection() ); + + QString moveTabLeftString = i18n("&Move Tab Left"); + QString moveTabRightString = i18n("&Move Tab Right"); + moveTabLeft = new KAction(QApplication::reverseLayout() ? moveTabRightString : moveTabLeftString, + QApplication::reverseLayout() ? "forward" : "back", + QApplication::reverseLayout() ? Qt::CTRL+Qt::SHIFT+Qt::Key_Right : Qt::CTRL+Qt::SHIFT+Qt::Key_Left, + this, SLOT(slotMoveTabLeft()), actionCollection(), + "move_tab_left"); + moveTabRight = new KAction(QApplication::reverseLayout() ? moveTabLeftString : moveTabRightString, + QApplication::reverseLayout() ? "back" : "forward", + QApplication::reverseLayout() ? Qt::CTRL+Qt::SHIFT+Qt::Key_Left : Qt::CTRL+Qt::SHIFT+Qt::Key_Right, + this, SLOT(slotMoveTabRight()), actionCollection(), + "move_tab_right"); + + QString selectTabLeftString = i18n("Select Diagram on Left"); + QString selectTabRightString = i18n("Select Diagram on Right"); + changeTabLeft = new KAction(QApplication::reverseLayout() ? selectTabRightString : selectTabLeftString, + QApplication::reverseLayout() ? Qt::SHIFT+Qt::Key_Right : Qt::SHIFT+Qt::Key_Left, + this, SLOT(slotChangeTabLeft()), actionCollection(), "previous_tab"); + changeTabRight = new KAction(QApplication::reverseLayout() ? selectTabLeftString : selectTabRightString, + QApplication::reverseLayout() ? Qt::SHIFT+Qt::Key_Left : Qt::SHIFT+Qt::Key_Right, + this, SLOT(slotChangeTabRight()), actionCollection(), "next_tab"); + + + initStatusBar(); //call this here because the statusBar is shown/hidden by setupGUI() + + // use the absolute path to your umbrelloui.rc file for testing purpose in setupGUI(); +#if KDE_IS_VERSION(3,2,90) + setupGUI(); +#else + createGUI(); +#endif + QPopupMenu* menu = findMenu( menuBar(), QString("settings") ); + menu->insertItem(i18n("&Windows"), dockHideShowMenu(), -1, 0); +} + +void UMLApp::slotZoomSliderMoved(int value) { + int zoom = (int)(value*0.01); + getCurrentView()->setZoom(zoom*zoom); +} + +void UMLApp::slotZoom100() { + setZoom(100); +} + +void UMLApp::setZoom(int zoom) { + getCurrentView()->setZoom(zoom); +} + +void UMLApp::setupZoomMenu() { + m_zoomSelect->clear(); + + //IMPORTANT: The ID's must match the zoom value (text) + m_zoomSelect->insertItem(i18n(" &33%"),33); + m_zoomSelect->insertItem(i18n(" &50%"),50); + m_zoomSelect->insertItem(i18n(" &75%"),75); + m_zoomSelect->insertItem(i18n("&100%"),100); + m_zoomSelect->insertItem(i18n("1&50%"),150); + m_zoomSelect->insertItem(i18n("&200%"),200); + m_zoomSelect->insertItem(i18n("3&00%"),300); + + + int zoom = getCurrentView()->currentZoom(); + //if current zoom is not a "standard zoom" (because of zoom in / zoom out step + //we add it for information + switch(zoom){ + case 33: + case 50: + case 75: + case 100: + case 150: + case 200: + case 300: + break; + default: + m_zoomSelect->insertSeparator(); + m_zoomSelect->insertItem(QString::number(zoom)+" %",zoom); + } + m_zoomSelect->setItemChecked(zoom, true); +} + +void UMLApp::initStatusBar() { + m_statusLabel = new KStatusBarLabel( i18n("Ready."), 0, statusBar() ); + m_statusLabel->setFixedHeight( m_statusLabel->sizeHint().height() ); + + m_statusLabel->setFrameStyle( QFrame::NoFrame | QFrame::Plain ); + m_statusLabel->setMargin( 0 ); + m_statusLabel->setLineWidth(0); + + statusBar()->addWidget( m_statusLabel, 1, false ); + + m_statusLabel->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + + connect(m_doc, SIGNAL( sigWriteToStatusBar(const QString &) ), this, SLOT( slotStatusMsg(const QString &) )); +} + +void UMLApp::initView() { + setCaption(m_doc->URL().fileName(),false); + m_view = NULL; + toolsbar = new WorkToolBar(this, ""); + toolsbar->setLabel(i18n("Diagram Toolbar")); + addToolBar(toolsbar, Qt::DockTop, false); + + m_alignToolBar = new AlignToolBar(this, ""); + m_alignToolBar->setLabel(i18n("Alignment Toolbar")); + addToolBar(m_alignToolBar, Qt::DockTop, false); + + m_mainDock = createDockWidget("maindock", 0L, 0L, "main dock"); + m_newSessionButton = NULL; + m_diagramMenu = NULL; + m_closeDiagramButton = NULL; + Settings::OptionState& optionState = Settings::getOptionState(); + if (optionState.generalState.tabdiagrams) { + m_viewStack = NULL; + m_tabWidget = new KTabWidget(m_mainDock, "tab_widget"); + +#if KDE_IS_VERSION(3,3,89) + m_tabWidget->setAutomaticResizeTabs( true ); +#endif + + m_newSessionButton = new KToolBarButton("tab_new", 0, m_tabWidget); + m_newSessionButton->setIconSet( SmallIcon( "tab_new" ) ); + m_newSessionButton->adjustSize(); + m_newSessionButton->setAutoRaise(true); + m_diagramMenu = new KPopupMenu(m_newSessionButton); + + m_diagramMenu->insertItem(Widget_Utils::iconSet(Uml::dt_Class), i18n("Class Diagram..."), this, SLOT(slotClassDiagram()) ); + m_diagramMenu->insertItem(Widget_Utils::iconSet(Uml::dt_Sequence), i18n("Sequence Diagram..."), this, SLOT(slotSequenceDiagram()) ); + m_diagramMenu->insertItem(Widget_Utils::iconSet(Uml::dt_Collaboration), i18n("Collaboration Diagram..."), this, SLOT(slotCollaborationDiagram()) ); + m_diagramMenu->insertItem(Widget_Utils::iconSet(Uml::dt_UseCase), i18n("Use Case Diagram..."), this, SLOT(slotUseCaseDiagram()) ); + m_diagramMenu->insertItem(Widget_Utils::iconSet(Uml::dt_State), i18n("State Diagram..."), this, SLOT(slotStateDiagram()) ); + m_diagramMenu->insertItem(Widget_Utils::iconSet(Uml::dt_Activity), i18n("Activity Diagram..."), this, SLOT(slotActivityDiagram()) ); + m_diagramMenu->insertItem(Widget_Utils::iconSet(Uml::dt_Component), i18n("Component Diagram..."), this, SLOT(slotComponentDiagram()) ); + m_diagramMenu->insertItem(Widget_Utils::iconSet(Uml::dt_Deployment), i18n("Deployment Diagram..."), this, SLOT(slotDeploymentDiagram()) ); + m_diagramMenu->insertItem(Widget_Utils::iconSet(Uml::dt_EntityRelationship), i18n("Entity Relationship Diagram..."), this, SLOT(slotEntityRelationshipDiagram()) ); + m_newSessionButton->setPopup(m_diagramMenu); + //FIXME why doesn't this work? + //m_newSessionButton->setPopup(newDiagram->popupMenu()); + + //m_closeDiagramButton = new KToolBarButton("tab_remove", 0, m_tabWidget); + m_closeDiagramButton = new QToolButton(m_tabWidget); + m_closeDiagramButton->setIconSet( SmallIcon("tab_remove") ); + m_closeDiagramButton->adjustSize(); + + connect(m_closeDiagramButton, SIGNAL(clicked()), SLOT(slotDeleteDiagram())); + connect(m_tabWidget, SIGNAL(currentChanged(QWidget*)), SLOT(slotTabChanged(QWidget*))); + connect(m_tabWidget, SIGNAL(contextMenu(QWidget*,const QPoint&)), m_doc, SLOT(slotDiagramPopupMenu(QWidget*,const QPoint&))); + m_tabWidget->setCornerWidget( m_newSessionButton, TopLeft ); + m_tabWidget->setCornerWidget( m_closeDiagramButton, TopRight ); + m_newSessionButton->installEventFilter(this); + + m_mainDock->setWidget(m_tabWidget); + } + else + { + m_tabWidget = NULL; + m_viewStack = new QWidgetStack(m_mainDock, "viewstack"); + m_mainDock->setWidget(m_viewStack); + } + m_mainDock->setDockSite(KDockWidget::DockCorner); + m_mainDock->setEnableDocking(KDockWidget::DockNone); + setView(m_mainDock); + setMainDockWidget(m_mainDock); + + m_listDock = createDockWidget( "Model", 0L, 0L, i18n("&Tree View") ); + m_listView = new UMLListView(m_listDock ,"LISTVIEW"); + //m_listView->setSorting(-1); + m_listView->setDocument(m_doc); + m_listView->init(); + m_listDock->setWidget(m_listView); + m_listDock->setDockSite(KDockWidget::DockCorner); + m_listDock->manualDock(m_mainDock, KDockWidget::DockLeft, 20); + + m_documentationDock = createDockWidget( "Documentation", 0L, 0L, i18n("&Documentation") ); + m_pDocWindow = new DocWindow(m_doc, m_documentationDock, "DOCWINDOW"); + m_documentationDock->setWidget(m_pDocWindow); + m_documentationDock->setDockSite(KDockWidget::DockCorner); + m_documentationDock->manualDock(m_listDock, KDockWidget::DockBottom, 80); + + m_doc->setupSignals();//make sure gets signal from list view + + readDockConfig(); //reposition all the DockWindows to their saved positions +} + +void UMLApp::openDocumentFile(const KURL& url) { + slotStatusMsg(i18n("Opening file...")); + + m_doc->openDocument( url); + fileOpenRecent->addURL( url ); + slotStatusMsg(i18n("Ready.")); + setCaption(m_doc->URL().fileName(), false); + enablePrint(true); +} + +UMLDoc *UMLApp::getDocument() const { + return m_doc; +} + +UMLListView* UMLApp::getListView() { + return m_listView; +} + + +void UMLApp::saveOptions() { + toolBar("mainToolBar")->saveSettings(m_config, "toolbar"); + toolsbar->saveSettings(m_config, "workbar"); + m_alignToolBar->saveSettings(m_config, "aligntoolbar"); + fileOpenRecent->saveEntries(m_config,"Recent Files"); + m_config->setGroup( "General Options" ); + m_config->writeEntry( "Geometry", size() ); + + Settings::OptionState& optionState = Settings::getOptionState(); + m_config->writeEntry( "undo", optionState.generalState.undo ); + m_config->writeEntry( "tabdiagrams", optionState.generalState.tabdiagrams ); + m_config->writeEntry( "newcodegen", optionState.generalState.newcodegen ); + m_config->writeEntry( "angularlines", optionState.generalState.angularlines ); + m_config->writeEntry( "autosave", optionState.generalState.autosave ); + m_config->writeEntry( "time", optionState.generalState.time ); + m_config->writeEntry( "autosavetime", optionState.generalState.autosavetime ); + m_config->writeEntry( "autosavesuffix", optionState.generalState.autosavesuffix ); + + m_config->writeEntry( "logo", optionState.generalState.logo ); + m_config->writeEntry( "loadlast", optionState.generalState.loadlast ); + + m_config->writeEntry( "diagram", optionState.generalState.diagram ); + if( m_doc->URL().fileName() == i18n( "Untitled" ) ) { + m_config -> writeEntry( "lastFile", "" ); + } else { + m_config -> writePathEntry( "lastFile", m_doc -> URL().prettyURL() ); + } + m_config->writeEntry( "imageMimeType", getImageMimeType() ); + + m_config->setGroup( "TipOfDay"); + optionState.generalState.tip = m_config -> readBoolEntry( "RunOnStart", true ); + m_config->writeEntry( "RunOnStart", optionState.generalState.tip ); + + m_config->setGroup( "UI Options" ); + m_config->writeEntry( "useFillColor", optionState.uiState.useFillColor ); + m_config->writeEntry( "fillColor", optionState.uiState.fillColor ); + m_config->writeEntry( "lineColor", optionState.uiState.lineColor ); + m_config->writeEntry( "lineWidth", optionState.uiState.lineWidth ); + m_config->writeEntry( "showDocWindow", m_documentationDock->isVisible() ); + m_config->writeEntry( "font", optionState.uiState.font ); + + m_config->setGroup( "Class Options" ); + m_config->writeEntry( "showVisibility", optionState.classState.showVisibility ); + m_config->writeEntry( "showAtts", optionState.classState.showAtts); + m_config->writeEntry( "showOps", optionState.classState.showOps ); + m_config->writeEntry( "showStereoType", optionState.classState.showStereoType ); + m_config->writeEntry( "showAttSig", optionState.classState.showAttSig ); + m_config->writeEntry( "ShowOpSig", optionState.classState.showOpSig ); + m_config->writeEntry( "showPackage", optionState.classState.showPackage ); + m_config->writeEntry( "defaultAttributeScope", optionState.classState.defaultAttributeScope); + m_config->writeEntry( "defaultOperationScope", optionState.classState.defaultOperationScope); + + m_config -> setGroup( "Code Viewer Options" ); + m_config->writeEntry( "height", optionState.codeViewerState.height ); + m_config->writeEntry( "width", optionState.codeViewerState.width); + m_config->writeEntry( "font", optionState.codeViewerState.font); + m_config->writeEntry( "fontColor", optionState.codeViewerState.fontColor); + m_config->writeEntry( "paperColor", optionState.codeViewerState.paperColor); + m_config->writeEntry( "selectedColor", optionState.codeViewerState.selectedColor); + m_config->writeEntry( "editBlockColor", optionState.codeViewerState.editBlockColor); + m_config->writeEntry( "nonEditBlockColor", optionState.codeViewerState.nonEditBlockColor); + m_config->writeEntry( "umlObjectBlockColor", optionState.codeViewerState.umlObjectColor); + m_config->writeEntry( "blocksAreHighlighted", optionState.codeViewerState.blocksAreHighlighted); + m_config->writeEntry( "showHiddenBlocks", optionState.codeViewerState.showHiddenBlocks); + m_config->writeEntry( "hiddenColor", optionState.codeViewerState.hiddenColor); + + // write the config for a language-specific code gen policy + if (m_policyext) + m_policyext->writeConfig(m_config); + + // now write the basic defaults to the m_config file + m_commoncodegenpolicy->writeConfig(m_config); + + // next, we record the activeLanguage in the Code Generation Group + if (m_codegen) { + m_config->setGroup("Code Generation"); + m_config->writeEntry("activeLanguage", Model_Utils::progLangToString(m_codegen->getLanguage())); + } +} + +void UMLApp::readOptions() { + // bar status settings + toolBar("mainToolBar")->applySettings(m_config, "toolbar"); + // do config for work toolbar + toolsbar->applySettings(m_config, "workbar"); + m_alignToolBar->applySettings(m_config, "aligntoolbar"); + fileOpenRecent->loadEntries(m_config,"Recent Files"); + m_config->setGroup("General Options"); + setImageMimeType(m_config->readEntry("imageMimeType","image/png")); + QSize tmpQSize(630,460); + resize( m_config->readSizeEntry("Geometry", & tmpQSize) ); +} + +void UMLApp::saveProperties(KConfig *_config) { + if(m_doc->URL().fileName()!=i18n("Untitled") && !m_doc->isModified()) { + // saving to tempfile not necessary + + } else { + KURL url=m_doc->URL(); + _config->writePathEntry("filename", url.url()); + _config->writeEntry("modified", m_doc->isModified()); + QString tempname = kapp->tempSaveName(url.url()); + QString tempurl= KURL::encode_string(tempname); + + KURL _url(tempurl); + m_doc->saveDocument(_url); + } +} + +void UMLApp::readProperties(KConfig* _config) { + QString filename = _config->readPathEntry("filename"); + KURL url(filename); + bool modified = _config->readBoolEntry("modified", false); + if(modified) { + bool canRecover; + QString tempname = kapp->checkRecoverFile(filename, canRecover); + KURL _url(tempname); + + + if(canRecover) { + m_doc->openDocument(_url); + m_doc->setModified(); + enablePrint(true); + setCaption(_url.fileName(),true); + QFile::remove + (tempname); + } else { + enablePrint(false); + } + } else { + if(!filename.isEmpty()) { + m_doc->openDocument(url); + enablePrint(true); + setCaption(url.fileName(),false); + + } else { + enablePrint(false); + } + } +} + +bool UMLApp::queryClose() { + writeDockConfig(); + return m_doc->saveModified(); +} + +bool UMLApp::queryExit() { + saveOptions(); + m_doc -> closeDocument(); + return true; +} + +void UMLApp::slotFileNew() { + slotStatusMsg(i18n("Creating new document...")); + if(m_doc->saveModified()) { + setDiagramMenuItemsState(false); + m_doc->newDocument(); + setCaption(m_doc->URL().fileName(), false); + fileOpenRecent->setCurrentItem( -1 ); + setModified(false); + enablePrint(false); + } + slotUpdateViews(); + slotStatusMsg(i18n("Ready.")); +} + +void UMLApp::slotFileOpen() { + slotStatusMsg(i18n("Opening file...")); + m_loading = true; + + if(!m_doc->saveModified()) { + + // here saving wasn't successful + + } else { + KURL url=KFileDialog::getOpenURL(":open-umbrello-file", + i18n("*.xmi *.xmi.tgz *.xmi.tar.bz2 *.mdl|All Supported Files (*.xmi, *.xmi.tgz, *.xmi.tar.bz2, *.mdl)\n" + "*.xmi|Uncompressed XMI Files (*.xmi)\n" + "*.xmi.tgz|Gzip Compressed XMI Files (*.xmi.tgz)\n" + "*.xmi.tar.bz2|Bzip2 Compressed XMI Files (*.xmi.tar.bz2)\n" + "*.mdl|Rose model files"), this, i18n("Open File")); + if(!url.isEmpty()) { + if(m_doc->openDocument(url)) + fileOpenRecent->addURL( url ); + enablePrint(true); + setCaption(m_doc->URL().fileName(), false); + } + + } + slotUpdateViews(); + m_loading = false; + slotStatusMsg(i18n("Ready.")); +} + +void UMLApp::slotFileOpenRecent(const KURL& url) { + slotStatusMsg(i18n("Opening file...")); + m_loading = true; + + KURL oldURL = m_doc->URL(); + + if(!m_doc->saveModified()) { + // here saving wasn't successful + } else { + if(!m_doc->openDocument(url)) { + fileOpenRecent->removeURL(url); + fileOpenRecent->setCurrentItem( -1 ); + } else { + fileOpenRecent->addURL(url); + } + enablePrint(true); + setCaption(m_doc->URL().fileName(), false); + } + + m_loading = false; + slotUpdateViews(); + slotStatusMsg(i18n("Ready.")); +} + +void UMLApp::slotFileSave() { + slotStatusMsg(i18n("Saving file...")); + if(m_doc->URL().fileName() == i18n("Untitled")) + slotFileSaveAs(); + else + m_doc->saveDocument(m_doc -> URL()); + + slotStatusMsg(i18n("Ready.")); +} + +bool UMLApp::slotFileSaveAs() +{ + slotStatusMsg(i18n("Saving file with a new filename...")); + bool cont = true; + KURL url; + QString ext; + while(cont) { + url=KFileDialog::getSaveURL(":save-umbrello-file", i18n("*.xmi|XMI File\n*.xmi.tgz|Gzip Compressed XMI File\n*.xmi.tar.bz2|Bzip2 Compressed XMI File\n*|All Files"), this, i18n("Save As")); + + if(url.isEmpty()) + cont = false; + else { + QDir d = url.path(-1); + + if(QFile::exists(d.path())) { + int want_save = KMessageBox::warningContinueCancel(this, i18n("The file %1 exists.\nDo you wish to overwrite it?").arg(url.path()), i18n("Warning"), i18n("Overwrite")); + if(want_save == KMessageBox::Continue) + cont = false; + } else + cont = false; + + } + } + if(!url.isEmpty()) { + bool b = m_doc->saveDocument(url); + if (b) { + fileOpenRecent->addURL(url); + setCaption(url.fileName(),m_doc->isModified()); + slotStatusMsg(i18n("Ready.")); + } + return b; + + } else { + slotStatusMsg(i18n("Ready.")); + return false; + } +} + +void UMLApp::slotFileClose() { + slotStatusMsg(i18n("Closing file...")); + + slotFileNew(); + +} + +void UMLApp::slotFilePrint() +{ + slotStatusMsg(i18n("Printing...")); + + KPrinter printer; + printer.setFullPage(true); + DiagramPrintPage * selectPage = new DiagramPrintPage(0, m_doc); + printer.addDialogPage(selectPage); + QString msg; + if (printer.setup(this, i18n("Print %1").arg(m_doc->URL().prettyURL()))) { + + m_doc -> print(&printer); + } + slotStatusMsg(i18n("Ready.")); +} + +void UMLApp::slotFileQuit() { + slotStatusMsg(i18n("Exiting...")); + if(m_doc->saveModified()) { + writeDockConfig(); + saveOptions(); + kapp->quit(); + } + slotStatusMsg(i18n("Ready.")); +} + +void UMLApp::slotFileExportDocbook() +{ + DocbookGenerator().generateDocbookForProject(); +} + +void UMLApp::slotFileExportXhtml() +{ + if (m_xhtmlGenerator != 0) + { + return; + } + m_xhtmlGenerator = new XhtmlGenerator(); + m_xhtmlGenerator->generateXhtmlForProject(); + connect(m_xhtmlGenerator,SIGNAL(finished()),this,SLOT(slotXhtmlDocGenerationFinished())); +} + +void UMLApp::slotEditUndo() { + m_doc->loadUndoData(); + slotStatusMsg(i18n("Ready.")); +} + +void UMLApp::slotEditRedo() { + m_doc->loadRedoData(); + slotStatusMsg(i18n("Ready.")); +} + +void UMLApp::slotEditCut() { + slotStatusMsg(i18n("Cutting selection...")); + //FIXME bug 59774 this fromview isn't very reliable. + //when cutting diagrams it is set to true even though it shouldn't be + bool fromview = (getCurrentView() && getCurrentView()->getSelectCount()); + if ( editCutCopy(fromview) ) { + emit sigCutSuccessful(); + slotDeleteSelectedWidget(); + m_doc->setModified(true); + } + slotStatusMsg(i18n("Ready.")); +} + +void UMLApp::slotEditCopy() { + slotStatusMsg(i18n("Copying selection to clipboard...")); + bool fromview = (getCurrentView() && getCurrentView()->getSelectCount()); + editCutCopy( fromview ); + slotStatusMsg(i18n("Ready.")); + m_doc -> setModified( true ); +} + +void UMLApp::slotEditPaste() { + slotStatusMsg(i18n("Inserting clipboard contents...")); + QMimeSource* data = QApplication::clipboard()->data(); + UMLClipboard clipboard; + setCursor(KCursor::waitCursor()); + if(!clipboard.paste(data)) { + KMessageBox::sorry( this, i18n("Umbrello could not paste the clipboard contents. " + "The objects in the clipboard may be of the wrong " + "type to be pasted here."), i18n("Paste Error") ); + } + slotStatusMsg(i18n("Ready.")); + setCursor(KCursor::arrowCursor()); + editPaste->setEnabled(false); + m_doc -> setModified( true ); +} + +//Remove these once we stop supporting KDE 3.1 +// #if !KDE_IS_VERSION(3,1,90) + +void UMLApp::slotViewToolBar() { + slotStatusMsg(i18n("Toggling toolbar...")); + + /////////////////////////////////////////////////////////////////// + // turn Toolbar on or off + + if(!viewToolBar->isChecked()) { + toolBar("mainToolBar")->hide(); + } else { + toolBar("mainToolBar")->show(); + } + + slotStatusMsg(i18n("Ready.")); +} + +void UMLApp::slotViewStatusBar() { + slotStatusMsg(i18n("Toggle the statusbar...")); + /////////////////////////////////////////////////////////////////// + //turn Statusbar on or off + if(!viewStatusBar->isChecked()) { + statusBar()->hide(); + } else { + statusBar()->show(); + } + + slotStatusMsg(i18n("Ready.")); +} +// #endif + + +void UMLApp::slotStatusMsg(const QString &text) { + /////////////////////////////////////////////////////////////////// + // change status message permanently + statusBar()->clear(); + m_statusLabel->setText( text ); + + m_statusLabel->repaint(); +} + +void UMLApp::slotClassDiagram() { + UMLFolder *root = m_doc->getRootFolder(Uml::mt_Logical); + getDocument()->createDiagram(root, Uml::dt_Class); +} + + +void UMLApp::slotSequenceDiagram() { + UMLFolder *root = m_doc->getRootFolder(Uml::mt_Logical); + m_doc->createDiagram(root, Uml::dt_Sequence); +} + +void UMLApp::slotCollaborationDiagram() { + UMLFolder *root = m_doc->getRootFolder(Uml::mt_Logical); + m_doc->createDiagram(root, Uml::dt_Collaboration); +} + +void UMLApp::slotUseCaseDiagram() { + UMLFolder *root = m_doc->getRootFolder(Uml::mt_UseCase); + m_doc->createDiagram(root, Uml::dt_UseCase); +} + +void UMLApp::slotStateDiagram() { + UMLFolder *root = m_doc->getRootFolder(Uml::mt_Logical); + m_doc->createDiagram(root, Uml::dt_State); +} + +void UMLApp::slotActivityDiagram() { + UMLFolder *root = m_doc->getRootFolder(Uml::mt_Logical); + m_doc->createDiagram(root, Uml::dt_Activity); +} + +void UMLApp::slotComponentDiagram() { + UMLFolder *root = m_doc->getRootFolder(Uml::mt_Component); + m_doc->createDiagram(root, Uml::dt_Component ); +} + +void UMLApp::slotDeploymentDiagram() { + UMLFolder *root = m_doc->getRootFolder(Uml::mt_Deployment); + m_doc->createDiagram(root, Uml::dt_Deployment); +} + +void UMLApp::slotEntityRelationshipDiagram() { + UMLFolder *root = m_doc->getRootFolder(Uml::mt_EntityRelationship); + m_doc->createDiagram(root, Uml::dt_EntityRelationship); +} + +WorkToolBar* UMLApp::getWorkToolBar() { + return toolsbar; +} + +void UMLApp::setModified(bool modified) { + //fileSave -> setEnabled(modified); + + //if anything else needs to be done on a mofication, put it here + + // printing should be possible whenever there is something to print + if ( m_loading == false && modified == true && getCurrentView() ) { + enablePrint(true); + } + + if (m_loading == false) { + setCaption(m_doc->URL().fileName(), modified); //add disk icon to taskbar if modified + } +} + +void UMLApp::enablePrint(bool enable) { + filePrint->setEnabled(enable); +} + +void UMLApp::enableUndo(bool enable) { + editUndo->setEnabled(enable); +} + +void UMLApp::enableRedo(bool enable) { + editRedo->setEnabled(enable); +} + +/** initialize the QT's global clipboard support for the application */ +void UMLApp::initClip() { + QClipboard* clip = QApplication::clipboard(); + connect(clip, SIGNAL(dataChanged()), this, SLOT(slotClipDataChanged())); + + // Don't poll the X11 clipboard every second. This is a little expensive and resulted + // in very annoying umbrello slowdowns / hangs. Qt will notify us about clipboard + // changes anyway (see dataChanged() signal above), albeit only when a Qt application + // changes the clipboard. Work is in progress to make this work with other toolkits + // as well. (pfeiffer) + // m_clipTimer = new QTimer(this, "timer"); + // m_clipTimer->start(1000, false); + // connect(m_clipTimer, SIGNAL(timeout()), this, SLOT(slotClipDataChanged())); + + m_copyTimer = new QTimer(this, "copytimer"); + m_copyTimer->start(500, false); + connect(m_copyTimer, SIGNAL(timeout()), this, SLOT(slotCopyChanged())); +} + +bool UMLApp::canDecode(const QMimeSource* mimeSource) { + const char* f; + for (int i=0; (f=mimeSource->format(i)); i++) { + if ( !qstrnicmp(f,"application/x-uml-clip", 22) ) { + //FIXME need to test for clip1, clip2, clip3, clip4 or clip5 + //(the only valid clip types) + return true; + } + } + return false; +} + +void UMLApp::slotClipDataChanged() { + QMimeSource * data = QApplication::clipboard()->data(); + + //Pass the MimeSource to the Doc + editPaste->setEnabled( data && canDecode(data) ); +} + +void UMLApp::slotCopyChanged() { + if(m_listView->getSelectedCount() || (getCurrentView() && getCurrentView()->getSelectCount())) { + editCopy->setEnabled(true); + editCut->setEnabled(true); + } else { + editCopy->setEnabled(false); + editCut->setEnabled(false); + } +} + +void UMLApp::slotPrefs() { + /* the KTipDialog may have changed the value */ + m_config->setGroup("TipOfDay"); + Settings::OptionState& optionState = Settings::getOptionState(); + optionState.generalState.tip = m_config->readBoolEntry( "RunOnStart", true ); + + m_dlg = new SettingsDlg(this, &optionState); + connect(m_dlg, SIGNAL( applyClicked() ), this, SLOT( slotApplyPrefs() ) ); + + if ( m_dlg->exec() == QDialog::Accepted && m_dlg->getChangesApplied() ) { + slotApplyPrefs(); + } + + delete m_dlg; + m_dlg = NULL; +} + +void UMLApp::slotApplyPrefs() { + if (m_dlg) { + /* we need this to sync both values */ + m_config -> setGroup( "TipOfDay"); + Settings::OptionState& optionState = Settings::getOptionState(); + m_config -> writeEntry( "RunOnStart", optionState.generalState.tip ); + + m_doc -> settingsChanged( optionState ); + const QString plStr = m_dlg->getCodeGenerationLanguage(); + Uml::Programming_Language pl = Model_Utils::stringToProgLang(plStr); + setGenerator(pl); + } +} + +bool UMLApp::getUndoEnabled() { + return editUndo->isEnabled(); +} + +bool UMLApp::getRedoEnabled() { + return editRedo->isEnabled(); +} + +bool UMLApp::getPasteState() { + return editPaste -> isEnabled(); +} + +bool UMLApp::getCutCopyState() { + return editCopy -> isEnabled(); +} + +bool UMLApp::editCutCopy( bool bFromView ) { + UMLClipboard clipboard; + QMimeSource * clipdata = 0; + + if ((clipdata = clipboard.copy(bFromView)) != 0) { + QClipboard* clip = QApplication::clipboard(); + clip->setData(clipdata);//the global clipboard takes ownership of the clipdata memory + connect(clip, SIGNAL(dataChanged()), this, SLOT(slotClipDataChanged())); + return true; + } + return false; +} + +void UMLApp::readOptionState() { + m_config -> setGroup( "General Options" ); + Settings::OptionState& optionState = Settings::getOptionState(); + optionState.generalState.undo = m_config -> readBoolEntry( "undo", true ); + optionState.generalState.tabdiagrams = m_config -> readBoolEntry("tabdiagrams", false); +#if defined (WORK_ON_BUG_126262) + optionState.generalState.newcodegen = m_config -> readBoolEntry("newcodegen", false); +#else + optionState.generalState.newcodegen = false; +#endif + optionState.generalState.angularlines = m_config->readBoolEntry("angularlines", false); + optionState.generalState.autosave = m_config -> readBoolEntry( "autosave", true ); + optionState.generalState.time = m_config -> readNumEntry( "time", 0 ); //old autosavetime value kept for compatibility + optionState.generalState.autosavetime = m_config -> readNumEntry( "autosavetime", 0 ); + //if we don't have a "new" autosavetime value, convert the old one + if (optionState.generalState.autosavetime == 0) { + switch (optionState.generalState.time) { + case 0: optionState.generalState.autosavetime = 5; break; + case 1: optionState.generalState.autosavetime = 10; break; + case 2: optionState.generalState.autosavetime = 15; break; + case 3: optionState.generalState.autosavetime = 20; break; + case 4: optionState.generalState.autosavetime = 25; break; + default: optionState.generalState.autosavetime = 5; break; + } + } + // 2004-05-17 Achim Spangler: read new config entry for autosave sufix + optionState.generalState.autosavesuffix = m_config -> readEntry( "autosavesuffix", ".xmi" ); + + optionState.generalState.logo = m_config -> readBoolEntry( "logo", true ); + optionState.generalState.loadlast = m_config -> readBoolEntry( "loadlast", true ); + + optionState.generalState.diagram = (Uml::Diagram_Type) m_config->readNumEntry("diagram", 1); + m_config -> setGroup( "TipOfDay"); + + optionState.generalState.tip = m_config -> readBoolEntry( "RunOnStart", true ); + + m_config -> setGroup( "UI Options" ); + optionState.uiState.useFillColor = m_config -> readBoolEntry( "useFillColor", true ); + QColor defaultYellow = QColor( 255, 255, 192 ); + QColor red ( Qt::red ); + + optionState.uiState.fillColor = m_config -> readColorEntry( "fillColor", &defaultYellow ); + optionState.uiState.lineColor = m_config -> readColorEntry( "lineColor", &red ); + optionState.uiState.lineWidth = m_config -> readNumEntry( "lineWidth", 0 ); + QFont font = ((QWidget *) this)->font() ; + optionState.uiState.font = m_config -> readFontEntry("font", &font ); + + m_config -> setGroup( "Class Options" ); + + optionState.classState.showVisibility = m_config -> readBoolEntry("showVisibility", true); + optionState.classState.showAtts = m_config -> readBoolEntry("showAtts", true); + optionState.classState.showOps = m_config -> readBoolEntry("showOps", true); + optionState.classState.showStereoType = m_config -> readBoolEntry("showStereoType", false); + optionState.classState.showAttSig = m_config -> readBoolEntry("showAttSig", true); + optionState.classState.showOpSig = m_config -> readBoolEntry("ShowOpSig", true); + optionState.classState.showPackage = m_config -> readBoolEntry("showPackage", false); + optionState.classState.defaultAttributeScope = (Uml::Visibility::Value) m_config -> readNumEntry("defaultAttributeScope", Uml::Visibility::Private); + optionState.classState.defaultOperationScope = (Uml::Visibility::Value) m_config -> readNumEntry("defaultOperationScope", Uml::Visibility::Public); + + m_config -> setGroup( "Code Viewer Options" ); + + QColor defaultWhite = QColor( "white" ); + QColor defaultBlack = QColor( "black" ); + QColor defaultPink = QColor( "pink" ); + QColor defaultGrey = QColor( "grey" ); + + optionState.codeViewerState.height = m_config -> readNumEntry( "height", 40 ); + optionState.codeViewerState.width = m_config -> readNumEntry( "width", 80 ); + optionState.codeViewerState.font = m_config -> readFontEntry("font", &font ); + optionState.codeViewerState.showHiddenBlocks = m_config -> readBoolEntry( "showHiddenBlocks", false); + optionState.codeViewerState.blocksAreHighlighted = m_config -> readBoolEntry( "blocksAreHighlighted", false); + optionState.codeViewerState.selectedColor = m_config -> readColorEntry( "selectedColor", &defaultYellow ); + optionState.codeViewerState.paperColor = m_config -> readColorEntry( "paperColor", &defaultWhite); + optionState.codeViewerState.fontColor = m_config -> readColorEntry( "fontColor", &defaultBlack); + optionState.codeViewerState.editBlockColor = m_config -> readColorEntry( "editBlockColor", &defaultPink); + optionState.codeViewerState.umlObjectColor = m_config -> readColorEntry( "umlObjectBlockColor", &defaultPink); + optionState.codeViewerState.nonEditBlockColor = m_config -> readColorEntry( "nonEditBlockColor", &defaultGrey); + optionState.codeViewerState.hiddenColor = m_config -> readColorEntry( "hiddenColor", &defaultGrey); + +} + + +/** Call the code viewing assistant on a code document */ +void UMLApp::viewCodeDocument(UMLClassifier* classifier) { + + CodeGenerator * currentGen = getGenerator(); + if(currentGen && classifier) { + if(!dynamic_cast(currentGen)) + { + CodeDocument *cdoc = currentGen->findCodeDocumentByClassifier(classifier); + + if (cdoc) { + Settings::OptionState& optionState = Settings::getOptionState(); + CodeViewerDialog * dialog = currentGen->getCodeViewerDialog(this,cdoc,optionState.codeViewerState); + dialog->exec(); + optionState.codeViewerState = dialog->getState(); + delete dialog; + dialog = NULL; + } else { + // shouldn't happen.. + KMessageBox::sorry(0, i18n("Cannot view code until you generate some first."),i18n("Cannot View Code")); + } + } else { + KMessageBox::sorry(0, i18n("Cannot view code from simple code writer."),i18n("Cannot View Code")); + } + } + +} + +void UMLApp::refactor(UMLClassifier* classifier) { + if (!m_refactoringAssist) { + m_refactoringAssist = new RefactoringAssistant( m_doc, 0, 0, "refactoring_assistant" ); + } + m_refactoringAssist->refactor(classifier); + m_refactoringAssist->show(); +} + +CodeGenerationPolicy *UMLApp::getCommonPolicy() { + return m_commoncodegenpolicy; +} + +void UMLApp::setPolicyExt(CodeGenPolicyExt *policy) { + m_policyext = policy; +} + +CodeGenPolicyExt *UMLApp::getPolicyExt() { + return m_policyext; +} + +CodeGenerator *UMLApp::setGenerator(Uml::Programming_Language pl) { + if (pl == Uml::pl_Reserved) { + if (m_codegen) { + delete m_codegen; + m_codegen = NULL; + } + return NULL; + } + // does the code generator already exist? + // then simply return that + if (m_codegen) { + if (m_codegen->getLanguage() == pl) + return m_codegen; + delete m_codegen; // ATTENTION! remove all refs to it or its policy first + m_codegen = NULL; + } + m_activeLanguage = pl; + m_codegen = CodeGenFactory::createObject(pl); + updateLangSelectMenu(pl); + + if (m_policyext) + m_policyext->setDefaults(m_config, false); // picks up language specific stuff + return m_codegen; +} + +CodeGenerator* UMLApp::getGenerator() { + return m_codegen; +} + +void UMLApp::generateAllCode() { + if (m_codegen) { + m_codegen->writeCodeToFile(); + } +} + +void UMLApp::generationWizard() { + CodeGenerationWizard wizard(0 /*classList*/); + wizard.exec(); +} + +void UMLApp::setActiveLanguage(int menuId) { + + // only change the active language IF different from one we currently have + if (!m_langSelect->isItemChecked(menuId)) + { + uint index = 0; + for(unsigned int i=0; i < m_langSelect->count(); i++) { + int id = m_langSelect->idAt(i); + m_langSelect->setItemChecked(id, false); //uncheck everything + if (id == menuId) + index = i; + } + + m_langSelect->setItemChecked(menuId,true); + m_activeLanguage = (Uml::Programming_Language)index; + + // update the generator + setGenerator(m_activeLanguage); + } +} + +void UMLApp::setActiveLanguage( const QString &activeLanguage ) { + + for(unsigned int j=0; j < m_langSelect->count(); j++) { + int id = m_langSelect->idAt(j); + + if (m_langSelect->text(id) == activeLanguage && + m_langSelect->isItemChecked(id)) + return; // already set.. no need to do anything + } + + for(unsigned int i=0; i < m_langSelect->count(); i++) { + bool isActiveLang = (m_langSelect->text(m_langSelect->idAt(i)) == activeLanguage); + //uncheck everything except the active language + m_langSelect->setItemChecked(m_langSelect->idAt(i), isActiveLang); + } + + setGenerator(Model_Utils::stringToProgLang(activeLanguage)); +} + +Uml::Programming_Language UMLApp::getActiveLanguage() { + return m_activeLanguage; +} + +bool UMLApp::activeLanguageIsCaseSensitive() { + return (m_activeLanguage != Uml::pl_Pascal && + m_activeLanguage != Uml::pl_Ada && + m_activeLanguage != Uml::pl_SQL); +} + +QString UMLApp::activeLanguageScopeSeparator() { + Uml::Programming_Language pl = getActiveLanguage(); + if (pl == Uml::pl_Ada || + pl == Uml::pl_CSharp || + pl == Uml::pl_Pascal || + pl == Uml::pl_Java || + pl == Uml::pl_JavaScript || + pl == Uml::pl_Python) // CHECK: more? + return "."; + return "::"; +} + +void UMLApp::slotCurrentViewClearDiagram() { + getCurrentView()->clearDiagram(); +} + +void UMLApp::slotCurrentViewToggleSnapToGrid() { + getCurrentView()->toggleSnapToGrid(); + viewSnapToGrid->setChecked( getCurrentView()->getSnapToGrid() ); +} + +void UMLApp::slotCurrentViewToggleShowGrid() { + getCurrentView()->toggleShowGrid(); + viewShowGrid->setChecked( getCurrentView()->getShowSnapGrid() ); +} + +void UMLApp::slotCurrentViewExportImage() { + getCurrentView()->getImageExporter()->exportView(); +} + +void UMLApp::slotAllViewsExportImage() { + m_imageExporterAll->exportAllViews(); +} + +void UMLApp::slotCurrentViewProperties() { + getCurrentView()->showPropDialog(); +} + +void UMLApp::setDiagramMenuItemsState(bool bState) { + viewClearDiagram->setEnabled( bState ); + viewSnapToGrid->setEnabled( bState ); + viewShowGrid->setEnabled( bState ); + deleteDiagram->setEnabled(bState); + viewExportImage->setEnabled( bState ); + viewProperties->setEnabled( bState ); + filePrint->setEnabled( bState ); + if ( getCurrentView() ) { + viewSnapToGrid->setChecked( getCurrentView()->getSnapToGrid() ); + viewShowGrid->setChecked( getCurrentView()->getShowSnapGrid() ); + } +} + +void UMLApp::slotUpdateViews() { + QPopupMenu* menu = findMenu( menuBar(), QString("views") ); + if (!menu) { + kWarning() << "view menu not found" << endl; + return; + } + + menu = findMenu( menu, QString("show_view") ); + if (!menu) { + kWarning() << "show menu not found" << endl; + return; + } + + menu->clear(); + + UMLViewList views = getDocument()->getViewIterator(); + for(UMLView *view = views.first(); view; view = views.next()) { + menu->insertItem( view->getName(), view, SLOT( slotShowView() ) ); + view->fileLoaded(); + } +} + +void UMLApp::slotImportClasses() { + m_doc->setLoading(true); + // File selection is separated from invocation of ClassImport::import() + // because the user might decide to choose a language different from + // the active language (by using the "All Files" option). + QString preselectedExtension; + const Uml::Programming_Language pl = m_codegen->getLanguage(); + if (pl == Uml::pl_IDL) { + preselectedExtension = i18n("*.idl|IDL Files (*.idl)"); + } else if (pl == Uml::pl_Python) { + preselectedExtension = i18n("*.py|Python Files (*.py)"); + } else if (pl == Uml::pl_Java) { + preselectedExtension = i18n("*.java|Java Files (*.java)"); + } else if (pl == Uml::pl_Pascal) { + preselectedExtension = i18n("*.pas|Pascal Files (*.pas)"); + } else if (pl == Uml::pl_Ada) { + preselectedExtension = i18n("*.ads *.ada|Ada Files (*.ads *.ada)"); + } else { + preselectedExtension = i18n("*.h *.hh *.hpp *.hxx *.H|Header Files (*.h *.hh *.hpp *.hxx *.H)"); + } + preselectedExtension.append("\n*|" + i18n("All Files")); + QStringList fileList = KFileDialog::getOpenFileNames(":import-classes", preselectedExtension, + this, i18n("Select Code to Import") ); + const QString& firstFile = fileList.first(); + ClassImport *classImporter = ClassImport::createImporterByFileExt(firstFile); + classImporter->importFiles(fileList); + delete classImporter; + m_doc->setLoading(false); + //Modification is set after the import is made, because the file was modified when adding the classes + //Allowing undo of the whole class importing. I think it eats a lot of memory + //m_doc->setModified(true); + //Setting the modification, but without allowing undo + m_doc->setModified(true, false); +} + +void UMLApp::slotClassWizard() { + ClassWizard dlg( m_doc ); + dlg.exec(); +} + +void UMLApp::slotAddDefaultDatatypes() { + m_doc->addDefaultDatatypes(); +} + +void UMLApp::slotCurrentViewChanged() { + UMLView *view = getCurrentView(); + if (view) { + connect(view, SIGNAL( sigShowGridToggled(bool) ), + this, SLOT( slotShowGridToggled(bool) ) ); + connect(view, SIGNAL( sigSnapToGridToggled(bool) ), + this, SLOT( slotSnapToGridToggled(bool) ) ); + } +} +void UMLApp::slotSnapToGridToggled(bool gridOn) { + viewSnapToGrid->setChecked(gridOn); +} + +void UMLApp::slotShowGridToggled(bool gridOn) { + viewShowGrid->setChecked(gridOn); +} + +void UMLApp::slotSelectAll() { + getCurrentView()->selectAll(); +} + +void UMLApp::slotDeleteSelectedWidget() { + if ( getCurrentView() ) { + getCurrentView()->deleteSelection(); + } else { + kWarning() << " trying to delete widgets when there is no current view (see bug 59774)" << endl; + } +} + +void UMLApp::slotDeleteDiagram() { + m_doc->removeDiagram( getCurrentView()->getID() ); +} + +Uml::Programming_Language UMLApp::getDefaultLanguage() { + m_config->setGroup("Code Generation"); + QString activeLanguage = m_config->readEntry("activeLanguage", "C++"); + return Model_Utils::stringToProgLang(activeLanguage); +} + +void UMLApp::initGenerator() { + if (m_codegen) { + delete m_codegen; + m_codegen = NULL; + } + Uml::Programming_Language defaultLanguage = getDefaultLanguage(); + setActiveLanguage(Model_Utils::progLangToString(defaultLanguage)); + if (m_codegen == NULL) + setGenerator(defaultLanguage); + updateLangSelectMenu(defaultLanguage); +} + +void UMLApp::updateLangSelectMenu(Uml::Programming_Language activeLanguage) { + m_langSelect->clear(); + m_langSelect->setCheckable(true); + for (int i = 0; i < Uml::pl_Reserved; i++) { + QString language = Model_Utils::progLangToString((Uml::Programming_Language) i); + int id = m_langSelect->insertItem(language,this,SLOT(setActiveLanguage(int))); + const bool isActiveLanguage = (activeLanguage == i); + m_langSelect->setItemChecked(id, isActiveLanguage); + } +} + +void UMLApp::tipOfTheDay() +{ + KTipDialog::showTip(this ,QString::null, true); +} + +void UMLApp::keyPressEvent(QKeyEvent *e) { + switch(e->key()) { + case Qt::Key_Shift: + //toolsbar->setOldTool(); + e->accept(); + break; + + default: + e->ignore(); + } + +} + +void UMLApp::customEvent(QCustomEvent* e) { + if (e->type() == CmdLineExportAllViewsEvent::getType()) { + CmdLineExportAllViewsEvent* exportAllViewsEvent = static_cast(e); + exportAllViewsEvent->exportAllViews(); + } +} + +//TODO Move this to UMLWidgetController? +void UMLApp::handleCursorKeyReleaseEvent(QKeyEvent* e) { + // in case we have selected something in the diagram, move it by one pixel + // to the direction pointed by the cursor key + if (m_view == NULL || !m_view->getSelectCount() || e->state() != Qt::AltButton) { + e->ignore(); + return; + } + int dx = 0; + int dy = 0; + switch (e->key()) { + case Qt::Key_Left: + dx = -1; + break; + case Qt::Key_Right: + dx = 1; + break; + case Qt::Key_Up: + dy = -1; + break; + case Qt::Key_Down: + dy = 1; + break; + default: + e->ignore(); + return; + } + m_view->moveSelectedBy(dx, dy); + + // notify about modification only at the first key release of possible sequence of auto repeat key releases, + // this reduces the slow down caused by setModified() and makes the cursor moving of widgets smoother + if (!e->isAutoRepeat()) { + m_doc->setModified(); + } + e->accept(); +} + +void UMLApp::keyReleaseEvent(QKeyEvent *e) { + switch(e->key()) { + case Qt::Key_Backspace: + if (!m_pDocWindow->isTyping()) + toolsbar->setOldTool(); + e->accept(); + break; + case Qt::Key_Escape: + toolsbar->setDefaultTool(); + e->accept(); + break; + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_Up: + case Qt::Key_Down: + handleCursorKeyReleaseEvent(e); + break; + default: + e->ignore(); + } + +} + +void UMLApp::newDocument() { + m_doc->newDocument(); + Uml::Programming_Language defaultLanguage = getDefaultLanguage(); + if (m_codegen) { + defaultLanguage = m_codegen->getLanguage(); + delete m_codegen; + m_codegen = NULL; + } + setGenerator(defaultLanguage); + slotUpdateViews(); +} + +QWidget* UMLApp::getMainViewWidget() { + Settings::OptionState& optionState = Settings::getOptionState(); + if (optionState.generalState.tabdiagrams) + return m_tabWidget; + return m_viewStack; +} + +void UMLApp::setCurrentView(UMLView* view) { + m_view = view; + if (m_viewStack == NULL) { + kError() << "UMLApp::setCurrentView: m_viewStack is NULL" << endl; + return; + } + if (view == NULL) { + kDebug() << "UMLApp::setCurrentView: view is NULL" << endl; + return; + } + if (m_viewStack->id(view) < 0) + m_viewStack->addWidget(view); + m_viewStack->raiseWidget(view); + slotStatusMsg(view->getName()); + UMLListViewItem* lvitem = m_listView->findView(view); + if (lvitem) + m_listView->setCurrentItem(lvitem); +} + +UMLView* UMLApp::getCurrentView() { + return m_view; +} + +QPopupMenu* UMLApp::findMenu(QMenuData* menu, const QString &name) { + + if (menu) { + int menuCount = menu->count(); + + for (int i=0; iidAt(i); + QPopupMenu* popupMenu = menu->findItem(idAt)->popup(); + if (popupMenu) { + QString menuName = popupMenu->name(); + if( menuName == name) { + return popupMenu; + } + } + } + } + return 0; +} + +void UMLApp::slotTabChanged(QWidget* view) { + UMLView* umlview = ( UMLView* )view; + m_doc->changeCurrentView( umlview->getID() ); +} + +void UMLApp::slotChangeTabLeft() { + if (m_tabWidget) { + m_tabWidget->setCurrentPage( m_tabWidget->currentPageIndex() - 1 ); + return; + } + UMLViewList views = m_doc->getViewIterator(); + UMLView *currView = m_view; + if (views.find(currView) < 0) { + kError() << "UMLApp::slotChangeTabLeft(): currView not found in viewlist" << endl; + return; + } + if ((currView = views.prev()) != NULL) + setCurrentView(currView); + else + setCurrentView(views.last()); +} + +void UMLApp::slotChangeTabRight() { + if (m_tabWidget) { + m_tabWidget->setCurrentPage( m_tabWidget->currentPageIndex() + 1 ); + return; + } + UMLViewList views = m_doc->getViewIterator(); + UMLView *currView = m_view; + if (views.find(currView) < 0) { + kError() << "UMLApp::slotChangeTabRight(): currView not found in viewlist" << endl; + return; + } + if ((currView = views.next()) != NULL) + setCurrentView(currView); + else + setCurrentView(views.first()); +} + +void UMLApp::slotMoveTabLeft() { + //causes problems + //does strange things when moving right most diagram to the right + //doesn't save order in file + //m_tabWidget->moveTab( m_tabWidget->currentPageIndex(), m_tabWidget->currentPageIndex() - 1 ); +} + +void UMLApp::slotMoveTabRight() { + //causes problems + //m_tabWidget->moveTab( m_tabWidget->currentPageIndex(), m_tabWidget->currentPageIndex() + 1 ); +} + +void UMLApp::slotAutolayout(){ +#ifdef HAVE_DOT +/* + QDialog* d = new AutolayoutDlg(getCurrentView()); + d->show(); + */ +#endif +} + +void UMLApp::slotXhtmlDocGenerationFinished() +{ + delete m_xhtmlGenerator; + m_xhtmlGenerator = 0; +} + +KTabWidget* UMLApp::tabWidget() { + return m_tabWidget; +} + +QString UMLApp::getStatusBarMsg() { + return m_statusLabel->text(); +} + +//static pointer, holding the unique instance +UMLApp* UMLApp::s_instance; + +#include "uml.moc" diff --git a/umbrello/umbrello/uml.h b/umbrello/umbrello/uml.h new file mode 100644 index 00000000..ee669d88 --- /dev/null +++ b/umbrello/umbrello/uml.h @@ -0,0 +1,1026 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UML_H +#define UML_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "umlnamespace.h" + +#include +#include + +#include +#include +#include + +// forward declaration of the UML classes +class AlignToolBar; +class CodeDocument; +class CodeGenerator; +class CodeGenerationPolicy; +class CodeGenPolicyExt; +class DocWindow; +class UMLClassifier; +class UMLDoc; +class UMLListView; +class UMLView; +class WorkToolBar; +class SettingsDlg; +class UMLViewImageExporterAll; +class RefactoringAssistant; +class KPlayerPopupSliderAction; +class XhtmlGenerator; + +// KDE forward declarations +class KActionMenu; +class KRecentFilesAction; +class KStatusBarLabel; +class KToggleAction; +class KDockWidget; +class KTabWidget; +class KToolBarButton; +class KPopupMenu; + +// Qt forward declarations +class QWidgetStack; +class QMenuData; +class QClipboard; +class QToolButton; +class QCustomEvent; + +/** + * The base class for UML application windows. It sets up the main + * window and reads the config file as well as providing a menubar, toolbar + * and statusbar. An instance of UMLView creates your center view, which is connected + * to the window's Doc object. + * UMLApp reimplements the methods that KMainWindow provides for main window handling and supports + * full session management as well as using KActions. + * @see KMainWindow + * @see KApplication + * @see KConfig + * + * @author Paul Hensgen + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + +class UMLApp : public KDockMainWindow { + Q_OBJECT +public: + /** + * Constructor. Calls all init functions to create the application. + */ + UMLApp(QWidget* parent=0, const char* name=0); + + /** + * Standard deconstructor. + */ + ~UMLApp(); + + static UMLApp* app(); + + /** + * Opens a file specified by commandline option. + */ + void openDocumentFile(const KURL& url=KURL()); + + /** + * Calls the UMLDoc method to create a new Document. + */ + void newDocument(); + + /** + * Returns a pointer to the current document connected to the + * KMainWindow instance. + * Used by the View class to access the document object's methods. + */ + UMLDoc *getDocument() const; + + /** + * Returns a pointer to the list view. + * + * @return The listview being used. + */ + UMLListView* getListView(); + + /** + * Returns the toolbar being used. + * + * @return The toolbar being used. + */ + WorkToolBar* getWorkToolBar(); + + /** + * Sets whether the program has been modified. + * This will change how the program saves/exits. + * + * @param _m true - modified. + */ + void setModified(bool _m); + + /** + * Set whether to allow printing. + * It will enable/disable the menu/toolbar options. + * + * @param enable Set whether to allow printing. + */ + void enablePrint(bool enable); + + /** + * Set whether to allow printing. + * It will enable/disable the menu/toolbar options. + * + * @param enable Set whether to allow printing. + */ + void enableUndo(bool enable); + + /** + * Set whether to allow printing. + * It will enable/disable the menu/toolbar options. + * + * @param enable Set whether to allow printing. + */ + void enableRedo(bool enable); + + /** + * Returns a pointer to the documentation window. + * + * @return Pointer to the DocWindow. + */ + DocWindow * getDocWindow() { + return m_pDocWindow; + } + + /** + * Returns the undo state. + * + * @return True if Undo is enabled. + */ + bool getUndoEnabled(); + + /** + * Returns the redo state. + * + * @return True if Redo is enabled. + */ + bool getRedoEnabled(); + + /** + * Returns the paste state. + * + * @return True if Paste is enabled. + */ + bool getPasteState(); + + /** + * Returns the state on Cut/Copy. + * + * @return True if Cut/Copy is enabled. + */ + bool getCutCopyState(); + + /** + * Gets the appropriate CodeGenerator. + * + * @return Pointer to the CodeGenerator. + */ + CodeGenerator* getGenerator(); + + /** + * Set the current generator for this app. + * If giveWarning is true, then a popup box warning that the + * code generation library is out-of-date will show if you + * attempt to set the generator to NULL. + * + * @param gen Pointer to the CodeGenerator to set. + * @param giveWarning True to enable out-of-date warning. + */ + void setGenerator(CodeGenerator* gen, bool giveWarning = true); + + /** + * Creates a new code generator for the given active language. + * + * @return Pointer to the CodeGenerator created. + */ + CodeGenerator* createGenerator(); + + /** + * Auxiliary function for UMLDoc::loadExtensionsFromXMI(): + * Return the code generator of the given language if it already + * exists; if it does not yet exist then create it and return + * the newly created generator. It is the caller's responsibility + * to load XMI into the newly created generator. + */ + CodeGenerator *setGenerator(Uml::Programming_Language pl); + + /** + * Call the refactoring assistant on a classifier. + * + * @param classifier Pointer to the classifier to refactor. + */ + void refactor(UMLClassifier* classifier); + + /** + * Call the code viewing assistant on a given UMLClassifier. + * + * @param classifier Pointer to the classifier to view. + */ + void viewCodeDocument(UMLClassifier* classifier); + + /** + * Sets the state of the view properties menu item. + * + * @param bState Boolean, true to enable the view properties item. + */ + void setDiagramMenuItemsState(bool bState); + + /** + * Returns the widget used as the parent for UMLViews. + * @return The main view widget. + */ + QWidget* getMainViewWidget(); + + /** + * Puts this view to the top of the viewStack, i.e. makes it + * visible to the user. + * + * @param view Pointer to the UMLView to push. + */ + void setCurrentView(UMLView* view); + + /** + * Get the current view. + * This may return a null pointer (when no view was previously + * specified.) + * + */ + UMLView* getCurrentView(); + + /** + * Sets the default mime type for all diagrams that are exported as + * images. + * + * @param mimeType The MIME type to set as the default. + */ + void setImageMimeType(QString const & mimeType){m_imageMimeType=mimeType;}; + + /** + * Gets the default mime type for all diagrams that are exported as + * images. + * + * @return The default MIME type for images. + */ + QString const & getImageMimeType()const{return m_imageMimeType;}; + + /** + * Carries out the cut/copy command with different action performed + * depending on if from view or list view. + * Cut/Copy are the same. It is up to the caller to delete/cut the selection.. + * + * If the operation is successful, the signal sigCutSuccessful() is emitted. + * + * Callers should connect to this signal to know what to do next. + */ + bool editCutCopy( bool bFromView ); + + /** + * Return the tab widget. + */ + KTabWidget *tabWidget(); + + /** + * Returns the current text in the status bar. + * + * @return The text in the status bar. + */ + QString getStatusBarMsg(); + + /** + * Returns the default code generation policy. + */ + CodeGenerationPolicy *getCommonPolicy(); + + /** + * Sets the CodeGenPolicyExt object. + */ + void setPolicyExt(CodeGenPolicyExt *policy); + + /** + * Returns the CodeGenPolicyExt object. + */ + CodeGenPolicyExt *getPolicyExt(); + +protected: + virtual void keyPressEvent(QKeyEvent* e); + virtual void keyReleaseEvent(QKeyEvent* e); + + /** + * Event handler to receive custom events. + * It handles events such as exporting all views from command line (in + * that case, it executes the exportAllViews method in the event). + */ + virtual void customEvent(QCustomEvent* e); + + /** + * Helper method for handling cursor key release events (refactoring). + */ + void handleCursorKeyReleaseEvent(QKeyEvent* e); + + /** + * Save general Options like all bar positions and status + * as well as the geometry and the recent file list to + * the configuration file. + */ + void saveOptions(); + + /** + * Read general Options again and initialize all variables + * like the recent file list. + */ + void readOptions(); + + /** + * Initializes the KActions of the application. + */ + void initActions(); + + /** + * Sets up the statusbar for the main window by + * initialzing a statuslabel. + */ + void initStatusBar(); + + /** + * Creates the centerwidget of the KMainWindow instance and + * sets it as the view. + */ + void initView(); + + /** + * queryClose is called by KMainWindow on each closeEvent of a + * window. Counter to the default implementation (which only + * returns true), this calls saveModified() on the document object + * to ask if the document shall be saved if Modified; on cancel + * the closeEvent is rejected. + * @see KMainWindow#queryClose + * @see KMainWindow#closeEvent + * + * @return True if window may be closed. + */ + virtual bool queryClose(); + + /** + * queryExit is called by KMainWindow when the last + * window of the application is going to be closed during + * the closeEvent(). In contrast to the default + * implementation that just returns true, this calls + * saveOptions() to save the settings of the last + * window's properties. + * @see KMainWindow#queryExit + * @see KMainWindow#closeEvent + * + * @return True if window may be closed. + */ + virtual bool queryExit(); + + /** + * Saves the window properties for each open window + * during session end to the session config file, + * including saving the currently opened file by a + * temporary filename provided by KApplication. + * @see KMainWindow#saveProperties + */ + virtual void saveProperties(KConfig *_cfg); + + /** + * Reads the session config file and restores the + * application's state including the last opened files and + * documents by reading the temporary files saved by + * saveProperties() + * @see KMainWindow#readProperties + */ + virtual void readProperties(KConfig *_cfg); + + CodeGenerationPolicy * m_commoncodegenpolicy; + + /** + * Updates the Menu for language selection and sets the + * active lanugage. If no active lanugage is found or if it is + * not one of the registered languages it tries to fall back + * to Cpp + */ + void updateLangSelectMenu(Uml::Programming_Language activeLanguage); + +protected slots: + + /** + * Show "Tip of the Day" dialog + */ + void tipOfTheDay(); + +public slots: + + /** + * Reads the activeLanguage from the KConfig and calls updateLangSelectMenu() + */ + void initGenerator(); + + /** + * Runs the code generation wizard. + */ + void generationWizard(); + + /** + * Clears the document in the actual view to reuse it as the new + * document. + */ + void slotFileNew(); + + /** + * Open a file and load it into the document. + */ + void slotFileOpen(); + + /** + * Opens a file from the recent files menu. + */ + void slotFileOpenRecent(const KURL& url); + + /** + * Save a document. + */ + void slotFileSave(); + + /** + * Save a document by a new filename. + */ + bool slotFileSaveAs(); + + /** + * Asks for saving if the file is modified, then closes the current + * file and window. + */ + void slotFileClose(); + + /** + * Print the current file. + */ + void slotFilePrint(); + + /** + * Closes all open windows by calling close() on each + * memberList item until the list is empty, then quits the + * application. If queryClose() returns false because the + * user canceled the saveModified() dialog, the closing + * aborts. + */ + void slotFileQuit(); + + /** + * Exports the current model to docbook in a subdir of the + * current model directory named from the model name. + * @todo Let the user chose the destination directory and + * name, using network transparency. + */ + void slotFileExportDocbook(); + + /** + * Exports the current model to XHTML in a subdir of the + * current model directory named from the model name. + * @todo Let the user chose the destination directory and + * name, using network transparency. + */ + void slotFileExportXhtml(); + + /** + * Put the marked text/object into the clipboard and remove + * it from the document. + */ + void slotEditCut(); + + /** + * Put the marked text/object into the clipboard. + */ + void slotEditCopy(); + + /** + * Paste the clipboard into the document. + */ + void slotEditPaste(); + + /** + * Toggles the toolbar. + * Deprecated. For compatibility with KDE 3.1, remove if we stop supporting KDE 3.1 + */ + void slotViewToolBar(); + + /** + * Toggles the statusbar. + * Deprecated. For compatibility with KDE 3.1, remove if we stop supporting KDE 3.1 + */ + void slotViewStatusBar(); + + /** + * Autolayouts the current class diagram + */ + void slotAutolayout(); + + /** + * Changes the statusbar contents for the standard label + * permanently, used to indicate current actions. + * @param text The text that is displayed in the statusbar + */ + void slotStatusMsg(const QString &text); + + /** + * Create this view. + */ + void slotClassDiagram(); + + /** + * Create this view. + */ + void slotSequenceDiagram(); + + /** + * Create this view. + */ + void slotCollaborationDiagram(); + + /** + * Create this view. + */ + void slotUseCaseDiagram(); + + /** + * Create this view. + */ + void slotStateDiagram(); + + /** + * Create this view. + */ + void slotActivityDiagram(); + + /** + * Create this view. + */ + void slotComponentDiagram(); + + /** + * Create this view. + */ + void slotDeploymentDiagram(); + /** + * Create this view. + */ + void slotEntityRelationshipDiagram(); + + /** + * Notification of changed clipboard data. + */ + void slotClipDataChanged(); + + /** + * + */ + void slotCopyChanged(); + + /** + * Shows the global preferences dialog. + */ + void slotPrefs(); + + /** + * Commits the changes from the global preferences dialog. + */ + void slotApplyPrefs(); + + /** + * Register new views (aka diagram) with the GUI so they show up + * in the menu. + */ + void slotUpdateViews(); + + /** + * Generate code for all classes. + */ + void generateAllCode(); + + /** + * Set the language for which code will be generated. + * + * @param menuID the ID of the langSelect menu item for + * the relevant language. + */ + void setActiveLanguage(int menuID); + + /** + * Set the language for which code will be generated. + * + * @param activeLanguage The name of the language to set + */ + void setActiveLanguage( const QString &activeLanguage ); + + /** + * Get the language for import and code generation. + */ + Uml::Programming_Language getActiveLanguage(); + + /** + * Return true if the active language is case sensitive. + */ + bool activeLanguageIsCaseSensitive(); + + /** + * Return the target language depedent scope separator. + */ + QString activeLanguageScopeSeparator(); + + /** + * Return the default code generation language as configured by KConfig. + * If the activeLanguage is not found in the KConfig then use Uml::pl_Cpp + * as the default. + */ + Uml::Programming_Language getDefaultLanguage(); + + /** + * Menu selection for clear current view. + */ + void slotCurrentViewClearDiagram(); + + /** + * Menu selection for current view snap to grid property. + */ + void slotCurrentViewToggleSnapToGrid(); + + /** + * Menu selection for current view show grid property. + */ + void slotCurrentViewToggleShowGrid(); + + /** + * Menu selection for exporting current view as an image. + */ + void slotCurrentViewExportImage(); + + /** + * Menu selection for exporting all views as images. + */ + void slotAllViewsExportImage(); + + /** + * Menu selection for current view properties. + */ + void slotCurrentViewProperties(); + + /** + * Import classes menu selection. + */ + void slotImportClasses(); + + /** + * Class wizard menu selection. + */ + void slotClassWizard(); + + /** + * Calls the active code generator to add its default datatypes + */ + void slotAddDefaultDatatypes(); + + /** + * The displayed diagram has changed. + */ + void slotCurrentViewChanged(); + + /** + * The snap to grid value has been changed. + */ + void slotSnapToGridToggled(bool gridOn); + + /** + * The show grid value has been changed. + */ + void slotShowGridToggled(bool gridOn); + + /** + * Select all widgets on the current diagram. + */ + void slotSelectAll(); + + /** + * Deletes the selected widget. + */ + void slotDeleteSelectedWidget(); + + /** + * Deletes the current diagram. + */ + void slotDeleteDiagram(); + + /** + * Set the zoom factor of the current diagram. + * + * @param zoom Zoom factor in percentage. + */ + void setZoom(int zoom); + + /** + * Connected to by the KPlayerSliderAction zoomAction, a value of between 300 + * and 2200 is scaled to zoom to between 9% and 525%. + * The min and max values of the slider are hard coded in KPlayerSliderAction for now. + * @param value Zoom factor before scaleing + */ + void slotZoomSliderMoved(int value); + + /** + * Set zoom to 100% + */ + void slotZoom100(); + + /** + * Prepares the zoom menu for display. + */ + void setupZoomMenu(); + + /** + * Reverts the document back to the state it was prior to the + * last action performed by the user. + */ + void slotEditUndo(); + + /** + * Reverts the document back to the state it was prior to the + * last undo. + */ + void slotEditRedo(); + + /** + * Searches for a menu with the given name + * + * @param menu The QPopupMenu or QMenuBar to search through. + * @param name The name of the menu to search for (name, not text) + */ + QPopupMenu* findMenu(QMenuData* menu, const QString &name); + + /** + * called when the tab has changed + */ + void slotTabChanged(QWidget* view); + + /** + * make the tab on the left of the current one the active one + */ + void slotChangeTabLeft(); + + /** + * make the tab on the right of the current one the active one + */ + void slotChangeTabRight(); + + /** + * Move the current tab left, not implemented + */ + void slotMoveTabLeft(); + + /** + * Move the current tab right, not implemented + */ + void slotMoveTabRight(); + + KConfig *getConfig() { return m_config; } + + /** + * This slot deletes the current XHTML documentation generator as soon as + * this one signals that it has finished. + */ + void slotXhtmlDocGenerationFinished(); + +private: + static UMLApp* s_instance; + + /** + * For selecting the active language. + */ + QPopupMenu *m_langSelect; + + /** + * Popup menu for zoom selection. + */ + QPopupMenu *m_zoomSelect; + + /** + * Active language. + */ + Uml::Programming_Language m_activeLanguage; + + /** + * Active code generator. + */ + CodeGenerator *m_codegen; + + /** + * Active policy extension. + * Only used for new code generators ({Cpp,Java,Ruby}CodeGenerator) + */ + CodeGenPolicyExt *m_policyext; + + /** + * Returns whether we can decode the given mimesource + */ + static bool canDecode(const QMimeSource* mimeSource); + + /** + * Reads from the config file the options state. + * Not in @ref readOptions as it needs to be read earlier than some + * of the other options, before some items are created. + */ + void readOptionState(); + + /** + * Initialize Qt's global clipboard support for the application. + */ + void initClip(); + + /** + * Initialize code generators at startup. + * Why is this important? Because IF we don't do this, then changes + * to the UML may not be synced with the saved code generation params + * for those languages which arent currently active. + */ + void initSavedCodeGenerators(); + + /** + * The configuration object of the application. + */ + KConfig* m_config; + + /** + * View is the main widget which represents your working area. + * The View class should handle all events of the view widget. + * It is kept empty so you can create your view according to your + * application's needs by changing the view class. + */ + UMLView* m_view; + + /** + * doc represents your actual document and is created only once. + * It keeps information such as filename and does the loading and + * saving of your files. + */ + UMLDoc* m_doc; + + /** + * Listview shows the current open file. + */ + UMLListView* m_listView; + + /** + * The widget which shows the diagrams. + */ + KDockWidget* m_mainDock; + + /** + * Contains the UMLListView tree view. + */ + KDockWidget* m_listDock; + + /** + * Contains the documentation DocWindow widget. + */ + KDockWidget* m_documentationDock; + + /** + * Documentation window. + */ + DocWindow* m_pDocWindow; + + /** Refactoring assistant. */ + RefactoringAssistant* m_refactoringAssist; + + //KAction pointers to enable/disable actions + KAction* fileNew; + KAction* fileOpen; + KRecentFilesAction* fileOpenRecent; + KAction* fileSave; + KAction* fileSaveAs; + KAction* fileClose; + KAction* filePrint; + KAction* fileQuit; + KAction* fileExportDocbook; + KAction* fileExportXhtml; + + KAction* editCut; + KAction* editCopy; + KAction* editPaste; + KAction* editUndo; + KAction* editRedo; + KAction* selectAll; + KAction* preferences; + + KActionMenu* newDiagram; + KAction* classDiagram; + KAction* sequenceDiagram; + KAction* collaborationDiagram; + KAction* useCaseDiagram; + KAction* stateDiagram; + KAction* activityDiagram; + KAction* componentDiagram; + KAction* deploymentDiagram; + KAction* entityRelationshipDiagram; + KAction* viewClearDiagram; + + KToggleAction* viewSnapToGrid; + KToggleAction* viewShowGrid; + KAction* viewExportImage; + KAction* viewExportImageAll; + KAction* viewProperties; + + KAction* zoom100Action; + KPlayerPopupSliderAction* zoomAction; + + KAction* genAll; + KAction* genWizard; + KAction* importClasses; + KAction* classWizard; + KAction* deleteSelectedWidget; + KAction* deleteDiagram; +#ifdef HAVE_DOT + KAction* autolayout; +#endif + + KAction* changeTabLeft; + KAction* changeTabRight; + KAction* moveTabLeft; + KAction* moveTabRight; + KToolBarButton* m_newSessionButton; + KPopupMenu* m_diagramMenu; + QToolButton* m_closeDiagramButton; + KToggleAction* viewToolBar; + KToggleAction* viewStatusBar; + WorkToolBar* toolsbar; + QTimer* m_clipTimer; + QTimer* m_copyTimer; + AlignToolBar* m_alignToolBar; + + KStatusBarLabel* m_statusLabel; + + /** + * True if the application is opening an existing document + */ + bool m_loading; + + /** + * Shows, and is parent of, all the UMLViews (diagrams) + * if tabbed diagrams are not enabled. + */ + QWidgetStack* m_viewStack; + + /** + * Shows, and is parent of, all the UMLViews (diagrams) + * if tabbed diagrams are enabled. + */ + KTabWidget* m_tabWidget; + + /** + * Default mime type to use for image export. + */ + QString m_imageMimeType; + + /** + * the global UML settings dialog + */ + SettingsDlg* m_dlg; + + /** + * The UMLViewImageExporterAll used to export all the views. + */ + UMLViewImageExporterAll* m_imageExporterAll; + + /** + * The running XHTML documentation generator. null when no generation is + * running + */ + XhtmlGenerator* m_xhtmlGenerator; + +signals: + + /** + * Emitted when a cut operation is successful. + */ + void sigCutSuccessful(); +}; + +#endif // UML_H diff --git a/umbrello/umbrello/umlassociationlist.h b/umbrello/umbrello/umlassociationlist.h new file mode 100644 index 00000000..55810c7d --- /dev/null +++ b/umbrello/umbrello/umlassociationlist.h @@ -0,0 +1,23 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLASSOCIATIONLIST_H +#define UMLASSOCIATIONLIST_H + +#include + +// forward declaration +class UMLAssociation; + +typedef QPtrList UMLAssociationList; +typedef QPtrListIterator UMLAssociationListIt; + +#endif diff --git a/umbrello/umbrello/umlattributelist.cpp b/umbrello/umbrello/umlattributelist.cpp new file mode 100644 index 00000000..f335b7e7 --- /dev/null +++ b/umbrello/umbrello/umlattributelist.cpp @@ -0,0 +1,39 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#include "umlattributelist.h" +#include "attribute.h" +#include +#include + +void UMLAttributeList::copyInto(UMLAttributeList *rhs) const { + // Don't copy yourself. + if (rhs == this) return; + + rhs->clear(); + + // Suffering from const; we shall not modify our object. + UMLAttributeList *tmp = new UMLAttributeList(*this); + + UMLAttribute *item; + for (item = tmp->first(); item; item = tmp->next() ) + { + rhs->append((UMLAttribute*)item->clone()); + } + delete tmp; +} + + +UMLAttributeList* UMLAttributeList::clone() const { + UMLAttributeList *clone = new UMLAttributeList(); + copyInto(clone); + return clone; +} diff --git a/umbrello/umbrello/umlattributelist.h b/umbrello/umbrello/umlattributelist.h new file mode 100644 index 00000000..6dd25db2 --- /dev/null +++ b/umbrello/umbrello/umlattributelist.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLATTRIBUTELIST_H +#define UMLATTRIBUTELIST_H + +#include + +#include "attribute.h" + +//typedef QPtrList UMLAttributeList; +typedef QPtrListIterator UMLAttributeListIt; + +/** + * This sub-class adds copyInto and clone to the QPtrList + * base class. + */ +class UMLAttributeList : public QPtrList +{ +public: + + /** + * Copy the internal presentation of this object into the new + * object. + */ + virtual void copyInto (UMLAttributeList *rhs) const; + + /** + * Make a clone of this object. + */ + virtual UMLAttributeList* clone() const; +}; + + +#endif diff --git a/umbrello/umbrello/umlcanvasobject.cpp b/umbrello/umbrello/umlcanvasobject.cpp new file mode 100644 index 00000000..4b002228 --- /dev/null +++ b/umbrello/umbrello/umlcanvasobject.cpp @@ -0,0 +1,322 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "umlcanvasobject.h" + +// qt/kde includes +#include +#include + +// local includes +#include "uml.h" +#include "umldoc.h" +#include "classifier.h" +#include "association.h" +#include "attribute.h" +#include "operation.h" +#include "template.h" +#include "stereotype.h" +#include "clipboard/idchangelog.h" + +UMLCanvasObject::UMLCanvasObject(const QString & name, Uml::IDType id) + : UMLObject(name, id) +{ + init(); +} + +UMLCanvasObject::~UMLCanvasObject() { + //removeAllAssociations(); + /* No! This is way too late to do that. + It should have been called explicitly before destructing the + UMLCanvasObject. + Here is an example crash that happens if we rely on + removeAllAssociations() at this point: +#4 0x415aac7f in __dynamic_cast () from /usr/lib/libstdc++.so.5 +#5 0x081acdbd in UMLCanvasObject::removeAllAssociations() (this=0x89e5b08) + at umlcanvasobject.cpp:83 +#6 0x081ac9fa in ~UMLCanvasObject (this=0x89e5b08) at umlcanvasobject.cpp:29 +#7 0x08193ffc in ~UMLPackage (this=0x89e5b08) at package.cpp:35 +#8 0x0813cbf6 in ~UMLClassifier (this=0x89e5b08) at classifier.cpp:40 +#9 0x081af3a6 in UMLDoc::closeDocument() (this=0x8468b10) at umldoc.cpp:284 + */ + if (associations()) + kDebug() << "UMLCanvasObject destructor: FIXME: there are still associations()" << endl; +} + +UMLAssociationList UMLCanvasObject::getSpecificAssocs(Uml::Association_Type assocType) { + UMLAssociationList list; + UMLObject *o; + for (UMLObjectListIt oit(m_List); (o = oit.current()) != NULL; ++oit) { + if (o->getBaseType() != Uml::ot_Association) + continue; + UMLAssociation *a = static_cast(o); + if (a->getAssocType() == assocType) + list.append(a); + } + return list; +} + +bool UMLCanvasObject::addAssociationEnd(UMLAssociation* assoc) { + // add association only if not already present in list + if(!hasAssociation(assoc)) + { + m_List.append( assoc ); + + // Don't emit signals during load from XMI + UMLObject::emitModified(); + emit sigAssociationEndAdded(assoc); + return true; + } + return false; +} + +bool UMLCanvasObject::hasAssociation(UMLAssociation* assoc) { + if(m_List.containsRef(assoc) > 0) + return true; + return false; +} + +int UMLCanvasObject::removeAssociationEnd(UMLAssociation * assoc) { + if(!hasAssociation(assoc) || !m_List.remove(assoc)) { + kWarning() << "UMLCanvasObject::removeAssociation: " + << "can't find given assoc in list" << endl; + return -1; + } + UMLObject::emitModified(); + emit sigAssociationEndRemoved(assoc); + return m_List.count(); +} + +void UMLCanvasObject::removeAllAssociationEnds() { + UMLObject *o; + for (UMLObjectListIt oit(m_List); (o = oit.current()) != NULL; ) { + if (o->getBaseType() != Uml::ot_Association) { + ++oit; + continue; + } + UMLAssociation *assoc = static_cast(o); + //umldoc->slotRemoveUMLObject(assoc); + UMLObject* objA = assoc->getObject(Uml::A); + UMLObject* objB = assoc->getObject(Uml::B); + UMLCanvasObject *roleAObj = dynamic_cast(objA); + if (roleAObj) { + roleAObj->removeAssociationEnd(assoc); + } else if (objA) + kDebug() << "UMLCanvasObject::removeAllAssociations(" << m_Name + << "): objA " << objA->getName() << " is not a UMLCanvasObject" + << endl; + else + kDebug() << "UMLCanvasObject::removeAllAssociations(" << m_Name + << "): objA is NULL" << endl; + UMLCanvasObject *roleBObj = dynamic_cast(objB); + if (roleBObj) { + roleBObj->removeAssociationEnd(assoc); + } else if (objB) + kDebug() << "UMLCanvasObject::removeAllAssociations(" << m_Name + << "): objB " << objB->getName() << " is not a UMLCanvasObject" + << endl; + else + kDebug() << "UMLCanvasObject::removeAllAssociations(" << m_Name + << "): objB is NULL" << endl; + m_List.remove(assoc); + } +} + +void UMLCanvasObject::removeAllChildObjects() { + removeAllAssociationEnds(); + m_List.setAutoDelete(true); + m_List.clear(); + m_List.setAutoDelete(false); +} + +QString UMLCanvasObject::uniqChildName( const Uml::Object_Type type, + const QString &prefix /* = QString() */ ) { + QString currentName = prefix; + if (currentName.isEmpty()) { + switch (type) { + case Uml::ot_Association: + currentName = i18n("new_association"); + break; + case Uml::ot_Attribute: + currentName = i18n("new_attribute"); + break; + case Uml::ot_Template: + currentName = i18n("new_template"); + break; + case Uml::ot_Operation: + currentName = i18n("new_operation"); + break; + case Uml::ot_EnumLiteral: + currentName = i18n("new_literal"); + break; + case Uml::ot_EntityAttribute: + currentName = i18n("new_field"); + break; + default: + kWarning() << "uniqChildName() called for unknown child type " << type << endl; + return "ERROR_in_UMLCanvasObject_uniqChildName"; + } + } + + QString name = currentName; + for (int number = 1; findChildObject(name); ++number) { + name = currentName + '_' + QString::number(number); + } + return name; +} + +UMLObject * UMLCanvasObject::findChildObject(const QString &n, Uml::Object_Type t) { + const bool caseSensitive = UMLApp::app()->activeLanguageIsCaseSensitive(); + UMLObject *obj; + for (UMLObjectListIt oit(m_List); (obj = oit.current()) != NULL; ++oit) { + if (t != Uml::ot_UMLObject && obj->getBaseType() != t) + continue; + if (caseSensitive) { + if (obj->getName() == n) + return obj; + } else if (obj->getName().lower() == n.lower()) { + return obj; + } + } + return NULL; +} + +UMLObject* UMLCanvasObject::findChildObjectById(Uml::IDType id, bool /* considerAncestors */) { + UMLObject *o; + for (UMLObjectListIt oit(m_List); (o = oit.current()) != NULL; ++oit) { + if (o->getID() == id) + return o; + } + return 0; +} + +void UMLCanvasObject::init() { + m_List.setAutoDelete(false); +} + +bool UMLCanvasObject::operator==(UMLCanvasObject& rhs) { + if (this == &rhs) { + return true; + } + if ( !UMLObject::operator==(rhs) ) { + return false; + } + if ( m_List.count() != rhs.m_List.count() ) { + return false; + } + if ( &m_List != &(rhs.m_List) ) { + return false; + } + return true; +} + +void UMLCanvasObject::copyInto(UMLCanvasObject *rhs) const +{ + UMLObject::copyInto(rhs); + + // TODO Associations are not copied at the moment. This because + // the duplicate function (on umlwidgets) do not copy the associations. + // + //rhs->m_List = m_List; +} + +int UMLCanvasObject::associations() { + int count = 0; + UMLObject *obj; + for (UMLObjectListIt oit(m_List); (obj = oit.current()) != NULL; ++oit) { + if (obj->getBaseType() == Uml::ot_Association) + count++; + } + return count; +} + +UMLAssociationList UMLCanvasObject::getAssociations() { + UMLAssociationList assocs; + UMLObject *o; + for (UMLObjectListIt oit(m_List); (o = oit.current()) != NULL; ++oit) { + if (o->getBaseType() != Uml::ot_Association) + continue; + UMLAssociation *assoc = static_cast(o); + assocs.append(assoc); + } + return assocs; +} + +UMLClassifierList UMLCanvasObject::getSuperClasses() { + UMLClassifierList list; + UMLAssociationList assocs = getAssociations(); + for (UMLAssociation* a = assocs.first(); a; a = assocs.next()) { + if ((a->getAssocType() != Uml::at_Generalization && + a->getAssocType() != Uml::at_Realization) || + a->getObjectId(Uml::A) != getID() ) + continue; + UMLClassifier *c = dynamic_cast(a->getObject(Uml::B)); + if (c) + list.append(c); + else + kDebug() << "UMLCanvasObject::getSuperClasses(" << m_Name + << "): generalization's other end is not a " + << "UMLClassifier (id= " << ID2STR(a->getObjectId(Uml::B)) << ")" + << endl; + } + return list; +} + +UMLClassifierList UMLCanvasObject::getSubClasses() { + UMLClassifierList list; + UMLAssociationList assocs = getAssociations(); + for (UMLAssociation* a = assocs.first(); a; a = assocs.next()) { + if ((a->getAssocType() != Uml::at_Generalization && + a->getAssocType() != Uml::at_Realization) || + a->getObjectId(Uml::B) != getID() ) + continue; + UMLClassifier *c = dynamic_cast(a->getObject(Uml::A)); + if (c) + list.append(c); + else + kDebug() << "UMLCanvasObject::getSubClasses: specialization's" + << " other end is not a UMLClassifier" + << " (id=" << ID2STR(a->getObjectId(Uml::A)) << ")" << endl; + } + return list; +} + +UMLAssociationList UMLCanvasObject::getRealizations() { + return getSpecificAssocs(Uml::at_Realization); +} + +UMLAssociationList UMLCanvasObject::getAggregations() { + return getSpecificAssocs(Uml::at_Aggregation); +} + +UMLAssociationList UMLCanvasObject::getCompositions() { + return getSpecificAssocs(Uml::at_Composition); +} + +UMLAssociationList UMLCanvasObject::getRelationships() { + return getSpecificAssocs(Uml::at_Relationship); +} + +bool UMLCanvasObject::resolveRef() { + bool overallSuccess = UMLObject::resolveRef(); + for (UMLObjectListIt ait(m_List); ait.current(); ++ait) { + UMLObject *obj = ait.current(); + if (! obj->resolveRef()) { + m_List.remove(obj); + overallSuccess = false; + } + } + return overallSuccess; +} + +#include "umlcanvasobject.moc" + diff --git a/umbrello/umbrello/umlcanvasobject.h b/umbrello/umbrello/umlcanvasobject.h new file mode 100644 index 00000000..626b9fe9 --- /dev/null +++ b/umbrello/umbrello/umlcanvasobject.h @@ -0,0 +1,249 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef CANVASOBJECT_H +#define CANVASOBJECT_H + +#include "umlobject.h" +#include "umlobjectlist.h" +#include "umlclassifierlist.h" +#include "umlassociationlist.h" + + +/** + * This class contains the non-graphical information required for UMLObjects + * which appear as moveable widgets on the canvas. + * + * This class inherits from @ref UMLObject which contains most of the + * information. + * It is not instantiated itself, it's just used as a super class for + * actual model objects. + * + * @short Non-graphical information for a UMLCanvasObject. + * @author Jonathan Riddell + * @see UMLObject + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + +class UMLCanvasObject : public UMLObject { + Q_OBJECT +public: + /** + * Sets up a UMLCanvasObject. + * + * @param name The name of the Concept. + * @param id The unique id of the Concept. + */ + explicit UMLCanvasObject(const QString & name = "", Uml::IDType id = Uml::id_None); + + /** + * Standard deconstructor. + */ + virtual ~UMLCanvasObject(); + + /** + * Overloaded '==' operator + */ + virtual bool operator==(UMLCanvasObject& rhs); + + /** + * Copy the internal presentation of this object into the new + * object. + */ + virtual void copyInto(UMLCanvasObject *rhs) const; + + // The abstract method UMLObject::clone() is implemented + // in the classes inheriting from UMLCanvasObject. + + /** + * Adds an association end to m_List. + * + * @param assoc The association to add. + * @todo change param type to UMLRole + */ + bool addAssociationEnd(UMLAssociation* assoc); + + /** + * Determine if this canvasobject has the given association. + * + * @param assoc The association to check. + */ + bool hasAssociation(UMLAssociation* assoc); + + /** + * Remove an association end from the CanvasObject. + * + * @param assoc The association to remove. + * @todo change param type to UMLRole + */ + int removeAssociationEnd(UMLAssociation *assoc); + + /** + * Remove all association ends from the CanvasObject. + */ + void removeAllAssociationEnds(); + + /** + * Returns the number of associations for the CanvasObject. + * This is the sum of the aggregations and compositions. + * + * @return The number of associations for the Concept. + */ + int associations(); + + /** + * Return the list of associations for the CanvasObject. + * + * @return The list of associations for the CanvasObject. + */ + UMLAssociationList getAssociations(); + + /** + * Return the subset of m_List that matches the given type. + * + * @param assocType The Association_Type to match. + * @return The list of associations that match assocType. + */ + UMLAssociationList getSpecificAssocs(Uml::Association_Type assocType); + + /** + * Return a list of the superclasses of this concept. + * TODO: This overlaps with UMLClassifier::findSuperClassConcepts(), + * see if we can merge the two. + * + * @return The list of superclasses for the concept. + */ + UMLClassifierList getSuperClasses(); + + /** + * Return a list of the classes that inherit from this concept. + * TODO: This overlaps with UMLClassifier::findSubClassConcepts(), + * see if we can merge the two. + * + * @return The list of classes inheriting from the concept. + */ + UMLClassifierList getSubClasses(); + + /** + * Shorthand for getSpecificAssocs(Uml::at_Realization) + * + * @return The list of realizations for the Concept. + */ + virtual UMLAssociationList getRealizations(); + + /** + * Shorthand for getSpecificAssocs(Uml::at_Aggregation) + * + * @return The list of aggregations for the Concept. + */ + UMLAssociationList getAggregations(); + + /** + * Shorthand for getSpecificAssocs(Uml::at_Composition) + * + * @return The list of compositions for the Concept. + */ + UMLAssociationList getCompositions(); + + /** + * Shorthand for getSpecificAssocs(Uml::at_Relationship) + * + * @return The list of relationships for the entity. + */ + UMLAssociationList getRelationships(); + + /** + * Find a child object with the given name. + * + * @param n The name of the object to find. + * @param t The type to find (optional.) If not given then + * any object type will match. + * @return Pointer to the object found; NULL if none found. + */ + virtual UMLObject *findChildObject(const QString &n, Uml::Object_Type t = Uml::ot_UMLObject); + + /** + * Find an association. + * + * @param id The id of the object to find. + * @param considerAncestors boolean switch to consider ancestors while searching + * @return Pointer to the object found (NULL if not found.) + */ + virtual UMLObject *findChildObjectById(Uml::IDType id, bool considerAncestors = false); + + /** + * Returns a name for the new association, operation, template + * or attribute appended with a number if the default name is + * taken e.g. new_association, new_association_1 etc. + * + * @param type The object type for which to make a name. + * @param prefix Optional prefix to use for the name. + * If not given then uniqChildName() will choose the prefix + * internally based on the object type. + * @return Unique name string for the Object_Type given. + */ + virtual QString uniqChildName(const Uml::Object_Type type, + const QString &prefix = QString()); + + virtual void removeAllChildObjects(); + + /** + * Return the list of subordinate items. + */ + UMLObjectList subordinates() const { + return m_List; + } + + /** + * Reimplementation of UMLObject method. + */ + virtual bool resolveRef(); + + // The abstract method UMLObject::saveToXMI() is implemented + // in the classes inheriting from UMLCanvasObject. + +protected: + + /** + * List of all the associations in this object. + * Inheriting classes add more types of objects that are possible in this list; + * for example, UMLClassifier adds operations, attributes, and templates. + * + * @todo Only a pointer to the appropriate assocation end object + * (UMLRole) should be saved here, not the entire UMLAssociation. + * + */ + UMLObjectList m_List; + +private: + + /** + * Initialises key variables of the class. + */ + void init(); + +signals: + + /** + * Emit when new association is added. + * @param assoc Pointer to the association which has been added. + */ + void sigAssociationEndAdded(UMLAssociation * assoc); + + /** + * Emit when new association is removed. + * @param assoc Pointer to the association which has been removed. + */ + void sigAssociationEndRemoved(UMLAssociation * assoc); + +}; + +#endif diff --git a/umbrello/umbrello/umlclassifierlist.h b/umbrello/umbrello/umlclassifierlist.h new file mode 100644 index 00000000..45bd2b62 --- /dev/null +++ b/umbrello/umbrello/umlclassifierlist.h @@ -0,0 +1,23 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLCLASSIFIERLIST_H +#define UMLCLASSIFIERLIST_H + +#include + +// forward declaration +class UMLClassifier; + +typedef QPtrList UMLClassifierList; +typedef QPtrListIterator UMLClassifierListIt; + +#endif diff --git a/umbrello/umbrello/umlclassifierlistitemlist.cpp b/umbrello/umbrello/umlclassifierlistitemlist.cpp new file mode 100644 index 00000000..1672c0cb --- /dev/null +++ b/umbrello/umbrello/umlclassifierlistitemlist.cpp @@ -0,0 +1,42 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#include "umlclassifierlistitemlist.h" +#include "classifierlistitem.h" +#include +#include + +void UMLClassifierListItemList::copyInto(UMLClassifierListItemList *rhs) const { + // Prevent copying to yourself. (Can cause serious injuries) + if (rhs == this) return; + + rhs->clear(); + + // Suffering from const; we shall not modify our object. + UMLClassifierListItemList *tmp = new UMLClassifierListItemList(*this); + + UMLClassifierListItem *item; + for (item = tmp->first(); item; item = tmp->next() ) + { + rhs->append((UMLClassifierListItem*)item->clone()); + } + delete tmp; +} + + +UMLClassifierListItemList* UMLClassifierListItemList::clone() const { + UMLClassifierListItemList *clone = new UMLClassifierListItemList(); + copyInto(clone); + return clone; +} + + + diff --git a/umbrello/umbrello/umlclassifierlistitemlist.h b/umbrello/umbrello/umlclassifierlistitemlist.h new file mode 100644 index 00000000..675e8d5c --- /dev/null +++ b/umbrello/umbrello/umlclassifierlistitemlist.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLCLASSIFIERLISTITEMLIST_H +#define UMLCLASSIFIERLISTITEMLIST_H + +#include + +// forward declaration +class UMLClassifierListItem; + +//typedef QPtrList UMLClassifierListItemList; +typedef QPtrListIterator UMLClassifierListItemListIt; + +/** + * This sub-class adds copyInto and clone to the QPtrList + * base class. + */ +class UMLClassifierListItemList : public QPtrList +{ + +public: + + /** + * Copy the internal presentation of this object into the new + * object. + */ + virtual void copyInto (UMLClassifierListItemList *rhs) const; + + /** + * Make a clone of this object. + */ + virtual UMLClassifierListItemList* clone() const; + +}; + +#endif diff --git a/umbrello/umbrello/umldoc.cpp b/umbrello/umbrello/umldoc.cpp new file mode 100644 index 00000000..9783cfbd --- /dev/null +++ b/umbrello/umbrello/umldoc.cpp @@ -0,0 +1,2356 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "umldoc.h" + +// qt includes +#include +#include +#include +#include +#include +#include +#include + +// kde includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// app includes +#include "uniqueid.h" +#include "associationwidget.h" +#include "association.h" +#include "package.h" +#include "folder.h" +#include "codegenerator.h" +#include "classifier.h" +#include "enum.h" +#include "entity.h" +#include "docwindow.h" +#include "operation.h" +#include "attribute.h" +#include "template.h" +#include "enumliteral.h" +#include "entityattribute.h" +#include "stereotype.h" +#include "classifierlistitem.h" +#include "object_factory.h" +#include "import_rose.h" +#include "model_utils.h" +#include "widget_utils.h" +#include "uml.h" +#include "umllistview.h" +#include "umllistviewitem.h" +#include "umlview.h" +#include "clipboard/idchangelog.h" +#include "dialogs/classpropdlg.h" +#include "codegenerators/codegenfactory.h" +#include "listpopupmenu.h" +#include "version.h" + +#define XMI_FILE_VERSION UMBRELLO_VERSION +// For the moment, the XMI_FILE_VERSION changes with each UMBRELLO_VERSION. +// But someday that may stabilize ;) + +using namespace Uml; + +static const uint undoMax = 30; + +UMLDoc::UMLDoc() { + m_Name = i18n("UML Model"); + m_modelID = "m1"; + m_count = 0; + m_pChangeLog = 0; + m_Doc = ""; + m_modified = false; + m_bLoading = false; + m_bTypesAreResolved = false; + m_pAutoSaveTimer = 0; + m_nViewID = Uml::id_None; + m_pTabPopupMenu = 0; + m_pCurrentRoot = NULL; +} + +void UMLDoc::init() { + // Initialize predefined folders. + const QString nativeRootName[Uml::N_MODELTYPES] = { + "Logical View", + "Use Case View", + "Component View", + "Deployment View", + "Entity Relationship Model" + }; + const QString localizedRootName[Uml::N_MODELTYPES] = { + i18n("Logical View"), + i18n("Use Case View"), + i18n("Component View"), + i18n("Deployment View"), + i18n("Entity Relationship Model") + }; + for (int i = 0; i < Uml::N_MODELTYPES; i++) { + m_root[i] = new UMLFolder(nativeRootName[i], STR2ID(nativeRootName[i])); + m_root[i]->setLocalName(localizedRootName[i]); + } + m_datatypeRoot = new UMLFolder("Datatypes", "Datatypes"); + m_datatypeRoot->setLocalName(i18n("Datatypes")); + m_datatypeRoot->setUMLPackage(m_root[Uml::mt_Logical]); + m_root[Uml::mt_Logical]->addObject(m_datatypeRoot); + + // Connect signals. + UMLApp * pApp = UMLApp::app(); + connect(this, SIGNAL(sigDiagramCreated(Uml::IDType)), pApp, SLOT(slotUpdateViews())); + connect(this, SIGNAL(sigDiagramRemoved(Uml::IDType)), pApp, SLOT(slotUpdateViews())); + connect(this, SIGNAL(sigDiagramRenamed(Uml::IDType)), pApp, SLOT(slotUpdateViews())); + connect(this, SIGNAL( sigCurrentViewChanged() ), pApp, SLOT( slotCurrentViewChanged() ) ); +} + +UMLDoc::~UMLDoc() { + delete m_pChangeLog; + m_pChangeLog = 0; +} + +void UMLDoc::addView(UMLView *view) { + if (view == NULL) { + kError() << "UMLDoc::addView: argument is NULL" << endl; + return; + } + UMLFolder *f = view->getFolder(); + if (f == NULL) { + kError() << "UMLDoc::addView: view folder is not set" << endl; + return; + } + f->addView(view); + + UMLApp * pApp = UMLApp::app(); + if ( pApp->getListView() ) + connect(this, SIGNAL(sigObjectRemoved(UMLObject *)), view, SLOT(slotObjectRemoved(UMLObject *))); + + pApp->setCurrentView(view); + if ( ! m_bLoading ) { + view -> show(); + emit sigDiagramChanged(view ->getType()); + } + + Settings::OptionState optionState = Settings::getOptionState(); + KTabWidget* tabWidget = NULL; + if (optionState.generalState.tabdiagrams) { + tabWidget = UMLApp::app()->tabWidget(); + tabWidget->addTab(view, view->getName()); + tabWidget->setTabIconSet(view, Widget_Utils::iconSet(view->getType())); + } + pApp->setDiagramMenuItemsState(true); + pApp->slotUpdateViews(); + pApp->setCurrentView(view); + if (tabWidget) { + tabWidget->showPage(view); + tabWidget->setCurrentPage(tabWidget->currentPageIndex()); + } +} + +void UMLDoc::removeView(UMLView *view , bool enforceCurrentView ) { + if(!view) + { + kError()<<"UMLDoc::removeView(UMLView *view) called with view = 0"<getListView() ) { + disconnect(this,SIGNAL(sigObjectRemoved(UMLObject *)), view,SLOT(slotObjectRemoved(UMLObject *))); + } + view->hide(); + //remove all widgets before deleting view + view->removeAllWidgets(); + UMLFolder *f = view->getFolder(); + if (f == NULL) { + kError() << "UMLDoc::removeView(" << view->getName() + << "): view->getFolder() returns NULL" << endl; + return; + } + f->removeView(view); + UMLView *currentView = UMLApp::app()->getCurrentView(); + if (currentView == view) + { + UMLApp::app()->setCurrentView(NULL); + UMLViewList viewList; + m_root[mt_Logical]->appendViews(viewList); + UMLView* firstView = viewList.first(); + if (!firstView && enforceCurrentView) //create a diagram + { + createDiagram(m_root[mt_Logical], dt_Class, false); + kapp->processEvents(); + m_root[mt_Logical]->appendViews(viewList); + firstView = viewList.first(); + } + + if ( firstView ) + { + changeCurrentView( firstView->getID() ); + UMLApp::app()->setDiagramMenuItemsState(true); + } + } +} + +void UMLDoc::setURL(const KURL &url) { + m_doc_url = url; + return; +} + +const KURL& UMLDoc::URL() const { + return m_doc_url; +} + +bool UMLDoc::saveModified() { + bool completed(true); + if (!m_modified) + return completed; + + UMLApp *win = UMLApp::app(); + int want_save = KMessageBox::warningYesNoCancel(win, i18n("The current file has been modified.\nDo you want to save it?"), i18n("Warning"),KStdGuiItem::save(),KStdGuiItem::discard()); + switch(want_save) { + case KMessageBox::Yes: + if (m_doc_url.fileName() == i18n("Untitled")) { + if (win->slotFileSaveAs()) { + closeDocument(); + completed=true; + } else { + completed=false; + } + } else { + saveDocument(URL()); + closeDocument(); + completed=true; + } + break; + + case KMessageBox::No: + setModified(false); + closeDocument(); + completed=true; + break; + + case KMessageBox::Cancel: + completed=false; + break; + + default: + completed=false; + break; + } + return completed; +} + +void UMLDoc::closeDocument() { + UMLApp::app()->setGenerator(Uml::pl_Reserved); // delete the codegen + m_Doc = ""; + DocWindow* dw = UMLApp::app()->getDocWindow(); + if (dw) { + dw->newDocumentation(); + } + + UMLListView *listView = UMLApp::app()->getListView(); + if (listView) { + listView->init(); + // store old setting - for restore of last setting + bool m_bLoading_old = m_bLoading; + m_bLoading = true; // This is to prevent document becoming modified. + // For reference, here is an example of a call sequence that would + // otherwise result in futile addToUndoStack() calls: + // removeAllViews() => + // UMLView::removeAllAssociations() => + // UMLView::removeAssoc() => + // UMLDoc::setModified(true, true) => + // addToUndoStack(). + removeAllViews(); + m_bLoading = m_bLoading_old; + // Remove all objects from the predefined folders. + // @fixme With advanced code generation enabled, this crashes. + UMLObject *obj; + for (int i = 0; i < Uml::N_MODELTYPES; i++) + m_root[i]->removeAllObjects(); + // Restore the datatype folder, it has been deleted above. + m_datatypeRoot = new UMLFolder("Datatypes", "Datatypes"); + m_datatypeRoot->setLocalName(i18n("Datatypes")); + m_datatypeRoot->setUMLPackage(m_root[Uml::mt_Logical]); + m_root[Uml::mt_Logical]->addObject(m_datatypeRoot); + listView->theDatatypeFolder()->setUMLObject(m_datatypeRoot); + /* Remove any stereotypes. + if (m_stereoList.count() > 0) { + UMLStereotype *s; + for (UMLStereotypeListIt sit(m_stereoList); (s = sit.current()) != 0; ++sit) + delete s; + m_stereoList.clear(); + } + */ + } + m_bTypesAreResolved = false; +} + +bool UMLDoc::newDocument() { + closeDocument(); + UMLApp::app()->setCurrentView(NULL); + m_doc_url.setFileName(i18n("Untitled")); + //see if we need to start with a new diagram + Settings::OptionState optionState = Settings::getOptionState(); + Uml::Diagram_Type dt = optionState.generalState.diagram; + Uml::Model_Type mt = Model_Utils::convert_DT_MT(dt); + if (mt == Uml::N_MODELTYPES) { // don't allow no diagram + dt = Uml::dt_Class; + mt = Uml::mt_Logical; + } + createDiagram(m_root[mt], dt, false); + + UMLApp::app()->initGenerator(); + addDefaultDatatypes(); + addDefaultStereotypes(); + + setModified(false); + initSaveTimer(); + + UMLApp::app()->enableUndo(false); + clearUndoStack(); + addToUndoStack(); + + return true; +} + +bool UMLDoc::openDocument(const KURL& url, const char* /*format =0*/) { + if(url.fileName().length() == 0) { + newDocument(); + return false; + } + + m_doc_url = url; + QDir d = url.path(1); + closeDocument(); + // IMPORTANT: set m_bLoading to true + // _AFTER_ the call of UMLDoc::closeDocument() + // as it sets m_bLoading to false afer it was temporarily + // changed to true to block recording of changes in redo-buffer + m_bLoading = true; + QString tmpfile; + KIO::NetAccess::download( url, tmpfile, UMLApp::app() ); + QFile file( tmpfile ); + if ( !file.exists() ) { + KMessageBox::error(0, i18n("The file %1 does not exist.").arg(d.path()), i18n("Load Error")); + m_doc_url.setFileName(i18n("Untitled")); + m_bLoading = false; + newDocument(); + return false; + } + + // status of XMI loading + bool status = false; + + // check if the xmi file is a compressed archive like tar.bzip2 or tar.gz + QString filetype = m_doc_url.fileName(true); + QString mimetype = ""; + if (filetype.find(QRegExp("\\.tgz$")) != -1) + { + mimetype = "application/x-gzip"; + } else if (filetype.find(QRegExp("\\.tar.bz2$")) != -1) { + mimetype = "application/x-bzip2"; + } + + if (mimetype.isEmpty() == false) + { + KTar archive(tmpfile, mimetype); + if (archive.open(IO_ReadOnly) == false) + { + KMessageBox::error(0, i18n("The file %1 seems to be corrupted.").arg(d.path()), i18n("Load Error")); + m_doc_url.setFileName(i18n("Untitled")); + m_bLoading = false; + newDocument(); + return false; + } + + // get the root directory and all entries in + const KArchiveDirectory * rootDir = archive.directory(); + QStringList entries = rootDir->entries(); + QString entryMimeType; + bool foundXMI = false; + QStringList::Iterator it; + QStringList::Iterator end(entries.end()); + + // now go through all entries till we find an xmi file + for (it = entries.begin(); it != end; ++it) + { + // only check files, we do not go in subdirectories + if (rootDir->entry(*it)->isFile() == true) + { + // we found a file, check the mimetype + entryMimeType = KMimeType::findByPath(*it, 0, true)->name(); + if (entryMimeType == "application/x-uml") + { + foundXMI = true; + break; + } + } + } + + // if we found an XMI file, we have to extract it to a temporary file + if (foundXMI == true) + { + KTempDir tmp_dir; + KArchiveEntry * entry; + KArchiveFile * fileEntry; + + // try to cast the file entry in the archive to an archive entry + entry = const_cast(rootDir->entry(*it)); + if (entry == 0) + { + KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.").arg(d.path()), i18n("Load Error")); + m_doc_url.setFileName(i18n("Untitled")); + m_bLoading = false; + newDocument(); + return false; + } + + // now try to cast the archive entry to a file entry, so that we can + // extract the file + fileEntry = dynamic_cast(entry); + if (fileEntry == 0) + { + KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.").arg(d.path()), i18n("Load Error")); + m_doc_url.setFileName(i18n("Untitled")); + m_bLoading = false; + newDocument(); + return false; + } + + // now we can extract the file to the temporary directory + fileEntry->copyTo(tmp_dir.name()); + + // now open the extracted file for reading + QFile xmi_file(tmp_dir.name() + *it); + if( !xmi_file.open( IO_ReadOnly ) ) + { + KMessageBox::error(0, i18n("There was a problem loading the extracted file: %1").arg(d.path()), i18n("Load Error")); + m_doc_url.setFileName(i18n("Untitled")); + m_bLoading = false; + newDocument(); + return false; + } + status = loadFromXMI( xmi_file, ENC_UNKNOWN ); + + // close the extracted file and the temporary directory + xmi_file.close(); + tmp_dir.unlink(); + + } else { + KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.").arg(d.path()), i18n("Load Error")); + m_doc_url.setFileName(i18n("Untitled")); + m_bLoading = false; + newDocument(); + return false; + } + + archive.close(); + } else { + // no, it seems to be an ordinary file + if( !file.open( IO_ReadOnly ) ) { + KMessageBox::error(0, i18n("There was a problem loading file: %1").arg(d.path()), i18n("Load Error")); + m_doc_url.setFileName(i18n("Untitled")); + m_bLoading = false; + newDocument(); + return false; + } + if (filetype.endsWith(".mdl")) + status = Import_Rose::loadFromMDL(file); + else + status = loadFromXMI( file, ENC_UNKNOWN ); + } + + file.close(); + KIO::NetAccess::removeTempFile( tmpfile ); + if( !status ) + { + KMessageBox::error(0, i18n("There was a problem loading file: %1").arg(d.path()), i18n("Load Error")); + m_bLoading = false; + newDocument(); + return false; + } + setModified(false); + m_bLoading = false; + initSaveTimer(); + + UMLApp::app()->enableUndo(false); + clearUndoStack(); + addToUndoStack(); + // for compatibility + addDefaultStereotypes(); + + return true; +} + +bool UMLDoc::saveDocument(const KURL& url, const char * /* format */) { + m_doc_url = url; + QDir d = m_doc_url.path(1); + QFile file; + bool uploaded = true; + + // first, we have to find out which format to use + QString strFileName = url.path(-1); + QFileInfo fileInfo(strFileName); + QString fileExt = fileInfo.extension(); + QString fileFormat = "xmi"; + if (fileExt == "xmi" || fileExt == "bak.xmi") + { + fileFormat = "xmi"; + } else if (fileExt == "xmi.tgz" || fileExt == "bak.xmi.tgz") { + fileFormat = "tgz"; + } else if (fileExt == "xmi.tar.bz2" || fileExt == "bak.xmi.tar.bz2") { + fileFormat = "bz2"; + } else { + fileFormat = "xmi"; + } + + initSaveTimer(); + + if (fileFormat == "tgz" || fileFormat == "bz2") + { + KTar * archive; + KTempFile tmp_tgz_file; + + // first we have to check if we are saving to a local or remote file + if (url.isLocalFile()) + { + if (fileFormat == "tgz") // check tgz or bzip2 + { + archive = new KTar(d.path(), "application/x-gzip"); + } else { + archive = new KTar(d.path(), "application/x-bzip2"); + } + } else { + if (fileFormat == "tgz") // check tgz or bzip2 + { + archive = new KTar(tmp_tgz_file.name(), "application/x-gzip"); + } else { + archive = new KTar(tmp_tgz_file.name(), "application/x-bzip2"); + } + } + + // now check if we can write to the file + if (archive->open(IO_WriteOnly) == false) + { + KMessageBox::error(0, i18n("There was a problem saving file: %1").arg(d.path()), i18n("Save Error")); + return false; + } + + // we have to create a temporary xmi file + // we will add this file later to the archive + KTempFile tmp_xmi_file; + file.setName(tmp_xmi_file.name()); + if( !file.open( IO_WriteOnly ) ) { + KMessageBox::error(0, i18n("There was a problem saving file: %1").arg(d.path()), i18n("Save Error")); + return false; + } + saveToXMI(file); // save XMI to this file... + file.close(); // ...and close it + + // now add this file to the archive, but without the extension + QString tmpQString = url.fileName(); + if (fileFormat == "tgz") + { + tmpQString.replace(QRegExp("\\.tgz$"), ""); + } else { + tmpQString.replace(QRegExp("\\.tar\\.bz2$"), ""); + } + archive->addLocalFile(tmp_xmi_file.name(), tmpQString); + archive->close(); + +#if KDE_IS_VERSION(3,4,89) + if (!archive->closeSucceeded()) + { + KMessageBox::error(0, i18n("There was a problem saving file: %1").arg(d.path()), i18n("Save Error")); + return false; + } +#endif + // now the xmi file was added to the archive, so we can delete it + tmp_xmi_file.close(); + tmp_xmi_file.unlink(); + + // now we have to check, if we have to upload the file + if ( !url.isLocalFile() ) { + uploaded = KIO::NetAccess::upload( tmp_tgz_file.name(), m_doc_url, + UMLApp::app() ); + } + + // now the archive was written to disk (or remote) so we can delete the + // objects + tmp_tgz_file.close(); + tmp_tgz_file.unlink(); + delete archive; + + } else { + // save as normal uncompressed XMI + + KTempFile tmpfile; // we need this tmp file if we are writing to a remote file + + // save in _any_ case to a temp file + // -> if something goes wrong during saveToXmi, the + // original content is preserved + // ( e.g. if umbrello dies in the middle of the document model parsing + // for saveToXMI due to some problems ) + /// @TODO insert some checks in saveToXMI to detect a failed save attempt + file.setName( tmpfile.name() ); + + // lets open the file for writing + if( !file.open( IO_WriteOnly ) ) { + KMessageBox::error(0, i18n("There was a problem saving file: %1").arg(d.path()), i18n("Save Error")); + return false; + } + saveToXMI(file); // save the xmi stuff to it + file.close(); + tmpfile.close(); + + // if it is a remote file, we have to upload the tmp file + if ( !url.isLocalFile() ) { + uploaded = KIO::NetAccess::upload( tmpfile.name(), m_doc_url, UMLApp::app() ); + } else { + // now remove the original file + if ( KIO::NetAccess::file_move( tmpfile.name(), d.path(), -1, true ) == false ) { + KMessageBox::error(0, i18n("There was a problem saving file: %1").arg(d.path()), i18n("Save Error")); + m_doc_url.setFileName(i18n("Untitled")); + return false; + } + } + } + if( !uploaded ) + { + KMessageBox::error(0, i18n("There was a problem uploading file: %1").arg(d.path()), i18n("Save Error")); + m_doc_url.setFileName(i18n("Untitled")); + } + setModified(false); + return uploaded; +} + +void UMLDoc::setupSignals() { + WorkToolBar *tb = UMLApp::app() -> getWorkToolBar(); + + + connect(this, SIGNAL(sigDiagramChanged(Uml::Diagram_Type)), tb, SLOT(slotCheckToolBar(Uml::Diagram_Type))); + //new signals below + + return; +} + +UMLView * UMLDoc::findView(Uml::IDType id) { + UMLView *v = NULL; + for (int i = 0; i < Uml::N_MODELTYPES; i++) { + v = m_root[i]->findView(id); + if (v) + break; + } + return v; +} + +UMLView * UMLDoc::findView(Uml::Diagram_Type type, const QString &name, + bool searchAllScopes /* =false */) { + Uml::Model_Type mt = Model_Utils::convert_DT_MT(type); + return m_root[mt]->findView(type, name, searchAllScopes); +} + +UMLObject* UMLDoc::findObjectById(Uml::IDType id) { + UMLObject *o = NULL; + for (int i = 0; i < Uml::N_MODELTYPES; i++) { + if (id == m_root[i]->getID()) + return m_root[i]; + o = m_root[i]->findObjectById(id); + if (o) + return o; + } + o = findStereotypeById(id); + return o; +} + +UMLStereotype * UMLDoc::findStereotypeById(Uml::IDType id) { + for (UMLStereotype *s = m_stereoList.first(); s; s = m_stereoList.next() ) { + if (s->getID() == id) + return s; + } + return NULL; +} + +UMLObject* UMLDoc::findUMLObject(const QString &name, + Uml::Object_Type type /* = ot_UMLObject */, + UMLObject *currentObj /* = NULL */) { + UMLObject *o = m_datatypeRoot->findObject(name); + if (o) + return o; + for (int i = 0; i < Uml::N_MODELTYPES; i++) { + UMLObjectList list = m_root[i]->containedObjects(); + o = Model_Utils::findUMLObject(list, name, type, currentObj); + if (o) + return o; + if ((type == ot_UMLObject || type == ot_Folder) && + name == m_root[i]->getName()) + return m_root[i]; + } + return NULL; +} + +UMLClassifier* UMLDoc::findUMLClassifier(const QString &name) { + //this is used only by code generator so we don't need to look at Datatypes + UMLObject * obj = findUMLObject(name); + return dynamic_cast(obj); +} + +/** + * Adds a UMLObject thats already created but doesn't change + * any ids or signal. Used by the list view. Use + * AddUMLObjectPaste if pasting. + */ +bool UMLDoc::addUMLObject(UMLObject* object) { + Object_Type ot = object->getBaseType(); + if (ot == ot_Attribute || ot == ot_Operation || ot == ot_EnumLiteral + || ot == ot_EntityAttribute || ot == ot_Template || ot == ot_Stereotype) { + kDebug() << "UMLDoc::addUMLObject(" << object->getName() + << "): not adding type " << ot << endl; + return false; + } + UMLPackage *pkg = object->getUMLPackage(); + if (pkg == NULL) { + pkg = currentRoot(); + kDebug() << "UMLDoc::addUMLObject(" << object->getName() + << "): no parent package set, assuming " << pkg->getName() << endl; + object->setUMLPackage( pkg ); + } + return pkg->addObject(object); +} + +void UMLDoc::addStereotype(const UMLStereotype *s) { + if (! m_stereoList.contains(s)) + m_stereoList.append(s); +} + +void UMLDoc::removeStereotype(const UMLStereotype *s) { + if (m_stereoList.contains(s)) + m_stereoList.remove(s); +} + +void UMLDoc::writeToStatusBar(const QString &text) { + emit sigWriteToStatusBar(text); +} + +// simple removal of an object +void UMLDoc::slotRemoveUMLObject(UMLObject* object) { + //m_objectList.remove(object); + UMLPackage *pkg = object->getUMLPackage(); + if (pkg == NULL) { + kError() << "UMLDoc::slotRemoveUMLObject(" << object->getName() + << "): parent package is not set !" << endl; + return; + } + pkg->removeObject(object); +} + +bool UMLDoc::isUnique(const QString &name) +{ + UMLListView *listView = UMLApp::app()->getListView(); + UMLListViewItem *currentItem = (UMLListViewItem*)listView->currentItem(); + UMLListViewItem *parentItem = 0; + + // check for current item, if its a package, then we do a check on that + // otherwise, if current item exists, find its parent and check if thats + // a package.. + if(currentItem) + { + // its possible that the current item *is* a package, then just + // do check now + if (Model_Utils::typeIsContainer(currentItem->getType())) + return isUnique (name, (UMLPackage*) currentItem->getUMLObject()); + parentItem = (UMLListViewItem*)currentItem->parent(); + } + + // item is in a package so do check only in that + if (parentItem != NULL && Model_Utils::typeIsContainer(parentItem->getType())) { + UMLPackage *parentPkg = static_cast(parentItem->getUMLObject()); + return isUnique(name, parentPkg); + } + + kError() << "UMLDoc::isUnique(" << name << "): Not currently in a package" + << endl; + /* Check against all objects that _don't_ have a parent package. + for (UMLObjectListIt oit(m_objectList); oit.current(); ++oit) { + UMLObject *obj = oit.current(); + if (obj->getUMLPackage() == NULL && obj->getName() == name) + return false; + } + */ + return true; +} + +bool UMLDoc::isUnique(const QString &name, UMLPackage *package) +{ + // if a package, then only do check in that + if (package) + return (package->findObject(name) == NULL); + + // Not currently in a package: ERROR + kError() << "UMLDoc::isUnique(2)(" << name << "): Not currently in a package" + << endl; + /* Check against all objects that _don't_ have a parent package. + for (UMLObjectListIt oit(m_objectList); oit.current(); ++oit) { + UMLObject *obj = oit.current(); + if (obj->getUMLPackage() == NULL && obj->getName() == name) + return false; + } + */ + return true; +} + +UMLStereotype* UMLDoc::findStereotype(const QString &name) { + UMLStereotype *s; + for (UMLStereotypeListIt it(m_stereoList); (s = it.current()) != NULL; ++it) { + if (s->getName() == name) + return s; + } + return NULL; +} + +UMLStereotype* UMLDoc::findOrCreateStereotype(const QString &name) { + UMLStereotype *s = findStereotype(name); + if (s != NULL) { + return s; + } + s = new UMLStereotype(name, STR2ID(name)); + addStereotype(s); + //emit modified(); + return s; +} + +void UMLDoc::removeAssociation (UMLAssociation * assoc, bool doSetModified /*=true*/) { + if(!assoc) + return; + + // Remove the UMLAssociation from m_objectList. + UMLPackage *pkg = assoc->getUMLPackage(); + if (pkg == NULL) { + kError() << "UMLDoc::removeAssociation(" << assoc->getName() + << "): parent package is not set !" << endl; + return; + } + pkg->removeObject(assoc); + + if (doSetModified) // so we will save our document + setModified(true, false); +} + +UMLAssociation * UMLDoc::findAssociation(Uml::Association_Type assocType, + const UMLObject *roleAObj, + const UMLObject *roleBObj, + bool *swap) +{ + UMLAssociationList assocs = getAssociations(); + UMLAssociation *a, *ret = NULL; + for (a = assocs.first(); a; a = assocs.next()) { + if (a->getAssocType() != assocType) + continue; + if (a->getObject(Uml::A) == roleAObj && a->getObject(Uml::B) == roleBObj) + return a; + if (a->getObject(Uml::A) == roleBObj && a->getObject(Uml::B) == roleAObj) { + ret = a; + } + } + if (swap) + *swap = (ret != NULL); + return ret; +} + +// create AND add an association. Used by refactoring assistant. +UMLAssociation* UMLDoc::createUMLAssociation(UMLObject *a, UMLObject *b, Uml::Association_Type type) +{ + bool swap; + UMLAssociation *assoc = findAssociation(type, a, b, &swap); + if (assoc == NULL) { + assoc = new UMLAssociation(type, a, b ); + addAssociation(assoc); + } + return assoc; +} + +void UMLDoc::addAssociation(UMLAssociation *Assoc) +{ + if (Assoc == NULL) + return; + + // First, check that this association has not already been added. + // This may happen when loading old XMI files where all the association + // information was taken from the tag. + UMLAssociationList assocs = getAssociations(); + for (UMLAssociationListIt ait(assocs); ait.current(); ++ait) { + UMLAssociation *a = ait.current(); + // check if its already been added (shouldn't be the case right now + // as UMLAssociations only belong to one associationwidget at a time) + if (a == Assoc) + { + kDebug() << "UMLDoc::addAssociation: duplicate addition attempted" + << endl; + return; + } + } + + // If we get here it's really a new association. + + // Add the UMLAssociation at the owning UMLPackage. + UMLPackage *pkg = Assoc->getUMLPackage(); + if (pkg == NULL) { + kError() << "UMLDoc::addAssociation(" << Assoc->getName() + << "): parent package is not set !" << endl; + return; + } + pkg->addObject(Assoc); + + // I don't believe this appropriate, UMLAssociations ARENT UMLWidgets -b.t. + // emit sigObjectCreated(o); + + setModified(true); +} + +QString UMLDoc::uniqViewName(const Uml::Diagram_Type type) { + QString dname; + if(type == dt_UseCase) + dname = i18n("use case diagram"); + else if(type == dt_Class) + dname = i18n("class diagram"); + else if(type == dt_Sequence) + dname = i18n("sequence diagram"); + else if(type == dt_Collaboration) + dname = i18n("collaboration diagram"); + else if( type == dt_State ) + dname = i18n( "state diagram" ); + else if( type == dt_Activity ) + dname = i18n( "activity diagram" ); + else if( type == dt_Component ) + dname = i18n( "component diagram" ); + else if( type == dt_Deployment ) + dname = i18n( "deployment diagram" ); + else if( type == dt_EntityRelationship ) + dname = i18n( "entity relationship diagram" ); + else { + kWarning() << "uniqViewName() called with unknown diagram type" << endl; + } + QString name = dname; + for (int number = 0; findView(type, name, true); ++number, + name = dname + '_' + QString::number(number)) + ; + return name; +} + +bool UMLDoc::loading() const { + return m_bLoading; +} + +void UMLDoc::setLoading(bool state /* = true */) { + m_bLoading = state; +} + +UMLView* UMLDoc::createDiagram(UMLFolder *folder, Uml::Diagram_Type type, bool askForName /*= true */) { + bool ok = true; + QString name, + dname = uniqViewName(type); + + while(true) { + if (askForName) { + name = KInputDialog::getText(i18n("Name"), i18n("Enter name:"), dname, &ok, (QWidget*)UMLApp::app()); + } else { + name = dname; + } + if (!ok) { + break; + } + if (name.length() == 0) { + KMessageBox::error(0, i18n("That is an invalid name for a diagram."), i18n("Invalid Name")); + } else if(!findView(type, name)) { + UMLView* temp = new UMLView(folder); + temp -> setOptionState( Settings::getOptionState() ); + temp->setName( name ); + temp->setType( type ); + temp->setID( UniqueID::gen() ); + addView(temp); + emit sigDiagramCreated( temp->getID() ); + setModified(true, false); + UMLApp::app()->enablePrint(true); + changeCurrentView( temp->getID() ); + return temp; + } else { + KMessageBox::error(0, i18n("A diagram is already using that name."), i18n("Not a Unique Name")); + } + }//end while + return 0; +} + +void UMLDoc::renameDiagram(Uml::IDType id) { + bool ok = false; + + UMLView *temp = findView(id); + Diagram_Type type = temp->getType(); + + QString oldName= temp->getName(); + while(true) { + QString name = KInputDialog::getText(i18n("Name"), i18n("Enter name:"), oldName, &ok, (QWidget*)UMLApp::app()); + + if(!ok) + break; + if(name.length() == 0) + KMessageBox::error(0, i18n("That is an invalid name for a diagram."), i18n("Invalid Name")); + else if(!findView(type, name)) { + temp->setName(name); + + emit sigDiagramRenamed(id); + setModified(true); + break; + } else + KMessageBox::error(0, i18n("A diagram is already using that name."), i18n("Not a Unique Name")); + } +} + +void UMLDoc::renameUMLObject(UMLObject *o) { + bool ok = false; + QString oldName= o->getName(); + while(true) { + QString name = KInputDialog::getText(i18n("Name"), i18n("Enter name:"), oldName, &ok, (QWidget*)UMLApp::app()); + if(!ok) + break; + if(name.length() == 0) + KMessageBox::error(0, i18n("That is an invalid name."), i18n("Invalid Name")); + else if (isUnique(name)) { + o->setName(name); + setModified(true); + break; + } else { + KMessageBox::error(0, i18n("That name is already being used."), i18n("Not a Unique Name")); + } + } + return; +} + +void UMLDoc::renameChildUMLObject(UMLObject *o) { + bool ok = false; + UMLClassifier* p = dynamic_cast(o->parent()); + if(!p) { + kDebug() << "Can't create object, no parent found" << endl; + return; + } + + QString oldName= o->getName(); + while(true) { + QString name = KInputDialog::getText(i18n("Name"), i18n("Enter name:"), oldName, &ok, (QWidget*)UMLApp::app()); + if(!ok) + break; + if(name.length() == 0) + KMessageBox::error(0, i18n("That is an invalid name."), i18n("Invalid Name")); + else { + if (p->findChildObject(name) == NULL + || ((o->getBaseType() == Uml::ot_Operation) && KMessageBox::warningYesNo( kapp -> mainWidget() , + i18n( "The name you entered was not unique.\nIs this what you wanted?" ), + i18n( "Name Not Unique"),i18n("Use Name"),i18n("Enter New Name")) == KMessageBox::Yes) ) { + o->setName(name); + setModified(true); + break; + } else { + KMessageBox::error(0, i18n("That name is already being used."), i18n("Not a Unique Name")); + } + } + } +} + +void UMLDoc::changeCurrentView(Uml::IDType id) { + UMLApp* pApp = UMLApp::app(); + UMLView* w = findView(id); + if (w) { + pApp->setCurrentView(w); + emit sigDiagramChanged(w->getType()); + pApp->setDiagramMenuItemsState( true ); + setModified(true); + } + emit sigCurrentViewChanged(); +} + +void UMLDoc::removeDiagram(Uml::IDType id) { + UMLApp::app()->getDocWindow()->updateDocumentation(true); + UMLView* umlview = findView(id); + if(!umlview) + { + kError()<<"Request to remove diagram " << ID2STR(id) << ": Diagram not found!"<getName()), i18n("Delete Diagram"),KGuiItem( i18n("&Delete"), "editdelete")) == KMessageBox::Continue) { + removeView(umlview); + emit sigDiagramRemoved(id); + setModified(true); + /* if (infoWidget->isVisible()) { + emit sigDiagramChanged(dt_Undefined); + UMLApp::app()->enablePrint(false); + } + */ //FIXME sort out all the KActions for when there's no diagram + //also remove the buttons from the WorkToolBar, then get rid of infowidget + } +} + +UMLFolder *UMLDoc::currentRoot() { + UMLView *currentView = UMLApp::app()->getCurrentView(); + if (currentView == NULL) { + if (m_pCurrentRoot) + return m_pCurrentRoot; + kError() << "UMLDoc::currentRoot: m_pCurrentRoot is NULL" << endl; + return NULL; + } + UMLFolder *f = currentView->getFolder(); + while (f->getUMLPackage()) { + f = static_cast(f->getUMLPackage()); + } + return f; +} + +void UMLDoc::setCurrentRoot(Uml::Model_Type rootType) { + m_pCurrentRoot = m_root[rootType]; +} + +void UMLDoc::removeUMLObject(UMLObject* umlobject) { + UMLApp::app()->getDocWindow()->updateDocumentation(true); + Object_Type type = umlobject->getBaseType(); + + umlobject->setUMLStereotype(NULL); // triggers possible cleanup of UMLStereotype + if (dynamic_cast(umlobject)) { + UMLClassifier* parent = dynamic_cast(umlobject->parent()); + if (parent == NULL) { + kError() << "UMLDoc::removeUMLObject: parent of umlobject is NULL" + << endl; + return; + } + if (type == ot_Operation) { + parent->removeOperation(static_cast(umlobject)); + } else if (type == ot_EnumLiteral) { + UMLEnum *e = static_cast(parent); + e->removeEnumLiteral(static_cast(umlobject)); + } else if (type == ot_EntityAttribute) { + UMLEntity *ent = static_cast(parent); + ent->removeEntityAttribute(static_cast(umlobject)); + } else { + UMLClassifier* pClass = dynamic_cast(parent); + if (pClass == NULL) { + kError() << "UMLDoc::removeUMLObject: parent of umlobject has " + << "unexpected type " << parent->getBaseType() << endl; + return; + } + if (type == ot_Attribute) { + pClass->removeAttribute(static_cast(umlobject)); + } else if (type == ot_Template) { + pClass->removeTemplate(static_cast(umlobject)); + } else { + kError() << "UMLDoc::removeUMLObject: umlobject has " + << "unexpected type " << type << endl; + } + } + } else { + if (type == ot_Association) { + UMLAssociation *a = (UMLAssociation *)umlobject; + removeAssociation(a, false); // don't call setModified here, it's done below + } else { + UMLPackage* pkg = umlobject->getUMLPackage(); + if (pkg) { + pkg->removeObject(umlobject); + } else { + kError() << "UMLDoc::removeUMLObject(" << umlobject->getName() + << "): parent package is not set !" << endl; + } + } + emit sigObjectRemoved(umlobject); + } + setModified(true); +} + +void UMLDoc::signalUMLObjectCreated(UMLObject * o) { + emit sigObjectCreated(o); + /* This is the wrong place to do: + setModified(true); + Instead, that should be done by the callers when object creation and all + its side effects (e.g. new widget in view, new list view item, etc.) is + finalized. + */ +} + +void UMLDoc::setName(const QString& name) { + m_Name = name; +} + +QString UMLDoc::getName() const { + return m_Name; +} + +Uml::IDType UMLDoc::getModelID() const { + return m_modelID; +} + +void UMLDoc::saveToXMI(QIODevice& file) { + QDomDocument doc; + + QDomProcessingInstruction xmlHeading = + doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""); + doc.appendChild(xmlHeading); + + QDomElement root = doc.createElement( "XMI" ); + root.setAttribute( "xmi.version", "1.2" ); + QDateTime now = QDateTime::currentDateTime(); + root.setAttribute( "timestamp", now.toString(Qt::ISODate)); + root.setAttribute( "verified", "false"); + root.setAttribute( "xmlns:UML", "http://schema.omg.org/spec/UML/1.3"); + doc.appendChild( root ); + + QDomElement header = doc.createElement( "XMI.header" ); + QDomElement meta = doc.createElement( "XMI.metamodel" ); + meta.setAttribute( "xmi.name", "UML" ); + meta.setAttribute( "xmi.version", "1.3" ); + meta.setAttribute( "href", "UML.xml" ); + header.appendChild( meta ); + + /** + * bugs.kde.org/56184 comment by M. Alanen 2004-12-19: + * " XMI.model requires xmi.version. (or leave the whole XMI.model out, + * it's not required) " + QDomElement model = doc.createElement( "XMI.model" ); + QFile* qfile = dynamic_cast(&file); + if (qfile) { + QString modelName = qfile->name(); + modelName = modelName.section('/', -1 ); + modelName = modelName.section('.', 0, 0); + model.setAttribute( "xmi.name", modelName ); + model.setAttribute( "href", qfile->name() ); + } + */ + + QDomElement documentation = doc.createElement( "XMI.documentation" ); + + // If we consider it useful we might add user and contact details + // QDomElement owner = doc.createElement( "XMI.owner" ); + // owner.appendChild( doc.createTextNode( "Jens Kruger" ) ); // Add a User + // documentation.appendChild( owner ); + + // QDomElement contact = doc.createElement( "XMI.contact" ); + // contact.appendChild( doc.createTextNode( "je.krueger@web.de" ) ); // add a contact + // documentation.appendChild( contact ); + + QDomElement exporter = doc.createElement( "XMI.exporter" ); + exporter.appendChild( doc.createTextNode( "umbrello uml modeller http://uml.sf.net" ) ); + documentation.appendChild( exporter ); + + QDomElement exporterVersion = doc.createElement( "XMI.exporterVersion" ); + exporterVersion.appendChild( doc.createTextNode( XMI_FILE_VERSION ) ); + documentation.appendChild( exporterVersion ); + + // all files are now saved with correct Unicode encoding, we add this + // information to the header, so that the file will be loaded correctly + QDomElement exporterEncoding = doc.createElement( "XMI.exporterEncoding" ); + exporterEncoding.appendChild( doc.createTextNode( "UnicodeUTF8" ) ); + documentation.appendChild( exporterEncoding ); + + header.appendChild( documentation ); + + /** + * See comment on above + header.appendChild( model ); + */ + header.appendChild( meta ); + root.appendChild( header ); + + QDomElement content = doc.createElement( "XMI.content" ); + + QDomElement contentNS = doc.createElement( "UML:Namespace.contents" ); + + QDomElement objectsElement = doc.createElement( "UML:Model" ); + objectsElement.setAttribute( "xmi.id", ID2STR(m_modelID) ); + objectsElement.setAttribute( "name", m_Name ); + objectsElement.setAttribute( "isSpecification", "false" ); + objectsElement.setAttribute( "isAbstract", "false" ); + objectsElement.setAttribute( "isRoot", "false" ); + objectsElement.setAttribute( "isLeaf", "false" ); + + QDomElement ownedNS = doc.createElement( "UML:Namespace.ownedElement" ); + + // Save stereotypes and toplevel datatypes first so that upon loading + // they are known first. + // There is a bug causing duplication of the same stereotype in m_stereoList. + // As a workaround, we use a string list to memorize which stereotype has been saved. + QStringList stereoNames; + QValueList stereoIDs; + for (UMLStereotype *s = m_stereoList.first(); s; s = m_stereoList.next() ) { + QString stName = s->getName(); + Uml::IDType stID = s->getID(); + if (!stereoNames.contains(stName) && !stereoIDs.contains(stID)) { + s->saveToXMI(doc, ownedNS); + stereoNames.append(stName); + stereoIDs.append(stID); + } else { + kDebug() << "UMLDoc::saveToXMI: encountered duplicated stereotype " + << stName << " (id " << ID2STR(stID) << "), see bug 144924" << endl; + } + } + for (int i = 0; i < Uml::N_MODELTYPES; i++) { + m_root[i]->saveToXMI(doc, ownedNS); + } + + objectsElement.appendChild( ownedNS ); + + content.appendChild( objectsElement ); + + root.appendChild( content ); + + // Save the XMI extensions: docsettings, diagrams, listview, and codegeneration. + QDomElement extensions = doc.createElement( "XMI.extensions" ); + extensions.setAttribute( "xmi.extender", "umbrello" ); + + QDomElement docElement = doc.createElement( "docsettings" ); + Uml::IDType viewID = Uml::id_None; + UMLView *currentView = UMLApp::app()->getCurrentView(); + if (currentView) + viewID = currentView->getID(); + docElement.setAttribute( "viewid", ID2STR(viewID) ); + docElement.setAttribute( "documentation", m_Doc ); + docElement.setAttribute( "uniqueid", ID2STR(UniqueID::get()) ); + extensions.appendChild( docElement ); + + // save listview + UMLApp::app()->getListView()->saveToXMI(doc, extensions); + + // save code generator + CodeGenerator *codegen = UMLApp::app()->getGenerator(); + if (codegen) { + QDomElement codeGenElement = doc.createElement( "codegeneration" ); + codegen->saveToXMI( doc, codeGenElement ); + extensions.appendChild( codeGenElement ); + } + + root.appendChild( extensions ); + + QTextStream stream( &file ); + stream.setEncoding(QTextStream::UnicodeUTF8); + stream << doc.toString(); +} + +short UMLDoc::getEncoding(QIODevice & file) +{ + QTextStream stream( &file ); + stream.setEncoding(QTextStream::UnicodeUTF8); + QString data = stream.read(); + QString error; + int line; + QDomDocument doc; + if( !doc.setContent( data, false, &error, &line ) ) + { + kWarning()<<"Can't set content: "<processEvents(); // give UI events a chance + QString error; + int line; + QDomDocument doc; + if( !doc.setContent( data, false, &error, &line ) ) { + kWarning()<<"Can't set content:"<processEvents(); // give UI events a chance + QDomNode node = doc.firstChild(); + //Before Umbrello 1.1-rc1 we didn't add a " << endl; + continue; + } + bool seen_UMLObjects = false; + //process content + for (QDomNode child = node.firstChild(); !child.isNull(); + child = child.nextSibling()) { + if (child.isComment()) + continue; + element = child.toElement(); + QString tag = element.tagName(); + if (tag == "umlobjects" // for bkwd compat. + || tagEq(tag, "Subsystem") + || tagEq(tag, "Model") ) { + if( !loadUMLObjectsFromXMI( element ) ) { + kWarning() << "failed load on objects" << endl; + return false; + } + m_Name = element.attribute( "name", i18n("UML Model") ); + UMLListView *lv = UMLApp::app()->getListView(); + lv->setColumnText(0, m_Name); + seen_UMLObjects = true; + } else if (tagEq(tag, "Package") || + tagEq(tag, "Class") || + tagEq(tag, "Interface")) { + // These tests are only for foreign XMI files that + // are missing the tag (e.g. NSUML) + QDomElement parentElem = node.toElement(); + if( !loadUMLObjectsFromXMI( parentElem ) ) { + kWarning() << "failed load on model objects" << endl; + return false; + } + seen_UMLObjects = true; + } else if (tagEq(tag, "TaggedValue")) { + // This tag is produced here, i.e. outside of , + // by the Unisys.JCR.1 Rose-to-XMI tool. + if (! seen_UMLObjects) { + kDebug() << "skipping TaggedValue because not seen_UMLObjects" + << endl; + continue; + } + tag = element.attribute("tag", ""); + if (tag != "documentation") { + continue; + } + QString modelElement = element.attribute("modelElement", ""); + if (modelElement.isEmpty()) { + kDebug() << "skipping TaggedValue(documentation) because " + << "modelElement.isEmpty()" << endl; + continue; + } + UMLObject *o = findObjectById(STR2ID(modelElement)); + if (o == NULL) { + kDebug() << "TaggedValue(documentation): cannot find object" + << " for modelElement " << modelElement << endl; + continue; + } + QString value = element.attribute("value", ""); + if (! value.isEmpty()) + o->setDoc(value); + } else { + // for backward compatibility + loadExtensionsFromXMI(child); + } + } + } +#ifdef VERBOSE_DEBUGGING + kDebug() << "UMLDoc::m_objectList.count() is " << m_objectList.count() << endl; +#endif + resolveTypes(); + // set a default code generator if no tag seen + if (UMLApp::app()->getGenerator() == NULL) + UMLApp::app()->setGenerator(UMLApp::app()->getDefaultLanguage()); + emit sigWriteToStatusBar( i18n("Setting up the document...") ); + kapp->processEvents(); // give UI events a chance + activateAllViews(); + + UMLView *viewToBeSet = NULL; + if (m_nViewID != Uml::id_None) + viewToBeSet = findView( m_nViewID ); + if (viewToBeSet) { + changeCurrentView( m_nViewID ); + Settings::OptionState optionState = Settings::getOptionState(); + if (optionState.generalState.tabdiagrams) { + UMLApp::app()->tabWidget()->showPage(viewToBeSet); + } + } else { + createDiagram(m_root[mt_Logical], Uml::dt_Class, false); + m_pCurrentRoot = m_root[mt_Logical]; + } + emit sigResetStatusbarProgress(); + return true; +} + +void UMLDoc::resolveTypes() { + // Resolve the types. + // This is done in a separate pass because of possible forward references. + if (m_bTypesAreResolved) + return; + m_bTypesAreResolved = true; + writeToStatusBar( i18n("Resolving object references...") ); + for (int i = 0; i < Uml::N_MODELTYPES; i++) { + UMLFolder *obj = m_root[i]; +#ifdef VERBOSE_DEBUGGING + kDebug() << "UMLDoc: invoking resolveRef() for " << obj->getName() + << " (id=" << ID2STR(obj->getID()) << ")" << endl; +#endif + obj->resolveRef(); + } + kapp->processEvents(); // give UI events a chance +} + +bool UMLDoc::validateXMIHeader(QDomNode& headerNode) { + QDomElement headerElement = headerNode.toElement(); + while ( !headerNode.isNull() ) { + /* //Seems older Umbrello files used a different metamodel, so don't validate it for now + if( !headerElement.isNull() && headerElement.tagName() == "XMI.metamodel" ) { + String metamodel = headerElement.attribute("xmi.name", ""); + if (metamodel != "UML") { + return false; + } + } + */ + headerNode = headerNode.nextSibling(); + headerElement = headerNode.toElement(); + } + return true; +} + +bool UMLDoc::loadUMLObjectsFromXMI(QDomElement& element) { + /* FIXME need a way to make status bar actually reflect + how much of the file has been loaded rather than just + counting to 10 (an arbitrary number) + emit sigResetStatusbarProgress(); + emit sigSetStatusbarProgress( 0 ); + emit sigSetStatusbarProgressSteps( 10 ); + m_count = 0; + */ + emit sigWriteToStatusBar( i18n("Loading UML elements...") ); + + for (QDomNode node = element.firstChild(); !node.isNull(); + node = node.nextSibling()) { + if (node.isComment()) + continue; + QDomElement tempElement = node.toElement(); + QString type = tempElement.tagName(); + if (tagEq(type, "Model")) { + bool foundUmbrelloRootFolder = false; + QString name = tempElement.attribute("name"); + for (int i = 0; i < Uml::N_MODELTYPES; i++) { + if (name == m_root[i]->getName()) { + m_pCurrentRoot = m_root[i]; + m_root[i]->loadFromXMI(tempElement); + foundUmbrelloRootFolder = true; + break; + } + } + if (foundUmbrelloRootFolder) + continue; + } + // From here on, it's support for stereotypes, pre 1.5.5 versions, and foreign files + if (tagEq(type, "Namespace.ownedElement") || + tagEq(type, "Namespace.contents") || + tagEq(type, "Model")) { + //CHECK: Umbrello currently assumes that nested elements + // are ownedElements anyway. + // Therefore the tag is of no + // significance. + if( !loadUMLObjectsFromXMI( tempElement ) ) { + kWarning() << "failed load on " << type << endl; + return false; + } + continue; + } + if (Model_Utils::isCommonXMIAttribute(type)) + continue; + if (! tempElement.hasAttribute("xmi.id")) { + QString idref = tempElement.attribute("xmi.idref", ""); + if (! idref.isEmpty()) { + kDebug() << "resolution of xmi.idref " << idref + << " is not yet implemented" << endl; + } else { + kError() << "Cannot load " << type + << " because xmi.id is missing" << endl; + } + continue; + } + QString stID = tempElement.attribute("stereotype", ""); + UMLObject *pObject = Object_Factory::makeObjectFromXMI(type, stID); + if( !pObject ) { + kWarning() << "Unknown type of umlobject to create: " << type << endl; + // We want a best effort, therefore this is handled as a + // soft error. + continue; + } + Uml::Object_Type ot = pObject->getBaseType(); + // Set the parent root folder. + UMLPackage *pkg = NULL; + if (ot == Uml::ot_Datatype) { + pkg = m_datatypeRoot; + } else { + Uml::Model_Type guess = Model_Utils::guessContainer(pObject); + if (guess != Uml::N_MODELTYPES) + pkg = m_root[guess]; + } + pObject->setUMLPackage(pkg); + + bool status = pObject -> loadFromXMI( tempElement ); + if ( !status ) { + //delete pObject; + // Unfortunately we cannot do this because the pObject + // may be still referenced by other model objects. + kError() << "loadFromXMI failed for " << pObject->getName() << " xmi.id=" + << ID2STR(pObject->getID()) << endl; + continue; + } + pkg = pObject->getUMLPackage(); + if (ot == ot_Stereotype) { + UMLStereotype *s = static_cast(pObject); + UMLStereotype *exist = findStereotype(pObject->getName()); + if (exist) { + if (exist->getID() == pObject->getID()) { + delete pObject; + } else { + kDebug() << "Stereotype " << pObject->getName() + << "(id=" << ID2STR(pObject->getID()) + << ") already exists with id=" + << ID2STR(exist->getID()) << endl; + addStereotype(s); + } + } else { + addStereotype(s); + } + continue; + } + if (pkg == NULL) + kError() << "UMLDoc::loadUMLObjectsFromXMI: pkg is NULL for " + << pObject->getName() << " xmi.id=" + << ID2STR(pObject->getID()) << endl; + else + pkg->addObject(pObject); + + /* FIXME see comment at loadUMLObjectsFromXMI + emit sigSetStatusbarProgress( ++m_count ); + */ + } + return true; +} + +void UMLDoc::setMainViewID(Uml::IDType viewID) { + m_nViewID = viewID; +} + +void UMLDoc::loadExtensionsFromXMI(QDomNode& node) { + QDomElement element = node.toElement(); + QString tag = element.tagName(); + + if (tag == "docsettings") { + QString viewID = element.attribute( "viewid", "-1" ); + m_Doc = element.attribute( "documentation", "" ); + QString uniqueid = element.attribute( "uniqueid", "0" ); + + m_nViewID = STR2ID(viewID); + UniqueID::set(STR2ID(uniqueid)); + UMLApp::app()->getDocWindow() -> newDocumentation(); + + } else if (tag == "diagrams" || tag == "UISModelElement") { + // For backward compatibility only: + // Since version 1.5.5 diagrams are saved as part of the UMLFolder. + QDomNode diagramNode = node.firstChild(); + if (tag == "UISModelElement") { // Unisys.IntegratePlus.2 + element = diagramNode.toElement(); + tag = element.tagName(); + if (tag != "uisOwnedDiagram") { + kError() << "unknown child node " << tag << endl; + return; + } + diagramNode = diagramNode.firstChild(); + } + if( !loadDiagramsFromXMI( diagramNode ) ) { + kWarning() << "failed load on diagrams" << endl; + } + + } else if (tag == "listview") { + //FIXME: Need to resolveTypes() before loading listview, + // else listview items are duplicated. + resolveTypes(); + if( !UMLApp::app()->getListView() -> loadFromXMI( element ) ) { + kWarning() << "failed load on listview" << endl; + } + + } else if (tag == "codegeneration") { + QDomNode cgnode = node.firstChild(); + QDomElement cgelement = cgnode.toElement(); + while( !cgelement.isNull() ) { + QString nodeName = cgelement.tagName(); + QString lang = cgelement.attribute("language","UNKNOWN"); + Uml::Programming_Language pl = Model_Utils::stringToProgLang(lang); + CodeGenerator *g = UMLApp::app()->setGenerator(pl); + g->loadFromXMI(cgelement); + cgnode = cgnode.nextSibling(); + cgelement = cgnode.toElement(); + } + if (UMLApp::app()->getGenerator() == NULL) + UMLApp::app()->setGenerator(UMLApp::app()->getDefaultLanguage()); + } +} + +// For backward compatibility only: +// Since version 1.5.5 diagrams are saved as part of the UMLFolder. +bool UMLDoc::loadDiagramsFromXMI( QDomNode & node ) { + emit sigWriteToStatusBar( i18n("Loading diagrams...") ); + emit sigResetStatusbarProgress(); + emit sigSetStatusbarProgress( 0 ); + emit sigSetStatusbarProgressSteps( 10 ); //FIX ME + QDomElement element = node.toElement(); + if( element.isNull() ) + return true;//return ok as it means there is no umlobjects + const Settings::OptionState state = Settings::getOptionState(); + UMLView * pView = 0; + int count = 0; + while( !element.isNull() ) { + QString tag = element.tagName(); + if (tag == "diagram" || tag == "UISDiagram") { + pView = new UMLView(NULL); + // IMPORTANT: Set OptionState of new UMLView _BEFORE_ + // reading the corresponding diagram: + // + allow using per-diagram color and line-width settings + // + avoid crashes due to uninitialized values for lineWidth + pView -> setOptionState( state ); + bool success = false; + if (tag == "UISDiagram") { + success = pView->loadUISDiagram(element); + } else { + success = pView->loadFromXMI(element); + } + if (!success) { + kWarning() << "failed load on viewdata loadfromXMI" << endl; + delete pView; + return false; + } + // Put diagram in default predefined folder. + // @todo pass in the parent folder - it might be a user defined one. + Uml::Model_Type mt = Model_Utils::convert_DT_MT(pView->getType()); + pView->setFolder(m_root[mt]); + pView -> hide(); + addView( pView ); + emit sigSetStatusbarProgress( ++count ); + kapp->processEvents(); // give UI events a chance + } + node = node.nextSibling(); + element = node.toElement(); + } + return true; +} + +void UMLDoc::removeAllViews() { + for (int i = 0; i < Uml::N_MODELTYPES; i++) + m_root[i]->removeAllViews(); + UMLApp::app()->setCurrentView(NULL); + emit sigDiagramChanged(dt_Undefined); + UMLApp::app()->setDiagramMenuItemsState(false); +} + +UMLClassifierList UMLDoc::getConcepts(bool includeNested /* =true */) { + UMLClassifierList conceptList; + m_root[mt_Logical]->appendClassifiers(conceptList, includeNested); + return conceptList; +} + +UMLClassifierList UMLDoc::getClasses(bool includeNested /* =true */) { + UMLClassifierList conceptList; + m_root[mt_Logical]->appendClasses(conceptList, includeNested); + return conceptList; +} + +UMLClassifierList UMLDoc::getClassesAndInterfaces(bool includeNested /* =true */) { + UMLClassifierList conceptList; + m_root[mt_Logical]->appendClassesAndInterfaces(conceptList, includeNested); + return conceptList; +} + +UMLClassifierList UMLDoc::getInterfaces(bool includeNested /* =true */) { + UMLClassifierList interfaceList; + m_root[mt_Logical]->appendInterfaces(interfaceList, includeNested); + return interfaceList; +} + +UMLClassifierList UMLDoc::getDatatypes() { + UMLObjectList objects = m_datatypeRoot->containedObjects(); + UMLClassifierList datatypeList; + UMLObject *obj; + for (UMLObjectListIt oit(objects); (obj = oit.current()) != NULL; ++oit) { + if (obj->getBaseType() == ot_Datatype) { + datatypeList.append(static_cast(obj)); + } + } + return datatypeList; +} + +UMLAssociationList UMLDoc::getAssociations() { + UMLAssociationList associationList; + for (int i = 0; i < Uml::N_MODELTYPES; i++) { + UMLAssociationList assocs = m_root[i]->getAssociations(); + UMLAssociation *a; + for (UMLAssociationListIt ait(assocs); (a = ait.current()) != NULL; ++ait) + associationList.append(a); + } + return associationList; +} + +void UMLDoc::print(KPrinter * pPrinter) { + UMLView * printView = 0; + int count = QString(pPrinter -> option("kde-uml-count")).toInt(); + QPainter painter(pPrinter); + for(int i = 0;i < count;i++) { + if(i>0) + pPrinter -> newPage(); + QString diagram = i18n("kde-uml-Diagram") + QString("%1").arg(i); + QString sID = pPrinter -> option(diagram); + Uml::IDType id = STR2ID(sID); + printView = findView(id); + + if(printView) + printView ->print(pPrinter, painter); + printView = 0; + } + painter.end(); +} + +UMLViewList UMLDoc::getViewIterator() { + UMLViewList accumulator; + for (int i = 0; i < Uml::N_MODELTYPES; i++) + m_root[i]->appendViews(accumulator, true); + return accumulator; +} + +void UMLDoc::setModified(bool modified /*=true*/, bool addToUndo /*=true*/) { + if(!m_bLoading) { + m_modified = modified; + UMLApp::app()->setModified(modified); + + if (modified && addToUndo) { + addToUndoStack(); + clearRedoStack(); + } + } +} + +bool UMLDoc::assignNewIDs(UMLObject* Obj) { + if(!Obj || !m_pChangeLog) { + kDebug() << "no Obj || Changelog" << endl; + return false; + } + Uml::IDType result = assignNewID(Obj->getID()); + Obj->setID(result); + + //If it is a CONCEPT then change the ids of all its operations and attributes + if(Obj->getBaseType() == ot_Class ) { + UMLClassifier *c = static_cast(Obj); + UMLClassifierListItemList attributes = c->getFilteredList(ot_Attribute); + for(UMLObject* listItem = attributes.first(); listItem; listItem = attributes.next()) { + result = assignNewID(listItem->getID()); + listItem->setID(result); + } + + UMLClassifierListItemList templates = c->getFilteredList(ot_Template); + for(UMLObject* listItem = templates.first(); listItem; listItem = templates.next()) { + result = assignNewID(listItem->getID()); + listItem->setID(result); + } + } + + if(Obj->getBaseType() == ot_Interface || Obj->getBaseType() == ot_Class ) { + UMLOperationList operations(((UMLClassifier*)Obj)->getOpList()); + for(UMLObject* listItem = operations.first(); listItem; listItem = operations.next()) { + result = assignNewID(listItem->getID()); + listItem->setID(result); + } + } + + setModified(true); + + return true; +} + +UMLFolder *UMLDoc::getRootFolder(Uml::Model_Type mt) { + if (mt < Uml::mt_Logical || mt >= Uml::N_MODELTYPES) { + kError() << "UMLDoc::getRootFolder: illegal input value " << mt << endl; + return NULL; + } + return m_root[mt]; +} + +Uml::Model_Type UMLDoc::rootFolderType(UMLObject *obj) { + for (int i = 0; i < Uml::N_MODELTYPES; i++) { + const Uml::Model_Type m = (Uml::Model_Type)i; + if (obj == m_root[m]) + return m; + } + return Uml::N_MODELTYPES; +} + +/** Read property of IDChangeLog* m_pChangeLog. */ +IDChangeLog* UMLDoc::getChangeLog() { + return m_pChangeLog; +} + +/** Opens a Paste session, +Deletes the Old ChangeLog and Creates an empty one */ + +void UMLDoc::beginPaste() { + if(m_pChangeLog) { + delete m_pChangeLog; + m_pChangeLog = 0; + } + m_pChangeLog = new IDChangeLog; +} + +/** Closes a Paste session, +Deletes the ChangeLog */ +void UMLDoc::endPaste() { + if(m_pChangeLog) { + delete m_pChangeLog; + m_pChangeLog = 0; + } +} + +/** Assigns a New ID to an Object, and also logs the assignment to its internal +ChangeLog */ +Uml::IDType UMLDoc::assignNewID(Uml::IDType OldID) { + Uml::IDType result = UniqueID::gen(); + if (m_pChangeLog) { + m_pChangeLog->addIDChange(OldID, result); + } + return result; +} + +/** Adds an already created UMLView to the document, it gets assigned a new ID. + If its name is already in use then the function appends a number to it to + differentiate it from the others; this number is incremental so if + number 1 is in use then it tries 2 and then 3 and so on */ +bool UMLDoc::addUMLView(UMLView * pView ) { + if(!pView || !m_pChangeLog) + return false; + + int i = 0; + QString viewName = (QString)pView->getName(); + QString name = viewName; + while( findView(pView->getType(), name) != NULL) { + name = viewName + '_' + QString::number(++i); + } + if(i) //If name was modified + pView->setName(name); + Uml::IDType result = assignNewID(pView->getID()); + pView->setID(result); + + pView->activateAfterLoad( true ); + pView->endPartialWidgetPaste(); + pView->setOptionState( Settings::getOptionState() ); + addView(pView); + setModified(true); + return true; +} + +void UMLDoc::activateAllViews() { + // store old setting - for restore of last setting + bool m_bLoading_old = m_bLoading; + m_bLoading = true; //this is to prevent document becoming modified when activating a view + + for (int i = 0; i < Uml::N_MODELTYPES; i++) + m_root[i]->activateViews(); + m_bLoading = m_bLoading_old; +} + +void UMLDoc::settingsChanged(Settings::OptionState optionState) { + for (int i = 0; i < Uml::N_MODELTYPES; i++) + m_root[i]->setViewOptions(optionState); + initSaveTimer(); +} + +void UMLDoc::initSaveTimer() { + if( m_pAutoSaveTimer ) { + m_pAutoSaveTimer -> stop(); + disconnect( m_pAutoSaveTimer, SIGNAL( timeout() ), this, SLOT( slotAutoSave() ) ); + delete m_pAutoSaveTimer; + m_pAutoSaveTimer = 0; + } + Settings::OptionState optionState = Settings::getOptionState(); + if( optionState.generalState.autosave ) { + m_pAutoSaveTimer = new QTimer(this, "_AUTOSAVETIMER_" ); + connect( m_pAutoSaveTimer, SIGNAL( timeout() ), this, SLOT( slotAutoSave() ) ); + m_pAutoSaveTimer->start( optionState.generalState.autosavetime * 60000, false ); + } + return; +} + +void UMLDoc::slotAutoSave() { + //Only save if modified. + if( !m_modified ) { + return; + } + KURL tempURL = m_doc_url; + if( tempURL.fileName() == i18n("Untitled") ) { + tempURL.setPath( QDir::homeDirPath() + i18n("/autosave%1").arg(".xmi") ); + saveDocument( tempURL ); + m_doc_url.setFileName( i18n("Untitled") ); + m_modified = true; + UMLApp::app()->setModified( m_modified ); + } else { + // 2004-05-17 Achim Spangler + KURL orgDocUrl = m_doc_url; + QString orgFileName = m_doc_url.fileName(); + // don't overwrite manually saved file with autosave content + QString fileName = tempURL.fileName(); + Settings::OptionState optionState = Settings::getOptionState(); + fileName.replace( ".xmi", optionState.generalState.autosavesuffix ); + tempURL.setFileName( fileName ); + // End Achim Spangler + + saveDocument( tempURL ); + // 2004-05-17 Achim Spangler + // re-activate m_modified if autosave is writing to other file + // than the main project file -> autosave-suffix != ".xmi" + if ( ".xmi" != optionState.generalState.autosavesuffix ) { + m_modified = true; + UMLApp::app()->setModified( m_modified ); + } + // restore original file name - + // UMLDoc::saveDocument() sets doc_url to filename which is given as autosave-filename + setURL( orgDocUrl ); + UMLApp * pApp = UMLApp::app(); + pApp->setCaption(orgFileName, isModified() ); + // End Achim Spangler + } +} + +void UMLDoc::signalDiagramRenamed(UMLView* pView ) { + Settings::OptionState optionState = Settings::getOptionState(); + if (optionState.generalState.tabdiagrams) + UMLApp::app()->tabWidget()->setTabLabel( pView, pView->getName() ); + emit sigDiagramRenamed( pView -> getID() ); +} + +void UMLDoc::addToUndoStack() { + Settings::OptionState optionState = Settings::getOptionState(); + if (!m_bLoading && optionState.generalState.undo) { + QBuffer* buffer = new QBuffer(); + buffer->open(IO_WriteOnly); + QDataStream* undoData = new QDataStream(); + undoData->setDevice(buffer); + saveToXMI(*buffer); + buffer->close(); + undoStack.prepend(undoData); + + if (undoStack.count() > 1) { + UMLApp::app()->enableUndo(true); + } + } +} + +void UMLDoc::clearUndoStack() { + undoStack.setAutoDelete(true); + undoStack.clear(); + UMLApp::app()->enableRedo(false); + undoStack.setAutoDelete(false); + clearRedoStack(); +} + +void UMLDoc::clearRedoStack() { + redoStack.setAutoDelete(true); + redoStack.clear(); + UMLApp::app()->enableRedo(false); + redoStack.setAutoDelete(false); +} + +void UMLDoc::loadUndoData() { + if (undoStack.count() < 1) { + kWarning() << "no data in undostack" << endl; + return; + } + UMLView *currentView = UMLApp::app()->getCurrentView(); + if (currentView == NULL) { + kWarning() << "UMLDoc::loadUndoData: currentView is NULL" << endl; + undoStack.setAutoDelete(true); + undoStack.clear(); + undoStack.setAutoDelete(false); + UMLApp::app()->enableUndo(false); + return; + } + Uml::IDType currentViewID = currentView->getID(); + // store old setting - for restore of last setting + bool m_bLoading_old = m_bLoading; + m_bLoading = true; + closeDocument(); + redoStack.prepend( undoStack.take(0) ); + QDataStream* undoData = undoStack.getFirst(); + QBuffer* buffer = static_cast( undoData->device() ); + buffer->open(IO_ReadOnly); + loadFromXMI(*buffer); + buffer->close(); + + setModified(true, false); + m_bLoading = m_bLoading_old; + + undoStack.setAutoDelete(true); + if (undoStack.count() <= 1) { + UMLApp::app()->enableUndo(false); + } + if (redoStack.count() >= 1) { + UMLApp::app()->enableRedo(true); + } + while (undoStack.count() > undoMax) { + undoStack.removeLast(); + } + undoStack.setAutoDelete(false); + + currentView = UMLApp::app()->getCurrentView(); + if (currentView) { + if (currentView->getID() != currentViewID) + changeCurrentView( currentView->getID() ); + currentView->resizeCanvasToItems(); + } +} + +void UMLDoc::loadRedoData() { + if (redoStack.count() >= 1) { + UMLView *currentView = UMLApp::app()->getCurrentView(); + Uml::IDType currentViewID = currentView->getID(); + // store old setting - for restore of last setting + bool m_bLoading_old = m_bLoading; + m_bLoading = true; + closeDocument(); + undoStack.prepend( redoStack.getFirst() ); + QDataStream* redoData = redoStack.getFirst(); + redoStack.removeFirst(); + QBuffer* buffer = static_cast( redoData->device() ); + buffer->open(IO_ReadOnly); + loadFromXMI(*buffer); + buffer->close(); + + setModified(true, false); + currentView = UMLApp::app()->getCurrentView(); + currentView->resizeCanvasToItems(); + m_bLoading = m_bLoading_old; + + redoStack.setAutoDelete(true); + if (redoStack.count() < 1) { + UMLApp::app()->enableRedo(false); + } + if (undoStack.count() > 1) { + UMLApp::app()->enableUndo(true); + } + if (currentView->getID() != currentViewID) { + changeCurrentView(currentViewID); + } + redoStack.setAutoDelete(false); + } else { + kWarning() << "no data in redostack" << endl; + } +} + +void UMLDoc::addDefaultDatatypes() { + CodeGenerator *cg = UMLApp::app()->getGenerator(); + if (cg == NULL) { + kDebug() << "UMLDoc::addDefaultDatatypes: CodeGenerator is still NULL" + << endl; + return; + } + QStringList entries = cg->defaultDatatypes(); + QStringList::Iterator end(entries.end()); + for (QStringList::Iterator it = entries.begin(); it != end; ++it) + createDatatype(*it); +} + +void UMLDoc::createDatatype(const QString &name) { + UMLObjectList datatypes = m_datatypeRoot->containedObjects(); + UMLObject* umlobject = Model_Utils::findUMLObject(datatypes, name, + ot_Datatype, m_datatypeRoot); + if (!umlobject) { + Object_Factory::createUMLObject(ot_Datatype, name, m_datatypeRoot); + } + UMLApp::app()->getListView()->closeDatatypesFolder(); +} + +void UMLDoc::slotDiagramPopupMenu(QWidget* umlview, const QPoint& point) { + UMLView* view = (UMLView*) umlview; + if(m_pTabPopupMenu != 0) { + m_pTabPopupMenu->hide(); + delete m_pTabPopupMenu; + m_pTabPopupMenu = 0; + } + Settings::OptionState optionState = Settings::getOptionState(); + if (! optionState.generalState.tabdiagrams) + return; + + Uml::ListView_Type type = lvt_Unknown; + switch( view->getType() ) { + case dt_Class: + type = lvt_Class_Diagram; + break; + + case dt_UseCase: + type = lvt_UseCase_Diagram; + break; + + case dt_Sequence: + type = lvt_Sequence_Diagram; + break; + + case dt_Collaboration: + type = lvt_Collaboration_Diagram; + break; + + case dt_State: + type = lvt_State_Diagram; + break; + + case dt_Activity: + type = lvt_Activity_Diagram; + break; + + case dt_Component: + type = lvt_Component_Diagram; + break; + + case dt_Deployment: + type = lvt_Deployment_Diagram; + break; + + case dt_EntityRelationship: + type = lvt_EntityRelationship_Diagram; + break; + + default: + kWarning() << "unknown diagram type in slotDiagramPopupMenu()" << endl; + break; + }//end switch + + m_pTabPopupMenu = new ListPopupMenu(UMLApp::app()->getMainViewWidget(), type); + m_pTabPopupMenu->popup(point); + connect(m_pTabPopupMenu, SIGNAL(activated(int)), view, SLOT(slotMenuSelection(int))); +} + +void UMLDoc::addDefaultStereotypes() { + CodeGenerator *gen = UMLApp::app()->getGenerator(); + if (gen) + gen->createDefaultStereotypes(); +} + +const UMLStereotypeList& UMLDoc::getStereotypes() { + return m_stereoList; +} + + +#include "umldoc.moc" + diff --git a/umbrello/umbrello/umldoc.h b/umbrello/umbrello/umldoc.h new file mode 100644 index 00000000..2e936a3f --- /dev/null +++ b/umbrello/umbrello/umldoc.h @@ -0,0 +1,920 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLDOC_H +#define UMLDOC_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +// system includes +#include + +// qt includes +#include +#include +#include +#include + +// kde includes +#include +#include +#include + +// app includes +#include "umlnamespace.h" +#include "optionstate.h" +#include "umlobjectlist.h" +#include "umlassociationlist.h" +#include "umlclassifierlist.h" +#include "umlviewlist.h" +#include "umlstereotypelist.h" + +#define ENC_UNKNOWN 0 +#define ENC_UNICODE 1 +#define ENC_OLD_ENC 2 + + +// forward declarations +class QDomNode; +class QFile; +class QSplitter; + +class KPrinter; + +class DocWindow; +class IDChangeLog; +class ObjectWidget; +class UMLWidget; +class UMLPackage; +class UMLFolder; + +/** + * UMLDoc provides a document object for a document-view model. + * + * The UMLDoc class provides a document object that can be used + * in conjunction with the classes UMLApp and UMLView to create + * a document-view model for standard KDE applications based on + * KApplication and KMainWindow. Thereby, the document object + * is created by the UMLApp instance and contains the document + * structure with the according methods for manipulation of the + * document data by UMLView objects. Also, UMLDoc contains the + * methods for serialization of the document data from and to + * files. + * + * @author Paul Hensgen + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class UMLDoc : public QObject { + Q_OBJECT +public: + /** + * Constructor for the fileclass of the application + */ + UMLDoc(); + + /** + * Destructor for the fileclass of the application + */ + ~UMLDoc(); + + /** + * Initialize the UMLDoc. + * To be called after the constructor, before anything else. + */ + void init(); + + /** + * Adds a view to the document which represents the document + * contents. Usually this is your main view. + * + * @param view Pointer to the UMLView to add. + */ + void addView(UMLView *view); + + /** + * Removes a view from the list of currently connected views. + * + * @param view Pointer to the UMLView to remove. + * @param enforceOneView switch to determine if we have a current view or not. + * most of the time, we DO want this, except when exiting the program. + */ + void removeView(UMLView *view , bool enforceOneView = true ); + + /** + * Sets m_nViewID. + */ + void setMainViewID(Uml::IDType viewID); + + /** + * Sets the modified flag for the document after a modifying + * action on the view connected to the document. + * + * @param _m The value to set the modified flag to. + * @param addToUndo Whether this is an action which should be + * added to the undo stack. + */ + void setModified(bool _m=true, bool addToUndo=true); + + /** + * Returns if the document is modified or not. Use this to + * determine if your document needs saving by the user on + * closing. + * + * @return True if this UMLDoc is modified. + */ + bool isModified() { + return m_modified; + } + + /** + * "save modified" - Asks the user for saving if the document + * is modified. + * + * @return True if document can be closed. + */ + bool saveModified(); + + /** + * Initializes the document generally. + * + * @return True if operation successful. + */ + bool newDocument(); + + /** + * Closes the current document. + */ + void closeDocument(); + + /** + * Loads the document by filename and format and emits the + * updateViews() signal. + * + * @param url The filename in KURL format. + * @param format The format (optional.) + * @return True if operation successful. + */ + bool openDocument(const KURL& url, const char *format=0); + + /** + * Saves the document using the given filename and format. + * + * @param url The filename in KURL format. + * @param format The format (optional.) + * @return True if operation successful. + */ + bool saveDocument(const KURL& url, const char *format=0); + + /** + * Returns the KURL of the document. + * + * @return The KURL of this UMLDoc. + */ + const KURL& URL() const; + + /** + * Sets the URL of the document. + * + * @param url The KURL to set. + */ + void setURL(const KURL& url); + + /** + * Sets up the signals needed by the program for it to work. + */ + void setupSignals(); + + /** + * Returns true if the given name is unique within its scope. + * + * @param name The name to check. + * @return True if name is unique. + */ + bool isUnique(const QString &name); + + /** + * Returns true if the given name is unique within its scope of given package. + * + * @param name The name to check. + * @package The UMLPackage in which we have to determine the unique-ness + * @return True if name is unique. + */ + bool isUnique(const QString &name, UMLPackage *package); + + /** + * Finds or creates a stereotype for the parent object. + */ + UMLStereotype* findOrCreateStereotype(const QString &name); + + /** + * Creates an association between two UMLObjects. + * NOTE: this method does not check if the association is valid / legal + * + * @param a The UMLObject "A" for the association (source) + * @param b The UMLObject "B" for the association (destination) + * @param type The association's type + * @return The Association created + */ + UMLAssociation* createUMLAssociation(UMLObject *a, UMLObject *b, Uml::Association_Type type); + + /** + * Adds an association. + * + * @param pAssoc Pointer to the UMLAssociation to add. + */ + void addAssociation(UMLAssociation *pAssoc); + + /** + * Removes an association. + * + * @param pAssoc Pointer to the UMLAssociation to remove. + * @param doSetModified Whether to mark the document as modified (default: true.) + */ + void removeAssociation(UMLAssociation *pAssoc, bool doSetModified = true); + + /** + * Finds an association. + * + * @param assocType Type of the UMLAssociation to seek. + * @param roleAObj Pointer to the role A UMLCanvasObject. + * @param roleBObj Pointer to the role B UMLCanvasObject. + * @param swap Optional pointer to boolean. + * The bool is set to true if the assocation + * matched with swapped roles, else it is set + * to false. + * @return Pointer to the UMLAssociation found or NULL if not found. + */ + UMLAssociation * findAssociation(Uml::Association_Type assocType, + const UMLObject *roleAObj, + const UMLObject *roleBObj, + bool *swap = NULL); + + /** + * Creates a diagram of the given type. + * + * @param folder The folder in which tp create the diagram. + * @param type The type of diagram to create. + * @param askForName If true shows a dialog box asking for name, + * else uses a default name. + * @return Pointer to the UMLView of the new diagram. + */ + UMLView* createDiagram(UMLFolder *folder, Uml::Diagram_Type type, bool askForName = true); + + /** + * Removes an @ref UMLObject from the current file. If this object + * is being represented on a diagram it will also delete all those + * representations. + * + * @param o Pointer to the UMLObject to delete. + */ + void removeUMLObject(UMLObject*o); + + /** + * Used to rename a document. This method takes care of everything. + * You just need to give the ID of the diagram to the method. + * + * @param id The ID of the diagram to rename. + */ + void renameDiagram(Uml::IDType id); + + /** + * Used to rename a @ref UMLObject. The @ref UMLObject is to be an + * actor, use case or concept. + * + * @param o The object to rename. + */ + void renameUMLObject(UMLObject *o); + + /** + * Used to rename an operation or attribute of a concept. + * + * @param o The attribute or operation to rename. + */ + void renameChildUMLObject(UMLObject *o); + + + /** + * Changes the current view (diagram) to the view with the given ID. + * + * @param id The ID of the view to change to. + */ + void changeCurrentView(Uml::IDType id); + + /** + * Deletes a diagram from the current file. + * + * @param id The ID of the diagram to delete. + */ + void removeDiagram(Uml::IDType id); + + /** + * Used to find a reference to a @ref UMLObject by its ID. + * + * @param id The @ref UMLObject to find. + * @return Pointer to the UMLObject found, or NULL if not found. + */ + UMLObject* findObjectById(Uml::IDType id); + + /** + * Used to find a @ref UMLObject by its type and name. + * + * @param name The name of the @ref UMLObject to find. + * @param type Object_Type of the object to find (optional.) + * When the given type is ot_UMLObject the type is + * disregarded, i.e. the given name is the only + * search criterion. + * @param currentObj Object relative to which to search (optional.) + * If given then the enclosing scope(s) of this + * object are searched before the global scope. + * @return Pointer to the UMLObject found, or NULL if not found. + */ + UMLObject* findUMLObject(const QString &name, + Uml::Object_Type type = Uml::ot_UMLObject, + UMLObject *currentObj = NULL); + + /** + * Used to find a reference to a @ref UMLObject given its non-numeric + * ID string. + * Only used for intermediate processing while loading files + * containing objects with non-numeric xmi.id's. + * + * @param idStr The AuxId for the @ref UMLObject to find. + * @return Pointer to the UMLObject found, or NULL if not found. + */ + UMLObject* findObjectByAuxId(const QString &idStr); + + /** + * Used to find a @ref UMLClassifier by its name. + * + * @param name The name of the @ref UMLObject to find. + */ + UMLClassifier * findUMLClassifier (const QString &name); + + /** + * Finds a UMLStereotype by its name. + * + * @param name The name of the UMLStereotype to find. + * @return Pointer to the UMLStereotype found, or NULL if not found. + */ + UMLStereotype * findStereotype(const QString &name); + + /** + * Finds a view (diagram) by the ID given to method. + * + * @param id The ID of the view to search for. + * @return Pointer to the view found, or NULL if not found. + */ + UMLView * findView(Uml::IDType id); + + /** + * Finds a view (diagram) by the type and name given. + * + * @param type The type of view to find. + * @param name The name of the view to find. + * @param searchAllScopes Search in all subfolders (default: false.) + * @return Pointer to the view found, or NULL if not found. + */ + UMLView * findView(Uml::Diagram_Type type, const QString &name, + bool searchAllScopes = false); + + /** + * Set the name of this model. + */ + void setName(const QString& name); + + /** + * Return the name of this model. + */ + QString getName() const; + + /** + * Return the m_modelID (currently this a fixed value: + * Umbrello supports only a single document.) + */ + Uml::IDType getModelID() const; + + /** + * This method is called for saving the given model as a XMI file. + * It is virtual and calls the corresponding saveToXMI() functions + * of the derived classes. + * + * @param file The file to be saved to. + */ + virtual void saveToXMI(QIODevice& file); + + /** + * Checks the given XMI file if it was saved with correct Unicode + * encoding set or not. + * + * @param file The file to be checked. + */ + short getEncoding(QIODevice & file); + + /** + * Load a given XMI model from a file. If the encoding of the file + * is already known it can be passed to the function. If this info + * isn't given, loadFromXMI will check which encoding was used. + * + * @param file The file to be loaded. + * @param encode The encoding used. + */ + virtual bool loadFromXMI(QIODevice& file, short encode = ENC_UNKNOWN); + + /** + * Ensures the XMI file is a valid UML file. + * Currently only checks for metamodel=UML. + * + * @param headerNode The node + */ + bool validateXMIHeader(QDomNode& headerNode); + + /** + * Loads all UML objects from XMI into the current UMLDoc. + * + * @return True if operation successful. + */ + bool loadUMLObjectsFromXMI( QDomElement & element ); + + /** + * Loads umbrello specific extensions from XMI to the UMLDoc. + * The extension tags are: "docsettings", "diagrams", "listview", + * and "codegeneration". + */ + void loadExtensionsFromXMI(QDomNode & node); + + /** + * Loads all diagrams from XMI into the current UMLDoc. + * + * @return True if operation successful. + */ + bool loadDiagramsFromXMI( QDomNode & node ); + + /** + * Signal a view/diagram has been renamed. + */ + void signalDiagramRenamed(UMLView * pView ); + + /** + * Call to remove all the views (diagrams) in the current file. + */ + void removeAllViews(); + + /** + * Signal that a UMLObject has been created. + * + * @param o The object that has been created. + */ + void signalUMLObjectCreated(UMLObject * o); + + /** + * Returns the datatype folder. + * + * @return Pointer to the predefined folder for datatypes. + */ + UMLFolder * getDatatypeFolder() { + return m_datatypeRoot; + } + + /** + * Returns a list of the concepts in this UMLDoc. + * + * @param includeNested Whether to include the concepts from + * nested packages (default: true.) + * @return List of UML concepts. + */ + UMLClassifierList getConcepts(bool includeNested = true); + + /** + * Returns a list of the classes in this UMLDoc. + * + * @param includeNested Whether to include the concepts from + * nested packages (default: true.) + * @return List of UML classes. + */ + UMLClassifierList getClasses(bool includeNested = true); + + /** + * Returns a list of the classes and interfaces in this UMLDoc. + * + * @param includeNested Whether to include the concepts from + * nested packages (default: true.) + * @return List of UML concepts. + */ + UMLClassifierList getClassesAndInterfaces(bool includeNested = true); + + /** + * Returns a list of the interfaces in this UMLDoc. + * + * @param includeNested Whether to include the concepts from + * nested packages (default: true.) + * @return List of UML interfaces. + */ + UMLClassifierList getInterfaces(bool includeNested = true); + + /** + * Returns a list of the datatypes in this UMLDoc. + * + * @return List of datatypes. + */ + UMLClassifierList getDatatypes(); + + /** + * Returns a list of the associations in this UMLDoc. + * + * @return List of UML associations. + */ + UMLAssociationList getAssociations(); + + /** + * Controls the printing of the program. + * + * @param pPrinter The printer (object) to use. + */ + void print(KPrinter * pPrinter); + + /** + * Return the list of views for this document. + * + * @return List of UML views. + */ + UMLViewList getViewIterator(); + + /** + * Assigns an already created UMLObject a new ID. + * If the object is a classifier then the operations/attributes + * are also assigned new IDs. + * + * @param Obj Pointer to the UMLObject to add. + * @return True if operation successful. + */ + bool assignNewIDs(UMLObject* Obj); + + /** + * Adds a UMLObject thats already created but doesn't change + * any ids or signal. Use AddUMLObjectPaste if pasting. + * + * @param object The object to add. + * @return True if the object was actually added. + */ + bool addUMLObject(UMLObject * object); + + /** + * Adds an already created UMLView to the document, it gets + * assigned a new ID, if its name is already in use then the + * function appends a number to it to differentiate it from + * the others; this number is incremental so if number 1 is in + * use then it tries 2 and then 3 and so on + * + * @param pView Pointer to the UMLView to add. + * @return True if operation successful. + */ + bool addUMLView(UMLView * pView ); + + /** + * Return the predefined root folder of the given type. + */ + UMLFolder *getRootFolder(Uml::Model_Type mt); + + /** + * Return the corresponding Model_Type if the given object + * is one of the root folders. + * When the given object is not one of the root folders then + * return Uml::N_MODELTYPES. + */ + Uml::Model_Type rootFolderType(UMLObject *obj); + + /** + * Return the currently selected root folder. + * This will be an element from the m_root[] array. + */ + UMLFolder *currentRoot(); + + /** + * Set the current root folder. + * + * @param rootType The type of the root folder to set. + * The element from m_root[] which is indexed + * by this type is selected. + */ + void setCurrentRoot(Uml::Model_Type rootType); + + /** + * Read property of IDChangeLog* m_pChangeLog. + * + * @return Pointer to the IDChangeLog object. + */ + virtual IDChangeLog* getChangeLog(); + + /** + * Closes a paste session, deletes the ChangeLog. + */ + void endPaste(); + + /** + * Opens a Paste session, deletes the old ChangeLog and + * creates an empty one. + */ + void beginPaste(); + + /** + * Assigns a New ID to an Object, and also logs the assignment + * to its internal ChangeLog. + * + * @param OldID The present ID of the object. + * @return The new ID assigned to the object. + */ + Uml::IDType assignNewID(Uml::IDType OldID); + + /** + * Returns the documentation for the project. + * + * @return The documentation text of this UMLDoc. + */ + QString getDocumentation() const { + return m_Doc; + } + + /** + * Sets the documentation for the project. + * + * @param doc The documentation to set for this UMLDoc. + */ + void setDocumentation(const QString &doc) { + m_Doc = doc; + } + + /** + * Activate all the diagrams/views after loading so all their + * widgets keep their IDs. + */ + void activateAllViews(); + + /** + * Sets the default settings to the given settings. + */ + void settingsChanged(Settings::OptionState optionState); + + + /** + * Returns the version of the old UML files. + */ + int getFileVersion(void) {return version;} + + /** + * Performs the undo function, loading the document back to the + * state is was before the last addToUndoStack() + */ + void loadUndoData(); + + /** + * Performs the redo function, loading the document back to the + * state is was before the last undo() + */ + void loadRedoData(); + + /** + * Takes an image of the document and adds it to the UndoStack. + * Implemented using the saveToXMI functions. + */ + void addToUndoStack(); + + /** + * Removes all entries from the UndoStack and RedoStack and disables the + * undo and redo actions. + */ + void clearUndoStack(); + + /** + * Removes all entries from the RedoStack and disables the + * redo action. + */ + void clearRedoStack(); + + /** + * Returns a name for the new object, appended with a number + * if the default name is taken e.g. class diagram, class + * diagram_1 etc + */ + QString uniqViewName(const Uml::Diagram_Type type); + + /** + * Returns true when loading a document file. + */ + bool loading() const; + + /** + * Sets loading boolean flag to the value given. + */ + void setLoading(bool state = true); + + /** + * Calls the active code generator to create its default datatypes + */ + void addDefaultDatatypes(); + + /** + * Add a datatype if it doesn't already exist. + * Used by code generators and attribute dialog. + */ + void createDatatype(const QString &name); + + /** + * Find a UMLStereotype by its unique ID. + */ + UMLStereotype * findStereotypeById(Uml::IDType id); + + /** + * Add a UMLStereotype to the application. + */ + void addStereotype(const UMLStereotype *s); + + /** + * Remove a UMLStereotype from the application. + */ + void removeStereotype(const UMLStereotype *s); + + /** + * Add a stereotype if it doesn't already exist. + * Used by code generators, operations and attribute dialog. + */ + void addDefaultStereotypes(); + + /** + * Returns a list of the stereotypes in this UMLDoc. + * + * @return List of UML stereotypes. + */ + const UMLStereotypeList& getStereotypes(); + + /** + * Write text to the status bar. + */ + void writeToStatusBar(const QString &text); + + /** + * Type resolution pass. + */ + void resolveTypes(); + +private: + /** + * Sets up the autosave timer. + */ + void initSaveTimer(); + + /** + * Array of predefined root folders. + */ + UMLFolder *m_root[Uml::N_MODELTYPES]; + /** + * Predefined root folder for datatypes, contained in + * m_root[Uml::mt_Logical] + */ + UMLFolder *m_datatypeRoot; + + /** + * The UMLDoc is the sole owner of all stereotypes. + * UMLStereotype instances are reference counted. + * When an UMLStereotype is no longer referenced anywhere, + * its refcount drops to zero. It is then removed from the + * m_stereoList and it is physically deleted. + */ + UMLStereotypeList m_stereoList; + + QString m_Name; ///< name of this model as stored in the tag + Uml::IDType m_modelID; ///< xmi.id of this model in the + int m_count; ///< auxiliary counter for the progress bar + bool m_modified; + KURL m_doc_url; + + /** + * Contains all the UMLObject id changes of paste session. + */ + IDChangeLog* m_pChangeLog; + + /** + * true if the we're loading a new document + */ + bool m_bLoading; + + /** + * Documentation for the project. + */ + QString m_Doc; + + /** + * Used for autosave + */ + QTimer * m_pAutoSaveTimer; + + /** + * Stores the version of old UML files. + */ + int version; + + /** + * The stack of images of the document added to each time + * something is changed. A QPtrList is used rather than a + * QPtrStack to be able to remove the ones off the bottom once + * the stack gets too big. + */ + QPtrList undoStack; + + /** + * The stack of images of the document added to each time + * undo is called. + */ + QPtrList redoStack; + + /** + * Auxiliary to processing + */ + Uml::IDType m_nViewID; + + /** + * True when type resolution pass has been executed. + */ + bool m_bTypesAreResolved; + + /** + * the context menu on the tabs, + * plugs into umlview::slotMenuSelection() + */ + KPopupMenu* m_pTabPopupMenu; + + /** + * Auxiliary variable for currentRoot(): + * m_pCurrentRoot is only used if UMLApp::app()->getCurrentView() + * returns NULL. + */ + UMLFolder * m_pCurrentRoot; + +public slots: + + void slotRemoveUMLObject(UMLObject*o); + + /** + * Called after a specified time to autosave the document. + */ + void slotAutoSave(); + + /** + * Make a popup menu for the tabs + * signalled from tabWidget's contextMenu() + */ + void slotDiagramPopupMenu(QWidget* umlview, const QPoint& point); + +signals: + void sigDiagramCreated(Uml::IDType id); + void sigDiagramRemoved(Uml::IDType id); + void sigDiagramRenamed(Uml::IDType t); + void sigDiagramChanged(Uml::Diagram_Type); + + void sigObjectCreated(UMLObject *); + void sigObjectRemoved(UMLObject *); + + /** + * Reset the status bar. + */ + void sigResetStatusbarProgress(); + + /** + * Set the total range of the progressbar. + * + * @param totalSteps Total range of the progressbar (0..totalSteps) + */ + void sigSetStatusbarProgressSteps(int totalSteps); + + + /** + * Set the progress position of the progressbar. + * + * @param stepPosition The step position to set. + */ + void sigSetStatusbarProgress(int stepPosition); + + /** + * Write text to the status bar. + */ + void sigWriteToStatusBar(const QString &text); + + /** + * The diagram being displayed has changed. + * UMLApp uses this to keep its menu items state up to date. + */ + void sigCurrentViewChanged(); + + +}; + +#endif // UMLDOC_H diff --git a/umbrello/umbrello/umlentityattributelist.cpp b/umbrello/umbrello/umlentityattributelist.cpp new file mode 100644 index 00000000..5855859a --- /dev/null +++ b/umbrello/umbrello/umlentityattributelist.cpp @@ -0,0 +1,38 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#include "umlentityattributelist.h" +#include "entityattribute.h" +#include +#include + +void UMLEntityAttributeList::copyInto(UMLEntityAttributeList* rhs) const { + // Don't copy yourself. + if (rhs == this) return; + + rhs->clear(); + + // Suffering from const; we shall not modify our object. + UMLEntityAttributeList* tmp = new UMLEntityAttributeList(*this); + + UMLEntityAttribute* item; + for (item = tmp->first(); item; item = tmp->next() ) { + rhs->append((UMLEntityAttribute*)item->clone()); + } + delete tmp; +} + + +UMLEntityAttributeList* UMLEntityAttributeList::clone() const { + UMLEntityAttributeList *clone = new UMLEntityAttributeList(); + copyInto(clone); + return clone; +} diff --git a/umbrello/umbrello/umlentityattributelist.h b/umbrello/umbrello/umlentityattributelist.h new file mode 100644 index 00000000..35c0a150 --- /dev/null +++ b/umbrello/umbrello/umlentityattributelist.h @@ -0,0 +1,44 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLENTITYATTRIBUTELIST_H +#define UMLENTITYATTRIBUTELIST_H + +#include + +// forward declaration +class UMLEntityAttribute; + +//typedef QPtrList UMLEntityAttributeList; +typedef QPtrListIterator UMLEntityAttributeListIt; + +/** + * This sub-class adds copyInto and clone to the QPtrList + * base class. + */ +class UMLEntityAttributeList : public QPtrList +{ +public: + + /** + * Copy the internal presentation of this object into the new + * object. + */ + virtual void copyInto (UMLEntityAttributeList* rhs) const; + + /** + * Make a clone of this object. + */ + virtual UMLEntityAttributeList* clone() const; +}; + + +#endif diff --git a/umbrello/umbrello/umlenumliterallist.h b/umbrello/umbrello/umlenumliterallist.h new file mode 100644 index 00000000..60e03199 --- /dev/null +++ b/umbrello/umbrello/umlenumliterallist.h @@ -0,0 +1,23 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLENUMLITERALLIST_H +#define UMLENUMLITERALLIST_H + +#include + +// forward declaration +class UMLEnumLiteral; + +typedef QPtrList UMLEnumLiteralList; +typedef QPtrListIterator UMLEnumLiteralListIt; + +#endif diff --git a/umbrello/umbrello/umllistview.cpp b/umbrello/umbrello/umllistview.cpp new file mode 100644 index 00000000..d12fed58 --- /dev/null +++ b/umbrello/umbrello/umllistview.cpp @@ -0,0 +1,2703 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "umllistview.h" + +// qt/kde includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// app includes +#include "actor.h" +#include "classifier.h" +#include "package.h" +#include "folder.h" +#include "component.h" +#include "node.h" +#include "artifact.h" +#include "enum.h" +#include "enumliteral.h" +#include "entity.h" +#include "docwindow.h" +#include "listpopupmenu.h" +#include "template.h" +#include "operation.h" +#include "attribute.h" +#include "entityattribute.h" +#include "uml.h" +#include "umldoc.h" +#include "umllistviewitemlist.h" +#include "umllistviewitem.h" +#include "umlview.h" +#include "umlviewimageexporter.h" +#include "usecase.h" +#include "model_utils.h" +#include "uniqueid.h" +#include "clipboard/idchangelog.h" +#include "clipboard/umldrag.h" +#include "dialogs/classpropdlg.h" +#include "dialogs/umlattributedialog.h" +#include "dialogs/umlentityattributedialog.h" +#include "dialogs/umloperationdialog.h" +#include "dialogs/umltemplatedialog.h" + +#ifdef WANT_LVTOOLTIP +class LVToolTip : public QToolTip +{ +public: + LVToolTip (QWidget* parent) : QToolTip (parent) {} + virtual ~LVToolTip () {} +protected: + /** + * Reimplemented from QToolTip for internal reasons. + * At classifiers, only the method names are shown in the list view - + * we use a tooltip for the full signature display. + * Once KListView's tooltip overriding mechanism works, we can kick + * this class out. + */ + virtual void maybeTip (const QPoint& pos) { + UMLListView *lv = UMLApp::app()->getListView(); + UMLListViewItem * item = (UMLListViewItem*)lv->itemAt(pos); + if (item == NULL) + return; + UMLObject *obj = item->getUMLObject(); + if (obj == NULL || obj->getBaseType() != Uml::ot_Operation) + return; + UMLOperation *op = static_cast(obj); + QString text = op->toString(Uml::st_ShowSig); + QRect rect = lv->itemRect(item); + tip(rect, text); + } +}; +#endif + + +UMLListView::UMLListView(QWidget *parent, const char *name) + : KListView(parent,name), m_pMenu(0), m_doc(UMLApp::app()->getDocument()) +{ + loadPixmaps(); + + //setup list view + setBackgroundColor(Qt::white); + setAcceptDrops(true); + setDropVisualizer(false); + setItemsMovable(true); + setItemsRenameable( true ); + setSelectionModeExt(FileManager); + setFocusPolicy(QWidget::StrongFocus); + setDragEnabled(true); + setColumnWidthMode( 0, Manual ); + setDefaultRenameAction( Accept ); + setResizeMode( LastColumn ); + header()->setClickEnabled(true); + //add columns and initial items + addColumn(m_doc->getName()); + +#ifdef WANT_LVTOOLTIP + /* In KDE-3.3, we cannot use KListView's builtin mechanism for + overriding the tooltips. Instead, see the above class LVToolTip. + setShowToolTips( true ); + setTooltipColumn( 0 ); + */ + (void) new LVToolTip(viewport()); +#endif + m_pMenu = NULL; + m_bStartedCut = m_bStartedCopy = false; + m_bIgnoreCancelRename = true; + m_bCreatingChildObject = false; + m_rv = NULL; + for (int i = 0; i < Uml::N_MODELTYPES; i++) + m_lv[i] = NULL; + m_datatypeFolder = NULL; + //setup slots/signals + connect(this, SIGNAL(dropped(QDropEvent *, QListViewItem *, QListViewItem *)), + this, SLOT(slotDropped(QDropEvent *, QListViewItem *, QListViewItem *))); + connect( this, SIGNAL( collapsed( QListViewItem * ) ), + this, SLOT( slotCollapsed( QListViewItem * ) ) ); + connect( this, SIGNAL( expanded( QListViewItem * ) ), this, SLOT( slotExpanded( QListViewItem * ) ) ); + connect( UMLApp::app(), SIGNAL( sigCutSuccessful() ), this, SLOT( slotCutSuccessful() ) ); +} + +UMLListView::~UMLListView() {} + +bool UMLListView::eventFilter(QObject *o, QEvent *e) { + if (e->type() != QEvent::MouseButtonPress || !o->isA("QHeader")) + return QListView::eventFilter(o, e); + QMouseEvent *me = static_cast(e); + if (me->button() == Qt::RightButton) { + if (m_pMenu) { + m_pMenu->hide(); + disconnect(m_pMenu, SIGNAL(activated(int)), this, SLOT(popupMenuSel(int))); + delete m_pMenu; + } + m_pMenu = new ListPopupMenu(this, Uml::lvt_Model); + m_pMenu->popup(me->globalPos()); + connect(m_pMenu, SIGNAL(activated(int)), this, SLOT(popupMenuSel(int))); + return true; + } + return QListView::eventFilter(o, e); +} + +void UMLListView::contentsMousePressEvent(QMouseEvent *me) { + UMLView *currentView = UMLApp::app()->getCurrentView(); + if (currentView) + currentView->clearSelected(); + if( me -> state() != Qt::ShiftButton ) + clearSelection(); + QPoint pt = this->QScrollView::contentsToViewport( me->pos() ); + UMLListViewItem * item = (UMLListViewItem*)itemAt(pt); + const Qt::ButtonState button = me->button(); + + if (!item || (button != Qt::RightButton && button != Qt::LeftButton)) { + UMLApp::app()->getDocWindow()->updateDocumentation(true); + return; + } + + if (button == Qt::LeftButton) { + UMLObject *o = item->getUMLObject(); + if (o) + UMLApp::app()->getDocWindow()->showDocumentation(o, false); + else + UMLApp::app()->getDocWindow()->updateDocumentation(true); + } + if (button == Qt::RightButton) { + if(m_pMenu != 0) { + m_pMenu->hide(); + disconnect(m_pMenu, SIGNAL(activated(int)), this, SLOT(popupMenuSel(int))); + delete m_pMenu; + m_pMenu = 0; + } + const Uml::ListView_Type type = item->getType(); + m_pMenu = new ListPopupMenu(this, type); + m_pMenu->popup(me->globalPos()); + connect(m_pMenu, SIGNAL(activated(int)), this, SLOT(popupMenuSel(int))); + }//end if right button + + this->KListView::contentsMousePressEvent(me); +} + +void UMLListView::contentsMouseReleaseEvent(QMouseEvent *me) { + if (me->button() != Qt::LeftButton) { + this->KListView::contentsMouseReleaseEvent(me); + return; + } + const QPoint pt = this->QScrollView::contentsToViewport( me->pos() ); + UMLListViewItem *item = dynamic_cast(itemAt(pt)); + if (item == NULL || !Model_Utils::typeIsDiagram(item->getType())) { + this->KListView::contentsMouseReleaseEvent(me); + return; + } + // Switch to diagram on mouse release - not on mouse press + // because the user might intend a drag-to-note. + m_doc->changeCurrentView( item->getID() ); + UMLApp::app()->getDocWindow()->showDocumentation(m_doc->findView(item->getID()), false); + this->KListView::contentsMouseReleaseEvent(me); +} + +void UMLListView::keyPressEvent(QKeyEvent *ke) { + UMLView *view = UMLApp::app()->getCurrentView(); + if (view && view->getSelectCount()) { + // Widgets have been selected in the diagram area, + // assume they handle the keypress. + ke->accept(); // munge and do nothing + } else { + const int k = ke->key(); + if (k == Qt::Key_Delete || k == Qt::Key_Backspace) { + // delete every selected item + UMLListViewItemList selecteditems; + getSelectedItemsRoot(selecteditems); + UMLListViewItemListIt it(selecteditems); + for (UMLListViewItem *item = 0; (item = it.current()); ++it) { + deleteItem(dynamic_cast(item)); + } + } else { + QListView::keyPressEvent(ke); // let parent handle it + } + } +} + +void UMLListView::popupMenuSel(int sel) { + UMLListViewItem * temp = (UMLListViewItem*)currentItem(); + if ( !temp ) { + kDebug() << "popupMenuSel invoked without currently selectedItem" << endl; + return; + } + UMLObject * object = temp -> getUMLObject(); + Uml::ListView_Type lvt = temp -> getType(); + Uml::Object_Type umlType = Uml::ot_UMLObject; + ListPopupMenu::Menu_Type menuType = (ListPopupMenu::Menu_Type)sel; + QString name; + + switch (menuType) { + case ListPopupMenu::mt_Class: + addNewItem( temp, Uml::lvt_Class ); + break; + + case ListPopupMenu::mt_Package: + addNewItem(temp, Uml::lvt_Package); + break; + + case ListPopupMenu::mt_Subsystem: + addNewItem(temp, Uml::lvt_Subsystem); + break; + + case ListPopupMenu::mt_Component: + addNewItem(temp, Uml::lvt_Component); + break; + + case ListPopupMenu::mt_Node: + addNewItem(temp, Uml::lvt_Node); + break; + + case ListPopupMenu::mt_Artifact: + addNewItem(temp, Uml::lvt_Artifact); + break; + + case ListPopupMenu::mt_Interface: + addNewItem(temp, Uml::lvt_Interface); + break; + + case ListPopupMenu::mt_Enum: + addNewItem(temp, Uml::lvt_Enum); + break; + + case ListPopupMenu::mt_EnumLiteral: + addNewItem(temp, Uml::lvt_EnumLiteral); + break; + + case ListPopupMenu::mt_Template: + addNewItem(temp, Uml::lvt_Template); + break; + + case ListPopupMenu::mt_Entity: + addNewItem(temp, Uml::lvt_Entity); + break; + + case ListPopupMenu::mt_Datatype: + addNewItem(temp, Uml::lvt_Datatype); + break; + + case ListPopupMenu::mt_Actor: + addNewItem( temp, Uml::lvt_Actor ); + break; + + case ListPopupMenu::mt_UseCase: + addNewItem( temp, Uml::lvt_UseCase ); + break; + + case ListPopupMenu::mt_Attribute: + addNewItem( temp, Uml::lvt_Attribute ); + break; + + case ListPopupMenu::mt_EntityAttribute: + addNewItem( temp, Uml::lvt_EntityAttribute ); + break; + + case ListPopupMenu::mt_Operation: + addNewItem( temp, Uml::lvt_Operation ); + break; + + case ListPopupMenu::mt_Import_Classes: + UMLApp::app()->slotImportClasses(); + break; + + case ListPopupMenu::mt_Expand_All: + expandAll(temp); + break; + + case ListPopupMenu::mt_Collapse_All: + collapseAll(temp); + break; + + case ListPopupMenu::mt_Export_Image: + m_doc->findView(temp->getID())->getImageExporter()->exportView(); + break; + + case ListPopupMenu::mt_Externalize_Folder: + { + UMLListViewItem *current = static_cast(currentItem()); + UMLFolder *modelFolder = dynamic_cast(current->getUMLObject()); + if (modelFolder == NULL) { + kError() << "UMLListView::popupMenuSel: modelFolder is NULL" << endl; + return; + } + // configure & show the file dialog + const QString rootDir(m_doc->URL().directory()); + KFileDialog fileDialog(rootDir, "*.xml", this, ":externalize-folder", true); + fileDialog.setCaption(i18n("Externalize Folder")); + fileDialog.setOperationMode(KFileDialog::Other); + // set a sensible default filename + QString defaultFilename = current->getText().lower(); + defaultFilename.replace(QRegExp("\\W+"), "_"); + defaultFilename.append(".xml"); // default extension + fileDialog.setSelection(defaultFilename); + fileDialog.exec(); + KURL selURL = fileDialog.selectedURL(); + if (selURL.isEmpty()) + return; + QString path = selURL.path(); + QString fileName = path; + if (fileName.startsWith(rootDir)) { + fileName.remove(rootDir); + } else { + // This should be done using a KMessageBox but we currently + // cannot add new i18n strings. + kError() << "Folder " << path + << " must be relative to the main model directory, " + << rootDir << endl; + return; + } + QFile file(path); + // Warn if file exists. + if (file.exists()) { + // This should be done using a KMessageBox but we currently + // cannot add new i18n strings. + kWarning() << "file " << fileName << " already exists!" << endl; + kWarning() << "The existing file will be overwritten." << endl; + } + // Test if file is writable. + if (file.open(IO_WriteOnly)) { + file.close(); + } else { + KMessageBox::error(0, + i18n("There was a problem saving file: %1").arg(fileName), + i18n("Save Error")); + return; + } + modelFolder->setFolderFile(fileName); + // Recompute text of the folder + QString folderText = current->getText(); + folderText.remove( QRegExp("\\s*\\(.*$") ); + folderText.append( " (" + fileName + ')' ); + current->setText(folderText); + break; + } + + case ListPopupMenu::mt_Internalize_Folder: + { + UMLListViewItem *current = static_cast(currentItem()); + UMLFolder *modelFolder = dynamic_cast(current->getUMLObject()); + if (modelFolder == NULL) { + kError() << "UMLListView::popupMenuSel: modelFolder is NULL" << endl; + return; + } + modelFolder->setFolderFile(QString::null); + // Recompute text of the folder + QString folderText = current->getText(); + folderText.remove( QRegExp("\\s*\\(.*$") ); + current->setText(folderText); + break; + } + + case ListPopupMenu::mt_Model: + { + bool ok = false; + QString name = KInputDialog::getText( i18n("Enter Model Name"), + i18n("Enter the new name of the model:"), + m_doc->getName(), &ok, UMLApp::app() ); + if (ok) { + setColumnText(0, name); + m_doc->setName(name); + } + break; + } + + case ListPopupMenu::mt_Rename: + temp-> startRename(0); + break; + + case ListPopupMenu::mt_Delete: + deleteItem(temp); + + return; + break; + + case ListPopupMenu::mt_Properties: + /* first check if we are on a diagram */ + if( Model_Utils::typeIsDiagram(lvt) ) { + UMLView * pView = m_doc->findView( temp->getID() ); + if( !pView ) { + return; + } + UMLApp::app()->getDocWindow()->updateDocumentation(false); + pView->showPropDialog(); + UMLApp::app()->getDocWindow()->showDocumentation(pView, true); + temp->cancelRename(0); + return; + } + + /* ok, we are on another object, so find out on which one */ + umlType = object->getBaseType(); + + if ( Model_Utils::typeIsCanvasWidget(lvt) ) { + object->showProperties(ClassPropDlg::page_gen); + } else if(umlType == Uml::ot_Attribute) { + // show the attribute dialog + UMLAttribute* selectedAttribute = static_cast(object); + UMLAttributeDialog dialog( this, selectedAttribute ); + dialog.exec(); + } else if(umlType == Uml::ot_EntityAttribute) { + // show the attribute dialog + UMLEntityAttribute* selectedAttribute = static_cast(object); + UMLEntityAttributeDialog dialog( this, selectedAttribute ); + dialog.exec(); + } else if(umlType == Uml::ot_Operation) { + // show the operation dialog + UMLOperation* selectedOperation = static_cast(object); + UMLOperationDialog dialog( this, selectedOperation ); + dialog.exec(); + } else if(umlType == Uml::ot_Template) { + // show the template dialog + UMLTemplate* selectedTemplate = static_cast(object); + UMLTemplateDialog dialog( this, selectedTemplate ); + dialog.exec(); + } else { + kWarning() << "calling properties on unknown type" << endl; + } + temp -> cancelRename( 0 ); + break; + + case ListPopupMenu::mt_Logical_Folder: + addNewItem( temp, Uml::lvt_Logical_Folder ); + break; + + case ListPopupMenu::mt_UseCase_Folder: + addNewItem( temp, Uml::lvt_UseCase_Folder ); + break; + + case ListPopupMenu::mt_Component_Folder: + addNewItem(temp, Uml::lvt_Component_Folder); + break; + + case ListPopupMenu::mt_Deployment_Folder: + addNewItem(temp, Uml::lvt_Deployment_Folder); + break; + + case ListPopupMenu::mt_EntityRelationship_Folder: + addNewItem(temp, Uml::lvt_EntityRelationship_Folder); + break; + + case ListPopupMenu::mt_Cut: + m_bStartedCut = true; + m_bStartedCopy = false; + UMLApp::app() -> slotEditCut(); + break; + + case ListPopupMenu::mt_Copy: + m_bStartedCut = false; + m_bStartedCopy = true; + UMLApp::app() -> slotEditCopy(); + break; + + case ListPopupMenu::mt_Paste: + UMLApp::app() -> slotEditPaste(); + break; + + default: + { + Uml::Diagram_Type dt = ListPopupMenu::convert_MT_DT(menuType); + if (dt == Uml::dt_Undefined) { + kWarning() << "UMLListView::popupMenuSel: unknown type" + << sel << endl; + } else { + UMLFolder *f = dynamic_cast(object); + if (f == NULL) + kError() << "UMLListView::popupMenuSel(" << menuType + << "): current item's UMLObject is not a UMLFolder" << endl; + else + m_doc->createDiagram(f, dt); + } + } + break; + }//end switch +} + +UMLListViewItem *UMLListView::findFolderForDiagram(Uml::Diagram_Type dt) { + UMLListViewItem *p = static_cast(currentItem()); + if (p && Model_Utils::typeIsFolder(p->getType()) + && !Model_Utils::typeIsRootView(p->getType())) { + return p; + } + switch (dt) { + case Uml::dt_UseCase: + p = m_lv[Uml::mt_UseCase]; + break; + case Uml::dt_Component: + p = m_lv[Uml::mt_Component]; + break; + case Uml::dt_Deployment: + p = m_lv[Uml::mt_Deployment]; + break; + case Uml::dt_EntityRelationship: + p = m_lv[Uml::mt_EntityRelationship]; + break; + default: + p = m_lv[Uml::mt_Logical]; + break; + } + return p; +} + +void UMLListView::slotDiagramCreated( Uml::IDType id ) { + if( m_doc->loading() ) + return; + UMLView *v = m_doc -> findView( id ); + if (!v) + return; + const Uml::Diagram_Type dt = v->getType(); + UMLListViewItem * temp = 0, *p = findFolderForDiagram(dt); + temp = new UMLListViewItem(p, v->getName(), Model_Utils::convert_DT_LVT(dt), id); + setSelected( temp, true ); + UMLApp::app() -> getDocWindow() -> showDocumentation( v , false ); +} + +UMLListViewItem* UMLListView::determineParentItem(UMLObject* object) const { + UMLListViewItem* parentItem = NULL; + UMLListViewItem* current = (UMLListViewItem*) currentItem(); + Uml::ListView_Type lvt = Uml::lvt_Unknown; + if (current) + lvt = current->getType(); + Uml::Object_Type t = object->getBaseType(); + + switch (t) { + case Uml::ot_Attribute: + case Uml::ot_Operation: + case Uml::ot_Template: + case Uml::ot_EnumLiteral: + case Uml::ot_EntityAttribute: + //this will be handled by childObjectAdded + return NULL; + break; + case Uml::ot_Association: + case Uml::ot_Role: + case Uml::ot_Stereotype: + return NULL; // currently no representation in list view + break; + default: + { + UMLPackage *pkg = object->getUMLPackage(); + if (pkg) { + UMLListViewItem* pkgItem = findUMLObject(pkg); + if (pkgItem == NULL) + kError() << "UMLListView::determineParentItem: could not find " + << "parent package " << pkg->getName() << endl; + else + parentItem = pkgItem; + } else if ((lvt == Uml::lvt_UseCase_Folder && + (t == Uml::ot_Actor || t == Uml::ot_UseCase)) + || (lvt == Uml::lvt_Component_Folder && t == Uml::ot_Component) + || (lvt == Uml::lvt_Deployment_Folder && t == Uml::ot_Node) + || (lvt == Uml::lvt_EntityRelationship_Folder && t == Uml::ot_Entity)) { + parentItem = current; + } else if (t == Uml::ot_Datatype) { + parentItem = m_datatypeFolder; + } else { + Uml::Model_Type guess = Model_Utils::guessContainer(object); + parentItem = m_lv[guess]; + } + } + break; + } + return parentItem; +} + +bool UMLListView::mayHaveChildItems(Uml::Object_Type type) { + bool retval = false; + switch (type) { + case Uml::ot_Class: + case Uml::ot_Interface: + case Uml::ot_Enum: + case Uml::ot_Entity: // CHECK: more? + retval = true; + break; + default: + break; + } + return retval; +} + +void UMLListView::slotObjectCreated(UMLObject* object) { + if (m_bCreatingChildObject) { + // @todo eliminate futile signal traffic + // e.g. we get here thru various indirections from + // ClassifierListPage::slot{Up,Down}Clicked() + return; + } + UMLListViewItem* newItem = findUMLObject(object); + if (newItem) { + kDebug() << "UMLListView::slotObjectCreated(" << object->getName() + << ", id= " << ID2STR(object->getID()) + << "): item already exists." << endl; + Uml::Icon_Type icon = Model_Utils::convert_LVT_IT(newItem->getType()); + newItem->setIcon(icon); + return; + } + UMLListViewItem* parentItem = determineParentItem(object); + if (parentItem == NULL) + return; + Uml::Object_Type type = object->getBaseType(); + + connectNewObjectsSlots(object); + const Uml::ListView_Type lvt = Model_Utils::convert_OT_LVT(object); + QString name = object->getName(); + if (type == Uml::ot_Folder) { + UMLFolder *f = static_cast(object); + QString folderFile = f->getFolderFile(); + if (!folderFile.isEmpty()) + name.append(" (" + folderFile + ')'); + } + newItem = new UMLListViewItem(parentItem, name, lvt, object); + if (mayHaveChildItems(type)) { + UMLClassifier *c = static_cast(object); + UMLClassifierListItemList cListItems = c->getFilteredList(Uml::ot_UMLObject); + UMLClassifierListItem *cli; + for (UMLClassifierListItemListIt it(cListItems); (cli = it.current()) != NULL; ++it) + childObjectAdded(cli, c); + } + if (m_doc->loading()) + return; + ensureItemVisible(newItem); + newItem->setOpen(true); + clearSelection(); + setSelected(newItem, true); + UMLApp::app()->getDocWindow()->showDocumentation(object, false); +} + +void UMLListView::connectNewObjectsSlots(UMLObject* object) { + Uml::Object_Type type = object->getBaseType(); + switch( type ) + { + case Uml::ot_Class: + case Uml::ot_Interface: + { + UMLClassifier *c = static_cast(object); + connect(c, SIGNAL(attributeAdded(UMLClassifierListItem*)), + this, SLOT(childObjectAdded(UMLClassifierListItem*))); + connect(c, SIGNAL(attributeRemoved(UMLClassifierListItem*)), + this, SLOT(childObjectRemoved(UMLClassifierListItem*))); + connect(c, SIGNAL(operationAdded(UMLClassifierListItem*)), + this, SLOT(childObjectAdded(UMLClassifierListItem*))); + connect(c, SIGNAL(operationRemoved(UMLClassifierListItem*)), + this, SLOT(childObjectRemoved(UMLClassifierListItem*))); + connect(c, SIGNAL(templateAdded(UMLClassifierListItem*)), + this, SLOT(childObjectAdded(UMLClassifierListItem*))); + connect(c, SIGNAL(templateRemoved(UMLClassifierListItem*)), + this, SLOT(childObjectRemoved(UMLClassifierListItem*))); + connect(object,SIGNAL(modified()),this,SLOT(slotObjectChanged())); + } + break; + case Uml::ot_Enum: + { + UMLEnum *e = static_cast(object); + connect(e, SIGNAL(enumLiteralAdded(UMLClassifierListItem*)), + this, SLOT(childObjectAdded(UMLClassifierListItem*))); + connect(e, SIGNAL(enumLiteralRemoved(UMLClassifierListItem*)), + this, SLOT(childObjectRemoved(UMLClassifierListItem*))); + } + connect(object,SIGNAL(modified()),this,SLOT(slotObjectChanged())); + break; + case Uml::ot_Entity: + { + UMLEntity *ent = static_cast(object); + connect(ent, SIGNAL(entityAttributeAdded(UMLClassifierListItem*)), + this, SLOT(childObjectAdded(UMLClassifierListItem*))); + connect(ent, SIGNAL(entityAttributeRemoved(UMLClassifierListItem*)), + this, SLOT(childObjectRemoved(UMLClassifierListItem*))); + } + connect(object,SIGNAL(modified()),this,SLOT(slotObjectChanged())); + break; + case Uml::ot_Datatype: + case Uml::ot_Attribute: + case Uml::ot_Operation: + case Uml::ot_Template: + case Uml::ot_EnumLiteral: + case Uml::ot_EntityAttribute: + case Uml::ot_Package: + case Uml::ot_Actor: + case Uml::ot_UseCase: + case Uml::ot_Component: + case Uml::ot_Artifact: + case Uml::ot_Node: + case Uml::ot_Folder: + connect(object,SIGNAL(modified()),this,SLOT(slotObjectChanged())); + break; + case Uml::ot_UMLObject: + case Uml::ot_Association: + case Uml::ot_Stereotype: + break; + default: + kWarning() << "unknown type in connectNewObjectsSlots" << endl; + break; + } +} + +void UMLListView::slotObjectChanged() { + if (m_doc->loading()) { //needed for class wizard + return; + } + UMLObject* obj = const_cast( dynamic_cast(sender()) ); + UMLListViewItem* item = findUMLObject(obj); + if(item) { + item->updateObject(); + } +} + +void UMLListView::childObjectAdded(UMLClassifierListItem* obj) { + UMLClassifier *parent = const_cast(dynamic_cast(sender())); + childObjectAdded(obj, parent); +} + +void UMLListView::childObjectAdded(UMLClassifierListItem* child, UMLClassifier* parent) { + if (m_bCreatingChildObject) + return; + const QString text = child->toString(Uml::st_SigNoVis); + UMLListViewItem *childItem = NULL; + UMLListViewItem *parentItem = findUMLObject(parent); + if (parentItem == NULL) { + kDebug() << "UMLListView::childObjectAdded(" << child->getName() + << "): parent " << parent->getName() + << " does not yet exist, creating it now." << endl; + const Uml::ListView_Type lvt = Model_Utils::convert_OT_LVT(parent); + parentItem = new UMLListViewItem(m_lv[Uml::mt_Logical], parent->getName(), lvt, parent); + } else { + childItem = parentItem->findChildObject(child); + } + if (childItem) { + childItem->setText(text); + } else { + const Uml::ListView_Type lvt = Model_Utils::convert_OT_LVT(child); + childItem = new UMLListViewItem(parentItem, text, lvt, child); + if (! m_doc->loading()) { + ensureItemVisible(childItem); + clearSelection(); + setSelected(childItem, true); + } + connectNewObjectsSlots(child); + } +} + +void UMLListView::childObjectRemoved(UMLClassifierListItem* obj) { + UMLClassifier *parent = const_cast(dynamic_cast(sender())); + UMLListViewItem *parentItem = findUMLObject(parent); + if (parentItem == NULL) { + kError() << "UMLListView::childObjectRemoved(" << obj->getName() + << "): cannot find parent UMLListViewItem" << endl; + return; + } + parentItem->deleteChildItem(obj); +} + +void UMLListView::slotDiagramRenamed(Uml::IDType id) { + UMLListViewItem* temp; + UMLView* v = m_doc->findView(id); + if ((temp = findView(v)) == NULL) { + kError() << "UMLListView::slotDiagramRenamed: UMLDoc::findView(" + << ID2STR(id) << ") returns NULL" << endl; + return; + } + temp->setText( v->getName() ); +} + +void UMLListView::setDocument(UMLDoc *d) { + if( m_doc && m_doc != d) + { + //disconnect signals from old doc and reset view + } + m_doc = d; + + connect(m_doc, SIGNAL(sigDiagramCreated(Uml::IDType)), this, SLOT(slotDiagramCreated(Uml::IDType))); + connect(m_doc, SIGNAL(sigDiagramRemoved(Uml::IDType)), this, SLOT(slotDiagramRemoved(Uml::IDType))); + connect(m_doc, SIGNAL(sigDiagramRenamed(Uml::IDType)), this, SLOT(slotDiagramRenamed(Uml::IDType))); + connect(m_doc, SIGNAL(sigObjectCreated(UMLObject *)), this, SLOT(slotObjectCreated(UMLObject *))); + connect(m_doc, SIGNAL(sigObjectRemoved(UMLObject *)), this, SLOT(slotObjectRemoved(UMLObject *))); +} + +void UMLListView::slotObjectRemoved(UMLObject* object) { + if (m_doc->loading()) { //needed for class wizard + return; + } + disconnect(object,SIGNAL(modified()),this,SLOT(slotObjectChanged())); + UMLListViewItem* item = findItem(object->getID()); + delete item; + UMLApp::app()->getDocWindow()->updateDocumentation(true); +} + +void UMLListView::slotDiagramRemoved(Uml::IDType id) { + UMLListViewItem* item = findItem(id); + delete item; + UMLApp::app()->getDocWindow()->updateDocumentation(true); +} + +QDragObject* UMLListView::dragObject() { + UMLListViewItemList selecteditems; + getSelectedItems(selecteditems); + selecteditems.setAutoDelete( false ); + UMLListViewItemListIt it(selecteditems); + UMLListViewItem * item = 0; + UMLListViewItemList list; + list.setAutoDelete( false ); + while((item=it.current()) != 0) { + ++it; + Uml::ListView_Type type = item->getType(); + if (!Model_Utils::typeIsCanvasWidget(type) && !Model_Utils::typeIsDiagram(type) + && !Model_Utils::typeIsClassifierList(type)) { + return 0; + } + list.append(item); + } + UMLDrag *t = new UMLDrag(list, this); + + return t; +} + +void UMLListView::startDrag() { + QDragObject *o = dragObject(); + if (o) + o->dragCopy(); +} + +UMLListViewItem * UMLListView::findUMLObjectInFolder(UMLListViewItem* folder, UMLObject* obj) { + UMLListViewItem *item = static_cast(folder->firstChild()); + while(item) + { + switch(item->getType()) + { + case Uml::lvt_Actor : + case Uml::lvt_UseCase : + case Uml::lvt_Class : + case Uml::lvt_Package : + case Uml::lvt_Subsystem : + case Uml::lvt_Component : + case Uml::lvt_Node : + case Uml::lvt_Artifact : + case Uml::lvt_Interface : + case Uml::lvt_Datatype : + case Uml::lvt_Enum : + case Uml::lvt_Entity : + if(item->getUMLObject() == obj) + return item; + break; + case Uml::lvt_Logical_Folder : + case Uml::lvt_UseCase_Folder : + case Uml::lvt_Component_Folder : + case Uml::lvt_Deployment_Folder : + case Uml::lvt_EntityRelationship_Folder : + case Uml::lvt_Datatype_Folder : + { + UMLListViewItem *temp = findUMLObjectInFolder(item, obj); + if (temp) + return temp; + } + default: + break; + } + item = static_cast(item->nextSibling()); + } + return 0; +} + +UMLListViewItem * UMLListView::findUMLObject(const UMLObject *p) const { + UMLListViewItem *item = static_cast(firstChild()); + while (item) { + UMLListViewItem *testItem = item->findUMLObject(p); + if (testItem) + return testItem; + item = static_cast(item->nextSibling()); + } + return item; +} + +void UMLListView::changeIconOf(UMLObject *o, Uml::Icon_Type to) { + UMLListViewItem *item = findUMLObject(o); + if (item == NULL) + return; + item->setIcon(to); +} + +UMLListViewItem* UMLListView::findView(UMLView* v) { + if (!v) { + kWarning() << "returning 0 from UMLListView::findView()" << endl; + return 0; + } + UMLListViewItem* item; + Uml::Diagram_Type dType = v->getType(); + Uml::ListView_Type type = Model_Utils::convert_DT_LVT( dType ); + Uml::IDType id = v->getID(); + if (dType == Uml::dt_UseCase) { + item = m_lv[Uml::mt_UseCase]; + } else if (dType == Uml::dt_Component) { + item = m_lv[Uml::mt_Component]; + } else if (dType == Uml::dt_Deployment) { + item = m_lv[Uml::mt_Deployment]; + } else if (dType == Uml::dt_EntityRelationship) { + item = m_lv[Uml::mt_EntityRelationship]; + } else { + item = m_lv[Uml::mt_Logical]; + } + + UMLListViewItem* searchStartItem = (UMLListViewItem *)item->firstChild(); + + UMLListViewItem* foundItem = recursiveSearchForView(searchStartItem, type, id); + + if (!foundItem) { + kWarning() << "returning 0 at UMLListView::findView" << endl; + } + return foundItem; +} + +UMLListViewItem* UMLListView::recursiveSearchForView(UMLListViewItem* listViewItem, + Uml::ListView_Type type, Uml::IDType id) { + while (listViewItem) { + if ( Model_Utils::typeIsFolder(listViewItem->getType()) ) { + UMLListViewItem* child = (UMLListViewItem *)listViewItem->firstChild(); + UMLListViewItem* resultListViewItem = recursiveSearchForView(child, type, id); + if (resultListViewItem) { + return resultListViewItem; + } + } else { + if(listViewItem->getType() == type && listViewItem->getID() == id) { + return listViewItem; + } + } + listViewItem = (UMLListViewItem*)listViewItem->nextSibling(); + } + return 0; +} + +UMLListViewItem* UMLListView::findItem(Uml::IDType id) { + UMLListViewItem *temp; + QListViewItemIterator it(this); + for( ; (temp = (UMLListViewItem*)it.current()); ++it ) { + UMLListViewItem * item = temp->findItem(id); + if (item) + return item; + } + return 0; +} + + +// +// This method is called more than once during an instance's lifetime (by UMLDoc)! +// So we must not allocate any memory before freeing the previously allocated one +// or do connect()s. +// +void UMLListView::init() { + if (m_rv == NULL) { + m_rv = new UMLListViewItem(this, i18n("Views"), Uml::lvt_View); + for (int i = 0; i < Uml::N_MODELTYPES; i++) { + Uml::Model_Type mt = (Uml::Model_Type)i; + UMLFolder *sysFolder = m_doc->getRootFolder(mt); + Uml::ListView_Type lvt = Model_Utils::convert_MT_LVT(mt); + m_lv[i] = new UMLListViewItem(m_rv, sysFolder->getLocalName(), lvt, sysFolder); + } + } else { + for (int i = 0; i < Uml::N_MODELTYPES; i++) + deleteChildrenOf(m_lv[i]); + } + UMLFolder *datatypeFolder = m_doc->getDatatypeFolder(); + m_datatypeFolder = new UMLListViewItem(m_lv[Uml::mt_Logical], datatypeFolder->getLocalName(), + Uml::lvt_Datatype_Folder, datatypeFolder); + m_rv->setOpen(true); + for (int i = 0; i < Uml::N_MODELTYPES; i++) + m_lv[i]->setOpen(true); + m_datatypeFolder->setOpen(false); + + //setup misc. + delete m_pMenu; + m_pMenu = 0; + m_bStartedCut = m_bStartedCopy = false; + m_bIgnoreCancelRename = true; + m_bCreatingChildObject = false; +} + +void UMLListView::setView(UMLView * v) { + if(!v) + return; + UMLListViewItem * temp = findView(v); + if(temp) + setSelected(temp, true); +} + +void UMLListView::contentsMouseDoubleClickEvent(QMouseEvent * me) { + UMLListViewItem * item = static_cast( currentItem() ); + if( !item || me -> button() != Qt::LeftButton ) + return; + //see if on view + Uml::ListView_Type lvType = item -> getType(); + if( Model_Utils::typeIsDiagram(lvType) ) { + UMLView * pView = m_doc -> findView( item -> getID() ); + if( !pView ) + return; + UMLApp::app() -> getDocWindow() -> updateDocumentation( false ); + pView -> showPropDialog(); + UMLApp::app() -> getDocWindow() -> showDocumentation( pView, true ); + item -> cancelRename( 0 ); + return; + } + //else see if an object + UMLObject * object = item -> getUMLObject(); + //continue only if we are on a UMLObject + if(!object) + return; + + + Uml::Object_Type type = object -> getBaseType(); + int page = ClassPropDlg::page_gen; + if(type == Uml::ot_Attribute || type == Uml::ot_Operation) + object = (UMLObject *)object -> parent(); + //set what page to show + if(type == Uml::ot_Attribute) + page = ClassPropDlg::page_att; + else if(type == Uml::ot_Operation) + page = ClassPropDlg::page_op; + //FIXME for entityattributes + + if(object) + object->showProperties(page); + item -> cancelRename( 0 );//double click can cause it to go into rename mode. +} + + +bool UMLListView::acceptDrag(QDropEvent* event) const { + QPoint mousePoint = ((UMLListView*)this)->contentsToViewport( event->pos() ); + + UMLListViewItem* item = (UMLListViewItem*)itemAt(mousePoint); + if(!item) { + kDebug() << "UMLListView::acceptDrag: itemAt(mousePoint) returns NULL" + << endl; + return false; + } + ((QListView*)this)->setCurrentItem( (QListViewItem*)item ); + + UMLDrag::LvTypeAndID_List list; + if (! UMLDrag::getClip3TypeAndID(event, list)) { + kDebug() << "UMLListView::acceptDrag: UMLDrag::getClip3TypeAndID returns false" + << endl; + return false; + } + + UMLDrag::LvTypeAndID_It it(list); + UMLDrag::LvTypeAndID * data = 0; + Uml::ListView_Type dstType = item->getType(); + bool accept = true; + while(accept && ((data = it.current()) != 0)) { + ++it; + Uml::ListView_Type srcType = data->type; + switch (srcType) { + case Uml::lvt_Class: + case Uml::lvt_Package: + case Uml::lvt_Interface: + case Uml::lvt_Enum: + if (dstType == Uml::lvt_Logical_View || + dstType == Uml::lvt_Class || + dstType == Uml::lvt_Package) { + accept = !item->isOwnParent(data->id); + } else { + accept = (dstType == Uml::lvt_Logical_Folder); + } + break; + case Uml::lvt_Attribute: + if (dstType == Uml::lvt_Class) { + accept = !item->isOwnParent(data->id); + } + break; + case Uml::lvt_EntityAttribute: + if (dstType == Uml::lvt_Entity) { + accept = !item->isOwnParent(data->id); + } + break; + case Uml::lvt_Operation: + if (dstType == Uml::lvt_Class || + dstType == Uml::lvt_Interface) { + accept = !item->isOwnParent(data->id); + } + break; + case Uml::lvt_Datatype: + accept = (dstType == Uml::lvt_Logical_Folder || + dstType == Uml::lvt_Datatype_Folder || + dstType == Uml::lvt_Class || + dstType == Uml::lvt_Interface || + dstType == Uml::lvt_Package); + break; + case Uml::lvt_Class_Diagram: + case Uml::lvt_Collaboration_Diagram: + case Uml::lvt_State_Diagram: + case Uml::lvt_Activity_Diagram: + case Uml::lvt_Sequence_Diagram: + accept = (dstType == Uml::lvt_Logical_Folder || + dstType == Uml::lvt_Logical_View); + break; + case Uml::lvt_Logical_Folder: + if (dstType == Uml::lvt_Logical_Folder) { + accept = !item->isOwnParent(data->id); + } else { + accept = (dstType == Uml::lvt_Logical_View); + } + break; + case Uml::lvt_UseCase_Folder: + if (dstType == Uml::lvt_UseCase_Folder) { + accept = !item->isOwnParent(data->id); + } else { + accept = (dstType == Uml::lvt_UseCase_View); + } + break; + case Uml::lvt_Component_Folder: + if (dstType == Uml::lvt_Component_Folder) { + accept = !item->isOwnParent(data->id); + } else { + accept = (dstType == Uml::lvt_Component_View); + } + break; + case Uml::lvt_Deployment_Folder: + if (dstType == Uml::lvt_Deployment_Folder) { + accept = !item->isOwnParent(data->id); + } else { + accept = (dstType == Uml::lvt_Deployment_View); + } + break; + case Uml::lvt_EntityRelationship_Folder: + if (dstType == Uml::lvt_EntityRelationship_Folder) { + accept = !item->isOwnParent(data->id); + } else { + accept = (dstType == Uml::lvt_EntityRelationship_Model); + } + break; + case Uml::lvt_Actor: + case Uml::lvt_UseCase: + case Uml::lvt_UseCase_Diagram: + accept = (dstType == Uml::lvt_UseCase_Folder || + dstType == Uml::lvt_UseCase_View); + break; + case Uml::lvt_Subsystem: + accept = (dstType == Uml::lvt_Component_Folder || + dstType == Uml::lvt_Subsystem); + break; + case Uml::lvt_Component: + accept = (dstType == Uml::lvt_Component_Folder || + dstType == Uml::lvt_Component || + dstType == Uml::lvt_Subsystem); + break; + case Uml::lvt_Artifact: + case Uml::lvt_Component_Diagram: + accept = (dstType == Uml::lvt_Component_Folder || + dstType == Uml::lvt_Component_View); + break; + case Uml::lvt_Node: + case Uml::lvt_Deployment_Diagram: + accept = (dstType == Uml::lvt_Deployment_Folder); + break; + case Uml::lvt_Entity: + case Uml::lvt_EntityRelationship_Diagram: + accept = (dstType == Uml::lvt_EntityRelationship_Folder); + break; + default: + accept = false; + break; + } + } + + //kDebug() << "UMLListView::acceptDrag: dstType = " << dstType + // << ", accept=" << accept << endl; + return accept; +} + +void UMLListView::addAtContainer(UMLListViewItem *item, UMLListViewItem *parent) { + UMLCanvasObject *o = static_cast(item->getUMLObject()); + if (o == NULL) { + kDebug() << "UMLListView::addAtContainer(" << item->getText() + << "): item's UMLObject is NULL" << endl; + } else if (Model_Utils::typeIsContainer(parent->getType())) { + /**** TBC: Do this here? + If yes then remove that logic at the callers + and rename this method to moveAtContainer() + UMLPackage *oldPkg = o->getUMLPackage(); + if (oldPkg) + oldPkg->removeObject(o); + *********/ + UMLPackage *pkg = static_cast(parent->getUMLObject()); + o->setUMLPackage(pkg); + pkg->addObject(o); + } else { + kError() << "UMLListView::addAtContainer(" << item->getText() + << "): parent type is " << parent->getType() << endl; + } + UMLView *currentView = UMLApp::app()->getCurrentView(); + if (currentView) + currentView->updateContainment(o); +} + +UMLListViewItem * UMLListView::moveObject(Uml::IDType srcId, Uml::ListView_Type srcType, + UMLListViewItem *newParent) { + if (newParent == NULL) + return NULL; + UMLListViewItem * move = findItem( srcId ); + if (move == NULL) + return NULL; + + UMLObject *newParentObj = NULL; + // Remove the source object at the old parent package. + UMLObject *srcObj = m_doc->findObjectById(srcId); + if (srcObj) { + newParentObj = newParent->getUMLObject(); + if (srcObj == newParentObj) { + kError() << "UMLListView::moveObject(" << srcObj->getName() + << "): Cannot move onto self" << endl; + return NULL; + } + UMLPackage *srcPkg = srcObj->getUMLPackage(); + if (srcPkg) { + if (srcPkg == newParentObj) { + kError() << "UMLListView::moveObject(" << srcObj->getName() + << "): Object is already in target package" << endl; + return NULL; + } + srcPkg->removeObject(srcObj); + } + } + + Uml::ListView_Type newParentType = newParent->getType(); + kDebug() << "UMLListView::moveObject: newParentType is " << newParentType << endl; + UMLListViewItem *newItem = NULL; + + //make sure trying to place in correct location + switch (srcType) { + case Uml::lvt_UseCase_Folder: + case Uml::lvt_Actor: + case Uml::lvt_UseCase: + case Uml::lvt_UseCase_Diagram: + if (newParentType == Uml::lvt_UseCase_Folder || + newParentType == Uml::lvt_UseCase_View) { + newItem = move->deepCopy(newParent); + if (m_doc->loading()) // deletion is not safe while loading + move->setVisible(false); // (the XMI may be corrupted) + else + delete move; + addAtContainer(newItem, newParent); + } + break; + case Uml::lvt_Component_Folder: + case Uml::lvt_Artifact: + case Uml::lvt_Component_Diagram: + if (newParentType == Uml::lvt_Component_Folder || + newParentType == Uml::lvt_Component_View) { + newItem = move->deepCopy(newParent); + if (m_doc->loading()) // deletion is not safe while loading + move->setVisible(false); // (the XMI may be corrupted) + else + delete move; + addAtContainer(newItem, newParent); + } + break; + case Uml::lvt_Subsystem: + if (newParentType == Uml::lvt_Component_Folder || + newParentType == Uml::lvt_Component_View || + newParentType == Uml::lvt_Subsystem) { + newItem = move->deepCopy(newParent); + if (m_doc->loading()) // deletion is not safe while loading + move->setVisible(false); // (the XMI may be corrupted) + else + delete move; + addAtContainer(newItem, newParent); + } + break; + case Uml::lvt_Component: + if (newParentType == Uml::lvt_Component_Folder || + newParentType == Uml::lvt_Component_View || + newParentType == Uml::lvt_Component || + newParentType == Uml::lvt_Subsystem) { + newItem = move->deepCopy(newParent); + if (m_doc->loading()) // deletion is not safe while loading + move->setVisible(false); // (the XMI may be corrupted) + else + delete move; + addAtContainer(newItem, newParent); + } + break; + case Uml::lvt_Deployment_Folder: + case Uml::lvt_Node: + case Uml::lvt_Deployment_Diagram: + if (newParentType == Uml::lvt_Deployment_Folder || + newParentType == Uml::lvt_Deployment_View) { + newItem = move->deepCopy(newParent); + if (m_doc->loading()) // deletion is not safe while loading + move->setVisible(false); // (the XMI may be corrupted) + else + delete move; + addAtContainer(newItem, newParent); + } + break; + case Uml::lvt_EntityRelationship_Folder: + case Uml::lvt_Entity: + case Uml::lvt_EntityRelationship_Diagram: + if (newParentType == Uml::lvt_EntityRelationship_Folder || + newParentType == Uml::lvt_EntityRelationship_Model) { + newItem = move->deepCopy(newParent); + if (m_doc->loading()) // deletion is not safe while loading + move->setVisible(false); // (the XMI may be corrupted) + else + delete move; + addAtContainer(newItem, newParent); + } + break; + case Uml::lvt_Collaboration_Diagram: + case Uml::lvt_Class_Diagram: + case Uml::lvt_State_Diagram: + case Uml::lvt_Activity_Diagram: + case Uml::lvt_Sequence_Diagram: + case Uml::lvt_Logical_Folder: + if (newParentType == Uml::lvt_Logical_Folder || + newParentType == Uml::lvt_Logical_View) { + newItem = move->deepCopy(newParent); + if (m_doc->loading()) // deletion is not safe while loading + move->setVisible(false); // (the XMI may be corrupted) + else + delete move; + addAtContainer(newItem, newParent); + } + break; + case Uml::lvt_Class: + case Uml::lvt_Package: + case Uml::lvt_Interface: + case Uml::lvt_Enum: + case Uml::lvt_Datatype: + if (newParentType == Uml::lvt_Logical_Folder || + newParentType == Uml::lvt_Datatype_Folder || + newParentType == Uml::lvt_Logical_View || + newParentType == Uml::lvt_Class || + newParentType == Uml::lvt_Interface || + newParentType == Uml::lvt_Package) { + newItem = move->deepCopy(newParent); + if (m_doc->loading()) // deletion is not safe while loading + move->setVisible(false); // (the XMI may be corrupted) + else + delete move; + UMLCanvasObject *o = static_cast(newItem->getUMLObject()); + if (o == NULL) { + kDebug() << "moveObject: newItem's UMLObject is NULL" << endl; + } else if (newParentObj == NULL) { + kError() << "UMLListView::moveObject(" << o->getName() + << "): newParentObj is NULL" << endl; + } else { + UMLPackage *pkg = static_cast(newParentObj); + o->setUMLPackage( pkg ); + pkg->addObject( o ); + } + UMLView *currentView = UMLApp::app()->getCurrentView(); + if (currentView) + currentView->updateContainment(o); + } + break; + case Uml::lvt_Attribute: + case Uml::lvt_Operation: + if (newParentType == Uml::lvt_Class || + newParentType == Uml::lvt_Interface) { + // update list view + + newItem = move->deepCopy(newParent); + // we don't delete move right away, it will be deleted in slots, + // called by subsequent steps + //delete move; + + // update model objects + m_bCreatingChildObject = true; + + UMLClassifier *oldParentClassifier = dynamic_cast(srcObj->parent()); + UMLClassifier *newParentClassifier = dynamic_cast(newParentObj); + if (srcType == Uml::lvt_Attribute) { + UMLAttribute *att = dynamic_cast(srcObj); + // We can't use the existing 'att' directly + // because its parent is fixed to the old classifier + // and we have no way of changing that: + // QObject does not permit changing the parent(). + if (att == NULL) { + kError() << "moveObject internal error: srcObj " + << srcObj->getName() << " is not a UMLAttribute" << endl; + } else if (oldParentClassifier->takeItem(att) == -1) { + kError() << "moveObject: oldParentClassifier->takeItem(att " + << att->getName() << ") returns NULL" << endl; + } else { + const QString& nm = att->getName(); + UMLAttribute *newAtt = newParentClassifier->createAttribute(nm, + att->getType(), + att->getVisibility(), + att->getInitialValue()); + newItem->setUMLObject(newAtt); + newParent->addClassifierListItem( newAtt, newItem ); + + connectNewObjectsSlots( newAtt ); + // Let's not forget to update the DocWindow::m_pObject + // because the old one is about to be physically deleted ! + UMLApp::app()->getDocWindow()->showDocumentation(newAtt, true); + delete att; + } + } else { + UMLOperation *op = dynamic_cast(srcObj); + // We can't use the existing 'op' directly + // because its parent is fixed to the old classifier + // and we have no way of changing that: + // QObject does not permit changing the parent(). + if (op && oldParentClassifier->takeItem(op) != -1) { + bool isExistingOp; + Model_Utils::NameAndType_List ntDummyList; + // We need to provide a dummy NameAndType_List + // else UMLClassifier::createOperation will + // bring up an operation dialog. + UMLOperation *newOp = newParentClassifier->createOperation( + op->getName(), &isExistingOp, &ntDummyList); + newOp->setType(op->getType()); + newOp->setVisibility(op->getVisibility()); + UMLAttributeList parmList = op->getParmList(); + for (UMLAttributeListIt plit(parmList); plit.current(); ++plit) { + UMLAttribute *parm = plit.current(); + UMLAttribute *newParm = new UMLAttribute(newParentClassifier, + parm->getName(), + Uml::id_None, + parm->getVisibility(), + parm->getType(), + parm->getInitialValue()); + newParm->setParmKind(parm->getParmKind()); + newOp->addParm(newParm); + } + newItem->setUMLObject(newOp); + newParent->addClassifierListItem( newOp, newItem ); + + connectNewObjectsSlots( newOp ); + + // Let's not forget to update the DocWindow::m_pObject + // because the old one is about to be physically deleted ! + UMLApp::app()->getDocWindow()->showDocumentation(newOp, true); + delete op; + } else { + kError() << "moveObject: oldParentClassifier->takeItem(op) returns NULL" + << endl; + } + } + m_bCreatingChildObject = false; + } + break; + default: + break; + } + return newItem; +} + +void UMLListView::slotDropped(QDropEvent* de, QListViewItem* /* parent */, QListViewItem* item) { + item = (UMLListViewItem *)currentItem(); + if(!item) { + kDebug() << "UMLListView::slotDropped: item is NULL - doing nothing" << endl; + return; + } + UMLDrag::LvTypeAndID_List srcList; + if (! UMLDrag::getClip3TypeAndID(de, srcList)) { + return; + } + UMLListViewItem *newParent = (UMLListViewItem*)item; + kDebug() << "slotDropped: newParent->getText() is " << newParent->getText() << endl; + UMLDrag::LvTypeAndID_It it(srcList); + UMLDrag::LvTypeAndID * src = 0; + while((src = it.current()) != 0) { + ++it; + moveObject(src->id, src->type, newParent); + } +} + +int UMLListView::getSelectedItems(UMLListViewItemList &ItemList) { + ItemList.setAutoDelete( false ); + QListViewItemIterator it(this); + // iterate through all items of the list view + for ( ; it.current(); ++it ) { + if ( it.current()->isSelected() ) { + UMLListViewItem *item = (UMLListViewItem*)it.current(); + ItemList.append(item); + } + } + kDebug() << "UMLListView::getSelectedItems: selItems = " << ItemList.count() << endl; + + return (int)ItemList.count(); +} + +int UMLListView::getSelectedItemsRoot(UMLListViewItemList &ItemList) { + ItemList.setAutoDelete( false ); + QListViewItemIterator it(this); + + // iterate through all items of the list view + for ( ; it.current(); ++it ) { + if ( it.current()->isSelected() ) { + UMLListViewItem *item = (UMLListViewItem*)it.current(); + // this is the trick, we select only the item with a parent unselected + // since we can't select a child and its grandfather without its parent + // we would be able to delete each item individually, without an invalid iterator + if (item && item->parent() && item->parent()->isSelected()==false) { + ItemList.append(item); + } + } + } + kDebug() << "UMLListView::getSelectedItemsRoot: selItems = " << ItemList.count() << endl; + + return (int)ItemList.count(); +} + +UMLListViewItem* UMLListView::createDiagramItem(UMLView *v) { + Uml::ListView_Type lvt = Model_Utils::convert_DT_LVT(v->getType()); + UMLListViewItem *parent = NULL; + UMLFolder *f = v->getFolder(); + if (f) { + parent = findUMLObject(f); + if (parent == NULL) + kError() << "UMLListView::createDiagramItem(" << v->getName() + << "): findUMLObject(" << f->getName() << ") returns NULL" + << endl; + } else { + kDebug() << "UMLListView::createDiagramItem(" << v->getName() + << "): no parent folder set, using predefined folder" << endl; + } + if (parent == NULL) { + parent = determineParentItem(lvt); + lvt = Model_Utils::convert_DT_LVT(v->getType()); + } + UMLListViewItem *item = new UMLListViewItem(parent, v->getName(), lvt, v->getID()); + return item; +} + +/** Creates a new UMLListViewItem from a UMLListViewItem, + if parent is null the ListView Decides who is going to be + the parent */ +UMLListViewItem* UMLListView::createItem(UMLListViewItem& Data, IDChangeLog& IDChanges, + UMLListViewItem* parent /*= 0*/) { + UMLObject* pObject = 0; + UMLListViewItem* item = 0; + Uml::ListView_Type lvt = Data.getType(); + if(!parent) { + parent = determineParentItem(lvt); + if (!parent) + return 0; + } + + switch(lvt) { + case Uml::lvt_Actor: + case Uml::lvt_UseCase: + case Uml::lvt_Class: + case Uml::lvt_Package: + case Uml::lvt_Subsystem: + case Uml::lvt_Component: + case Uml::lvt_Node: + case Uml::lvt_Artifact: + case Uml::lvt_Interface: + case Uml::lvt_Datatype: + case Uml::lvt_Enum: + case Uml::lvt_Entity: + case Uml::lvt_Logical_Folder: + case Uml::lvt_UseCase_Folder: + case Uml::lvt_Component_Folder: + case Uml::lvt_Deployment_Folder: + case Uml::lvt_EntityRelationship_Folder: + /*** + int newID = IDChanges.findNewID(Data.getID()); + //if there is no ListViewItem associated with the new ID, + //it could exist an Item already asocciated if the user chose to reuse an uml object + if(!(item = findItem(newID))) { + pObject = m_doc->findObjectById( IDChanges.findNewID(Data.getID()) ); + item = new UMLListViewItem(parent, Data.getText(), lvt, pObject); + } ***/ + pObject = m_doc->findObjectById( Data.getID() ); + item = new UMLListViewItem(parent, Data.getText(), lvt, pObject); + break; + case Uml::lvt_Datatype_Folder: + item = new UMLListViewItem(parent, Data.getText(), lvt); + break; + case Uml::lvt_Attribute: + case Uml::lvt_EntityAttribute: + case Uml::lvt_Operation: + case Uml::lvt_Template: + case Uml::lvt_EnumLiteral: + { + UMLClassifier *pClass = static_cast(parent->getUMLObject()); + Uml::IDType newID = IDChanges.findNewID( Data.getID() ); + pObject = pClass->findChildObjectById(newID); + if (pObject) { + item = new UMLListViewItem( parent, Data.getText(), lvt, pObject ); + } else { + item = 0; + } + break; + } + case Uml::lvt_UseCase_Diagram: + case Uml::lvt_Sequence_Diagram: + case Uml::lvt_Collaboration_Diagram: + case Uml::lvt_Class_Diagram: + case Uml::lvt_State_Diagram: + case Uml::lvt_Activity_Diagram: + case Uml::lvt_Component_Diagram: + case Uml::lvt_Deployment_Diagram: + case Uml::lvt_EntityRelationship_Diagram: + { + Uml::IDType newID = IDChanges.findNewID(Data.getID()); + UMLView* v = m_doc->findView(newID); + if (v == NULL) { + return NULL; + } + const Uml::ListView_Type lvt = Model_Utils::convert_DT_LVT(v->getType()); + item = new UMLListViewItem(parent, v->getName(), lvt, newID); + } + break; + default: + kWarning() << "createItem() called on unknown type" << endl; + break; + } + return item; +} + +UMLListViewItem* UMLListView::determineParentItem(Uml::ListView_Type lvt) const { + UMLListViewItem* parent = 0; + switch (lvt) { + case Uml::lvt_Datatype: + parent = m_datatypeFolder; + break; + case Uml::lvt_Actor: + case Uml::lvt_UseCase: + case Uml::lvt_UseCase_Folder: + case Uml::lvt_UseCase_Diagram: + parent = m_lv[Uml::mt_UseCase]; + break; + case Uml::lvt_Component_Diagram: + case Uml::lvt_Component: + case Uml::lvt_Artifact: + parent = m_lv[Uml::mt_Component]; + break; + case Uml::lvt_Deployment_Diagram: + case Uml::lvt_Node: + parent = m_lv[Uml::mt_Deployment]; + break; + case Uml::lvt_EntityRelationship_Diagram: + case Uml::lvt_Entity: + parent = m_lv[Uml::mt_EntityRelationship]; + break; + default: + if (Model_Utils::typeIsDiagram(lvt) || !Model_Utils::typeIsClassifierList(lvt)) + parent = m_lv[Uml::mt_Logical]; + break; + } + return parent; +} + +int UMLListView::getSelectedCount() { + QListViewItemIterator it(this); + int count = 0; + // iterate through all items of the list view + for ( ; it.current(); ++it ) { + if ( it.current()->isSelected() ) { + count++; + } + } + + return count; +} + +void UMLListView::focusOutEvent ( QFocusEvent * fe) { + QFocusEvent::Reason reason = fe->reason(); + if (reason != QFocusEvent::Popup) { + clearSelection(); + triggerUpdate(); + } + //repaint(); + + QListView::focusOutEvent(fe); +} + +Uml::ListView_Type UMLListView::rootViewType(UMLListViewItem *item) { + if (item == m_rv) + return Uml::lvt_View; + if (item == m_lv[Uml::mt_Logical]) + return Uml::lvt_Logical_View; + if (item == m_lv[Uml::mt_UseCase]) + return Uml::lvt_UseCase_View; + if (item == m_lv[Uml::mt_Component]) + return Uml::lvt_Component_View; + if (item == m_lv[Uml::mt_Deployment]) + return Uml::lvt_Deployment_View; + if (item == m_lv[Uml::mt_EntityRelationship]) + return Uml::lvt_EntityRelationship_Model; + UMLListViewItem *parent = dynamic_cast(item->parent()); + if (parent) + return rootViewType(parent); + return Uml::lvt_Unknown; +} + +QPixmap & UMLListView::getPixmap(Uml::Icon_Type type) { + if (type < Uml::it_Home || type >= Uml::N_ICONTYPES) { + kWarning() << "getPixmap() called on unknown icon " << type << endl; + // you'll know you have a problem if this shows up in the list + type = Uml::it_Home; + } + return m_Pixmaps[type]; +} + +void UMLListView::loadPixmaps() { + KStandardDirs * dirs = KGlobal::dirs(); + QString dataDir = dirs -> findResourceDir("data", "umbrello/pics/object.png"); + dataDir += "/umbrello/pics/"; + +#define makeBarIcon(iconType, barIcon) m_Pixmaps[iconType] = BarIcon(barIcon) + makeBarIcon(Uml::it_Home, "folder_home"); + makeBarIcon(Uml::it_Folder_Cyan, "folder"); + makeBarIcon(Uml::it_Folder_Cyan_Open, "folder_open"); + makeBarIcon(Uml::it_Folder_Green, "folder_green"); + makeBarIcon(Uml::it_Folder_Green_Open, "folder_green_open"); + makeBarIcon(Uml::it_Folder_Orange, "folder_orange"); + makeBarIcon(Uml::it_Folder_Orange_Open, "folder_orange_open"); + makeBarIcon(Uml::it_Folder_Grey, "folder_grey"); + makeBarIcon(Uml::it_Folder_Grey_Open, "folder_grey_open"); + makeBarIcon(Uml::it_Folder_Red, "folder_red"); + makeBarIcon(Uml::it_Folder_Red_Open, "folder_red_open"); + makeBarIcon(Uml::it_Folder_Violet, "folder_violet"); + makeBarIcon(Uml::it_Folder_Violet_Open, "folder_violet_open"); + + makeBarIcon(Uml::it_Diagram_Activity, "umbrello_diagram_activity"); + makeBarIcon(Uml::it_Diagram_Class, "umbrello_diagram_class"); + makeBarIcon(Uml::it_Diagram_Component, "umbrello_diagram_component"); + makeBarIcon(Uml::it_Diagram_State, "umbrello_diagram_state"); + makeBarIcon(Uml::it_Diagram_Sequence, "umbrello_diagram_sequence"); + makeBarIcon(Uml::it_Diagram_Deployment, "umbrello_diagram_deployment"); + makeBarIcon(Uml::it_Diagram_EntityRelationship, "umbrello_diagram_deployment"); + makeBarIcon(Uml::it_Diagram_Usecase, "umbrello_diagram_usecase"); + makeBarIcon(Uml::it_Diagram_Collaboration, "umbrello_diagram_collaboration"); +#undef makeBarIcon + +#define loadPixmap(iconType, pngName) m_Pixmaps[iconType].load(dataDir + pngName) + loadPixmap(Uml::it_Diagram, "CVnamespace.png"); + loadPixmap(Uml::it_Class, "class.png"); + loadPixmap(Uml::it_Template, "template.png"); + loadPixmap(Uml::it_Package, "package.png"); + loadPixmap(Uml::it_Subsystem, "subsystem.png"); + loadPixmap(Uml::it_Component, "component.png"); + loadPixmap(Uml::it_Node, "node.png"); + loadPixmap(Uml::it_Artifact, "artifact.png"); + loadPixmap(Uml::it_Interface, "interface.png"); + loadPixmap(Uml::it_Datatype, "datatype.png"); + loadPixmap(Uml::it_Enum, "enum.png"); + loadPixmap(Uml::it_Entity, "entity.png"); + loadPixmap(Uml::it_Actor, "actor.png"); + loadPixmap(Uml::it_UseCase, "usecase.png"); + loadPixmap(Uml::it_Public_Method, "CVpublic_meth.png"); + loadPixmap(Uml::it_Private_Method, "CVprivate_meth.png"); + loadPixmap(Uml::it_Protected_Method, "CVprotected_meth.png"); + loadPixmap(Uml::it_Public_Attribute, "CVpublic_var.png"); + loadPixmap(Uml::it_Private_Attribute, "CVprivate_var.png"); + loadPixmap(Uml::it_Protected_Attribute, "CVprotected_var.png"); +#undef loadPixmap +} + +bool UMLListView::isExpandable(Uml::ListView_Type lvt) { + if (Model_Utils::typeIsRootView(lvt) || Model_Utils::typeIsFolder(lvt)) + return true; + switch (lvt) { + case Uml::lvt_Package: + case Uml::lvt_Component: + case Uml::lvt_Subsystem: + return true; + break; + default: + break; + } + return false; +} + +void UMLListView::slotExpanded( QListViewItem * item ) { + UMLListViewItem * myItem= static_cast(item); + if (isExpandable(myItem->getType())) + myItem->updateFolder(); +} + +void UMLListView::slotCollapsed( QListViewItem * item ) { + UMLListViewItem * myItem = static_cast(item); + if (isExpandable(myItem->getType())) + myItem->updateFolder(); +} + +void UMLListView::slotCutSuccessful() { + if( m_bStartedCut ) { + popupMenuSel( ListPopupMenu::mt_Delete ); + //deletion code here + m_bStartedCut = false; + } +} + +void UMLListView::addNewItem(UMLListViewItem *parentItem, Uml::ListView_Type type) { + if (type == Uml::lvt_Datatype) { + parentItem = m_datatypeFolder; + } + + UMLListViewItem * newItem = NULL; + parentItem->setOpen( true ); + + Uml::Icon_Type icon = Model_Utils::convert_LVT_IT(type); + + QString name; + if (Model_Utils::typeIsDiagram(type)) { + Uml::Diagram_Type dt = Model_Utils::convert_LVT_DT(type); + name = getUniqueDiagramName(dt); + newItem = new UMLListViewItem(parentItem, name, type, Uml::id_None); + } else { + Uml::Object_Type ot = Model_Utils::convert_LVT_OT(type); + if (ot == Uml::ot_UMLObject) { + kDebug() << "UMLListView::addNewItem: no UMLObject for listview type " + << type << endl; + return; + } + UMLPackage *parentPkg = + dynamic_cast(parentItem->getUMLObject()); + if (parentPkg == NULL) { + kError() << "UMLListView::addNewItem(type " << type + << "): parentPkg is NULL" << endl; + return; + } + if (Model_Utils::typeIsClassifierList(type)) { + UMLClassifier *parent = static_cast(parentPkg); + name = parent->uniqChildName(ot); + } else { + name = Model_Utils::uniqObjectName(ot, parentPkg); + } + newItem = new UMLListViewItem(parentItem, name, type, (UMLObject*)0); + } + m_bIgnoreCancelRename = false; + newItem->setIcon( icon ); + newItem->setOpen( true ); + newItem->setCreating( true ); + newItem->startRename( 0 ); // calls QListView::ensureItemVisible() + // When the user accepts the rename operation, UMLListViewItem::okRename() + // is called (automatically by QListViewItem.) +} + +bool UMLListView::itemRenamed( QListViewItem * item , int /*col*/ ) { + //if true the item was cancel before this message + if( m_bIgnoreCancelRename ) { + return true; + } + m_bIgnoreCancelRename = true; + UMLListViewItem * renamedItem = static_cast< UMLListViewItem *>( item ) ; + Uml::ListView_Type type = renamedItem -> getType(); + QString newText = renamedItem -> text( 0 ); + renamedItem -> setCreating( false ); + + // If the type is empty then delete it. + if (newText.isEmpty() || newText.contains(QRegExp("^\\s+$"))) { + KMessageBox::error( + kapp -> mainWidget(), + i18n( "The name you entered was invalid.\nCreation process has been canceled." ), + i18n( "Name Not Valid" ) ); + return false; + } + + if( !isUnique( renamedItem, newText ) ) { + //if operation ask if ok not to be unique i.e overloading + if( type == Uml::lvt_Operation ) { + if( KMessageBox::warningYesNo( + kapp -> mainWidget(), + i18n( "The name you entered was not unique.\nIs this what you wanted?" ), + i18n( "Name Not Unique" ), i18n("Use Name"), i18n("Enter New Name") ) == KMessageBox::No ) { + return false; + } + } else { + KMessageBox::error( + kapp -> mainWidget(), + i18n( "The name you entered was not unique!\nCreation process has been canceled." ), + i18n( "Name Not Unique" ) ); + return false; + } + } + + switch( type ) { + case Uml::lvt_Actor: + case Uml::lvt_Class: + case Uml::lvt_Package: + case Uml::lvt_Logical_Folder: + case Uml::lvt_UseCase_Folder: + case Uml::lvt_Component_Folder: + case Uml::lvt_Deployment_Folder: + case Uml::lvt_EntityRelationship_Folder: + case Uml::lvt_Subsystem: + case Uml::lvt_Component: + case Uml::lvt_Node: + case Uml::lvt_Artifact: + case Uml::lvt_Interface: + case Uml::lvt_Datatype: + case Uml::lvt_Enum: + case Uml::lvt_Entity: + case Uml::lvt_UseCase: + { + Uml::Object_Type ot = Model_Utils::convert_LVT_OT(type); + if (! ot) { + kError() << "UMLListView::itemRenamed() internal" << endl; + return false; + } + UMLObject *o = createUMLObject( renamedItem, ot ); + if (type == Uml::lvt_Subsystem) + o->setStereotype("subsystem"); + else if (Model_Utils::typeIsFolder(type)) + o->setStereotype("folder"); + } + break; + + case Uml::lvt_Attribute: + case Uml::lvt_EntityAttribute: + case Uml::lvt_Operation: + case Uml::lvt_Template: + case Uml::lvt_EnumLiteral: + return createChildUMLObject( renamedItem, Model_Utils::convert_LVT_OT(type) ); + break; + + case Uml::lvt_Class_Diagram: + createDiagram( renamedItem, Uml::dt_Class ); + break; + + case Uml::lvt_UseCase_Diagram: + createDiagram( renamedItem, Uml::dt_UseCase ); + break; + + case Uml::lvt_Sequence_Diagram: + createDiagram( renamedItem, Uml::dt_Sequence ); + break; + + case Uml::lvt_Collaboration_Diagram: + createDiagram( renamedItem, Uml::dt_Collaboration ); + break; + + case Uml::lvt_State_Diagram: + createDiagram( renamedItem, Uml::dt_State ); + break; + + case Uml::lvt_Activity_Diagram: + createDiagram( renamedItem, Uml::dt_Activity ); + break; + + case Uml::lvt_Component_Diagram: + createDiagram( renamedItem, Uml::dt_Component ); + break; + + case Uml::lvt_Deployment_Diagram: + createDiagram( renamedItem, Uml::dt_Deployment ); + break; + + case Uml::lvt_EntityRelationship_Diagram: + createDiagram( renamedItem, Uml::dt_EntityRelationship ); + break; + + default: + break; + } + return true; +} + +UMLObject *UMLListView::createUMLObject( UMLListViewItem * item, Uml::Object_Type type ) { + QString name = item -> text( 0 ); + UMLObject * object = NULL; + switch( type ) { + case Uml::ot_UseCase: + object = new UMLUseCase( name ); + break; + + case Uml::ot_Actor: + object = new UMLActor( name ); + break; + + case Uml::ot_Class: + object = new UMLClassifier( name ); + break; + + case Uml::ot_Package: + object = new UMLPackage( name ); + break; + + case Uml::ot_Folder: + object = new UMLFolder( name ); + break; + + case Uml::ot_Component: + object = new UMLComponent( name ); + break; + + case Uml::ot_Node: + object = new UMLNode( name ); + break; + + case Uml::ot_Artifact: + object = new UMLArtifact( name ); + break; + + case Uml::ot_Interface: + { + UMLClassifier *c = new UMLClassifier(name); + c->setBaseType(Uml::ot_Interface); + object = c; + } + break; + + case Uml::ot_Datatype: + { + UMLClassifier *c = new UMLClassifier(name); + c->setBaseType(Uml::ot_Datatype); + object = c; + } + break; + + case Uml::ot_Enum: + object = new UMLEnum( name ); + break; + + case Uml::ot_Entity: + object = new UMLEntity( name ); + break; + + default: + kWarning() << "creating UML Object of unknown type" << endl; + return NULL; + } + + UMLListViewItem * parentItem = static_cast(item->parent()); + const Uml::ListView_Type lvt = parentItem->getType(); + if (! Model_Utils::typeIsContainer(lvt)) { + kError() << "UMLListView::createUMLObject(" << object->getName() + << "): parentItem (" << lvt << " is not a container" << endl; + delete object; + return NULL; + } + UMLPackage *pkg = static_cast(parentItem->getUMLObject()); + object->setUMLPackage(pkg); + pkg->addObject(object); + connectNewObjectsSlots(object); + item -> setUMLObject( object ); + item -> setText( name ); + return object; +} + +bool UMLListView::createChildUMLObject( UMLListViewItem * item, Uml::Object_Type type ) { + m_bCreatingChildObject = true; + QString text = item->text( 0 ); + UMLObject* parent = static_cast( item->parent() )->getUMLObject(); + if( !parent ) { + kError() << "UMLListView::createChildUMLObject: parent UMLObject is NULL" << endl; + m_bCreatingChildObject = false; + return false; + } + + //kDebug() << "UMLListView::createChildUMLObject (" << text << ")" << endl; + UMLObject* newObject = NULL; + if ( type == Uml::ot_EnumLiteral ) { + UMLEnum *owningEnum = static_cast(parent); + newObject = owningEnum->createEnumLiteral(text); + + UMLEnumLiteral* enumLiteral = static_cast(newObject); + text = enumLiteral->toString(Uml::st_SigNoVis); + } else if ( type == Uml::ot_Template ) { + UMLClassifier *owningClassifier = static_cast(parent); + Model_Utils::NameAndType nt; + Model_Utils::Parse_Status st = Model_Utils::parseTemplate(text, nt, owningClassifier); + if (st) { + KMessageBox::error( kapp->mainWidget(), + Model_Utils::psText(st), + i18n("Creation canceled") ); + m_bCreatingChildObject = false; + return false; + } + newObject = owningClassifier->createTemplate(nt.m_name); + UMLTemplate *tmplParm = static_cast(newObject); + tmplParm->setType(nt.m_type); + text = tmplParm->toString(Uml::st_SigNoVis); + } else if (type == Uml::ot_Attribute || type == Uml::ot_EntityAttribute) { + UMLClassifier *owningClass = static_cast(parent); + Model_Utils::NameAndType nt; + Uml::Visibility vis; + Model_Utils::Parse_Status st; + st = Model_Utils::parseAttribute(text, nt, owningClass, &vis); + if (st) { + KMessageBox::error( kapp->mainWidget(), + Model_Utils::psText(st), + i18n("Creation canceled") ); + m_bCreatingChildObject = false; + return false; + } + newObject = owningClass->createAttribute(nt.m_name, nt.m_type, vis, nt.m_initialValue); + UMLAttribute *att = static_cast(newObject); + att->setParmKind(nt.m_direction); + text = att->toString(Uml::st_SigNoVis); + } else if ( type == Uml::ot_Operation ) { + UMLClassifier *owningClassifier = static_cast(parent); + Model_Utils::OpDescriptor od; + Model_Utils::Parse_Status st = Model_Utils::parseOperation(text, od, owningClassifier); + if (st) { + KMessageBox::error( kapp->mainWidget(), + Model_Utils::psText(st), + i18n("Creation canceled") ); + m_bCreatingChildObject = false; + return false; + } + bool isExistingOp = false; + newObject = owningClassifier->createOperation(od.m_name, &isExistingOp, &od.m_args); + if (newObject == NULL || isExistingOp) { + if (isExistingOp) + KMessageBox::error( + kapp -> mainWidget(), + i18n( "The name you entered was not unique!\nCreation process has been canceled." ), + i18n( "Name Not Unique" ) ); + m_bCreatingChildObject = false; + return false; + } + UMLOperation *op = static_cast(newObject); + if (od.m_pReturnType) { + op->setType(od.m_pReturnType); + } + text = op->toString(Uml::st_SigNoVis); + } else { + kError() << "UMLListView::createChildUMLObject called for type " + << type << " (ignored)" << endl; + m_bCreatingChildObject = false; + return false; + } + + // make changes to the object visible to this umllistviewitem + connectNewObjectsSlots( newObject ); + item->setUMLObject( newObject ); + item->setText( text ); + ensureItemVisible(item); + + // as it's a ClassifierListItem add it to the childObjectMap of the parent + UMLClassifierListItem* classifierListItem = static_cast( newObject ); + static_cast( item->parent() )->addClassifierListItem(classifierListItem, item ); + + m_bCreatingChildObject = false; + + if (! m_doc->loading()) + m_doc->setModified(); + return true; +} + +void UMLListView::createDiagram( UMLListViewItem * item, Uml::Diagram_Type type ) { + QString name = item -> text( 0 ); + UMLView * view = m_doc -> findView( type, name ); + if( view ) { + delete item; + return; + } + UMLListViewItem *parentItem = static_cast(item->parent()); + UMLFolder *parentFolder = dynamic_cast(parentItem->getUMLObject()); + if (parentFolder == NULL) { + kError() << "UMLListView::createDiagram(" << name + << "): parent UMLObject is not a UMLFolder" << endl; + delete item; + return; + } + view = new UMLView(parentFolder); + view->setName( name ); + view->setType( type ); + view->setID( UniqueID::gen() ); + m_doc -> addView( view ); + view -> setOptionState( Settings::getOptionState() ); + item -> setID( view -> getID() ); + item -> setText( name ); + view->activate(); + m_doc -> changeCurrentView( view -> getID() ); +} + +QString UMLListView::getUniqueDiagramName(Uml::Diagram_Type type) { + return m_doc->uniqViewName(type); +} + +bool UMLListView::isUnique( UMLListViewItem * item, const QString &name ) { + UMLListViewItem * parentItem = static_cast( item -> parent() ); + Uml::ListView_Type type = item -> getType(); + switch( type ) { + case Uml::lvt_Class_Diagram: + return !m_doc -> findView( Uml::dt_Class, name ); + break; + + case Uml::lvt_Sequence_Diagram: + return !m_doc -> findView( Uml::dt_Sequence, name ); + break; + + case Uml::lvt_UseCase_Diagram: + return !m_doc -> findView( Uml::dt_UseCase, name ); + break; + + case Uml::lvt_Collaboration_Diagram: + return !m_doc -> findView( Uml::dt_Collaboration, name ); + break; + + case Uml::lvt_State_Diagram: + return !m_doc -> findView( Uml::dt_State, name ); + break; + + case Uml::lvt_Activity_Diagram: + return !m_doc -> findView( Uml::dt_Activity, name ); + break; + + case Uml::lvt_Component_Diagram: + return !m_doc->findView(Uml::dt_Component, name); + break; + + case Uml::lvt_Deployment_Diagram: + return !m_doc->findView(Uml::dt_Deployment, name); + break; + + case Uml::lvt_EntityRelationship_Diagram: + return !m_doc->findView(Uml::dt_EntityRelationship, name); + break; + + case Uml::lvt_Actor: + case Uml::lvt_UseCase: + case Uml::lvt_Node: + case Uml::lvt_Artifact: + return !m_doc->findUMLObject( name, Model_Utils::convert_LVT_OT(type) ); + break; + + case Uml::lvt_Class: + case Uml::lvt_Package: + case Uml::lvt_Interface: + case Uml::lvt_Datatype: + case Uml::lvt_Enum: + case Uml::lvt_Entity: + case Uml::lvt_Component: + case Uml::lvt_Subsystem: + case Uml::lvt_Logical_Folder: + case Uml::lvt_UseCase_Folder: + case Uml::lvt_Component_Folder: + case Uml::lvt_Deployment_Folder: + case Uml::lvt_EntityRelationship_Folder: + { + Uml::ListView_Type lvt = parentItem->getType(); + if (!Model_Utils::typeIsContainer(lvt)) + return (m_doc->findUMLObject(name) == NULL); + UMLPackage *pkg = static_cast(parentItem->getUMLObject()); + if (pkg == NULL) { + kError() << "UMLListView::isUnique: internal error - " + << "parent listviewitem is package but has no UMLObject" << endl; + return true; + } + return (pkg->findObject(name) == NULL); + break; + } + + case Uml::lvt_Template: + case Uml::lvt_Attribute: + case Uml::lvt_EntityAttribute: + case Uml::lvt_Operation: + case Uml::lvt_EnumLiteral: + { + UMLClassifier *parent = static_cast(parentItem->getUMLObject()); + return (parent->findChildObject(name) == NULL); + break; + } + + default: + break; + } + return false; +} + +void UMLListView::cancelRename( QListViewItem * item ) { + if( !m_bIgnoreCancelRename ) { + delete item; + m_bIgnoreCancelRename = true; + } +} + +void UMLListView::saveToXMI( QDomDocument & qDoc, QDomElement & qElement) { + QDomElement listElement = qDoc.createElement( "listview" ); + m_rv->saveToXMI(qDoc, listElement); + qElement.appendChild( listElement ); +} + +bool UMLListView::loadFromXMI( QDomElement & element ) { + /* + deleteChildrenOf( m_ucv ); + deleteChildrenOf( m_lv ); + deleteChildrenOf( m_cmpv ); + deleteChildrenOf( m_dplv ); + */ + QDomNode node = element.firstChild(); + QDomElement domElement = node.toElement(); + m_doc->writeToStatusBar( i18n("Loading listview...") ); + while( !domElement.isNull() ) { + if( domElement.tagName() == "listitem" ) { + QString type = domElement.attribute( "type", "-1" ); + if( type == "-1" ) + return false; + Uml::ListView_Type lvType = (Uml::ListView_Type)type.toInt(); + if( lvType == Uml::lvt_View ) { + if( !loadChildrenFromXMI( m_rv, domElement ) ) + return false; + } else + return false; + } + node = node.nextSibling(); + domElement = node.toElement(); + + }//end while + return true; +} + +bool UMLListView::loadChildrenFromXMI( UMLListViewItem * parent, QDomElement & element ) { + QDomNode node = element.firstChild(); + QDomElement domElement = node.toElement(); + const QString pfx("UMLListView::loadChildrenFromXMI: "); + while( !domElement.isNull() ) { + node = domElement.nextSibling(); + if( domElement.tagName() != "listitem" ) { + domElement = node.toElement(); + continue; + } + QString id = domElement.attribute( "id", "-1" ); + QString type = domElement.attribute( "type", "-1" ); + QString label = domElement.attribute( "label", "" ); + QString open = domElement.attribute( "open", "1" ); + if( type == "-1" ) + return false; + Uml::ListView_Type lvType = (Uml::ListView_Type)type.toInt(); + bool bOpen = (bool)open.toInt(); + Uml::IDType nID = STR2ID(id); + UMLObject * pObject = 0; + UMLListViewItem * item = 0; + if (nID != Uml::id_None) { + // The following is an ad hoc hack for the copy/paste code. + // The clip still contains the old children although new + // UMLCLassifierListItems have already been created. + // If the IDChangeLog finds new IDs this means we are in + // copy/paste and need to adjust the child listitems to the + // new UMLCLassifierListItems. + IDChangeLog *idchanges = m_doc->getChangeLog(); + if (idchanges != NULL) { + Uml::IDType newID = idchanges->findNewID(nID); + if (newID != Uml::id_None) { + kDebug() << pfx << " using id " << ID2STR(newID) + << " instead of " << ID2STR(nID) << endl; + nID = newID; + } + } + /************ End of hack for copy/paste code ************/ + + pObject = m_doc->findObjectById(nID); + if (pObject) { + if (label.isEmpty()) + label = pObject->getName(); + } else if (Model_Utils::typeIsFolder(lvType)) { + // Synthesize the UMLFolder here + UMLObject *umlParent = parent->getUMLObject(); + UMLPackage *parentPkg = dynamic_cast(umlParent); + if (parentPkg == NULL) { + kError() << pfx << "umlParent(" << umlParent << ") is not a UMLPackage" + << endl; + domElement = node.toElement(); + continue; + } + UMLFolder *f = new UMLFolder(label, nID); + f->setUMLPackage(parentPkg); + parentPkg->addObject(f); + pObject = f; + item = new UMLListViewItem(parent, label, lvType, pObject); + // Moving all relevant UMLObjects to the new UMLFolder is done below, + // in the switch(lvType) + } + } else if (Model_Utils::typeIsRootView(lvType)) { + // Predefined folders did not have their ID set. + const Uml::Model_Type mt = Model_Utils::convert_LVT_MT(lvType); + nID = m_doc->getRootFolder(mt)->getID(); + } else if (Model_Utils::typeIsFolder(lvType)) { + // Pre-1.2 format: Folders did not have their ID set. + // Pull a new ID now. + nID = UniqueID::get(); + } else { + kError() << pfx << "item of type " << type << " has no ID, skipping." << endl; + domElement = node.toElement(); + continue; + } + + switch( lvType ) { + case Uml::lvt_Actor: + case Uml::lvt_UseCase: + case Uml::lvt_Class: + case Uml::lvt_Interface: + case Uml::lvt_Datatype: + case Uml::lvt_Enum: + case Uml::lvt_Entity: + case Uml::lvt_Package: + case Uml::lvt_Subsystem: + case Uml::lvt_Component: + case Uml::lvt_Node: + case Uml::lvt_Artifact: + case Uml::lvt_Logical_Folder: + case Uml::lvt_UseCase_Folder: + case Uml::lvt_Component_Folder: + case Uml::lvt_Deployment_Folder: + case Uml::lvt_EntityRelationship_Folder: + item = findItem(nID); + if (item == NULL) { + kError() << pfx << "INTERNAL ERROR: " + << "findItem(id " << ID2STR(nID) << ") returns NULL" << endl; + /* + if (pObject && pObject->getUMLPackage() && + parent->getType() != Uml::lvt_Package) { + // Pre-1.2 file format: + // Objects were not nested in their packages. + // Synthesize the nesting here. + UMLPackage *umlpkg = pObject->getUMLPackage(); + UMLListViewItem *pkgItem = findUMLObject(umlpkg); + if (pkgItem == NULL) { + kDebug() << pfx << "synthesizing ListViewItem for package " + << ID2STR(umlpkg->getID()) << endl; + pkgItem = new UMLListViewItem(parent, umlpkg->getName(), + Uml::lvt_Package, umlpkg); + pkgItem->setOpen(true); + } + item = new UMLListViewItem(pkgItem, label, lvType, pObject); + } else { + item = new UMLListViewItem(parent, label, lvType, pObject); + } + */ + } + else if (parent != item->parent()) { + // The existing item was created by the slot event triggered + // by the loading of the corresponding model object from the + // XMI file. + // This early creation is done in order to support the loading + // of foreign XMI files that do not have the umbrello specific + // tag. + // However, now that we encountered the real info, + // we need to delete the existing item: Its parent is always + // one of the default predefined folders, but the actual + // listview item might be located in a user created folder. + // Thanks to Achim Spangler for spotting the problem. + UMLListViewItem *itmParent = dynamic_cast(item->parent()); + kDebug() << pfx << item->getText() << " parent " + << parent->getText() << " (" << parent << ") != " + << itmParent->getText() << " (" << itmParent << ")" << endl; + if (item == m_datatypeFolder && itmParent == m_lv[Uml::mt_Logical]) { + kDebug() << pfx << "Reparenting the Datatypes folder is prohibited" << endl; + } else { + UMLListViewItem *newItem = moveObject(nID, lvType, parent); + item = newItem; + if (item) { + kDebug() << pfx << "Attempted reparenting of " << item->getText() + << "(current parent: " << (itmParent ? itmParent->getText() : "NULL") + << ", new parent: " << parent->getText() << ")" << endl; + } + } + } + break; + case Uml::lvt_Attribute: + case Uml::lvt_EntityAttribute: + case Uml::lvt_Template: + case Uml::lvt_Operation: + case Uml::lvt_EnumLiteral: + item = findItem(nID); + if (item == NULL) { + kDebug() << pfx << "item " << ID2STR(nID) << " (of type " + << lvType << ") does not yet exist..." << endl; + UMLObject* umlObject = parent->getUMLObject(); + if (!umlObject) { + kDebug() << "And also the parent->getUMLObject() does not exist" << endl; + return false; + } + if (nID == Uml::id_None) { + kWarning() << pfx << "lvtype " << lvType << " has id -1" << endl; + } else { + UMLClassifier *classifier = dynamic_cast(umlObject); + if (classifier) { + umlObject = classifier->findChildObjectById(nID); + if (umlObject) { + connectNewObjectsSlots(umlObject); + label = umlObject->getName(); + item = new UMLListViewItem( parent, label, lvType, umlObject); + } else { + kDebug() << pfx << "lvtype " << lvType << " child object " + << ID2STR(nID) << " not found" << endl; + } + } else { + kDebug() << pfx << "cast to classifier object failed" << endl; + } + } + } + break; + case Uml::lvt_Logical_View: + item = m_lv[Uml::mt_Logical]; + break; + case Uml::lvt_Datatype_Folder: + item = m_datatypeFolder; + break; + case Uml::lvt_UseCase_View: + item = m_lv[Uml::mt_UseCase]; + break; + case Uml::lvt_Component_View: + item = m_lv[Uml::mt_Component]; + break; + case Uml::lvt_Deployment_View: + item = m_lv[Uml::mt_Deployment]; + break; + case Uml::lvt_EntityRelationship_Model: + item = m_lv[Uml::mt_EntityRelationship]; + break; + default: + if (Model_Utils::typeIsDiagram(lvType)) { + item = new UMLListViewItem( parent, label, lvType, nID ); + } else { + kError() << pfx << "INTERNAL ERROR: unexpected listview type " + << lvType << " (ID " << ID2STR(nID) << ")" << endl; + } + break; + }//end switch + + if (item) { + item->setOpen( (bool)bOpen ); + if ( !loadChildrenFromXMI(item, domElement) ) { + return false; + } + } else { + kWarning() << "unused list view item " << ID2STR(nID) + << " of lvtype " << lvType << endl; + } + domElement = node.toElement(); + }//end while + return true; +} + +/** Open all items in the list view*/ +void UMLListView::expandAll(QListViewItem *item) { + if(!item) item = firstChild(); + for (item = item->firstChild(); item; item = item->nextSibling()) { + item->setOpen(true); + } +} +/** Close all items in the list view*/ +void UMLListView::collapseAll(QListViewItem *item) { + if(!item) item = firstChild(); + for( item = item->firstChild(); item; item = item->nextSibling()) + item->setOpen(false); +} + +void UMLListView::setStartedCut(bool startedCut) { + m_bStartedCut = startedCut; +} + +void UMLListView::setStartedCopy(bool startedCopy) { + m_bStartedCopy = startedCopy; +} + +bool UMLListView::startedCopy() const { + return m_bStartedCopy; +} + +UMLListViewItem *UMLListView::rootView(Uml::ListView_Type type) { + UMLListViewItem *theView = NULL; + switch (type) { + case Uml::lvt_View: + theView = m_rv; + break; + case Uml::lvt_Logical_View: + theView = m_lv[Uml::mt_Logical]; + break; + case Uml::lvt_UseCase_View: + theView = m_lv[Uml::mt_UseCase]; + break; + case Uml::lvt_Component_View: + theView = m_lv[Uml::mt_Component]; + break; + case Uml::lvt_Deployment_View: + theView = m_lv[Uml::mt_Deployment]; + break; + case Uml::lvt_EntityRelationship_Model: + theView = m_lv[Uml::mt_EntityRelationship]; + break; + case Uml::lvt_Datatype_Folder: // @todo fix asymmetric naming + theView = m_datatypeFolder; + break; + default: + break; + } + return theView; +} + +void UMLListView::deleteChildrenOf(QListViewItem* parent) { + if ( !parent ) { + return; + } + if (parent == m_lv[Uml::mt_Logical]) + m_datatypeFolder = NULL; + while ( parent->firstChild() ) { + delete parent->firstChild(); + } +} + +void UMLListView::closeDatatypesFolder() { + m_datatypeFolder->setOpen(false); +} + + +bool UMLListView::deleteItem(UMLListViewItem *temp) { + if (!temp) + return false; + UMLObject *object = temp->getUMLObject(); + Uml::ListView_Type lvt = temp->getType(); + if ( Model_Utils::typeIsDiagram(lvt) ) { + m_doc->removeDiagram( temp->getID() ); + } else if (temp == m_datatypeFolder) { + // we can't delete the datatypeFolder because umbrello will crash without a special handling + return false; + } else if (Model_Utils::typeIsCanvasWidget(lvt) || Model_Utils::typeIsClassifierList(lvt)) { + UMLPackage *nmSpc = dynamic_cast(object); + if (nmSpc) { + UMLObjectList contained = nmSpc->containedObjects(); + if (contained.count()) { + KMessageBox::error( + kapp->mainWidget(), + i18n("The folder must be emptied before it can be deleted."), + i18n("Folder Not Empty")); + return false; + } + } + UMLCanvasObject *canvasObj = dynamic_cast(object); + if (canvasObj) { + /** + * We cannot just delete canvasObj here: What if the object + * is still being used by others (for example, as a parameter + * or return type of an operation) ? + * Deletion should not have been permitted in the first place + * if the object still has users - but Umbrello is lacking + * that logic. + */ + canvasObj->removeAllChildObjects(); + } + if (object) { + m_doc->removeUMLObject(object); + // Physical deletion of `temp' will be done by Qt signal, see + // UMLDoc::removeUMLObject() + } else { + delete temp; + } + } else { + kWarning() << "umllistview::listpopupmenu::mt_Delete called with unknown type" + << endl; + } + return true; +} + + +#include "umllistview.moc" diff --git a/umbrello/umbrello/umllistview.h b/umbrello/umbrello/umllistview.h new file mode 100644 index 00000000..5656a537 --- /dev/null +++ b/umbrello/umbrello/umllistview.h @@ -0,0 +1,473 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLLISTVIEW_H +#define UMLLISTVIEW_H + +#include +#include +#include +#include "umlnamespace.h" +#include "umllistviewitemlist.h" + +/** + * This is one of the main classes used in this program. + * Information is displayed here in a tree view. No objects are created + * here. A call to @ref UMLDoc make any additions/deletion or updates to + * objects. This class will then wait for a signal before updating the tree view. + * + * @short Displays the list view for the program. + * @author Paul Hensgen + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + +class QMouseEvent; +class QContextMenuEvent; +class QKeyEvent; +class IDChangeLog; +class ListPopupMenu; +class UMLClassifier; +class UMLDoc; +class UMLListViewItem; +class UMLView; +class UMLObject; +class UMLClassifierListItem; + +class UMLListView : public KListView { + Q_OBJECT +public: + + /** + * Constructs the tree view. + * + * @param parent The parent to this. + * @param name The internal name for this class. + */ + UMLListView(QWidget *parent,const char *name); + + /** + * Standard deconstructor. + */ + ~UMLListView(); + + /** + * Sets the document his is associated with. This is important as + * this is required as to setup the callbacks. + * + * @param d The document to associate with this class. + */ + void setDocument(UMLDoc * d); + + /** + * Carries out initalisation of attributes in class. + */ + void init(); + + /** + * Set the current view to the given view. + * + * @param v The current view. + */ + void setView(UMLView* v); + + /** + * Get selected items. + * + * @param ItemList List of UMLListViewItems returned. + * @return The number of selected items. + */ + int getSelectedItems(UMLListViewItemList &ItemList); + + /** + * Get selected items, but only root elements selected (without children). + * + * @param ItemList List of UMLListViewItems returned. + * @return The number of selected items. + */ + int getSelectedItemsRoot(UMLListViewItemList &ItemList); + + /** + * Create a listview item for an existing diagram. + * + * @param v The existing diagram. + */ + UMLListViewItem* createDiagramItem(UMLView *v); + + /** + * CHECK - This is perhaps redundant since the + * UMLListViewItemData => UMLListViewItem merge. + * Creates a new UMLListViewItem from a UMLListViewItem, if + * parent is null the ListView Decides who is going to be the + * parent + */ + UMLListViewItem* createItem(UMLListViewItem& Data, IDChangeLog& IDChanges, + UMLListViewItem* parent = 0); + + /** + * Find the parent folder for a diagram. + * If the currently selected item in the list view is a folder + * then that folder is returned as the parent. + * + * @param dt The Diagram_Type of the diagram. + * The type will only be used if there is no currently + * selected item, or if the current item is not a folder. + * In that case the root folder which is suitable for the + * Diagram_Type is returned. + * @return Pointer to the parent UMLListViewItem for the diagram. + */ + UMLListViewItem *findFolderForDiagram(Uml::Diagram_Type dt); + + /** + * Determine the parent ListViewItem given an UMLObject. + * + * @param object Pointer to the UMLObject for which to look up the parent. + * @return Pointer to the parent UMLListViewItem chosen. + * Returns NULL on error (no parent could be determined.) + */ + UMLListViewItem* determineParentItem(UMLObject* object) const; + + /** + * Determine the parent ListViewItem given a ListView_Type. + * This parent is used for creating new UMLListViewItems. + * + * @param lvt The ListView_Type for which to lookup the parent. + * @return Pointer to the parent UMLListViewItem chosen. + */ + UMLListViewItem* determineParentItem(Uml::ListView_Type lvt) const; + + /** + * Return true if the given Object_Type permits child items. + * A "child item" is anything that qualifies as a UMLClassifierListItem, + * e.g. operations and attributes of classifiers. + */ + static bool mayHaveChildItems(Uml::Object_Type type); + + /** + * Return the amount of items selected. + */ + int getSelectedCount(); + + /** + * Returns the correct pixmap for the given type. + */ + QPixmap & getPixmap( Uml::Icon_Type type ); + + /** + * Returns the document pointer. Called by the UMLListViewItem class. + */ + UMLDoc * getDocument() { + return m_doc; + } + + /** + * Adds a new item to the tree of the given type under the given parent. + * Method will take care of signalling anyone needed on creation of new item. + * e.g. UMLDoc if an UMLObject is created. + */ + void addNewItem(UMLListViewItem * parent, Uml::ListView_Type type); + + /** + * Find an UMLObject in the listview. + * + * @param p Pointer to the object to find in the list view. + * @return Pointer to the UMLObject found or NULL if not found. + */ + UMLListViewItem * findUMLObject(const UMLObject *p) const; + + /** + * Searches through the tree for the item which represents the diagram given + * @param v the diagram to search for + * @return the item which represents the diagram + */ + UMLListViewItem * findView(UMLView *v); + + /** + * Searches through the tree for the item with the given ID. + * + * @param id The ID to search for. + * @return The item with the given ID or NULL if not found. + */ + UMLListViewItem * findItem(Uml::IDType id); + + /** + * Returns the corresponding view if the listview type is one of the root views, + * Root/Logical/UseCase/Component/Deployment/EntityRelation View. + */ + UMLListViewItem *rootView(Uml::ListView_Type type); + + /** + * Changes the icon for the given UMLObject to the given icon. + */ + void changeIconOf(UMLObject *o, Uml::Icon_Type to); + + /** + * Creates a UMLObject out of the given list view item. + */ + UMLObject *createUMLObject( UMLListViewItem * item, Uml::Object_Type type ); + + /** + * Creates a child UMLObject out of the given list view item. + */ + bool createChildUMLObject( UMLListViewItem * item, Uml::Object_Type type ); + + /** + * Creates a diagram out of the given list view item. + */ + void createDiagram( UMLListViewItem * item, Uml::Diagram_Type type ); + + /** + * Returns a unique name for a diagram. + */ + QString getUniqueDiagramName( Uml::Diagram_Type type ); + + /** + * Returns if the given name is unique for the given items type. + */ + bool isUnique( UMLListViewItem * item, const QString &name ); + + /** + * Cancel rename event has occurred for the given item. + */ + void cancelRename( QListViewItem * item ); + + /** + * Set the variable m_bStartedCut + * to indicate that selection should be deleted + * in slotCutSuccessful() + */ + void setStartedCut(bool startedCut); + + /** + * Set the variable m_bStartedCopy. + * NB: While m_bStartedCut is reset as soon as the Cut operation is done, + * the variable m_bStartedCopy is reset much later - upon pasting. + */ + void setStartedCopy(bool startedCopy); + + /** + * Return the variable m_bStartedCopy. + */ + bool startedCopy() const; + + /** + * Moves an object given is unique ID and listview type to an + * other listview parent item. + * Also takes care of the corresponding move in the model. + */ + UMLListViewItem * moveObject(Uml::IDType srcId, Uml::ListView_Type srcType, + UMLListViewItem *newParent); + + /** + * Called for informing the list view that an item was renamed. + */ + bool itemRenamed(QListViewItem* item , int col); + + void closeDatatypesFolder(); + + UMLListViewItem *theRootView() { return m_rv; } + UMLListViewItem *theLogicalView() { return m_lv[Uml::mt_Logical]; } + UMLListViewItem *theUseCaseView() { return m_lv[Uml::mt_UseCase]; } + UMLListViewItem *theComponentView() { return m_lv[Uml::mt_Component]; } + UMLListViewItem *theDeploymentView() { return m_lv[Uml::mt_Deployment]; } + UMLListViewItem *theDatatypeFolder() { return m_datatypeFolder; } + + /** + * Determines the root listview type of the given UMLListViewItem. + * Starts at the given item, compares it against each of the + * predefined root views (Root, Logical, UseCase, Component, + * Deployment, EntityRelationship.) Returns the ListView_Type + * of the matching root view; if no match then continues the + * search using the item's parent, then grandparent, and so forth. + * Returns Uml::lvt_Unknown if no match at all is found. + */ + Uml::ListView_Type rootViewType(UMLListViewItem *item); + + void saveToXMI( QDomDocument & qDoc, QDomElement & qElement); + + bool loadFromXMI( QDomElement & element ); + + bool loadChildrenFromXMI( UMLListViewItem * parent, QDomElement & element ); + +protected: + UMLListViewItem* m_rv; // root view (home) + UMLListViewItem* m_lv[Uml::N_MODELTYPES]; // predefined list view roots + UMLListViewItem* m_datatypeFolder; + ListPopupMenu * m_pMenu; + QString oldText, message; + UMLDoc *m_doc; + bool m_bStartedCut, m_bStartedCopy, m_bIgnoreCancelRename; + + /** + * Used when creating an attribute or an operation to stop it adding a second listViewItem + */ + bool m_bCreatingChildObject; + + QPixmap m_Pixmaps[Uml::N_ICONTYPES]; + + bool eventFilter(QObject *o, QEvent *e); + void contentsMouseReleaseEvent(QMouseEvent * me); + void contentsMousePressEvent(QMouseEvent *me); + void contentsMouseDoubleClickEvent(QMouseEvent * me); + void focusOutEvent ( QFocusEvent * fe); + QDragObject* dragObject(); + void startDrag(); + bool acceptDrag (QDropEvent* event) const; + void keyPressEvent(QKeyEvent *); + + /** + * This methods looks for a object in a folder an its subfolders recursive. + * @param item The folder entry of the list view. + * @param o The object to be found in the folder. + * + * @return The object if found else a NULL pointer. + */ + UMLListViewItem * findUMLObjectInFolder(UMLListViewItem *item, UMLObject *o); + + /** + * Return true if the given list view type can be expanded/collapsed. + */ + static bool isExpandable(Uml::ListView_Type lvt); + + /** + * Loads the pixmaps to use in the list items. + */ + void loadPixmaps(); + + /** + * Deletes all child-items of @p parent. + */ + void deleteChildrenOf( QListViewItem *parent ); + + /** + * Delete a listview item. + * @param temp a non-null UMLListViewItem, for example: + (UMLListViewItem*)currentItem() + * @return true if correctly deleted + */ + bool deleteItem( UMLListViewItem *temp ); + + /** + * Adds a new operation, attribute or template item to a classifier, identical to + * childObjectAdded(obj) but with an explicit parent. + * @param child the child object + * @param parent the parent object + */ + void childObjectAdded(UMLClassifierListItem* child, UMLClassifier* parent); + + /** + * Auxiliary method for moveObject(): Adds the model object at the proper + * new container (package if nested, UMLDoc if at global level), and + * updates the containment relationships in the model. + */ + void addAtContainer(UMLListViewItem *item, UMLListViewItem *parent); + +public slots: + + /** + * Creates a new item to represent a new diagram + * @param id the id of the new diagram + */ + void slotDiagramCreated(Uml::IDType id); + + /** + * renames a diagram in the list view + * @param id the id of the renamed diagram + */ + void slotDiagramRenamed(Uml::IDType id); + + /** + * Creates a new list view item and connects the appropriate signals/slots + * @param object the newly created object + */ + void slotObjectCreated(UMLObject* object); + + /** + * connect some signals into slots in the list view for newly created UMLObjects + */ + void connectNewObjectsSlots(UMLObject* object); + + /** + * Adds a new operation, attribute or template item to a classifier + * @param obj the child object + */ + void childObjectAdded(UMLClassifierListItem* obj); + + /** + * disconnects signals and removes the list view item + * @param object the object about to be removed + */ + void slotObjectRemoved(UMLObject* object); + + /** + * deletes the list view item + * @param obj the object to remove + */ + void childObjectRemoved(UMLClassifierListItem* obj); + + /** + * calls updateObject() on the item representing the sending object + * no parameters, uses sender() to work out which object called the slot + */ + void slotObjectChanged(); + + /** + * removes the item representing a diagram + * @param id the id of the diagram + */ + void slotDiagramRemoved(Uml::IDType id); + + /** + * Called when a right mouse button menu has an item selected + */ + void popupMenuSel(int sel); + + /** + * Something has been dragged and dropped onto the list view + */ + void slotDropped(QDropEvent* de, QListViewItem* parent, QListViewItem* item); + + /** + * calls updateFolder() on the item to update the icon to open + */ + void slotExpanded(QListViewItem* item); + + /** + * calls updateFolder() on the item to update the icon to closed + */ + void slotCollapsed(QListViewItem* item); + + /** + * Open all items in the list view + */ + void expandAll(QListViewItem *item); + + /** + * Close all items in the list view + */ + void collapseAll(QListViewItem *item); + + /** + * Connects to the signal that @ref UMLApp emits when a + * cut operation is successful. + */ + void slotCutSuccessful(); + +private: + /** + * Searches the tree for a diagram (view). + * Used by findView(). + */ + UMLListViewItem* recursiveSearchForView(UMLListViewItem* folder, + Uml::ListView_Type type, Uml::IDType id); + +}; + +#endif diff --git a/umbrello/umbrello/umllistviewitem.cpp b/umbrello/umbrello/umllistviewitem.cpp new file mode 100644 index 00000000..c24ac853 --- /dev/null +++ b/umbrello/umbrello/umllistviewitem.cpp @@ -0,0 +1,696 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "umllistviewitem.h" + +// system includes +#include + +// qt/kde includes +#include +#include +#include +#include +#include +#include + +// app includes +#include "folder.h" +#include "classifier.h" +#include "template.h" +#include "attribute.h" +#include "operation.h" +#include "umldoc.h" +#include "umllistview.h" +#include "umlobjectlist.h" +#include "umlview.h" +#include "model_utils.h" +#include "uniqueid.h" +#include "uml.h" + +UMLListView* UMLListViewItem::s_pListView = 0; + +UMLListViewItem::UMLListViewItem( UMLListView * parent, const QString &name, + Uml::ListView_Type t, UMLObject* o) + : QListViewItem(parent, name) { + init(parent); + m_Type = t; + m_pObject = o; + if (o) + m_nId = o->getID(); + setIcon(Uml::it_Home); + setText( name ); + setRenameEnabled( 0, false ); +} + +UMLListViewItem::UMLListViewItem(UMLListView * parent) + : QListViewItem(parent) { + init(parent); + if (parent == NULL) + kDebug() << "UMLListViewItem constructor called with a NULL listview parent" << endl; +} + +UMLListViewItem::UMLListViewItem(UMLListViewItem * parent) + : QListViewItem(parent) { + init(); +} + +UMLListViewItem::UMLListViewItem(UMLListViewItem * parent, const QString &name, Uml::ListView_Type t,UMLObject*o) + : QListViewItem(parent, name) { + init(); + m_Type = t; + m_pObject = o; + if( !o ) { + m_nId = Uml::id_None; + updateFolder(); + } else { + UMLClassifierListItem *umlchild = dynamic_cast(o); + if (umlchild) + parent->addClassifierListItem(umlchild, this); + updateObject(); + m_nId = o->getID(); + } + setRenameEnabled( 0, !Model_Utils::typeIsRootView(t) ); + setText( name ); +} + +UMLListViewItem::UMLListViewItem(UMLListViewItem * parent, const QString &name, Uml::ListView_Type t,Uml::IDType id) + : QListViewItem(parent, name) { + init(); + m_Type = t; + m_nId = id; + switch (m_Type) { + case Uml::lvt_Collaboration_Diagram: + setIcon(Uml::it_Diagram_Collaboration); + break; + case Uml::lvt_Class_Diagram: + setIcon(Uml::it_Diagram_Class); + break; + case Uml::lvt_State_Diagram: + setIcon(Uml::it_Diagram_State); + break; + case Uml::lvt_Activity_Diagram: + setIcon(Uml::it_Diagram_Activity); + break; + case Uml::lvt_Sequence_Diagram: + setIcon(Uml::it_Diagram_Sequence); + break; + case Uml::lvt_Component_Diagram: + setIcon(Uml::it_Diagram_Component); + break; + case Uml::lvt_Deployment_Diagram: + setIcon(Uml::it_Diagram_Deployment); + break; + case Uml::lvt_UseCase_Diagram: + setIcon(Uml::it_Diagram_Usecase); + break; + default: + setIcon(Uml::it_Diagram); + } + /* + Constructor also used by folder so just make sure we don't need to + to set pixmap to folder. doesn't hurt diagrams. + */ + updateFolder(); + setText( name ); + setRenameEnabled( 0, true ); +} + +UMLListViewItem::~UMLListViewItem() {} + +void UMLListViewItem::init(UMLListView * parent) { + m_Type = Uml::lvt_Unknown; + m_bCreating = false; + m_pObject = NULL; + m_nId = Uml::id_None; + m_nChildren = 0; + if (s_pListView == NULL && parent != NULL) { + kDebug() << "UMLListViewItem::init: s_pListView still NULL, setting it now " + << endl; + s_pListView = parent; + } +} + +Uml::ListView_Type UMLListViewItem::getType() const { + return m_Type; +} + +void UMLListViewItem::addClassifierListItem(UMLClassifierListItem *child, UMLListViewItem *childItem) { + m_comap[child] = childItem; +} + +void UMLListViewItem::deleteChildItem(UMLClassifierListItem *child) { + UMLListViewItem *childItem = findChildObject(child); + if (childItem == NULL) { + kError() << "UMLListViewItem::deleteChildItem(" << child->getName() + << "): child listview item not found" << endl; + return; + } + m_comap.remove(child); + delete childItem; +} + +Uml::IDType UMLListViewItem::getID() const { + if (m_pObject) + return m_pObject->getID(); + return m_nId; +} + +void UMLListViewItem::setID(Uml::IDType id) { + if (m_pObject) { + Uml::IDType oid = m_pObject->getID(); + if (id != Uml::id_None && oid != id) + kDebug() << "UMLListViewItem::setID: new id " << ID2STR(id) + << " does not agree with object id " << ID2STR(oid) << endl; + } + m_nId = id; +} + +bool UMLListViewItem::isOwnParent(Uml::IDType listViewItemID) { + QListViewItem *lvi = (QListViewItem*)s_pListView->findItem(listViewItemID); + if (lvi == NULL) { + kError() << "UMLListViewItem::isOwnParent: ListView->findItem(" + << ID2STR(listViewItemID) << ") returns NULL" << endl; + return true; + } + for (QListViewItem *self = (QListViewItem*)this; self; self = self->parent()) { + if (lvi == self) + return true; + } + return false; +} + +void UMLListViewItem::updateObject() { + if( m_pObject == NULL ) + return; + + Uml::Visibility scope = m_pObject->getVisibility(); + Uml::Object_Type ot = m_pObject->getBaseType(); + QString modelObjText = m_pObject->getName(); + if (Model_Utils::isClassifierListitem(ot)) { + UMLClassifierListItem *pNarrowed = static_cast(m_pObject); + modelObjText = pNarrowed->toString(Uml::st_SigNoVis); + } + setText(modelObjText); + + Uml::Icon_Type icon = Uml::it_Home; + switch (ot) { + case Uml::ot_Package: + if (m_pObject->getStereotype() == "subsystem") + icon = Uml::it_Subsystem; + else + icon = Uml::it_Package; + break; +/* + case Uml::ot_Folder: + { + Uml::ListView_Type lvt = Model_Utils::convert_OT_LVT(m_pObject); + icon = Model_Utils::convert_LVT_IT(lvt); + } + break; + */ + case Uml::ot_Operation: + if (scope == Uml::Visibility::Public) + icon = Uml::it_Public_Method; + else if (scope == Uml::Visibility::Private) + icon = Uml::it_Private_Method; + else if (scope == Uml::Visibility::Implementation) + icon = Uml::it_Private_Method; + else + icon = Uml::it_Protected_Method; + break; + + case Uml::ot_Attribute: + case Uml::ot_EntityAttribute: + if (scope == Uml::Visibility::Public) + icon = Uml::it_Public_Attribute; + else if (scope == Uml::Visibility::Private) + icon = Uml::it_Private_Attribute; + else if (scope == Uml::Visibility::Implementation) + icon = Uml::it_Private_Attribute; + else + icon = Uml::it_Protected_Attribute; + break; + default: + icon = Model_Utils::convert_LVT_IT(m_Type); + break; + }//end switch + if (icon) + setIcon(icon); +} + +void UMLListViewItem::updateFolder() { + Uml::Icon_Type icon = Model_Utils::convert_LVT_IT(m_Type); + if (icon) { + if (Model_Utils::typeIsFolder(m_Type)) + icon = (Uml::Icon_Type)((int)icon + (int)isOpen()); + setIcon(icon); + } +} + +void UMLListViewItem::setOpen( bool open ) { + QListViewItem::setOpen( open ); + updateFolder(); +} + +void UMLListViewItem::setText(const QString &newText) { + m_Label = newText; + QListViewItem::setText(0, newText); +} + +QString UMLListViewItem::getText() const { + return m_Label; +} + +void UMLListViewItem::setIcon(Uml::Icon_Type iconType) { + setPixmap(0, s_pListView->getPixmap(iconType)); +} + +void UMLListViewItem::okRename( int col ) { + QListViewItem::okRename( col ); + UMLDoc* doc = s_pListView->getDocument(); + if (m_bCreating) { + m_bCreating = false; + QString savedLabel = m_Label; + m_Label = text(col); + if ( s_pListView->itemRenamed( this, col ) ) { + s_pListView->ensureItemVisible(this); + doc->setModified(true); + } else { + delete this; + } + return; + } + QString newText = text( col ); + if ( newText == m_Label ) { + return; + } + if( newText.isEmpty() ) { + cancelRenameWithMsg(); + return; + } + switch( m_Type ) { + case Uml::lvt_UseCase: + case Uml::lvt_Actor: + case Uml::lvt_Class: + case Uml::lvt_Package: + case Uml::lvt_UseCase_Folder: + case Uml::lvt_Logical_Folder: + case Uml::lvt_Component_Folder: + case Uml::lvt_Deployment_Folder: + case Uml::lvt_EntityRelationship_Folder: + case Uml::lvt_Interface: + case Uml::lvt_Datatype: + case Uml::lvt_Enum: + case Uml::lvt_EnumLiteral: + case Uml::lvt_Subsystem: + case Uml::lvt_Component: + case Uml::lvt_Node: + if (m_pObject == NULL || !doc->isUnique(newText)) { + cancelRenameWithMsg(); + return; + } + m_pObject -> setName( newText ); + doc->setModified(true); + m_Label = newText; + break; + + case Uml::lvt_Operation: + { + if (m_pObject == NULL) { + cancelRenameWithMsg(); + return; + } + UMLOperation *op = static_cast(m_pObject); + UMLClassifier *parent = static_cast( op -> parent() ); + Model_Utils::OpDescriptor od; + Model_Utils::Parse_Status st = Model_Utils::parseOperation(newText, od, parent); + if (st == Model_Utils::PS_OK) { + // TODO: Check that no operation with the exact same profile exists. + op->setName( od.m_name ); + op->setType( od.m_pReturnType ); + UMLAttributeList parmList = op->getParmList(); + const unsigned newParmListCount = parmList.count(); + if (newParmListCount > od.m_args.count()) { + // Remove parameters at end of of list that no longer exist. + for (unsigned i = od.m_args.count(); i < newParmListCount; i++) { + UMLAttribute *a = parmList.at(i); + op->removeParm(a, false); + } + } + Model_Utils::NameAndType_ListIt lit = od.m_args.begin(); + for (unsigned i = 0; lit != od.m_args.end(); ++lit, ++i) { + const Model_Utils::NameAndType& nm_tp = *lit; + UMLAttribute *a; + if (i < newParmListCount) { + a = parmList.at(i); + } else { + a = new UMLAttribute(op); + a->setID( UniqueID::gen() ); + } + a->setName(nm_tp.m_name); + a->setType(nm_tp.m_type); + a->setParmKind(nm_tp.m_direction); + a->setInitialValue(nm_tp.m_initialValue); + if (i >= newParmListCount) { + op->addParm(a); + } + } + m_Label = op->toString(Uml::st_SigNoVis); + } else { + KMessageBox::error( kapp->mainWidget(), + Model_Utils::psText(st), + i18n("Rename canceled") ); + } + QListViewItem::setText(0, m_Label); + break; + } + + case Uml::lvt_Attribute: + case Uml::lvt_EntityAttribute: + { + if (m_pObject == NULL) { + cancelRenameWithMsg(); + return; + } + UMLClassifier *parent = static_cast(m_pObject->parent()); + Model_Utils::NameAndType nt; + Uml::Visibility vis; + Model_Utils::Parse_Status st; + st = Model_Utils::parseAttribute(newText, nt, parent, &vis); + if (st == Model_Utils::PS_OK) { + UMLObject *exists = parent->findChildObject(newText); + if (exists) { + cancelRenameWithMsg(); + return; + } + m_pObject->setName(nt.m_name); + UMLAttribute *pAtt = static_cast(m_pObject); + pAtt->setType(nt.m_type); + pAtt->setVisibility(vis); + pAtt->setParmKind(nt.m_direction); + pAtt->setInitialValue(nt.m_initialValue); + m_Label = pAtt->toString(Uml::st_SigNoVis); + } else { + KMessageBox::error( kapp->mainWidget(), + Model_Utils::psText(st), + i18n("Rename canceled") ); + } + QListViewItem::setText(0, m_Label); + break; + } + + case Uml::lvt_Template: + { + if (m_pObject == NULL) { + cancelRenameWithMsg(); + return; + } + UMLClassifier *parent = static_cast(m_pObject->parent()); + Model_Utils::NameAndType nt; + Model_Utils::Parse_Status st = Model_Utils::parseTemplate(newText, nt, parent); + if (st == Model_Utils::PS_OK) { + UMLObject *exists = parent->findChildObject(newText); + if (exists) { + cancelRenameWithMsg(); + return; + } + m_pObject->setName(nt.m_name); + UMLTemplate *tmpl = static_cast(m_pObject); + tmpl->setType(nt.m_type); + m_Label = tmpl->toString(Uml::st_SigNoVis); + } else { + KMessageBox::error( kapp->mainWidget(), + Model_Utils::psText(st), + i18n("Rename canceled") ); + } + QListViewItem::setText(0, m_Label); + break; + } + + case Uml::lvt_UseCase_Diagram: + case Uml::lvt_Class_Diagram: + case Uml::lvt_Sequence_Diagram: + case Uml::lvt_Collaboration_Diagram: + case Uml::lvt_State_Diagram: + case Uml::lvt_Activity_Diagram: + case Uml::lvt_Component_Diagram: + case Uml::lvt_Deployment_Diagram: + { + UMLView *view = doc -> findView( getID() ); + if (view == NULL) { + cancelRenameWithMsg(); + return; + } + UMLView *anotherView = doc -> findView( view->getType(), newText ); + if( anotherView && anotherView -> getID() == getID() ) + anotherView = 0; + if (anotherView) { + cancelRenameWithMsg(); + return; + } + view->setName( newText ); + setText(newText); + doc->signalDiagramRenamed(view); + break; + } + default: + KMessageBox::error( kapp->mainWidget() , + i18n("Renaming an item of listview type %1 is not yet implemented.").arg(m_Type), + i18n("Function Not Implemented") ); + QListViewItem::setText(0, m_Label); + break; + } + doc->setModified(true); +} + +void UMLListViewItem::cancelRenameWithMsg() { + KMessageBox::error( kapp->mainWidget() , + i18n("The name you entered was invalid.\nRenaming process has been canceled."), + i18n("Name Not Valid") ); + QListViewItem::setText(0, m_Label); +} + +void UMLListViewItem::cancelRename(int col) { + QListViewItem::cancelRename(col); + if (m_bCreating) { + s_pListView->cancelRename(this); + } +} + +// Sort the listview items by type and position within the corresponding list +// of UMLObjects. If the item does not have an UMLObject then place it last. +int UMLListViewItem::compare(QListViewItem *other, int col, bool ascending) const +{ + UMLListViewItem *ulvi = static_cast(other); + Uml::ListView_Type ourType = getType(); + Uml::ListView_Type otherType = ulvi->getType(); + + if ( ourType < otherType ) + return -1; + if ( ourType > otherType ) + return 1; + // ourType == otherType + const bool subItem = Model_Utils::typeIsClassifierList(ourType); + const int alphaOrder = key(col, ascending).compare(other->key(col, ascending)); + int retval = 0; + QString dbgPfx = "compare(type=" + QString::number((int)ourType) + + ", self=" + getText() + ", other=" + ulvi->getText() + + "): return "; + UMLObject *otherObj = ulvi->getUMLObject(); + if (m_pObject == NULL) { + retval = (subItem ? 1 : alphaOrder); +#ifdef DEBUG_LVITEM_INSERTION_ORDER + kDebug() << dbgPfx << retval << " because (m_pObject==NULL)" << endl; +#endif + return retval; + } + if (otherObj == NULL) { + retval = (subItem ? -1 : alphaOrder); +#ifdef DEBUG_LVITEM_INSERTION_ORDER + kDebug() << dbgPfx << retval << " because (otherObj==NULL)" << endl; +#endif + return retval; + } + UMLClassifier *ourParent = dynamic_cast(m_pObject->parent()); + UMLClassifier *otherParent = dynamic_cast(otherObj->parent()); + if (ourParent == NULL) { + retval = (subItem ? 1 : alphaOrder); +#ifdef DEBUG_LVITEM_INSERTION_ORDER + kDebug() << dbgPfx << retval << " because (ourParent==NULL)" << endl; +#endif + return retval; + } + if (otherParent == NULL) { + retval = (subItem ? -1 : alphaOrder); +#ifdef DEBUG_LVITEM_INSERTION_ORDER + kDebug() << dbgPfx << retval << " because (otherParent==NULL)" << endl; +#endif + return retval; + } + if (ourParent != otherParent) { + retval = (subItem ? 0 : alphaOrder); +#ifdef DEBUG_LVITEM_INSERTION_ORDER + kDebug() << dbgPfx << retval << " because (ourParent != otherParent)" << endl; +#endif + return retval; + } + UMLClassifierListItem *thisUmlItem = dynamic_cast(m_pObject); + UMLClassifierListItem *otherUmlItem = dynamic_cast(otherObj); + if (thisUmlItem == NULL) { + retval = (subItem ? 1 : alphaOrder); +#ifdef DEBUG_LVITEM_INSERTION_ORDER + kDebug() << dbgPfx << retval << " because (thisUmlItem==NULL)" << endl; +#endif + return retval; + } + if (otherUmlItem == NULL) { + retval = (subItem ? -1 : alphaOrder); +#ifdef DEBUG_LVITEM_INSERTION_ORDER + kDebug() << dbgPfx << retval << " because (otherUmlItem==NULL)" << endl; +#endif + return retval; + } + UMLClassifierListItemList items = ourParent->getFilteredList(thisUmlItem->getBaseType()); + int myIndex = items.findRef(thisUmlItem); + int otherIndex = items.findRef(otherUmlItem); + if (myIndex < 0) { + retval = (subItem ? -1 : alphaOrder); + kError() << dbgPfx << retval << " because (myIndex < 0)" << endl; + return retval; + } + if (otherIndex < 0) { + retval = (subItem ? 1 : alphaOrder); + kError() << dbgPfx << retval << " because (otherIndex < 0)" << endl; + return retval; + } + return (myIndex < otherIndex ? -1 : myIndex > otherIndex ? 1 : 0); +} + +UMLListViewItem* UMLListViewItem::deepCopy(UMLListViewItem *newParent) { + QString nm = getText(); + Uml::ListView_Type t = getType(); + UMLObject *o = getUMLObject(); + UMLListViewItem* newItem; + if (o) + newItem = new UMLListViewItem(newParent, nm, t, o); + else + newItem = new UMLListViewItem(newParent, nm, t, m_nId); + UMLListViewItem *childItem = static_cast(firstChild()); + while (childItem) { + childItem->deepCopy(newItem); + childItem = static_cast(childItem->nextSibling()); + } + return newItem; +} + +UMLListViewItem* UMLListViewItem::findUMLObject(const UMLObject *o) { + if (m_pObject == o) + return this; + UMLListViewItem *childItem = static_cast(firstChild()); + while (childItem) { + UMLListViewItem *inner = childItem->findUMLObject(o); + if (inner) + return inner; + childItem = static_cast(childItem->nextSibling()); + } + return NULL; +} + +UMLListViewItem* UMLListViewItem::findChildObject(UMLClassifierListItem *cli) { + ChildObjectMap::iterator it = m_comap.find(cli); + if (it != m_comap.end()) { + return *it; + } + return NULL; +} + +UMLListViewItem * UMLListViewItem::findItem(Uml::IDType id) { + if (getID() == id) + return this; + UMLListViewItem *childItem = static_cast(firstChild()); + while (childItem) { + UMLListViewItem *inner = childItem->findItem(id); + if (inner) + return inner; + childItem = static_cast(childItem->nextSibling()); + } + return NULL; +} + +void UMLListViewItem::saveToXMI( QDomDocument & qDoc, QDomElement & qElement) { + QDomElement itemElement = qDoc.createElement( "listitem" ); + Uml::IDType id = getID(); + QString idStr = ID2STR(id); + //kDebug() << "UMLListViewItem::saveToXMI: id = " << idStr + // << ", type = " << m_Type << endl; + if (id != Uml::id_None) + itemElement.setAttribute( "id", idStr ); + itemElement.setAttribute( "type", m_Type ); + UMLFolder *extFolder = NULL; + if (m_pObject == NULL) { + if (! Model_Utils::typeIsDiagram(m_Type) && m_Type != Uml::lvt_View) + kError() << "UMLListViewItem::saveToXMI(" << m_Label + << "): m_pObject is NULL" << endl; + itemElement.setAttribute( "label", m_Label ); + } else if (m_pObject->getID() == Uml::id_None) { + if (m_Label.isEmpty()) { + kDebug() << "UMLListViewItem::saveToXMI(): Skipping empty item" + << endl; + return; + } + kDebug() << "UMLListViewItem::saveToXMI(): saving local label " + << m_Label << " because umlobject ID is not set" << endl; + itemElement.setAttribute( "label", m_Label ); + } else if (m_pObject->getBaseType() == Uml::ot_Folder) { + extFolder = static_cast(m_pObject); + if (!extFolder->getFolderFile().isEmpty()) { + itemElement.setAttribute("open", "0"); + qElement.appendChild(itemElement); + return; + } + } + itemElement.setAttribute("open", isOpen()); + QDomElement folderRoot; + UMLListViewItem *childItem = static_cast( firstChild() ); + while (childItem) { + childItem->saveToXMI(qDoc, itemElement); + childItem = dynamic_cast ( childItem->nextSibling() ); + } + qElement.appendChild( itemElement ); +} + +bool UMLListViewItem::loadFromXMI(QDomElement& qElement) { + QString id = qElement.attribute( "id", "-1" ); + QString type = qElement.attribute( "type", "-1" ); + QString label = qElement.attribute( "label", "" ); + QString open = qElement.attribute( "open", "1" ); + if (!label.isEmpty()) + setText( label ); + else if (id == "-1") { + kError() << "UMLListViewItem::loadFromXMI: Item of type " + << type << " has neither ID nor label" << endl; + return false; + } + + m_nChildren = qElement.childNodes().count(); + + m_nId = STR2ID(id); + if (m_nId != Uml::id_None) + m_pObject = s_pListView->getDocument()->findObjectById( m_nId ); + m_Type = (Uml::ListView_Type)(type.toInt()); + if (m_pObject) + updateObject(); + setOpen( (bool)open.toInt() ); + return true; +} + diff --git a/umbrello/umbrello/umllistviewitem.h b/umbrello/umbrello/umllistviewitem.h new file mode 100644 index 00000000..99cedf4e --- /dev/null +++ b/umbrello/umbrello/umllistviewitem.h @@ -0,0 +1,285 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + * * + ***************************************************************************/ + +#ifndef UMLLISTVIEWITEM_H +#define UMLLISTVIEWITEM_H + +#include +#include +#include +#include "umlnamespace.h" + +// forward declarations +class UMLListView; +class UMLObject; +class UMLClassifierListItem; + +/** + * Items used by the class @ref UMLListView. This is needed as the type + * and object information is required to be stored. + * + * @short Items used by @ref UMLListView. + * @author Paul Hensgen + * @see UMLListView + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + +class UMLListViewItem : public QListViewItem { +public: + /** + * Sets up an instance. + * + * @param parent The parent to this instance. + * @param name The name of this instance. + * @param t The type of this instance. + * @param o The object it represents. + */ + UMLListViewItem(UMLListView * parent, const QString &name, Uml::ListView_Type t, UMLObject*o=0); + + /** + * Sets up an instance for subsequent loadFromXMI(). + * + * @param parent The parent to this instance. + */ + UMLListViewItem(UMLListView * parent); + + /** + * Sets up an instance for subsequent loadFromXMI(). + * + * @param parent The parent to this instance. + */ + UMLListViewItem(UMLListViewItem * parent); + + /** + * Sets up an instance. + * + * @param parent The parent to this instance. + * @param name The name of this instance. + * @param t The type of this instance. + * @param o The object it represents. + */ + UMLListViewItem(UMLListViewItem * parent, const QString &name, Uml::ListView_Type t, UMLObject*o=0); + + /** + * Sets up an instance. + * + * @param parent The parent to this instance. + * @param name The name of this instance. + * @param t The type of this instance. + * @param id The id of this instance. + */ + UMLListViewItem(UMLListViewItem * parent, const QString &name, Uml::ListView_Type t, Uml::IDType id); + + /** + * Standard deconstructor. + */ + ~UMLListViewItem(); + + /** + * Returns the type this instance represents. + * + * @return The type this instance represents. + */ + Uml::ListView_Type getType() const; + + /** + * Sets the id this class represents. + * This only sets the ID locally, not at the UMLObject that is perhaps + * associated to this UMLListViewItem. + * + * @return The id this class represents. + */ + void setID(Uml::IDType id); + + /** + * Returns the id this class represents. + * + * @return The id this class represents. + */ + Uml::IDType getID() const; + + /** + * Set the UMLObject associated with this instance. + * + * @param obj The object this class represents. + */ + void setUMLObject(UMLObject * obj) { + m_pObject = obj; + } + + /** + * Return the UMLObject associated with this instance. + * + * @return The object this class represents. + */ + UMLObject * getUMLObject() { + return m_pObject; + } + + /** + * Returns true if the UMLListViewItem of the given ID is a parent of + * this UMLListViewItem. + */ + bool isOwnParent(Uml::IDType listViewItemID); + + /** + * Updates the representation of the object. + */ + void updateObject(); + + /** + * Updates the icon on a folder. + */ + void updateFolder(); + + /** + * Overrides default method. + * Will call default method but also makes sure correct icon is shown. + */ + void setOpen( bool open ); + + /** + * Changes the current text and updates the tooltip. + */ + void setText( const QString &text ); + + /** + * Returns the current text. + */ + QString getText() const; + + /** + * Sets if the item is in the middle of being created. + */ + void setCreating( bool creating ) { + m_bCreating = creating; + } + + /** + * Set the pixmap corresponding to the given Icon_Type. + */ + void setIcon(Uml::Icon_Type iconType); + + /** + * Overrides default method to make public. + */ + void cancelRename( int col ); + + /** + * Adds the child listview item representing the given UMLClassifierListItem. + */ + void addClassifierListItem(UMLClassifierListItem *child, UMLListViewItem *childItem); + + /** + * Deletes the child listview item representing the given UMLClassifierListItem. + */ + void deleteChildItem(UMLClassifierListItem *child); + + /** + * Overrides the default sorting to sort by item type. + */ + virtual int compare(QListViewItem *other, int col, bool ascending) const; + + /** + * Returns the number of children of the UMLListViewItem + * containing this object + */ + int childCount() const { + return m_nChildren; + } + + /** + * Create a deep copy of this UMLListViewItem, but using the + * given parent instead of the parent of this UMLListViewItem. + * Return the new UMLListViewItem created. + */ + UMLListViewItem* deepCopy(UMLListViewItem *newParent); + + /** + * Find the UMLListViewItem that is related to the given UMLObject + * in the tree rooted at the current UMLListViewItem. + * Return a pointer to the item or NULL if not found. + */ + UMLListViewItem* findUMLObject(const UMLObject *o); + + /** + * Find the UMLListViewItem that represents the given UMLClassifierListItem + * in the children of the current UMLListViewItem. (Only makes sense if + * the current UMLListViewItem represents a UMLClassifier.) + * Return a pointer to the item or NULL if not found. + */ + UMLListViewItem* findChildObject(UMLClassifierListItem *cli); + + /** + * Find the UMLListViewItem of the given ID in the tree rooted at + * the current UMLListViewItem. + * Return a pointer to the item or NULL if not found. + * + * @param id The ID to search for. + * @return The item with the given ID or NULL if not found. + */ + UMLListViewItem * findItem(Uml::IDType id); + + /** + * saves the listview item to a "listitem" tag + */ + void saveToXMI( QDomDocument& qDoc, QDomElement& qElement); + + /** + * Loads a "listitem" tag, this is only used by the clipboard currently + */ + bool loadFromXMI(QDomElement& qElement); + +protected: + /** + * Initializes key variables of the class. + */ + void init(UMLListView * parent = 0); + + /** + * This function is called if the user presses Enter during in-place renaming + * of the item in column col, reimplemented from QlistViewItem + */ + void okRename( int col ); + + /** + * Auxiliary method for okRename(). + */ + void cancelRenameWithMsg(); + + /** + * This list view all the instance of this class are displayed on. + */ + static UMLListView * s_pListView; + + /** + * Flag used to set the state of creating. + */ + bool m_bCreating; + + /** + * Auxiliary map of child UMLLisViewItems keyed by UMLClassifierListItem. + * Used by findChildObject() for efficiency instead of looping using + * firstChild()/nextSibling() because the latter incur enforceItemVisible() + * and thus expensive sorting. + */ + typedef QMap ChildObjectMap; + + Uml::ListView_Type m_Type; + Uml::IDType m_nId; + int m_nChildren; + UMLObject * m_pObject; + QString m_Label; + ChildObjectMap m_comap; +}; + +#endif diff --git a/umbrello/umbrello/umllistviewitemlist.h b/umbrello/umbrello/umllistviewitemlist.h new file mode 100644 index 00000000..49663163 --- /dev/null +++ b/umbrello/umbrello/umllistviewitemlist.h @@ -0,0 +1,29 @@ +/*************************************************************************** + umllistviewitemlist.h - description + ------------------- + begin : Sat Dec 29 2001 + copyright : (C) 2001 by Gustavo Madrigal + email : gmadrigal@nextphere.com + Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef UMLLISTVIEWITEMLIST_H +#define UMLLISTVIEWITEMLIST_H + +#include + +class UMLListViewItem; + +typedef QPtrList UMLListViewItemList; +typedef QPtrListIterator UMLListViewItemListIt; + +#endif diff --git a/umbrello/umbrello/umlnamespace.cpp b/umbrello/umbrello/umlnamespace.cpp new file mode 100644 index 00000000..f2d81eeb --- /dev/null +++ b/umbrello/umbrello/umlnamespace.cpp @@ -0,0 +1,76 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#include "umlnamespace.h" +#include "qregexp.h" + +namespace Uml { + +bool tagEq (const QString& inTag, const QString& inPattern) { + QString tag = inTag; + QString pattern = inPattern; + tag.remove( QRegExp("^\\w+:") ); // remove leading "UML:" or other + int patSections = pattern.contains( '.' ) + 1; + QString tagEnd = tag.section( '.', -patSections ); + return (tagEnd.lower() == pattern.lower()); +} + +QString Visibility::toString(Value value, bool mnemonic) { + switch (value) { + case Protected: + return (mnemonic ? "#" : "protected"); + break; + case Private: + return (mnemonic ? "-" : "private"); + break; + case Implementation: + return (mnemonic ? "~" : "implementation"); + break; + case Public: + default: + return (mnemonic ? "+" : "public"); + break; + } +} + +Visibility Visibility::fromString(const QString& vis) { + if (vis == "public" || vis == "+") + return Visibility(Public); + else if (vis == "protected" || vis == "#") + return Visibility(Protected); + else if (vis == "private" || vis == "-") + return Visibility(Private); + else if (vis == "~") + return Visibility(Implementation); + else if (vis == "signals") + return Visibility(Protected); + else if (vis == "class") + return Visibility(Private); + else + return Visibility(Public); +} + +Visibility::Visibility(): _v(Public) { +} + +Visibility::Visibility(Value v): _v(v) { +} + +QString Visibility::toString(bool mnemonic) const { + return toString(_v, mnemonic); +} + +Visibility::operator Visibility::Value() const { + return _v; +} + +} // end namespace Uml + diff --git a/umbrello/umbrello/umlnamespace.h b/umbrello/umbrello/umlnamespace.h new file mode 100644 index 00000000..0c0ea2fd --- /dev/null +++ b/umbrello/umbrello/umlnamespace.h @@ -0,0 +1,353 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLNAMESPACE_H +#define UMLNAMESPACE_H + +#include +#include + + +/** + *@author Paul Hensgen + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +namespace Uml { + +enum Model_Type +{ + mt_Logical, + mt_UseCase, + mt_Component, + mt_Deployment, + mt_EntityRelationship, + N_MODELTYPES // must remain last +}; + +enum Object_Type +{ + ot_UMLObject = 100, + ot_Actor, + ot_UseCase, + ot_Package, + ot_Interface, + ot_Datatype, + ot_Enum, + ot_Class, + ot_Association, + ot_Attribute, + ot_Operation, + ot_EnumLiteral, + ot_Template, + ot_Component, + ot_Artifact, + ot_Node, + ot_Stereotype, + ot_Role, + ot_Entity, + ot_EntityAttribute, + ot_Folder +}; + +class Visibility { + public: + enum Value { + Public = 200, + Private, + Protected, + Implementation // objects marked with this are declared in the implementation file. + }; + Visibility(); + Visibility(Value v); + static QString toString(Value value, bool mnemonic); + static Visibility fromString(const QString& vis); + /** + * Convert Visibility value into QString representation. + * + * @param mnemonic If true then return a single character: + * "+" for public, "-" for private, + * "#" for protected or "~" for implementation + */ + QString toString(bool mnemonic = false) const; + operator Value () const; + private: + Value _v; +}; + +enum Widget_Type +{ + wt_UMLWidget = 300, // does not have UMLObject representation + wt_Actor, // has UMLObject representation + wt_UseCase, // has UMLObject representation + wt_Class, // has UMLObject representation + wt_Interface, // has UMLObject representation + wt_Datatype, // has UMLObject representation + wt_Enum, // has UMLObject representation + wt_Entity, // has UMLObject representation + wt_Package, // has UMLObject representation + wt_Object, // has UMLObject representation + wt_Note, // does not have UMLObject representation + wt_Box, // does not have UMLObject representation + wt_Message, // does not have UMLObject representation + wt_Text, // does not have UMLObject representation + wt_State, // does not have UMLObject representation + wt_Activity, // does not have UMLObject representation + wt_Component, // has UMLObject representation + wt_Artifact, // has UMLObject representation + wt_Node, // has UMLObject representation + wt_Association, // has UMLObject representation + wt_ForkJoin // does not have UMLObject representation +}; + +enum Diagram_Type +{ + //the values in this enum are saved out to the file + //for file compatibility, only add new values to the end + dt_Undefined = 0, + dt_Class, + dt_UseCase, + dt_Sequence, + dt_Collaboration, + dt_State, + dt_Activity, + dt_Component, + dt_Deployment, + dt_EntityRelationship +}; + +enum Association_Type +{ + at_Generalization = 500, + at_Aggregation, + at_Dependency, + at_Association, + at_Association_Self, + at_Coll_Message, + at_Seq_Message, + at_Coll_Message_Self, + at_Seq_Message_Self, + at_Containment, + at_Composition, + at_Realization, + at_UniAssociation, + at_Anchor, + at_State, + at_Activity, + at_Relationship, + at_Unknown = - 1 +}; + +enum Signature_Type +{ + st_NoSig = 600, + st_ShowSig, + st_SigNoVis, + st_NoSigNoVis +}; + +enum Text_Role +{ + tr_Floating = 700, //text widget on diagrams + tr_MultiA, //Text for Multiple A + tr_MultiB, //Text for Multiple B + tr_Name, //middle text on most associations + tr_Seq_Message, //message on seq diagram between two objects + tr_Seq_Message_Self, //message to self on seq diagram - feature not implemented yet + tr_Coll_Message, //message between two objects on a collab diagram + tr_Coll_Message_Self, //message to object self on collab diagram + tr_State, + tr_RoleAName, //RoleA text on associations + tr_RoleBName, //RoleB text on associations + tr_ChangeA, //Changeability A text on associations + tr_ChangeB //Changeability B text on associations +}; + +enum ListView_Type +{ + //the values in this enum are saved out to the file + //for file compatibility, only add new values to the end + lvt_View = 800, + lvt_Logical_View, + lvt_UseCase_View, + lvt_Logical_Folder, + lvt_UseCase_Folder, + lvt_UseCase_Diagram, + lvt_Collaboration_Diagram, + lvt_Class_Diagram, + lvt_State_Diagram, + lvt_Activity_Diagram, + lvt_Sequence_Diagram, + lvt_Actor, + lvt_UseCase, + lvt_Class, + lvt_Attribute, + lvt_Operation, + lvt_Template, + lvt_Interface, + lvt_Package, + lvt_Component_Diagram, + lvt_Component_Folder, + lvt_Component_View, + lvt_Component, + lvt_Diagrams, // currently unused + lvt_Artifact, + lvt_Deployment_Diagram, + lvt_Deployment_Folder, + lvt_Deployment_View, + lvt_Node, + lvt_Datatype, + lvt_Datatype_Folder, + lvt_Enum, + lvt_Entity, + lvt_EntityAttribute, + lvt_EntityRelationship_Diagram, + lvt_EntityRelationship_Folder, + lvt_EntityRelationship_Model, + lvt_Subsystem, + lvt_Model, + lvt_EnumLiteral, + lvt_Unknown = -1 +}; + +enum Icon_Type +{ + it_Home = 0, + it_Folder_Cyan, + it_Folder_Cyan_Open, + it_Folder_Green, + it_Folder_Green_Open, + it_Folder_Grey, + it_Folder_Grey_Open, + it_Folder_Red, + it_Folder_Red_Open, + it_Folder_Violet, + it_Folder_Violet_Open, + it_Folder_Orange, + it_Folder_Orange_Open, + it_Diagram, //change to have different one for each type of diagram + it_Class, + it_Template, + it_Package, + it_Subsystem, + it_Component, + it_Node, + it_Artifact, + it_Interface, + it_Datatype, + it_Enum, + it_Entity, + it_Actor, + it_UseCase, + it_Public_Method, + it_Private_Method, + it_Protected_Method, + it_Public_Attribute, + it_Private_Attribute, + it_Protected_Attribute, + it_Diagram_Activity, + it_Diagram_Class, + it_Diagram_Collaboration, + it_Diagram_Component, + it_Diagram_Deployment, + it_Diagram_EntityRelationship, + it_Diagram_Sequence, + it_Diagram_State, + it_Diagram_Usecase, + N_ICONTYPES // must remain last +}; + +enum Changeability_Type +{ + chg_Changeable = 900, + chg_Frozen, + chg_AddOnly +}; + +enum Sequence_Message_Type +{ + //This is saved out to the file so only add new entries at the end + sequence_message_synchronous = 1000, + sequence_message_asynchronous, + sequence_message_creation +}; + +enum DBIndex_Type +{ + None = 1100, + Primary, + Index, + Unique +}; + +/** + * Constants used for indexing the roles of associations. + */ +enum Role_Type { A, B }; + +/** + * Direction of operation parameters: + * in = operation uses the parameter as an input value + * out = operation fills the parameter as a return value + * inout = operation both reads and writes the parameter + * The numeric values of this enum are not currently saved to file. + */ +enum Parameter_Direction { pd_In, pd_InOut, pd_Out }; + +/** + * Supported programming languages + */ +enum Programming_Language { + pl_ActionScript, + pl_Ada, + pl_Cpp, + pl_CSharp, + pl_D, + pl_IDL, + pl_Java, + pl_JavaScript, + pl_Pascal, + pl_Perl, + pl_PHP, + pl_PHP5, + pl_Python, + pl_Ruby, + pl_SQL, + pl_Tcl, + pl_XMLSchema, + pl_Reserved +}; + +/** + * The data type used for unique IDs. + */ +typedef std::string IDType; +/** + * Reserved value for uninitialized/illegal ID. + */ +const IDType id_None = "-1"; +const IDType id_Reserved = "0"; + +# define STR2ID(id) id.ascii() +# define ID2STR(id) QString(id.c_str()) + +// KDE4 compatibility +# define kDebug kdDebug +# define kWarning kdWarning +# define kError kdError + +/** + * Function for comparing tags in XMI files. + */ +bool tagEq (const QString& tag, const QString& pattern); + +} // end namespace Uml + +#endif diff --git a/umbrello/umbrello/umlobject.cpp b/umbrello/umbrello/umlobject.cpp new file mode 100644 index 00000000..e28ed7da --- /dev/null +++ b/umbrello/umbrello/umlobject.cpp @@ -0,0 +1,753 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "umlobject.h" +// qt/kde includes +#include +#include +#include +// app includes +#include "uniqueid.h" +#include "uml.h" +#include "umldoc.h" +#include "umllistview.h" +#include "umllistviewitem.h" +#include "package.h" +#include "folder.h" +#include "stereotype.h" +#include "object_factory.h" +#include "model_utils.h" +#include "codeimport/import_utils.h" +#include "docwindow.h" +#include "dialogs/classpropdlg.h" + +UMLObject::UMLObject(const UMLObject * parent, const QString &name, Uml::IDType id) + : QObject(const_cast(parent), "UMLObject" ) { + init(); + if (id == Uml::id_None) + m_nId = UniqueID::gen(); + else + m_nId = id; + m_Name = name; +} + +UMLObject::UMLObject(const QString &name, Uml::IDType id) + : QObject(UMLApp::app()->getDocument()) { + init(); + if (id == Uml::id_None) + m_nId = UniqueID::gen(); + else + m_nId = id; + m_Name = name; +} + +UMLObject::UMLObject(const UMLObject * parent) + : QObject(const_cast(parent)) { + init(); +} + +UMLObject::~UMLObject() { +} + +void UMLObject::init() { + m_BaseType = Uml::ot_UMLObject; + m_nId = Uml::id_None; + m_pUMLPackage = NULL; + m_Name = ""; + m_Vis = Uml::Visibility::Public; + m_pStereotype = NULL; + m_Doc = ""; + m_bAbstract = false; + m_bStatic = false; + m_bInPaste = false; + m_bCreationWasSignalled = false; + m_pSecondary = NULL; +} + +bool UMLObject::showProperties(int page, bool assoc) { + DocWindow *docwindow = UMLApp::app()->getDocWindow(); + docwindow->updateDocumentation(false); + ClassPropDlg* dlg = new ClassPropDlg((QWidget*)UMLApp::app(), this, page, assoc); + bool modified = false; + if (dlg->exec()) { + docwindow->showDocumentation(this, true); + UMLApp::app()->getDocument()->setModified(true); + modified = true; + } + dlg->close(true); //wipe from memory + return modified; +} + +bool UMLObject::acceptAssociationType(Uml::Association_Type) +{// A UMLObject accepts nothing. This should be reimplemented by the subclasses + return false; +} + +void UMLObject::setID(Uml::IDType NewID) { + m_nId = NewID; + emitModified(); +} + +void UMLObject::setName(const QString &strName) { + m_Name = strName; + emitModified(); +} + +QString UMLObject::getName() const { + return m_Name; +} + +QString UMLObject::getFullyQualifiedName(const QString& separator, + bool includeRoot /* = false */) const { + QString fqn; + if (m_pUMLPackage) { + bool skipPackage = false; + if (!includeRoot) { + UMLDoc *umldoc = UMLApp::app()->getDocument(); + if (umldoc->rootFolderType(m_pUMLPackage) != Uml::N_MODELTYPES || + m_pUMLPackage == umldoc->getDatatypeFolder()) + skipPackage = true; + } + if (!skipPackage) { + QString tempSeparator = separator; + if (tempSeparator.isEmpty()) + tempSeparator = UMLApp::app()->activeLanguageScopeSeparator(); + fqn = m_pUMLPackage->getFullyQualifiedName(tempSeparator, includeRoot); + fqn.append(tempSeparator); + } + } + fqn.append(m_Name); + return fqn; +} + +bool UMLObject::operator==(UMLObject & rhs ) { + if( this == &rhs ) + return true; + + //don't compare IDs, these are program specific and + //don't mean the objects are the same + //***** CHECK: Who put in this comment? What was the reason? + //***** Currently some operator== in umbrello compare the IDs + //***** while others don't. + + if( m_Name != rhs.m_Name ) + return false; + + // Packages create different namespaces, therefore they should be + // part of the equality test. + if( m_pUMLPackage != rhs.m_pUMLPackage ) + return false; + + // Making the type part of an object's identity has its problems: + // Not all programming languages support declarations of the same + // name but different type. + // In such cases, the code generator is responsible for generating + // the appropriate error message. + if( m_BaseType != rhs.m_BaseType ) + return false; + + // The documentation should not be part of the equality test. + // If two objects are the same but differ only in their documentation, + // what does that mean? + //if( m_Doc != rhs.m_Doc ) + // return false; + + // The scope should not be part of the equality test. + // What does it mean if two objects are the same but differ in their + // scope? - I'm not aware of any programming language that would + // support that. + //if( m_Vis != rhs.m_Vis ) + // return false; + + // See comments above + //if( m_pStereotype != rhs.m_pStereotype ) + // return false; + + // See comments above + //if( m_bAbstract != rhs.m_bAbstract ) + // return false; + + // See comments above + //if( m_bStatic != rhs.m_bStatic ) + // return false; + + return true; +} + +void UMLObject::copyInto(UMLObject *rhs) const +{ + // Data members with copy constructor + rhs->m_Doc = m_Doc; + rhs->m_pStereotype = m_pStereotype; + rhs->m_bAbstract = m_bAbstract; + rhs->m_bStatic = m_bStatic; + rhs->m_BaseType = m_BaseType; + rhs->m_Vis = m_Vis; + rhs->m_pUMLPackage = m_pUMLPackage; + + // We don't want the same name existing twice. + rhs->m_Name = Model_Utils::uniqObjectName(m_BaseType, m_pUMLPackage, m_Name); + + // Create a new ID. + rhs->m_nId = UniqueID::gen(); + + // Hope that the parent from QObject is okay. + if (rhs->parent() != parent()) + kDebug() << "copyInto has a wrong parent" << endl; +} + + +bool UMLObject::getAbstract() const{ + return m_bAbstract; +} + +void UMLObject::setAbstract(bool bAbstract) { + m_bAbstract = bAbstract; + emitModified(); +} + +void UMLObject::setInPaste(bool bInPaste /* =true */) { + m_bInPaste = bInPaste; +} + +/** Returns true if this UMLObject has classifier scope, otherwise false (the default). */ +bool UMLObject::getStatic() const +{ + return m_bStatic; +} +/** Sets the value for m_bStatic. */ +void UMLObject::setStatic(bool bStatic) +{ + m_bStatic = bStatic; + emitModified(); +} + +void UMLObject::emitModified() +{ + UMLDoc *umldoc = UMLApp::app()->getDocument(); + if (! umldoc->loading()) + emit modified(); +} + +void UMLObject::setDoc(const QString &d) { + m_Doc = d; + //emit modified(); No, this is done centrally at DocWindow::updateDocumentation() +} + +Uml::Object_Type UMLObject::getBaseType() const { + return m_BaseType; +} + +void UMLObject::setBaseType(Uml::Object_Type ot) { + m_BaseType = ot; +} + +Uml::IDType UMLObject::getID() const { + return m_nId; +} + +QString UMLObject::getDoc() const { + return m_Doc; +} + +Uml::Visibility UMLObject::getVisibility() const { + return m_Vis; +} + +void UMLObject::setVisibility(Uml::Visibility s) { + m_Vis = s; + emitModified(); +} + +void UMLObject::setUMLStereotype(UMLStereotype *stereo) { + if (stereo == m_pStereotype) + return; + if (stereo) { + stereo->incrRefCount(); + } + if (m_pStereotype) { + m_pStereotype->decrRefCount(); + if (m_pStereotype->refCount() == 0) { + UMLDoc *pDoc = UMLApp::app()->getDocument(); + pDoc->removeStereotype(m_pStereotype); + delete m_pStereotype; + } + } + m_pStereotype = stereo; + // TODO: don't emit modified() if predefined folder + emitModified(); +} + +void UMLObject::setStereotype(const QString &_name) { + if (_name.isEmpty()) { + setUMLStereotype(NULL); + return; + } + UMLDoc *pDoc = UMLApp::app()->getDocument(); + UMLStereotype *s = pDoc->findOrCreateStereotype(_name); + setUMLStereotype(s); +} + +void UMLObject::setPackage(const QString &_name) { + UMLObject *pkgObj = NULL; + if (!_name.isEmpty()) { + UMLDoc* umldoc = UMLApp::app()->getDocument(); + pkgObj = umldoc->findUMLObject(_name); + if (pkgObj == NULL) { + kDebug() << "UMLObject::setPackage: creating UMLPackage " + << _name << " for " << m_Name << endl; + pkgObj = Import_Utils::createUMLObject(Uml::ot_Package, _name); + } else { + const Uml::Object_Type ot = pkgObj->getBaseType(); + if (ot != Uml::ot_Package && ot != Uml::ot_Folder && ot != Uml::ot_Component) { + kError() << "UMLObject::setPackage(" << m_Name << "): " + << "existing " << _name << " is not a container" << endl; + // This should not happen - if it does, there may be further problems. + // A container name should not overlap with another name in the same scope. + pkgObj = Import_Utils::createUMLObject(Uml::ot_Package, _name); + } + } + } + setUMLPackage( static_cast(pkgObj) ); +} + +void UMLObject::setUMLPackage(UMLPackage* pPkg) { + m_pUMLPackage = pPkg; + emitModified(); +} + +const UMLStereotype * UMLObject::getUMLStereotype() { + return m_pStereotype; +} + +QString UMLObject::getStereotype(bool includeAdornments /* = false */) const { + if (m_pStereotype == NULL) + return ""; + QString name = m_pStereotype->getName(); + if (includeAdornments) + name = QString::fromUtf8("«") + name + QString::fromUtf8("»"); + return name; +} + +QString UMLObject::getPackage(const QString& separator, bool includeRoot) { + QString tempSeparator = separator; + if (tempSeparator.isEmpty()) + tempSeparator = UMLApp::app()->activeLanguageScopeSeparator(); + QString fqn = getFullyQualifiedName(tempSeparator, includeRoot); + if (!fqn.contains(tempSeparator)) + return ""; + QString scope = fqn.left(fqn.length() - tempSeparator.length() - m_Name.length()); + return scope; +} + +UMLPackageList UMLObject::getPackages(bool includeRoot) const { + UMLPackageList pkgList; + UMLPackage* pkg = m_pUMLPackage; + while (pkg != NULL) { + pkgList.prepend(pkg); + pkg = pkg->getUMLPackage(); + } + if (!includeRoot) + pkgList.removeFirst(); + return pkgList; +} + +UMLPackage* UMLObject::getUMLPackage() { + return m_pUMLPackage; +} + +QString UMLObject::getSecondaryId() const { + return m_SecondaryId; +} + +void UMLObject::setSecondaryId(const QString& id) { + m_SecondaryId = id; +} + +QString UMLObject::getSecondaryFallback() const { + return m_SecondaryFallback; +} + +void UMLObject::setSecondaryFallback(const QString& id) { + m_SecondaryFallback = id; +} + +void UMLObject::maybeSignalObjectCreated() { + if (!m_bCreationWasSignalled && + m_BaseType != Uml::ot_Stereotype && + m_BaseType != Uml::ot_Association && + m_BaseType != Uml::ot_Role) { + m_bCreationWasSignalled = true; + UMLDoc* umldoc = UMLApp::app()->getDocument(); + umldoc->signalUMLObjectCreated(this); + } +} + +bool UMLObject::resolveRef() { + if (m_pSecondary || (m_SecondaryId.isEmpty() && m_SecondaryFallback.isEmpty())) { + maybeSignalObjectCreated(); + return true; + } +#ifdef VERBOSE_DEBUGGING + kDebug() << "UMLObject::resolveRef(" << m_Name << "): m_SecondaryId is " + << m_SecondaryId << endl; +#endif + UMLDoc *pDoc = UMLApp::app()->getDocument(); + // In the new, XMI standard compliant save format, + // the type is the xmi.id of a UMLClassifier. + if (! m_SecondaryId.isEmpty()) { + m_pSecondary = pDoc->findObjectById(STR2ID(m_SecondaryId)); + if (m_pSecondary != NULL) { + if (m_pSecondary->getBaseType() == Uml::ot_Stereotype) { + m_pStereotype = static_cast(m_pSecondary); + m_pStereotype->incrRefCount(); + m_pSecondary = NULL; + } + m_SecondaryId = ""; + maybeSignalObjectCreated(); + return true; + } + if (m_SecondaryFallback.isEmpty()) { + kDebug() << "UMLObject::resolveRef: object with xmi.id=" << m_SecondaryId + << " not found, setting to undef" << endl; + UMLFolder *datatypes = pDoc->getDatatypeFolder(); + m_pSecondary = Object_Factory::createUMLObject(Uml::ot_Datatype, "undef", datatypes, false); + return true; + } + } + if (m_SecondaryFallback.isEmpty()) { + kError() << "UMLObject::resolveRef(" << m_Name + << "): cannot find type with id " + << m_SecondaryId << endl; + return false; + } +#ifdef VERBOSE_DEBUGGING + kDebug() << "UMLObject::resolveRef(" << m_Name + << "): could not resolve secondary ID " << m_SecondaryId + << ", using secondary fallback " << m_SecondaryFallback + << endl; +#endif + m_SecondaryId = m_SecondaryFallback; + // Assume we're dealing with the older Umbrello format where + // the type name was saved in the "type" attribute rather + // than the xmi.id of the model object of the attribute type. + m_pSecondary = pDoc->findUMLObject( m_SecondaryId, Uml::ot_UMLObject, this ); + if (m_pSecondary) { + m_SecondaryId = ""; + maybeSignalObjectCreated(); + return true; + } + // Work around Object_Factory::createUMLObject()'s incapability + // of on-the-fly scope creation: + if (m_SecondaryId.contains("::")) { + // TODO: Merge Import_Utils::createUMLObject() into Object_Factory::createUMLObject() + m_pSecondary = Import_Utils::createUMLObject(Uml::ot_UMLObject, m_SecondaryId, m_pUMLPackage); + if (m_pSecondary) { + if (Import_Utils::newUMLObjectWasCreated()) { + maybeSignalObjectCreated(); + kapp->processEvents(); + kDebug() << "UMLObject::resolveRef: Import_Utils::createUMLObject() " + << "created a new type for " << m_SecondaryId << endl; + } else { + kDebug() << "UMLObject::resolveRef: Import_Utils::createUMLObject() " + << "returned an existing type for " << m_SecondaryId << endl; + } + m_SecondaryId = ""; + return true; + } + kError() << "UMLObject::resolveRef: Import_Utils::createUMLObject() " + << "failed to create a new type for " << m_SecondaryId << endl; + return false; + } + kDebug() << "UMLObject::resolveRef: Creating new type for " << m_SecondaryId << endl; + // This is very C++ specific - we rely on some '*' or + // '&' to decide it's a ref type. Plus, we don't recognize + // typedefs of ref types. + bool isReferenceType = ( m_SecondaryId.contains('*') || + m_SecondaryId.contains('&') ); + Uml::Object_Type ot = Uml::ot_Class; + if (isReferenceType) { + ot = Uml::ot_Datatype; + } else { + if (Model_Utils::isCommonDataType(m_SecondaryId)) + ot = Uml::ot_Datatype; + } + m_pSecondary = Object_Factory::createUMLObject(ot, m_SecondaryId, NULL); + if (m_pSecondary == NULL) + return false; + m_SecondaryId = ""; + maybeSignalObjectCreated(); + //kapp->processEvents(); + return true; +} + +QDomElement UMLObject::save( const QString &tag, QDomDocument & qDoc ) { + /* + Call as the first action of saveToXMI() in child class: + This creates the QDomElement with which to work. + */ + QDomElement qElement = qDoc.createElement(tag); + qElement.setAttribute( "isSpecification", "false" ); + if (m_BaseType != Uml::ot_Association && + m_BaseType != Uml::ot_Role && + m_BaseType != Uml::ot_Attribute) { + qElement.setAttribute( "isLeaf", "false" ); + qElement.setAttribute( "isRoot", "false" ); + if (m_bAbstract) + qElement.setAttribute( "isAbstract", "true" ); + else + qElement.setAttribute( "isAbstract", "false" ); + } + qElement.setAttribute( "xmi.id", ID2STR(m_nId) ); + qElement.setAttribute( "name", m_Name ); + if (m_BaseType != Uml::ot_Operation && + m_BaseType != Uml::ot_Role && + m_BaseType != Uml::ot_Attribute) { + Uml::IDType nmSpc; + if (m_pUMLPackage) + nmSpc = m_pUMLPackage->getID(); + else + nmSpc = UMLApp::app()->getDocument()->getModelID(); + qElement.setAttribute( "namespace", ID2STR(nmSpc) ); + } + if (! m_Doc.isEmpty()) + qElement.setAttribute( "comment", m_Doc ); //CHECK: uml13.dtd compliance +#ifdef XMI_FLAT_PACKAGES + if (m_pUMLPackage) //FIXME: uml13.dtd compliance + qElement.setAttribute( "package", m_pUMLPackage->getID() ); +#endif + QString visibility = m_Vis.toString(false); + qElement.setAttribute( "visibility", visibility); + if (m_pStereotype != NULL) + qElement.setAttribute( "stereotype", ID2STR(m_pStereotype->getID()) ); + if (m_bStatic) + qElement.setAttribute( "ownerScope", "classifier" ); + /* else + qElement.setAttribute( "ownerScope", "instance" ); + *** ownerScope defaults to instance if not set **********/ + return qElement; +} + +bool UMLObject::load( QDomElement& ) { + // This body is not usually executed because child classes + // overwrite the load method. + return true; +} + +bool UMLObject::loadStereotype(QDomElement & element) { + QString tag = element.tagName(); + if (!Uml::tagEq(tag, "stereotype")) + return false; + QString stereo = element.attribute("xmi.value", ""); + if (stereo.isEmpty() && element.hasChildNodes()) { + /* like so: + + + + */ + QDomNode stereoNode = element.firstChild(); + QDomElement stereoElem = stereoNode.toElement(); + tag = stereoElem.tagName(); + if (Uml::tagEq(tag, "Stereotype")) { + stereo = stereoElem.attribute("xmi.idref", ""); + } + } + if (stereo.isEmpty()) + return false; + Uml::IDType stereoID = STR2ID(stereo); + UMLDoc *pDoc = UMLApp::app()->getDocument(); + m_pStereotype = pDoc->findStereotypeById(stereoID); + if (m_pStereotype) + m_pStereotype->incrRefCount(); + else + m_SecondaryId = stereo; // leave it to resolveRef() + return true; +} + +bool UMLObject::loadFromXMI( QDomElement & element) { + UMLDoc* umldoc = UMLApp::app()->getDocument(); + if (umldoc == NULL) { + kError() << "UMLObject::loadFromXMI: umldoc is NULL" << endl; + return false; + } + // Read the name first so that if we encounter a problem, the error + // message can say the name. + m_Name = element.attribute( "name", "" ); + QString id = element.attribute( "xmi.id", "" ); + if (id.isEmpty() || id == "-1") { + if (m_BaseType == Uml::ot_Role) { + // Before version 1.4, Umbrello did not save the xmi.id + // of UMLRole objects. + m_nId = UniqueID::gen(); + } else { + kError() << "UMLObject::loadFromXMI(" << m_Name + << "): nonexistent or illegal xmi.id" << endl; + return false; + } + } else { + m_nId = STR2ID(id); + if (m_BaseType == Uml::ot_Role) { + // Some older Umbrello versions had a problem with xmi.id's + // of other objects being reused for the UMLRole, see e.g. + // attachment 21179 at http://bugs.kde.org/147988 . + // If the xmi.id is already being used then we generate a new one. + UMLObject *o = umldoc->findObjectById(m_nId); + if (o) { + kDebug() << "loadFromXMI(UMLRole): id " << id + << " is already in use, generating a new one." << endl; + m_nId = UniqueID::gen(); + } + } + } + + if (element.hasAttribute("documentation")) // for bkwd compat. + m_Doc = element.attribute( "documentation", "" ); + else + m_Doc = element.attribute( "comment", "" ); //CHECK: need a UML:Comment? + + m_Vis = Uml::Visibility::Public; + if (element.hasAttribute("scope")) { // for bkwd compat. + QString scope = element.attribute( "scope", "" ); + if (scope == "instance_level") // nsuml compat. + m_bStatic = false; + else if (scope == "classifier_level") // nsuml compat. + m_bStatic = true; + else { + int nScope = scope.toInt(); + if (nScope >= Uml::Visibility::Public && nScope <= Uml::Visibility::Protected) + m_Vis = (Uml::Visibility::Value)nScope; + else + kError() << "UMLObject::loadFromXMI(" << m_Name + << "): illegal scope" << endl; // soft error + } + } else { + QString visibility = element.attribute( "visibility", "public" ); + if (visibility == "private" + || visibility == "private_vis") // for compatibility with other programs + m_Vis = Uml::Visibility::Private; + else if (visibility == "protected" + || visibility == "protected_vis") // for compatibility with other programs + m_Vis = Uml::Visibility::Protected; + else if (visibility == "implementation") + m_Vis = Uml::Visibility::Implementation; + } + + QString stereo = element.attribute( "stereotype", "" ); + if (!stereo.isEmpty()) { + Uml::IDType stereoID = STR2ID(stereo); + m_pStereotype = umldoc->findStereotypeById(stereoID); + if (m_pStereotype) { + m_pStereotype->incrRefCount(); + } else { + kDebug() << "UMLObject::loadFromXMI(" << m_Name << "): " + << "UMLStereotype " << ID2STR(stereoID) + << " not found, creating now." << endl; + setStereotype(stereo); + } + } + + if( element.hasAttribute("abstract") ) { // for bkwd compat. + QString abstract = element.attribute( "abstract", "0" ); + m_bAbstract = (bool)abstract.toInt(); + } else { + QString isAbstract = element.attribute( "isAbstract", "false" ); + m_bAbstract = (isAbstract == "true"); + } + + if( element.hasAttribute("static") ) { // for bkwd compat. + QString staticScope = element.attribute( "static", "0" ); + m_bStatic = (bool)staticScope.toInt(); + } else { + QString ownerScope = element.attribute( "ownerScope", "instance" ); + m_bStatic = (ownerScope == "classifier"); + } + + // If the node has child nodes, check whether attributes can be + // extracted from them. + if (element.hasChildNodes()) { + QDomNode node = element.firstChild(); + if (node.isComment()) + node = node.nextSibling(); + QDomElement elem = node.toElement(); + while( !elem.isNull() ) { + QString tag = elem.tagName(); + if (Uml::tagEq(tag, "name")) { + m_Name = elem.attribute("xmi.value", ""); + if (m_Name.isEmpty()) + m_Name = elem.text(); + } else if (Uml::tagEq(tag, "visibility")) { + QString vis = elem.attribute("xmi.value", ""); + if (vis.isEmpty()) + vis = elem.text(); + if (vis == "private" || vis == "private_vis") + m_Vis = Uml::Visibility::Private; + else if (vis == "protected" || vis == "protected_vis") + m_Vis = Uml::Visibility::Protected; + else if (vis == "implementation") + m_Vis = Uml::Visibility::Implementation; + } else if (Uml::tagEq(tag, "isAbstract")) { + QString isAbstract = elem.attribute("xmi.value", ""); + if (isAbstract.isEmpty()) + isAbstract = elem.text(); + m_bAbstract = (isAbstract == "true"); + } else if (Uml::tagEq(tag, "ownerScope")) { + QString ownerScope = elem.attribute("xmi.value", ""); + if (ownerScope.isEmpty()) + ownerScope = elem.text(); + m_bStatic = (ownerScope == "classifier"); + } else { + loadStereotype(elem); + } + node = node.nextSibling(); + if (node.isComment()) + node = node.nextSibling(); + elem = node.toElement(); + } + } + + // Operations, attributes, enum literals, templates, stereotypes, + // and association role objects get added and signaled elsewhere. + if (m_BaseType != Uml::ot_Operation && m_BaseType != Uml::ot_Attribute && + m_BaseType != Uml::ot_EnumLiteral && m_BaseType != Uml::ot_EntityAttribute && + m_BaseType != Uml::ot_Template && m_BaseType != Uml::ot_Stereotype && + m_BaseType != Uml::ot_Role) { + if (m_bInPaste) { + m_pUMLPackage = NULL; // forget any old parent + UMLListView *listView = UMLApp::app()->getListView(); + UMLListViewItem *parentItem = (UMLListViewItem*)listView->currentItem(); + if (parentItem) { + Uml::ListView_Type lvt = parentItem->getType(); + if (Model_Utils::typeIsContainer(lvt) || + lvt == Uml::lvt_Class || + lvt == Uml::lvt_Interface) { + UMLObject *o = parentItem->getUMLObject(); + m_pUMLPackage = static_cast( o ); + } + } + } + if (m_pUMLPackage) { + m_pUMLPackage->addObject(this); + } else if (umldoc->rootFolderType(this) == Uml::N_MODELTYPES) { + // m_pUMLPackage is not set on the root folders. + kDebug() << "UMLObject::loadFromXMI(" << m_Name << "): m_pUMLPackage is not set" + << endl; + } + } + return load(element); +} + +kdbgstream& operator<< (kdbgstream& s, const UMLObject& a) { + s << a.getName(); + return s; +} + +#include "umlobject.moc" diff --git a/umbrello/umbrello/umlobject.h b/umbrello/umbrello/umlobject.h new file mode 100644 index 00000000..d912bafc --- /dev/null +++ b/umbrello/umbrello/umlobject.h @@ -0,0 +1,495 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLOBJECT_H +#define UMLOBJECT_H + +//qt includes +#include +#include +#include + +#include "umlnamespace.h" +#include "umlpackagelist.h" + +class kdbgstream; +class UMLStereotype; +class UMLObject; + +/** + * This class is the non-graphical version of @ref UMLWidget. These are + * created and maintained in the class @ref UMLDoc. This class holds all + * the generic information needed for all UML objects. + * + * @short The base class for UML objects. + * @author Paul Hensgen + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class UMLObject : public QObject { + Q_OBJECT +public: + + /** + * Creates a UMLObject. + * + * @param parent The parent of the object. + * @param name The name of the object. + * @param id The ID of the object (optional.) If omitted + * then a new ID will be assigned internally. + */ + UMLObject(const UMLObject * parent, const QString &name, Uml::IDType id = Uml::id_None); + + /** + * Creates a UMLObject. + * + * @param parent The parent of the object. + */ + UMLObject(const UMLObject * parent); + + /** + * Creates a UMLObject with a given name and unique ID. + * + * @param name The name of the object. + * @param id The unique ID of the object. + */ + explicit UMLObject(const QString &name = "" , Uml::IDType id = Uml::id_None); + + /** + * Overloaded '==' operator + */ + virtual bool operator==(UMLObject & rhs ); + + /** + * Standard deconstructor. + */ + virtual ~UMLObject(); + + /** + * Copy the internal presentation of this object into the new + * object. + */ + virtual void copyInto(UMLObject *rhs) const; + + /** + * Make a clone of this object. + * To be implemented by inheriting classes. + */ + virtual UMLObject* clone() const = 0; + + /** + * Returns the type of the object. + * + * @return Returns the type of the object. + */ + Uml::Object_Type getBaseType() const; + + /** + * Set the type of the object. + * + * @param ot The Uml::Object_Type to set. + */ + virtual void setBaseType(Uml::Object_Type ot); + + /** + * Returns the ID of the object. + * + * @return Returns the ID of the object. + */ + virtual Uml::IDType getID() const; + + /** + * Sets the documentation for the object. + * + * @param d The documentation for the object. + */ + void setDoc(const QString &d); + + /** + * Returns the documentation for the object. + * + * @return Returns the documentation for the object. + */ + QString getDoc() const; + + /** + * Returns the visibility of the object. + * + * @return Returns the visibility of the object. + */ + Uml::Visibility getVisibility() const; + + /** + * Sets the visibility of the object. + * + * @param s The visibility of the object. + */ + virtual void setVisibility(Uml::Visibility s); + + /** + * Sets the classes stereotype name. + * Internally uses setUMLStereotype(). + * + * @param _name Sets the classes stereotype name. + */ + void setStereotype(const QString &_name); + + /** + * Sets the class' UMLStereotype. Adjusts the reference counts + * at the previously set stereotype and at the new stereotype. + * If the previously set UMLStereotype's reference count drops + * to zero then the UMLStereotype is removed at the UMLDoc and + * it is then physically deleted. + * + * @param s Sets the classes UMLStereotype. + */ + void setUMLStereotype(UMLStereotype *s); + + /** + * Sets the classes Package. + * DEPRECATED - use SetUMLPackage instead. + * + * @param _name The classes Package name. + */ + void setPackage(const QString &_name); + + /** + * Sets the UMLPackage in which this class is located. + * + * @param pPkg Pointer to the class' UMLPackage. + */ + void setUMLPackage(UMLPackage* pPkg); + + /** + * Returns the classes UMLStereotype object. + * + * @return Returns the classes UMLStereotype object. + */ + const UMLStereotype * getUMLStereotype(); + + /** + * Returns the classes stereotype name. + * Returns an empty string if no stereotype object is set. + * + * @param includeAdornments Include surrounding angled brackets + * "«" and "»". + * @return Returns the classes stereotype name. + */ + QString getStereotype(bool includeAdornments = false) const; + + /** + * Return the package(s) in which this UMLObject is contained + * as a text. + * + * @param separator Separator string for joining together the + * individual package prefixes (optional.) + * If no separator is given then the separator + * of the currently selected language is used. + * @param includeRoot Whether to prefix the root folder name. + * Default: false. + * @return The UMLObject's enclosing package(s) as a text. + */ + QString getPackage(const QString& separator = QString::null, + bool includeRoot = false); + + /** + * Return a list of the packages in which this class is embedded. + * The outermost package is first in the list. + * + * @param includeRoot Whether to prefix the root folder name. + * Default: false. + * @return UMLPackageList of the containing packages. + */ + UMLPackageList getPackages(bool includeRoot = false) const; + + /** + * Returns the UMLPackage that this class is located in. + * + * @return Pointer to the UMLPackage of this class. + */ + UMLPackage* getUMLPackage(); + + /** + * Assigns a new Id to the object + */ + virtual void setID(Uml::IDType NewID); + + /** + * Returns a copy of m_Name + */ + QString getName() const; + + /** + * Set the UMLObject's name + */ + virtual void setName(const QString &strName); + + /** + * Returns the fully qualified name, i.e. all package prefixes and then m_Name. + * + * @param separator The separator string to use (optional.) + * If not given then the separator is chosen according + * to the currently selected active programming language + * of import and code generation. + * @param includeRoot Whether to prefix the root folder name to the FQN. + * See UMLDoc::getRootFolder(). Default: false. + * @return The fully qualified name of this UMLObject. + */ + virtual QString getFullyQualifiedName(const QString& separator = QString::null, + bool includeRoot = false) const; + + /** + * Returns the abstract state of the object. + */ + bool getAbstract() const; + + /** + * Sets the abstract state of the object. + */ + void setAbstract(bool bAbstract); + + /** + * Sets the abstract state of the object. + */ + void setInPaste(bool bInPaste = true); + + /** + * This method is called if you wish to see the properties of a + * UMLObject. A dialog box will be displayed from which you + * can change the object's properties. + * + * @param page The page to show. + * @param assoc Whether to show association page. + * @return True if we modified the object. + */ + bool showProperties(int page = 0, bool assoc = false); + + /** + * Resolve referenced objects (if any.) + * Needs to be called after all UML objects are loaded from file. + * This needs to be done after all model objects are loaded because + * some of the xmi.id's might be forward references, i.e. they may + * identify model objects which were not yet loaded at the point of + * reference. + * The default implementation attempts resolution of the m_SecondaryId. + * + * @return True for success. + */ + virtual bool resolveRef(); + + /** + * This method saves the XMI attributes of each specific model class. + * It needs to be implemented by each child class. + * For creating the QDomElement and saving the common XMI parts, + * it can use the save() method. + */ + virtual void saveToXMI( QDomDocument & qDoc, QDomElement & qElement ) = 0; + + /** + * This method loads the generic parts of the XMI common to most model + * classes. It is not usually reimplemented by child classes. + * Instead, it invokes the load() method which implements the loading + * of the specifics of each child class. + * + * @param element The QDomElement from which to load. + */ + virtual bool loadFromXMI( QDomElement & element ); + + /** + * Analyzes the given QDomElement for a reference to a stereotype. + * + * @param element QDomElement to analyze. + * @return True if a stereotype reference was found, else false. + */ + bool loadStereotype(QDomElement & element); + + /** + * Returns true if this UMLObject has classifier scope, + * otherwise false (the default). + */ + bool getStatic() const; + + + /** + * Sets the value for m_bStatic. + */ + void setStatic(bool bStatic); + + /** + * This should be reimplemented by subclasses if they wish to + * accept certain types of associations. Note that this only + * tells if this UMLObject can accept the association + * type. When creating an association another check is made to + * see if the association is valid. For example a UMLClass + * (UMLClassifier) can accept generalizations and should + * return true. If while creating a generalization the + * superclass is already subclassed from this, the association + * is not valid and will not be created. The default accepts + * nothing (returns false) + */ + virtual bool acceptAssociationType(Uml::Association_Type); + + /** + * Return secondary ID. Required by resolveRef(). + */ + QString getSecondaryId() const; + + /** + * Set the secondary ID. + * Currently only required by petalTree2Uml(); all other setting of the + * m_SecondaryID is internal to the UMLObject class hierarchy. + */ + void setSecondaryId(const QString& id); + + /** + * Return secondary ID fallback. + * Required by resolveRef() for imported model files. + */ + QString getSecondaryFallback() const; + + /** + * Set the secondary ID fallback. + * Currently only used by petalTree2Uml(). + */ + void setSecondaryFallback(const QString& id); + + /** + * Auxiliary to saveToXMI. + * Create a QDomElement with the given tag, and save the XMI attributes + * that are common to all child classes to the newly created element. + * This method does not need to be overridden by child classes. + */ + QDomElement save( const QString &tag, QDomDocument & qDoc ); + +public slots: + /** + * Forces the emission of the modified signal. Useful when + * updating several attributes at a time: you can block the + * signals, update all atts, and then force the signal. + */ + void emitModified(); + +signals: + /** Emitted when the UMLObject has changed. Note that some objects emit + * this signal when one of its children changes, for example, a UMLClass + * emits a modified() signal when one of its operation changes while the Operation + * itself emits the corresponding signal as well. + */ + void modified(); + +protected: + /** + * Initializes key variables of the class. + */ + virtual void init(); + + /** + * Calls UMLDoc::signalUMLObjectCreated() if m_BaseType affords + * doing so. + */ + void maybeSignalObjectCreated(); + + /** + * Auxiliary to loadFromXMI. + * This method is usually overridden by child classes. + * It is responsible for loading the specific XMI structure + * of the child class. + */ + virtual bool load( QDomElement& element ); + + /** + * The object's id. + */ + Uml::IDType m_nId; + + /** + * The object's documentation. + */ + QString m_Doc; + + /** + * The package the object belongs to if applicable. + */ + UMLPackage* m_pUMLPackage; + + /** + * The stereotype of the object if applicable. + */ + UMLStereotype* m_pStereotype; + + /** + * The objects name. + */ + QString m_Name; + + /** + * The objects type. + */ + Uml::Object_Type m_BaseType; + + /** + * The objects visibility. + */ + Uml::Visibility m_Vis; + + /** + * The state of whether the object is abstract or not. + */ + bool m_bAbstract; + + /** + * This attribute holds whether the UMLObject has instance scope + * (false - the default) or classifier scope (true). + */ + bool m_bStatic; + + /** + * Caller sets this true when in paste operation. + */ + bool m_bInPaste; + + /** + * Auxiliary to maybeSignalObjectCreated(). + */ + bool m_bCreationWasSignalled; + + /** + * Pointer to an associated object. + * Only a few of the classes inheriting from UMLObject use this. + * However, it needs to be here because of inheritance graph + * disjunctness. + */ + UMLObject* m_pSecondary; + + /** + * xmi.id of the secondary object for intermediate use during + * loading. The secondary ID is resolved to the m_pSecondary + * in the course of resolveRef() at the end of loading. + */ + QString m_SecondaryId; + + /** + * Last-chance backup for when m_SecondaryId is not found. + * Used by Rose import: MDL files specify both a "quidu" + * (which corresponds to m_SecondaryId) and the human readable + * fully qualified target name of a reference. + * In case the quidu is not found, the human readable name is + * used which we store in m_SecondaryFallback. + */ + QString m_SecondaryFallback; +}; + +/** + * Print UML Object to kdgstream, so it can be used like + * kdWarn() << "This object shouldn't be here:" << illegalObject << endl; + */ +kdbgstream& operator<< (kdbgstream& s, const UMLObject& a); + +#endif diff --git a/umbrello/umbrello/umlobjectlist.cpp b/umbrello/umbrello/umlobjectlist.cpp new file mode 100644 index 00000000..f2850ebd --- /dev/null +++ b/umbrello/umbrello/umlobjectlist.cpp @@ -0,0 +1,42 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#include "umlobjectlist.h" +#include "umlobject.h" +#include +#include + +void UMLObjectList::copyInto(UMLObjectList *rhs) const { + // Don't copy yourself. + if (rhs == this) return; + + rhs->clear(); + + // Suffering from const; we shall not modify our object. + UMLObjectList *tmp = new UMLObjectList(*this); + + UMLObject *item; + for (item = tmp->first(); item; item = tmp->next() ) + { + rhs->append(item->clone()); + } + delete tmp; +} + + +UMLObjectList* UMLObjectList::clone() const { + UMLObjectList *clone = new UMLObjectList(); + copyInto(clone); + return clone; +} + + + diff --git a/umbrello/umbrello/umlobjectlist.h b/umbrello/umbrello/umlobjectlist.h new file mode 100644 index 00000000..d9d11c83 --- /dev/null +++ b/umbrello/umbrello/umlobjectlist.h @@ -0,0 +1,53 @@ +/*************************************************************************** + umlobjectlist.h - description + ------------------- + begin : Sat Dec 29 2001 + copyright : (C) 2001 by Gustavo Madrigal + email : gmadrigal@nextphere.com + Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef UMLOBJECTLIST_H +#define UMLOBJECTLIST_H + +#include + +// forward declarations +class UMLObject; + +//typedef QPtrList UMLObjectList; +typedef QPtrListIterator UMLObjectListIt; + + +/** + * This sub-class adds copyInto and clone to the QPtrList + * base class. + */ +class UMLObjectList : public QPtrList +{ + +public: + + /** + * Copy the internal presentation of this object into the new + * object. + */ + virtual void copyInto (UMLObjectList *rhs) const; + + /** + * Make a clone of this object. + */ + virtual UMLObjectList* clone() const; +}; + + +#endif diff --git a/umbrello/umbrello/umloperationlist.h b/umbrello/umbrello/umloperationlist.h new file mode 100644 index 00000000..dfb9007e --- /dev/null +++ b/umbrello/umbrello/umloperationlist.h @@ -0,0 +1,23 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLOPERATIONLIST_H +#define UMLOPERATIONLIST_H + +#include + +// forward declaration +class UMLOperation; + +typedef QPtrList UMLOperationList; +typedef QPtrListIterator UMLOperationListIt; + +#endif diff --git a/umbrello/umbrello/umlpackagelist.h b/umbrello/umbrello/umlpackagelist.h new file mode 100644 index 00000000..d74e8c33 --- /dev/null +++ b/umbrello/umbrello/umlpackagelist.h @@ -0,0 +1,22 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLPACKAGELIST_H +#define UMLPACKAGELIST_H + +#include + +class UMLPackage; + +typedef QPtrList UMLPackageList; +typedef QPtrListIterator UMLPackageListIt; + +#endif diff --git a/umbrello/umbrello/umlrole.cpp b/umbrello/umbrello/umlrole.cpp new file mode 100644 index 00000000..9b4449fc --- /dev/null +++ b/umbrello/umbrello/umlrole.cpp @@ -0,0 +1,339 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "umlrole.h" + +// qt/kde includes +#include +#include + +// local includes +#include "association.h" +#include "umldoc.h" +#include "uml.h" + + +// constructor +UMLRole::UMLRole(UMLAssociation * parent, UMLObject * parentObj, Uml::Role_Type role) + : UMLObject(const_cast(parent)) +{ + init(parent, parentObj, role); +} + +UMLRole::~UMLRole() { } + +bool UMLRole::operator==(UMLRole &rhs) { + if (this == &rhs) { + return true; + } + return ( UMLObject::operator==( rhs ) && + m_Changeability == rhs.m_Changeability && + m_Multi == rhs.m_Multi && + m_Name == rhs.m_Name + ); +} + +UMLAssociation * UMLRole::getParentAssociation () { + return m_pAssoc; +} + +UMLObject* UMLRole::getObject() { + return m_pSecondary; +} + +Uml::Changeability_Type UMLRole::getChangeability() const { + return m_Changeability; +} + +QString UMLRole::getMultiplicity() const { + return m_Multi; +} + +void UMLRole::setObject (UMLObject *obj) { + // because we will get the id of this role from the parent + // object, we CANT allow UMLRoles to take other UMLRoles as + // parent objects. In fact, there is probably good reason + // to only take UMLClassifiers here, but I'll leave it more open + // for the time being. -b.t. + if (obj && dynamic_cast(obj)) { + kError() << "UMLRole(" << ID2STR(m_nId) << ") cannot setObject() to another UMLRole(" + << ID2STR(obj->getID()) << ")" << endl; + return; + } + + m_pSecondary = obj; + UMLObject::emitModified(); +} + +void UMLRole::setChangeability (Uml::Changeability_Type value) { + m_Changeability = value; + UMLObject::emitModified(); +} + +void UMLRole::setMultiplicity ( const QString &multi ) { + m_Multi = multi; + UMLObject::emitModified(); +} + +Uml::Role_Type UMLRole::getRole() { + return m_role; +} + +void UMLRole::init(UMLAssociation * parent, UMLObject * parentObj, Uml::Role_Type r) { + m_BaseType = Uml::ot_Role; + m_role = r; + m_pAssoc = parent; + m_pSecondary = parentObj; + m_Multi = ""; + m_Name = ""; + m_Changeability = Uml::chg_Changeable; + + // connect this up to parent + connect(this,SIGNAL(modified()),parent,SIGNAL(modified())); +} + +void UMLRole::saveToXMI( QDomDocument & qDoc, QDomElement & qElement ) { + QDomElement roleElement = UMLObject::save("UML:AssociationEnd", qDoc); + if (m_pSecondary) + roleElement.setAttribute( "type", ID2STR(m_pSecondary->getID()) ); + else + kError() << "UMLRole::saveToXMI(id " << ID2STR(m_nId) + << "): m_pSecondary is NULL" << endl; + if (!m_Multi.isEmpty()) + roleElement.setAttribute("multiplicity", m_Multi); + if (m_role == Uml::A) { // role aggregation based on parent type + // role A + switch (m_pAssoc->getAssocType()) { + case Uml::at_Composition: + roleElement.setAttribute("aggregation", "composite"); + break; + case Uml::at_Aggregation: + roleElement.setAttribute("aggregation", "aggregate"); + break; + default: + roleElement.setAttribute("aggregation", "none"); + break; + } + if (m_pAssoc->getAssocType() == Uml::at_UniAssociation) { + // Normally the isNavigable attribute is "true". + // We set it to false on role A to indicate that + // role B gets an explicit arrowhead. + roleElement.setAttribute("isNavigable", "false"); + } else { + roleElement.setAttribute("isNavigable", "true"); + } + } else { + roleElement.setAttribute("aggregation", "none"); + roleElement.setAttribute("isNavigable", "true"); + //FIXME obviously this isn't standard XMI + if (m_pAssoc->getAssocType() == Uml::at_Relationship) { + roleElement.setAttribute("relationship", "true"); + } + } + + roleElement.setAttribute("visibility", getVisibility().toString(false)); + + switch (m_Changeability) { + case Uml::chg_Frozen: + roleElement.setAttribute("changeability", "frozen"); + break; + case Uml::chg_AddOnly: + roleElement.setAttribute("changeability", "addOnly"); + break; + case Uml::chg_Changeable: + roleElement.setAttribute("changeability", "changeable"); + break; + } + qElement.appendChild( roleElement ); +} + +bool UMLRole::load( QDomElement & element ) { + UMLDoc * doc = UMLApp::app()->getDocument(); + QString type = element.attribute("type", ""); + if (!type.isEmpty()) { + if (!m_SecondaryId.isEmpty()) + kWarning() << "UMLRole::load: overwriting old m_SecondaryId \"" + << m_SecondaryId << " with new value \"" + << type << "\"" << endl; + m_SecondaryId = type; + } + // Inspect child nodes - for multiplicity (and type if not set above.) + for (QDomNode node = element.firstChild(); !node.isNull(); node = node.nextSibling()) { + if (node.isComment()) + continue; + QDomElement tempElement = node.toElement(); + QString tag = tempElement.tagName(); + if (Uml::tagEq(tag, "name")) { + m_Name = tempElement.text(); + } else if (Uml::tagEq(tag, "AssociationEnd.multiplicity")) { + /** + * There are different ways in which the multiplicity might be given: + * - direct value in the tag, + * - attributes "lower" and "upper" of a subordinate , + * - direct value in subordinate and + * tags + */ + QDomNode n = tempElement.firstChild(); + if (node.isNull() || tempElement.isNull() || n.isNull() || + n.toElement().isNull()) { + m_Multi = tempElement.text().stripWhiteSpace(); + continue; + } + tempElement = n.toElement(); + tag = tempElement.tagName(); + if (!Uml::tagEq(tag, "Multiplicity")) { + m_Multi = tempElement.text().stripWhiteSpace(); + continue; + } + n = tempElement.firstChild(); + tempElement = n.toElement(); + tag = tempElement.tagName(); + if (!Uml::tagEq(tag, "Multiplicity.range")) { + m_Multi = tempElement.text().stripWhiteSpace(); + continue; + } + n = tempElement.firstChild(); + tempElement = n.toElement(); + tag = tempElement.tagName(); + if (!Uml::tagEq(tag, "MultiplicityRange")) { + m_Multi = tempElement.text().stripWhiteSpace(); + continue; + } + QString multiUpper; + if (tempElement.hasAttribute("lower")) { + m_Multi = tempElement.attribute("lower", ""); + multiUpper = tempElement.attribute("upper", ""); + if (!multiUpper.isEmpty()) { + if (!m_Multi.isEmpty()) + m_Multi.append(".."); + m_Multi.append(multiUpper); + } + continue; + } + n = tempElement.firstChild(); + while (!n.isNull()) { + tempElement = n.toElement(); + tag = tempElement.tagName(); + if (Uml::tagEq(tag, "MultiplicityRange.lower")) { + m_Multi = tempElement.text(); + } else if (Uml::tagEq(tag, "MultiplicityRange.upper")) { + multiUpper = tempElement.text(); + } + n = n.nextSibling(); + } + if (!multiUpper.isEmpty()) { + if (!m_Multi.isEmpty()) + m_Multi.append(".."); + m_Multi.append(multiUpper); + } + } else if (m_SecondaryId.isEmpty() && + (Uml::tagEq(tag, "type") || + Uml::tagEq(tag, "participant"))) { + m_SecondaryId = tempElement.attribute("xmi.id", ""); + if (m_SecondaryId.isEmpty()) + m_SecondaryId = tempElement.attribute("xmi.idref", ""); + if (m_SecondaryId.isEmpty()) { + QDomNode inner = tempElement.firstChild(); + QDomElement innerElem = inner.toElement(); + m_SecondaryId = innerElem.attribute("xmi.id", ""); + if (m_SecondaryId.isEmpty()) + m_SecondaryId = innerElem.attribute("xmi.idref", ""); + } + } + } + if (!m_Multi.isEmpty()) + kDebug() << "UMLRole::load(" << m_Name << "): m_Multi is " << m_Multi << endl; + if (m_SecondaryId.isEmpty()) { + kError() << "UMLRole::load(" << m_Name << "): type not given or illegal" << endl; + return false; + } + UMLObject * obj; + obj = doc->findObjectById(STR2ID(m_SecondaryId)); + if (obj) { + m_pSecondary = obj; + m_SecondaryId = ""; + } + + // block signals to prevent needless updating + blockSignals(true); + // Here comes the handling of the association type. + // This is open for discussion - I'm pretty sure there are better ways.. + + // Yeah, for one, setting the *parent* object parameters from here is sucky + // as hell. Why are we using roleA to store what is essentially a parent (association) + // parameter, eh? The UML13.dtd is pretty silly, but since that is what + // is driving us to that point, we have to go with it. Some analysis of + // the component roles/linked items needs to be done in order to get things + // right. *sigh* -b.t. + + // Setting association type from the role (A) + // Determination of the "aggregation" attribute used to be done only + // when (m_role == Uml::A) but some XMI writers (e.g. StarUML) place + // the aggregation attribute at role B. + // The role end with the aggregation unequal to "none" wins. + QString aggregation = element.attribute("aggregation", "none"); + if (aggregation == "composite") + m_pAssoc->setAssocType(Uml::at_Composition); + else if (aggregation == "shared" // UML1.3 + || aggregation == "aggregate") // UML1.4 + m_pAssoc->setAssocType(Uml::at_Aggregation); + + if (!element.hasAttribute("isNavigable")) { + /* Backward compatibility mode: In Umbrello version 1.3.x the + logic for saving the isNavigable flag was wrong. + May happen on loading role A. + */ + m_pAssoc->setOldLoadMode(true); + } else if (m_pAssoc->getOldLoadMode() == true) { + /* Here is the original logic: + " Role B: + If isNavigable is not given, we make no change to the + association type. + If isNavigable is given, and is "true", then we assume that + the association's other end (role A) is not navigable, and + therefore we change the association type to UniAssociation. + The case that isNavigable is given as "false" is ignored. + Combined with the association type logic for role A, this + allows us to support at_Association and at_UniAssociation. " + */ + if (element.attribute("isNavigable") == "true") + m_pAssoc->setAssocType(Uml::at_UniAssociation); + } else if (element.attribute("isNavigable") == "false") { + m_pAssoc->setAssocType(Uml::at_UniAssociation); + } + + //FIXME not standard XMI + if (element.hasAttribute("relationship")) { + if (element.attribute("relationship") == "true") { + m_pAssoc->setAssocType(Uml::at_Relationship); + } + } + + if (m_Multi.isEmpty()) + m_Multi = element.attribute("multiplicity", ""); + + // Changeability defaults to Changeable if it cant set it here.. + m_Changeability = Uml::chg_Changeable; + QString changeability = element.attribute("changeability", ""); + if (changeability.isEmpty()) + element.attribute("changeable", ""); // for backward compatibility + if (changeability == "frozen") + m_Changeability = Uml::chg_Frozen; + else if (changeability == "addOnly") + m_Changeability = Uml::chg_AddOnly; + + // finished config, now unblock + blockSignals(false); + return true; +} + +#include "umlrole.moc" diff --git a/umbrello/umbrello/umlrole.h b/umbrello/umbrello/umlrole.h new file mode 100644 index 00000000..63fe42b2 --- /dev/null +++ b/umbrello/umbrello/umlrole.h @@ -0,0 +1,128 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLROLE_H +#define UMLROLE_H + +#include "umlobject.h" + +class UMLAssociation; + +/** + * This class contains the non-graphic representation of an association role. + * + * @author Brian Thomas + * @see UMLObject + */ + +class UMLRole : public UMLObject { + Q_OBJECT +public: + /** + * Sets up an association. + * + * @param parent The parent (association) of this UMLRole. + * @param parentUMLObject The Parent UML Object of this UMLRole + * @param role The Uml::Role_Type of this UMLRole + */ + UMLRole (UMLAssociation * parent, UMLObject * parentUMLObject, Uml::Role_Type role); + + /** + * Overloaded '==' operator + */ + bool operator==(UMLRole & rhs); + + /** + * Standard deconstructor. + */ + virtual ~UMLRole(); + + /** + * Returns the UMLObject assigned to the role. + * @return Pointer to the UMLObject in role. + */ + UMLObject* getObject(); + + /** + * Returns the Changeablity of the role. + * + * @return Changeability_Type of role. + */ + Uml::Changeability_Type getChangeability() const; + + /** + * Returns the multiplicity assigned to the role. + * + * @return The multiplicity assigned to the role. + */ + QString getMultiplicity() const; + + /** + * Sets the UMLObject playing the role in the association. + * + * @param obj Pointer to the UMLObject of role. + */ + void setObject(UMLObject *obj); + + /** + * Sets the changeability of the role. + * + * @param value Changeability_Type of role changeability. + */ + void setChangeability (Uml::Changeability_Type value); + + /** + * Sets the multiplicity of the role. + * + * @param multi The multiplicity of role. + */ + void setMultiplicity ( const QString &multi ); + + UMLAssociation * getParentAssociation (); + + /** get the 'id' of the role (NOT the parent object). This could be + * either Uml::A or Uml::B. Yes, it would be better if we + * could get along without this, but we need it to distinguish saved + * umlrole objects in the XMI for 'self' associations where both roles + * will point to the same underlying UMLObject. + */ + Uml::Role_Type getRole(); + + /** + * Make a clone of this object. + * Not yet implemented. + */ + UMLObject* clone() const { return NULL; } + + /** + * Creates the XMI element. + */ + void saveToXMI(QDomDocument& qDoc, QDomElement& qElement); + +protected: + /** + * Loads the XMI element. + * Auxiliary to UMLObject::loadFromXMI. + */ + bool load(QDomElement& element); + +private: + + /** do some initialization at construction time */ + void init (UMLAssociation * parent, UMLObject * parentObj, Uml::Role_Type r); + + UMLAssociation * m_pAssoc; + Uml::Role_Type m_role; + QString m_Multi; + Uml::Changeability_Type m_Changeability; +}; + +#endif diff --git a/umbrello/umbrello/umlstereotypelist.h b/umbrello/umbrello/umlstereotypelist.h new file mode 100644 index 00000000..acf2edd7 --- /dev/null +++ b/umbrello/umbrello/umlstereotypelist.h @@ -0,0 +1,23 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2003-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLSTEREOTYPELIST_H +#define UMLSTEREOTYPELIST_H + +#include + +// forward declaration +class UMLStereotype; + +typedef QPtrList UMLStereotypeList; +typedef QPtrListIterator UMLStereotypeListIt; + +#endif diff --git a/umbrello/umbrello/umltemplatelist.h b/umbrello/umbrello/umltemplatelist.h new file mode 100644 index 00000000..aa7415a4 --- /dev/null +++ b/umbrello/umbrello/umltemplatelist.h @@ -0,0 +1,23 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLTEMPLATELIST_H +#define UMLTEMPLATELIST_H + +#include + +// forward declaration +class UMLTemplate; + +typedef QPtrList UMLTemplateList; +typedef QPtrListIterator UMLTemplateListIt; + +#endif diff --git a/umbrello/umbrello/umlview.cpp b/umbrello/umbrello/umlview.cpp new file mode 100644 index 00000000..0b6a7935 --- /dev/null +++ b/umbrello/umbrello/umlview.cpp @@ -0,0 +1,3352 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "umlview.h" + +// system includes +#include +#include + +// include files for Qt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//kde include files +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// application specific includes +#include "umlviewimageexporter.h" +#include "listpopupmenu.h" +#include "uml.h" +#include "umldoc.h" +#include "umlobject.h" +#include "docwindow.h" +#include "assocrules.h" +#include "umlrole.h" +#include "umlviewcanvas.h" +#include "dialogs/classoptionspage.h" +#include "dialogs/umlviewdialog.h" +#include "clipboard/idchangelog.h" +#include "clipboard/umldrag.h" +#include "widget_factory.h" +#include "floatingtextwidget.h" +#include "classifierwidget.h" +#include "classifier.h" +#include "packagewidget.h" +#include "package.h" +#include "folder.h" +#include "componentwidget.h" +#include "nodewidget.h" +#include "artifactwidget.h" +#include "datatypewidget.h" +#include "enumwidget.h" +#include "entitywidget.h" +#include "actorwidget.h" +#include "usecasewidget.h" +#include "notewidget.h" +#include "boxwidget.h" +#include "associationwidget.h" +#include "objectwidget.h" +#include "messagewidget.h" +#include "statewidget.h" +#include "forkjoinwidget.h" +#include "activitywidget.h" +#include "seqlinewidget.h" +#include "uniqueid.h" +#include "umllistviewitemlist.h" +#include "umllistviewitem.h" +#include "umllistview.h" +#include "umlobjectlist.h" +#include "association.h" +#include "attribute.h" +#include "model_utils.h" +#include "object_factory.h" +#include "umlwidget.h" +#include "toolbarstatefactory.h" + + +// control the manual DoubleBuffering of QCanvas +// with a define, so that this memory X11 effect can +// be tested more easily +#define MANUAL_CONTROL_DOUBLE_BUFFERING + +// static members +const int UMLView::defaultCanvasSize = 1300; + +using namespace Uml; + + +// constructor +UMLView::UMLView(UMLFolder *parentFolder) : QCanvasView(UMLApp::app()->getMainViewWidget()) { + init(); + m_pDoc = UMLApp::app()->getDocument(); + m_pFolder = parentFolder; +} + +void UMLView::init() { + // Initialize loaded/saved data + m_nID = Uml::id_None; + m_pDoc = NULL; + m_Documentation = ""; + m_Type = dt_Undefined; + m_bUseSnapToGrid = false; + m_bUseSnapComponentSizeToGrid = false; + m_bShowSnapGrid = false; + m_nSnapX = 10; + m_nSnapY = 10; + m_nZoom = 100; + m_nCanvasWidth = UMLView::defaultCanvasSize; + m_nCanvasHeight = UMLView::defaultCanvasSize; + m_nCollaborationId = 0; + + // Initialize other data + m_AssociationList.setAutoDelete( true ); + + //Setup up booleans + m_bChildDisplayedDoc = false; + m_bPaste = false; + m_bActivated = false; + m_bCreateObject = false; + m_bDrawSelectedOnly = false; + m_bPopupShowing = false; + m_bStartedCut = false; + //clear pointers + m_PastePoint = QPoint(0, 0); + m_pIDChangesLog = 0; + m_pMenu = 0; + + m_pImageExporter = new UMLViewImageExporter(this); + + //setup graphical items + viewport() -> setBackgroundMode( Qt::NoBackground ); + setCanvas( new UMLViewCanvas( this ) ); + // don't set the quite frequent update rate for each + // diagram, as that causes also an update of invisible + // diagrams, which can cost high CPU load for many + // diagrams. + // Instead: set the updatePeriod to 20 on Show event, + // and switch update back off on Hide event + canvas() -> setUpdatePeriod( -1 ); + resizeContents(defaultCanvasSize, defaultCanvasSize); + canvas() -> resize(defaultCanvasSize, defaultCanvasSize); + setAcceptDrops(true); + viewport() -> setAcceptDrops(true); + setDragAutoScroll(false); + + viewport() -> setMouseTracking(false); + + //setup signals + connect( this, SIGNAL(sigRemovePopupMenu()), this, SLOT(slotRemovePopupMenu() ) ); + connect( UMLApp::app(), SIGNAL( sigCutSuccessful() ), + this, SLOT( slotCutSuccessful() ) ); + + // Create the ToolBarState factory. This class is not a singleton, because it + // needs a pointer to this object. + m_pToolBarStateFactory = new ToolBarStateFactory(this); + m_pToolBarState = m_pToolBarStateFactory->getState(WorkToolBar::tbb_Arrow); + +} + +UMLView::~UMLView() { + delete m_pImageExporter; + + if(m_pIDChangesLog) { + delete m_pIDChangesLog; + m_pIDChangesLog = 0; + } + + // before we can delete the QCanvas, all widgets must be explicitly + // removed + // otherwise the implicit remove of the contained widgets will cause + // events which would demand a valid connected QCanvas + // ==> this causes umbrello to crash for some - larger?? - projects + // first avoid all events, which would cause some update actions + // on deletion of each removed widget + blockSignals( true ); + removeAllWidgets(); + + delete m_pToolBarStateFactory; + m_pToolBarStateFactory = NULL; + + // Qt Doc for QCanvasView::~QCanvasView () states: + // "Destroys the canvas view. The associated canvas is not deleted." + // we should do it now + delete canvas(); +} + +QString UMLView::getName() const { + return m_Name; +} + +void UMLView::setName(const QString &name) { + m_Name = name; +} + +int UMLView::generateCollaborationId() { + return ++m_nCollaborationId; +} + +void UMLView::print(KPrinter *pPrinter, QPainter & pPainter) { + int height, width; + //get the size of the page + pPrinter->setFullPage( true ); + QPaintDeviceMetrics metrics(pPrinter); + QFontMetrics fm = pPainter.fontMetrics(); // use the painter font metrics, not the screen fm! + int fontHeight = fm.lineSpacing(); + uint left, right, top, bottom; + // fetch printer margins individual for all four page sides, as at least top and bottom are not the same + pPrinter->margins ( &top, &left, &bottom, &right ); + // give a little extra space at each side + left += 2; + right += 2; + top += 2; + bottom += 2; + + if(pPrinter->orientation() == KPrinter::Landscape) { + // we are printing in LANDSCAPE --> swap marginX and marginY + uint right_old = right; + // the DiagramRight side is printed at PrintersTop + right = top; + // the DiagramTop side is printed at PrintersLeft + top = left; + // the DiagramLeft side is printed at PrintersBottom + left = bottom; + // the DiagramBottom side is printed at PrintersRight + bottom = right_old; + } + + // The printer will probably use a different font with different font metrics, + // force the widgets to update accordingly on paint + forceUpdateWidgetFontMetrics(&pPainter); + + width = metrics.width() - left - right; + height = metrics.height() - top - bottom; + + //get the smallest rect holding the diagram + QRect rect = getDiagramRect(); + //now draw to printer + +#if 0 + int offsetX = 0, offsetY = 0, widthX = 0, heightY = 0; + // respect the margin + pPainter.translate(marginX, marginY); + + // clip away everything outside of the margin + pPainter.setClipRect(marginX, marginY, + width, metrics.height() - marginY * 2); + + //loop until all of the picture is printed + int numPagesX = (int)ceil((double)rect.width()/(double)width); + int numPagesY = (int)ceil((double)rect.height()/(double)height); + int page = 0; + + // print the canvas to multiple pages + for (int pageY = 0; pageY < numPagesY; ++pageY) { + // tile vertically + offsetY = pageY * height + rect.y(); + heightY = (pageY + 1) * height > rect.height() + ? rect.height() - pageY * height + : height; + for (int pageX = 0; pageX < numPagesX; ++pageX) { + // tile horizontally + offsetX = pageX * width + rect.x(); + widthX = (pageX + 1) * width > rect.width() + ? rect.width() - pageX * width + : width; + + // make sure the part of the diagram is painted at the correct + // place in the printout + pPainter.translate(-offsetX,-offsetY); + getDiagram(QRect(offsetX, offsetY,widthX, heightY), + pPainter); + // undo the translation so the coordinates for the painter + // correspond to the page again + pPainter.translate(offsetX,offsetY); + + //draw foot note + QString string = i18n("Diagram: %2 Page %1").arg(page + 1).arg(getName()); + QColor textColor(50, 50, 50); + pPainter.setPen(textColor); + pPainter.drawLine(0, height + 2, width, height + 2); + pPainter.drawText(0, height + 4, width, fontHeight, Qt::AlignLeft, string); + + if(pageX+1 < numPagesX || pageY+1 < numPagesY) { + pPrinter -> newPage(); + page++; + } + } + } +#else + // be gentle - as described in Qt-Doc "The Coordinate System" + pPainter.save(); + + int diagramHeight = rect.height(); + // + 4+fontHeight between diagram and footline as space-buffer + // + 2 between line and foot-text + // + 1 for foot-line + // + fontHeight for foot-text + // ============== + // (2*fontHeight) + 7 + int footHeight = (2*fontHeight) + 7; + int footTop = rect.y() + diagramHeight + 4+fontHeight; + int drawHeight = diagramHeight + footHeight; + + // set window of painter to dimensions of diagram + // set window to viewport relation so that x:y isn't changed + double dScaleX = (double)rect.width()/ (double)width; + double dScaleY = (double)drawHeight/ (double)height; + // select the scaling factor so that the larger dimension + // fits on the printer page -> use the larger scaling factor + // -> the virtual diagram window has some additional space at the + // shorter dimension + double dScaleUse = ( dScaleX > dScaleY )?dScaleX:dScaleY; + + int windowWidth = (int)ceil(dScaleUse*width); + int windowHeight = (int)ceil(dScaleUse*height); +#ifdef DEBUG_PRINTING + kDebug() << "drawHeight: " << drawHeight << ", width: " << rect.width() + << "\nPageHeight: " << height << ", PageWidht: " << width + << "\nScaleY: " << dScaleY << ", ScaleX: " << dScaleX + << "\ndScaleUse: " << dScaleUse + << "\nVirtualSize: Width: " << windowWidth << ", Height: " << windowHeight + << "\nFoot Top: " << footTop + << endl; +#endif + // set virtual drawing area window - where diagram fits 100% in + pPainter.setWindow( rect.x(), rect.y(), windowWidth, windowHeight ); + + // set viewport - the physical mapping + // --> Qt's QPainter will map all drawed elements from diagram area ( window ) + // to printer area ( viewport ) + pPainter.setViewport( left, top, width, height ); + + // get Diagram + getDiagram(QRect(rect.x(), rect.y(), windowWidth, diagramHeight), pPainter); + + //draw foot note + QString string = i18n("Diagram: %2 Page %1").arg( 1).arg(getName()); + QColor textColor(50, 50, 50); + pPainter.setPen(textColor); + pPainter.drawLine(rect.x(), footTop , windowWidth, footTop); + pPainter.drawText(rect.x(), footTop + 3, windowWidth, fontHeight, Qt::AlignLeft, string); + + // now restore scaling + pPainter.restore(); + +#endif + // next painting will most probably be to a different device (i.e. the screen) + forceUpdateWidgetFontMetrics(0); +} + +void UMLView::setupNewWidget(UMLWidget *w) { + w->setX( m_Pos.x() ); + w->setY( m_Pos.y() ); + w->setVisible( true ); + w->setActivated(); + w->setFont( getFont() ); + w->slotColorChanged( getID() ); + w->slotLineWidthChanged( getID() ); + resizeCanvasToItems(); + m_WidgetList.append( w ); + m_pDoc->setModified(); +} + +void UMLView::contentsMouseReleaseEvent(QMouseEvent* ome) { + m_pToolBarState->mouseRelease(ome); +} + +void UMLView::slotToolBarChanged(int c) { + m_pToolBarState->cleanBeforeChange(); + m_pToolBarState = m_pToolBarStateFactory->getState((WorkToolBar::ToolBar_Buttons)c); + m_pToolBarState->init(); + + m_bPaste = false; +} + +void UMLView::showEvent(QShowEvent* /*se*/) { + +# ifdef MANUAL_CONTROL_DOUBLE_BUFFERING + //kWarning() << "Show Event for " << getName() << endl; + canvas()->setDoubleBuffering( true ); + // as the diagram gets now visible again, + // the update of the diagram elements shall be + // at the normal value of 20 + canvas()-> setUpdatePeriod( 20 ); +# endif + + UMLApp* theApp = UMLApp::app(); + WorkToolBar* tb = theApp->getWorkToolBar(); + connect(tb,SIGNAL(sigButtonChanged(int)), this, SLOT(slotToolBarChanged(int))); + connect(this,SIGNAL(sigResetToolBar()), tb, SLOT(slotResetToolBar())); + connect(m_pDoc, SIGNAL(sigObjectCreated(UMLObject *)), + this, SLOT(slotObjectCreated(UMLObject *))); + connect(this, SIGNAL(sigAssociationRemoved(AssociationWidget*)), + UMLApp::app()->getDocWindow(), SLOT(slotAssociationRemoved(AssociationWidget*))); + connect(this, SIGNAL(sigWidgetRemoved(UMLWidget*)), + UMLApp::app()->getDocWindow(), SLOT(slotWidgetRemoved(UMLWidget*))); + resetToolbar(); + +} + +void UMLView::hideEvent(QHideEvent* /*he*/) { + UMLApp* theApp = UMLApp::app(); + WorkToolBar* tb = theApp->getWorkToolBar(); + disconnect(tb,SIGNAL(sigButtonChanged(int)), this, SLOT(slotToolBarChanged(int))); + disconnect(this,SIGNAL(sigResetToolBar()), tb, SLOT(slotResetToolBar())); + disconnect(m_pDoc, SIGNAL(sigObjectCreated(UMLObject *)), this, SLOT(slotObjectCreated(UMLObject *))); + disconnect(this, SIGNAL(sigAssociationRemoved(AssociationWidget*)), + UMLApp::app()->getDocWindow(), SLOT(slotAssociationRemoved(AssociationWidget*))); + disconnect(this, SIGNAL(sigWidgetRemoved(UMLWidget*)), + UMLApp::app()->getDocWindow(), SLOT(slotWidgetRemoved(UMLWidget*))); + +# ifdef MANUAL_CONTROL_DOUBLE_BUFFERING + //kWarning() << "Hide Event for " << getName() << endl; + canvas()->setDoubleBuffering( false ); + // a periodic update of all - also invisible - diagrams + // can cause a very high CPU load if more than 100diagrams + // are inside a project - and this without any need + // => switch the update off for hidden diagrams + canvas()-> setUpdatePeriod( -1 ); +# endif +} + +void UMLView::slotObjectCreated(UMLObject* o) { + m_bPaste = false; + //check to see if we want the message + //may be wanted by someone else e.g. list view + if (!m_bCreateObject) { + return; + } + + UMLWidget* newWidget = Widget_Factory::createWidget(this, o); + if (newWidget == NULL) + return; + newWidget->setVisible( true ); + newWidget->setActivated(); + newWidget->setFont( getFont() ); + newWidget->slotColorChanged( getID() ); + newWidget->slotLineWidthChanged( getID() ); + newWidget->updateComponentSize(); + if (m_Type == Uml::dt_Sequence) { + // Set proper position on the sequence line widget which is + // attached to the object widget. + ObjectWidget *ow = dynamic_cast(newWidget); + if (ow) + ow->moveEvent(NULL); + } + m_bCreateObject = false; + m_WidgetList.append(newWidget); + switch (o->getBaseType()) { + case ot_Actor: + case ot_UseCase: + case ot_Class: + case ot_Package: + case ot_Component: + case ot_Node: + case ot_Artifact: + case ot_Interface: + case ot_Enum: + case ot_Entity: + case ot_Datatype: + createAutoAssociations(newWidget); + // We need to invoke createAutoAttributeAssociations() + // on all other widgets again because the newly created + // widget might saturate some latent attribute assocs. + for (UMLWidgetListIt it(m_WidgetList); it.current(); ++it) { + UMLWidget *w = it.current(); + if (w != newWidget) + createAutoAttributeAssociations(w); + } + break; + default: + break; + } + resizeCanvasToItems(); +} + +void UMLView::slotObjectRemoved(UMLObject * o) { + m_bPaste = false; + Uml::IDType id = o->getID(); + UMLWidgetListIt it( m_WidgetList ); + UMLWidget *obj; + + while ((obj = it.current()) != 0 ) { + ++it; + if(obj -> getID() != id) + continue; + removeWidget(obj); + } +} + +void UMLView::contentsDragEnterEvent(QDragEnterEvent *e) { + UMLDrag::LvTypeAndID_List tidList; + if(!UMLDrag::getClip3TypeAndID(e, tidList)) { + return; + } + UMLDrag::LvTypeAndID_It tidIt(tidList); + UMLDrag::LvTypeAndID * tid = tidIt.current(); + if (!tid) { + kDebug() << "UMLView::contentsDragEnterEvent: " + << "UMLDrag::getClip3TypeAndID returned empty list" << endl; + return; + } + ListView_Type lvtype = tid->type; + Uml::IDType id = tid->id; + + Diagram_Type diagramType = getType(); + + UMLObject* temp = 0; + //if dragging diagram - might be a drag-to-note + if (Model_Utils::typeIsDiagram(lvtype)) { + e->accept(true); + return; + } + //can't drag anything onto state/activity diagrams + if( diagramType == dt_State || diagramType == dt_Activity) { + e->accept(false); + return; + } + //make sure can find UMLObject + if( !(temp = m_pDoc->findObjectById(id) ) ) { + kDebug() << "object " << ID2STR(id) << " not found" << endl; + e->accept(false); + return; + } + //make sure dragging item onto correct diagram + // concept - class,seq,coll diagram + // actor,usecase - usecase diagram + Object_Type ot = temp->getBaseType(); + bool bAccept = true; + switch (diagramType) { + case dt_UseCase: + if ((widgetOnDiagram(id) && ot == ot_Actor) || + (ot != ot_Actor && ot != ot_UseCase)) + bAccept = false; + break; + case dt_Class: + if (widgetOnDiagram(id) || + (ot != ot_Class && + ot != ot_Package && + ot != ot_Interface && + ot != ot_Enum && + ot != ot_Datatype)) { + bAccept = false; + } + break; + case dt_Sequence: + case dt_Collaboration: + if (ot != ot_Class && + ot != ot_Interface && + ot != ot_Actor) + bAccept = false; + break; + case dt_Deployment: + if (widgetOnDiagram(id)) + bAccept = false; + else if (ot != ot_Interface && + ot != ot_Package && + ot != ot_Component && + ot != ot_Class && + ot != ot_Node) + bAccept = false; + else if (ot == ot_Package && + temp->getStereotype() != "subsystem") + bAccept = false; + break; + case dt_Component: + if (widgetOnDiagram(id) || + (ot != ot_Interface && + ot != ot_Package && + ot != ot_Component && + ot != ot_Artifact && + ot != ot_Class)) + bAccept = false; + if (ot == ot_Class && !temp->getAbstract()) + bAccept = false; + break; + case dt_EntityRelationship: + if (ot != ot_Entity) + bAccept = false; + break; + default: + break; + } + e->accept(bAccept); +} + +void UMLView::contentsDropEvent(QDropEvent *e) { + UMLDrag::LvTypeAndID_List tidList; + if( !UMLDrag::getClip3TypeAndID(e, tidList) ) { + return; + } + UMLDrag::LvTypeAndID_It tidIt(tidList); + UMLDrag::LvTypeAndID * tid = tidIt.current(); + if (!tid) { + kDebug() << "UMLView::contentsDropEvent: " + << "UMLDrag::getClip3TypeAndID returned empty list" << endl; + return; + } + ListView_Type lvtype = tid->type; + Uml::IDType id = tid->id; + + if (Model_Utils::typeIsDiagram(lvtype)) { + UMLWidget *w = NULL; + for (w = m_WidgetList.first(); w; w = m_WidgetList.next()) { + if (w->getBaseType() == Uml::wt_Note && w->onWidget(e->pos())) + break; + } + if (w) { + NoteWidget *note = static_cast(w); + note->setDiagramLink(id); + } + return; + } + UMLObject* o = m_pDoc->findObjectById(id); + if( !o ) { + kDebug() << "UMLView::contentsDropEvent: object id=" << ID2STR(id) + << " not found" << endl; + return; + } + m_bCreateObject = true; + m_Pos = (e->pos() * 100 ) / m_nZoom; + + slotObjectCreated(o); + + m_pDoc -> setModified(true); +} + +ObjectWidget * UMLView::onWidgetLine( const QPoint &point ) { + UMLWidget *obj; + for (UMLWidgetListIt it(m_WidgetList); (obj = it.current()) != NULL; ++it) { + ObjectWidget *ow = dynamic_cast(obj); + if (ow == NULL) + continue; + SeqLineWidget *pLine = ow->getSeqLine(); + if (pLine == NULL) { + kError() << "UMLView::onWidgetLine: SeqLineWidget of " << ow->getName() + << " (id=" << ID2STR(ow->getLocalID()) << ") is NULL" << endl; + continue; + } + if (pLine->onWidget(point)) + return ow; + } + return 0; +} + +UMLWidget *UMLView::getWidgetAt(const QPoint& p) { + int relativeSize = 10000; // start with an arbitrary large number + UMLWidget *obj, *retObj = NULL; + UMLWidgetListIt it(m_WidgetList); + for (UMLWidgetListIt it(m_WidgetList); (obj = it.current()) != NULL; ++it) { + const int s = obj->onWidget(p); + if (!s) + continue; + if (s < relativeSize) { + relativeSize = s; + retObj = obj; + } + } + return retObj; +} + +void UMLView::checkMessages(ObjectWidget * w) { + if(getType() != dt_Sequence) + return; + + MessageWidgetListIt it( m_MessageList ); + MessageWidget *obj; + while ( (obj = it.current()) != 0 ) { + ++it; + if(! obj -> contains(w)) + continue; + //make sure message doesn't have any associations + removeAssociations(obj); + obj -> cleanup(); + //make sure not in selected list + m_SelectedList.remove(obj); + m_MessageList.remove(obj); + delete obj; + } +} + +bool UMLView::widgetOnDiagram(Uml::IDType id) { + UMLWidget *obj; + + UMLWidgetListIt it( m_WidgetList ); + while ( (obj = it.current()) != 0 ) { + ++it; + if(id == obj -> getID()) + return true; + } + + MessageWidgetListIt mit( m_MessageList ); + while ( (obj = (UMLWidget*)mit.current()) != 0 ) { + ++mit; + if(id == obj -> getID()) + return true; + } + + return false; +} + +void UMLView::contentsMouseMoveEvent(QMouseEvent* ome) { + m_pToolBarState->mouseMove(ome); +} + +// search both our UMLWidget AND MessageWidget lists +UMLWidget * UMLView::findWidget( Uml::IDType id ) { + + UMLWidgetListIt it( m_WidgetList ); + UMLWidget * obj = NULL; + while ( (obj = it.current()) != 0 ) { + ++it; + // object widgets are special..the widget id is held by 'localId' attribute (crappy!) + if( obj -> getBaseType() == wt_Object ) { + if( static_cast( obj ) -> getLocalID() == id ) + return obj; + } else if( obj -> getID() == id ) { + return obj; + } + } + + MessageWidgetListIt mit( m_MessageList ); + while ( (obj = (UMLWidget*)mit.current()) != 0 ) { + ++mit; + if( obj -> getID() == id ) + return obj; + } + + return 0; +} + + + +AssociationWidget * UMLView::findAssocWidget( Uml::IDType id ) { + AssociationWidget *obj; + AssociationWidgetListIt it( m_AssociationList ); + while ( (obj = it.current()) != 0 ) { + ++it; + UMLAssociation* umlassoc = obj -> getAssociation(); + if ( umlassoc && umlassoc->getID() == id ) { + return obj; + } + } + return 0; +} + +AssociationWidget * UMLView::findAssocWidget(UMLWidget *pWidgetA, + UMLWidget *pWidgetB, const QString& roleNameB) { + AssociationWidget *assoc; + AssociationWidgetListIt it(m_AssociationList); + while ((assoc = it.current()) != 0) { + ++it; + const Association_Type testType = assoc->getAssocType(); + if (testType != Uml::at_Association && + testType != Uml::at_UniAssociation && + testType != Uml::at_Composition && + testType != Uml::at_Aggregation) + continue; + if (pWidgetA->getID() == assoc->getWidgetID(A) && + pWidgetB->getID() == assoc->getWidgetID(B) && + assoc->getRoleName(Uml::B) == roleNameB) + return assoc; + } + return 0; +} + + +AssociationWidget * UMLView::findAssocWidget(Uml::Association_Type at, + UMLWidget *pWidgetA, UMLWidget *pWidgetB) { + AssociationWidget *assoc; + AssociationWidgetListIt it(m_AssociationList); + while ((assoc = it.current()) != 0) { + ++it; + Association_Type testType = assoc->getAssocType(); + if (testType != at) + continue; + if (pWidgetA->getID() == assoc->getWidgetID(A) && + pWidgetB->getID() == assoc->getWidgetID(B)) + return assoc; + } + return 0; +} + +void UMLView::removeWidget(UMLWidget * o) { + if(!o) + return; + + emit sigWidgetRemoved(o); + + removeAssociations(o); + + Widget_Type t = o->getBaseType(); + if(getType() == dt_Sequence && t == wt_Object) + checkMessages( static_cast(o) ); + + o -> cleanup(); + m_SelectedList.remove(o); + disconnect( this, SIGNAL( sigRemovePopupMenu() ), o, SLOT( slotRemovePopupMenu() ) ); + disconnect( this, SIGNAL( sigClearAllSelected() ), o, SLOT( slotClearAllSelected() ) ); + disconnect( this, SIGNAL(sigColorChanged(Uml::IDType)), o, SLOT(slotColorChanged(Uml::IDType))); + if (t == wt_Message) + m_MessageList.remove(static_cast(o)); + else + m_WidgetList.remove(o); + m_pDoc->setModified(); + delete o; +} + +bool UMLView::getUseFillColor() const { + return m_Options.uiState.useFillColor; +} + +void UMLView::setUseFillColor(bool ufc) { + m_Options.uiState.useFillColor = ufc; +} + +QColor UMLView::getFillColor() const { + return m_Options.uiState.fillColor; +} + +void UMLView::setFillColor(const QColor &color) { + m_Options.uiState.fillColor = color; + emit sigColorChanged( getID() ); + canvas()->setAllChanged(); +} + +QColor UMLView::getLineColor() const { + return m_Options.uiState.lineColor; +} + +void UMLView::setLineColor(const QColor &color) { + m_Options.uiState.lineColor = color; + emit sigColorChanged( getID() ); + canvas() -> setAllChanged(); +} + +uint UMLView::getLineWidth() const { + return m_Options.uiState.lineWidth; +} + +void UMLView::setLineWidth(uint width) { + m_Options.uiState.lineWidth = width; + emit sigLineWidthChanged( getID() ); + canvas() -> setAllChanged(); +} + +void UMLView::contentsMouseDoubleClickEvent(QMouseEvent* ome) { + m_pToolBarState->mouseDoubleClick(ome); +} + +QRect UMLView::getDiagramRect() { + int startx, starty, endx, endy; + startx = starty = INT_MAX; + endx = endy = 0; + UMLWidgetListIt it( m_WidgetList ); + UMLWidget *obj; + while ( (obj = it.current()) != 0 ) { + ++it; + if (! obj->isVisible()) + continue; + int objEndX = obj -> getX() + obj -> getWidth(); + int objEndY = obj -> getY() + obj -> getHeight(); + int objStartX = obj -> getX(); + int objStartY = obj -> getY(); + if (startx >= objStartX) + startx = objStartX; + if (starty >= objStartY) + starty = objStartY; + if(endx <= objEndX) + endx = objEndX; + if(endy <= objEndY) + endy = objEndY; + } + //if seq. diagram, make sure print all of the lines + if (getType() == dt_Sequence ) { + for (UMLWidgetListIt it(m_WidgetList); (obj = it.current()) != NULL; ++it) { + ObjectWidget *ow = dynamic_cast(obj); + if (ow == NULL) + continue; + int y = ow->getEndLineY(); + if (endy < y) + endy = y; + } + } + + /* now we need another look at the associations, because they are no + * UMLWidgets */ + AssociationWidgetListIt assoc_it (m_AssociationList); + AssociationWidget * assoc_obj; + QRect rect; + + while ((assoc_obj = assoc_it.current()) != 0) + { + /* get the rectangle around all segments of the assoc */ + rect = assoc_obj->getAssocLineRectangle(); + + if (startx >= rect.x()) + startx = rect.x(); + if (starty >= rect.y()) + starty = rect.y(); + if (endx <= rect.x() + rect.width()) + endx = rect.x() + rect.width(); + if (endy <= rect.y() + rect.height()) + endy = rect.y() + rect.height(); + ++assoc_it; // next assoc + } + + /* Margin causes problems of black border around the edge + // Margin: + startx -= 24; + starty -= 20; + endx += 24; + endy += 20; + */ + + return QRect(startx, starty, endx - startx, endy - starty); +} + +void UMLView::setSelected(UMLWidget * w, QMouseEvent * /*me*/) { + //only add if wasn't in list + if(!m_SelectedList.remove(w)) + m_SelectedList.append(w); + int count = m_SelectedList.count(); + //only call once - if we select more, no need to keep clearing window + + // if count == 1, widget will update the doc window with their data when selected + if( count == 2 ) + updateDocumentation( true );//clear doc window + + /* selection changed, we have to make sure the copy and paste items + * are correctly enabled/disabled */ + UMLApp::app()->slotCopyChanged(); +} + +void UMLView::clearSelected() { + m_SelectedList.clear(); + emit sigClearAllSelected(); + //m_pDoc -> enableCutCopy(false); +} + +//TODO Only used in MLApp::handleCursorKeyReleaseEvent +void UMLView::moveSelectedBy(int dX, int dY) { + for (UMLWidget *w = m_SelectedList.first(); w; w = m_SelectedList.next()) + w->moveBy(dX, dY); +} + +void UMLView::selectionUseFillColor(bool useFC) { + UMLWidget * temp = 0; + for(temp=(UMLWidget *)m_SelectedList.first();temp;temp=(UMLWidget *)m_SelectedList.next()) + temp -> setUseFillColour(useFC); +} + +void UMLView::selectionSetFont( const QFont &font ) +{ + UMLWidget * temp = 0; + for(temp=(UMLWidget *)m_SelectedList.first();temp;temp=(UMLWidget *)m_SelectedList.next()) + temp -> setFont( font ); +} + +void UMLView::selectionSetLineColor( const QColor &color ) +{ + UMLWidget * temp = 0; + for (temp = m_SelectedList.first(); temp; temp = m_SelectedList.next()) { + temp->setLineColor(color); + temp->setUsesDiagramLineColour(false); + } + AssociationWidgetList assoclist = getSelectedAssocs(); + for (AssociationWidget *aw = assoclist.first(); aw; aw = assoclist.next()) { + aw->setLineColor(color); + aw->setUsesDiagramLineColour(false); + } +} + +void UMLView::selectionSetLineWidth( uint width ) +{ + UMLWidget * temp = 0; + for (temp = m_SelectedList.first(); temp; temp = m_SelectedList.next()) { + temp->setLineWidth(width); + temp->setUsesDiagramLineWidth(false); + } + AssociationWidgetList assoclist = getSelectedAssocs(); + for (AssociationWidget *aw = assoclist.first(); aw; aw = assoclist.next()) { + aw->setLineWidth(width); + aw->setUsesDiagramLineWidth(false); + } +} + +void UMLView::selectionSetFillColor( const QColor &color ) +{ + UMLWidget * temp = 0; + for(temp=(UMLWidget *) m_SelectedList.first(); + temp; + temp=(UMLWidget *)m_SelectedList.next()) { + temp -> setFillColour( color ); + temp -> setUsesDiagramFillColour(false); + } +} + +void UMLView::selectionToggleShow(int sel) +{ + // loop through all selected items + for(UMLWidget *temp = (UMLWidget *)m_SelectedList.first(); + temp; temp=(UMLWidget *)m_SelectedList.next()) { + Widget_Type type = temp->getBaseType(); + ClassifierWidget *cw = dynamic_cast(temp); + + // toggle the show setting sel + switch (sel) + { + // some setting are only available for class, some for interface and some + // for both + case ListPopupMenu::mt_Show_Attributes_Selection: + if (type == wt_Class) + cw -> toggleShowAtts(); + break; + case ListPopupMenu::mt_Show_Operations_Selection: + if (cw) + cw -> toggleShowOps(); + break; + case ListPopupMenu::mt_Visibility_Selection: + if (cw) + cw -> toggleShowVisibility(); + break; + case ListPopupMenu::mt_DrawAsCircle_Selection: + if (type == wt_Interface) + cw -> toggleDrawAsCircle(); + break; + case ListPopupMenu::mt_Show_Operation_Signature_Selection: + if (cw) + cw -> toggleShowOpSigs(); + break; + case ListPopupMenu::mt_Show_Attribute_Signature_Selection: + if (type == wt_Class) + cw -> toggleShowAttSigs(); + break; + case ListPopupMenu::mt_Show_Packages_Selection: + if (cw) + cw -> toggleShowPackage(); + break; + case ListPopupMenu::mt_Show_Stereotypes_Selection: + if (type == wt_Class) + cw -> toggleShowStereotype(); + break; + case ListPopupMenu::mt_Show_Public_Only_Selection: + if (cw) + cw -> toggleShowPublicOnly(); + break; + default: + break; + } // switch (sel) + } +} + +void UMLView::deleteSelection() +{ + /* + Don't delete text widget that are connect to associations as these will + be cleaned up by the associations. + */ + UMLWidget * temp = 0; + for(temp=(UMLWidget *) m_SelectedList.first(); + temp; + temp=(UMLWidget *)m_SelectedList.next()) + { + if( temp -> getBaseType() == wt_Text && + ((FloatingTextWidget *)temp) -> getRole() != tr_Floating ) + { + m_SelectedList.remove(); // remove advances the iterator to the next position, + m_SelectedList.prev(); // let's allow for statement do the advancing + temp -> hide(); + } else { + removeWidget(temp); + } + } + + // Delete any selected associations. + AssociationWidgetListIt assoc_it( m_AssociationList ); + AssociationWidget* assocwidget = 0; + while((assocwidget=assoc_it.current())) { + ++assoc_it; + if( assocwidget-> getSelected() ) + removeAssoc(assocwidget); + // MARK + } + + /* we also have to remove selected messages from sequence diagrams */ + MessageWidget * cur_msgWgt; + + /* loop through all messages and check the selection state */ + for (cur_msgWgt = m_MessageList.first(); cur_msgWgt; + cur_msgWgt = m_MessageList.next()) + { + if (cur_msgWgt->getSelected() == true) + { + removeWidget(cur_msgWgt); // Remove message - it is selected. + } + } + + // sometimes we miss one widget, so call this function again to remove it as + // well + if (m_SelectedList.count() != 0) + deleteSelection(); + + //make sure list empty - it should be anyway, just a check. + m_SelectedList.clear(); +} + +void UMLView::selectAll() +{ + selectWidgets(0, 0, canvas()->width(), canvas()->height()); +} + +Uml::IDType UMLView::getLocalID() { + m_nLocalID = UniqueID::gen(); + return m_nLocalID; +} + +bool UMLView::isSavedInSeparateFile() { + if (getOptionState().generalState.tabdiagrams) { + // Umbrello currently does not support external folders + // when tabbed diagrams are enabled. + return false; + } + const QString msgPrefix("UMLView::isSavedInSeparateFile(" + getName() + "): "); + UMLListView *listView = UMLApp::app()->getListView(); + UMLListViewItem *lvItem = listView->findItem(m_nID); + if (lvItem == NULL) { + kError() << msgPrefix + << "listView->findUMLObject(this) returns false" << endl; + return false; + } + UMLListViewItem *parentItem = dynamic_cast( lvItem->parent() ); + if (parentItem == NULL) { + kError() << msgPrefix + << "parent item in listview is not a UMLListViewItem (?)" << endl; + return false; + } + const Uml::ListView_Type lvt = parentItem->getType(); + if (! Model_Utils::typeIsFolder(lvt)) + return false; + UMLFolder *modelFolder = dynamic_cast(parentItem->getUMLObject()); + if (modelFolder == NULL) { + kError() << msgPrefix + << "parent model object is not a UMLFolder (?)" << endl; + return false; + } + QString folderFile = modelFolder->getFolderFile(); + return !folderFile.isEmpty(); +} + +void UMLView::contentsMousePressEvent(QMouseEvent* ome) { + m_pToolBarState->mousePress(ome); + //TODO should be managed by widgets when are selected. Right now also has some + //problems, such as clicking on a widget, and clicking to move that widget shows + //documentation of the diagram instead of keeping the widget documentation. + //When should diagram documentation be shown? When clicking on an empty + //space in the diagram with arrow tool? + if (!m_bChildDisplayedDoc) { + UMLApp::app() -> getDocWindow() -> showDocumentation( this, true ); + } + m_bChildDisplayedDoc = false; +} + +void UMLView::makeSelected (UMLWidget * uw) { + if (uw == NULL) + return; + uw -> setSelected(true); + m_SelectedList.remove(uw); // make sure not in there + m_SelectedList.append(uw); +} + +void UMLView::selectWidgetsOfAssoc (AssociationWidget * a) { + if (!a) + return; + a -> setSelected(true); + //select the two widgets + makeSelected( a->getWidget(A) ); + makeSelected( a->getWidget(B) ); + //select all the text + makeSelected( a->getMultiWidget(A) ); + makeSelected( a->getMultiWidget(B) ); + makeSelected( a->getRoleWidget(A) ); + makeSelected( a->getRoleWidget(B) ); + makeSelected( a->getChangeWidget(A) ); + makeSelected( a->getChangeWidget(B) ); +} + +void UMLView::selectWidgets(int px, int py, int qx, int qy) { + clearSelected(); + + QRect rect; + if(px <= qx) { + rect.setLeft(px); + rect.setRight(qx); + } else { + rect.setLeft(qx); + rect.setRight(px); + } + if(py <= qy) { + rect.setTop(py); + rect.setBottom(qy); + } else { + rect.setTop(qy); + rect.setBottom(py); + } + UMLWidgetListIt it(m_WidgetList); + UMLWidget * temp = NULL; + while ( (temp = it.current()) != 0 ) { + int x = temp -> getX(); + int y = temp -> getY(); + int w = temp -> getWidth(); + int h = temp -> getHeight(); + QRect rect2(x, y, w, h); + ++it; + //see if any part of widget is in the rectangle + if( !rect.intersects(rect2) ) + continue; + //if it is text that is part of an association then select the association + //and the objects that are connected to it. + if (temp -> getBaseType() == wt_Text) { + FloatingTextWidget *ft = static_cast(temp); + Text_Role t = ft -> getRole(); + LinkWidget *lw = ft->getLink(); + MessageWidget * mw = dynamic_cast(lw); + if (mw) { + makeSelected( mw ); + makeSelected( mw->getWidget(A) ); + makeSelected( mw->getWidget(B) ); + } else if (t != tr_Floating) { + AssociationWidget * a = dynamic_cast(lw); + if (a) + selectWidgetsOfAssoc( a ); + } + } else if(temp -> getBaseType() == wt_Message) { + MessageWidget *mw = static_cast(temp); + makeSelected( mw -> getWidget(A) ); + makeSelected( mw -> getWidget(B) ); + } + if(temp -> isVisible()) { + makeSelected( temp ); + } + } + selectAssociations( true ); + + //now do the same for the messagewidgets + MessageWidgetListIt itw( m_MessageList ); + MessageWidget *w = 0; + while ( (w = itw.current()) != 0 ) { + ++itw; + if ( w -> getWidget(A) -> getSelected() && + w -> getWidget(B) -> getSelected() ) { + makeSelected( w ); + }//end if + }//end while +} + +void UMLView::getDiagram(const QRect &rect, QPixmap & diagram) { + QPixmap pixmap(rect.x() + rect.width(), rect.y() + rect.height()); + QPainter painter(&pixmap); + getDiagram(canvas()->rect(),painter); + bitBlt(&diagram, QPoint(0, 0), &pixmap, rect); +} + +void UMLView::getDiagram(const QRect &area, QPainter & painter) { + //TODO unselecting and selecting later doesn't work now as the selection is + //cleared in UMLViewImageExporter. Check if the anything else than the + //following is needed and, if it works, remove the clearSelected in + //UMLViewImageExporter and UMLViewImageExporterModel + UMLWidget* widget = 0; + for (widget=(UMLWidget*)m_SelectedList.first(); widget; widget=(UMLWidget*)m_SelectedList.next()) { + widget->setSelected(false); + } + AssociationWidgetList selectedAssociationsList = getSelectedAssocs(); + AssociationWidget* association = 0; + for (association=selectedAssociationsList.first(); association; + association=selectedAssociationsList.next()) { + association->setSelected(false); + } + + // we don't want to get the grid + bool showSnapGrid = getShowSnapGrid(); + setShowSnapGrid(false); + + canvas()->drawArea(area, &painter); + + setShowSnapGrid(showSnapGrid); + + canvas()->setAllChanged(); + //select again + for (widget=(UMLWidget *)m_SelectedList.first(); widget; widget=(UMLWidget *)m_SelectedList.next()) { + widget->setSelected( true ); + } + for (association=selectedAssociationsList.first(); association; + association=selectedAssociationsList.next()) { + association->setSelected(true); + } + + return; +} + +UMLViewImageExporter* UMLView::getImageExporter() { + return m_pImageExporter; +} + +void UMLView::slotActivate() { + m_pDoc->changeCurrentView(getID()); +} + +UMLObjectList UMLView::getUMLObjects() { + UMLObjectList list; + for (UMLWidgetListIt it(m_WidgetList); it.current(); ++it) { + UMLWidget *w = it.current(); + switch (w->getBaseType()) //use switch for easy future expansion + { + case wt_Actor: + case wt_Class: + case wt_Interface: + case wt_Package: + case wt_Component: + case wt_Node: + case wt_Artifact: + case wt_UseCase: + case wt_Object: + list.append( w->getUMLObject() ); + break; + default: + break; + } + } + return list; +} + +void UMLView::activate() { + UMLWidgetListIt it( m_WidgetList ); + UMLWidget *obj; + + //Activate Regular widgets then activate messages + while ( (obj = it.current()) != 0 ) { + ++it; + //If this UMLWidget is already activated or is a MessageWidget then skip it + if(obj->isActivated() || obj->getBaseType() == wt_Message) + continue; + + if (obj->activate()) { + obj->setVisible(true); + } else { + m_WidgetList.remove(obj); + delete obj; + } + }//end while + + MessageWidgetListIt it2( m_MessageList ); + //Activate Message widgets + while ( (obj = (UMLWidget*)it2.current()) != 0 ) { + ++it2; + //If this MessageWidget is already activated then skip it + if(obj->isActivated()) + continue; + + obj->activate(m_pDoc->getChangeLog()); + obj->setVisible( true ); + + }//end while + + // Activate all association widgets + AssociationWidget *aw; + for (AssociationWidgetListIt ait(m_AssociationList); + (aw = ait.current()); ++ait) { + if (aw->activate()) { + if (m_PastePoint.x() != 0) { + int x = m_PastePoint.x() - m_Pos.x(); + int y = m_PastePoint.y() - m_Pos.y(); + aw->moveEntireAssoc(x, y); + } + } else { + m_AssociationList.remove(aw); + } + } +} + +int UMLView::getSelectCount(bool filterText) const { + if (!filterText) + return m_SelectedList.count(); + int counter = 0; + const UMLWidget * temp = 0; + for (UMLWidgetListIt iter(m_SelectedList); (temp = iter.current()) != 0; ++iter) { + if (temp->getBaseType() == wt_Text) { + const FloatingTextWidget *ft = static_cast(temp); + if (ft->getRole() == tr_Floating) + counter++; + } else { + counter++; + } + } + return counter; +} + + +bool UMLView::getSelectedWidgets(UMLWidgetList &WidgetList, bool filterText /*= true*/) { + const UMLWidget * temp = 0; + for (UMLWidgetListIt it(m_SelectedList); (temp = it.current()) != NULL; ++it) { + if (filterText && temp->getBaseType() == wt_Text) { + const FloatingTextWidget *ft = static_cast(temp); + if (ft->getRole() == tr_Floating) + WidgetList.append(temp); + } else { + WidgetList.append(temp); + } + }//end for + return true; +} + +AssociationWidgetList UMLView::getSelectedAssocs() { + AssociationWidgetList assocWidgetList; + AssociationWidgetListIt assoc_it( m_AssociationList ); + AssociationWidget* assocwidget = 0; + while((assocwidget=assoc_it.current())) { + ++assoc_it; + if( assocwidget -> getSelected() ) + assocWidgetList.append(assocwidget); + } + return assocWidgetList; +} + +bool UMLView::addWidget( UMLWidget * pWidget , bool isPasteOperation ) { + if( !pWidget ) { + return false; + } + Widget_Type type = pWidget->getBaseType(); + if (isPasteOperation) { + if (type == Uml::wt_Message) + m_MessageList.append(static_cast(pWidget)); + else + m_WidgetList.append(pWidget); + return true; + } + if (!isPasteOperation && findWidget(pWidget->getID())) { + kError() << "UMLView::addWidget: Not adding " + << "(id=" << ID2STR(pWidget->getID()) + << "/type=" << type << "/name=" << pWidget->getName() + << ") because it's already there" << endl; + return false; + } + //kDebug() << "UMLView::addWidget called for basetype " << type << endl; + IDChangeLog * log = m_pDoc -> getChangeLog(); + if( isPasteOperation && (!log || !m_pIDChangesLog)) { + kError()<<" Cant addWidget to view in paste op because a log is not open"< getX(); + int wY = pWidget -> getY(); + bool xIsOutOfRange = (wX <= 0 || wX >= FloatingTextWidget::restrictPositionMax); + bool yIsOutOfRange = (wY <= 0 || wY >= FloatingTextWidget::restrictPositionMax); + if (xIsOutOfRange || yIsOutOfRange) { + QString name = pWidget->getName(); + if (name.isEmpty()) { + FloatingTextWidget *ft = dynamic_cast(pWidget); + if (ft) + name = ft->getDisplayText(); + } + kDebug() << "UMLView::addWidget (" << name << " type=" + << pWidget->getBaseType() << "): position (" << wX << "," + << wY << ") is out of range" << endl; + if (xIsOutOfRange) { + pWidget->setX(0); + wX = 0; + } + if (yIsOutOfRange) { + pWidget->setY(0); + wY = 0; + } + } + if( wX < m_Pos.x() ) + m_Pos.setX( wX ); + if( wY < m_Pos.y() ) + m_Pos.setY( wY ); + + //see if we need a new id to match object + switch( type ) { + + case wt_Class: + case wt_Package: + case wt_Component: + case wt_Node: + case wt_Artifact: + case wt_Interface: + case wt_Enum: + case wt_Entity: + case wt_Datatype: + case wt_Actor: + case wt_UseCase: + { + Uml::IDType id = pWidget -> getID(); + Uml::IDType newID = log->findNewID( id ); + if( newID == Uml::id_None ) { // happens after a cut + if (id == Uml::id_None) + return false; + newID = id; //don't stop paste + } else + pWidget -> setID( newID ); + UMLObject * pObject = m_pDoc -> findObjectById( newID ); + if( !pObject ) { + kDebug() << "addWidget: Can't find UMLObject for id " + << ID2STR(newID) << endl; + return false; + } + pWidget -> setUMLObject( pObject ); + //make sure it doesn't already exist. + if (findWidget(newID)) { + kDebug() << "UMLView::addWidget: Not adding " + << "(id=" << ID2STR(pWidget->getID()) + << "/type=" << pWidget->getBaseType() + << "/name=" << pWidget->getName() + << ") because it's already there" << endl; + delete pWidget; // Not nice but if _we_ don't do it nobody else will + return true;//don't stop paste just because widget found. + } + m_WidgetList.append( pWidget ); + } + break; + + case wt_Message: + case wt_Note: + case wt_Box: + case wt_Text: + case wt_State: + case wt_Activity: + { + Uml::IDType newID = m_pDoc->assignNewID( pWidget->getID() ); + pWidget->setID(newID); + if (type != wt_Message) { + m_WidgetList.append( pWidget ); + return true; + } + // CHECK + // Handling of wt_Message: + MessageWidget *pMessage = static_cast( pWidget ); + if (pMessage == NULL) { + kDebug() << "UMLView::addWidget(): pMessage is NULL" << endl; + return false; + } + ObjectWidget *objWidgetA = pMessage -> getWidget(A); + ObjectWidget *objWidgetB = pMessage -> getWidget(B); + Uml::IDType waID = objWidgetA -> getLocalID(); + Uml::IDType wbID = objWidgetB -> getLocalID(); + Uml::IDType newWAID = m_pIDChangesLog ->findNewID( waID ); + Uml::IDType newWBID = m_pIDChangesLog ->findNewID( wbID ); + if( newWAID == Uml::id_None || newWBID == Uml::id_None ) { + kDebug() << "Error with ids : " << ID2STR(newWAID) + << " " << ID2STR(newWBID) << endl; + return false; + } + // Assumption here is that the A/B objectwidgets and the textwidget + // are pristine in the sense that we may freely change their local IDs. + objWidgetA -> setLocalID( newWAID ); + objWidgetB -> setLocalID( newWBID ); + FloatingTextWidget *ft = pMessage->getFloatingTextWidget(); + if (ft == NULL) + kDebug() << "UMLView::addWidget: FloatingTextWidget of Message is NULL" << endl; + else if (ft->getID() == Uml::id_None) + ft->setID( UniqueID::gen() ); + else { + Uml::IDType newTextID = m_pDoc->assignNewID( ft->getID() ); + ft->setID( newTextID ); + } + m_MessageList.append( pMessage ); + } + break; + + case wt_Object: + { + ObjectWidget* pObjectWidget = static_cast(pWidget); + if (pObjectWidget == NULL) { + kDebug() << "UMLView::addWidget(): pObjectWidget is NULL" << endl; + return false; + } + Uml::IDType nNewLocalID = getLocalID(); + Uml::IDType nOldLocalID = pObjectWidget -> getLocalID(); + m_pIDChangesLog->addIDChange( nOldLocalID, nNewLocalID ); + pObjectWidget -> setLocalID( nNewLocalID ); + UMLObject *pObject = m_pDoc->findObjectById(pWidget->getID()); + if( !pObject ) { + kDebug() << "addWidget::Can't find UMLObject" << endl; + return false; + } + pWidget -> setUMLObject( pObject ); + m_WidgetList.append( pWidget ); + } + break; + + default: + kDebug() << "Trying to add an invalid widget type" << endl; + return false; + break; + } + + return true; +} + +// Add the association, and its child widgets to this view +bool UMLView::addAssociation(AssociationWidget* pAssoc , bool isPasteOperation) { + + if (!pAssoc) { + return false; + } + const Association_Type type = pAssoc->getAssocType(); + + if( isPasteOperation ) + { + IDChangeLog * log = m_pDoc -> getChangeLog(); + + if(!log ) + return false; + + Uml::IDType ida = Uml::id_None, idb = Uml::id_None; + if( getType() == dt_Collaboration || getType() == dt_Sequence ) { + //check local log first + ida = m_pIDChangesLog->findNewID( pAssoc->getWidgetID(A) ); + idb = m_pIDChangesLog->findNewID( pAssoc->getWidgetID(B) ); + //if either is still not found and assoc type is anchor + //we are probably linking to a notewidet - else an error + if( ida == Uml::id_None && type == at_Anchor ) + ida = log->findNewID(pAssoc->getWidgetID(A)); + if( idb == Uml::id_None && type == at_Anchor ) + idb = log->findNewID(pAssoc->getWidgetID(B)); + } else { + Uml::IDType oldIdA = pAssoc->getWidgetID(A); + Uml::IDType oldIdB = pAssoc->getWidgetID(B); + ida = log->findNewID( oldIdA ); + if (ida == Uml::id_None) { // happens after a cut + if (oldIdA == Uml::id_None) + return false; + ida = oldIdA; + } + idb = log->findNewID( oldIdB ); + if (idb == Uml::id_None) { // happens after a cut + if (oldIdB == Uml::id_None) + return false; + idb = oldIdB; + } + } + if(ida == Uml::id_None || idb == Uml::id_None) { + return false; + } + // cant do this anymore.. may cause problem for pasting + // pAssoc->setWidgetID(ida, A); + // pAssoc->setWidgetID(idb, B); + pAssoc->setWidget(findWidget(ida), A); + pAssoc->setWidget(findWidget(idb), B); + } + + UMLWidget * pWidgetA = findWidget(pAssoc->getWidgetID(A)); + UMLWidget * pWidgetB = findWidget(pAssoc->getWidgetID(B)); + //make sure valid widget ids + if (!pWidgetA || !pWidgetB) { + return false; + } + + //make sure valid + if (!isPasteOperation && !m_pDoc->loading() && + !AssocRules::allowAssociation(type, pWidgetA, pWidgetB, false)) { + kWarning() << "UMLView::addAssociation: allowAssociation returns false " + << "for AssocType " << type << endl; + return false; + } + + //make sure there isn't already the same assoc + AssociationWidgetListIt assoc_it( m_AssociationList ); + AssociationWidget* assocwidget = 0; + while((assocwidget=assoc_it.current())) { + ++assoc_it; + if( *pAssoc == *assocwidget ) + // this is nuts. Paste operation wants to know if 'true' + // for duplicate, but loadFromXMI needs 'false' value + return (isPasteOperation? true: false); + } + + m_AssociationList.append(pAssoc); + + FloatingTextWidget *ft[5] = { pAssoc->getNameWidget(), + pAssoc->getRoleWidget(A), + pAssoc->getRoleWidget(B), + pAssoc->getMultiWidget(A), + pAssoc->getMultiWidget(B) }; + for (int i = 0; i < 5; i++) { + FloatingTextWidget *flotxt = ft[i]; + if (flotxt) { + flotxt->updateComponentSize(); + addWidget(flotxt); + } + } + + return true; +} + +void UMLView::activateAfterLoad(bool bUseLog) { + if (m_bActivated) + return; + if( bUseLog ) { + beginPartialWidgetPaste(); + } + + //now activate them all + activate(); + + if( bUseLog ) { + endPartialWidgetPaste(); + } + resizeCanvasToItems(); + setZoom( getZoom() ); + m_bActivated = true; +} + +void UMLView::beginPartialWidgetPaste() { + delete m_pIDChangesLog; + m_pIDChangesLog = 0; + + m_pIDChangesLog = new IDChangeLog(); + m_bPaste = true; +} + +void UMLView::endPartialWidgetPaste() { + delete m_pIDChangesLog; + m_pIDChangesLog = 0; + + m_bPaste = false; +} + +void UMLView::removeAssoc(AssociationWidget* pAssoc) { + if(!pAssoc) + return; + + emit sigAssociationRemoved(pAssoc); + + pAssoc->cleanup(); + m_AssociationList.remove(pAssoc); // will delete our association + m_pDoc->setModified(); +} + +void UMLView::removeAssocInViewAndDoc(AssociationWidget* a) { + // For umbrello 1.2, UMLAssociations can only be removed in two ways: + // 1. Right click on the assocwidget in the view and select Delete + // 2. Go to the Class Properties page, select Associations, right click + // on the association and select Delete + if(!a) + return; + if (a->getAssocType() == at_Containment) { + UMLObject *objToBeMoved = a->getWidget(B)->getUMLObject(); + if (objToBeMoved != NULL) { + UMLListView *lv = UMLApp::app()->getListView(); + lv->moveObject( objToBeMoved->getID(), + Model_Utils::convert_OT_LVT(objToBeMoved), + lv->theLogicalView() ); + // UMLListView::moveObject() will delete the containment + // AssociationWidget via UMLView::updateContainment(). + } else { + kDebug() << "removeAssocInViewAndDoc(containment): " + << "objB is NULL" << endl; + } + } else { + // Remove assoc in doc. + m_pDoc->removeAssociation(a->getAssociation()); + // Remove assoc in view. + removeAssoc(a); + } +} + +/** Removes all the associations related to Widget */ +void UMLView::removeAssociations(UMLWidget* Widget) { + AssociationWidgetListIt assoc_it(m_AssociationList); + AssociationWidget* assocwidget = 0; + while((assocwidget=assoc_it.current())) { + ++assoc_it; + if(assocwidget->contains(Widget)) { + removeAssoc(assocwidget); + } + } +} + +void UMLView::selectAssociations(bool bSelect) { + AssociationWidgetListIt assoc_it(m_AssociationList); + AssociationWidget* assocwidget = 0; + while((assocwidget=assoc_it.current())) { + ++assoc_it; + if(bSelect && + assocwidget->getWidget(A) && assocwidget->getWidget(A)->getSelected() && + assocwidget->getWidget(B) && assocwidget->getWidget(B)->getSelected() ) { + assocwidget->setSelected(true); + } else { + assocwidget->setSelected(false); + } + }//end while +} + +void UMLView::getWidgetAssocs(UMLObject* Obj, AssociationWidgetList & Associations) { + if( ! Obj ) + return; + + AssociationWidgetListIt assoc_it(m_AssociationList); + AssociationWidget * assocwidget; + while((assocwidget = assoc_it.current())) { + if (assocwidget->getWidget(A)->getUMLObject() == Obj || + assocwidget->getWidget(B)->getUMLObject() == Obj) + Associations.append(assocwidget); + ++assoc_it; + }//end while +} + +void UMLView::closeEvent ( QCloseEvent * e ) { + QWidget::closeEvent(e); +} + +void UMLView::removeAllAssociations() { + //Remove All association widgets + AssociationWidgetListIt assoc_it(m_AssociationList); + AssociationWidget* assocwidget = 0; + while((assocwidget=assoc_it.current())) + { + ++assoc_it; + removeAssoc(assocwidget); + } + m_AssociationList.clear(); +} + + +void UMLView::removeAllWidgets() { + // Remove widgets. + UMLWidgetListIt it( m_WidgetList ); + UMLWidget * temp = 0; + while ( (temp = it.current()) != 0 ) { + ++it; + // I had to take this condition back in, else umbrello + // crashes on exit. Still to be analyzed. --okellogg + if( !( temp -> getBaseType() == wt_Text && + ((FloatingTextWidget *)temp)-> getRole() != tr_Floating ) ) { + removeWidget( temp ); + } + } + m_WidgetList.clear(); +} + +void UMLView::showDocumentation( UMLObject * object, bool overwrite ) { + UMLApp::app() -> getDocWindow() -> showDocumentation( object, overwrite ); + m_bChildDisplayedDoc = true; +} + +void UMLView::showDocumentation( UMLWidget * widget, bool overwrite ) { + UMLApp::app() -> getDocWindow() -> showDocumentation( widget, overwrite ); + m_bChildDisplayedDoc = true; +} + +void UMLView::showDocumentation( AssociationWidget * widget, bool overwrite ) { + UMLApp::app() -> getDocWindow() -> showDocumentation( widget, overwrite ); + m_bChildDisplayedDoc = true; +} + +void UMLView::updateDocumentation( bool clear ) { + UMLApp::app() -> getDocWindow() -> updateDocumentation( clear ); +} + +void UMLView::updateContainment(UMLCanvasObject *self) { + if (self == NULL) + return; + // See if the object has a widget representation in this view. + // While we're at it, also see if the new parent has a widget here. + UMLWidget *selfWidget = NULL, *newParentWidget = NULL; + UMLPackage *newParent = self->getUMLPackage(); + for (UMLWidgetListIt wit(m_WidgetList); wit.current(); ++wit) { + UMLWidget *w = wit.current(); + UMLObject *o = w->getUMLObject(); + if (o == self) + selfWidget = w; + else if (newParent != NULL && o == newParent) + newParentWidget = w; + } + if (selfWidget == NULL) + return; + // Remove possibly obsoleted containment association. + for (AssociationWidgetListIt it(m_AssociationList); it.current(); ++it) { + AssociationWidget *a = it.current(); + if (a->getAssocType() != Uml::at_Containment) + continue; + // Container is at role A, containee at B. + // We only look at association for which we are B. + UMLWidget *wB = a->getWidget(B); + UMLObject *roleBObj = wB->getUMLObject(); + if (roleBObj != self) + continue; + UMLWidget *wA = a->getWidget(A); + UMLObject *roleAObj = wA->getUMLObject(); + if (roleAObj == newParent) { + // Wow, all done. Great! + return; + } + removeAssoc(a); // AutoDelete is true + // It's okay to break out because there can only be a single + // containing object. + break; + } + if (newParentWidget == NULL) + return; + // Create the new containment association. + AssociationWidget *a = new AssociationWidget(this, newParentWidget, + Uml::at_Containment, selfWidget); + a->calculateEndingPoints(); + a->setActivated(true); + m_AssociationList.append(a); +} + +void UMLView::createAutoAssociations( UMLWidget * widget ) { + if (widget == NULL || + (m_Type != Uml::dt_Class && + m_Type != Uml::dt_Component && + m_Type != Uml::dt_Deployment && + m_Type != Uml::dt_EntityRelationship)) + return; + // Recipe: + // If this widget has an underlying UMLCanvasObject then + // for each of the UMLCanvasObject's UMLAssociations + // if umlassoc's "other" role has a widget representation on this view then + // if the AssocWidget does not already exist then + // if the assoc type is permitted in the current diagram type then + // create the AssocWidget + // end if + // end if + // end if + // end loop + // Do createAutoAttributeAssociations() + // if this object is capable of containing nested objects then + // for each of the object's containedObjects + // if the containedObject has a widget representation on this view then + // if the containedWidget is not physically located inside this widget + // create the containment AssocWidget + // end if + // end if + // end loop + // end if + // if the UMLCanvasObject has a parentPackage then + // if the parentPackage has a widget representation on this view then + // create the containment AssocWidget + // end if + // end if + // end if + UMLObject *tmpUmlObj = widget->getUMLObject(); + if (tmpUmlObj == NULL) + return; + UMLCanvasObject *umlObj = dynamic_cast(tmpUmlObj); + if (umlObj == NULL) + return; + const UMLAssociationList& umlAssocs = umlObj->getAssociations(); + UMLAssociationListIt it(umlAssocs); + UMLAssociation *assoc = NULL; + Uml::IDType myID = umlObj->getID(); + while ((assoc = it.current()) != NULL) { + ++it; + UMLCanvasObject *other = NULL; + UMLObject *roleAObj = assoc->getObject(A); + if (roleAObj == NULL) { + kDebug() << "createAutoAssociations: roleA object is NULL at UMLAssoc " + << ID2STR(assoc->getID()) << endl; + continue; + } + UMLObject *roleBObj = assoc->getObject(B); + if (roleBObj == NULL) { + kDebug() << "createAutoAssociations: roleB object is NULL at UMLAssoc " + << ID2STR(assoc->getID()) << endl; + continue; + } + if (roleAObj->getID() == myID) { + other = static_cast(roleBObj); + } else if (roleBObj->getID() == myID) { + other = static_cast(roleAObj); + } else { + kDebug() << "createAutoAssociations: Can't find own object " + << ID2STR(myID) << " in UMLAssoc " + << ID2STR(assoc->getID()) << endl; + continue; + } + // Now that we have determined the "other" UMLObject, seek it in + // this view's UMLWidgets. + Uml::IDType otherID = other->getID(); + UMLWidget *pOtherWidget; + UMLWidgetListIt wit(m_WidgetList); + while ((pOtherWidget = wit.current()) != NULL) { + ++wit; + if (pOtherWidget->getID() == otherID) + break; + } + if (pOtherWidget == NULL) + continue; + // Both objects are represented in this view: + // Assign widget roles as indicated by the UMLAssociation. + UMLWidget *widgetA, *widgetB; + if (myID == roleAObj->getID()) { + widgetA = widget; + widgetB = pOtherWidget; + } else { + widgetA = pOtherWidget; + widgetB = widget; + } + // Check that the assocwidget does not already exist. + Uml::Association_Type assocType = assoc->getAssocType(); + AssociationWidget * assocwidget = findAssocWidget(assocType, widgetA, widgetB); + if (assocwidget) { + assocwidget->calculateEndingPoints(); // recompute assoc lines + continue; + } + // Check that the assoc is allowed. + if (!AssocRules::allowAssociation(assocType, widgetA, widgetB, false)) { + kDebug() << "createAutoAssociations: not transferring assoc " + << "of type " << assocType << endl; + continue; + } + // Create the AssociationWidget. + assocwidget = new AssociationWidget( this ); + assocwidget->setWidget(widgetA, A); + assocwidget->setWidget(widgetB, B); + assocwidget->setAssocType(assocType); + assocwidget->setUMLObject(assoc); + // Call calculateEndingPoints() before setting the FloatingTexts + // because their positions are computed according to the + // assocwidget line positions. + assocwidget->calculateEndingPoints(); + assocwidget->syncToModel(); + assocwidget->setActivated(true); + if (! addAssociation(assocwidget)) + delete assocwidget; + } + createAutoAttributeAssociations(widget); + // if this object is capable of containing nested objects then + Uml::Object_Type t = umlObj->getBaseType(); + if (t == ot_Package || t == ot_Class || t == ot_Interface || t == ot_Component) { + // for each of the object's containedObjects + UMLPackage *umlPkg = static_cast(umlObj); + UMLObjectList lst = umlPkg->containedObjects(); + for (UMLObject *obj = lst.first(); obj; obj = lst.next()) { + // if the containedObject has a widget representation on this view then + Uml::IDType id = obj->getID(); + for (UMLWidget *w = m_WidgetList.first(); w; w = m_WidgetList.next()) { + if (w->getID() != id) + continue; + // if the containedWidget is not physically located inside this widget + if (widget->rect().contains(w->rect())) + continue; + // create the containment AssocWidget + AssociationWidget *a = new AssociationWidget(this, widget, + at_Containment, w); + a->calculateEndingPoints(); + a->setActivated(true); + if (! addAssociation(a)) + delete a; + } + } + } + // if the UMLCanvasObject has a parentPackage then + UMLPackage *parent = umlObj->getUMLPackage(); + if (parent == NULL) + return; + // if the parentPackage has a widget representation on this view then + Uml::IDType pkgID = parent->getID(); + UMLWidget *pWidget; + UMLWidgetListIt wit(m_WidgetList); + while ((pWidget = wit.current()) != NULL) { + ++wit; + if (pWidget->getID() == pkgID) + break; + } + if (pWidget == NULL || pWidget->rect().contains(widget->rect())) + return; + // create the containment AssocWidget + AssociationWidget *a = new AssociationWidget(this, pWidget, at_Containment, widget); + a->calculateEndingPoints(); + a->setActivated(true); + if (! addAssociation(a)) + delete a; +} + +void UMLView::createAutoAttributeAssociations(UMLWidget *widget) { + if (widget == NULL || m_Type != Uml::dt_Class) + return; + + // Pseudocode: + // if the underlying model object is really a UMLClassifier then + // for each of the UMLClassifier's UMLAttributes + // if the attribute type has a widget representation on this view then + // if the AssocWidget does not already exist then + // if the current diagram type permits compositions then + // create a composition AssocWidget + // end if + // end if + // end if + // if the attribute type is a Datatype then + // if the Datatype is a reference (pointer) type then + // if the referenced type has a widget representation on this view then + // if the AssocWidget does not already exist then + // if the current diagram type permits aggregations then + // create an aggregation AssocWidget from the ClassifierWidget to the + // widget of the referenced type + // end if + // end if + // end if + // end if + // end if + // end loop + // end if + // + // Implementation: + UMLObject *tmpUmlObj = widget->getUMLObject(); + if (tmpUmlObj == NULL) + return; + // if the underlying model object is really a UMLClassifier then + if (tmpUmlObj->getBaseType() == Uml::ot_Datatype) { + UMLClassifier *dt = static_cast(tmpUmlObj); + while (dt->originType() != NULL) { + tmpUmlObj = dt->originType(); + if (tmpUmlObj->getBaseType() != Uml::ot_Datatype) + break; + dt = static_cast(tmpUmlObj); + } + } + if (tmpUmlObj->getBaseType() != Uml::ot_Class) + return; + UMLClassifier * klass = static_cast(tmpUmlObj); + // for each of the UMLClassifier's UMLAttributes + UMLAttributeList attrList = klass->getAttributeList(); + for (UMLAttributeListIt ait(attrList); ait.current(); ++ait) { + UMLAttribute *attr = ait.current(); + createAutoAttributeAssociation(attr->getType(), attr, widget); + /* + * The following code from attachment 19935 of http://bugs.kde.org/140669 + * creates Aggregation/Composition to the template parameters. + * The current solution uses Dependency instead, see handling of template + * instantiation at Import_Utils::createUMLObject(). + UMLClassifierList templateList = attr->getTemplateParams(); + for (UMLClassifierListIt it(templateList); it.current(); ++it) { + createAutoAttributeAssociation(it,attr,widget); + } + */ + } +} + +void UMLView::createAutoAttributeAssociation(UMLClassifier *type, UMLAttribute *attr, + UMLWidget *widget /*, UMLClassifier * klass*/) { + if (type == NULL) { + // kDebug() << "UMLView::createAutoAttributeAssociations(" + // << klass->getName() << "): type is NULL for " + // << "attribute " << attr->getName() << endl; + return; + } + Uml::Association_Type assocType = Uml::at_Composition; + UMLWidget *w = findWidget( type->getID() ); + AssociationWidget *aw = NULL; + // if the attribute type has a widget representation on this view + if (w) { + aw = findAssocWidget(widget, w, attr->getName()); + if ( aw == NULL && + // if the current diagram type permits compositions + AssocRules::allowAssociation(assocType, widget, w, false) ) { + // Create a composition AssocWidget, or, if the attribute type is + // stereotyped <>, create a UniAssociation widget. + if (type->getStereotype() == "CORBAInterface") + assocType = at_UniAssociation; + AssociationWidget *a = new AssociationWidget (this, widget, assocType, w, attr); + a->calculateEndingPoints(); + a->setVisibility(attr->getVisibility(), B); + /* + if (assocType == at_Aggregation || assocType == at_UniAssociation) + a->setMulti("0..1", B); + */ + a->setRoleName(attr->getName(), B); + a->setActivated(true); + if (! addAssociation(a)) + delete a; + } + } + // if the attribute type is a Datatype then + if (type->getBaseType() == ot_Datatype) { + UMLClassifier *dt = static_cast(type); + // if the Datatype is a reference (pointer) type + if (dt->isReference()) { + //Uml::Association_Type assocType = Uml::at_Composition; + UMLClassifier *c = dt->originType(); + UMLWidget *w = c ? findWidget( c->getID() ) : 0; + // if the referenced type has a widget representation on this view + if (w) { + aw = findAssocWidget(widget, w, attr->getName()); + if (aw == NULL && + // if the current diagram type permits aggregations + AssocRules::allowAssociation(at_Aggregation, widget, w, false)) { + // create an aggregation AssocWidget from the ClassifierWidget + // to the widget of the referenced type + AssociationWidget *a = new AssociationWidget + (this, widget, at_Aggregation, w, attr); + a->calculateEndingPoints(); + a->setVisibility(attr->getVisibility(), B); + //a->setChangeability(true, B); + a->setMulti("0..1", B); + a->setRoleName(attr->getName(), B); + a->setActivated(true); + if (! addAssociation(a)) + delete a; + } + } + } + } +} + +void UMLView::findMaxBoundingRectangle(const FloatingTextWidget* ft, int& px, int& py, int& qx, int& qy) +{ + if (ft == NULL || !ft->isVisible()) + return; + + int x = ft -> getX(); + int y = ft -> getY(); + int x1 = x + ft -> getWidth() - 1; + int y1 = y + ft -> getHeight() - 1; + + if (px == -1 || x < px) + px = x; + if (py == -1 || y < py) + py = y; + if (qx == -1 || x1 > qx) + qx = x1; + if (qy == -1 || y1 > qy) + qy = y1; +} + +void UMLView::copyAsImage(QPixmap*& pix) { + //get the smallest rect holding the diagram + QRect rect = getDiagramRect(); + QPixmap diagram( rect.width(), rect.height() ); + + //only draw what is selected + m_bDrawSelectedOnly = true; + selectAssociations(true); + getDiagram(rect, diagram); + + //now get the selection cut + int px = -1, py = -1, qx = -1, qy = -1; + + //first get the smallest rect holding the widgets + for (UMLWidget* temp = m_SelectedList.first(); temp; temp = m_SelectedList.next()) { + int x = temp -> getX(); + int y = temp -> getY(); + int x1 = x + temp -> width() - 1; + int y1 = y + temp -> height() - 1; + if(px == -1 || x < px) { + px = x; + } + if(py == -1 || y < py) { + py = y; + } + if(qx == -1 || x1 > qx) { + qx = x1; + } + if(qy == -1 || y1 > qy) { + qy = y1; + } + } + + //also take into account any text lines in assocs or messages + AssociationWidget *a; + AssociationWidgetListIt assoc_it(m_AssociationList); + + //get each type of associations + //This needs to be reimplemented to increase the rectangle + //if a part of any association is not included + while ((a = assoc_it.current()) != NULL) { + ++assoc_it; + if (! a->getSelected()) + continue; + const FloatingTextWidget* multiA = const_cast(a->getMultiWidget(A)); + const FloatingTextWidget* multiB = const_cast(a->getMultiWidget(B)); + const FloatingTextWidget* roleA = const_cast(a->getRoleWidget(A)); + const FloatingTextWidget* roleB = const_cast(a->getRoleWidget(B)); + const FloatingTextWidget* changeA = const_cast(a->getChangeWidget(A)); + const FloatingTextWidget* changeB = const_cast(a->getChangeWidget(B)); + findMaxBoundingRectangle(multiA, px, py, qx, qy); + findMaxBoundingRectangle(multiB, px, py, qx, qy); + findMaxBoundingRectangle(roleA, px, py, qx, qy); + findMaxBoundingRectangle(roleB, px, py, qx, qy); + findMaxBoundingRectangle(changeA, px, py, qx, qy); + findMaxBoundingRectangle(changeB, px, py, qx, qy); + }//end while + + QRect imageRect; //area with respect to getDiagramRect() + //i.e. all widgets on the canvas. Was previously with + //respect to whole canvas + + imageRect.setLeft( px - rect.left() ); + imageRect.setTop( py - rect.top() ); + imageRect.setRight( qx - rect.left() ); + imageRect.setBottom( qy - rect.top() ); + + pix = new QPixmap(imageRect.width(), imageRect.height()); + bitBlt(pix, QPoint(0, 0), &diagram, imageRect); + m_bDrawSelectedOnly = false; +} + +void UMLView::setMenu() { + slotRemovePopupMenu(); + ListPopupMenu::Menu_Type menu = ListPopupMenu::mt_Undefined; + switch( getType() ) { + case dt_Class: + menu = ListPopupMenu::mt_On_Class_Diagram; + break; + + case dt_UseCase: + menu = ListPopupMenu::mt_On_UseCase_Diagram; + break; + + case dt_Sequence: + menu = ListPopupMenu::mt_On_Sequence_Diagram; + break; + + case dt_Collaboration: + menu = ListPopupMenu::mt_On_Collaboration_Diagram; + break; + + case dt_State: + menu = ListPopupMenu::mt_On_State_Diagram; + break; + + case dt_Activity: + menu = ListPopupMenu::mt_On_Activity_Diagram; + break; + + case dt_Component: + menu = ListPopupMenu::mt_On_Component_Diagram; + break; + + case dt_Deployment: + menu = ListPopupMenu::mt_On_Deployment_Diagram; + break; + + case dt_EntityRelationship: + menu = ListPopupMenu::mt_On_EntityRelationship_Diagram; + break; + + default: + kWarning() << "setMenu() called on unknown diagram type" << endl; + menu = ListPopupMenu::mt_Undefined; + break; + }//end switch + if( menu != ListPopupMenu::mt_Undefined ) { + m_pMenu = new ListPopupMenu(this, menu, this); + connect(m_pMenu, SIGNAL(activated(int)), this, SLOT(slotMenuSelection(int))); + m_pMenu->popup( mapToGlobal( contentsToViewport(worldMatrix().map(m_Pos)) ) ); + } +} + +void UMLView::slotRemovePopupMenu() { + if(m_pMenu) { + disconnect(m_pMenu, SIGNAL(activated(int)), this, SLOT(slotMenuSelection(int))); + delete m_pMenu; + m_pMenu = 0; + } +} + +void UMLView::slotMenuSelection(int sel) { + switch( (ListPopupMenu::Menu_Type)sel ) { + case ListPopupMenu::mt_Undo: + m_pDoc->loadUndoData(); + break; + + case ListPopupMenu::mt_Redo: + m_pDoc->loadRedoData(); + break; + + case ListPopupMenu::mt_Clear: + clearDiagram(); + break; + + case ListPopupMenu::mt_Export_Image: + m_pImageExporter->exportView(); + break; + + case ListPopupMenu::mt_FloatText: + { + FloatingTextWidget* ft = new FloatingTextWidget(this); + ft->changeTextDlg(); + //if no text entered delete + if(!FloatingTextWidget::isTextValid(ft->getText())) { + delete ft; + } else { + ft->setID(UniqueID::gen()); + setupNewWidget(ft); + } + } + break; + + case ListPopupMenu::mt_UseCase: + m_bCreateObject = true; + Object_Factory::createUMLObject( ot_UseCase ); + break; + + case ListPopupMenu::mt_Actor: + m_bCreateObject = true; + Object_Factory::createUMLObject( ot_Actor ); + break; + + case ListPopupMenu::mt_Class: + case ListPopupMenu::mt_Object: + m_bCreateObject = true; + Object_Factory::createUMLObject( ot_Class); + break; + + case ListPopupMenu::mt_Package: + m_bCreateObject = true; + Object_Factory::createUMLObject(ot_Package); + break; + + case ListPopupMenu::mt_Component: + m_bCreateObject = true; + Object_Factory::createUMLObject(ot_Component); + break; + + case ListPopupMenu::mt_Node: + m_bCreateObject = true; + Object_Factory::createUMLObject(ot_Node); + break; + + case ListPopupMenu::mt_Artifact: + m_bCreateObject = true; + Object_Factory::createUMLObject(ot_Artifact); + break; + + case ListPopupMenu::mt_Interface: + m_bCreateObject = true; + Object_Factory::createUMLObject(ot_Interface); + break; + + case ListPopupMenu::mt_Enum: + m_bCreateObject = true; + Object_Factory::createUMLObject(ot_Enum); + break; + + case ListPopupMenu::mt_Entity: + m_bCreateObject = true; + Object_Factory::createUMLObject(ot_Entity); + break; + + case ListPopupMenu::mt_Datatype: + m_bCreateObject = true; + Object_Factory::createUMLObject(ot_Datatype); + break; + + case ListPopupMenu::mt_Cut: + //FIXME make this work for diagram's right click menu + if ( m_SelectedList.count() && + UMLApp::app()->editCutCopy(true) ) { + deleteSelection(); + m_pDoc->setModified(true); + } + break; + + case ListPopupMenu::mt_Copy: + //FIXME make this work for diagram's right click menu + m_SelectedList.count() && UMLApp::app()->editCutCopy(true); + break; + + case ListPopupMenu::mt_Paste: + m_PastePoint = m_Pos; + m_Pos.setX( 2000 ); + m_Pos.setY( 2000 ); + UMLApp::app()->slotEditPaste(); + + m_PastePoint.setX( 0 ); + m_PastePoint.setY( 0 ); + break; + + case ListPopupMenu::mt_Initial_State: + { + StateWidget* state = new StateWidget( this, StateWidget::Initial ); + setupNewWidget( state ); + } + break; + + case ListPopupMenu::mt_End_State: + { + StateWidget* state = new StateWidget( this, StateWidget::End ); + setupNewWidget( state ); + } + break; + + case ListPopupMenu::mt_State: + { + bool ok = false; + QString name = KInputDialog::getText( i18n("Enter State Name"), + i18n("Enter the name of the new state:"), + i18n("new state"), &ok, UMLApp::app() ); + if ( ok ) { + StateWidget* state = new StateWidget( this ); + state->setName( name ); + setupNewWidget( state ); + } + } + break; + + case ListPopupMenu::mt_Initial_Activity: + { + ActivityWidget* activity = new ActivityWidget( this, ActivityWidget::Initial ); + setupNewWidget(activity); + } + break; + + + case ListPopupMenu::mt_End_Activity: + { + ActivityWidget* activity = new ActivityWidget( this, ActivityWidget::End ); + setupNewWidget(activity); + } + break; + + case ListPopupMenu::mt_Branch: + { + ActivityWidget* activity = new ActivityWidget( this, ActivityWidget::Branch ); + setupNewWidget(activity); + } + break; + + case ListPopupMenu::mt_Activity: + { + bool ok = false; + QString name = KInputDialog::getText( i18n("Enter Activity Name"), + i18n("Enter the name of the new activity:"), + i18n("new activity"), &ok, UMLApp::app() ); + if ( ok ) { + ActivityWidget* activity = new ActivityWidget( this, ActivityWidget::Normal ); + activity->setName( name ); + setupNewWidget(activity); + } + } + break; + + case ListPopupMenu::mt_SnapToGrid: + toggleSnapToGrid(); + m_pDoc->setModified(); + break; + + case ListPopupMenu::mt_ShowSnapGrid: + toggleShowGrid(); + m_pDoc->setModified(); + break; + + case ListPopupMenu::mt_Properties: + if (showPropDialog() == true) + m_pDoc->setModified(); + break; + + case ListPopupMenu::mt_Delete: + m_pDoc->removeDiagram( getID() ); + break; + + case ListPopupMenu::mt_Rename: + { + bool ok = false; + QString name = KInputDialog::getText( i18n("Enter Diagram Name"), + i18n("Enter the new name of the diagram:"), + getName(), &ok, UMLApp::app() ); + if (ok) { + setName(name); + m_pDoc->signalDiagramRenamed(this); + } + } + break; + + default: + break; + } +} + +void UMLView::slotCutSuccessful() { + if( m_bStartedCut ) { + deleteSelection(); + m_bStartedCut = false; + } +} + +void UMLView::slotShowView() { + m_pDoc -> changeCurrentView( getID() ); +} + +QPoint UMLView::getPastePoint() { + QPoint point = m_PastePoint; + point.setX( point.x() - m_Pos.x() ); + point.setY( point.y() - m_Pos.y() ); + return point; +} + +void UMLView::resetPastePoint() { + m_PastePoint = m_Pos; +} + +int UMLView::snappedX (int x) { + if (getSnapToGrid()) { + int gridX = getSnapX(); + int modX = x % gridX; + x -= modX; + if (modX >= gridX / 2) + x += gridX; + } + return x; +} + +int UMLView::snappedY (int y) { + if (getSnapToGrid()) { + int gridY = getSnapY(); + int modY = y % gridY; + y -= modY; + if (modY >= gridY / 2) + y += gridY; + } + return y; +} + +bool UMLView::showPropDialog() { + UMLViewDialog dlg( this, this ); + if( dlg.exec() ) { + return true; + } + return false; +} + + +QFont UMLView::getFont() const { + return m_Options.uiState.font; +} + +void UMLView::setFont(QFont font, bool changeAllWidgets /* = false */) { + m_Options.uiState.font = font; + if (!changeAllWidgets) + return; + for (UMLWidgetListIt wit(m_WidgetList); wit.current(); ++wit) { + UMLWidget *w = wit.current(); + w->setFont(font); + } +} + +void UMLView::setClassWidgetOptions( ClassOptionsPage * page ) { + UMLWidget * pWidget = 0; + UMLWidgetListIt wit( m_WidgetList ); + while ( (pWidget = wit.current()) != 0 ) { + ++wit; + Uml::Widget_Type wt = pWidget->getBaseType(); + if (wt == Uml::wt_Class || wt == Uml::wt_Interface) { + page -> setWidget( static_cast(pWidget) ); + page -> updateUMLWidget(); + } + } +} + + +void UMLView::checkSelections() { + UMLWidget * pWA = 0, * pWB = 0, * pTemp = 0; + //check messages + for(pTemp=(UMLWidget *)m_SelectedList.first();pTemp;pTemp=(UMLWidget *)m_SelectedList.next()) { + if( pTemp->getBaseType() == wt_Message && pTemp -> getSelected() ) { + MessageWidget * pMessage = static_cast( pTemp ); + pWA = pMessage -> getWidget(A); + pWB = pMessage -> getWidget(B); + if( !pWA -> getSelected() ) { + pWA -> setSelectedFlag( true ); + m_SelectedList.append( pWA ); + } + if( !pWB -> getSelected() ) { + pWB -> setSelectedFlag( true ); + m_SelectedList.append( pWB ); + } + }//end if + }//end for + //check Associations + AssociationWidgetListIt it(m_AssociationList); + AssociationWidget * pAssoc = 0; + while((pAssoc = it.current())) { + ++it; + if( pAssoc -> getSelected() ) { + pWA = pAssoc -> getWidget(A); + pWB = pAssoc -> getWidget(B); + if( !pWA -> getSelected() ) { + pWA -> setSelectedFlag( true ); + m_SelectedList.append( pWA ); + } + if( !pWB -> getSelected() ) { + pWB -> setSelectedFlag( true ); + m_SelectedList.append( pWB ); + } + }//end if + }//end while +} + +bool UMLView::checkUniqueSelection() +{ + // if there are no selected items, we return true + if (m_SelectedList.count() <= 0) + return true; + + // get the first item and its base type + UMLWidget * pTemp = (UMLWidget *) m_SelectedList.first(); + Widget_Type tmpType = pTemp -> getBaseType(); + + // check all selected items, if they have the same BaseType + for ( pTemp = (UMLWidget *) m_SelectedList.first(); + pTemp; + pTemp = (UMLWidget *) m_SelectedList.next() ) { + if( pTemp->getBaseType() != tmpType) + { + return false; // the base types are different, the list is not unique + } + } // for ( through all selected items ) + + return true; // selected items are unique +} + +void UMLView::clearDiagram() { + if( KMessageBox::Continue == KMessageBox::warningContinueCancel( this, i18n("You are about to delete " + "the entire diagram.\nAre you sure?"), + i18n("Delete Diagram?"),KGuiItem( i18n("&Delete"), "editdelete") ) ) { + removeAllWidgets(); + } +} + +void UMLView::toggleSnapToGrid() { + setSnapToGrid( !getSnapToGrid() ); +} + +void UMLView::toggleSnapComponentSizeToGrid() { + setSnapComponentSizeToGrid( !getSnapComponentSizeToGrid() ); +} + +void UMLView::toggleShowGrid() { + setShowSnapGrid( !getShowSnapGrid() ); +} + +void UMLView::setSnapToGrid(bool bSnap) { + m_bUseSnapToGrid = bSnap; + emit sigSnapToGridToggled( getSnapToGrid() ); +} + +void UMLView::setSnapComponentSizeToGrid(bool bSnap) { + m_bUseSnapComponentSizeToGrid = bSnap; + updateComponentSizes(); + emit sigSnapComponentSizeToGridToggled( getSnapComponentSizeToGrid() ); +} + +bool UMLView::getShowSnapGrid() const { + return m_bShowSnapGrid; +} + +void UMLView::setShowSnapGrid(bool bShow) { + m_bShowSnapGrid = bShow; + canvas()->setAllChanged(); + emit sigShowGridToggled( getShowSnapGrid() ); +} + +bool UMLView::getShowOpSig() const { + return m_Options.classState.showOpSig; +} + +void UMLView::setShowOpSig(bool bShowOpSig) { + m_Options.classState.showOpSig = bShowOpSig; +} + +void UMLView::setZoom(int zoom) { + if (zoom < 10) { + zoom = 10; + } else if (zoom > 500) { + zoom = 500; + } + + QWMatrix wm; + wm.scale(zoom/100.0,zoom/100.0); + setWorldMatrix(wm); + + m_nZoom = currentZoom(); + resizeCanvasToItems(); +} + +int UMLView::currentZoom() { + return (int)(worldMatrix().m11()*100.0); +} + +void UMLView::zoomIn() { + QWMatrix wm = worldMatrix(); + wm.scale(1.5,1.5); // adjust zooming step here + setZoom( (int)(wm.m11()*100.0) ); +} + +void UMLView::zoomOut() { + QWMatrix wm = worldMatrix(); + wm.scale(2.0/3.0, 2.0/3.0); //adjust zooming step here + setZoom( (int)(wm.m11()*100.0) ); +} + +void UMLView::fileLoaded() { + setZoom( getZoom() ); + resizeCanvasToItems(); +} + +void UMLView::setCanvasSize(int width, int height) { + setCanvasWidth(width); + setCanvasHeight(height); + canvas()->resize(width, height); +} + +void UMLView::resizeCanvasToItems() { + QRect canvasSize = getDiagramRect(); + int canvasWidth = canvasSize.right() + 5; + int canvasHeight = canvasSize.bottom() + 5; + + //Find out the bottom right visible pixel and size to at least that + int contentsX, contentsY; + int contentsWMX, contentsWMY; + viewportToContents(viewport()->width(), viewport()->height(), contentsX, contentsY); + inverseWorldMatrix().map(contentsX, contentsY, &contentsWMX, &contentsWMY); + + if (canvasWidth < contentsWMX) { + canvasWidth = contentsWMX; + } + + if (canvasHeight < contentsWMY) { + canvasHeight = contentsWMY; + } + + setCanvasSize(canvasWidth, canvasHeight); +} + +void UMLView::show() { + QWidget::show(); + resizeCanvasToItems(); +} + +void UMLView::updateComponentSizes() { + // update sizes of all components + UMLWidgetListIt it( m_WidgetList ); + UMLWidget *obj; + while ( (obj=(UMLWidget*)it.current()) != 0 ) { + ++it; + obj->updateComponentSize(); + } +} + +/** + * Force the widget font metrics to be updated next time + * the widgets are drawn. + * This is necessary because the widget size might depend on the + * font metrics and the font metrics might change for different + * QPainter, i.e. font metrics for Display font and Printer font are + * usually different. + * Call this when you change the QPainter. + */ +void UMLView::forceUpdateWidgetFontMetrics(QPainter * painter) { + UMLWidgetListIt it( m_WidgetList ); + UMLWidget *obj; + + while ((obj = it.current()) != 0 ) { + ++it; + obj->forceUpdateFontMetrics(painter); + } +} + +void UMLView::saveToXMI( QDomDocument & qDoc, QDomElement & qElement ) { + QDomElement viewElement = qDoc.createElement( "diagram" ); + viewElement.setAttribute( "xmi.id", ID2STR(m_nID) ); + viewElement.setAttribute( "name", getName() ); + viewElement.setAttribute( "type", m_Type ); + viewElement.setAttribute( "documentation", m_Documentation ); + //optionstate uistate + viewElement.setAttribute( "fillcolor", m_Options.uiState.fillColor.name() ); + viewElement.setAttribute( "linecolor", m_Options.uiState.lineColor.name() ); + viewElement.setAttribute( "linewidth", m_Options.uiState.lineWidth ); + viewElement.setAttribute( "usefillcolor", m_Options.uiState.useFillColor ); + viewElement.setAttribute( "font", m_Options.uiState.font.toString() ); + //optionstate classstate + viewElement.setAttribute( "showattsig", m_Options.classState.showAttSig ); + viewElement.setAttribute( "showatts", m_Options.classState.showAtts); + viewElement.setAttribute( "showopsig", m_Options.classState.showOpSig ); + viewElement.setAttribute( "showops", m_Options.classState.showOps ); + viewElement.setAttribute( "showpackage", m_Options.classState.showPackage ); + viewElement.setAttribute( "showscope", m_Options.classState.showVisibility ); + viewElement.setAttribute( "showstereotype", m_Options.classState.showStereoType ); + //misc + viewElement.setAttribute( "localid", ID2STR(m_nLocalID) ); + viewElement.setAttribute( "showgrid", m_bShowSnapGrid ); + viewElement.setAttribute( "snapgrid", m_bUseSnapToGrid ); + viewElement.setAttribute( "snapcsgrid", m_bUseSnapComponentSizeToGrid ); + viewElement.setAttribute( "snapx", m_nSnapX ); + viewElement.setAttribute( "snapy", m_nSnapY ); + viewElement.setAttribute( "zoom", m_nZoom ); + viewElement.setAttribute( "canvasheight", m_nCanvasHeight ); + viewElement.setAttribute( "canvaswidth", m_nCanvasWidth ); + //now save all the widgets + UMLWidget * widget = 0; + UMLWidgetListIt w_it( m_WidgetList ); + QDomElement widgetElement = qDoc.createElement( "widgets" ); + while( ( widget = w_it.current() ) ) { + ++w_it; + // Having an exception is bad I know, but gotta work with + // system we are given. + // We DONT want to record any text widgets which are belonging + // to associations as they are recorded later in the "associations" + // section when each owning association is dumped. -b.t. + if (widget->getBaseType() != wt_Text || + static_cast(widget)->getLink() == NULL) + widget->saveToXMI( qDoc, widgetElement ); + } + viewElement.appendChild( widgetElement ); + //now save the message widgets + MessageWidgetListIt m_it( m_MessageList ); + QDomElement messageElement = qDoc.createElement( "messages" ); + while( ( widget = m_it.current() ) ) { + ++m_it; + widget -> saveToXMI( qDoc, messageElement ); + } + viewElement.appendChild( messageElement ); + //now save the associations + QDomElement assocElement = qDoc.createElement( "associations" ); + if ( m_AssociationList.count() ) { + // We guard against ( m_AssociationList.count() == 0 ) because + // this code could be reached as follows: + // ^ UMLView::saveToXMI() + // ^ UMLDoc::saveToXMI() + // ^ UMLDoc::addToUndoStack() + // ^ UMLDoc::setModified() + // ^ UMLDoc::createDiagram() + // ^ UMLDoc::newDocument() + // ^ UMLApp::newDocument() + // ^ main() + // + AssociationWidgetListIt a_it( m_AssociationList ); + AssociationWidget * assoc = 0; + while( ( assoc = a_it.current() ) ) { + ++a_it; + assoc -> saveToXMI( qDoc, assocElement ); + } + // kDebug() << "UMLView::saveToXMI() saved " + // << m_AssociationList.count() << " assocData." << endl; + } + viewElement.appendChild( assocElement ); + qElement.appendChild( viewElement ); +} + +bool UMLView::loadFromXMI( QDomElement & qElement ) { + QString id = qElement.attribute( "xmi.id", "-1" ); + m_nID = STR2ID(id); + if( m_nID == Uml::id_None ) + return false; + setName( qElement.attribute( "name", "" ) ); + QString type = qElement.attribute( "type", "0" ); + m_Documentation = qElement.attribute( "documentation", "" ); + QString localid = qElement.attribute( "localid", "0" ); + //optionstate uistate + QString font = qElement.attribute( "font", "" ); + if (!font.isEmpty()) { + m_Options.uiState.font.fromString( font ); + m_Options.uiState.font.setUnderline(false); + } + QString fillcolor = qElement.attribute( "fillcolor", "" ); + QString linecolor = qElement.attribute( "linecolor", "" ); + QString linewidth = qElement.attribute( "linewidth", "" ); + QString usefillcolor = qElement.attribute( "usefillcolor", "0" ); + m_Options.uiState.useFillColor = (bool)usefillcolor.toInt(); + //optionstate classstate + QString temp = qElement.attribute( "showattsig", "0" ); + m_Options.classState.showAttSig = (bool)temp.toInt(); + temp = qElement.attribute( "showatts", "0" ); + m_Options.classState.showAtts = (bool)temp.toInt(); + temp = qElement.attribute( "showopsig", "0" ); + m_Options.classState.showOpSig = (bool)temp.toInt(); + temp = qElement.attribute( "showops", "0" ); + m_Options.classState.showOps = (bool)temp.toInt(); + temp = qElement.attribute( "showpackage", "0" ); + m_Options.classState.showPackage = (bool)temp.toInt(); + temp = qElement.attribute( "showscope", "0" ); + m_Options.classState.showVisibility = (bool)temp.toInt(); + temp = qElement.attribute( "showstereotype", "0" ); + m_Options.classState.showStereoType = (bool)temp.toInt(); + //misc + QString showgrid = qElement.attribute( "showgrid", "0" ); + m_bShowSnapGrid = (bool)showgrid.toInt(); + + QString snapgrid = qElement.attribute( "snapgrid", "0" ); + m_bUseSnapToGrid = (bool)snapgrid.toInt(); + + QString snapcsgrid = qElement.attribute( "snapcsgrid", "0" ); + m_bUseSnapComponentSizeToGrid = (bool)snapcsgrid.toInt(); + + QString snapx = qElement.attribute( "snapx", "10" ); + m_nSnapX = snapx.toInt(); + + QString snapy = qElement.attribute( "snapy", "10" ); + m_nSnapY = snapy.toInt(); + + QString zoom = qElement.attribute( "zoom", "100" ); + m_nZoom = zoom.toInt(); + + QString height = qElement.attribute( "canvasheight", QString("%1").arg(UMLView::defaultCanvasSize) ); + m_nCanvasHeight = height.toInt(); + + QString width = qElement.attribute( "canvaswidth", QString("%1").arg(UMLView::defaultCanvasSize) ); + m_nCanvasWidth = width.toInt(); + + int nType = type.toInt(); + if (nType == -1 || nType >= 400) { + // Pre 1.5.5 numeric values + // Values of "type" were changed in 1.5.5 to merge with Settings::Diagram + switch (nType) { + case 400: + m_Type = Uml::dt_UseCase; + break; + case 401: + m_Type = Uml::dt_Collaboration; + break; + case 402: + m_Type = Uml::dt_Class; + break; + case 403: + m_Type = Uml::dt_Sequence; + break; + case 404: + m_Type = Uml::dt_State; + break; + case 405: + m_Type = Uml::dt_Activity; + break; + case 406: + m_Type = Uml::dt_Component; + break; + case 407: + m_Type = Uml::dt_Deployment; + break; + case 408: + m_Type = Uml::dt_EntityRelationship; + break; + default: + m_Type = Uml::dt_Undefined; + break; + } + } else { + m_Type = (Uml::Diagram_Type)nType; + } + if( !fillcolor.isEmpty() ) + m_Options.uiState.fillColor = QColor( fillcolor ); + if( !linecolor.isEmpty() ) + m_Options.uiState.lineColor = QColor( linecolor ); + if( !linewidth.isEmpty() ) + m_Options.uiState.lineWidth = linewidth.toInt(); + m_nLocalID = STR2ID(localid); + + QDomNode node = qElement.firstChild(); + bool widgetsLoaded = false, messagesLoaded = false, associationsLoaded = false; + while (!node.isNull()) { + QDomElement element = node.toElement(); + if (!element.isNull()) { + if (element.tagName() == "widgets") + widgetsLoaded = loadWidgetsFromXMI( element ); + else if (element.tagName() == "messages") + messagesLoaded = loadMessagesFromXMI( element ); + else if (element.tagName() == "associations") + associationsLoaded = loadAssociationsFromXMI( element ); + } + node = node.nextSibling(); + } + + if (!widgetsLoaded) { + kWarning() << "failed umlview load on widgets" << endl; + return false; + } + if (!messagesLoaded) { + kWarning() << "failed umlview load on messages" << endl; + return false; + } + if (!associationsLoaded) { + kWarning() << "failed umlview load on associations" << endl; + return false; + } + return true; +} + +bool UMLView::loadWidgetsFromXMI( QDomElement & qElement ) { + UMLWidget* widget = 0; + QDomNode node = qElement.firstChild(); + QDomElement widgetElement = node.toElement(); + while( !widgetElement.isNull() ) { + widget = loadWidgetFromXMI(widgetElement); + if (widget) { + m_WidgetList.append( widget ); + // In the interest of best-effort loading, in case of a + // (widget == NULL) we still go on. + // The individual widget's loadFromXMI method should + // already have generated an error message to tell the + // user that something went wrong. + } + node = widgetElement.nextSibling(); + widgetElement = node.toElement(); + } + + return true; +} + +UMLWidget* UMLView::loadWidgetFromXMI(QDomElement& widgetElement) { + + if ( !m_pDoc ) { + kWarning() << "UMLView::loadWidgetFromXMI(): m_pDoc is NULL" << endl; + return 0L; + } + + QString tag = widgetElement.tagName(); + QString idstr = widgetElement.attribute( "xmi.id", "-1" ); + UMLWidget* widget = Widget_Factory::makeWidgetFromXMI(tag, idstr, this); + if (widget == NULL) + return NULL; + if (!widget->loadFromXMI(widgetElement)) { + widget->cleanup(); + delete widget; + return 0; + } + return widget; +} + +bool UMLView::loadMessagesFromXMI( QDomElement & qElement ) { + MessageWidget * message = 0; + QDomNode node = qElement.firstChild(); + QDomElement messageElement = node.toElement(); + while( !messageElement.isNull() ) { + QString tag = messageElement.tagName(); + if (tag == "messagewidget" || + tag == "UML:MessageWidget" ) { // for bkwd compatibility + message = new MessageWidget(this, sequence_message_asynchronous, + Uml::id_Reserved); + if( !message -> loadFromXMI( messageElement ) ) { + delete message; + return false; + } + m_MessageList.append( message ); + FloatingTextWidget *ft = message->getFloatingTextWidget(); + if (ft) + m_WidgetList.append( ft ); + else if (message->getSequenceMessageType() != sequence_message_creation) + kDebug() << "UMLView::loadMessagesFromXMI: ft is NULL" + << " for message " << ID2STR(message->getID()) << endl; + } + node = messageElement.nextSibling(); + messageElement = node.toElement(); + } + return true; +} + +bool UMLView::loadAssociationsFromXMI( QDomElement & qElement ) { + QDomNode node = qElement.firstChild(); + QDomElement assocElement = node.toElement(); + int countr = 0; + while( !assocElement.isNull() ) { + QString tag = assocElement.tagName(); + if (tag == "assocwidget" || + tag == "UML:AssocWidget") { // for bkwd compatibility + countr++; + AssociationWidget *assoc = new AssociationWidget(this); + if( !assoc->loadFromXMI( assocElement ) ) { + kError() << "couldn't loadFromXMI association widget:" + << assoc << ", bad XMI file? Deleting from umlview." + << endl; + delete assoc; + /* return false; + Returning false here is a little harsh when the + rest of the diagram might load okay. + */ + } else { + if(!addAssociation(assoc, false)) + { + kError()<<"Couldnt addAssociation("<cleanup(); + delete assoc; + //return false; // soften error.. may not be that bad + } + } + } + node = assocElement.nextSibling(); + assocElement = node.toElement(); + } + return true; +} + +void UMLView::addObject(UMLObject *object) +{ + m_bCreateObject = true; + if (m_pDoc->addUMLObject(object)) + m_pDoc->signalUMLObjectCreated(object); // m_bCreateObject is reset by slotObjectCreated() + else + m_bCreateObject = false; +} + +bool UMLView::loadUisDiagramPresentation(QDomElement & qElement) { + for (QDomNode node = qElement.firstChild(); !node.isNull(); node = node.nextSibling()) { + QDomElement elem = node.toElement(); + QString tag = elem.tagName(); + if (! Uml::tagEq(tag, "Presentation")) { + kError() << "ignoring unknown UisDiagramPresentation tag " + << tag << endl; + continue; + } + QDomNode n = elem.firstChild(); + QDomElement e = n.toElement(); + QString idStr; + int x = 0, y = 0, w = 0, h = 0; + while (!e.isNull()) { + tag = e.tagName(); + kDebug() << "Presentation: tag = " << tag << endl; + if (Uml::tagEq(tag, "Presentation.geometry")) { + QDomNode gnode = e.firstChild(); + QDomElement gelem = gnode.toElement(); + QString csv = gelem.text(); + QStringList dim = QStringList::split(",", csv); + x = dim[0].toInt(); + y = dim[1].toInt(); + w = dim[2].toInt(); + h = dim[3].toInt(); + } else if (Uml::tagEq(tag, "Presentation.style")) { + // TBD + } else if (Uml::tagEq(tag, "Presentation.model")) { + QDomNode mnode = e.firstChild(); + QDomElement melem = mnode.toElement(); + idStr = melem.attribute("xmi.idref", ""); + } else { + kDebug() << "UMLView::uisLoadFromXMI: ignoring tag " + << tag << endl; + } + n = n.nextSibling(); + e = n.toElement(); + } + Uml::IDType id = STR2ID(idStr); + UMLObject *o = m_pDoc->findObjectById(id); + if (o == NULL) { + kError() << "UMLView::uisLoadFromXMI: Cannot find object for id " + << idStr << endl; + } else { + Uml::Object_Type ot = o->getBaseType(); + kDebug() << "Create widget for model object of type " << ot << endl; + UMLWidget *widget = NULL; + switch (ot) { + case Uml::ot_Class: + widget = new ClassifierWidget(this, static_cast(o)); + break; + case Uml::ot_Association: + { + UMLAssociation *umla = static_cast(o); + Uml::Association_Type at = umla->getAssocType(); + UMLObject* objA = umla->getObject(Uml::A); + UMLObject* objB = umla->getObject(Uml::B); + if (objA == NULL || objB == NULL) { + kError() << "intern err 1" << endl; + return false; + } + UMLWidget *wA = findWidget(objA->getID()); + UMLWidget *wB = findWidget(objB->getID()); + if (wA != NULL && wB != NULL) { + AssociationWidget *aw = + new AssociationWidget(this, wA, at, wB, umla); + aw->syncToModel(); + m_AssociationList.append(aw); + } else { + kError() << "cannot create assocwidget from (" + << wA << ", " << wB << ")" << endl; + } + break; + } + case Uml::ot_Role: + { + UMLRole *robj = static_cast(o); + UMLAssociation *umla = robj->getParentAssociation(); + // @todo properly display role names. + // For now, in order to get the role names displayed + // simply delete the participating diagram objects + // and drag them from the list view to the diagram. + break; + } + default: + kError() << "UMLView::uisLoadFromXMI: " + << "Cannot create widget of type " + << ot << endl; + } + if (widget) { + kDebug() << "Widget: x=" << x << ", y=" << y + << ", w=" << w << ", h=" << h << endl; + widget->setX(x); + widget->setY(y); + widget->setSize(w, h); + m_WidgetList.append(widget); + } + } + } + return true; +} + +bool UMLView::loadUISDiagram(QDomElement & qElement) { + QString idStr = qElement.attribute( "xmi.id", "" ); + if (idStr.isEmpty()) + return false; + m_nID = STR2ID(idStr); + UMLListViewItem *ulvi = NULL; + for (QDomNode node = qElement.firstChild(); !node.isNull(); node = node.nextSibling()) { + if (node.isComment()) + continue; + QDomElement elem = node.toElement(); + QString tag = elem.tagName(); + if (tag == "uisDiagramName") { + setName( elem.text() ); + if (ulvi) + ulvi->setText( getName() ); + } else if (tag == "uisDiagramStyle") { + QString diagramStyle = elem.text(); + if (diagramStyle != "ClassDiagram") { + kError() << "UMLView::uisLoadFromXMI: diagram style " << diagramStyle + << " is not yet implemented" << endl; + continue; + } + m_pDoc->setMainViewID(m_nID); + m_Type = Uml::dt_Class; + UMLListView *lv = UMLApp::app()->getListView(); + ulvi = new UMLListViewItem( lv->theLogicalView(), getName(), + Uml::lvt_Class_Diagram, m_nID ); + } else if (tag == "uisDiagramPresentation") { + loadUisDiagramPresentation(elem); + } else if (tag != "uisToolName") { + kDebug() << "UMLView::uisLoadFromXMI: ignoring tag " << tag << endl; + } + } + return true; +} + + +#include "umlview.moc" diff --git a/umbrello/umbrello/umlview.h b/umbrello/umbrello/umlview.h new file mode 100644 index 00000000..99a39198 --- /dev/null +++ b/umbrello/umbrello/umlview.h @@ -0,0 +1,1255 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLVIEW_H +#define UMLVIEW_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +// system includes +#include +#include +#include + +//local includes +#include "umlobjectlist.h" +#include "umlwidgetlist.h" +#include "associationwidgetlist.h" +#include "messagewidgetlist.h" +#include "optionstate.h" +#include "worktoolbar.h" + +// forward declarations +class ClassOptionsPage; +class IDChangeLog; +class ListPopupMenu; +class FloatingTextWidget; +class ObjectWidget; +class UMLFolder; +class UMLApp; +class UMLDoc; +class UMLAttribute; +class UMLCanvasObject; +class UMLClassifier; +class UMLViewImageExporter; + +class KPrinter; +class ToolBarState; +class ToolBarStateFactory; + +/** + * UMLView instances represent diagrams. + * The UMLApp instance manages a QWidgetStack of UMLView instances. + * The visible diagram is at the top of stack. + * The UMLView class inherits from QCanvasView and it owns the + * objects displayed on its related QCanvas (see m_WidgetList.) + * + * @author Paul Hensgen + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class UMLView : public QCanvasView { + Q_OBJECT +public: + friend class UMLViewImageExporterModel; + + /** + * Constructor + */ + UMLView(UMLFolder *parentFolder); + + /** + * Destructor + */ + virtual ~UMLView(); + + // Accessors and other methods dealing with loaded/saved data + + /** + * Return the UMLFolder in which this diagram lives. + */ + UMLFolder *getFolder() { + return m_pFolder; + } + + /** + * Set the UMLFolder in which this diagram lives. + */ + void setFolder(UMLFolder *folder) { + m_pFolder = folder; + } + + /** + * Return the documentation of the diagram. + */ + QString getDoc() const { + return m_Documentation; + } + + /** + * Set the documentation of the diagram. + */ + void setDoc( const QString &doc ) { + m_Documentation = doc; + } + + /** + * Return the name of the diagram. + */ + QString getName() const; + + /** + * Set the name of the diagram. + */ + void setName(const QString &name); + + /** + * Returns the type of the diagram. + */ + Uml::Diagram_Type getType() const { + return m_Type; + } + + /** + * Set the type of diagram. + */ + void setType( Uml::Diagram_Type type ) { + m_Type = type; + } + + /** + * Returns the fill color to use. + */ + QColor getFillColor() const; + + /** + * Set the background color. + * + * @param color The color to use. + */ + void setFillColor( const QColor &color ); + + /** + * Returns the line color to use. + */ + QColor getLineColor() const; + + /** + * Sets the line color. + * + * @param color The color to use. + */ + void setLineColor( const QColor &color ); + + /** + * Returns the line width to use. + */ + uint getLineWidth() const; + + /** + * Sets the line width. + * + * @param width The width to use. + */ + void setLineWidth( uint width ); + + /** + * Returns the ID of the diagram. + */ + Uml::IDType getID() const { + return m_nID; + } + + /** + * Sets the ID of the diagram. + */ + void setID( Uml::IDType id ) { + m_nID = id; + } + + /** + * Returns the zoom of the diagram. + */ + int getZoom() const { + return m_nZoom; + } + + /** + * Sets the zoom of the diagram. + */ + void setZoom(int zoom); + + /** + * Returns the height of the diagram. + */ + int getCanvasHeight() const { + return m_nCanvasHeight; + } + + /** + * Sets the height of the diagram. + */ + void setCanvasHeight(int height) { + m_nCanvasHeight = height; + } + + /** + * Returns the width of the diagram. + */ + int getCanvasWidth() const { + return m_nCanvasWidth; + } + + /** + * Sets the height of the diagram. + */ + void setCanvasWidth(int width) { + m_nCanvasWidth = width; + } + + /** + * Return whether to use snap to grid. + */ + bool getSnapToGrid() const { + return m_bUseSnapToGrid; + } + + /** + * Sets whether to snap to grid. + */ + void setSnapToGrid( bool bSnap ); + + /** + * Return whether to use snap to grid for component size. + */ + bool getSnapComponentSizeToGrid() const { + return m_bUseSnapComponentSizeToGrid; + } + + /** + * Returns the x grid size. + */ + int getSnapX() const { + return m_nSnapX; + } + + /** + * Returns the y grid size. + */ + int getSnapY() const { + return m_nSnapY; + } + + /** + * Returns the input coordinate with possible grid-snap applied. + */ + int snappedX(int x); + + /** + * Returns the input coordinate with possible grid-snap applied. + */ + int snappedY(int y); + + /** + * Returns whether to show snap grid or not. + */ + bool getShowSnapGrid() const; + + /** + * Sets whether to show snap grid. + */ + void setShowSnapGrid( bool bShow ); + + /** + * Sets whether to snap to grid for component size. + */ + void setSnapComponentSizeToGrid( bool bSnap ); + + /** + * Returns whether to use the fill/background color + */ + bool getUseFillColor() const; + + /** + * Sets whether to use the fill/background color + */ + void setUseFillColor(bool ufc); + + /** + * Returns the font to use + */ + QFont getFont() const; + + /** + * Sets the font for the view and optionally all the widgets on the view. + */ + void setFont(QFont font, bool changeAllWidgets = false); + + /** + * Returns whether to show operation signatures. + */ + bool getShowOpSig() const; + + /** + * Sets whether to show operation signatures. + */ + void setShowOpSig(bool bShowOpSig); + + /** + * Returns the options being used. + */ + const Settings::OptionState& getOptionState() const { + return m_Options; + } + + /** + * Sets the options to be used. + */ + void setOptionState( const Settings::OptionState& options) { + m_Options = options; + } + + /** + * Returns a reference to the association list. + */ + AssociationWidgetList& getAssociationList() { + return m_AssociationList; + } + + /** + * Returns a reference to the widget list. + */ + UMLWidgetList& getWidgetList() { + return m_WidgetList; + } + + /** + * Returns a reference to the message list. + */ + MessageWidgetList& getMessageList() { + return m_MessageList; + } + + // End of accessors and methods that only deal with loaded/saved data + //////////////////////////////////////////////////////////////////////// + + /** + * return the current zoom factor + */ + int currentZoom(); + + /** + * contains the implementation for printing functionality + */ + void print(KPrinter *pPrinter, QPainter & pPainter); + + /** + * Overrides the standard operation. + */ + void hideEvent(QHideEvent *he); + + /** + * Overrides the standard operation. + */ + void showEvent(QShowEvent *se); + + /** + * Sees if a message is relevant to the given widget. If it does delete it. + * @param w The widget to check messages against. + */ + void checkMessages(ObjectWidget * w); + + /** + * Finds a widget with the given ID. + * + * @param id The ID of the widget to find. + * + * @return Returns the widget found, returns 0 if no widget found. + */ + UMLWidget * findWidget(Uml::IDType id); + + /** + * Finds an association widget with the given ID. + * + * @param id The ID of the widget to find. + * + * @return Returns the widget found, returns 0 if no widget found. + */ + AssociationWidget * findAssocWidget(Uml::IDType id); + + /** + * Finds an association widget with the given type and widgets. + * + * @param at The Association_Type of the widget to find. + * @param pWidgetA Pointer to the UMLWidget of role A. + * @param pWidgetB Pointer to the UMLWidget of role B. + * + * @return Returns the widget found, returns 0 if no widget found. + */ + AssociationWidget * findAssocWidget(Uml::Association_Type at, + UMLWidget *pWidgetA, UMLWidget *pWidgetB); + + /** + * Finds an association widget with the given widgets and the given role B name. + * Considers the following association types: + * at_Association, at_UniAssociation, at_Composition, at_Aggregation + * This is used for seeking an attribute association. + * + * @param pWidgetA Pointer to the UMLWidget of role A. + * @param pWidgetB Pointer to the UMLWidget of role B. + * @param roleNameB Name at the B side of the association (the attribute name) + * + * @return Returns the widget found, returns 0 if no widget found. + */ + AssociationWidget * findAssocWidget(UMLWidget *pWidgetA, + UMLWidget *pWidgetB, const QString& roleNameB); + + /** + * Remove a widget from view. + * + * @param o The widget to remove. + */ + void removeWidget(UMLWidget * o); + + /** + * Sets a widget to a selected state and adds it to a list of selected widgets. + * + * @param w The widget to set to selected. + * @param me The mouse event containing the information about the selection. + */ + void setSelected(UMLWidget * w, QMouseEvent * me); + + /** + * Clear the selected widgets list. + */ + void clearSelected(); + + /** + * Move all the selected widgets by a relative X and Y offset. + * + * @param dX The distance to move horizontally. + * @param dY The distance to move vertically. + */ + void moveSelectedBy(int dX, int dY); + + /** + * Return the amount of widgets selected. + * + * @param filterText When true, do NOT count floating text widgets that + * belong to other widgets (i.e. only count tr_Floating.) + * Default: Count all widgets. + * @return Number of widgets selected. + */ + int getSelectCount(bool filterText = false) const; + + /** + * Set the useFillColor variable to all selected widgets + * + * @param useFC The state to set the widget to. + */ + void selectionUseFillColor(bool useFC); + + /** + * Set the font for all the currently selected items. + */ + void selectionSetFont( const QFont &font ); + + /** + * Set the line color for all the currently selected items. + */ + void selectionSetLineColor( const QColor &color ); + + /** + * Set the line width for all the currently selected items. + */ + void selectionSetLineWidth( uint width ); + + /** + * Set the fill color for all the currently selected items. + */ + void selectionSetFillColor( const QColor &color ); + + /** + * Toggles the show setting sel of all selected items. + */ + void selectionToggleShow(int sel); + + /** + * Delete the selected widgets list and the widgets in it. + */ + void deleteSelection(); + + /** + * Selects all widgets + */ + void selectAll(); + + /** + * Return a unique ID for the diagram. Used by the @ref ObjectWidget class. + * + * @return Return a unique ID for the diagram. + */ + Uml::IDType getLocalID(); + + /** + * Returns whether a widget is already on the diagram. + * + * @param id The id of the widget to check for. + * + * @return Returns true if the widget is already on the diagram, false if not. + */ + bool widgetOnDiagram(Uml::IDType id); + + /** + * Returns true if this diagram resides in an externalized folder. + * CHECK: It is probably cleaner to move this to the UMLListViewItem. + */ + bool isSavedInSeparateFile(); + + /** + * Get the pos variable. Used internally to keep track of the cursor. + */ + QPoint & getPos() { + return m_Pos; + } + + /** + * Set the pos variable. Used internally to keep track of the cursor. + * + * @param _pos The position to set to. + */ + void setPos(const QPoint &_pos) { + m_Pos = _pos; + } + + /** + * Sets the popup menu to use when clicking on a diagram background + * (rather than a widget or listView). + */ + void setMenu(); + + /** + * Reset the toolbar. + */ + void resetToolbar() { + emit sigResetToolBar(); + } + + /** + * Returns the status on whether in a paste state. + * + * @return Returns the status on whether in a paste state. + */ + bool getPaste() const { + return m_bPaste; + } + + /** + * Sets the status on whether in a paste state. + */ + void setPaste(bool paste) { + m_bPaste = paste; + } + + /** + * Returns a List of all the UMLObjects(Use Cases, Concepts and Actors) in the View + */ + UMLObjectList getUMLObjects(); + + /** + * Activate all the objects and associations after a load from the clipboard + */ + void activate(); + + /** + * Returns a list with all the selected associations from the diagram + */ + AssociationWidgetList getSelectedAssocs(); + + /** + * Fills the List with all the selected widgets from the diagram + * The list can be filled with all the selected widgets, or be filtered to prevent + * text widgets other than tr_Floating to be append. + * + * @param WidgetList The UMLWidgetList to fill. + * @param filterText Don't append the text, unless their role is tr_Floating + */ + bool getSelectedWidgets(UMLWidgetList& WidgetList, bool filterText = true); + + /** + * Activate the view after a load a new file + */ + void activateAfterLoad( bool bUseLog = false ); + + void endPartialWidgetPaste(); + void beginPartialWidgetPaste(); + + /** + * Removes a AssociationWidget from a diagram + * Physically deletes the AssociationWidget passed in. + * + * @param pAssoc Pointer to the AssociationWidget. + */ + void removeAssoc(AssociationWidget* pAssoc); + + /** + * Removes all the associations related to Widget + * + * @param pWidget Pointer to the widget to remove. + */ + void removeAssociations(UMLWidget* pWidget); + + /** + * Sets each association as selected if the widgets it associates are selected + */ + void selectAssociations(bool bSelect); + + /** + * Fills Associations with all the associations that includes a widget related to object + */ + void getWidgetAssocs(UMLObject* Obj, AssociationWidgetList & Associations); + + /** + * Removes All the associations of the diagram + */ + void removeAllAssociations(); + + /** + * Removes All the widgets of the diagram + */ + void removeAllWidgets(); + + /** + * Calls the same method in the DocWindow. + */ + void showDocumentation( UMLObject * object, bool overwrite ); + + /** + * Calls the same method in the DocWindow. + */ + void showDocumentation( UMLWidget * widget, bool overwrite ); + + /** + * Calls the same method in the DocWindow. + */ + void showDocumentation( AssociationWidget * widget, bool overwrite ); + + /** + * Calls the same method in the DocWindow. + */ + void updateDocumentation( bool clear ); + + /** + * Returns the PNG picture of the paste operation. + * + * @param rect the area of the diagram to copy + * @param diagram the class to store PNG picture of the paste operation. + */ + void getDiagram(const QRect &rect, QPixmap & diagram); + + /** + * Paint diagram to the paint device + */ + void getDiagram(const QRect &area, QPainter & painter); + + /** + * Returns the PNG picture of the paste operation. + */ + void copyAsImage(QPixmap*& pix); + + /** + * Returns the imageExporter used to export the view. + * + * @return The imageExporter used to export the view. + */ + UMLViewImageExporter* getImageExporter(); + + /** + * Adds an association to the view from the given data. + * Use this method when pasting. + */ + bool addAssociation( AssociationWidget* pAssoc , bool isPasteOperation = false); + + /** + * Removes an AssociationWidget from the association list + * and removes the corresponding UMLAssociation from the current UMLDoc. + */ + void removeAssocInViewAndDoc(AssociationWidget* assoc); + + /** + * Adds a widget to the view from the given data. + * Use this method when pasting. + */ + bool addWidget( UMLWidget * pWidget , bool isPasteOperation = false); + + /** + * Returns the offset point at which to place the paste from clipboard. + * Just add the amount to your co-ords. + * Only call this straight after the event, the value won't stay valid. + * Should only be called by Assoc widgets at the moment. no one else needs it. + */ + QPoint getPastePoint(); + + /** + * Reset the paste point. + */ + void resetPastePoint(); + + /** + * Called by the view or any of its children when they start a cut + * operation. + */ + void setStartedCut() { + m_bStartedCut = true; + } + + /** + * Creates automatically any Associations that the given @ref UMLWidget + * may have on any diagram. This method is used when you just add the UMLWidget + * to a diagram. + */ + void createAutoAssociations( UMLWidget * widget ); + + /** + * If the m_Type of the given widget is Uml::wt_Class then + * iterate through the class' attributes and create an + * association to each attribute type widget that is present + * on the current diagram. + */ + void createAutoAttributeAssociations(UMLWidget *widget); + + /** + * Refreshes containment association, i.e. removes possible old + * containment and adds new containment association if applicable. + * + * @param self Pointer to the contained object for which + * the association to the containing object is + * recomputed. + */ + void updateContainment(UMLCanvasObject *self); + + /** + * Sets the x grid size. + */ + void setSnapX( int x) { + m_nSnapX = x; + canvas() -> setAllChanged(); + } + + /** + * Sets the y grid size. + */ + void setSnapY( int y) { + m_nSnapY = y; + canvas() -> setAllChanged(); + } + + /** + * Shows the properties dialog for the view. + */ + bool showPropDialog(); + + /** + * Sets some options for all the @ref ClassifierWidget on the view. + */ + void setClassWidgetOptions( ClassOptionsPage * page ); + + /** + * Call before copying/cutting selected widgets. This will make sure + * any associations/message selected will make sure both the widgets + * widgets they are connected to are selected. + */ + void checkSelections(); + + /** + * This function checks if the currently selected items have all the same + * type (class, interface, ...). If true, the selection is unique and true + * will be returned. + * If there are no items selected, the function will return always true. + */ + bool checkUniqueSelection(); + + /** + * Asks for confirmation and clears everything on the diagram. + * Called from menus. + */ + void clearDiagram(); + + /** + * Changes snap to grid boolean. + * Called from menus. + */ + void toggleSnapToGrid(); + + /** + * Changes snap to grid for component size boolean. + * Called from menus. + */ + void toggleSnapComponentSizeToGrid(); + + /** + * Changes show grid boolean. + * Called from menus. + */ + void toggleShowGrid(); + + /** + * Changes the zoom to the currently set level (now loaded from file) + * Called from UMLApp::slotUpdateViews() + */ + void fileLoaded(); + + /** + * Sets the diagram width and height in pixels + */ + void setCanvasSize(int width, int height); + + /** + * Sets the size of the canvas to just fit on all the items + */ + void resizeCanvasToItems(); + + /** + * The width and height of a diagram canvas in pixels. + */ + static const int defaultCanvasSize; + + // Load/Save interface: + + /** + * Creates the "diagram" tag and fills it with the contents of the diagram. + */ + virtual void saveToXMI( QDomDocument & qDoc, QDomElement & qElement ); + + /** + * Loads the "diagram" tag. + */ + virtual bool loadFromXMI( QDomElement & qElement ); + + /** + * Loads the "UISDiagram" tag of Unisys.IntegratePlus.2 generated files. + */ + bool loadUISDiagram(QDomElement & qElement); + + /** + * Loads a "widget" element from XMI, used by loadFromXMI() and the clipboard. + */ + UMLWidget* loadWidgetFromXMI(QDomElement& widgetElement); + + /** + * Add an object to the application, and update the view. + */ + void addObject(UMLObject *object); + + /** + * Selects all the widgets within an internally kept rectangle. + */ + void selectWidgets(int px, int py, int qx, int qy); + + /** + * Determine whether on a sequence diagram we have clicked on a line + * of an Object. + * + * @return The widget thats line was clicked on. + * Returns 0 if no line was clicked on. + */ + ObjectWidget * onWidgetLine( const QPoint &point ); + + /** + * Return pointer to the first selected widget (for multi-selection) + */ + UMLWidget* getFirstMultiSelectedWidget() { + return m_SelectedList.first(); + } + + /** + * Tests the given point against all widgets and returns the + * widget for which the point is within its bounding rectangle. + * In case of multiple matches, returns the smallest widget. + * Returns NULL if the point is not inside any widget. + * Does not use or modify the m_pOnWidget member. + */ + UMLWidget *getWidgetAt(const QPoint& p); + + /** + * Initialize and announce a newly created widget. + * Auxiliary to contentsMouseReleaseEvent(). + */ + void setupNewWidget(UMLWidget *w); + + /** + * Return whether we are currently creating an object. + */ + bool getCreateObject() const { + return m_bCreateObject; + } + + /** + * Set whether we are currently creating an object. + */ + void setCreateObject(bool bCreate) { + m_bCreateObject = bCreate; + } + + /** + * Emit the sigRemovePopupMenu Qt signal. + */ + void emitRemovePopupMenu() { + emit sigRemovePopupMenu(); + } + + /** + * Used for creating unique name of collaboration messages. + */ + int generateCollaborationId(); + +protected: + + // Methods and members related to loading/saving + + bool loadWidgetsFromXMI( QDomElement & qElement ); + + bool loadMessagesFromXMI( QDomElement & qElement ); + + bool loadAssociationsFromXMI( QDomElement & qElement ); + + bool loadUisDiagramPresentation(QDomElement & qElement); + + /** + * Contains the unique ID to allocate to a widget that needs an + * ID for the view. @ref ObjectWidget is an example of this. + */ + Uml::IDType m_nLocalID; + + /** + * The ID of the view. Allocated by @ref UMLDoc + */ + Uml::IDType m_nID; + + /** + * The type of diagram to represent. + */ + Uml::Diagram_Type m_Type; + + /** + * The name of the diagram. + */ + QString m_Name; + + /** + * The documentation of the diagram. + */ + QString m_Documentation; + + /** + * Options used by view + */ + Settings::OptionState m_Options; + + /** + * Contains all the message widgets on the diagram. + */ + MessageWidgetList m_MessageList; + + /** + * Contains all the UMLWidgets on the diagram. + */ + UMLWidgetList m_WidgetList; + + /** + * Contains all the AssociationWidgets on the diagram. + */ + AssociationWidgetList m_AssociationList; + + /** + * The snap to grid x size. + */ + int m_nSnapX; + + /** + * The snap to grid y size. + */ + int m_nSnapY; + + /** + * Determines whether to use snap to grid. The default is off. + */ + bool m_bUseSnapToGrid; + + /** + * Determines whether to use snap to grid for component + * size. The default is off. + */ + bool m_bUseSnapComponentSizeToGrid; + + /** + * Determines whether to show the snap grid. The default will be on if the grid is on. + */ + bool m_bShowSnapGrid; + + /** + * The zoom level in percent, default 100 + */ + int m_nZoom; + + /** + * Width of canvas in pixels + */ + int m_nCanvasWidth; + + /** + * Height of canvas in pixels + */ + int m_nCanvasHeight; + + // End of methods and members related to loading/saving + //////////////////////////////////////////////////////////////////////// + + /** + * Override standard method. + */ + void closeEvent ( QCloseEvent * e ); + + /** + * Override standard method. + */ + void contentsDragEnterEvent(QDragEnterEvent* mouseEvent); + + /** + * Override standard method. + */ + void contentsDropEvent(QDropEvent* mouseEvent); + + + /** + * Gets the smallest area to print. + * + * @return Returns the smallest area to print. + */ + QRect getDiagramRect(); + + + /** + * Initializes key variables. + */ + void init(); + + /** + * Overrides the standard operation. + * Calls the same method in the current tool bar state. + */ + void contentsMouseReleaseEvent(QMouseEvent* mouseEvent); + + /** + * Overrides the standard operation. + * Calls the same method in the current tool bar state. + */ + void contentsMouseMoveEvent(QMouseEvent* mouseEvent); + + /** + * Override standard method. + * Calls the same method in the current tool bar state. + */ + void contentsMouseDoubleClickEvent(QMouseEvent* mouseEvent); + + /** + * Override standard method. + * Calls the same method in the current tool bar state. + */ + void contentsMousePressEvent(QMouseEvent* mouseEvent); + + + /** + * Selects all the widgets of the given association widget. + */ + void selectWidgetsOfAssoc (AssociationWidget * a); + + /** + * Calls setSelected on the given UMLWidget and enters + * it into the m_SelectedList while making sure it is + * there only once. + */ + void makeSelected (UMLWidget * uw); + + /** + * Updates the size of all components in this view. + */ + void updateComponentSizes(); + + /** + * Find the maximum bounding rectangle of FloatingTextWidget widgets. + * Auxiliary to copyAsImage(). + * + * @param ft Pointer to the FloatingTextWidget widget to consider. + * @param px X coordinate of lower left corner. This value will be + * updated if the X coordinate of the lower left corner + * of ft is smaller than the px value passed in. + * @param py Y coordinate of lower left corner. This value will be + * updated if the Y coordinate of the lower left corner + * of ft is smaller than the py value passed in. + * @param qx X coordinate of upper right corner. This value will be + * updated if the X coordinate of the upper right corner + * of ft is larger than the qx value passed in. + * @param qy Y coordinate of upper right corner. This value will be + * updated if the Y coordinate of the upper right corner + * of ft is larger than the qy value passed in. + */ + void findMaxBoundingRectangle(const FloatingTextWidget* ft, + int& px, int& py, int& qx, int& qy); + + void forceUpdateWidgetFontMetrics(QPainter *painter); + + /** + * Used for creating unique name of collaboration messages. + */ + int m_nCollaborationId; + + QPoint m_Pos; + bool m_bCreateObject, m_bDrawSelectedOnly, m_bPaste; + ListPopupMenu * m_pMenu; + UMLWidgetList m_SelectedList; + + /** + * Flag if view/children started cut operation. + */ + bool m_bStartedCut; + +private: + /** + * The folder in which this UMLView is contained + */ + UMLFolder *m_pFolder; + + /** + * set to true when a child has used the showDocumentation method, + * thus when one clicks on a child widget. + * Reset to false when clicking in an empty region of the view. + */ + bool m_bChildDisplayedDoc; + + ToolBarStateFactory* m_pToolBarStateFactory; + ToolBarState* m_pToolBarState; + + /** + * LocalID Changes Log for paste actions + */ + IDChangeLog * m_pIDChangesLog; + + /** + * + * True if the view was activated after the serialization(load) + */ + bool m_bActivated; + + /** + * Status of a popupmenu on view. + * true - a popup is on view + */ + bool m_bPopupShowing; + + /** + * The offset at which to paste the clipboard. + */ + QPoint m_PastePoint; + + /** + * Pointer to the UMLDoc + */ + UMLDoc* m_pDoc; + + /** + * The UMLViewImageExporter used to export the view. + */ + UMLViewImageExporter* m_pImageExporter; + + /** + * Create an association with the attribute attr associated with the UMLWidget + * widget if the UMLClassifier type is present on the current diagram. + */ + void createAutoAttributeAssociation(UMLClassifier *type, + UMLAttribute *attr, + UMLWidget *widget); + +public slots: + + void zoomIn(); + void zoomOut(); + + /** + * Changes the current tool to the selected tool. + * The current tool is cleaned and the selected tool initialized. + */ + void slotToolBarChanged(int c); + void slotObjectCreated(UMLObject * o); + void slotObjectRemoved(UMLObject * o); + + /** + * When a menu selection has been made on the menu + * that this view created, this method gets called. + */ + void slotMenuSelection(int sel); + + /** + * This slot is entered when an event has occurred on the views display, + * most likely a mouse event. Before it sends out that mouse event everyone + * that displays a menu on the views surface (widgets and this ) thould remove any + * menu. This stops more then one menu bieing displayed. + */ + void slotRemovePopupMenu(); + + /** + * makes this view the active view by asking the document to show us + */ + void slotActivate(); + + /** + * Connects to the signal that @ref UMLApp emits when a cut operation + * is successful. + * If the view or a child started the operation the flag m_bStartedCut will + * be set and we can carry out any operation that is needed, like deleting the selected + * widgets for the cut operation. + */ + void slotCutSuccessful(); + + /** + * Called by menu when to show the instance of the view. + */ + void slotShowView(); + + /** + * Overrides standard method from QWidget to resize canvas when + * it's shown. + */ + void show(); + +signals: + void sigResetToolBar(); + void sigColorChanged( Uml::IDType ); + void sigRemovePopupMenu(); + void sigClearAllSelected(); + void sigLineWidthChanged( Uml::IDType ); + void sigSnapToGridToggled(bool); + void sigSnapComponentSizeToGridToggled(bool); + void sigShowGridToggled(bool); + + /** + * Emitted when an association is removed. + */ + void sigAssociationRemoved(AssociationWidget*); + + /** + * Emitted when a widget is removed. + */ + void sigWidgetRemoved(UMLWidget*); +}; + +#endif // UMLVIEW_H diff --git a/umbrello/umbrello/umlviewcanvas.cpp b/umbrello/umbrello/umlviewcanvas.cpp new file mode 100644 index 00000000..15de4169 --- /dev/null +++ b/umbrello/umbrello/umlviewcanvas.cpp @@ -0,0 +1,41 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "umlviewcanvas.h" + +// qt/kde includes +#include + +// app includes +#include "umlview.h" + + +UMLViewCanvas::UMLViewCanvas( UMLView * pView ) : QCanvas( pView ) { + m_pView = pView; +} + +UMLViewCanvas::~UMLViewCanvas() {} + +void UMLViewCanvas::drawBackground( QPainter & painter, const QRect & clip ) { + QCanvas::drawBackground( painter, clip ); + if( m_pView -> getShowSnapGrid() ) { + painter.setPen( Qt::gray ); + int gridX = m_pView -> getSnapX(); + int gridY = m_pView -> getSnapY(); + int numX = width() / gridX; + int numY = height() / gridY; + for( int x = 0; x <= numX; x++ ) + for( int y = 0; y < numY; y++ ) + painter.drawPoint( x * gridX, y * gridY ); + } +} + diff --git a/umbrello/umbrello/umlviewcanvas.h b/umbrello/umbrello/umlviewcanvas.h new file mode 100644 index 00000000..78d7d4dd --- /dev/null +++ b/umbrello/umbrello/umlviewcanvas.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLVIEWCANVAS_H +#define UMLVIEWCANVAS_H + +#include + +/** + *@author Paul Hensgen + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class UMLView; + +class UMLViewCanvas : public QCanvas { +public: + /** + * Constructor + */ + UMLViewCanvas( UMLView * pView ); + + /** + * Deconstructor + */ + virtual ~UMLViewCanvas(); + +protected: + + /** + * Overrides default method. + */ + virtual void drawBackground( QPainter & painter, const QRect & clip ); + + /** + * The view the canvas is associated with. + */ + UMLView * m_pView; +}; + +#endif diff --git a/umbrello/umbrello/umlviewimageexporter.cpp b/umbrello/umbrello/umlviewimageexporter.cpp new file mode 100644 index 00000000..251d0ffb --- /dev/null +++ b/umbrello/umbrello/umlviewimageexporter.cpp @@ -0,0 +1,128 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "umlviewimageexporter.h" + +// include files for Qt +#include +#include + +//kde include files +#include +#include +#include +#include +#include + +// application specific includes +#include "umlviewimageexportermodel.h" +#include "uml.h" +#include "umldoc.h" +#include "umlview.h" + + +UMLViewImageExporter::UMLViewImageExporter(UMLView* view) { + m_view = view; + m_imageMimeType = UMLApp::app()->getImageMimeType(); +} + +void UMLViewImageExporter::exportView() { + if (!prepareExportView()) { + return; + } + + UMLApp *app = UMLApp::app(); + + // export the view + app->getDocument()->writeToStatusBar(i18n("Exporting view...")); + QString error = UMLViewImageExporterModel().exportView(m_view, + UMLViewImageExporterModel::mimeTypeToImageType(m_imageMimeType), m_imageURL); + if (!error.isNull()) { + KMessageBox::error(app, i18n("An error happened when exporting the image:\n") + error); + } + app->getDocument()->writeToStatusBar(i18n("Ready.")); +} + +bool UMLViewImageExporter::prepareExportView() { + bool exportPrepared = false; + + do { + if (!getParametersFromUser()) { + return false; + } + + // check if the file exists + if (KIO::NetAccess::exists(m_imageURL, true, UMLApp::app())) { + int wantSave = KMessageBox::warningContinueCancel(0, + i18n("The selected file %1 exists.\nDo you want to overwrite it?").arg(m_imageURL.prettyURL()), + i18n("File Already Exists"), i18n("&Overwrite")); + if (wantSave == KMessageBox::Continue) { + exportPrepared = true; + } + } else { + exportPrepared = true; + } + } while (!exportPrepared); + + return true; +} + +bool UMLViewImageExporter::getParametersFromUser() { + UMLApp *app = UMLApp::app(); + + // configure & show the file dialog + KFileDialog fileDialog(QString::null, QString::null, m_view, + ":export-image", true); + prepareFileDialog(fileDialog); + fileDialog.exec(); + + if (fileDialog.selectedURL().isEmpty()) + return false; + m_view->clearSelected(); // Thanks to Peter Soetens for the idea + + // update image url and mime type + m_imageMimeType = fileDialog.currentMimeFilter(); + app->setImageMimeType(m_imageMimeType); + m_imageURL = fileDialog.selectedURL(); + + // check if the extension is the extension of the mime type + QFileInfo info(m_imageURL.filename()); + QString ext = info.extension(false); + QString extDef = UMLViewImageExporterModel::mimeTypeToImageType(m_imageMimeType); + if(ext != extDef) { + m_imageURL.setFileName(m_imageURL.fileName() + '.' + extDef); + } + + return true; +} + +void UMLViewImageExporter::prepareFileDialog(KFileDialog &fileDialog) { + // get all supported mime types + QStringList mimeTypes = UMLViewImageExporterModel::supportedMimeTypes(); + + fileDialog.setCaption(i18n("Save As")); + fileDialog.setOperationMode(KFileDialog::Saving); + fileDialog.setMimeFilter(mimeTypes, m_imageMimeType); + + // set a sensible default filename + if (m_imageURL.isEmpty()) { + KURL docURL = UMLApp::app()->getDocument()->URL(); + KURL directory = docURL; + directory.setPath(docURL.directory()); + + fileDialog.setURL(directory); + fileDialog.setSelection(m_view->getName() + '.' + UMLViewImageExporterModel::mimeTypeToImageType(m_imageMimeType)); + } else { + fileDialog.setURL(m_imageURL); + fileDialog.setSelection(m_imageURL.fileName()); + } +} diff --git a/umbrello/umbrello/umlviewimageexporter.h b/umbrello/umbrello/umlviewimageexporter.h new file mode 100644 index 00000000..2400213b --- /dev/null +++ b/umbrello/umbrello/umlviewimageexporter.h @@ -0,0 +1,122 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLVIEWIMAGEEXPORTER_H +#define UMLVIEWIMAGEEXPORTER_H + +#include +#include + +class UMLView; +class KFileDialog; + +/** + * Exports the view as an image. + * This class takes care of asking the user the needed parameters and + * then exports the view. + */ +class UMLViewImageExporter { +public: + + /** + * Constructor for UMLViewImageExporter + */ + UMLViewImageExporter(UMLView* view); + + /** + * Destructor for UMLViewImageExporter + */ + virtual ~UMLViewImageExporter() { + } + + /** + * Shows a save dialog to the user to get the needed parameters and then exports + * the view. + * If the selected file already exists, an overwrite confirmation + * dialog is shown. If the user doesn't want to overwrite the file, + * the save dialog is shown again. + * The dialog remembers values between calls (in the same application instance, + * although it's not persistent between Umbrello executions). + * + * The status bar shows an information message until the export finishes. + * + * If something went wrong while exporting, an error dialog is shown to the + * user with the error message explaining the problem that happened. + */ + void exportView(); + + /** + * Returns the URL used to save the image. + * + * @return The URL used to save the image. + */ + KURL getImageURL() const { + return m_imageURL; + } + + /** + * Returns the mime type used to save the image. + * + * @return The mime type used to save the image. + */ + QString getImageMimeType() const { + return m_imageMimeType; + } + +private: + + /** + * The view to export. + */ + UMLView* m_view; + + /** + * The URL used to save the image. + */ + KURL m_imageURL; + + /** + * The mime type used to save the image. + */ + QString m_imageMimeType; + + /** + * Shows a save file dialog to the user to get the parameters used + * to export the view. + * If the selected file already exists, an overwrite confirmation + * dialog is shown. If the user doesn't want to overwrite the file, + * the save dialog is shown again. + * + * @return True if the user wants to save the image, + * false if the operation is cancelled. + */ + bool prepareExportView(); + + /** + * Shows a save file dialog to the user to get the parameters used + * to export the view and updates the attributes with the parameters got. + * + * @return True if the user wants to save the image, + * false if the operation is cancelled. + */ + bool getParametersFromUser(); + + /** + * Prepares the save file dialog. + * Sets the mime type filter, sensible default values... + * + * @param fileDialog The dialog to prepare. + */ + void prepareFileDialog(KFileDialog &fileDialog); + +}; + +#endif diff --git a/umbrello/umbrello/umlviewimageexporterall.cpp b/umbrello/umbrello/umlviewimageexporterall.cpp new file mode 100644 index 00000000..fceb6aa3 --- /dev/null +++ b/umbrello/umbrello/umlviewimageexporterall.cpp @@ -0,0 +1,74 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "umlviewimageexporterall.h" + +// include files for Qt +#include +#include +#include + +// kde include files +#include +#include +#include +#include +#include + +// application specific includes +#include "dialogs/exportallviewsdialog.h" +#include "umlviewimageexportermodel.h" +#include "uml.h" +#include "umldoc.h" + +UMLViewImageExporterAll::UMLViewImageExporterAll() { + m_dialog = new ExportAllViewsDialog(0, "exportAllViewsDialog", false, 0, UMLApp::app()->getImageMimeType()); +} + +UMLViewImageExporterAll::~UMLViewImageExporterAll() { + delete m_dialog; +} + +void UMLViewImageExporterAll::exportAllViews() { + UMLApp* app = UMLApp::app(); + UMLDoc* umlDoc = app->getDocument(); + + // default url can't be set when creating the action because the + // document wasn't loaded + if (m_dialog->m_kURL->url().isEmpty()) { + m_dialog->m_kURL->setURL(umlDoc->URL().directory()); + } + + if (m_dialog->exec() == QDialog::Rejected) { + return; + } + + app->setImageMimeType(m_dialog->m_imageType->currentFilter()); + + // export all views + umlDoc->writeToStatusBar(i18n("Exporting all views...")); + QStringList errors = UMLViewImageExporterModel().exportAllViews( + UMLViewImageExporterModel::mimeTypeToImageType(m_dialog->m_imageType->currentFilter()), + KURL(m_dialog->m_kURL->url()), m_dialog->m_useFolders->isChecked()); + if (!errors.empty()) { +#if KDE_IS_VERSION(3,4,0) + KMessageBox::errorList(app, i18n("Some errors happened when exporting the images:"), errors); +#else + QString errorsCaption; + for (QStringList::Iterator it = errors.begin(); it != errors.end(); ++it) { + errorsCaption += "\n" + *it; + } + KMessageBox::error(app, i18n("Some errors happened when exporting the images:") + errorsCaption); +#endif + } + umlDoc->writeToStatusBar(i18n("Ready.")); +} diff --git a/umbrello/umbrello/umlviewimageexporterall.h b/umbrello/umbrello/umlviewimageexporterall.h new file mode 100644 index 00000000..d2368921 --- /dev/null +++ b/umbrello/umbrello/umlviewimageexporterall.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLVIEWIMAGEEXPORTERALL_H +#define UMLVIEWIMAGEEXPORTERALL_H + +class ExportAllViewsDialog; + +/** + * Exports all the views in the UML document as images. + * This class takes care of asking the user the needed parameters and + * then exports the views using UMLViewImageExporterModel. + */ +class UMLViewImageExporterAll { +public: + + /** + * Constructor for UMLViewImageExporterAll + */ + UMLViewImageExporterAll(); + + /** + * Destructor for UMLViewImageExporterAll + */ + virtual ~UMLViewImageExporterAll(); + + /** + * Shows a dialog to the user to get the needed parameters and then exports + * the views. + * The dialog remembers values between calls (in the same application instance, + * although it's not persistent between Umbrello executions). + * + * Once the export begins, it can't be stopped until it ends itself. The status + * bar shows an information message until the export finishes. + * + * If something went wrong while exporting, an error dialog is shown to the + * user with the error messages explaining the problems occurred. + */ + void exportAllViews(); + +private: + + /** + * The dialog to get the needed parameters from the user. + */ + ExportAllViewsDialog* m_dialog; + +}; + +#endif diff --git a/umbrello/umbrello/umlviewimageexportermodel.cpp b/umbrello/umbrello/umlviewimageexportermodel.cpp new file mode 100644 index 00000000..e829a3f7 --- /dev/null +++ b/umbrello/umbrello/umlviewimageexportermodel.cpp @@ -0,0 +1,366 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "umlviewimageexportermodel.h" + +// system includes +#include + +// include files for Qt +#include +#include +#include +#include +#include +#include +#include +#include + +// kde include files +#include +#include +#include +#include +#include + +// application specific includes +#include "uml.h" +#include "umldoc.h" +#include "umlview.h" +#include "umllistview.h" +#include "umllistviewitem.h" + +static QStringList supportedImageTypesList; +static QStringList supportedMimeTypesList; + +QStringList UMLViewImageExporterModel::supportedImageTypes() { + if (!supportedImageTypesList.size()) { + // specific supported formats + supportedImageTypesList << "eps"; + supportedImageTypesList << "svg"; + + // QT supported formats + QStrList qImageFormats = QImage::outputFormats(); + for (const char* format = qImageFormats.first(); format; format = qImageFormats.next()) { + supportedImageTypesList << QString(format).lower(); + } + } + + return supportedImageTypesList; +} + +QStringList UMLViewImageExporterModel::supportedMimeTypes() { + if (!supportedMimeTypesList.size()) { + QStringList imageTypes = UMLViewImageExporterModel::supportedImageTypes(); + for(QStringList::Iterator it = imageTypes.begin(); it != imageTypes.end(); ++it ) { + QString mimeType = imageTypeToMimeType(*it); + if (!mimeType.isNull()) + supportedMimeTypesList.append(mimeType); + } + } + + return supportedMimeTypesList; +} + +QString UMLViewImageExporterModel::imageTypeToMimeType(const QString& imageType) { + const QString imgType = imageType.lower(); + if (QString("bmp") == imgType) return "image/x-bmp"; + if (QString("jpeg") == imgType) return "image/jpeg"; + if (QString("pbm") == imgType) return "image/x-portable-bitmap"; + if (QString("pgm") == imgType) return "image/x-portable-greymap"; + if (QString("png") == imgType) return "image/png"; + if (QString("ppm") == imgType) return "image/x-portable-pixmap"; + if (QString("xbm") == imgType) return "image/x-xbm"; + if (QString("xpm") == imgType) return "image/x-xpm"; + if (QString("eps") == imgType) return "image/x-eps"; + if (QString("svg") == imgType) return "image/svg+xml"; + return QString::null; +} + +QString UMLViewImageExporterModel::mimeTypeToImageType(const QString& mimeType) { + if (QString("image/x-bmp") == mimeType) return "bmp"; + if (QString("image/jpeg") == mimeType) return "jpeg"; + if (QString("image/x-portable-bitmap") == mimeType) return "pbm"; + if (QString("image/x-portable-greymap") == mimeType) return "pgm"; + if (QString("image/png") == mimeType) return "png"; + if (QString("image/x-portable-pixmap") == mimeType) return "ppm"; + if (QString("image/x-xbm") == mimeType) return "xbm"; + if (QString("image/x-xpm") == mimeType) return "xpm"; + if (QString("image/x-eps") == mimeType) return "eps"; + if (QString("image/svg+xml") == mimeType) return "svg"; + return QString::null; +} + +QStringList UMLViewImageExporterModel::exportAllViews(const QString &imageType, const KURL &directory, bool useFolders) const { + UMLApp *app = UMLApp::app(); + + // contains all the error messages returned by exportView calls + QStringList errors; + + UMLViewList views = app->getDocument()->getViewIterator(); + for(UMLView *view = views.first(); view; view = views.next()) { + KURL url = directory; + url.addPath(getDiagramFileName(view, imageType, useFolders)); + + QString returnString = exportView(view, imageType, url); + if (!returnString.isNull()) { + errors.append(view->getName() + ": " + returnString); + } + } + + return errors; +} + +QString UMLViewImageExporterModel::exportView(UMLView* view, const QString &imageType, const KURL &url) const { + // create the needed directories + if (!prepareDirectory(url)) { + return i18n("Can not create directory: %1").arg(url.directory()); + } + + // The fileName will be used when exporting the image. If the url isn't local, + // the fileName is the name of a temporal local file to export the image to, and then + // upload it to its destiny + QString fileName; + // tmpFile needs to be unlinked before exiting the method!!! + KTempFile tmpFile; + if (url.isLocalFile()) { + fileName = url.path(); + } else { + fileName = tmpFile.name(); + } + + // check that the diagram isn't empty + QRect rect = view->getDiagramRect(); + if (rect.isEmpty()) { + tmpFile.unlink(); + return i18n("Can not save an empty diagram"); + } + + // exporting the view to the file + if (!exportViewTo(view, imageType, fileName)) { + tmpFile.unlink(); + return i18n("A problem occured while saving diagram in %1").arg(fileName); + } + + // if the file wasn't local, upload the temp file to the target + if (!url.isLocalFile()) { + if (!KIO::NetAccess::upload(tmpFile.name(), url, UMLApp::app())) { + tmpFile.unlink(); + return i18n("There was a problem saving file: %1").arg(url.path()); + } + } //!isLocalFile + + tmpFile.unlink(); + return QString::null; +} + +QString UMLViewImageExporterModel::getDiagramFileName(UMLView *view, const QString &imageType, bool useFolders /* = false */) const { + QString name = view->getName() + '.' + imageType.lower(); + + if (!useFolders) { + return name; + } + + kapp->processEvents(); + UMLListView *listView = UMLApp::app()->getListView(); + UMLListViewItem* listViewItem = listView->findItem(view->getID()); + // skip the name of the first item because it's the View + listViewItem = static_cast(listViewItem->parent()); + + // Relies on the tree structure of the UMLListView. There are a base "Views" folder + // and five children, one for each view type (Logical, use case, components, deployment + // and entity relationship) + while (listView->rootView(listViewItem->getType()) == NULL) { + name.insert(0, listViewItem->getText() + '/'); + listViewItem = static_cast(listViewItem->parent()); + if (listViewItem == NULL) + break; + } + return name; +} + +bool UMLViewImageExporterModel::prepareDirectory(const KURL &url) const { + // the KURL is copied to get protocol, user and so on and then the path is cleaned + KURL directory = url; + directory.setPath(""); + + // creates the directory and any needed parent directories + QStringList dirs = QStringList::split(QDir::separator(), url.directory()); + for (QStringList::ConstIterator it = dirs.begin() ; it != dirs.end(); ++it ) { + directory.addPath(*it); + + if (!KIO::NetAccess::exists(directory, true, UMLApp::app())) { + + if (!KIO::NetAccess::mkdir(directory, UMLApp::app())) { + return false; + } + } + } + + return true; +} + +bool UMLViewImageExporterModel::exportViewTo(UMLView* view, const QString &imageType, const QString &fileName) const { + // remove 'blue squares' from exported picture. + view->clearSelected(); + + QString imageMimeType = UMLViewImageExporterModel::imageTypeToMimeType(imageType); + if (imageMimeType == "image/x-eps") { + if (!exportViewToEps(view, fileName, true)) { + return false; + } + } else if (imageMimeType == "image/svg+xml") { + if (!exportViewToSvg(view, fileName)) { + return false; + } + } else { + if (!exportViewToPixmap(view, imageType, fileName)) { + return false; + } + } + + return true; +} + +bool UMLViewImageExporterModel::exportViewToEps(UMLView* view, const QString &fileName, bool isEPS) const { + bool exportSuccessful = true; + + // print the image to a normal postscript file, + // do not clip so that everything ends up in the file + // regardless of "paper size" + + // because we want to work with postscript + // user-coordinates, set to the resolution + // of the printer (which should be 72dpi here) + QPrinter *printer; + + if (isEPS == false) { + printer = new QPrinter(QPrinter::PrinterResolution); + } else { + printer = new QPrinter(QPrinter::ScreenResolution); + } + printer->setOutputToFile(true); + printer->setOutputFileName(fileName); + printer->setColorMode(QPrinter::Color); + + // do not call printer.setup(); because we want no user + // interaction here + QPainter *painter = new QPainter(printer); + + // make sure the widget sizes will be according to the + // actually used printer font, important for getDiagramRect() + // and the actual painting + view->forceUpdateWidgetFontMetrics(painter); + + QRect rect = view->getDiagramRect(); + painter->translate(-rect.x(),-rect.y()); + view->getDiagram(rect,*painter); + + int resolution = printer->resolution(); + + // delete painter and printer before we try to open and fix the file + delete painter; + delete printer; + if (isEPS) { + // modify bounding box from screen to eps resolution. + rect.setWidth( int(ceil(rect.width() * 72.0/resolution)) ); + rect.setHeight( int(ceil(rect.height() * 72.0/resolution)) ); + exportSuccessful = fixEPS(fileName,rect); + } + // next painting will most probably be to a different device (i.e. the screen) + view->forceUpdateWidgetFontMetrics(0); + + return exportSuccessful; +} + +bool UMLViewImageExporterModel::fixEPS(const QString &fileName, const QRect& rect) const { + // now open the file and make a correct eps out of it + QFile epsfile(fileName); + if (! epsfile.open(IO_ReadOnly)) { + return false; + } + // read + QTextStream ts(&epsfile); + QString fileContent = ts.read(); + epsfile.close(); + + // read information + QRegExp rx("%%BoundingBox:\\s*(-?[\\d\\.:]+)\\s*(-?[\\d\\.:]+)\\s*(-?[\\d\\.:]+)\\s*(-?[\\d\\.:]+)"); + const int pos = rx.search(fileContent); + if (pos < 0) { + kError() << "UMLViewImageExporterModel::fixEPS(" << fileName + << "): cannot find %%BoundingBox" << endl; + return false; + } + + // write new content to file + if (! epsfile.open(IO_WriteOnly | IO_Truncate)) { + kError() << "UMLViewImageExporterModel::fixEPS(" << fileName + << "): cannot open file for writing" << endl; + return false; + } + + // be careful when rounding (ceil/floor) the BB, these roundings + // were mainly obtained experimentally... + const double epsleft = rx.cap(1).toFloat(); + const double epstop = rx.cap(4).toFloat(); + const int left = int(floor(epsleft)); + const int right = int(ceil(epsleft)) + rect.width(); + const int top = int(ceil(epstop)) + 1; + const int bottom = int(floor(epstop)) - rect.height() + 1; + + // modify content + fileContent.replace(pos,rx.cap(0).length(), + QString("%%BoundingBox: %1 %2 %3 %4").arg(left).arg(bottom).arg(right).arg(top)); + + ts << fileContent; + epsfile.close(); + + return true; +} + +bool UMLViewImageExporterModel::exportViewToSvg(UMLView* view, const QString &fileName) const { + bool exportSuccesful; + + QPicture* diagram = new QPicture(); + + // do not call printer.setup(); because we want no user + // interaction here + QPainter* painter = new QPainter(); + painter->begin( diagram ); + + // make sure the widget sizes will be according to the + // actually used printer font, important for getDiagramRect() + // and the actual painting + view->forceUpdateWidgetFontMetrics(painter); + + QRect rect = view->getDiagramRect(); + painter->translate(-rect.x(),-rect.y()); + view->getDiagram(rect,*painter); + painter->end(); + exportSuccesful = diagram->save(fileName, QString("SVG").ascii()); + + // delete painter and printer before we try to open and fix the file + delete painter; + delete diagram; + // next painting will most probably be to a different device (i.e. the screen) + view->forceUpdateWidgetFontMetrics(0); + + return exportSuccesful; +} + +bool UMLViewImageExporterModel::exportViewToPixmap(UMLView* view, const QString &imageType, const QString &fileName) const { + QRect rect = view->getDiagramRect(); + QPixmap diagram(rect.width(), rect.height()); + view->getDiagram(rect, diagram); + return diagram.save(fileName, imageType.upper().ascii()); +} diff --git a/umbrello/umbrello/umlviewimageexportermodel.h b/umbrello/umbrello/umlviewimageexportermodel.h new file mode 100644 index 00000000..d69796ac --- /dev/null +++ b/umbrello/umbrello/umlviewimageexportermodel.h @@ -0,0 +1,214 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLVIEWIMAGEEXPORTERMODEL_H +#define UMLVIEWIMAGEEXPORTERMODEL_H + +#include +#include + +// forward declarations +class UMLView; + +// KDE forward declarations +class KTempFile; +class KURL; + +/** + * Exports an UMLView in various image formats. + * It can also export all the views in the current document. + * + * The methods in this class don't communicate with the user, so asking the format + * to save the images in, checking if the target file exists and so on must be done before + * calling those methods, if needed. + * The only exception is asking passwords for example when KIO slaves are used, as this + * operation is made automatically by the KIO classes. + */ +class UMLViewImageExporterModel { +public: + + /** + * Returns a QStringList containing all the supported image types to use when exporting. + * All the types will be lower case. + * + * @return A QStringList containing all the supported image types to use when exporting. + */ + static QStringList supportedImageTypes(); + + /** + * Returns a QStringList containing all the supported mime types to use when exporting. + * All the types will be lower case. + * + * @return A QStringList containing all the supported mime types to use when exporting. + */ + static QStringList supportedMimeTypes(); + + /** + * Returns the mime type for an image type. + * The supported image types are those that the diagrams can be exported to. + * + * @param imageType The type of the image. + * @return A QString with the equivalent mime type, or QString::null if + * it's unknown. + */ + static QString imageTypeToMimeType(const QString& imageType); + + /** + * Returns the image type for a mime type. + * The supported image types are those that the diagrams can be exported to. + * + * @param mimeType The mime type. + * @return A lowercase QString with the equivalent image type, or QString::null + * if it's unknown. + */ + static QString mimeTypeToImageType(const QString& mimeType); + + /** + * Constructor for UMLViewImageExporterModel. + */ + UMLViewImageExporterModel() { + } + + /** + * Destructor for UMLViewImageExporterModel. + */ + virtual ~UMLViewImageExporterModel() { + } + + /** + * Exports all the views in the document to the directory specified in the url + * using the 'imageType' for the images. + * The name of the exported images will be like their view's name and using the + * 'imageType' as extension. + * + * The views are stored in folders in the document. The same tree structure used + * in the document to store the views can be created in the target directory with + * 'useFolders'. Only the folders made by the user are created in the target + * directory (Logical view, use case view and so on aren't created). + * + * This method creates the specified target directory if needed. If there was an + * existing file with the same path as one to be created overwrites it without asking. + * The url used can be local or remote, using supported KIO slaves. + * + * @param imageType The type of the images the views will be exported to. + * @param directory The url of the directory where the images will be saved. + * @param useFolders If the tree structure of the views in the document must be created + * in the target directory. + * @return A QStringList with all the error messages that occurred during export. + * If the list is empty, all the views were exported successfully. + */ + QStringList exportAllViews(const QString &imageType, const KURL &directory, bool useFolders) const; + + /** + * Exports the view to the url using the 'imageType' for the image. + * + * This method creates the needed directories, if any. If there was an existing + * file in the specified url overwrites it without asking. + * The url used can be local or remote, using supported KIO slaves. + * + * If some problem occurs when exporting, an error message is returned. + * + * @param view The view to export. + * @param imageType The type of the image the view will be exported to. + * @param url The url where the image will be saved. + * @return The message error if some problem occurred when exporting, or + * QString::null if all went fine. + */ + QString exportView(UMLView* view, const QString &imageType, const KURL &url) const; + +private: + + /** + * Returns the name of the file where the view will be exported to. + * The name of the exported images will be like their view's name and using the + * 'imageType' as extension. It can also include the parent folders of the view. + * + * The views are stored in folders in the document. The same tree structure used + * in the document to store the views can be created with 'useFolders', so the file name + * will include recursively also its parent folders. Only the folders made by the user + * are included in the file name (Logical view, use case view and so on aren't created). + * + * @param view The view to export. + * @param imageType The type of the image the view will be exported to. + * @param useFolders If the tree structure of the views in the document must be included + * in the file name. + * @return The name of the file where the view will be exported to. + */ + QString getDiagramFileName(UMLView *view, const QString &imageType, bool useFolders = false) const; + + /** + * Creates, if it doesn't exist, the directory to save the file. + * It also creates all the needed parent directories. + * + * @param url The url where the image will be saved. + * @return True if the operation was successful, + * false if the directory didn't exist and couldn't be created. + */ + bool prepareDirectory(const KURL &url) const; + + /** + * Exports the view to the file 'fileName' as the specified type. + * + * @param view The view to export. + * @param imageType The type of the image the view will be exported to. + * @param fileName The name of the file where the image will be saved. + * @return True if the operation was successful, + * false if a problem occurred while exporting. + */ + bool exportViewTo(UMLView* view, const QString &imageType, const QString &fileName) const; + + /** + * Exports the view to the file 'fileName' as EPS. + * + * @param view The view to export. + * @param fileName The name of the file where the image will be saved. + * @param isEPS The file is an eps file and needs adjusting + * of the eps bounding box values. + * @return True if the operation was successful, + * false if a problem occurred while exporting. + */ + bool exportViewToEps(UMLView* view, const QString &fileName, bool isEPS) const; + + /** + * Fix the file 'fileName' to be a valid EPS containing the + * specified area (rect) of the diagram. + * Corrects the bounding box. + * + * @return True if the operation was successful, + * false if a problem occurred while exporting. + */ + bool fixEPS(const QString &fileName, const QRect& rect) const; + + /** + * Exports the view to the file 'fileName' as SVG. + * + * @param view The view to export. + * @param fileName The name of the file where the image will be saved. + * @return True if the operation was successful, + * false if a problem occurred while exporting. + */ + bool exportViewToSvg(UMLView* view, const QString &fileName) const; + + /** + * Exports the view to the file 'fileName' as a pixmap of the specified type. + * The valid types are those supported by QPixmap save method. + * + * @param view The view to export. + * @param imageType The type of the image the view will be exported to. + * @param fileName The name of the file where the image will be saved. + * @return True if the operation was successful, + * false if a problem occurred while exporting. + */ + bool exportViewToPixmap(UMLView* view, const QString &imageType, const QString &fileName) const; + +}; + +#endif diff --git a/umbrello/umbrello/umlviewlist.h b/umbrello/umbrello/umlviewlist.h new file mode 100644 index 00000000..90d5e199 --- /dev/null +++ b/umbrello/umbrello/umlviewlist.h @@ -0,0 +1,29 @@ +/*************************************************************************** + umlviewlist.h - description + ------------------- + begin : Sat Dec 29 2001 + copyright : (C) 2001 by Gustavo Madrigal + email : gmadrigal@nextphere.com + Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef UMLVIEWLIST_H +#define UMLVIEWLIST_H + +//#include "umlview.h" + +class UMLView; + +typedef QPtrList UMLViewList; +typedef QPtrListIterator UMLViewListIt; + +#endif diff --git a/umbrello/umbrello/umlwidget.cpp b/umbrello/umbrello/umlwidget.cpp new file mode 100644 index 00000000..3e947cd1 --- /dev/null +++ b/umbrello/umbrello/umlwidget.cpp @@ -0,0 +1,1025 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header file +#include "umlwidget.h" +// system includes +#include +#include +#include +#include +#include +#include +// local includes +#include "umlwidgetcontroller.h" +#include "umlobject.h" +#include "classifier.h" +#include "uniqueid.h" +#include "uml.h" +#include "umldoc.h" +#include "umlview.h" +#include "umlclassifierlistitemlist.h" +#include "codegenerator.h" +#include "codegenerators/simplecodegenerator.h" +#include "listpopupmenu.h" +#include "associationwidget.h" +#include "dialogs/settingsdlg.h" +#include "codedocument.h" +#include "floatingtextwidget.h" +#include "docwindow.h" +#include "dialogs/classpropdlg.h" +#include "clipboard/idchangelog.h" + +using namespace Uml; + + +UMLWidget::UMLWidget( UMLView * view, UMLObject * o, UMLWidgetController *widgetController /* = 0*/ ) + : WidgetBase(view), QCanvasRectangle( view->canvas() ), + m_pMenu(0) +{ + if (widgetController) { + m_widgetController = widgetController; + } else { + m_widgetController = new UMLWidgetController(this); + } + init(); + m_pObject = o; + if(m_pObject) { + connect( m_pObject, SIGNAL(modified()), this, SLOT(updateWidget()) ); + m_nId = m_pObject->getID(); + } +} + +UMLWidget::UMLWidget(UMLView * view, Uml::IDType id /* = Uml::id_None */, UMLWidgetController *widgetController /* = 0*/) + : WidgetBase(view), QCanvasRectangle( view->canvas() ), + m_pMenu(0) +{ + if (widgetController) { + m_widgetController = widgetController; + } else { + m_widgetController = new UMLWidgetController(this); + } + init(); + if (id == Uml::id_None) + m_nId = UniqueID::gen(); + else + m_nId = id; +} + +UMLWidget::~UMLWidget() { + //slotRemovePopupMenu(); + delete m_widgetController; + cleanup(); +} + +UMLWidget& UMLWidget::operator=(const UMLWidget& other) { + if (this == &other) + return *this; + + // assign members loaded/saved + m_bUseFillColour = other.m_bUseFillColour; + m_nId = other.m_nId; + m_Type = other.m_Type; + setX( other.getX() ); + setY( other.getY() ); + m_Assocs = other.m_Assocs; + m_Font = other.m_Font; + QCanvasRectangle::setSize( other.width(), other.height() ); + m_bUsesDiagramFillColour = other.m_bUsesDiagramFillColour; + m_bUsesDiagramLineColour = other.m_bUsesDiagramLineColour; + m_bUsesDiagramLineWidth = other.m_bUsesDiagramLineWidth; + m_bUsesDiagramUseFillColour = other.m_bUsesDiagramUseFillColour; + m_LineColour = other.m_LineColour; + m_LineWidth = other.m_LineWidth; + m_FillColour = other.m_FillColour; + m_bIsInstance = other.m_bIsInstance; + m_instanceName = other.m_instanceName; + + // assign volatile (non-saved) members + m_bSelected = other.m_bSelected; + m_bStartMove = other.m_bStartMove; + m_nPosX = other.m_nPosX; + m_pObject = other.m_pObject; + m_pView = other.m_pView; + m_pMenu = other.m_pMenu; + m_bResizable = other.m_bResizable; + for (unsigned i = 0; i < FT_INVALID; i++) + m_pFontMetrics[i] = other.m_pFontMetrics[i]; + m_bActivated = other.m_bActivated; + m_bIgnoreSnapToGrid = other.m_bIgnoreSnapToGrid; + m_bIgnoreSnapComponentSizeToGrid = other.m_bIgnoreSnapComponentSizeToGrid; + return *this; +} + +bool UMLWidget::operator==(const UMLWidget& other) { + if( this == &other ) + return true; + + if(m_Type != other.m_Type) { + return false; + } + + if (getID() != other.getID()) + return false; + + /* Testing the associations is already an exaggeration, no? + The type and ID should uniquely identify an UMLWidget. + */ + if (m_Assocs.count() != other.m_Assocs.count()) { + return false; + } + + // if(getBaseType() != wt_Text) // DON'T do this for floatingtext widgets, an infinite loop will result + // { + AssociationWidgetListIt assoc_it( m_Assocs ); + AssociationWidgetListIt assoc_it2( other.m_Assocs ); + AssociationWidget * assoc = 0, *assoc2 = 0; + while ( ((assoc=assoc_it.current()) != 0) && ((assoc2=assoc_it2.current()) != 0)) { + ++assoc_it; + ++assoc_it2; + if(!(*assoc == *assoc2)) { + return false; + } + } + // } + return true; + // NOTE: In the comparison tests we are going to do, we don't need these values. + // They will actually stop things functioning correctly so if you change these, be aware of that. + /* + if(m_bUseFillColour != other.m_bUseFillColour) + return false; + if(m_nId != other.m_nId) + return false; + if( m_Font != other.m_Font ) + return false; + if(m_nX != other.m_nX) + return false; + if(m_nY != other.m_nY) + return false; + */ +} + +void UMLWidget::mouseMoveEvent(QMouseEvent* me) { + m_widgetController->mouseMoveEvent(me); +} + +void UMLWidget::mousePressEvent(QMouseEvent *me) { + m_widgetController->mousePressEvent(me); +} + +void UMLWidget::updateWidget() +{ + updateComponentSize(); + adjustAssocs( getX(), getY() ); //adjust assoc lines. + if (m_Type == Uml::wt_Class) { + m_pView->createAutoAttributeAssociations(this); + } + if(isVisible()) + update(); +} + +QSize UMLWidget::calculateSize() { + return QSize(20, 20); +} + +void UMLWidget::constrain(int& width, int& height) { + const QSize minSize = calculateSize(); + if (width < minSize.width()) + width = minSize.width(); + if (height < minSize.height()) + height = minSize.height(); +} + +void UMLWidget::mouseReleaseEvent(QMouseEvent *me) { + m_widgetController->mouseReleaseEvent(me); +} + +void UMLWidget::init() { + m_nId = Uml::id_None; + m_bIsInstance = false; + if (m_pView) { + m_bUseFillColour = true; + m_bUsesDiagramFillColour = true; + m_bUsesDiagramUseFillColour = true; + const Settings::OptionState& optionState = m_pView->getOptionState(); + m_FillColour = optionState.uiState.fillColor; + m_Font = optionState.uiState.font; + m_bShowStereotype = optionState.classState.showStereoType; + } else { + kError() << "UMLWidget::init: SERIOUS PROBLEM - m_pView is NULL" << endl; + m_bUseFillColour = false; + m_bUsesDiagramFillColour = false; + m_bUsesDiagramUseFillColour = false; + m_bShowStereotype = false; + } + + for (int i = 0; i < (int)FT_INVALID; ++i) + m_pFontMetrics[(UMLWidget::FontType)i] = 0; + + m_bResizable = true; + + m_bSelected = false; + m_bStartMove = false; + m_bActivated = false; + m_bIgnoreSnapToGrid = false; + m_bIgnoreSnapComponentSizeToGrid = false; + m_pMenu = 0; + m_pDoc = UMLApp::app()->getDocument(); + m_nPosX = 0; + connect( m_pView, SIGNAL( sigRemovePopupMenu() ), this, SLOT( slotRemovePopupMenu() ) ); + connect( m_pView, SIGNAL( sigClearAllSelected() ), this, SLOT( slotClearAllSelected() ) ); + + connect( m_pView, SIGNAL(sigColorChanged(Uml::IDType)), this, SLOT(slotColorChanged(Uml::IDType))); + connect( m_pView, SIGNAL(sigLineWidthChanged(Uml::IDType)), this, SLOT(slotLineWidthChanged(Uml::IDType))); + + + // connect( m_pView, SIGNAL(sigColorChanged(int)), this, SLOT(slotColorChanged(int))); + m_pObject = NULL; + setZ(m_origZ = 2); // default for most widgets +} + +void UMLWidget::slotMenuSelection(int sel) { + QFont font; + QColor newColour; + const Uml::Widget_Type wt = m_Type; + UMLWidget* widget = 0; // use for select the first object properties (fill, line color) + + switch(sel) { + case ListPopupMenu::mt_Rename: + m_pDoc -> renameUMLObject(m_pObject); + // adjustAssocs( getX(), getY() );//adjust assoc lines + break; + + case ListPopupMenu::mt_Delete: + //remove self from diagram + m_pView -> removeWidget(this); + break; + + //UMLWidgetController::doMouseDoubleClick relies on this implementation + case ListPopupMenu::mt_Properties: + if (wt == wt_Actor || wt == wt_UseCase || + wt == wt_Package || wt == wt_Interface || wt == wt_Datatype || + wt == wt_Component || wt == wt_Artifact || + wt == wt_Node || wt == wt_Enum || wt == wt_Entity || + (wt == wt_Class && m_pView -> getType() == dt_Class)) { + showProperties(); + } else if (wt == wt_Object) { + m_pObject->showProperties(); + } else { + kWarning() << "making properties dialog for unknown widget type" << endl; + } + // adjustAssocs( getX(), getY() );//adjust assoc lines + break; + + case ListPopupMenu::mt_Line_Color: + case ListPopupMenu::mt_Line_Color_Selection: + widget = m_pView->getFirstMultiSelectedWidget(); + if (widget) { newColour = widget->getLineColor(); } + if( KColorDialog::getColor(newColour) ) { + m_pView -> selectionSetLineColor( newColour ); + m_pDoc -> setModified(true); + } + break; + + case ListPopupMenu::mt_Fill_Color: + case ListPopupMenu::mt_Fill_Color_Selection: + widget = m_pView->getFirstMultiSelectedWidget(); + if (widget) { newColour = widget->getFillColour(); } + if ( KColorDialog::getColor(newColour) ) { + m_pView -> selectionSetFillColor( newColour ); + m_pDoc -> setModified(true); + } + break; + + case ListPopupMenu::mt_Use_Fill_Color: + m_bUseFillColour = !m_bUseFillColour; + m_bUsesDiagramUseFillColour = false; + m_pView->selectionUseFillColor( m_bUseFillColour ); + break; + case ListPopupMenu::mt_Show_Attributes_Selection: + case ListPopupMenu::mt_Show_Operations_Selection: + case ListPopupMenu::mt_Visibility_Selection: + case ListPopupMenu::mt_DrawAsCircle_Selection: + case ListPopupMenu::mt_Show_Operation_Signature_Selection: + case ListPopupMenu::mt_Show_Attribute_Signature_Selection: + case ListPopupMenu::mt_Show_Packages_Selection: + case ListPopupMenu::mt_Show_Stereotypes_Selection: + case ListPopupMenu::mt_Show_Public_Only_Selection: + m_pView->selectionToggleShow(sel); + m_pDoc->setModified(true); + break; + + case ListPopupMenu::mt_ViewCode: { + UMLClassifier *c = dynamic_cast(m_pObject); + if(c) + { + UMLApp::app()->viewCodeDocument(c); + } + break; + } + + case ListPopupMenu::mt_Delete_Selection: + m_pView -> deleteSelection(); + break; + + case ListPopupMenu::mt_Change_Font: + font = getFont(); + if( KFontDialog::getFont( font, false, m_pView ) ) + { + setFont( font ); + m_pDoc->setModified(true); + } + break; + + case ListPopupMenu::mt_Change_Font_Selection: + font = getFont(); + if( KFontDialog::getFont( font, false, m_pView ) ) + { + m_pView -> selectionSetFont( font ); + m_pDoc->setModified(true); + } + break; + + case ListPopupMenu::mt_Cut: + m_pView -> setStartedCut(); + UMLApp::app() -> slotEditCut(); + break; + + case ListPopupMenu::mt_Copy: + UMLApp::app() -> slotEditCopy(); + break; + + case ListPopupMenu::mt_Paste: + UMLApp::app() -> slotEditPaste(); + break; + + case ListPopupMenu::mt_Refactoring: + //check if we are operating on a classifier, or some other kind of UMLObject + if(dynamic_cast(m_pObject)) + { + UMLApp::app()->refactor(static_cast(m_pObject)); + } + break; + + case ListPopupMenu::mt_Clone: + // In principle we clone all the uml objects. + { + UMLObject *pClone = m_pObject->clone(); + m_pView->addObject(pClone); + } + break; + + case ListPopupMenu::mt_Rename_MultiA: + case ListPopupMenu::mt_Rename_MultiB: + case ListPopupMenu::mt_Rename_Name: + case ListPopupMenu::mt_Rename_RoleAName: + case ListPopupMenu::mt_Rename_RoleBName: + { + FloatingTextWidget *ft = static_cast(this); + ft->handleRename(); + break; + } + } +} + +void UMLWidget::slotWidgetMoved(Uml::IDType /*id*/) {} + +void UMLWidget::slotColorChanged(Uml::IDType viewID) { + //only change if on the diagram concerned + if(m_pView->getID() != viewID) { + return; + } + if ( m_bUsesDiagramFillColour ) { + m_FillColour = m_pView->getFillColor(); + } + if ( m_bUsesDiagramLineColour ) { + m_LineColour = m_pView->getLineColor(); + } + if ( m_bUsesDiagramUseFillColour ) { + m_bUseFillColour = m_pView->getUseFillColor(); + } + update(); +} + +void UMLWidget::slotLineWidthChanged(Uml::IDType viewID) { + //only change if on the diagram concerned + if(m_pView->getID() != viewID) { + return; + } + if ( m_bUsesDiagramLineWidth ) { + m_LineWidth = m_pView->getLineWidth(); + } + update(); +} + +void UMLWidget::mouseDoubleClickEvent( QMouseEvent * me ) { + m_widgetController->mouseDoubleClickEvent(me); +} + +void UMLWidget::setUseFillColour(bool fc) { + m_bUseFillColour = fc; + m_bUsesDiagramUseFillColour = false; + update(); +} + +void UMLWidget::setLineColor(const QColor &colour) { + WidgetBase::setLineColor(colour); + update(); +} + +void UMLWidget::setLineWidth(uint width) { + WidgetBase::setLineWidth(width); + update(); +} + +void UMLWidget::setFillColour(const QColor &colour) { + m_FillColour = colour; + m_bUsesDiagramFillColour = false; + update(); +} + +void UMLWidget::drawSelected(QPainter * p, int offsetX, int offsetY) { + int w = width(); + int h = height(); + int s = 4; + QBrush brush(Qt::blue); + p -> fillRect(offsetX, offsetY, s, s, brush); + p -> fillRect(offsetX, offsetY + h - s, s, s, brush); + p -> fillRect(offsetX + w - s, offsetY, s, s, brush); + + // Draw the resize anchor in the lower right corner. + if (m_bResizable) { + brush.setColor(Qt::red); + const int right = offsetX + w; + const int bottom = offsetY + h; + p->drawLine(right - s, offsetY + h - 1, offsetX + w - 1, offsetY + h - s); + p->drawLine(right - (s*2), bottom - 1, right - 1, bottom - (s*2) ); + p->drawLine(right - (s*3), bottom - 1, right - 1, bottom - (s*3) ); + } else { + p->fillRect(offsetX + w - s, offsetY + h - s, s, s, brush); + } +} + +bool UMLWidget::activate(IDChangeLog* /*ChangeLog = 0 */) { + if (widgetHasUMLObject(m_Type) && m_pObject == NULL) { + m_pObject = m_pDoc->findObjectById(m_nId); + if (m_pObject == NULL) { + kError() << "UMLWidget::activate: cannot find UMLObject with id=" + << ID2STR(m_nId) << endl; + return false; + } + } + setFont(m_Font); + setSize(getWidth(), getHeight()); + m_bActivated = true; + updateComponentSize(); + if (m_pView->getPaste()) { + FloatingTextWidget * ft = 0; + QPoint point = m_pView -> getPastePoint(); + int x = point.x() + getX(); + int y = point.y() + getY(); + x = x < 0?0:x; + y = y < 0?0:y; + if( m_pView -> getType() == dt_Sequence ) { + switch( getBaseType() ) { + case wt_Object: + case wt_Message: + setY( getY() ); + setX( x ); + break; + + case wt_Text: + ft = static_cast( this ); + if (ft->getRole() == tr_Seq_Message) { + setX( x ); + setY( getY() ); + } else { + setX( getX() ); + setY( getY() ); + } + break; + + default: + setY( y ); + break; + }//end switch base type + }//end if sequence + else { + setX( x ); + setY( y ); + } + }//end if pastepoint + else { + setX( getX() ); + setY( getY() ); + } + if ( m_pView -> getPaste() ) + m_pView -> createAutoAssociations( this ); + updateComponentSize(); + return true; +} + +/** Read property of bool m_bActivated. */ +bool UMLWidget::isActivated() { + return m_bActivated; +} + +void UMLWidget::setActivated(bool Active /*=true*/) { + m_bActivated = Active; +} + +void UMLWidget::addAssoc(AssociationWidget* pAssoc) { + if (pAssoc && !m_Assocs.contains(pAssoc)) { + m_Assocs.append(pAssoc); + } +} + +void UMLWidget::removeAssoc(AssociationWidget* pAssoc) { + if(pAssoc) { + m_Assocs.remove(pAssoc); + } +} + +void UMLWidget::adjustAssocs(int x, int y) +{ + // 2004-04-30: Achim Spangler + // don't adjust Assocs on file load, as + // the original positions, which are stored in XMI + // should be reproduced exactly + // ( don't try to reposition assocs as long + // as file is only partly loaded -> reposition + // could be misguided ) + /// @todo avoid trigger of this event during load + if ( m_pDoc->loading() ) { + // don't recalculate the assocs during load of XMI + // -> return immediately without action + return; + } + AssociationWidgetListIt assoc_it(m_Assocs); + AssociationWidget* assocwidget = 0; + while ((assocwidget = assoc_it.current())) { + ++assoc_it; + assocwidget->saveIdealTextPositions(); + } + assoc_it.toFirst(); + while ((assocwidget = assoc_it.current())) { + ++assoc_it; + assocwidget->widgetMoved(this, x, y); + } +} + +void UMLWidget::adjustUnselectedAssocs(int x, int y) +{ + AssociationWidgetListIt assoc_it(m_Assocs); + AssociationWidget* assocwidget = 0; + while ((assocwidget = assoc_it.current())) { + ++assoc_it; + if(!assocwidget->getSelected()) + assocwidget->saveIdealTextPositions(); + } + assoc_it.toFirst(); + while ((assocwidget = assoc_it.current())) { + ++assoc_it; + if(!assocwidget->getSelected()) + assocwidget->widgetMoved(this, x, y); + } +} + +void UMLWidget::showProperties() { + // will already be selected so make sure docWindow updates the doc + // back it the widget + DocWindow *docwindow = UMLApp::app()->getDocWindow(); + docwindow->updateDocumentation( false ); + ClassPropDlg *dlg = new ClassPropDlg((QWidget*)UMLApp::app(), this); + + if (dlg->exec()) { + docwindow->showDocumentation( getUMLObject() , true ); + m_pDoc->setModified(true); + } + dlg->close(true); //wipe from memory +} + +void UMLWidget::startPopupMenu( const QPoint &At) { + slotRemovePopupMenu(); + + //if in a multi- selection to a specific m_pMenu for that + // NEW: ask UMLView to count ONLY the widgets and not their floatingtextwidgets + int count = m_pView->getSelectCount(true); + //a MessageWidget when selected will select its text widget and vice versa + //so take that into account for popup menu. + + // determine multi state + bool multi = (m_bSelected && count > 1); + + // if multiple selected items have the same type + bool unique = false; + + // if multiple items are selected, we have to check if they all have the same + // base type + if (multi == true) + unique = m_pView -> checkUniqueSelection(); + + // create the right click context menu + m_pMenu = new ListPopupMenu(m_pView, this, multi, unique); + + // disable the "view code" menu for simple code generators + CodeGenerator * currentCG = UMLApp::app()->getGenerator(); + if(currentCG && dynamic_cast(currentCG)) + m_pMenu->setItemEnabled(ListPopupMenu::mt_ViewCode, false); + + m_pMenu->popup(At); + + connect(m_pMenu, SIGNAL(activated(int)), this, SLOT(slotMenuSelection(int))); +} + +void UMLWidget::slotRemovePopupMenu() { + if(m_pMenu) { + disconnect(m_pMenu, SIGNAL(activated(int)), this, SLOT(slotMenuSelection(int))); + delete m_pMenu; + m_pMenu = 0; + } +} + +int UMLWidget::onWidget(const QPoint & p) { + const int w = width(); + const int h = height(); + const int left = getX(); + const int right = left + w; + const int top = getY(); + const int bottom = top + h; + if (p.x() < left || p.x() > right || + p.y() < top || p.y() > bottom) // Qt coord.sys. origin in top left corner + return 0; + return (w + h) / 2; +} + +void UMLWidget::moveBy(int dx, int dy) { + int newX = getX() + dx; + int newY = getY() + dy; + setX(newX); + setY(newY); + adjustAssocs(newX, newY); +} + +void UMLWidget::setPen(QPainter & p) { + p.setPen( QPen(m_LineColour, m_LineWidth) ); +} + +void UMLWidget::drawShape(QPainter &p ) { + draw( p, getX(), getY() ); +} + +void UMLWidget::setSelected(bool _select) { + const Uml::Widget_Type wt = m_Type; + if( _select ) { + if( m_pView -> getSelectCount() == 0 ) { + if ( widgetHasUMLObject(wt) ) { + m_pView->showDocumentation(m_pObject, false); + } else { + m_pView->showDocumentation(this, false); + } + }//end if + /* if (wt != wt_Text && wt != wt_Box) { + setZ(9);//keep text on top and boxes behind so don't touch Z value + } */ + } else { + /* if (wt != wt_Text && wt != wt_Box) { + setZ(m_origZ); + } */ + if( m_bSelected ) + m_pView -> updateDocumentation( true ); + } + m_bSelected = _select; + + const QPoint pos(getX(), getY()); + UMLWidget *bkgnd = m_pView->getWidgetAt(pos); + if (bkgnd && bkgnd != this && _select) { + kDebug() << "UMLWidget::setSelected: setting Z to " + << bkgnd->getZ() + 1 << ", SelectState: " << _select << endl; + setZ( bkgnd->getZ() + 1 ); + } else { + setZ( m_origZ ); + } + + update(); + + /* selection changed, we have to make sure the copy and paste items + * are correctly enabled/disabled */ + UMLApp::app()->slotCopyChanged(); +} + +void UMLWidget::slotClearAllSelected() +{ + setSelected( false ); +} + +void UMLWidget::setView(UMLView * v) { + //remove signals from old view - was probably 0 anyway + disconnect( m_pView, SIGNAL( sigRemovePopupMenu() ), this, SLOT( slotRemovePopupMenu() ) ); + disconnect( m_pView, SIGNAL( sigClearAllSelected() ), this, SLOT( slotClearAllSelected() ) ); + disconnect( m_pView, SIGNAL(sigColorChanged(Uml::IDType)), this, SLOT(slotColorChanged(Uml::IDType))); + disconnect( m_pView, SIGNAL(sigLineWidthChanged(Uml::IDType)), this, SLOT(slotLineWidthChanged(Uml::IDType))); + m_pView = v; + connect( m_pView, SIGNAL( sigRemovePopupMenu() ), this, SLOT( slotRemovePopupMenu() ) ); + connect( m_pView, SIGNAL( sigClearAllSelected() ), this, SLOT( slotClearAllSelected() ) ); + connect( m_pView, SIGNAL(sigColorChanged(Uml::IDType)), this, SLOT(slotColorChanged(Uml::IDType))); + connect( m_pView, SIGNAL(sigLineWidthChanged(Uml::IDType)), this, SLOT(slotLineWidthChanged(Uml::IDType))); +} + +void UMLWidget::setX( int x ) { + if (!m_bIgnoreSnapToGrid) { + x = m_pView->snappedX(x); + } + QCanvasItem::setX( (double)x ); +} + +void UMLWidget::setY( int y ) { + if (!m_bIgnoreSnapToGrid){ + y = m_pView->snappedX(y); + } + QCanvasItem::setY( (double)y ); +} + +void UMLWidget::setZ(int z) { + m_origZ = getZ(); + QCanvasItem::setZ(z); +} + +void UMLWidget::setName(const QString &strName) { + if (m_pObject) + m_pObject->setName(strName); + else + m_Text = strName; + updateComponentSize(); + adjustAssocs( getX(), getY() ); +} + +QString UMLWidget::getName() const { + if (m_pObject) + return m_pObject->getName(); + return m_Text; +} + +void UMLWidget::cleanup() { +} + +void UMLWidget::slotSnapToGrid( ) { + setX( getX() ); + setY( getY() ); +} + +bool UMLWidget::widgetHasUMLObject(Uml::Widget_Type type) { + if (type == wt_Actor || + type == wt_UseCase || + type == wt_Class || + type == wt_Interface || + type == wt_Enum || + type == wt_Datatype || + type == wt_Package || + type == wt_Component || + type == wt_Node || + type == wt_Artifact || + type == wt_Object) { + return true; + } else { + return false; + } +} + +void UMLWidget::setIgnoreSnapToGrid(bool to) { + m_bIgnoreSnapToGrid = to; +} + +bool UMLWidget::getIgnoreSnapToGrid() const { + return m_bIgnoreSnapToGrid; +} + +void UMLWidget::setSize(int width,int height) { + // snap to the next larger size that is a multiple of the grid + if (!m_bIgnoreSnapComponentSizeToGrid + && m_pView -> getSnapComponentSizeToGrid() ) + { + // integer divisions + int numX = width / m_pView->getSnapX(); + int numY = height / m_pView->getSnapY(); + // snap to the next larger valid value + if (width > numX * m_pView->getSnapX()) + width = (numX + 1) * m_pView->getSnapX(); + if (height > numY * m_pView->getSnapY()) + height = (numY + 1) * m_pView->getSnapY(); + } + + QCanvasRectangle::setSize(width,height); +} + +void UMLWidget::updateComponentSize() { + if (m_pDoc->loading()) + return; + const QSize minSize = calculateSize(); + const int w = minSize.width(); + const int h = minSize.height(); + setSize(w, h); + adjustAssocs( getX(), getY() ); // adjust assoc lines +} + +void UMLWidget::setDefaultFontMetrics(UMLWidget::FontType fontType) { + setupFontType(m_Font, fontType); + setFontMetrics(fontType, QFontMetrics(m_Font)); +} + +void UMLWidget::setupFontType(QFont &font, UMLWidget::FontType fontType) { + switch(fontType){ + case FT_NORMAL: + font.setBold(false); + font.setItalic(false); + font.setUnderline(false); + break; + case FT_BOLD: + font.setBold(true); + font.setItalic(false); + font.setUnderline(false); + break; + case FT_ITALIC: + font.setBold(false); + font.setItalic(true); + font.setUnderline(false); + break; + case FT_UNDERLINE: + font.setBold(false); + font.setItalic(false); + font.setUnderline(true); + break; + case FT_BOLD_ITALIC: + font.setBold(true); + font.setItalic(true); + font.setUnderline(false); + break; + case FT_BOLD_UNDERLINE: + font.setBold(true); + font.setItalic(false); + font.setUnderline(true); + break; + case FT_ITALIC_UNDERLINE: + font.setBold(false); + font.setItalic(true); + font.setUnderline(true); + break; + case FT_BOLD_ITALIC_UNDERLINE: + font.setBold(true); + font.setItalic(true); + font.setUnderline(true); + break; + default: return; + } +} + +void UMLWidget::setDefaultFontMetrics(UMLWidget::FontType fontType, QPainter &painter) { + setupFontType(m_Font, fontType); + painter.setFont(m_Font); + setFontMetrics(fontType, painter.fontMetrics()); +} + +//FIXME this is probably the source of problems with widgets not being wide enough +QFontMetrics &UMLWidget::getFontMetrics(UMLWidget::FontType fontType) { + if (m_pFontMetrics[fontType] == 0) { + setDefaultFontMetrics(fontType); + } + return *m_pFontMetrics[fontType]; +} + +void UMLWidget::setFontMetrics(UMLWidget::FontType fontType, QFontMetrics fm) { + delete m_pFontMetrics[fontType]; + m_pFontMetrics[fontType] = new QFontMetrics(fm); +} + +QFont UMLWidget::getFont() const { + return m_Font; +} + +void UMLWidget::setFont( QFont font ) { + m_Font = font; + forceUpdateFontMetrics(0); + if (m_pDoc->loading()) + return; + update(); +} + +void UMLWidget::forceUpdateFontMetrics(QPainter *painter) { + if (painter == 0) { + for (int i = 0; i < (int)UMLWidget::FT_INVALID; ++i) { + if (m_pFontMetrics[(UMLWidget::FontType)i]!=0) + setDefaultFontMetrics((UMLWidget::FontType)i); + } + } else { + for (int i2 = 0; i2 < (int)UMLWidget::FT_INVALID; ++i2) { + if (m_pFontMetrics[(UMLWidget::FontType)i2]!=0) + setDefaultFontMetrics((UMLWidget::FontType)i2,*painter); + } + } + // calculate the size, based on the new font metric + updateComponentSize(); +} + +void UMLWidget::setShowStereotype(bool _status) { + m_bShowStereotype = _status; + updateComponentSize(); + update(); +} + +bool UMLWidget::getShowStereotype() const { + return m_bShowStereotype; +} + +void UMLWidget::moveEvent(QMoveEvent* /*me*/) { +} + +void UMLWidget::saveToXMI( QDomDocument & qDoc, QDomElement & qElement ) { + /* + Call after required actions in child class. + Type must be set in the child class. + */ + WidgetBase::saveToXMI(qDoc, qElement); + qElement.setAttribute( "xmi.id", ID2STR(getID()) ); + qElement.setAttribute( "font", m_Font.toString() ); + qElement.setAttribute( "usefillcolor", m_bUseFillColour ); + qElement.setAttribute( "x", getX() ); + qElement.setAttribute( "y", getY() ); + qElement.setAttribute( "width", getWidth() ); + qElement.setAttribute( "height", getHeight() ); + // for consistency the following attributes now use american spelling for "color" + qElement.setAttribute( "usesdiagramfillcolor", m_bUsesDiagramFillColour ); + qElement.setAttribute( "usesdiagramusefillcolor", m_bUsesDiagramUseFillColour ); + if (m_bUsesDiagramFillColour) { + qElement.setAttribute( "fillcolor", "none" ); + } else { + qElement.setAttribute( "fillcolor", m_FillColour.name() ); + } + qElement.setAttribute("isinstance", m_bIsInstance); + if (!m_instanceName.isEmpty()) + qElement.setAttribute("instancename", m_instanceName); + if (m_bShowStereotype) + qElement.setAttribute("showstereotype", m_bShowStereotype); +} + +bool UMLWidget::loadFromXMI( QDomElement & qElement ) { + WidgetBase::loadFromXMI(qElement); + QString id = qElement.attribute( "xmi.id", "-1" ); + QString font = qElement.attribute( "font", "" ); + QString usefillcolor = qElement.attribute( "usefillcolor", "1" ); + QString x = qElement.attribute( "x", "0" ); + QString y = qElement.attribute( "y", "0" ); + QString h = qElement.attribute( "height", "0" ); + QString w = qElement.attribute( "width", "0" ); + /* + For the next three *color attributes, there was a mixup of american and english spelling for "color". + So first we need to keep backward compatibility and try to retrieve the *colour attribute. + Next we overwrite this value if we find a *color, otherwise the former *colour is kept. + */ + QString fillColour = qElement.attribute( "fillcolour", "none" ); + fillColour = qElement.attribute( "fillcolor", fillColour ); + QString usesDiagramFillColour = qElement.attribute( "usesdiagramfillcolour", "1" ); + usesDiagramFillColour = qElement.attribute( "usesdiagramfillcolor", usesDiagramFillColour ); + QString usesDiagramUseFillColour = qElement.attribute( "usesdiagramusefillcolour", "1" ); + usesDiagramUseFillColour = qElement.attribute( "usesdiagramusefillcolor", usesDiagramUseFillColour ); + + m_nId = STR2ID(id); + + if( !font.isEmpty() ) { + //QFont newFont; + m_Font.fromString(font); + //setFont(newFont); + } else { + kWarning() << "Using default font " << m_Font.toString() + << " for widget with xmi.id " << ID2STR(m_nId) << endl; + //setFont( m_Font ); + } + m_bUseFillColour = (bool)usefillcolor.toInt(); + m_bUsesDiagramFillColour = (bool)usesDiagramFillColour.toInt(); + m_bUsesDiagramUseFillColour = (bool)usesDiagramUseFillColour.toInt(); + setSize( w.toInt(), h.toInt() ); + setX( x.toInt() ); + setY( y.toInt() ); + if (fillColour != "none") { + m_FillColour = QColor(fillColour); + } + QString isinstance = qElement.attribute("isinstance", "0"); + m_bIsInstance = (bool)isinstance.toInt(); + m_instanceName = qElement.attribute("instancename", ""); + QString showstereo = qElement.attribute("showstereotype", "0"); + m_bShowStereotype = (bool)showstereo.toInt(); + return true; +} + +UMLWidgetController* UMLWidget::getWidgetController() { + return m_widgetController; +} + +#include "umlwidget.moc" diff --git a/umbrello/umbrello/umlwidget.h b/umbrello/umbrello/umlwidget.h new file mode 100644 index 00000000..7ed472a9 --- /dev/null +++ b/umbrello/umbrello/umlwidget.h @@ -0,0 +1,728 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLWIDGET_H +#define UMLWIDGET_H + +#include +#include +#include + +#include "umlnamespace.h" +#include "widgetbase.h" +#include "associationwidgetlist.h" +#include "optionstate.h" + +class UMLWidgetController; + +class UMLObject; +class UMLView; +class UMLDoc; +class ListPopupMenu; +class IDChangeLog; + +class QPainter; +class QFont; +class QFontMetrics; + +/** + * This is the base class for nearly all graphical widgets. + * + * @short The base class for graphical UML objects. + * @author Paul Hensgen + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class UMLWidget : public WidgetBase, public QCanvasRectangle { + Q_OBJECT +public: + friend class UMLWidgetController; + + /** + * Creates a UMLWidget object. + * + * @param view The view to be displayed on. + * @param o The UMLObject to represent. + * @param widgetController The UMLWidgetController of this UMLWidget + */ + UMLWidget( UMLView * view, UMLObject * o, UMLWidgetController *widgetController = 0 ); + + /** + * Creates a UMLWidget object. + * + * @param view The view to be displayed on. + * @param id The id of the widget. + * The default value (id_None) will prompt generation of a new ID. + * @param widgetController The UMLWidgetController of this UMLWidget + */ + explicit UMLWidget( UMLView * view, Uml::IDType id = Uml::id_None, UMLWidgetController *widgetController = 0 ); + + /** + * Standard deconstructor + */ + virtual ~UMLWidget(); + + /** + * Copy constructor + */ + UMLWidget(const UMLWidget& other); + + /** + * Assignment operator + */ + virtual UMLWidget& operator=(const UMLWidget& other); + + /** + * Overload '==' operator + */ + virtual bool operator==(const UMLWidget& other); + + /** + * Calls the method with the same name in UMLWidgetController. + * @see UMLWidgetController#mouseReleaseEvent + * + * @param me The QMouseEvent event. + */ + virtual void mouseReleaseEvent(QMouseEvent * me); + + /** + * Calls the method with the same name in UMLWidgetController. + * @see UMLWidgetController#mouseDoubleClickEvent + * + * @param me The QMouseEvent event. + */ + virtual void mouseDoubleClickEvent(QMouseEvent *me); + + /** + * Set the status of using fill color. + * + * @param fc the status of using fill color. + */ + void setUseFillColour(bool fc); + + /** + * Read property of bool m_bUseFillColour. + */ + bool getUseFillColour() const { + return m_bUseFillColour; + } + + /** + * Overrides the method from WidgetBase. + */ + void setLineColor(const QColor &colour); + + /** + * Overrides the method from WidgetBase. + */ + void setLineWidth(uint width); + + /** + * Sets the background fill colour + * + * @param colour the new fill colour + */ + void setFillColour(const QColor &colour); + + /** + * Read property of QColor m_FillColour. + */ + QColor getFillColour() const { + return m_FillColour; + } + + /** + * Calls the method with the same name in UMLWidgetController. + * @see UMLWidgetController#mouseMoveEvent + * + * @param me The QMouseEvent event. + */ + virtual void mouseMoveEvent(QMouseEvent* me); + + /** + * Returns whether this is a line of text. + * Used for transparency in printing. + * + * @return always false + */ + virtual bool isText() { + return false; + } + + /** + * Sets the state of whether the widget is selected. + * + * @param _select The state of whether the widget is selected. + */ + virtual void setSelected(bool _select); + + /** + * Returns the state of whether the widget is selected. + * + * @return Returns the state of whether the widget is selected. + */ + bool getSelected() const { + return m_bSelected; + } + + void setSelectedFlag(bool _select) { + m_bSelected = _select; + } + + /** + * Sets the view the widget is on. + * + * @param v The view the widget is on. + */ + void setView(UMLView * v); + + /** + * Activate the object after serializing it from a QDataStream + * + * @param ChangeLog + * @return true for success + */ + virtual bool activate(IDChangeLog* ChangeLog = 0); + + /** + * Returns 0 if the given point is not in the boundaries of the widget, + * else returns a number which is proportional to the size of the widget. + * + * @param p Point to be checked. + * + * @return 0 if the given point is not in the boundaries of the widget; + * (width()+height())/2 if the point is within the boundaries. + */ + virtual int onWidget(const QPoint & p); + + /** + * Draws the UMLWidget on the given paint device + * + * @param p The painter for the drawing device + * @param offsetX x position to start the drawing. + * @param offsetY y position to start the drawing. + * + */ + virtual void draw(QPainter & p, int offsetX, int offsetY) = 0; + + /** + * Set the pen. + */ + void setPen(QPainter & p); + + /** + * Sets the font the widget is to use. + * + * @param font Font to be set. + */ + virtual void setFont( QFont font ); + + /** + * Returns the font the widget is to use. + */ + virtual QFont getFont() const; + + /** + * Returns whether we triggered the update of position movement. + * If so, you probably don't want to move it. + * + * @return The moving state. + */ + bool getStartMove() { + return m_bStartMove; + } + + /** + * Sets the x-coordinate. + * Currently, the only class that reimplements this method is + * ObjectWidget. + * + * @param x The x-coordinate to be set. + */ + virtual void setX( int x ); + + /** + * Sets the y-coordinate. + * Currently, the only class that reimplements this method is + * ObjectWidget. + * + * @param y The y-coordinate to be set. + */ + virtual void setY( int y ); + + /** + * Sets the z-coordinate. + * + * @param z The z-coordinate to be set. + */ + virtual void setZ( int z ); + + /** + * Gets the x-coordinate. + */ + int getX() const { + return (int)QCanvasItem::x(); + } + + /** + * Gets the y-coordinate. + */ + int getY() const { + return (int)QCanvasItem::y(); + } + + /** + * Gets the z-coordinate. + */ + int getZ() const { + return (int)QCanvasItem::z(); + } + + /** + * Returns the height of widget. + */ + int getHeight() const { + return QCanvasRectangle::height(); + } + + /** + * Returns the width of the widget. + */ + int getWidth() const { + return QCanvasRectangle::width(); + } + + /** + * Sets the size. + * If m_pView->getSnapComponentSizeToGrid() is true, then + * set the next larger size that snaps to the grid. + */ + void setSize(int width,int height); + + /** + * Set m_bIgnoreSnapToGrid. + */ + void setIgnoreSnapToGrid(bool to); + + /** + * Return the value of m_bIgnoreSnapToGrid. + */ + bool getIgnoreSnapToGrid() const; + + /** + * Move the widget by an X and Y offset relative to + * the current position. + */ + void moveBy(int dx, int dy); + + /** + * Removes an already created association from the list of + * associations that include this UMLWidget + */ + void removeAssoc(AssociationWidget* pAssoc); + + /** + * Adds an already created association to the list of + * associations that include this UMLWidget + */ + void addAssoc(AssociationWidget* pAssoc); + + /** + * Returns the list of associations connected to this widget. + */ + AssociationWidgetList & getAssocList() { + return m_Assocs; + } + + /** + * Returns m_bUsesDiagramFillColour + */ + bool getUsesDiagramFillColour() const { + return m_bUsesDiagramFillColour; + } + + /** + * Returns m_bUsesDiagramUseFillColour + */ + bool getUsesDiagramUseFillColour() const { + return m_bUsesDiagramUseFillColour; + } + + /** + * Sets m_bUsesDiagramFillColour + */ + void setUsesDiagramFillColour(bool usesDiagramFillColour) { + m_bUsesDiagramFillColour = usesDiagramFillColour; + } + + /** + * Sets m_bUsesDiagramUseFillColour + */ + void setUsesDiagramUseFillColour(bool usesDiagramUseFillColour) { + m_bUsesDiagramUseFillColour = usesDiagramUseFillColour; + } + + /** + * Write property of bool m_bIsInstance + */ + void setIsInstance(bool isInstance) { + m_bIsInstance = isInstance; + } + + /** + * Read property of bool m_bIsInstance + */ + bool getIsInstance() const { + return m_bIsInstance; + } + + /** + * Write property of m_instanceName + */ + void setInstanceName(const QString &instanceName) { + m_instanceName = instanceName; + } + + /** + * Read property of m_instanceName + */ + QString getInstanceName() const { + return m_instanceName; + } + + /** + * Returns the status of whether to show Stereotype. + * + * @return True if stereotype is shown. + */ + bool getShowStereotype() const; + + /** + * Set the status of whether to show Stereotype. + * + * @param _status True if stereotype shall be shown. + */ + virtual void setShowStereotype(bool _status); + + /** + * Show a properties dialog for a UMLWidget. + */ + virtual void showProperties(); + + /** + * Returns true if the Activate method has been called for this instance + * + * @return The activate status. + */ + bool isActivated(); + + /** + * Sets the name in the corresponding UMLObject. + * Sets the local m_Text if m_pObject is NULL. + * + * @param strName The name to be set. + */ + virtual void setName(const QString &strName); + + /** + * Gets the name from the corresponding UMLObject. + * Returns the local m_Text if m_pObject is NULL. + * + * @return The currently set name. + */ + virtual QString getName() const; + + /** + * Starts the popup menu. + * + * @param At The Point where the diagram is to be coming up. + */ + void startPopupMenu( const QPoint &At ); + + /** + * Adjusts associations with the given co-ordinates + * + * @param x The x-coordinate. + * @param y The y-coordinate. + */ + virtual void adjustAssocs(int x, int y); + + /** + * Adjusts all unselected associations with the given co-ordinates + * + * @param x The x-coordinate. + * @param y The y-coordinate. + */ + void adjustUnselectedAssocs(int x, int y); + + /** + * Set the m_bActivated flag of a widget but does not perform the Activate method + * + * @param Active Status of activation is to be set. + */ + void setActivated(bool Active = true); + + /** + * Used to cleanup any other widget it may need to delete. + * Used by child classes. This should be called before deleting a widget of a diagram. + */ + virtual void cleanup(); + + /** + * Returns whether the widget type has an associated UMLObject + */ + static bool widgetHasUMLObject(Uml::Widget_Type type); + + /** + * Update the size of this widget. + */ + void updateComponentSize(); + + /** + * @note For performance Reasons, only FontMetrics for already used + * font types are updated. Not yet used font types will not get a font metric + * and will get the same font metric as if painter was zero. + * This behaviour is acceptable, because diagrams will always be showed on Display + * first before a special painter like a printer device is used. + */ + void forceUpdateFontMetrics(QPainter *painter); + + /** + * Calls the method with the same name in UMLWidgetController. + * @see UMLWidgetController#mousePressEvent + * + * @param me The QMouseEvent event. + */ + virtual void mousePressEvent(QMouseEvent *me); + + /** + * Overrides the standard operation. + * + * @param me The move event. + */ + virtual void moveEvent(QMoveEvent *me); + + virtual void saveToXMI( QDomDocument & qDoc, QDomElement & qElement ); + + virtual bool loadFromXMI( QDomElement & qElement ); + + /** + * Returns the UMLWdigetController for this widget. + */ + UMLWidgetController* getWidgetController(); + +protected: + /** + * Apply possible constraints to the given candidate width and height. + * The default implementation calls calculateSize() and + * assigns the returned values if they are greater than the + * input values. + * + * @param width input value, may be modified by the constraint + * @param height input value, may be modified by the constraint + */ + virtual void constrain(int& width, int& height); + + /** + * Draws that the widget is selected. + * + * @param p Device on which is the selection is to be drawn. + * @param offsetX The x-coordinate for drawing. + * @param offsetY The y-coordinate for drawing. + */ + virtual void drawSelected(QPainter * p, int offsetX, int offsetY); + + /** + * Overrides default method. + * + * @param p Device on which the shape has to be drawn. + */ + virtual void drawShape(QPainter &p ); + + /** + * Compute the minimum possible width and height. + * The default implementation returns width=20, height=20. + * + * @return QSize(mininum_width, minimum_height) + */ + virtual QSize calculateSize(); + + typedef enum { + FT_NORMAL = 0, + FT_BOLD = 1, + FT_ITALIC = 2, + FT_UNDERLINE = 3, + FT_BOLD_ITALIC = 4, + FT_BOLD_UNDERLINE = 5, + FT_ITALIC_UNDERLINE = 6, + FT_BOLD_ITALIC_UNDERLINE = 7, + FT_INVALID = 8 + } FontType; + + /** Template Method, override this to set the default + * font metric. + */ + virtual void setDefaultFontMetrics(UMLWidget::FontType fontType); + virtual void setDefaultFontMetrics(UMLWidget::FontType fontType, QPainter &painter); + + /** Returns the font metric used by this object for Text which uses bold/italic fonts*/ + QFontMetrics &getFontMetrics(UMLWidget::FontType fontType); + /** set the font metric to use */ + void setFontMetrics(UMLWidget::FontType fontType, QFontMetrics fm); + void setupFontType(QFont &font, UMLWidget::FontType fontType); + + /** + * Initializes key attributes of the class. + */ + void init(); + + ///////////////// Data Loaded/Saved ///////////////////////////////// + + /** + * This flag indicates if the UMLWidget uses the Diagram FillColour + */ + bool m_bUseFillColour; + + /** + * true by default, false if the colours have + * been explicitly set for this widget + */ + bool m_bUsesDiagramFillColour; + bool m_bUsesDiagramUseFillColour; + + /** + * Color of the background of the widget + */ + QColor m_FillColour; + + /** + * A list of AssociationWidgets between the UMLWidget and other UMLWidgets in the diagram + */ + AssociationWidgetList m_Assocs; + + /** + * getName() returns the name from the UMLObject if this widget has an + * underlying UMLObject; if it does not, then getName() returns the local + * m_Text (notably the case for FloatingTextWidget.) + */ + QString m_Text; + + /** + * The font the widget will use. + */ + QFont m_Font; + + /** + * Holds whether this widget is a component instance (i.e. on a deployment diagram) + */ + bool m_bIsInstance; + + /** + * The instance name (used if on a deployment diagram) + */ + QString m_instanceName; + + /** + * Should the stereotype be displayed + */ + bool m_bShowStereotype; + + ///////////////// End of Data Loaded/Saved ////////////////////////// + + bool m_bSelected, m_bStartMove; + + int m_nPosX, m_origZ; + ListPopupMenu *m_pMenu; + UMLDoc *m_pDoc; ///< shortcut for UMLApp::app()->getDocument() + bool m_bResizable; + QFontMetrics *m_pFontMetrics[FT_INVALID]; + + /** + * It is true if the Activate Function has been called for this + * class instance + */ + bool m_bActivated; + + /** + * Change Widget Behaviour + */ + bool m_bIgnoreSnapToGrid; + bool m_bIgnoreSnapComponentSizeToGrid; + + /** + * Controller for user interaction events. + */ + UMLWidgetController *m_widgetController; + +public slots: + + /** + * This slot is entered when an event has occurred on the views display, + * most likely a mouse event. Before it sends out that mouse event all + * children should make sure that they don't have a menu active or there + * could be more than one popup menu displayed. + */ + virtual void slotRemovePopupMenu(); + + /** + * When a widget changes this slot captures that signal. + */ + virtual void updateWidget(); + + + /** + * Captures any popup menu signals for menus it created. + * + * @param sel The command which has to be executed. + */ + virtual void slotMenuSelection(int sel); + + /** + * Captures when another widget moves if it is link to it that signal. + * + * @param id The id of object behind the widget. + */ + virtual void slotWidgetMoved(Uml::IDType id); + + /** + * Captures a color change signal. + * + * @param viewID The id of the object behind the widget. + */ + virtual void slotColorChanged(Uml::IDType viewID); + + /** + * Captures a linewidth change signal. + * + * @param viewID The id of the object behind the widget. + */ + virtual void slotLineWidthChanged(Uml::IDType viewID); + + /** + * Captures a sigClearAllSelected signal sent by @ref UMLView + */ + void slotClearAllSelected(); + + /** + * Tells the widget to snap to grid. + * Will use the grid settings of the @ref UMLView it belongs to. + */ + void slotSnapToGrid(); + +signals: + /** + * Emit when the widget moves its' position. + * + * @param id The id of the object behind the widget. + */ + void sigWidgetMoved(Uml::IDType id); +}; + +#endif diff --git a/umbrello/umbrello/umlwidgetcontroller.cpp b/umbrello/umbrello/umlwidgetcontroller.cpp new file mode 100644 index 00000000..29ca98a1 --- /dev/null +++ b/umbrello/umbrello/umlwidgetcontroller.cpp @@ -0,0 +1,540 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "umlwidgetcontroller.h" + +// qt includes +#include +#include + +// kde includes +#include +#include +#include + +// app includes +#include "umlwidget.h" +#include "umlwidgetlist.h" +#include "umlnamespace.h" +#include "uml.h" +#include "umldoc.h" +#include "umlview.h" +#include "umlobject.h" +#include "listpopupmenu.h" +#include "classifierwidget.h" +#include "associationwidget.h" +#include "messagewidget.h" + +using namespace Uml; + +UMLWidgetController::UMLWidgetController(UMLWidget *widget) { + m_widget = widget; + + m_pressOffsetX = m_pressOffsetY = 0; + m_oldX = m_oldY = 0; + m_oldW = m_oldH = 0; + m_minSelectedX = m_minSelectedY = m_maxSelectedX = m_maxSelectedY = 0; + + m_shiftPressed = false; + m_leftButtonDown = m_middleButtonDown = m_rightButtonDown = false; + m_inMoveArea = m_inResizeArea = 0; + m_wasSelected = m_moved = m_resized = 0; +} + +UMLWidgetController::~UMLWidgetController() { +} + +void UMLWidgetController::mousePressEvent(QMouseEvent *me) { + // If there is a button pressed already ignore other press events + if (m_leftButtonDown || m_middleButtonDown || m_rightButtonDown) { + return; + } + + if (me->button() == Qt::LeftButton) { + m_leftButtonDown = true; + } else if (me->button() == Qt::RightButton) { + m_rightButtonDown = true; + } else { + m_middleButtonDown = true; + return; + } + + //There is no harm in saving all the values of the widget even when + //they aren't going to be used + saveWidgetValues(me); + + m_oldStatusBarMsg = UMLApp::app()->getStatusBarMsg(); + + if (me->state() == Qt::ShiftButton || me->state() == Qt::ControlButton) { + m_shiftPressed = true; + + if (me->button() == Qt::LeftButton) { + m_inMoveArea = true; + } + + if (!m_widget->m_bSelected) { + selectMultiple(me); + } else if (!m_rightButtonDown) { + m_wasSelected = false; + } + return; + } + + m_shiftPressed = false; + + int count = m_widget->m_pView->getSelectCount(true); + if (me->button() == Qt::LeftButton) { + if (m_widget->m_bSelected && count > 1) { + //Single selection is made in release event if the widget wasn't moved + m_inMoveArea = true; + lastUpdate.start(); + return; + } + + if (isInResizeArea(me)) { + m_inResizeArea = true; + } else { + m_inMoveArea = true; + } + } + + //If widget wasn't selected, or it was selected but with other widgets also selected + if (!m_widget->m_bSelected || count > 1) { + selectSingle(me); + } else if (!m_rightButtonDown) { + m_wasSelected = false; + } +} + +void UMLWidgetController::mouseMoveEvent(QMouseEvent* me) { + if (!m_leftButtonDown) + return; + + if (m_inResizeArea) { + resize(me); + return; + } + + if (!m_moved) { + UMLApp::app()->getDocument()->writeToStatusBar(i18n("Hold shift or ctrl to move in X axis. Hold shift and control to move in Y axis. Right button click to cancel move.")); + + m_moved = true; + //Maybe needed by AssociationWidget + m_widget->m_bStartMove = true; + + setSelectionBounds(); + } + + QPoint position = getPosition(me); + int diffX = position.x() - m_widget->getX(); + int diffY = position.y() - m_widget->getY(); + + if ((me->state() & Qt::ShiftButton) && (me->state() & Qt::ControlButton)) { + //Move in Y axis + diffX = 0; + } else if ((me->state() & Qt::ShiftButton) || (me->state() & Qt::ControlButton)) { + //Move in X axis + diffY = 0; + } + + // kDebug() << "UMLWidgetController::mouseMoveEvent before constrainMovementForAllWidgets:" + // << " diffX=" << diffX << ", diffY=" << diffY << endl; + constrainMovementForAllWidgets(diffX, diffY); + // kDebug() << "UMLWidgetController::mouseMoveEvent after constrainMovementForAllWidgets:" + // << " diffX=" << diffX << ", diffY=" << diffY << endl; + + //Nothing to move + if (diffX == 0 && diffY == 0) { + return; + } + + UMLWidgetListIt it(m_selectedWidgetsList); + UMLWidget* widget; + it.toFirst(); + + bool update = false; + if (lastUpdate.elapsed() > 25) { + update = true; + lastUpdate.restart(); + + m_widget->adjustUnselectedAssocs(m_widget->getX(), m_widget->getY()); + } + + while ((widget = it.current()) != 0) { + ++it; + widget->getWidgetController()->moveWidgetBy(diffX, diffY); + } + // kDebug() << endl; + + // Move any selected associations. + AssociationWidgetList awl = m_widget->m_pView->getSelectedAssocs(); + AssociationWidget *aw = NULL; + for (AssociationWidgetListIt ai(awl); (aw = ai.current()) != NULL; ++ai) { + if (aw->getSelected()) { + aw->moveEntireAssoc(diffX, diffY); + } + } + + m_widget->m_pView->resizeCanvasToItems(); + updateSelectionBounds(diffX, diffY); +} + +void UMLWidgetController::mouseReleaseEvent(QMouseEvent *me) { + if (me->button() != Qt::LeftButton && me->button() != Qt::RightButton) { + if (m_middleButtonDown) { + m_middleButtonDown = false; + resetSelection(); + } + } else if (me->button() == Qt::LeftButton) { + if (m_leftButtonDown) { + m_leftButtonDown = false; + + if (!m_moved && !m_resized) { + if (!m_shiftPressed && (m_widget->m_pView->getSelectCount(true) > 1)) { + selectSingle(me); + } else if (!m_wasSelected) { + deselect(me); + } + } else { + if (m_moved) { + m_moved = false; + + //Ensure associations are updated (the timer could prevent the + //adjustment in the last move event before the release) + UMLWidgetListIt it(m_selectedWidgetsList); + UMLWidget* widget; + it.toFirst(); + while ((widget = it.current()) != 0) { + ++it; + widget->adjustAssocs(widget->getX(), widget->getY()); + } + + m_widget->m_bStartMove = false; + } else { + m_resized = false; + } + + if ((m_inMoveArea && wasPositionChanged()) || + (m_inResizeArea && wasSizeChanged())) { + m_widget->m_pDoc->setModified(true); + } + + UMLApp::app()->getDocument()->writeToStatusBar(m_oldStatusBarMsg); + } + + if (m_inResizeArea) { + m_inResizeArea = false; + m_widget->m_pView->setCursor(KCursor::arrowCursor()); + } else { + m_inMoveArea = false; + } + } + } else if (me->button() == Qt::RightButton) { + if (m_rightButtonDown) { + m_rightButtonDown = false; + showPopupMenu(me); + } else if (m_leftButtonDown) { + //Cancel move/edit + QMouseEvent move(QMouseEvent::MouseMove, + QPoint(m_oldX + m_pressOffsetX, m_oldY + m_pressOffsetY), + Qt::LeftButton, Qt::NoButton); + mouseMoveEvent(&move); + QMouseEvent release(QMouseEvent::MouseButtonRelease, + QPoint(m_oldX + m_pressOffsetX, m_oldY + m_pressOffsetY), + Qt::LeftButton, Qt::NoButton); + mouseReleaseEvent(&release); + } + } + + //TODO Copied from old code. Does it really work as intended? + UMLWidget *bkgnd = m_widget->m_pView->getWidgetAt(me->pos()); + if (bkgnd) { + //kDebug() << "UMLWidgetController::mouseReleaseEvent: setting Z to " + // << bkgnd->getZ() + 1 << endl; + m_widget->setZ(bkgnd->getZ() + 1); + } else { + m_widget->setZ(0); + } +} + +void UMLWidgetController::mouseDoubleClickEvent(QMouseEvent *me) { + if (me->button() != Qt::LeftButton) { + return; + } + + selectSingle(me); + + doMouseDoubleClick(me); +} + +bool UMLWidgetController::isInResizeArea(QMouseEvent *me) { + const int m = 10; + + if (m_widget->m_bResizable && + me->x() >= (m_widget->getX() + m_widget->width() - m) && + me->y() >= (m_widget->getY() + m_widget->height() - m)) { + m_widget->m_pView->setCursor(getResizeCursor()); + return true; + } else { + m_widget->m_pView->setCursor(KCursor::arrowCursor()); + return false; + } +} + +QCursor UMLWidgetController::getResizeCursor() { + return KCursor::sizeFDiagCursor(); +} + +void UMLWidgetController::resizeWidget(int newW, int newH) { + m_widget->setSize(newW, newH); +} + +void UMLWidgetController::moveWidgetBy(int diffX, int diffY) { + m_widget->setX(m_widget->getX() + diffX); + m_widget->setY(m_widget->getY() + diffY); +} + +void UMLWidgetController::constrainMovementForAllWidgets(int &/*diffX*/, int &/*diffY*/) { +} + +void UMLWidgetController::doMouseDoubleClick(QMouseEvent *) { + if (!m_widget || !m_widget->m_pMenu) + return; + m_widget->slotMenuSelection(ListPopupMenu::mt_Properties); +} + +void UMLWidgetController::resetSelection() { + m_widget->m_pView->clearSelected(); + m_widget->m_pView->resetToolbar(); + m_widget->setSelected(false); + + m_wasSelected = false; +} + +void UMLWidgetController::selectSingle(QMouseEvent *me) { + m_widget->m_pView->clearSelected(); + + //Adds the widget to the selected widgets list, but as it has been cleared + //only the current widget is selected + selectMultiple(me); +} + +void UMLWidgetController::selectMultiple(QMouseEvent *me) { + m_widget->m_bSelected = true; + m_widget->setSelected(m_widget->m_bSelected); + m_widget->m_pView->setSelected(m_widget, me); + + m_wasSelected = true; +} + +void UMLWidgetController::deselect(QMouseEvent *me) { + m_widget->m_bSelected = false; + m_widget->setSelected(m_widget->m_bSelected); + m_widget->m_pView->setSelected(m_widget, me); + //m_wasSelected is false implicitly, no need to set it again +} + +void UMLWidgetController::saveWidgetValues(QMouseEvent *me) { + m_pressOffsetX = me->x() - m_widget->getX(); + m_pressOffsetY = me->y() - m_widget->getY(); + + m_oldX = m_widget->getX(); + m_oldY = m_widget->getY(); + + m_oldW = m_widget->width(); + m_oldH = m_widget->height(); +} + +void UMLWidgetController::setSelectionBounds() { + if (m_widget->m_pView->getSelectCount() > 0) { + m_selectedWidgetsList.clear(); + m_widget->m_pView->getSelectedWidgets(m_selectedWidgetsList, false); + + updateSelectionBounds(1, 1); + } +} + +//TODO optimize it +void UMLWidgetController::updateSelectionBounds(int diffX, int diffY) { + if (diffX != 0) { + m_minSelectedX = getSmallestX(m_selectedWidgetsList); + m_maxSelectedX = getBiggestX(m_selectedWidgetsList); + } + if (diffY != 0) { + m_minSelectedY = getSmallestY(m_selectedWidgetsList); + m_maxSelectedY = getBiggestY(m_selectedWidgetsList); + } +} + +void UMLWidgetController::resize(QMouseEvent *me) { + UMLApp::app()->getDocument()->writeToStatusBar(i18n("Hold shift or ctrl to move in X axis. Hold shift and control to move in Y axis. Right button click to cancel resize.")); + + m_resized = true; + + int newW = m_oldW + me->x() - m_widget->getX() - m_pressOffsetX; + int newH = m_oldH + me->y() - m_widget->getY() - m_pressOffsetY; + + if ((me->state() & Qt::ShiftButton) && (me->state() & Qt::ControlButton)) { + //Move in Y axis + newW = m_oldW; + } else if ((me->state() & Qt::ShiftButton) || (me->state() & Qt::ControlButton)) { + //Move in X axis + newH = m_oldH; + } + + m_widget->constrain(newW, newH); + resizeWidget(newW, newH); + m_widget->adjustAssocs(m_widget->getX(), m_widget->getY()); + + m_widget->m_pView->resizeCanvasToItems(); +} + +//TODO refactor with AlignToolbar method. +int UMLWidgetController::getSmallestX(const UMLWidgetList &widgetList) { + UMLWidgetListIt it(widgetList); + UMLWidget* widget; + + widget = it.toFirst(); + // leave function upon empty widget list + if (NULL == widget) return 0; + int smallestX = widget->getX(); + ++it; + + while ((widget = it.current()) != 0) { + ++it; + if (smallestX > widget->getX()) + smallestX = widget->getX(); + } + + return smallestX; +} + +//TODO refactor with AlignToolbar method. +int UMLWidgetController::getSmallestY(const UMLWidgetList &widgetList) { + UMLWidgetListIt it(widgetList); + UMLWidget* widget; + + widget = it.toFirst(); + // leave function upon empty widget list + if (NULL == widget) return 0; + int smallestY = widget->getY(); + ++it; + + while ((widget = it.current()) != 0) { + ++it; + if (smallestY > widget->getY()) + smallestY = widget->getY(); + } + + return smallestY; +} + +//TODO refactor with AlignToolbar method. +int UMLWidgetController::getBiggestX(const UMLWidgetList &widgetList) { + UMLWidgetListIt it(widgetList); + UMLWidget* widget; + + widget = it.toFirst(); + // leave function upon empty widget list + if (NULL == widget) return 0; + int biggestX = widget->getX(); + biggestX += it.current()->getWidth(); + ++it; + + while ((widget = it.current()) != 0) { + ++it; + if (biggestX < widget->getX() + widget->getWidth()) + biggestX = widget->getX() + widget->getWidth(); + } + + return biggestX; +} + +//TODO refactor with AlignToolbar method. +int UMLWidgetController::getBiggestY(const UMLWidgetList &widgetList) { + UMLWidgetListIt it(widgetList); + UMLWidget* widget; + + widget = it.toFirst(); + // leave function upon empty widget list + if (NULL == widget) return 0; + int biggestY = widget->getY(); + biggestY += it.current()->getHeight(); + ++it; + + while ((widget = it.current()) != 0) { + ++it; + if (biggestY < widget->getY() + widget->getHeight()) + biggestY = widget->getY() + widget->getHeight(); + } + + return biggestY; +} + +QPoint UMLWidgetController::getPosition(QMouseEvent* me) { + /* + kDebug() << "UMLWidgetController::getPosition: me->x=" << me->x() + << " m_widget->getX=" << m_widget->getX() << ", m_oldX=" << m_oldX + << ", m_pressOffsetX=" << m_pressOffsetX << endl; + kDebug() << "UMLWidgetController::getPosition: me->y=" << me->y() + << " m_widget->getY=" << m_widget->getY() << ", m_oldY=" << m_oldY + << ", m_pressOffsetY=" << m_pressOffsetY << endl; + */ + int newX = me->x() + m_widget->getX() - m_oldX - m_pressOffsetX; + int newY = me->y() + m_widget->getY() - m_oldY - m_pressOffsetY; + int maxX = m_widget->m_pView->canvas()->width(); + int maxY = m_widget->m_pView->canvas()->height(); + + m_oldX = newX; + m_oldY = newY; + + if (newX + (m_minSelectedX - m_widget->getX()) < 0) { + //kDebug() << "UMLWidgetController::getPosition: got into cond.1" << endl; + newX = m_widget->getX() - m_minSelectedX; + } + if (newY + (m_minSelectedY - m_widget->getY()) < 0) { + //kDebug() << "UMLWidgetController::getPosition: got into cond.2" << endl; + newY = m_widget->getY() - m_minSelectedY; + } + if (newX + (m_maxSelectedX - m_widget->getX()) > maxX) { + //kDebug() << "UMLWidgetController::getPosition: got into cond.3" << endl; + newX = maxX - (m_maxSelectedX - m_widget->getX()); + } + if (newY + (m_maxSelectedY - m_widget->getY()) > maxY) { + //kDebug() << "UMLWidgetController::getPosition: got into cond.4" << endl; + newY = maxY - (m_maxSelectedY - m_widget->getY()); + } + return QPoint(newX, newY); +} + +QPoint UMLWidgetController::getPositionDifference(QMouseEvent* me) { + QPoint newPoint = getPosition(me); + const int diffX = newPoint.x() - m_widget->getX(); + const int diffY = newPoint.y() - m_widget->getY(); + return QPoint(diffX, diffY); +} + +void UMLWidgetController::showPopupMenu(QMouseEvent *me) { + //TODO why this condition? + if (m_widget->m_pMenu) { + return; + } + m_widget->startPopupMenu(me->globalPos()); +} + +bool UMLWidgetController::wasSizeChanged() { + return m_oldW != m_widget->getWidth() || m_oldH != m_widget->getHeight(); +} + +bool UMLWidgetController::wasPositionChanged() { + return m_oldX != m_widget->getX() || m_oldY != m_widget->getY(); +} diff --git a/umbrello/umbrello/umlwidgetcontroller.h b/umbrello/umbrello/umlwidgetcontroller.h new file mode 100644 index 00000000..552ea174 --- /dev/null +++ b/umbrello/umbrello/umlwidgetcontroller.h @@ -0,0 +1,476 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UMLWIDGETCONTROLLER_H +#define UMLWIDGETCONTROLLER_H + +// qt includes +#include + +// app includes +#include "umlwidgetlist.h" + +class QCursor; +class QMouseEvent; +class QMoveEvent; +class QPoint; + +class UMLWidget; + +/** + * Controller for UMLWidget + * This class takes care of user interaction with UMLWidgets: select, deselect, + * move, resize... + * Those actions are done using events. There are handlers for mousePressEvent, + * mouseMoveEvent, mouseReleaseEvent and mouseDoubleClickEvent. There's more + * information about each of them in their respective documentation. + * + * Behaviour of this class can be customized overriding the handlers themselves + * (which isn't recommended) or, better, overriding the virtual protected + * methods. + * + * The area that is considered as "resize area" can be customized with + * isInResizeArea, so when there's a pressed button in this area, mouseMoveEvent + * will resize the widget. + * Also, if the resize area doesn't need to be modified, but the cursor to be + * used when the mouse is in that area can be done with getResizeCursor. + * When a widget is being resized, it's done using resizeWidget, so overriding + * it makes possible to, for example, constrain the resizing only in one axis + * no matter how the mouse was moved. + * + * Widget move can also be customized. The widgets are moved in mouseMoveEvent + * using the moveWidgetBy method of the controller of each selected widget. + * Overriding this method widget movement can be, for example, constrained to + * an axis, no matter if the widget is being moved explicitly or as part of a + * selection. + * On the other hand, the move of all the selected widgets can be constrained + * with constrainMovementForAllWidgets. This method, called in the controller + * that is handling the mouseMoveEvent, modifies the difference between the + * current position of the widgets and the new position to be moved to. For + * example, if a widget shouldn't be moved in X axis, it's receiving the + * mouseMoveEvents and there are other widgets selected, those other widgets + * shouldn't be allowed either to be moved in X axis. + * + * The behaviour when double clicking on the widget after it's selected can be + * customized with doMouseDoubleClick. + * + * @author Umbrello UML Modeller Authors + */ +class UMLWidgetController { +public: + + /** + * Constructor for UMLWidgetController. + * + * @param widget The widget which uses the controller. + */ + UMLWidgetController(UMLWidget *widget); + + /** + * Destructor for UMLWidgetController. + */ + virtual ~UMLWidgetController(); + + /** + * Handles a mouse press event. + * It'll select the widget (or mark it to be deselected) and prepare it to + * be moved or resized. Go on reading for more info about this. + * + * Widget values and message bar status are saved. + * + * If shift or control buttons are pressed, we're in move area no matter + * where the button was pressed in the widget. Moreover, if the widget + * wasn't already selected, it's added to the selection. If already selected, + * it's marked to be deselected when releasing the button (provided it isn't + * moved). + * Also, if the widget is already selected with other widgets but shift nor + * control buttons are pressed, we're in move area. If finally we don't move + * the widget, it's selected and the other widgets deselected when releasing + * the left button. + * + * If shift nor control buttons are pressed, we're facing a single selection. + * Depending on the position of the cursor, we're in move or in resize area. + * If the widget wasn't selected (both when there are no widgets selected, or + * when there're other widgets selected but not the one receiving the press + * event) it's selected and the others deselected, if any. If already selected, + * it's marked to be deselected when releasing the button (provided it wasn't + * moved or resized). + * + * @param me The QMouseEvent event. + */ + virtual void mousePressEvent(QMouseEvent *me); + + /** + * Handles a mouse move event. + * It resizes or moves the widget, depending on where the cursor is pressed + * on the widget. Go on reading for more info about this. + * + * If resizing, the widget is resized using resizeWidget (where specific + * widget resize constrain can be applied), and then the associations are + * adjusted. + * The resizing can be constrained also to an specific axis using control + * and shift buttons. If on or another is pressed, it's constrained to X axis. + * If both are pressed, it's constrained to Y axis. + * + * If not resizing, the widget is being moved. If the move is being started, + * the selection bounds are set (which includes updating the list of selected + * widgets). + * The difference between the previous position of the selection and the new + * one is got (taking in account the selection bounds so widgets don't go + * beyond the canvas limits). Then, it's constrained to X or Y axis depending + * on shift and control buttons. + * A further constrain is made using constrainMovementForAllWidgets (for example, + * if the widget that receives the event can only be moved in Y axis, with this + * method the movement of all the widgets in the selection can be constrained to + * be moved only in Y axis). + * Then, all the selected widgets are moved using moveWidgetBy (where specific + * widget movement constrain can be applied) and, if an specific amount of time + * passed from the last move event, the associations are also updated (they're + * not updated always to be easy on the CPU). Finally, the canvas is resized, + * and selection bounds updated. + * + * @param me The QMouseEvent event. + */ + virtual void mouseMoveEvent(QMouseEvent* me); + + /** + * Handles a mouse release event. + * It selects or deselects the widget and cancels or confirms the move or + * resize. Go on reading for more info about this. + * No matter which tool is selected, Z position of widget is updated. + * + * Middle button release resets the selection. + * Left button release, if it wasn't moved nor resized, selects the widget + * and deselect the others if it wasn't selected and there were other widgets + * selected. If the widget was marked to be deselected, deselects it. + * If it was moved or resized, the document is set to modified if position + * or size changed. Also, if moved, all the associations are adjusted because + * the timer could have prevented the adjustment in the last move event before + * the release. + * If mouse was pressed in resize area, cursor is set again to normal cursor + * Right button release if right button was pressed shows the pop up menu for + * the widget. + * If left button was pressed, it cancels the move or resize with a mouse move + * event at the same position than the cursor was when pressed. Another left + * button release is also sent. + * + * @param me The QMouseEvent event. + */ + virtual void mouseReleaseEvent(QMouseEvent * me); + + /** + * Handles a mouse double click event. + * If the button wasn't left button it does nothing. Otherwise, it selects + * the widget (deselecting other selected widgets, if any) and executes + * doMouseDoubleClick. + * @see doMouseDoubleClick + * + * @param me The QMouseEvent event. + */ + virtual void mouseDoubleClickEvent(QMouseEvent *me); + +protected: + + /** + * Saves the values of the widget needed for move/resize. + * The values saved are: the offset from the cursor respect to the upper left + * corner of the widget in m_pressOffsetX/Y, the position in m_oldX/Y and the + * size in m_oldW/H. + * + * It can be overridden to save subclass specific values whenever a move or + * resize begins. However, parent method (that is, this method) must be + * called in the overridden method. + * + * @param me The QMouseEvent to get the offset from. + */ + virtual void saveWidgetValues(QMouseEvent *me); + + /** + * Checks if the mouse is in resize area (right bottom corner), and sets + * the cursor depending on that. + * The cursor used when resizing is gotten from getResizeCursor(). + * + * @param me The QMouseEVent to check. + * @return true if the mouse is in resize area, false otherwise. + */ + virtual bool isInResizeArea(QMouseEvent *me); + + /** + * Returns the cursor to be shown when resizing the widget. + * + * Default cursor is KCursor::sizeFDiagCursor(). + * + * @return The cursor to be shown when resizing the widget. + */ + virtual QCursor getResizeCursor(); + + /** + * Resizes the widget. + * It's called from resize, after the values are constrained and before + * the associations are adjusted. + * + * Default behaviour is resize the widget using the new size values. + * @see resize + * + * @param newW The new width for the widget. + * @param newH The new height for the widget. + */ + virtual void resizeWidget(int newW, int newH); + + /** + * Moves the widget to a new position using the difference between the + * current position and the new position. + * This method doesn't adjust associations. It only moves the widget. + * + * It can be overridden to constrain movement of m_widget only in one axis even when + * the user isn't constraining the movement with shift or control buttons, for example. + * The movement policy set here is applied whenever the widget is moved, being it + * moving it explicitly, or as a part of a selection but not receiving directly the + * mouse events. + * + * Default behaviour is move the widget to the new position using the diffs. + * @see constrainMovementForAllWidgets + * + * @param diffX The difference between current X position and new X position. + * @param diffY The difference between current Y position and new Y position. + */ + virtual void moveWidgetBy(int diffX, int diffY); + + /** + * Modifies the value of the diffX and diffY variables used to move the widgets. + * + * It can be overridden to constrain movement of all the selected widgets only in one + * axis even when the user isn't constraining the movement with shift or control + * buttons, for example. + * The difference with moveWidgetBy is that the diff positions used here are + * applied to all the selected widgets instead of only to m_widget, and that + * moveWidgetBy, in fact, moves the widget, and here simply the diff positions + * are modified. + * + * Default behaviour is do nothing. + * @see moveWidgetBy + * + * @param diffX The difference between current X position and new X position. + * @param diffY The difference between current Y position and new Y position. + */ + virtual void constrainMovementForAllWidgets(int &diffX, int &diffY); + + /** + * Executes the action for double click in the widget. + * It's called only if the button used was left button. + * Before calling this method, the widget is selected. + * + * Default behaviour is show the properties dialog for the widget using + * m_widget->slotMenuSelection(ListPopupMenu::mt_Properties); + * If the widget doesn't have a property dialog (from the Widget_Type enum, those that + * don't have an UMLObject representation) there's no need to override + * the method, it simply does nothing. + * + * @param me The QMouseEvent which triggered the double click event. + */ + virtual void doMouseDoubleClick(QMouseEvent *me); + + /** + * Clears the selection, resets the toolbar and deselects the widget. + */ + void resetSelection(); + + /** + * Selects the widget and clears the other selected widgets, if any. + * + * @param me The QMouseEvent which made the selection. + */ + void selectSingle(QMouseEvent *me); + + /** + * Selects the widget and adds it to the list of selected widgets. + * + * @param me The QMouseEvent which made the selection. + */ + void selectMultiple(QMouseEvent *me); + + /** + * Deselects the widget and removes it from the list of selected widgets. + * + * @param me The QMouseEvent which made the selection. + */ + void deselect(QMouseEvent *me); + + /** + * Fills m_selectedWidgetsList and sets the selection bounds ((m_min/m_max)X/Y attributes). + */ + void setSelectionBounds(); + + /** + * Updates the selection bounds based on the movement made. + * If it was only a vertical movement, there's no need to update horizontal bounds, + * and vice versa. + * + * @param diffX The difference between current X position and new X position. + * @param diffY The difference between current Y position and new Y position. + */ + void updateSelectionBounds(int diffX, int diffY); + + /** + * Resizes the widget and adjusts the associations. + * It's called when a mouse move event happens and the cursor was + * in resize area when pressed. + * Resizing can be constrained to an specific axis using control and shift buttons. + * + * @param me The QMouseEvent to get the values from. + */ + void resize(QMouseEvent *me); + + /** + * Returns the smallest X position of all the widgets in the list. + * + * @param widgetList A list with UMLWidgets. + * @return The smallest X position. + */ + int getSmallestX(const UMLWidgetList &widgetList); + + /** + * Returns the smallest Y position of all the widgets in the list. + * + * @param widgetList A list with UMLWidgets. + * @return The smallest Y position. + */ + int getSmallestY(const UMLWidgetList &widgetList); + + /** + * Returns the biggest X position of all the widgets in the list. + * + * @param widgetList A list with UMLWidgets. + * @return The biggest X position. + */ + int getBiggestX(const UMLWidgetList &widgetList); + + /** + * Returns the biggest Y position of all the widgets in the list. + * + * @param widgetList A list with UMLWidgets. + * @return The biggest Y position. + */ + int getBiggestY(const UMLWidgetList &widgetList); + + /** + * Returns the adjusted position for the given mouse event. + * The adjusted position is computed using the current widget position + * m_widget->get{X,Y}(), the previous position m_old{X,Y}, and the + * mouse press offset m_pressOffset{X,Y}. + * + * @param me The QMouseEvent for which to get the adjusted position. + * @return A QPoint with the adjusted position. + */ + QPoint getPosition(QMouseEvent *me); + + /** + * Returns a QPoint with the new X and Y position difference of the mouse event + * respect to the position of the widget. + * + * @param me The QMouseEvent to get the position to compare. + * @return A QPoint with the position difference. + */ + QPoint getPositionDifference(QMouseEvent *me); + + /** + * Shows the widget popup menu where the mouse event points to. + * + * @param me The QMouseEvent which triggered the showing. + */ + void showPopupMenu(QMouseEvent *me); + + /** + * Checks if the size of the widget changed respect to the size that + * it had when press event was fired. + * + * @return true if was resized, false otherwise. + */ + bool wasSizeChanged(); + + /** + * Checks if the position of the widget changed respect to the position that + * it had when press event was fired. + * + * @return true if was moved, false otherwise. + */ + bool wasPositionChanged(); + + /** + * The widget which uses the controller. + */ + UMLWidget *m_widget; + + /** + * Timer that prevents excessive updates (be easy on the CPU). + */ + QTime lastUpdate; + + /** + * A list containing the selected widgets. + * It's filled by setSelectionBounds method. It must be filled again if + * selected widgets changed. It is cleared only in setSelectionBounds, just + * before filling it. + * Select, deselect and so on methods DON'T modify this list. + */ + UMLWidgetList m_selectedWidgetsList; + + /** + * The text in the status bar when the cursor was pressed. + */ + QString m_oldStatusBarMsg; + + /** + * The X/Y offset from the position of the cursor when it was pressed to the + * upper left corner of the widget. + */ + int m_pressOffsetX, m_pressOffsetY; + + /** + * The X/Y position the widget had when the movement started. + */ + int m_oldX, m_oldY; + + /** + * The width/height the widget had when the resize started. + */ + int m_oldW, m_oldH; + + /** + * The minimum/maximum X/Y position of all the selected widgets. + */ + int m_minSelectedX, m_minSelectedY, m_maxSelectedX, m_maxSelectedY; + + /** + * If shift or control button were pressed in mouse press event. + */ + bool m_shiftPressed; + + /** + * If the left/middle/right button is pressed. + */ + bool m_leftButtonDown, m_middleButtonDown, m_rightButtonDown; + + /** + * If cursor was in move/resize area when left button was pressed (and no + * other widgets were selected). + */ + bool m_inMoveArea, m_inResizeArea; + + /** + * If the widget was selected/moved/resized in the press and release cycle. + * Moved/resized is true if the widget was moved/resized even if the final + * position/size is the same as the starting one. + */ + bool m_wasSelected, m_moved, m_resized; +}; + +#endif diff --git a/umbrello/umbrello/umlwidgetlist.h b/umbrello/umbrello/umlwidgetlist.h new file mode 100644 index 00000000..45a9db6b --- /dev/null +++ b/umbrello/umbrello/umlwidgetlist.h @@ -0,0 +1,29 @@ +/*************************************************************************** + umlwidgetlist.h - description + ------------------- + begin : Sat Dec 29 2001 + copyright : (C) 2001 by Gustavo Madrigal + email : gmadrigal@nextphere.com + Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef UMLWIDGETLIST_H +#define UMLWIDGETLIST_H + +#include + +class UMLWidget; + +typedef QPtrList UMLWidgetList; +typedef QPtrListIterator UMLWidgetListIt; + +#endif diff --git a/umbrello/umbrello/uniqueid.cpp b/umbrello/umbrello/uniqueid.cpp new file mode 100644 index 00000000..ce4dc117 --- /dev/null +++ b/umbrello/umbrello/uniqueid.cpp @@ -0,0 +1,57 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "uniqueid.h" + +// system includes +#include + +namespace UniqueID { + +/** + * Each model object gets assigned a unique ID. + */ +Uml::IDType m_uniqueID; + +Uml::IDType gen() { + static char buf[20]; + int length = 12; + int i = 0; + // Source: KDE4 kdelibs/kdecore/krandom.cpp KRandom::randomString() + while (length--) { + int r = kapp->random() % 62; + r += 48; + if (r > 57) + r += 7; + if (r > 90) + r += 6; + buf[i++] = char(r); + } + buf[i] = '\0'; + m_uniqueID = std::string(buf); + return m_uniqueID; +} + +void init() { + m_uniqueID = Uml::id_Reserved; +} + +Uml::IDType get() { + return m_uniqueID; +} + +void set(Uml::IDType id) { + m_uniqueID = id; +} + +} // end namespace UniqueID + diff --git a/umbrello/umbrello/uniqueid.h b/umbrello/umbrello/uniqueid.h new file mode 100644 index 00000000..7dcbd4bb --- /dev/null +++ b/umbrello/umbrello/uniqueid.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef UNIQUEID_H +#define UNIQUEID_H + +#include "umlnamespace.h" + +namespace UniqueID { + + /** + * MAIN FUNCTION: Return a new unique ID. + */ + Uml::IDType gen(); + + + /////////// auxiliary functions //////////////////////////////////// + // Only required by code that does special operations on unique IDs. + // NB Try to avoid these functions if possible because their + // implementation and/or programming interface may change. + + /** + * Reinitialize the unique ID counter. + * Should not normally be required because the ID counter is + * initialized by default anyway. + */ + void init(); + + /** + * Return the last generated unique ID without generating a new one. + */ + Uml::IDType get(); + + /** + * Explicitly set a new ID value. + */ + void set(Uml::IDType id); + +} // end namespace UniqueID + +#endif + diff --git a/umbrello/umbrello/usecase.cpp b/umbrello/umbrello/usecase.cpp new file mode 100644 index 00000000..526f533b --- /dev/null +++ b/umbrello/umbrello/usecase.cpp @@ -0,0 +1,40 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#include "usecase.h" + +UMLUseCase::UMLUseCase(const QString & name, Uml::IDType id) + : UMLCanvasObject(name, id) { + init(); +} + +UMLUseCase::~UMLUseCase() {} + +void UMLUseCase::init() { + m_BaseType = Uml::ot_UseCase; +} + +UMLObject* UMLUseCase::clone() const { + UMLUseCase *clone = new UMLUseCase(); + UMLObject::copyInto(clone); + return clone; +} + +void UMLUseCase::saveToXMI(QDomDocument& qDoc, QDomElement& qElement) { + QDomElement usecaseElement = UMLObject::save("UML:UseCase", qDoc); + qElement.appendChild(usecaseElement); +} + +bool UMLUseCase::load(QDomElement& ) { + return true; +} + + diff --git a/umbrello/umbrello/usecase.h b/umbrello/umbrello/usecase.h new file mode 100644 index 00000000..5c262b10 --- /dev/null +++ b/umbrello/umbrello/usecase.h @@ -0,0 +1,63 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef USECASE_H +#define USECASE_H + +#include "umlcanvasobject.h" + +/** + * This class contains the non-graphical information required for a UML UseCase. + * This class inherits from @ref UMLCanvasObject which contains most of the information. + * + * @short Information for a non-graphical UML UseCase. + * @author Paul Hensgen + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + +class UMLUseCase : public UMLCanvasObject { +public: + /** + * Creates a UseCase object + * + * @param name The name of the object. + * @param id The id of the object. + */ + explicit UMLUseCase(const QString & name = "", Uml::IDType id = Uml::id_None); + + /** + * Standard deconstructor + */ + ~UMLUseCase(); + + /** + * Initializes key variables of the class. + */ + virtual void init(); + + /** + * Make a clone of this object. + */ + virtual UMLObject* clone() const; + + /** + * Creates the element. + */ + void saveToXMI( QDomDocument & qDoc, QDomElement & qElement ); + +protected: + /** + * Loads the element (empty.) + */ + bool load( QDomElement & element ); +}; + +#endif diff --git a/umbrello/umbrello/usecasewidget.cpp b/umbrello/umbrello/usecasewidget.cpp new file mode 100644 index 00000000..f7564036 --- /dev/null +++ b/umbrello/umbrello/usecasewidget.cpp @@ -0,0 +1,72 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header file +#include "usecasewidget.h" +// system includes +#include +#include +// local includes +#include "usecase.h" +#include "umlview.h" + + +UseCaseWidget::UseCaseWidget(UMLView * view, UMLUseCase *o) : UMLWidget(view, o) { + UMLWidget::setBaseType(Uml::wt_UseCase); + //updateComponentSize(); Doing this during loadFromXMI() gives futile updates. + // Instead, it is done afterwards by UMLWidget::activate() +} + +UseCaseWidget::~UseCaseWidget() {} + +void UseCaseWidget::draw(QPainter & p, int offsetX, int offsetY) { + UMLWidget::setPen(p); + if ( UMLWidget::getUseFillColour() ) + p.setBrush( UMLWidget::getFillColour() ); + QFont font = UMLWidget::getFont(); + font.setUnderline(false); + font.setBold(false); + font.setItalic( m_pObject->getAbstract() ); + p.setFont( font ); + const QFontMetrics &fm = getFontMetrics(FT_NORMAL); + const int fontHeight = fm.lineSpacing(); + const int w = width(); + const int h = height(); + //int middleX = w / 2; + const int textStartY = (h / 2) - (fontHeight / 2); + + p.drawEllipse(offsetX, offsetY, w, h); + p.setPen(Qt::black); + p.drawText(offsetX + UC_MARGIN, offsetY + textStartY, w - UC_MARGIN * 2, fontHeight, Qt::AlignCenter, getName()); + UMLWidget::setPen(p); + if(m_bSelected) + drawSelected(&p, offsetX, offsetY); +} + +QSize UseCaseWidget::calculateSize() { + const UMLWidget::FontType ft = ( m_pObject->getAbstract() ? FT_BOLD_ITALIC : FT_BOLD ); + const QFontMetrics &fm = UMLWidget::getFontMetrics(ft); + const int fontHeight = fm.lineSpacing(); + const int textWidth = fm.width(getName()); + int width = textWidth > UC_WIDTH?textWidth:UC_WIDTH; + int height = UC_HEIGHT + fontHeight + UC_MARGIN; + + width += UC_MARGIN * 2; + + return QSize(width, height); +} + +void UseCaseWidget::saveToXMI( QDomDocument & qDoc, QDomElement & qElement ) { + QDomElement usecaseElement = qDoc.createElement( "usecasewidget" ); + UMLWidget::saveToXMI( qDoc, usecaseElement ); + qElement.appendChild( usecaseElement ); +} + diff --git a/umbrello/umbrello/usecasewidget.h b/umbrello/umbrello/usecasewidget.h new file mode 100644 index 00000000..509ff7a5 --- /dev/null +++ b/umbrello/umbrello/usecasewidget.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef USECASEWIDGET_H +#define USECASEWIDGET_H +#include "umlwidget.h" + +#define UC_MARGIN 5 +#define UC_WIDTH 60 +#define UC_HEIGHT 30 + + +class UMLUseCase; + +/** + * This class is the graphical version of a UMLUseCase. A UseCaseWidget is created + * by a @ref UMLView. An UseCaseWidget belongs to only one @ref UMLView instance. + * When the @ref UMLView instance that this class belongs to, it will be automatically deleted. + * + * If the @ref UseCase class that this UseCaseWidget is displaying is deleted, the @ref UMLView will + * make sure that this instance is also deleted. + * + * The UseCaseWidget class inherits from the @ref UMLWidget class which adds most of the functionality + * to this class. + * + * @short A graphical version of a UMLUseCase. + * @author Paul Hensgen + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class UseCaseWidget : public UMLWidget { +public: + + /** + * Creates a UseCase widget. + * + * @param view The parent of the widget. + * @param o The UMLObject to represent. + */ + UseCaseWidget(UMLView * view, UMLUseCase *o); + + + /** + * destructor + */ + virtual ~UseCaseWidget(); + + /** + * Overrides the standard paint event. + */ + void draw(QPainter & p, int offsetX, int offsetY); + + /** + * Saves this UseCase to file. + */ + void saveToXMI( QDomDocument & qDoc, QDomElement & qElement ); + + // For loading we can use the loadFromXMI() inherited from UMLWidget. + +protected: + /** + * Overrides method from UMLWidget + */ + QSize calculateSize(); +}; + +#endif diff --git a/umbrello/umbrello/widget_factory.cpp b/umbrello/umbrello/widget_factory.cpp new file mode 100644 index 00000000..5bb8eeae --- /dev/null +++ b/umbrello/umbrello/widget_factory.cpp @@ -0,0 +1,241 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "widget_factory.h" + +// qt/kde includes +#include + +// app includes +#include "uml.h" +#include "umldoc.h" +#include "umlview.h" +#include "object_factory.h" +#include "floatingtextwidget.h" +#include "classifierwidget.h" +#include "classifier.h" +#include "packagewidget.h" +#include "package.h" +#include "folder.h" +#include "componentwidget.h" +#include "component.h" +#include "nodewidget.h" +#include "node.h" +#include "artifactwidget.h" +#include "artifact.h" +#include "datatypewidget.h" +#include "enumwidget.h" +#include "enum.h" +#include "entitywidget.h" +#include "entity.h" +#include "actorwidget.h" +#include "actor.h" +#include "usecasewidget.h" +#include "usecase.h" +#include "notewidget.h" +#include "boxwidget.h" +#include "associationwidget.h" +#include "messagewidget.h" +#include "objectwidget.h" +#include "statewidget.h" +#include "forkjoinwidget.h" +#include "activitywidget.h" +#include "seqlinewidget.h" + +namespace Widget_Factory { + +UMLWidget *createWidget(UMLView *view, UMLObject *o) { + QPoint pos = view->getPos(); + int y = pos.y(); + Uml::Diagram_Type diagramType = view->getType(); + Uml::Object_Type type = o->getBaseType(); + UMLWidget *newWidget = NULL; + switch (type) { + case Uml::ot_Actor: + if (diagramType == Uml::dt_Sequence) { + ObjectWidget *ow = new ObjectWidget(view, o, view->getLocalID()); + ow->setDrawAsActor(true); + y = ow->topMargin(); + newWidget = ow; + } else + newWidget = new ActorWidget(view, static_cast(o)); + break; + case Uml::ot_UseCase: + newWidget = new UseCaseWidget(view, static_cast(o)); + break; + case Uml::ot_Package: + newWidget = new PackageWidget(view, static_cast(o)); + break; + case Uml::ot_Component: + newWidget = new ComponentWidget(view, static_cast(o)); + if (diagramType == Uml::dt_Deployment) { + newWidget->setIsInstance(true); + } + break; + case Uml::ot_Node: + newWidget = new NodeWidget(view, static_cast(o)); + break; + case Uml::ot_Artifact: + newWidget = new ArtifactWidget(view, static_cast(o)); + break; + case Uml::ot_Datatype: + newWidget = new DatatypeWidget(view, static_cast(o)); + break; + case Uml::ot_Enum: + newWidget = new EnumWidget(view, static_cast(o)); + break; + case Uml::ot_Entity: + newWidget = new EntityWidget(view, static_cast(o)); + break; + case Uml::ot_Interface: + if (diagramType == Uml::dt_Sequence || diagramType == Uml::dt_Collaboration) { + ObjectWidget *ow = new ObjectWidget(view, o, view->getLocalID() ); + if (diagramType == Uml::dt_Sequence) { + y = ow->topMargin(); + } + newWidget = ow; + } else { + UMLClassifier *c = static_cast(o); + ClassifierWidget* interfaceWidget = new ClassifierWidget(view, c); + if (diagramType == Uml::dt_Component || diagramType == Uml::dt_Deployment) { + interfaceWidget->setDrawAsCircle(true); + } + newWidget = interfaceWidget; + } + break; + case Uml::ot_Class: + //see if we really want an object widget or class widget + if (diagramType == Uml::dt_Class || diagramType == Uml::dt_Component) { + UMLClassifier *c = static_cast(o); + ClassifierWidget *cw = new ClassifierWidget(view, c); + if (diagramType == Uml::dt_Component) + cw->setDrawAsCircle(true); + newWidget = cw; + } else { + ObjectWidget *ow = new ObjectWidget(view, o, view->getLocalID() ); + if (diagramType == Uml::dt_Sequence) { + y = ow->topMargin(); + } + newWidget = ow; + } + break; + default: + kWarning() << "trying to create an invalid widget" << endl; + } + + if (newWidget) { + newWidget->setX( pos.x() ); + newWidget->setY( y ); + } + return newWidget; +} + +bool validateObjType(Uml::Object_Type expected, UMLObject* &o, Uml::IDType id) { + if (o == NULL) { + kDebug() << "Widget_Factory::validateObjType: creating new object of type " + << expected << endl; + QString artificialName = "LOST_" + ID2STR(id); + o = Object_Factory::createUMLObject(expected, artificialName, NULL, false); + if (o == NULL) + return false; + o->setID(id); + UMLPackage *parentPkg = o->getUMLPackage(); + parentPkg->addObject(o); + return true; + } + Uml::Object_Type actual = o->getBaseType(); + if (actual == expected) + return true; + kError() << "validateObjType(" << o->getName() + << "): expected type " << expected << ", actual type " + << actual << endl; + return false; +} + +UMLWidget* makeWidgetFromXMI(const QString& tag, + const QString& idStr, UMLView *view) { + UMLWidget *widget = NULL; + + // Loading of widgets which do NOT represent any UMLObject, + // just graphic stuff with no real model information + //FIXME while boxes and texts are just diagram objects, activities and + // states should be UMLObjects + if (tag == "statewidget" || tag == "UML:StateWidget") { + widget = new StateWidget(view, StateWidget::Normal, Uml::id_Reserved); + } else if (tag == "notewidget" || tag == "UML:NoteWidget") { + widget = new NoteWidget(view, Uml::id_Reserved); + } else if (tag == "boxwidget") { + widget = new BoxWidget(view, Uml::id_Reserved); + } else if (tag == "floatingtext" || tag == "UML:FloatingTextWidget") { + widget = new FloatingTextWidget(view, Uml::tr_Floating, "", Uml::id_Reserved); + } else if (tag == "activitywidget" || tag == "UML:ActivityWidget") { + widget = new ActivityWidget(view, ActivityWidget::Initial, Uml::id_Reserved); + } else if (tag == "messagewidget") { + widget = new MessageWidget(view, Uml::sequence_message_asynchronous, Uml::id_Reserved); + } else if (tag == "forkjoin") { + widget = new ForkJoinWidget(view, false, Uml::id_Reserved); + } else { + // Loading of widgets which represent an UMLObject + + // Find the UMLObject and create the Widget to represent it + Uml::IDType id = STR2ID(idStr); + UMLDoc *umldoc = UMLApp::app()->getDocument(); + UMLObject *o = umldoc->findObjectById(id); + if (o == NULL) { + kDebug() << "makeWidgetFromXMI: cannot find object with id " + << ID2STR(id) << endl; + } + + if (tag == "actorwidget" || tag == "UML:ActorWidget") { + if (validateObjType(Uml::ot_Actor, o, id)) + widget = new ActorWidget(view, static_cast(o)); + } else if (tag == "usecasewidget" || tag == "UML:UseCaseWidget") { + if (validateObjType(Uml::ot_UseCase, o, id)) + widget = new UseCaseWidget(view, static_cast(o)); + } else if (tag == "classwidget" || tag == "UML:ClassWidget") { + if (validateObjType(Uml::ot_Class, o, id)) + widget = new ClassifierWidget(view, static_cast(o)); + } else if (tag == "packagewidget") { + if (validateObjType(Uml::ot_Package, o, id)) + widget = new PackageWidget(view, static_cast(o)); + } else if (tag == "componentwidget") { + if (validateObjType(Uml::ot_Component, o, id)) + widget = new ComponentWidget(view, static_cast(o)); + } else if (tag == "nodewidget") { + if (validateObjType(Uml::ot_Node, o, id)) + widget = new NodeWidget(view, static_cast(o)); + } else if (tag == "artifactwidget") { + if (validateObjType(Uml::ot_Artifact, o, id)) + widget = new ArtifactWidget(view, static_cast(o)); + } else if (tag == "interfacewidget") { + if (validateObjType(Uml::ot_Interface, o, id)) + widget = new ClassifierWidget(view, static_cast(o)); + } else if (tag == "datatypewidget") { + if (validateObjType(Uml::ot_Datatype, o, id)) + widget = new DatatypeWidget(view, static_cast(o)); + } else if (tag == "enumwidget") { + if (validateObjType(Uml::ot_Enum, o, id)) + widget = new EnumWidget(view, static_cast(o)); + } else if (tag == "entitywidget") { + if (validateObjType(Uml::ot_Entity, o, id)) + widget = new EntityWidget(view, static_cast(o)); + } else if (tag == "objectwidget" || tag == "UML:ObjectWidget") { + widget = new ObjectWidget(view, o ); + } else { + kWarning() << "Trying to create an unknown widget:" << tag << endl; + } + } + return widget; +} + +} // end namespace Widget_Factory + diff --git a/umbrello/umbrello/widget_factory.h b/umbrello/umbrello/widget_factory.h new file mode 100644 index 00000000..ed8c30b7 --- /dev/null +++ b/umbrello/umbrello/widget_factory.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef WIDGET_FACTORY_H +#define WIDGET_FACTORY_H + +#include + +// forward declarations +class UMLView; +class UMLObject; +class UMLWidget; + +/** + * Widget factory methods. + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +namespace Widget_Factory { + + /** + * Create a UMLWidget in the given view and representing the given document object. + */ + UMLWidget *createWidget(UMLView *view, UMLObject *docObj); + + /** + * Create a UMLWidget according to the given XMI tag. + */ + UMLWidget* makeWidgetFromXMI(const QString& tag, + const QString& idStr, UMLView *view); + +} // end namespace Widget_Factory + +#endif diff --git a/umbrello/umbrello/widget_utils.cpp b/umbrello/umbrello/widget_utils.cpp new file mode 100644 index 00000000..b022d828 --- /dev/null +++ b/umbrello/umbrello/widget_utils.cpp @@ -0,0 +1,110 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "widget_utils.h" + +// qt/kde includes +#include +#include +#include +#include +#include + +// app includes +#include "uml.h" +#include "umlview.h" +#include "umlwidget.h" +#include "objectwidget.h" + +namespace Widget_Utils { + +UMLWidget* findWidget(Uml::IDType id, + const UMLWidgetList& widgets, + const MessageWidgetList* pMessages /* = NULL */) +{ + UMLWidgetListIt it( widgets ); + UMLWidget * obj = NULL; + while ( (obj = it.current()) != NULL ) { + ++it; + if (obj->getBaseType() == Uml::wt_Object) { + if (static_cast(obj)->getLocalID() == id) + return obj; + } else if (obj->getID() == id) { + return obj; + } + } + + if (pMessages == NULL) + return NULL; + + MessageWidgetListIt mit( *pMessages ); + while ( (obj = (UMLWidget*)mit.current()) != NULL ) { + ++mit; + if( obj -> getID() == id ) + return obj; + } + return NULL; +} + +QIconSet iconSet(Uml::Diagram_Type dt) { + QIconSet diagramIconSet; + switch (dt) { + case Uml::dt_UseCase: + diagramIconSet = BarIconSet("umbrello_diagram_usecase"); + break; + case Uml::dt_Collaboration: + diagramIconSet = BarIconSet("umbrello_diagram_collaboration"); + break; + case Uml::dt_Class: + diagramIconSet = BarIconSet("umbrello_diagram_class"); + break; + case Uml::dt_Sequence: + diagramIconSet = BarIconSet("umbrello_diagram_sequence"); + break; + case Uml::dt_State: + diagramIconSet = BarIconSet("umbrello_diagram_state"); + break; + case Uml::dt_Activity: + diagramIconSet = BarIconSet("umbrello_diagram_activity"); + break; + case Uml::dt_Component: + diagramIconSet = BarIconSet("umbrello_diagram_component"); + break; + case Uml::dt_Deployment: + diagramIconSet = BarIconSet("umbrello_diagram_deployment"); + break; + case Uml::dt_EntityRelationship: + diagramIconSet = BarIconSet("umbrello_diagram_entityrelationship"); + break; + default: + kDebug() << "Widget_Utils::iconSet: unknown diagram type " << dt << endl; + diagramIconSet = BarIconSet("unknown"); + } + return diagramIconSet; +} + +QCanvasRectangle *decoratePoint(const QPoint& p) { + const int SIZE = 4; + UMLView *currentView = UMLApp::app()->getCurrentView(); + QCanvasRectangle *rect; + rect = new QCanvasRectangle(p.x() - SIZE / 2, + p.y() - SIZE / 2, + SIZE, SIZE, currentView->canvas()); + rect->setBrush( QBrush(Qt::blue) ); + rect->setPen( QPen(Qt::blue) ); + rect->setVisible(true); + return rect; +} + + +} // namespace Widget_Utils + diff --git a/umbrello/umbrello/widget_utils.h b/umbrello/umbrello/widget_utils.h new file mode 100644 index 00000000..419f9d99 --- /dev/null +++ b/umbrello/umbrello/widget_utils.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2006 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef WIDGET_UTILS_H +#define WIDGET_UTILS_H + +#include +#include +#include "umlnamespace.h" +#include "umlwidgetlist.h" +#include "messagewidgetlist.h" + +// forward declarations +class QCanvasRectangle; + +/** + * General purpose widget utilities. + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +namespace Widget_Utils { + +/** + * Find the widget identified by the given ID in the given widget + * or message list. + * + * @param id The unique ID to find. + * @param widgets The UMLWidgetList to search in. + * @param pMessages Optional pointer to a MessageWidgetList to + * search in. + */ +UMLWidget* findWidget(Uml::IDType id, + const UMLWidgetList& widgets, + const MessageWidgetList* pMessages = NULL); + +/** + * Return the icon corresponding to the given Diagram_Type. + */ +QIconSet iconSet(Uml::Diagram_Type dt); + +QCanvasRectangle *decoratePoint(const QPoint& p); + +} + +#endif diff --git a/umbrello/umbrello/widgetbase.cpp b/umbrello/umbrello/widgetbase.cpp new file mode 100644 index 00000000..225f5848 --- /dev/null +++ b/umbrello/umbrello/widgetbase.cpp @@ -0,0 +1,136 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#include "widgetbase.h" + +#include +#include "umlview.h" +#include "umlobject.h" +#include "optionstate.h" + +WidgetBase::WidgetBase(UMLView *view) : QObject(view) { + init(view); +} + +void WidgetBase::init(UMLView *view, Uml::Widget_Type type /* = Uml::wt_UMLWidget */) { + m_pView = view; + m_Type = type; + m_pObject = NULL; + if (m_pView) { + m_bUsesDiagramLineColour = true; + m_bUsesDiagramLineWidth = true; + const Settings::OptionState& optionState = m_pView->getOptionState(); + m_LineColour = optionState.uiState.lineColor; + m_LineWidth = optionState.uiState.lineWidth; + } else { + kError() << "WidgetBase constructor: SERIOUS PROBLEM - m_pView is NULL" << endl; + m_bUsesDiagramLineColour = false; + m_bUsesDiagramLineWidth = false; + m_LineColour = QColor("black"); + m_LineWidth = 0; // initialize with 0 to have valid start condition + } +} + +void WidgetBase::setBaseType( Uml::Widget_Type type ) { + m_Type = type; +} + +Uml::Widget_Type WidgetBase::getBaseType() const { + return m_Type; +} + +UMLObject *WidgetBase::getUMLObject() { + return m_pObject; +} + +void WidgetBase::setUMLObject(UMLObject * o) { + m_pObject = o; +} + +void WidgetBase::setID(Uml::IDType id) { + if (m_pObject) { + if (m_pObject->getID() != Uml::id_None) + kWarning() << "WidgetBase::setID(): changing old UMLObject " + << ID2STR(m_pObject->getID()) << " to " + << ID2STR(id) << endl; + m_pObject->setID(id); + } + m_nId = id; +} + +Uml::IDType WidgetBase::getID() const { + if (m_pObject) + return m_pObject->getID(); + return m_nId; +} + +QString WidgetBase::getDoc() const { + if (m_pObject != NULL) + return m_pObject->getDoc(); + return m_Doc; +} + +void WidgetBase::setDoc( const QString &doc ) { + if (m_pObject != NULL) + m_pObject->setDoc( doc ); + else + m_Doc = doc; +} + +void WidgetBase::setLineColor(const QColor &colour) { + m_LineColour = colour; + m_bUsesDiagramLineColour = false; +} + +void WidgetBase::setLineWidth(uint width) { + m_LineWidth = width; + m_bUsesDiagramLineWidth = false; +} + +void WidgetBase::saveToXMI( QDomDocument & /*qDoc*/, QDomElement & qElement ) { + if (m_bUsesDiagramLineColour) { + qElement.setAttribute( "linecolor", "none" ); + } else { + qElement.setAttribute( "linecolor", m_LineColour.name() ); + } + if (m_bUsesDiagramLineWidth) { + qElement.setAttribute( "linewidth", "none" ); + } else { + qElement.setAttribute( "linewidth", m_LineWidth ); + } +} + +bool WidgetBase::loadFromXMI( QDomElement & qElement ) { + // first load from "linecolour" and then overwrite with the "linecolor" + // attribute if that one is present. The "linecolour" name was a "typo" in + // earlier versions of Umbrello + QString lineColor = qElement.attribute( "linecolour", "none" ); + lineColor = qElement.attribute( "linecolor", lineColor ); + + QString lineWidth = qElement.attribute( "linewidth", "none" ); + if (lineColor != "none") { + setLineColor( QColor(lineColor) ); + m_bUsesDiagramLineColour = false; + } else if (m_Type != Uml::wt_Box && m_pView != NULL) { + setLineColor( m_pView->getLineColor() ); + m_bUsesDiagramLineColour = true; + } + if (lineWidth != "none") { + setLineWidth( lineWidth.toInt() ); + m_bUsesDiagramLineWidth = false; + } else if ( m_pView ) { + setLineWidth( m_pView->getLineWidth() ); + m_bUsesDiagramLineWidth = true; + } + return true; +} + +#include "widgetbase.moc" diff --git a/umbrello/umbrello/widgetbase.h b/umbrello/umbrello/widgetbase.h new file mode 100644 index 00000000..5051418e --- /dev/null +++ b/umbrello/umbrello/widgetbase.h @@ -0,0 +1,201 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef WIDGETBASE_H +#define WIDGETBASE_H + +#include +#include +#include + +#include "umlnamespace.h" + +// forward declarations +class UMLView; +class UMLObject; + +/** + * @short Common base class for UMLWidget and AssociationWidget + * @author Oliver Kellogg + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class WidgetBase : public QObject { + Q_OBJECT +public: + /** + * Creates a WidgetBase object. + * + * @param view The view to be displayed on. + */ + WidgetBase(UMLView * view); + + /** + * Standard deconstructor + */ + virtual ~WidgetBase() {} + + /** + * Write property of m_Type. + */ + void setBaseType(Uml::Widget_Type type); + + /** + * Read property of m_Type. + */ + Uml::Widget_Type getBaseType() const; + + /** + * Returns the @ref UMLObject set to represent. + * + * @return the UMLObject to represent. + */ + UMLObject *getUMLObject(); + + /** + * Deliver a const pointer to the connected UMLView + * ( needed esp. by event handling of LinePath ) + */ + const UMLView *getUMLView() const { return m_pView; } + + /** + * Sets the @ref UMLObject to represent. + * + * @param o The object to represent. + */ + virtual void setUMLObject(UMLObject * o); + + /** + * Used by some child classes to get documentation. + * + * @return The documentation from the UMLObject (if m_pObject is set.) + */ + virtual QString getDoc() const; + + /** + * Used by some child classes to set documentation. + * + * @param doc The documentation to be set in the UMLObject + * (if m_pObject is set.) + */ + virtual void setDoc( const QString &doc ); + + /** + * Sets the line colour + * + * @param colour the new line colour + */ + virtual void setLineColor(const QColor &colour); + + /** + * Sets the line width + * + * @param width the new line width + */ + virtual void setLineWidth(uint width); + + /** + * Read property of m_LineColour. + */ + QColor getLineColor() const { + return m_LineColour; + } + + /** + * Read property of m_LineWidth. + */ + uint getLineWidth() const { + return m_LineWidth; + } + + /** + * Returns m_bUsesDiagramLineColour + */ + bool getUsesDiagramLineColour() const { + return m_bUsesDiagramLineColour; + } + + /** + * Returns m_bUsesDiagramLineWidth + */ + bool getUsesDiagramLineWidth() const { + return m_bUsesDiagramLineWidth; + } + + /** + * Sets m_bUsesDiagramLineColour + */ + void setUsesDiagramLineColour(bool usesDiagramLineColour) { + m_bUsesDiagramLineColour = usesDiagramLineColour; + } + + /** + * Sets m_bUsesDiagramLineWidth + */ + void setUsesDiagramLineWidth(bool usesDiagramLineWidth) { + m_bUsesDiagramLineWidth = usesDiagramLineWidth; + } + + /** + * Write property of m_nId. + */ + void setID( Uml::IDType id ); + + /** + * Read property of m_nId. + */ + Uml::IDType getID() const; + + virtual void saveToXMI( QDomDocument & qDoc, QDomElement & qElement ); + + virtual bool loadFromXMI( QDomElement & qElement ); + +protected: + /** + * Initialize members. + */ + void init(UMLView *view, Uml::Widget_Type type = Uml::wt_UMLWidget); + + /** + * Type of widget. + */ + Uml::Widget_Type m_Type; + + UMLView *m_pView; + UMLObject *m_pObject; + QString m_Doc; ///< Only used if m_pObject is not set. + + /** + * This ID is only used when the widget does not have a + * corresponding UMLObject (i.e. the m_pObject pointer is NULL.) + * For UMLObjects, the ID from the UMLObject is used. + */ + Uml::IDType m_nId; + + /** + * Color of the lines of the widget. Is saved to XMI. + */ + QColor m_LineColour; + + /** + * Width of the lines of the widget. Is saved to XMI. + */ + uint m_LineWidth; + + /** + * true by default, false if the colours have + * been explicitly set for this widget. + * These are saved to XMI. + */ + bool m_bUsesDiagramLineColour, m_bUsesDiagramLineWidth; + +}; + +#endif diff --git a/umbrello/umbrello/worktoolbar.cpp b/umbrello/umbrello/worktoolbar.cpp new file mode 100644 index 00000000..0f27be80 --- /dev/null +++ b/umbrello/umbrello/worktoolbar.cpp @@ -0,0 +1,315 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +// own header +#include "worktoolbar.h" + +// qt/kde include files +#include +#include +#include +#include +#include + +// application specific includes +#include "uml.h" +#include "umldoc.h" +#include "umlview.h" +#include "worktoolbar.h" + + +WorkToolBar::WorkToolBar(QMainWindow *parentWindow, const char*name) + : KToolBar(parentWindow,Qt::DockRight,false,name) { + m_CurrentButtonID = tbb_Undefined; + loadPixmaps(); + m_Type = Uml::dt_Class; /* first time in just want it to load arrow, + needs anything but dt_Undefined */ + setOrientation( Qt::Vertical ); + setVerticalStretchable( true ); + // initialize old tool map, everything starts with select tool (arrow) + m_map.insert(Uml::dt_UseCase,tbb_Arrow); + m_map.insert(Uml::dt_Collaboration,tbb_Arrow); + m_map.insert(Uml::dt_Class,tbb_Arrow); + m_map.insert(Uml::dt_Sequence,tbb_Arrow); + m_map.insert(Uml::dt_State,tbb_Arrow); + m_map.insert(Uml::dt_Activity,tbb_Arrow); + m_map.insert(Uml::dt_Undefined,tbb_Arrow); + + slotCheckToolBar( Uml::dt_Undefined ); + connect( this, SIGNAL( released( int ) ), this, SLOT( buttonChanged (int ) ) ); +} + +WorkToolBar::~WorkToolBar() { + disconnect(this, SIGNAL(released(int)),this,SLOT(buttonChanged(int))); +} + +void WorkToolBar::insertHotBtn(ToolBar_Buttons tbb) { + insertButton(m_ToolButtons[tbb].Symbol, tbb, true, m_ToolButtons[tbb].Label); + setToggle(tbb, true); +} + +void WorkToolBar::insertBasicAssociations() { + insertHotBtn(tbb_Association); + if (m_Type == Uml::dt_Class || m_Type == Uml::dt_UseCase) { + insertHotBtn(tbb_UniAssociation); + } + insertHotBtn(tbb_Dependency); + insertHotBtn(tbb_Generalization); +} + +void WorkToolBar::slotCheckToolBar(Uml::Diagram_Type dt) { + if ( dt == m_Type ) + return; + clear(); + m_Type = dt; + + if ( m_Type == Uml::dt_Undefined ) + return; + + //insert note, anchor and lines of text on all diagrams + insertHotBtn(tbb_Arrow); + toggleButton(tbb_Arrow); + m_CurrentButtonID = tbb_Arrow; + + insertHotBtn(tbb_Note); + insertHotBtn(tbb_Anchor); + insertHotBtn(tbb_Text); + insertHotBtn(tbb_Box); + + //insert diagram specific tools + switch (m_Type) { + case Uml::dt_UseCase: + insertHotBtn(tbb_Actor); + insertHotBtn(tbb_UseCase); + insertBasicAssociations(); + break; + + case Uml::dt_Class: + insertHotBtn(tbb_Class); + insertHotBtn(tbb_Interface); + insertHotBtn(tbb_Datatype); + insertHotBtn(tbb_Enum); + insertHotBtn(tbb_Package); + insertBasicAssociations(); + insertHotBtn(tbb_Composition); + insertHotBtn(tbb_Aggregation); + insertHotBtn(tbb_Containment); + break; + + case Uml::dt_Sequence: + insertHotBtn(tbb_Object); + insertHotBtn(tbb_Seq_Message_Synchronous); + insertHotBtn(tbb_Seq_Message_Asynchronous); + break; + + case Uml::dt_Collaboration: + insertHotBtn(tbb_Object); + insertHotBtn(tbb_Coll_Message); + break; + + case Uml::dt_State: + insertHotBtn(tbb_Initial_State); + insertHotBtn(tbb_State); + insertHotBtn(tbb_End_State); + insertHotBtn(tbb_State_Transition); + //insertHotBtn(tbb_DeepHistory); //NotYetImplemented + //insertHotBtn(tbb_ShallowHistory); //NotYetImplemented + //insertHotBtn(tbb_Join); //NotYetImplemented + insertHotBtn(tbb_StateFork); + //insertHotBtn(tbb_Junction); //NotYetImplemented + //insertHotBtn(tbb_Choice); //NotYetImplemented + //insertHotBtn(tbb_Andline); //NotYetImplemented + break; + + case Uml::dt_Activity: + insertHotBtn(tbb_Initial_Activity); + insertHotBtn(tbb_Activity); + insertHotBtn(tbb_End_Activity); + insertHotBtn(tbb_Branch); + insertHotBtn(tbb_Fork); + insertHotBtn(tbb_Activity_Transition); + break; + + case Uml::dt_Component: + insertHotBtn(tbb_Interface); + insertHotBtn(tbb_Component); + insertHotBtn(tbb_Artifact); + insertBasicAssociations(); + break; + + case Uml::dt_Deployment: + insertHotBtn(tbb_Object); + insertHotBtn(tbb_Interface); + insertHotBtn(tbb_Component); + insertHotBtn(tbb_Node); + insertBasicAssociations(); + break; + + case Uml::dt_EntityRelationship: + insertHotBtn(tbb_Entity); + insertHotBtn(tbb_Relationship); + break; + + default: + kWarning() << "slotCheckToolBar() on unknown diagram type:" + << m_Type << endl; + break; + } +} + +void WorkToolBar::buttonChanged(int b) { + UMLView* view = UMLApp::app()->getCurrentView(); + + //if trying to turn off arrow - stop it + ToolBar_Buttons tbb = (ToolBar_Buttons)b; + if (tbb == tbb_Arrow && m_CurrentButtonID == tbb_Arrow) { + toggleButton(tbb_Arrow); + + // signal needed, in the case ( when switching diagrams ) that + // Arrow Button gets activated, but the toolBarState of the Views may be different + emit sigButtonChanged( m_CurrentButtonID ); + + view->setCursor( currentCursor() ); + return; + } + + //if toggling off a button set to arrow + if (tbb == m_CurrentButtonID) { + m_map[m_Type] = m_CurrentButtonID; // store old tool for this diagram type + toggleButton(tbb_Arrow); + m_CurrentButtonID = tbb_Arrow; + emit sigButtonChanged(m_CurrentButtonID); + view->setCursor( currentCursor() ); + return; + } + m_map[m_Type] = m_CurrentButtonID; + toggleButton(m_CurrentButtonID); + m_CurrentButtonID = tbb; + emit sigButtonChanged(m_CurrentButtonID); + view->setCursor( currentCursor() ); +} + +QCursor WorkToolBar::currentCursor() { + return m_ToolButtons[m_CurrentButtonID].Cursor; +} + +void WorkToolBar::slotResetToolBar() { + if (m_CurrentButtonID == tbb_Arrow) + return;//really shouldn't occur + toggleButton(m_CurrentButtonID); + m_CurrentButtonID = tbb_Arrow; + toggleButton(m_CurrentButtonID); + emit sigButtonChanged(m_CurrentButtonID); + + QCursor curs; + curs.setShape(Qt::ArrowCursor); + + UMLView* view = UMLApp::app()->getCurrentView(); + if (view != NULL) { + view -> setCursor(curs); + } +} + +void WorkToolBar::setOldTool() { + KToolBarButton *b = (KToolBarButton*) getWidget(m_map[m_Type]); + if (b) + b -> animateClick(); +} + +void WorkToolBar::setDefaultTool() { + KToolBarButton *b = (KToolBarButton*) getWidget(tbb_Arrow); + if (b) + b -> animateClick(); +} + +QPixmap WorkToolBar::load(const QString & fileName) { + QPixmap pxm; + pxm.load(fileName); + return pxm; +} + +void WorkToolBar::loadPixmaps() { + const struct ButtonInfo { + const ToolBar_Buttons tbb; + const QString btnName; + const char *pngName; + } buttonInfo[] = { + { tbb_Object, i18n("Object"), "object.png" }, + { tbb_Seq_Message_Synchronous, i18n("Synchronous Message"), "message-synchronous.png" }, + { tbb_Seq_Message_Asynchronous, i18n("Asynchronous Message"), "message-asynchronous.png" }, + { tbb_Association, i18n("Association"), "association.png" }, + { tbb_Containment, i18n("Containment"), "containment.png" }, + { tbb_Anchor, i18n("Anchor"), "anchor.png" }, + { tbb_Text, i18n("Label"), "text.png" }, + { tbb_Note, i18n("Note"), "note.png" }, + { tbb_Box, i18n("Box"), "box.png" }, + { tbb_Actor, i18n("Actor"), "actor.png" }, + { tbb_Dependency, i18n("Dependency"), "dependency.png" }, + { tbb_Aggregation, i18n("Aggregation"), "aggregation.png" }, + { tbb_Relationship, i18n("Relationship"), "relationship.png" }, + { tbb_UniAssociation, i18n("Directional Association"), "uniassociation.png" }, + { tbb_Generalization, i18n("Implements (Generalisation/Realisation)"), "generalisation.png" }, + { tbb_Composition, i18n("Composition"), "composition.png" }, + { tbb_UseCase, i18n("Use Case"), "usecase.png" }, + { tbb_Class, i18n("Class"), "class.png" }, + { tbb_Initial_State, i18n("Initial State"), "initial_state.png" }, + { tbb_End_State, i18n("End State"), "end_state.png" }, + { tbb_Branch, i18n("Branch/Merge"), "branch.png" }, + { tbb_Fork, i18n("Fork/Join"), "fork.png" }, + { tbb_Package, i18n("Package"), "package.png" }, + { tbb_Component, i18n("Component"), "component.png" }, + { tbb_Node, i18n("Node"), "node.png" }, + { tbb_Artifact, i18n("Artifact"), "artifact.png" }, + { tbb_Interface, i18n("Interface"), "interface.png" }, + { tbb_Datatype, i18n("Datatype"), "datatype.png" }, + { tbb_Enum, i18n("Enum"), "enum.png" }, + { tbb_Entity, i18n("Entity"), "entity.png" }, + { tbb_DeepHistory, i18n("Deep History"), "deep-history.png" }, //NotYetImplemented + { tbb_ShallowHistory, i18n("Shallow History"), "shallow-history.png" }, //NotYetImplemented + { tbb_Join, i18n("Join"), "join.png" }, //NotYetImplemented + { tbb_StateFork, i18n("Fork"), "state-fork.png" }, + { tbb_Junction, i18n("Junction"), "junction.png" }, //NotYetImplemented + { tbb_Choice, i18n("Choice"), "choice-round.png" }, //NotYetImplemented + //:TODO: let the user decide which symbol he wants (setting an option) + // { tbb_Choice, i18n("Choice"), "choice-rhomb.png" }, //NotYetImplemented + //{ tbb_Andline, i18n("And Line"), "andline.png" }, //NotYetImplemented + { tbb_State_Transition, i18n("State Transition"), "uniassociation.png" }, + { tbb_Activity_Transition, i18n("Activity Transition"), "uniassociation.png" }, + { tbb_Activity, i18n("Activity"), "usecase.png" }, + { tbb_State, i18n("State"), "usecase.png" }, + { tbb_End_Activity, i18n("End Activity"), "end_state.png" }, + { tbb_Initial_Activity, i18n("Initial Activity"), "initial_state.png" }, + { tbb_Coll_Message, i18n("Message"), "message-asynchronous.png" } + }; + KStandardDirs * dirs = KGlobal::dirs(); + QString dataDir = dirs->findResourceDir( "data", "umbrello/pics/object.png" ); + dataDir += "/umbrello/pics/"; + const size_t n_buttonInfos = sizeof(buttonInfo) / sizeof(ButtonInfo); + + m_ToolButtons.insert(tbb_Undefined, + ToolButton(i18n("UNDEFINED"), + 0, + QCursor()) ); + m_ToolButtons.insert(tbb_Arrow, + ToolButton(i18n("Select"), + load(dataDir + "arrow.png"), + QCursor()) ); + kDebug() << "WorkToolBar::loadPixmaps: n_buttonInfos = " << n_buttonInfos << endl; + for (uint i = 0; i < n_buttonInfos; i++) { + const ButtonInfo& info = buttonInfo[i]; + m_ToolButtons.insert(info.tbb, + ToolButton(info.btnName, + load(dataDir + info.pngName), + QCursor(load(dataDir + "cursor-" + info.pngName), 9, 9))); + } +} + +#include "worktoolbar.moc" diff --git a/umbrello/umbrello/worktoolbar.h b/umbrello/umbrello/worktoolbar.h new file mode 100644 index 00000000..92012dde --- /dev/null +++ b/umbrello/umbrello/worktoolbar.h @@ -0,0 +1,183 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2002-2007 * + * Umbrello UML Modeller Authors * + ***************************************************************************/ + +#ifndef WORKTOOLBAR_H +#define WORKTOOLBAR_H + +#include +#include +#include +#include + +#include "umlnamespace.h" + +class QMainWindow; + + +/** + * This is the toolbar that is displayed on the right-hand side of the program + * window. For each type of diagram it will change to suit that document. + * + * To add a new tool button do the following: + * - create a button pixmap (symbol) + * - create a cursor pixmap + * - add an element to the ToolBar_Buttons enum + * - adjust function loadPixmaps + * - adjust function slotCheckToolBar + * + * @short The toolbar that is different for each type of diagram. + * @author Paul Hensgen + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + + +class WorkToolBar : public KToolBar { + Q_OBJECT +public: + + /** + * Creates a work tool bar. + * + * @param parentWindow The parent of the toolbar. + */ + WorkToolBar(QMainWindow *parentWindow, const char *name); + + /** + * Standard deconstructor. + */ + ~WorkToolBar(); + + /** + * Sets the current tool to the previously used Tool. This is just + * as if the user had pressed the button for the other tool. + */ + void setOldTool(); + + /** + * Sets the current tool to the default tool. (select tool) + * Calling this function is as if the user had pressed the "arrow" + * button on the toolbar. + */ + void setDefaultTool(); + + /** + * Enumeration of all available toolbar buttons. + */ + enum ToolBar_Buttons + { + tbb_Undefined = -1, + tbb_Arrow, + tbb_Generalization, + tbb_Aggregation, + tbb_Dependency, + tbb_Association, + tbb_Containment, + tbb_Coll_Message, + tbb_Seq_Message_Synchronous, + tbb_Seq_Message_Asynchronous, + tbb_Composition, + tbb_Relationship, + tbb_UniAssociation, + tbb_State_Transition, + tbb_Activity_Transition, + tbb_Anchor,//keep anchor as last association until code uses better algorithm for testing + tbb_Note, + tbb_Box, + tbb_Text, + tbb_Actor, + tbb_UseCase, + tbb_Class, + tbb_Interface, + tbb_Datatype, + tbb_Enum, + tbb_Entity, + tbb_Package, + tbb_Component, + tbb_Node, + tbb_Artifact, + tbb_Object, + tbb_Initial_State, + tbb_State, + tbb_End_State, + tbb_Initial_Activity, + tbb_Activity, + tbb_End_Activity, + tbb_Branch, + tbb_Fork, + tbb_DeepHistory, + tbb_ShallowHistory, + tbb_Join, + tbb_StateFork, + tbb_Junction, + tbb_Choice, + tbb_Andline + }; + +private: + + typedef QMap OldToolMap; + + /** + * This inner class holds label, symbol, and cursor of a tool button. + */ + class ToolButton { + public: + QString Label; + QPixmap Symbol; + QCursor Cursor; + ToolButton() : Label(QString("?")), Symbol(QPixmap()), Cursor(QCursor()) { } + ToolButton(const QString& lbl, const QPixmap& smb, const QCursor& cur) : + Label(lbl), Symbol(smb), Cursor(cur) { } + }; + + typedef QMap ToolButtonMap; + + ToolBar_Buttons m_CurrentButtonID; + OldToolMap m_map; + Uml::Diagram_Type m_Type; + ToolButtonMap m_ToolButtons; + + /** + * Loads a pixmap from file + */ + QPixmap load(const QString &fileName); + + /** + * Loads toolbar icon and mouse cursor images from disk + */ + void loadPixmaps(); + + /** + * Returns the current cursor depending on m_CurrentButtonID + */ + QCursor currentCursor(); + + /** + * Inserts the button corresponding to the tbb value given + * and activates the toggle. + */ + void insertHotBtn(ToolBar_Buttons tbb); + + /** + * Inserts most associations, just reduces some string + * duplication (nice to translators) + */ + void insertBasicAssociations(); + +signals: + void sigButtonChanged(int); +public slots: + void slotCheckToolBar(Uml::Diagram_Type dt); + void buttonChanged(int b); + void slotResetToolBar(); +}; + +#endif diff --git a/umbrello/umbrello/x-umbrello.desktop b/umbrello/umbrello/x-umbrello.desktop new file mode 100644 index 00000000..d8aaeb50 --- /dev/null +++ b/umbrello/umbrello/x-umbrello.desktop @@ -0,0 +1,53 @@ +[Desktop Entry] +Type=MimeType +MimeType=application/x-uml +Icon=umbrellofile +DefaultApp=umbrello +Patterns=*.xmi;*.XMI;*.xmi.tgz;*.xmi.tar.bz2; +Comment=Umbrello UML Modeller File +Comment[bg]=Файл Umbrello UML Modeller +Comment[bs]=Umbrello UML modeler datoteka +Comment[ca]=Fitxer de model UML +Comment[cs]=Soubor UML modeláře Umbrello +Comment[cy]=Ffeil Modelydd UML Umbrello +Comment[da]=Umbrello UML-fil +Comment[de]=Umbrello-Datei +Comment[el]=Αρχείο μοντελοποιητή UML Umbrello +Comment[eo]=Dosiero de Umbrello UML-Modelilo +Comment[es]=Archivo del modeladore UML Umbrello +Comment[et]=Umbrello UML-fail +Comment[eu]=Umbrello UML modelatzaile fitxategia +Comment[fa]=پروندۀ مدل‌ساز Umbrello UML +Comment[fi]=Umbrello UML -mallinnustiedosto +Comment[fr]=Fichier du modeleur UML Umbrello +Comment[ga]=Comhad le haghaidh an Mhúnlóra UML Umbrello +Comment[gl]=Ficheiro UML de Umbrello +Comment[hi]=अम्बरैलो यूएमएल मॉडलर फ़ाइल +Comment[hu]=Umbrello UML-fájl +Comment[is]=Umbrello UML Modeller Skrá +Comment[it]=File del modellatore UML Umbrello +Comment[ja]=Umbrello UML モデラーファイル +Comment[ka]=Umbrello UML მოდელერის ფაილი +Comment[kk]=Umbrello UML үлгілегішінің файлы +Comment[lt]=Umbrello UML Modeller byla +Comment[ms]=Fail Pemodel Umbrello UML +Comment[nb]=Umbrello UML-modelleringsfil +Comment[nds]=UML-Modellmaker-Datei vun Umbrello +Comment[ne]=अम्ब्रेलो यूएमएल मोडेलर फाइल +Comment[nl]=Umbrello UML Modeller-bestand +Comment[nn]=Umbrello UML-modelleringsfil +Comment[pl]=Plik modelera UML Umbrello +Comment[pt]=Ficheiro do Modelador de UML Umbrello +Comment[pt_BR]=Arquivo de Modelador UML Umbrello +Comment[ru]=Файл моделирования UML Umbrello +Comment[sk]=Súbor pre Umbrello modelár UML +Comment[sl]=Datoteka za modelirnik Umbrello UML +Comment[sr]=Фајл Umbrello-а, UML моделара +Comment[sr@Latn]=Fajl Umbrello-a, UML modelara +Comment[sv]=Umbrello UML-modelleringsfil +Comment[ta]= அம்ரல்லோ UML மாடுலர் கோப்பு +Comment[tg]=Файли моделкунонии UML Umbrello +Comment[tr]=Umbrello UML Modelleyici Dosyası +Comment[uk]=Файл моделювання UML Umbrello +Comment[zh_CN]=Umbrello UML 建模工具文件 +Comment[zh_TW]=Umbrello UML Modeller 檔案 diff --git a/umbrello/uml.kdevprj b/umbrello/uml.kdevprj new file mode 100644 index 00000000..284aee44 --- /dev/null +++ b/umbrello/uml.kdevprj @@ -0,0 +1,2154 @@ +[Config for BinMakefileAm] +addcxxflags= +bin_program=umbrello +cflags= +cppflags= +cxxflags=\s-O0 -g3 -Wall +ldadd=-lfl $(LIB_KDEPRINT) $(LIB_KDEUI) $(LIB_KDECORE) $(LIB_QT) +ldflags=\s -Iuml/classparser -Iuml/dialogs -Iuml/clipboard + +[General] +AMChanged=true +author=Umbrello UML Modeller Authors +configure_args=\s--build=i386-linux --host=i386-linux --target=i386-linux --prefix=/opt/kde3 --enable-debug\s +email=uml-devel@lists.sourceforge.net +kdevprj_version=1.3 +lfv_open_groups= +make_options=\s-j1 +makefiles=uml.spec.in,Makefile.am,uml/Makefile.am,doc/Makefile.am,doc/en/Makefile.am,po/Makefile.am,uml/classparser/Makefile.am,uml/clipboard/Makefile.am,uml/dialogs/Makefile.am,uml/pics/Makefile.am,doc/en/pics/Makefile.am,uml/codegenerators/Makefile.am,uml/headings/Makefile.am,doc/de/Makefile.am,doc/de/pics/Makefile.am +modifyMakefiles=true +project_name=Umbrello +project_type=normal_kde2 +short_info=Umbrello UML Modeller +showNonProjectFiles=true +sub_dir=uml/ +version=1.1rc1 +version_control=CVS +workspace=1 + +[KPP] +kpp_appgrp= +kpp_bldroot= +kpp_icon=0 +kpp_license=5 +kpp_summary= +kpp_url= +kpp_version=1.1 + +[LFV Groups] +Headers=*.h,*.hh,*.hxx,*.hpp,*.H +Others=* +Sources=*.cpp,*.c,*.cc,*.C,*.cxx,*.ec,*.ecpp,*.lxx,*.l++,*.ll,*.l +Translations=*.po +User Interface=*.kdevdlg,*.ui,*.rc,*.dlg +groups=Headers,Sources,User Interface,Translations,Others + +[Makefile.am] +files=uml.kdevprj,admin,uml.spec.in +sub_dirs=uml,po,doc +type=normal + +[admin] +dist=true +install=false +install_location= +type=DATA + +[doc/Makefile.am] +sub_dirs=en,de +type=normal + +[doc/de/Makefile.am] +files=doc/de/authors.docbook,doc/de/code_import_and_generation.docbook,doc/de/faq.docbook,doc/de/index.docbook,doc/de/installation.docbook,doc/de/introduction.docbook,doc/de/working_with_umbrello.docbook,doc/de/menu_reference.docbook,doc/de/other_features.docbook,doc/de/uml_basics.docbook +sub_dirs=pics +type=normal + +[doc/de/authors.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/de/code_import_and_generation.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/de/faq.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/de/index.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/de/installation.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/de/introduction.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/de/menu_reference.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/de/other_features.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/de/pics/Makefile.am] +files=doc/de/pics/activity-diagram.png,doc/de/pics/add-remove-languages.png,doc/de/pics/aggregation.png,doc/de/pics/association.png,doc/de/pics/class-diagram.png,doc/de/pics/class.png,doc/de/pics/code-import.png,doc/de/pics/collaboration-diagram.png,doc/de/pics/composition.png,doc/de/pics/folders.png,doc/de/pics/generalization.png,doc/de/pics/generation-options.png,doc/de/pics/sequence-diagram.png,doc/de/pics/state-diagram.png,doc/de/pics/umbrello-main-screen.png,doc/de/pics/umbrello-ui-clean.png,doc/de/pics/umbrello-ui.png,doc/de/pics/use-case-diagram.png +sub_dirs= +type=normal + +[doc/de/pics/activity-diagram.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/activity-diagram.png +type=DATA + +[doc/de/pics/add-remove-languages.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/add-remove-languages.png +type=DATA + +[doc/de/pics/aggregation.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/aggregation.png +type=DATA + +[doc/de/pics/association.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/association.png +type=DATA + +[doc/de/pics/class-diagram.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/class-diagram.png +type=DATA + +[doc/de/pics/class.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/class.png +type=DATA + +[doc/de/pics/code-import.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/code-import.png +type=DATA + +[doc/de/pics/collaboration-diagram.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/collaboration-diagram.png +type=DATA + +[doc/de/pics/composition.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/composition.png +type=DATA + +[doc/de/pics/folders.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/folders.png +type=DATA + +[doc/de/pics/generalization.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/generalization.png +type=DATA + +[doc/de/pics/generation-options.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/generation-options.png +type=DATA + +[doc/de/pics/sequence-diagram.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/sequence-diagram.png +type=DATA + +[doc/de/pics/state-diagram.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/state-diagram.png +type=DATA + +[doc/de/pics/umbrello-main-screen.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/umbrello-main-screen.png +type=DATA + +[doc/de/pics/umbrello-ui-clean.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/umbrello-ui-clean.png +type=DATA + +[doc/de/pics/umbrello-ui.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/umbrello-ui.png +type=DATA + +[doc/de/pics/use-case-diagram.png] +dist=true +install=true +install_location=$$(kde_htmldir)/de/umbrello/pics/use-case-diagram.png +type=DATA + +[doc/de/uml_basics.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/de/working_with_umbrello.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/en/Makefile.am] +files=doc/en/index.docbook,doc/en/introduction.docbook,doc/en/uml_basics.docbook,doc/en/installation.docbook,doc/en/faq.docbook,doc/en/menu_reference.docbook,doc/en/code_import_and_generation.docbook,doc/en/authors.docbook,doc/en/error_msgs.docbook,doc/en/working_with_umbrello.docbook,doc/en/other_features.docbook +sub_dirs=pics +type=normal + +[doc/en/authors.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/en/code_import_and_generation.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/en/error_msgs.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/en/faq.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/en/index.docbook] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/index.docbook +type=DATA + +[doc/en/installation.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/en/introduction.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/en/menu_reference.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/en/other_features.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/en/pics/Makefile.am] +files=doc/en/pics/add-remove-languages.png,doc/en/pics/aggregation.png,doc/en/pics/association.png,doc/en/pics/class-diagram.png,doc/en/pics/class.png,doc/en/pics/code-import.png,doc/en/pics/collaboration-diagram.png,doc/en/pics/composition.png,doc/en/pics/folders.png,doc/en/pics/generalization.png,doc/en/pics/generation-options.png,doc/en/pics/sequence-diagram.png,doc/en/pics/state-diagram.png,doc/en/pics/umbrello-main-screen.png,doc/en/pics/umbrello-ui.png,doc/en/pics/umbrello-ui-clean.png,doc/en/pics/activity-diagram.png,doc/en/pics/use-case-diagram.png +sub_dirs= +type=normal + +[doc/en/pics/activity-diagram.png] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/pics/activity-diagram.png +type=DATA + +[doc/en/pics/add-remove-languages.png] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/pics/add-remove-languages.png +type=DATA + +[doc/en/pics/aggregation.png] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/pics/aggregation.png +type=DATA + +[doc/en/pics/association.png] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/pics/association.png +type=DATA + +[doc/en/pics/class-diagram.png] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/pics/class-diagram.png +type=DATA + +[doc/en/pics/class.png] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/pics/class.png +type=DATA + +[doc/en/pics/code-import.png] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/pics/code-import.png +type=DATA + +[doc/en/pics/collaboration-diagram.png] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/pics/collaboration-diagram.png +type=DATA + +[doc/en/pics/composition.png] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/pics/composition.png +type=DATA + +[doc/en/pics/folders.png] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/pics/folders.png +type=DATA + +[doc/en/pics/generalization.png] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/pics/generalization.png +type=DATA + +[doc/en/pics/generation-options.png] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/pics/generation-options.png +type=DATA + +[doc/en/pics/sequence-diagram.png] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/pics/sequence-diagram.png +type=DATA + +[doc/en/pics/state-diagram.png] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/pics/state-diagram.png +type=DATA + +[doc/en/pics/umbrello-main-screen.png] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/pics/umbrello-main-screen.png +type=DATA + +[doc/en/pics/umbrello-ui-clean.png] +dist=true +install=false +install_location= +type=DATA + +[doc/en/pics/umbrello-ui.png] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/pics/umbrello-ui.png +type=DATA + +[doc/en/pics/use-case-diagram.png] +dist=true +install=true +install_location=$$(kde_htmldir)/en/umbrello/pics/use-case-diagram.png +type=DATA + +[doc/en/uml_basics.docbook] +dist=true +install=false +install_location= +type=DATA + +[doc/en/working_with_umbrello.docbook] +dist=true +install=false +install_location= +type=DATA + +[po/Makefile.am] +sub_dirs= +type=po + +[uml.kdevprj] +dist=true +install=false +install_location= +type=DATA + +[uml.spec.in] +dist=true +install=false +install_location= +type=DATA + +[uml/Makefile.am] +files=uml/actor.h,uml/actorwidgetdata.h,uml/actorwidget.h,uml/associationwidgetdata.h,uml/associationwidgetdatalist.h,uml/associationwidget.h,uml/assocrules.h,uml/attribute.h,uml/concept.h,uml/conceptwidgetdata.h,uml/conceptwidget.h,uml/docwindow.h,uml/floatingtextdata.h,uml/floatingtext.h,uml/infowidget.h,uml/kstartuplogo.h,uml/linepath.h,uml/listpopupmenu.h,uml/messagewidgetdata.h,uml/messagewidget.h,uml/actor.cpp,uml/actorwidget.cpp,uml/actorwidgetdata.cpp,uml/associationwidget.cpp,uml/associationwidgetdata.cpp,uml/assocrules.cpp,uml/attribute.cpp,uml/concept.cpp,uml/conceptwidget.cpp,uml/conceptwidgetdata.cpp,uml/docwindow.cpp,uml/floatingtext.cpp,uml/floatingtextdata.cpp,uml/infowidget.cpp,uml/kstartuplogo.cpp,uml/linepath.cpp,uml/listpopupmenu.cpp,uml/main.cpp,uml/messagewidget.cpp,uml/messagewidgetdata.cpp,uml/notewidget.cpp,uml/notewidgetdata.cpp,uml/notewidgetdata.h,uml/notewidget.h,uml/objectwidget.cpp,uml/objectwidgetdata.cpp,uml/objectwidgetdata.h,uml/objectwidget.h,uml/operation.cpp,uml/operation.h,uml/statewidget.cpp,uml/statewidgetdata.cpp,uml/statewidgetdata.h,uml/statewidget.h,uml/uml.cpp,uml/umldoc.cpp,uml/umldoc.h,uml/uml.h,uml/umlnamespace.cpp,uml/umlnamespace.h,uml/umlobject.cpp,uml/umlobject.h,uml/umlobjectlist.h,uml/umlviewcanvas.cpp,uml/umlviewcanvas.h,uml/umlview.cpp,uml/umlviewdata.cpp,uml/umlviewdata.h,uml/umlview.h,uml/umlviewlist.h,uml/umlwidget.cpp,uml/umlwidgetdata.cpp,uml/umlwidgetdata.h,uml/umlwidget.h,uml/umlwidgetlist.h,uml/usecase.cpp,uml/usecase.h,uml/usecasewidget.cpp,uml/usecasewidgetdata.cpp,uml/usecasewidgetdata.h,uml/usecasewidget.h,uml/worktoolbar.cpp,uml/worktoolbar.h,uml/hi16-app-uml.png,uml/hi32-app-uml.png,uml/lo16-app-uml.png,uml/lo32-app-uml.png,uml/tips,uml/uml.desktop,uml/umlui.rc,uml/x-uml.desktop,uml/activitywidgetdata.cpp,uml/activitywidgetdata.h,uml/activitywidget.cpp,uml/activitywidget.h,uml/codegenerator.cpp,uml/codegenerator.h,uml/seqlinewidget.cpp,uml/seqlinewidget.h,uml/umllistview.cpp,uml/umllistview.h,uml/umllistviewitem.cpp,uml/umllistviewitem.h,uml/umllistviewitemdata.cpp,uml/umllistviewitemdata.h,uml/umllistviewitemdatalist.h,uml/classimport.cpp,uml/classimport.h,uml/umllistviewitemlist.h +sub_dirs=classparser,clipboard,dialogs,pics,codegenerators,headings +type=prog_main + +[uml/activitywidget.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/activitywidget.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/activitywidgetdata.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/activitywidgetdata.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/actor.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/actor.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/actorwidget.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/actorwidget.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/actorwidgetdata.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/actorwidgetdata.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/associationwidget.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/associationwidget.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/associationwidgetdata.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/associationwidgetdata.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/associationwidgetdatalist.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/assocrules.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/assocrules.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/attribute.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/attribute.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classimport.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/classimport.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classparser/ClassParser.cc] +dist=true +install=false +install_location= +type=SOURCE + +[uml/classparser/ClassParser.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classparser/ClassStore.cc] +dist=true +install=false +install_location= +type=SOURCE + +[uml/classparser/ClassStore.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classparser/ClassTreeNode.cc] +dist=true +install=false +install_location= +type=SOURCE + +[uml/classparser/ClassTreeNode.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classparser/Makefile.am] +files=uml/classparser/ClassParser.h,uml/classparser/ClassStore.h,uml/classparser/ClassTreeNode.h,uml/classparser/ParsedArgument.h,uml/classparser/ParsedAttribute.h,uml/classparser/ParsedClassContainer.h,uml/classparser/ParsedClass.h,uml/classparser/ParsedContainer.h,uml/classparser/ParsedItem.h,uml/classparser/ParsedMethod.h,uml/classparser/ParsedParent.h,uml/classparser/ParsedScopeContainer.h,uml/classparser/ParsedSignalSlot.h,uml/classparser/ParsedStruct.h,uml/classparser/PersistantClassStore.h,uml/classparser/ProgrammingByContract.h,uml/classparser/tokenizer.h,uml/classparser/ClassParser.cc,uml/classparser/ClassStore.cc,uml/classparser/ClassTreeNode.cc,uml/classparser/ParsedArgument.cc,uml/classparser/ParsedAttribute.cc,uml/classparser/ParsedClass.cc,uml/classparser/ParsedClassContainer.cc,uml/classparser/ParsedContainer.cc,uml/classparser/ParsedItem.cc,uml/classparser/ParsedMethod.cc,uml/classparser/ParsedParent.cc,uml/classparser/ParsedScopeContainer.cc,uml/classparser/ParsedSignalSlot.cc,uml/classparser/ParsedStruct.cc,uml/classparser/PersistantClassStore.cc,uml/classparser/tokenizer.cc +sharedlib_LDFLAGS= +sharedlib_rootname= +sub_dirs= +type=static_library + +[uml/classparser/ParsedArgument.cc] +dist=true +install=false +install_location= +type=SOURCE + +[uml/classparser/ParsedArgument.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classparser/ParsedAttribute.cc] +dist=true +install=false +install_location= +type=SOURCE + +[uml/classparser/ParsedAttribute.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classparser/ParsedClass.cc] +dist=true +install=false +install_location= +type=SOURCE + +[uml/classparser/ParsedClass.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classparser/ParsedClassContainer.cc] +dist=true +install=false +install_location= +type=SOURCE + +[uml/classparser/ParsedClassContainer.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classparser/ParsedContainer.cc] +dist=true +install=false +install_location= +type=SOURCE + +[uml/classparser/ParsedContainer.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classparser/ParsedItem.cc] +dist=true +install=false +install_location= +type=SOURCE + +[uml/classparser/ParsedItem.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classparser/ParsedMethod.cc] +dist=true +install=false +install_location= +type=SOURCE + +[uml/classparser/ParsedMethod.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classparser/ParsedParent.cc] +dist=true +install=false +install_location= +type=SOURCE + +[uml/classparser/ParsedParent.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classparser/ParsedScopeContainer.cc] +dist=true +install=false +install_location= +type=SOURCE + +[uml/classparser/ParsedScopeContainer.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classparser/ParsedSignalSlot.cc] +dist=true +install=false +install_location= +type=SOURCE + +[uml/classparser/ParsedSignalSlot.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classparser/ParsedStruct.cc] +dist=true +install=false +install_location= +type=SOURCE + +[uml/classparser/ParsedStruct.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classparser/PersistantClassStore.cc] +dist=true +install=false +install_location= +type=SOURCE + +[uml/classparser/PersistantClassStore.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classparser/ProgrammingByContract.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/classparser/tokenizer.cc] +dist=true +install=false +install_location= +type=SOURCE + +[uml/classparser/tokenizer.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/clipboard/Makefile.am] +files=uml/clipboard/idchangelog.cpp,uml/clipboard/idchangelog.h,uml/clipboard/umlclipboard.cpp,uml/clipboard/umlclipboard.h,uml/clipboard/umldrag.cpp,uml/clipboard/umldrag.h +sharedlib_LDFLAGS=-version-info 0:0:0 +sharedlib_rootname=clipboard +sub_dirs= +type=static_library + +[uml/clipboard/idchangelog.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/clipboard/idchangelog.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/clipboard/umlclipboard.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/clipboard/umlclipboard.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/clipboard/umldrag.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/clipboard/umldrag.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/codegenerator.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/codegenerator.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/codegenerators/Makefile.am] +files=uml/codegenerators/cppwriter.cpp,uml/codegenerators/cppwriter.h,uml/codegenerators/factory.cpp,uml/codegenerators/javawriter.cpp,uml/codegenerators/javawriter.h,uml/codegenerators/phpwriter.cpp,uml/codegenerators/phpwriter.h +sharedlib_LDFLAGS=-module +sharedlib_rootname=codegenerator +sub_dirs= +type=shared_library + +[uml/codegenerators/cppwriter.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/codegenerators/cppwriter.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/codegenerators/factory.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/codegenerators/javawriter.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/codegenerators/javawriter.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/codegenerators/phpwriter.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/codegenerators/phpwriter.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/concept.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/concept.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/conceptwidget.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/conceptwidget.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/conceptwidgetdata.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/conceptwidgetdata.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/Makefile.am] +files=uml/dialogs/assocpage.cpp,uml/dialogs/assocpage.h,uml/dialogs/assocpropdlg.cpp,uml/dialogs/assocpropdlg.h,uml/dialogs/classattpage.cpp,uml/dialogs/classattpage.h,uml/dialogs/classgenpage.cpp,uml/dialogs/classgenpage.h,uml/dialogs/classopspage.cpp,uml/dialogs/classopspage.h,uml/dialogs/classoptionspage.cpp,uml/dialogs/classoptionspage.h,uml/dialogs/classpropdlg.cpp,uml/dialogs/classpropdlg.h,uml/dialogs/diagramprintpage.cpp,uml/dialogs/diagramprintpage.h,uml/dialogs/parmpropdlg.cpp,uml/dialogs/parmpropdlg.h,uml/dialogs/selectopdlg.cpp,uml/dialogs/selectopdlg.h,uml/dialogs/settingsdlg.cpp,uml/dialogs/settingsdlg.h,uml/dialogs/statedialog.cpp,uml/dialogs/statedialog.h,uml/dialogs/umlattributedialog.h,uml/dialogs/umloperationdialog.h,uml/dialogs/umlviewdialog.h,uml/dialogs/umlwidgetcolorpage.h,uml/dialogs/umlattributedialog.cpp,uml/dialogs/umloperationdialog.cpp,uml/dialogs/umlviewdialog.cpp,uml/dialogs/umlwidgetcolorpage.cpp,uml/dialogs/activitypage.cpp,uml/dialogs/activitypage.h,uml/dialogs/activitydialog.cpp,uml/dialogs/activitydialog.h,uml/dialogs/notedialog.cpp,uml/dialogs/notedialog.h,uml/dialogs/configcodegenerators.cpp,uml/dialogs/configcodegenerators.h,uml/dialogs/configgeneratorsbase.ui,uml/dialogs/classwizard.cpp,uml/dialogs/classwizard.h,uml/dialogs/sellanguagesbase.ui,uml/dialogs/selectlanguagesdlg.cpp,uml/dialogs/selectlanguagesdlg.h,uml/dialogs/codegenerationoptionsbase.ui,uml/dialogs/codegenerationwizardbase.ui,uml/dialogs/codegenerationoptionspage.cpp,uml/dialogs/codegenerationoptionspage.h,uml/dialogs/codegenerationwizard.cpp,uml/dialogs/codegenerationwizard.h,uml/dialogs/overwritedialogue.cpp,uml/dialogs/overwritedialogue.h +sharedlib_LDFLAGS=-version-info 0:0:0 +sharedlib_rootname=dialogs +sub_dirs= +type=static_library + +[uml/dialogs/activitydialog.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/activitydialog.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/activitypage.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/activitypage.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/assocpage.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/assocpage.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/assocpropdlg.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/assocpropdlg.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/classattpage.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/classattpage.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/classgenpage.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/classgenpage.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/classopspage.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/classopspage.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/classoptionspage.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/classoptionspage.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/classpropdlg.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/classpropdlg.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/classselectionpage.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/classselectionpage.h] +dist=false +install=false +install_location= +type=HEADER + +[uml/dialogs/classwizard.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/classwizard.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/codegenerationoptionsbase.ui] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/codegenerationoptionspage.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/codegenerationoptionspage.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/codegenerationwizard.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/codegenerationwizard.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/codegenerationwizardbase.ui] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/codegenwizard.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/codegenwizard.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/codeoptionspage.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/codeoptionspage.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/configcodegenerators.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/configcodegenerators.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/configgeneratorsbase.ui] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/diagramprintpage.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/diagramprintpage.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/notedialog.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/notedialog.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/overwritedialogue.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/overwritedialogue.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/parmpropdlg.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/parmpropdlg.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/selectlanguagesdlg.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/selectlanguagesdlg.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/selectopdlg.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/selectopdlg.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/sellanguagesbase.ui] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/settingsdlg.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/settingsdlg.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/statedialog.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/statedialog.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/umlattributedialog.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/umlattributedialog.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/umloperationdialog.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/umloperationdialog.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/umlviewdialog.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/umlviewdialog.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/dialogs/umlwidgetcolorpage.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/dialogs/umlwidgetcolorpage.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/docwindow.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/docwindow.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/floatingtext.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/floatingtext.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/floatingtextdata.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/floatingtextdata.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/headings/Makefile.am] +files=uml/headings/heading.java,uml/headings/heading.h,uml/headings/template.cpp,uml/headings/heading.php,uml/headings/heading.cpp +sub_dirs= +type=normal + +[uml/headings/heading.cpp] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/headings/heading.cpp +type=SOURCE + +[uml/headings/heading.h] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/headings/heading.h +type=DATA + +[uml/headings/heading.java] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/headings/heading.java +type=DATA + +[uml/headings/heading.php] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/headings/heading.php +type=DATA + +[uml/headings/template.cpp] +dist=false +install=false +install_location=$$(kde_datadir)/umbrello/headings/template.cpp +type=DATA + +[uml/hi16-app-uml.png] +dist=true +install=true +install_location=$$(kde_icondir)/hicolor/16x16/apps/umbrello.png +type=DATA + +[uml/hi32-app-uml.png] +dist=true +install=true +install_location=$$(kde_icondir)/hicolor/32x32/apps/umbrello.png +type=DATA + +[uml/infowidget.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/infowidget.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/kstartuplogo.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/kstartuplogo.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/linepath.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/linepath.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/listpopupmenu.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/listpopupmenu.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/lo16-app-uml.png] +dist=true +install=true +install_location=$$(kde_icondir)/locolor/16x16/apps/umbrello.png +type=DATA + +[uml/lo32-app-uml.png] +dist=true +install=true +install_location=$$(kde_icondir)/locolor/32x32/apps/umbrello.png +type=DATA + +[uml/main.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/messagewidget.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/messagewidget.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/messagewidgetdata.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/messagewidgetdata.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/notewidget.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/notewidget.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/notewidgetdata.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/notewidgetdata.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/objectwidget.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/objectwidget.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/objectwidgetdata.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/objectwidgetdata.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/operation.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/operation.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/pics/CVglobal_meth.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/CVglobal_meth.png +type=DATA + +[uml/pics/CVglobal_var.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/CVglobal_var.png +type=DATA + +[uml/pics/CVnamespace.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/CVnamespace.png +type=DATA + +[uml/pics/CVprivate_meth.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/CVprivate_meth.png +type=DATA + +[uml/pics/CVprivate_signal.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/CVprivate_signal.png +type=DATA + +[uml/pics/CVprivate_slot.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/CVprivate_slot.png +type=DATA + +[uml/pics/CVprivate_var.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/CVprivate_var.png +type=DATA + +[uml/pics/CVprotected_meth.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/CVprotected_meth.png +type=DATA + +[uml/pics/CVprotected_signal.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/CVprotected_signal.png +type=DATA + +[uml/pics/CVprotected_slot.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/CVprotected_slot.png +type=DATA + +[uml/pics/CVprotected_var.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/CVprotected_var.png +type=DATA + +[uml/pics/CVpublic_meth.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/CVpublic_meth.png +type=DATA + +[uml/pics/CVpublic_signal.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/CVpublic_signal.png +type=DATA + +[uml/pics/CVpublic_slot.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/CVpublic_slot.png +type=DATA + +[uml/pics/CVpublic_var.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/CVpublic_var.png +type=DATA + +[uml/pics/CVstruct.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/CVstruct.png +type=DATA + +[uml/pics/Makefile.am] +files=uml/pics/actor.xpm,uml/pics/aggregation.xpm,uml/pics/anchor.xpm,uml/pics/arrow.xpm,uml/pics/association.xpm,uml/pics/broom.xpm,uml/pics/case.xpm,uml/pics/classicon.xpm,uml/pics/component.xpm,uml/pics/composition.xpm,uml/pics/constraint.xpm,uml/pics/CVglobal_meth.png,uml/pics/CVglobal_var.png,uml/pics/CVnamespace.png,uml/pics/CVprivate_meth.png,uml/pics/CVprivate_signal.png,uml/pics/CVprivate_slot.png,uml/pics/CVprivate_var.png,uml/pics/CVprotected_meth.png,uml/pics/CVprotected_signal.png,uml/pics/CVprotected_slot.png,uml/pics/CVprotected_var.png,uml/pics/CVpublic_meth.png,uml/pics/CVpublic_signal.png,uml/pics/CVpublic_slot.png,uml/pics/CVpublic_var.png,uml/pics/CVstruct.png,uml/pics/dependency.xpm,uml/pics/end_state.xpm,uml/pics/folder_green_open.png,uml/pics/folder_green.png,uml/pics/folder_grey_open.png,uml/pics/folder_grey.png,uml/pics/folder_home.png,uml/pics/generalization.xpm,uml/pics/hline.xpm,uml/pics/implements.xpm,uml/pics/initial_state.xpm,uml/pics/interface.xpm,uml/pics/largepackage.xpm,uml/pics/lifeline.xpm,uml/pics/line.xpm,uml/pics/message.xpm,uml/pics/note.xpm,uml/pics/object.xpm,uml/pics/output_win.xpm,uml/pics/realizes.xpm,uml/pics/rectangle.xpm,uml/pics/smallpackage.xpm,uml/pics/snapshot.xpm,uml/pics/startlogo.png,uml/pics/text.xpm,uml/pics/umlclass_template.xpm,uml/pics/umlclass.xpm,uml/pics/uniassoc.xpm,uml/pics/usecaserelation.xpm,uml/pics/vline.xpm,uml/pics/branch.xpm,uml/pics/fork.xpm +sub_dirs= +type=normal + +[uml/pics/actor.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/actor.xpm +type=DATA + +[uml/pics/aggregation.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/aggregation.xpm +type=DATA + +[uml/pics/anchor.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/anchor.xpm +type=DATA + +[uml/pics/arrow.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/arrow.xpm +type=DATA + +[uml/pics/association.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/association.xpm +type=DATA + +[uml/pics/branch.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/branch.xpm +type=DATA + +[uml/pics/broom.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/broom.xpm +type=DATA + +[uml/pics/case.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/case.xpm +type=DATA + +[uml/pics/classicon.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/classicon.xpm +type=DATA + +[uml/pics/component.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/component.xpm +type=DATA + +[uml/pics/composition.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/composition.xpm +type=DATA + +[uml/pics/constraint.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/constraint.xpm +type=DATA + +[uml/pics/dependency.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/dependency.xpm +type=DATA + +[uml/pics/end_state.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/end_state.xpm +type=DATA + +[uml/pics/folder_green.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/folder_green.png +type=DATA + +[uml/pics/folder_green_open.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/folder_green_open.png +type=DATA + +[uml/pics/folder_grey.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/folder_grey.png +type=DATA + +[uml/pics/folder_grey_open.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/folder_grey_open.png +type=DATA + +[uml/pics/folder_home.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/folder_home.png +type=DATA + +[uml/pics/fork.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/fork.xpm +type=DATA + +[uml/pics/generalization.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/generalization.xpm +type=DATA + +[uml/pics/hline.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/hline.xpm +type=DATA + +[uml/pics/implements.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/implements.xpm +type=DATA + +[uml/pics/initial_state.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/initial_state.xpm +type=DATA + +[uml/pics/interface.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/interface.xpm +type=DATA + +[uml/pics/largepackage.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/largepackage.xpm +type=DATA + +[uml/pics/lifeline.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/lifeline.xpm +type=DATA + +[uml/pics/line.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/line.xpm +type=DATA + +[uml/pics/message.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/message.xpm +type=DATA + +[uml/pics/note.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/note.xpm +type=DATA + +[uml/pics/object.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/object.xpm +type=DATA + +[uml/pics/output_win.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/output_win.xpm +type=DATA + +[uml/pics/realizes.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/realizes.xpm +type=DATA + +[uml/pics/rectangle.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/rectangle.xpm +type=DATA + +[uml/pics/smallpackage.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/smallpackage.xpm +type=DATA + +[uml/pics/snapshot.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/snapshot.xpm +type=DATA + +[uml/pics/startlogo.png] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/startlogo.png +type=DATA + +[uml/pics/text.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/text.xpm +type=DATA + +[uml/pics/umlclass.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/umlclass.xpm +type=DATA + +[uml/pics/umlclass_template.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/umlclass_template.xpm +type=DATA + +[uml/pics/uniassoc.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/uniassoc.xpm +type=DATA + +[uml/pics/usecaserelation.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/usecaserelation.xpm +type=DATA + +[uml/pics/vline.xpm] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/pics/vline.xpm +type=DATA + +[uml/seqlinewidget.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/seqlinewidget.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/statewidget.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/statewidget.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/statewidgetdata.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/statewidgetdata.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/tips] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/tips +type=DATA + +[uml/uml.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/uml.desktop] +dist=true +install=true +install_location=$$(kde_appsdir)/Development/umbrello.desktop +type=DATA + +[uml/uml.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/umldoc.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/umldoc.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/umllistview.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/umllistview.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/umllistviewitem.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/umllistviewitem.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/umllistviewitemdata.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/umllistviewitemdata.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/umllistviewitemdatalist.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/umllistviewitemlist.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/umlnamespace.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/umlnamespace.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/umlobject.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/umlobject.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/umlobjectlist.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/umlui.rc] +dist=true +install=true +install_location=$$(kde_datadir)/umbrello/umbrelloui.rc +type=DATA + +[uml/umlview.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/umlview.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/umlviewcanvas.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/umlviewcanvas.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/umlviewdata.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/umlviewdata.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/umlviewlist.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/umlwidget.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/umlwidget.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/umlwidgetdata.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/umlwidgetdata.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/umlwidgetlist.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/usecase.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/usecase.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/usecasewidget.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/usecasewidget.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/usecasewidgetdata.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/usecasewidgetdata.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/worktoolbar.cpp] +dist=true +install=false +install_location= +type=SOURCE + +[uml/worktoolbar.h] +dist=true +install=false +install_location= +type=HEADER + +[uml/x-uml.desktop] +dist=true +install=true +install_location=$$(kde_mimedir)/application/x-uml.desktop +type=DATA diff --git a/umbrello/uml.lsm b/umbrello/uml.lsm new file mode 100644 index 00000000..9c8af94f --- /dev/null +++ b/umbrello/uml.lsm @@ -0,0 +1,14 @@ +Begin3 +Title: Umbrello UML Modeller +Version: 1.5.8 +Entered-date: +Description: A UML diagram Modeller +Keywords: uml diagram modeller +Author: Paul Hensgen +Maintained-by: Umbrello UML Modeller developers +Primary-site: http://uml.sf.net +Home-page: http://uml.sf.net +Original-site: +Platforms: KDE 3.3+ +Copying-policy: GPL +End -- cgit v1.2.1