diff options
author | François Andriot <[email protected]> | 2013-06-24 19:50:32 +0200 |
---|---|---|
committer | François Andriot <[email protected]> | 2013-06-24 19:50:32 +0200 |
commit | b4359e8bf97799f83dc1ca62744db7cfcc81bc87 (patch) | |
tree | 3346872613490cc467c19e1645d0026c1221bce7 /redhat/tdesdk/kdesdk-3.5.13.1-fix_kdecachegrind_ftbfs.patch | |
parent | 4cc71d79c5718d59078d06c497a56d7c05b41576 (diff) | |
download | tde-packaging-b4359e8bf97799f83dc1ca62744db7cfcc81bc87.tar.gz tde-packaging-b4359e8bf97799f83dc1ca62744db7cfcc81bc87.zip |
RPM Packaging: rename directories
Diffstat (limited to 'redhat/tdesdk/kdesdk-3.5.13.1-fix_kdecachegrind_ftbfs.patch')
-rw-r--r-- | redhat/tdesdk/kdesdk-3.5.13.1-fix_kdecachegrind_ftbfs.patch | 39683 |
1 files changed, 39683 insertions, 0 deletions
diff --git a/redhat/tdesdk/kdesdk-3.5.13.1-fix_kdecachegrind_ftbfs.patch b/redhat/tdesdk/kdesdk-3.5.13.1-fix_kdecachegrind_ftbfs.patch new file mode 100644 index 000000000..80ca10ff8 --- /dev/null +++ b/redhat/tdesdk/kdesdk-3.5.13.1-fix_kdecachegrind_ftbfs.patch @@ -0,0 +1,39683 @@ +commit cfccedd9c8db3af36d7c5635ca212fa170bb6ff5 +Author: Timothy Pearson <[email protected]> +Date: 1327976424 -0600 + + Part 2 of prior commit + +diff --git a/kdecachegrind/AUTHORS b/kdecachegrind/AUTHORS +new file mode 100644 +index 0000000..ded6005 +--- /dev/null ++++ b/kdecachegrind/AUTHORS +@@ -0,0 +1 @@ ++Josef Weidendorfer <[email protected]> +diff --git a/kdecachegrind/COPYING b/kdecachegrind/COPYING +new file mode 100644 +index 0000000..c13faf0 +--- /dev/null ++++ b/kdecachegrind/COPYING +@@ -0,0 +1,340 @@ ++ GNU GENERAL PUBLIC LICENSE ++ Version 2, June 1991 ++ ++ Copyright (C) 1989, 1991 Free Software Foundation, Inc. ++ 51 Franklin Street, 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. ++ ++ 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. ++ ++ <one line to give the program's name and a brief idea of what it does.> ++ Copyright (C) <year> <name of author> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the 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 ++ ++ ++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. ++ ++ <signature of Ty Coon>, 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/kdecachegrind/ChangeLog b/kdecachegrind/ChangeLog +new file mode 100644 +index 0000000..05f3081 +--- /dev/null ++++ b/kdecachegrind/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, kdecachegrindui.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/kdecachegrind/INSTALL b/kdecachegrind/INSTALL +new file mode 100644 +index 0000000..02a4a07 +--- /dev/null ++++ b/kdecachegrind/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/kdecachegrind/Makefile.am b/kdecachegrind/Makefile.am +new file mode 100644 +index 0000000..e93f6af +--- /dev/null ++++ b/kdecachegrind/Makefile.am +@@ -0,0 +1,6 @@ ++SUBDIRS = kdecachegrind pics converters ++ ++EXTRA_DIST = \ ++ AUTHORS COPYING NEWS ChangeLog INSTALL README TODO \ ++ kdecachegrind.lsm kdecachegrind.spec version.h ++ +diff --git a/kdecachegrind/NEWS b/kdecachegrind/NEWS +new file mode 100644 +index 0000000..e69de29 +diff --git a/kdecachegrind/README b/kdecachegrind/README +new file mode 100644 +index 0000000..0866eb8 +--- /dev/null ++++ b/kdecachegrind/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=<KDE base directory> ++ 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/kdecachegrind/TODO b/kdecachegrind/TODO +new file mode 100644 +index 0000000..1eca67e +--- /dev/null ++++ b/kdecachegrind/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/kdecachegrind/configure.in.in b/kdecachegrind/configure.in.in +new file mode 100644 +index 0000000..dfc8508 +--- /dev/null ++++ b/kdecachegrind/configure.in.in +@@ -0,0 +1,8 @@ ++KCACHEGRIND_VERSION=0.4.6kde ++AC_SUBST(KCACHEGRIND_VERSION) ++ ++AC_FUNC_MMAP ++ ++dnl AC_OUTPUT( kdecachegrind/version.h ) ++dnl AC_OUTPUT( kdecachegrind/kdecachegrind.spec ) ++dnl AC_OUTPUT( kdecachegrind/kdecachegrind.lsm ) +diff --git a/kdecachegrind/converters/Makefile.am b/kdecachegrind/converters/Makefile.am +new file mode 100644 +index 0000000..08b3696 +--- /dev/null ++++ b/kdecachegrind/converters/Makefile.am +@@ -0,0 +1,2 @@ ++bin_SCRIPTS = hotshot2calltree op2calltree pprof2calltree dprof2calltree \ ++ memprof2calltree +diff --git a/kdecachegrind/converters/README b/kdecachegrind/converters/README +new file mode 100644 +index 0000000..c27d3c6 +--- /dev/null ++++ b/kdecachegrind/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 <[email protected]> for ++ dprof2calltree and pprof2calltree, ++* J�rg Beyer <[email protected]> for ++ hotshot2calltree ++ ++If you want to write a converter, have a look at the calltree format ++description on the web site (kdecachegrind.sf.net). ++ ++Josef ++ +diff --git a/kdecachegrind/converters/dprof2calltree b/kdecachegrind/converters/dprof2calltree +new file mode 100644 +index 0000000..f276e18 +--- /dev/null ++++ b/kdecachegrind/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 <[email protected]> ++# 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 kdecachegrind 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 kdecachegrind 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 <tmon.out> [-o outfile]\n"; ++ exit -1; ++} ++ ++ ++# vim: set sts=2 ts=2 bs ai expandtab : +diff --git a/kdecachegrind/converters/hotshot2calltree b/kdecachegrind/converters/hotshot2calltree +new file mode 100644 +index 0000000..f62a46e +--- /dev/null ++++ b/kdecachegrind/converters/hotshot2calltree +@@ -0,0 +1,394 @@ ++#!/usr/bin/env python ++# _*_ coding: latin1 _*_ ++ ++# ++# Copyright (c) 2003 by WEB.DE, Karlsruhe ++# Autor: J�rg Beyer <[email protected]> ++# ++# 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., 51 Franklin Street, Fifth Floor, ++# Boston, MA 02110-1301, USA. ++# ++# ++# This script transforms the pstat output of the hotshot ++# python profiler into the input of kdecachegrind. ++# ++# 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 <output> <input> ++# or here: ++# hotshot2cachegrind cachegrind.out.0 pythongrind.prof ++# ++# then call kdecachegrind: ++# kdecachegrind 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 ber�cksichtigt, ++# 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 kdecachegrind ++ 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 kdecachegrind ++ 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 kdecachegrind ++ 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 kdecachegrind, 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/kdecachegrind/converters/memprof2calltree b/kdecachegrind/converters/memprof2calltree +new file mode 100755 +index 0000000..e82d6e8 +--- /dev/null ++++ b/kdecachegrind/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/kdecachegrind/converters/op2calltree b/kdecachegrind/converters/op2calltree +new file mode 100755 +index 0000000..ca121a2 +--- /dev/null ++++ b/kdecachegrind/converters/op2calltree +@@ -0,0 +1,238 @@ ++#!/usr/bin/perl ++# ++# Copyright (c) 2004 ++# Author: Josef Weidendorfer <[email protected]> ++# ++# 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., 51 Franklin Street, Fifth Floor, ++# Boston, MA 02110-1301, 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/kdecachegrind/converters/pprof2calltree b/kdecachegrind/converters/pprof2calltree +new file mode 100644 +index 0000000..0e70e1c +--- /dev/null ++++ b/kdecachegrind/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 <[email protected]> ++# 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 kdecachegrind. 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 ++ ++<?php ++ ++require "Console/Getopt.php"; ++ ++$con = new Console_Getopt; ++$args = $con->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 kdecachegrind compatible output to $outfile\n"; ++fwrite($OUT, $buffer); ++ ++function usage() ++{ ++ print <<<EOD ++pprof2calltree -f <tracefile> ++ ++EOD; ++ exit(1); ++} ++?> +diff --git a/kdecachegrind/pics/Makefile.am b/kdecachegrind/pics/Makefile.am +new file mode 100644 +index 0000000..f4a3186 +--- /dev/null ++++ b/kdecachegrind/pics/Makefile.am +@@ -0,0 +1,3 @@ ++kdecachegrindicondir = $(kde_datadir)/kdecachegrind/icons ++kdecachegrindicon_ICON = AUTO ++SUBDIRS = hicolor +diff --git a/kdecachegrind/pics/hicolor/Makefile.am b/kdecachegrind/pics/hicolor/Makefile.am +new file mode 100644 +index 0000000..068e319 +--- /dev/null ++++ b/kdecachegrind/pics/hicolor/Makefile.am +@@ -0,0 +1,2 @@ ++kdecachegrindicondir = $(kde_datadir)/kdecachegrind/icons ++kdecachegrindicon_ICON = AUTO +diff --git a/kdecachegrind/pics/hicolor/hi16-action-fromrec.png b/kdecachegrind/pics/hicolor/hi16-action-fromrec.png +new file mode 100644 +index 0000000..a5cb430 +Binary files /dev/null and b/kdecachegrind/pics/hicolor/hi16-action-fromrec.png differ +diff --git a/kdecachegrind/pics/hicolor/hi16-action-percent.png b/kdecachegrind/pics/hicolor/hi16-action-percent.png +new file mode 100644 +index 0000000..7a4ba47 +Binary files /dev/null and b/kdecachegrind/pics/hicolor/hi16-action-percent.png differ +diff --git a/kdecachegrind/pics/hicolor/hi16-action-recrec.png b/kdecachegrind/pics/hicolor/hi16-action-recrec.png +new file mode 100644 +index 0000000..ec11bfa +Binary files /dev/null and b/kdecachegrind/pics/hicolor/hi16-action-recrec.png differ +diff --git a/kdecachegrind/pics/hicolor/hi16-action-torec.png b/kdecachegrind/pics/hicolor/hi16-action-torec.png +new file mode 100644 +index 0000000..c092c01 +Binary files /dev/null and b/kdecachegrind/pics/hicolor/hi16-action-torec.png differ +diff --git a/kdecachegrind/pics/hicolor/hi22-action-percent.png b/kdecachegrind/pics/hicolor/hi22-action-percent.png +new file mode 100644 +index 0000000..c64a378 +Binary files /dev/null and b/kdecachegrind/pics/hicolor/hi22-action-percent.png differ +diff --git a/kdecachegrind/pics/hicolor/hi32-action-percent.png b/kdecachegrind/pics/hicolor/hi32-action-percent.png +new file mode 100644 +index 0000000..e876c30 +Binary files /dev/null and b/kdecachegrind/pics/hicolor/hi32-action-percent.png differ +diff --git a/kdecachegrind/kdecachegrind.lsm.in b/kdecachegrind/kdecachegrind.lsm.in +new file mode 100644 +index 0000000..fab7ced +--- /dev/null ++++ b/kdecachegrind/kdecachegrind.lsm.in +@@ -0,0 +1,11 @@ ++Begin3 ++Title: kdecachegrind ++Version: @KCACHEGRIND_VERSION@ ++Description: KDE Profiling Visualisation Tool ++Keywords: Profiling, Performance Analysis, Visualisation, Development ++Author: Josef Weidendorfer <[email protected]> ++Maintained-by: Josef Weidendorfer <[email protected]> ++Home-page: http://kcachegrind.sourceforge.net ++Platforms: Linux and other Unices ++Copying-policy: GNU Public License ++End +diff --git a/kdecachegrind/kdecachegrind.spec.in b/kdecachegrind/kdecachegrind.spec.in +new file mode 100644 +index 0000000..42b3e24 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind.spec.in +@@ -0,0 +1,55 @@ ++Summary: KDE Profiling Visualisation Tool ++Name: kdecachegrind ++Version: @KCACHEGRIND_VERSION@ ++Release: 1 ++Copyright: GPL ++Group: Development/Tools ++Vendor: (none) ++URL: http://kcachegrind.sourceforge.net ++Packager: Josef Weidendorfer <[email protected]> ++Source: kdecachegrind-@[email protected] ++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.kdecachegrind ++find . -type f | sed 's,^\.,\%attr(-\,root\,root) ,' >> $RPM_BUILD_DIR/file.list.kdecachegrind ++find . -type l | sed 's,^\.,\%attr(-\,root\,root) ,' >> $RPM_BUILD_DIR/file.list.kdecachegrind ++ ++%clean ++rm -rf $RPM_BUILD_ROOT/* ++rm -rf $RPM_BUILD_DIR/kdecachegrind ++rm -rf ../file.list.kdecachegrind ++ ++ ++%files -f ../file.list.kdecachegrind +diff --git a/kdecachegrind/kdecachegrind/Doxyfile b/kdecachegrind/kdecachegrind/Doxyfile +new file mode 100644 +index 0000000..9d5d050 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/Doxyfile +@@ -0,0 +1,157 @@ ++# Doxygen configuration generated by Doxywizard version 0.1 ++#--------------------------------------------------------------------------- ++# General configuration options ++#--------------------------------------------------------------------------- ++PROJECT_NAME = kdecachegrind ++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/kdecachegrind/kdecachegrind/Makefile.am b/kdecachegrind/kdecachegrind/Makefile.am +new file mode 100644 +index 0000000..53cd35d +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/Makefile.am +@@ -0,0 +1,62 @@ ++bin_PROGRAMS = kdecachegrind ++ ++kdecachegrind_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 ++ ++kdecachegrind_COMPILE_FIRST = ../version.h ++ ++kdecachegrind_LDADD = $(LIB_KIO) ++ ++KDE_ICON = AUTO ++ ++xdg_apps_DATA = kdecachegrind.desktop ++ ++mimeapplicationdir = $(kde_mimedir)/application ++mimeapplication_DATA = x-kcachegrind.desktop ++ ++EXTRA_DIST = \ ++ kdecachegrind.desktop \ ++ x-kcachegrind.desktop \ ++ hi32-app-kcachegrind.png \ ++ hi48-app-kcachegrind.png \ ++ Doxyfile \ ++ kdecachegrindui.rc ++ ++# set the include path for X, qt and KDE ++INCLUDES= $(all_includes) ++ ++METASOURCES = AUTO ++ ++# the library search path. ++kdecachegrind_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_QT) -lDCOP $(LIB_KDECORE) $(LIB_KDEUI) -lkdefx $(LIB_KIO) -lktexteditor ++ ++rcdir = $(kde_datadir)/kdecachegrind ++rc_DATA = kdecachegrindui.rc ++ ++tipdir = $(kde_datadir)/kdecachegrind ++tip_DATA = tips ++ ++messages: rc.cpp ++ $(PREPARETIPS) > tips.txt ++ LIST=`find . -name \*.h -o -name \*.cpp -o -name \*.txt`; \ ++ if test -n "$$LIST"; then \ ++ $(XGETTEXT) $$LIST -o $(podir)/kdecachegrind.pot; \ ++ fi ++ rm -f tips.txt ++ +diff --git a/kdecachegrind/kdecachegrind/cachegrindloader.cpp b/kdecachegrind/kdecachegrind/cachegrindloader.cpp +new file mode 100644 +index 0000000..4fe57d3 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/cachegrindloader.cpp +@@ -0,0 +1,1323 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002, 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++#include <errno.h> ++ ++#include <tqfile.h> ++#include <tqcstring.h> ++ ++#include <klocale.h> ++#include <kdebug.h> ++ ++#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(TQFile* file); ++ bool loadTrace(TracePart*); ++ bool isPartOfTrace(TQString 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 TQString&); ++ void setCalledObject(const TQString&); ++ void setFile(const TQString&); ++ void setCalledFile(const TQString&); ++ void setFunction(const TQString&); ++ void setCalledFunction(const TQString&); ++ ++ TQString _emptyString; ++ ++ // current line in file to read in ++ TQString _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 ++ * "(<Integer>) Name": this is a compression specification, ++ * mapping the integer number to Name and using Name. ++ * "(<Integer>)" : 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 TQString& checkUnknown(const TQString& n); ++ TraceObject* compressedObject(const TQString& name); ++ TraceFile* compressedFile(const TQString& name); ++ TraceFunction* compressedFunction(const TQString& name, ++ TraceFile*, TraceObject*); ++ ++ TQPtrVector<TraceCostItem> _objectVector, _fileVector, _functionVector; ++}; ++ ++ ++ ++/********************************************************** ++ * Loader ++ */ ++ ++ ++CachegrindLoader::CachegrindLoader() ++ : Loader("Callgrind", ++ i18n( "Import filter for Cachegrind/Callgrind generated profile data files") ) ++{ ++ _emptyString = TQString(""); ++} ++ ++bool CachegrindLoader::canLoadTrace(TQFile* file) ++{ ++ if (!file) return false; ++ ++ if (!file->isOpen()) { ++ if (!file->open( IO_ReadOnly ) ) { ++ kdDebug() << TQFile::encodeName(_filename).data() << ": " ++ << 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; ++ ++ TQCString 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, TQT_SIGNAL(updateStatus(TQString, int)), ++ this, TQT_SIGNAL(updateStatus(TQString, 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 TQString& CachegrindLoader::checkUnknown(const TQString& n) ++{ ++ if (n == "???") return _emptyString; ++ return n; ++} ++ ++TraceObject* CachegrindLoader::compressedObject(const TQString& 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); ++ } ++ ++ TQString 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 TQString& 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); ++ } ++ ++ TQString 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 TQString& 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); ++ } ++ ++ TQString 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 TQString& 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 TQString& 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 TQString& 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 TQString& 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 TQString& 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 TQString& 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(); ++ TQFile* 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; ++ TQString 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 ++ << " - '" << TQString(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 ('" ++ << TQString(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:")) { ++ TQString command = TQString(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(TQString(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:<name>[=<formula>][:<long name>] ++ 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(TQString(line).toInt()); ++ continue; ++ } ++ ++ // pid: ++ if (line.stripPrefix("id:")) { ++ part->setProcessID(TQString(line).toInt()); ++ continue; ++ } ++ ++ // positions: ++ if (line.stripPrefix("ositions:")) { ++ TQString 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 << TQString(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 <line> 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 ('" ++ << TQString(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 <line> 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 ('" ++ << TQString(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 ('" ++ << TQString(line) << "')" << endl; ++ } ++ ++ } ++ } ++ ++ ++ emit updateStatus(statusMsg,100); ++ ++ _part->invalidate(); ++ if (!totalsSet) { ++ _part->totals()->clear(); ++ _part->totals()->addCost(_part); ++ } ++ ++ pFile->close(); ++ ++ return true; ++} ++ +diff --git a/kdecachegrind/kdecachegrind/callgraphview.cpp b/kdecachegrind/kdecachegrind/callgraphview.cpp +new file mode 100644 +index 0000000..bc01da8 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/callgraphview.cpp +@@ -0,0 +1,2734 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Callgraph View ++ */ ++ ++#include <stdlib.h> ++#include <math.h> ++ ++#include <tqtooltip.h> ++#include <tqfile.h> ++#include <tqtextstream.h> ++#include <tqwhatsthis.h> ++#include <tqcanvas.h> ++#include <tqwmatrix.h> ++#include <tqpair.h> ++#include <tqpainter.h> ++#include <tqpopupmenu.h> ++#include <tqstyle.h> ++#include <tqprocess.h> ++ ++#include <kdebug.h> ++#include <klocale.h> ++#include <kconfig.h> ++#include <ktempfile.h> ++#include <kapplication.h> ++#include <kiconloader.h> ++#include <kfiledialog.h> ++ ++#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 { ++ TQPointArray a1 = e1->controlPoints(); ++ TQPointArray 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; ++} ++ ++TQString 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 ++// ++ ++TQString GraphOptions::layoutString(Layout l) ++{ ++ if (l == Circular) return TQString("Circular"); ++ if (l == LeftRight) return TQString("LeftRight"); ++ return TQString("TopDown"); ++} ++ ++GraphOptions::Layout GraphOptions::layout(TQString s) ++{ ++ if (s == TQString("Circular")) return Circular; ++ if (s == TQString("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, TQString()); ++} ++ ++ ++GraphExporter::GraphExporter(TraceData* d, TraceFunction* f, TraceCostType* ct, ++ TraceItem::CostType gt, TQString 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, TQString 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(TQString(), ".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); ++ TQPair<TraceFunction*,TraceFunction*> 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; ++ ++ TQFile* file = 0; ++ TQTextStream* stream = 0; ++ ++ if (_tmpFile) ++ stream = _tmpFile->textStream(); ++ else { ++ file = new TQFile(_dotName); ++ if ( !file->open( IO_WriteOnly ) ) { ++ kdError() << "Can't write dot file '" << _dotName << "'" << endl; ++ return; ++ } ++ stream = new TQTextStream(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 << TQString(" 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 << TQString(" center=F%1;\n").arg((long)f, 0, 16); ++ *stream << TQString(" overlap=false;\n splines=true;\n"); ++ } ++ ++ // for clustering ++ TQMap<TraceCostItem*,TQPtrList<GraphNode> > 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); ++ } ++ ++ TQMap<TraceCostItem*,TQPtrList<GraphNode> >::Iterator lit; ++ int cluster = 0; ++ for ( lit = nLists.begin(); ++ lit != nLists.end(); ++lit, cluster++ ) { ++ TQPtrList<GraphNode>& l = lit.data(); ++ TraceCostItem* i = lit.key(); ++ ++ if (_go->clusterGroups() && i) { ++ TQString iabr = i->prettyName(); ++ if ((int)iabr.length() > Configuration::maxSymbolLength()) ++ iabr = iabr.left(Configuration::maxSymbolLength()) + "..."; ++ ++ *stream << TQString("subgraph \"cluster%1\" { label=\"%2\";\n") ++ .arg(cluster).arg(iabr); ++ } ++ ++ GraphNode* np; ++ for(np = l.first(); np; np = l.next() ) { ++ TraceFunction* f = np->function(); ++ ++ TQString abr = f->prettyName(); ++ if ((int)abr.length() > Configuration::maxSymbolLength()) ++ abr = abr.left(Configuration::maxSymbolLength()) + "..."; ++ ++ *stream << TQString(" F%1 [").arg((long)f, 0, 16); ++ if (_useBox) { ++ // make label 3 lines for CallGraphView ++ *stream << TQString("shape=box,label=\"** %1 **\\n**\\n%2\"];\n") ++ .arg(abr) ++ .arg(SubCost(np->incl).pretty()); ++ } ++ else ++ *stream << TQString("label=\"%1\\n%2\"];\n") ++ .arg(abr) ++ .arg(SubCost(np->incl).pretty()); ++ } ++ ++ if (_go->clusterGroups() && i) ++ *stream << TQString("}\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 << TQString(" 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 << TQString(",label=\"%1\"") ++ .arg(SubCost(e.cost).pretty()); ++ else if (_go->detailLevel() ==2) ++ *stream << TQString(",label=\"%3\\n%4 x\"") ++ .arg(SubCost(e.cost).pretty()) ++ .arg(SubCost(e.count).pretty()); ++ ++ *stream << TQString("];\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) { ++ ++ TQPair<TraceFunction*,TraceFunction*> p(0, n.function()); ++ e = &(_edgeMap[p]); ++ e->setCalling(p.second); ++ e->cost = costSum; ++ e->count = countSum; ++ ++ *stream << TQString(" R%1 [shape=point,label=\"\"];\n") ++ .arg((long)n.function(), 0, 16); ++ *stream << TQString(" 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) { ++ ++ TQPair<TraceFunction*,TraceFunction*> p(n.function(), 0); ++ e = &(_edgeMap[p]); ++ e->setCaller(p.first); ++ e->cost = costSum; ++ e->count = countSum; ++ ++ *stream << TQString(" S%1 [shape=point,label=\"\"];\n") ++ .arg((long)n.function(), 0, 16); ++ *stream << TQString(" 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(TQString 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(tqMakePair(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; ++ TQPair<TraceFunction*,TraceFunction*> 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); ++ TQPair<TraceFunction*,TraceFunction*> 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(TQWidget * parent, const char * name) ++ : TQCanvasView(parent, name, WNoAutoErase | WStaticContents) ++{ ++ _movingZoomRect = false; ++ ++ // why doesn't this avoid flicker ? ++ viewport()->setBackgroundMode(TQt::NoBackground); ++ setBackgroundMode(TQt::NoBackground); ++} ++ ++void PannerView::setZoomRect(TQRect r) ++{ ++ TQRect oldRect = _zoomRect; ++ _zoomRect = r; ++ updateContents(oldRect); ++ updateContents(_zoomRect); ++} ++ ++void PannerView::drawContents(TQPainter * p, int clipx, int clipy, int clipw, int cliph) ++{ ++ // save/restore around TQCanvasView::drawContents seems to be needed ++ // for QT 3.0 to get the red rectangle drawn correct ++ p->save(); ++ TQCanvasView::drawContents(p,clipx,clipy,clipw,cliph); ++ p->restore(); ++ if (_zoomRect.isValid()) { ++ p->setPen(red.dark()); ++ p->drawRect(_zoomRect); ++ p->setPen(red); ++ p->drawRect(TQRect(_zoomRect.x()+1, _zoomRect.y()+1, ++ _zoomRect.width()-2, _zoomRect.height()-2)); ++ } ++} ++ ++void PannerView::contentsMousePressEvent(TQMouseEvent* 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(TQMouseEvent* e) ++{ ++ if (_movingZoomRect) { ++ emit zoomRectMoved(e->pos().x() - _lastPos.x(), e->pos().y() - _lastPos.y()); ++ _lastPos = e->pos(); ++ } ++} ++ ++void PannerView::contentsMouseReleaseEvent(TQMouseEvent*) ++{ ++ _movingZoomRect = false; ++ emit zoomRectMoveFinished(); ++} ++ ++ ++ ++ ++ ++// ++// CanvasNode ++// ++ ++CanvasNode::CanvasNode(CallGraphView* v, GraphNode* n, ++ int x, int y, int w, int h, TQCanvas* c) ++ : TQCanvasRectangle(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->TraceItemView::data(); ++ double total = totalCost->subCost(_view->costType()); ++ double inclP = 100.0 * n->incl / total; ++ if (_view->topLevel()->showPercentage()) ++ setText(1, TQString("%1 %") ++ .arg(inclP, 0, 'f', Configuration::percentPrecision())); ++ else ++ setText(1, SubCost(n->incl).pretty()); ++ setPixmap(1, percentagePixmap(25,10,(int)(inclP+.5), TQt::blue, true)); ++} ++ ++void CanvasNode::setSelected(bool s) ++{ ++ StoredDrawParams::setSelected(s); ++ update(); ++} ++ ++void CanvasNode::updateGroup() ++{ ++ if (!_view || !_node) return; ++ ++ TQColor c = Configuration::functionColor(_view->groupType(), ++ _node->function()); ++ setBackColor(c); ++ update(); ++} ++ ++void CanvasNode::drawShape(TQPainter& p) ++{ ++ TQRect 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().tqdrawPrimitive( TQStyle::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, TQCanvas* c) ++ : TQCanvasRectangle(x, y, w, h, c), _ce(ce), _view(v) ++{ ++ GraphEdge* e = ce->edge(); ++ if (!e) return; ++ ++ setPosition(1, DrawParams::TopCenter); ++ setText(1, TQString("%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->TraceItemView::data(); ++ double total = totalCost->subCost(_view->costType()); ++ double inclP = 100.0 * e->cost / total; ++ if (_view->topLevel()->showPercentage()) ++ setText(0, TQString("%1 %") ++ .arg(inclP, 0, 'f', Configuration::percentPrecision())); ++ else ++ setText(0, SubCost(e->cost).pretty()); ++ setPixmap(0, percentagePixmap(25,10,(int)(inclP+.5), TQt::blue, true)); ++ ++ if (e->call() && (e->call()->isRecursion() || e->call()->inCycle())) { ++ TQString icon = "undo"; ++ KIconLoader* loader = KApplication::kApplication()->iconLoader(); ++ TQPixmap p= loader->loadIcon(icon, KIcon::Small, 0, ++ KIcon::DefaultState, 0, true); ++ setPixmap(0, p); ++ } ++} ++ ++void CanvasEdgeLabel::drawShape(TQPainter& p) ++{ ++ TQRect 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, TQCanvas* c) ++ : TQCanvasPolygon(c), _ce(ce) ++{} ++ ++void CanvasEdgeArrow::drawShape(TQPainter& p) ++{ ++ if (_ce->isSelected()) p.setBrush(TQt::red); ++ ++ TQCanvasPolygon::drawShape(p); ++} ++ ++// ++// CanvasEdge ++// ++ ++CanvasEdge::CanvasEdge(GraphEdge* e, TQCanvas* c) ++ : TQCanvasSpline(c), _edge(e) ++{ ++ _label = 0; ++ _arrow = 0; ++} ++ ++void CanvasEdge::setSelected(bool s) ++{ ++ TQCanvasItem::setSelected(s); ++ update(); ++ if (_arrow) _arrow->setSelected(s); ++} ++ ++TQPointArray 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<len;i++) { ++ if (poly[i].x() < minX) minX = poly[i].x(); ++ if (poly[i].y() < minY) minY = poly[i].y(); ++ if (poly[i].x() > 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()); ++ } ++ TQPointArray 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;i<len;i++) ++ a[2 * len - 1 -i] = b[i]; ++ ++ if (0) { ++ qDebug(" Result:"); ++ for (i=0;i<2*len;i++) ++ qDebug(" P %d: %d/%d", i, a[i].x(), a[i].y()); ++ } ++ ++ return a; ++} ++ ++void CanvasEdge::drawShape(TQPainter& p) ++{ ++ if (isSelected()) p.setPen(TQt::red); ++ ++ p.drawPolyline(poly); ++} ++ ++ ++// ++// CanvasFrame ++// ++ ++TQPixmap* CanvasFrame::_p = 0; ++ ++CanvasFrame::CanvasFrame(CanvasNode* n, TQCanvas* c) ++ : TQCanvasRectangle(c) ++{ ++ if (!_p) { ++ ++ int d = 5; ++ float v1 = 130.0, v2 = 10.0, v = v1, f = 1.03; ++ ++ // calculate pix size ++ TQRect r(0, 0, 30, 30); ++ while (v>v2) { ++ r.setRect(r.x()-d, r.y()-d, r.width()+2*d, r.height()+2*d); ++ v /= f; ++ } ++ ++ _p = new TQPixmap(r.size()); ++ _p->fill(TQt::white); ++ TQPainter p(_p); ++ p.setPen(TQt::NoPen); ++ ++ r.moveBy(-r.x(), -r.y()); ++ ++ while (v<v1) { ++ v *= f; ++ p.setBrush(TQColor(265-(int)v, 265-(int)v, 265-(int)v)); ++ ++ p.drawRect(TQRect(r.x(), r.y(), r.width(), d)); ++ p.drawRect(TQRect(r.x(), r.bottom()-d, r.width(), d)); ++ p.drawRect(TQRect(r.x(), r.y()+d, d, r.height()-2*d)); ++ p.drawRect(TQRect(r.right()-d, r.y()+d, d, r.height()-2*d)); ++ ++ r.setRect(r.x()+d, r.y()+d, r.width()-2*d, r.height()-2*d); ++ } ++ } ++ ++ setSize(_p->width(), _p->height()); ++ move(n->rect().center().x()-_p->width()/2, ++ n->rect().center().y()-_p->height()/2); ++} ++ ++ ++void CanvasFrame::drawShape(TQPainter& p) ++{ ++ p.drawPixmap( int(x()), int(y()), *_p ); ++} ++ ++ ++ ++ ++// ++// Tooltips for CallGraphView ++// ++ ++class CallGraphTip: public TQToolTip ++{ ++public: ++ CallGraphTip( TQWidget* p ):TQToolTip(p) {} ++ ++protected: ++ void maybeTip( const TQPoint & ); ++}; ++ ++void CallGraphTip::maybeTip( const TQPoint& pos ) ++{ ++ if (!parentWidget()->inherits( "CallGraphView" )) return; ++ CallGraphView* cgv = (CallGraphView*)parentWidget(); ++ ++ TQPoint cPos = cgv->viewportToContents(pos); ++ ++ if (0) qDebug("CallGraphTip for (%d/%d) -> (%d/%d) ?", ++ pos.x(), pos.y(), cPos.x(), cPos.y()); ++ ++ TQCanvasItemList l = cgv->canvas()->collisions(cPos); ++ if (l.count() == 0) return; ++ TQCanvasItem* 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()); ++ ++ TQString tipStr = TQString("%1 (%2)").arg(cn->text(0)).arg(cn->text(1)); ++ TQPoint vPosTL = cgv->contentsToViewport(i->boundingRect().topLeft()); ++ TQPoint vPosBR = cgv->contentsToViewport(i->boundingRect().bottomRight()); ++ tip(TQRect(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()); ++ ++ TQString tipStr; ++ if (!ce->label()) ++ tipStr = e->prettyName(); ++ else ++ tipStr = TQString("%1 (%2)") ++ .arg(ce->label()->text(0)).arg(ce->label()->text(1)); ++ tip(TQRect(pos.x()-5,pos.y()-5,pos.x()+5,pos.y()+5), tipStr); ++ } ++} ++ ++ ++ ++ ++// ++// CallGraphView ++// ++CallGraphView::CallGraphView(TraceItemView* parentView, ++ TQWidget* parent, const char* name) ++ : TQCanvasView(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(TQScrollView::AlwaysOff); ++ _completeView->setHScrollBarMode(TQScrollView::AlwaysOff); ++ _completeView->raise(); ++ _completeView->hide(); ++ ++ setFocusPolicy(TQ_StrongFocus); ++ setBackgroundMode(TQt::NoBackground); ++ ++ connect(this, TQT_SIGNAL(contentsMoving(int,int)), ++ this, TQT_SLOT(contentsMovingSlot(int,int))); ++ connect(_completeView, TQT_SIGNAL(zoomRectMoved(int,int)), ++ this, TQT_SLOT(zoomRectMoved(int,int))); ++ connect(_completeView, TQT_SIGNAL(zoomRectMoveFinished()), ++ this, TQT_SLOT(zoomRectMoveFinished())); ++ ++ TQWhatsThis::add( this, whatsThis() ); ++ ++ // tooltips... ++ _tip = new CallGraphTip(this); ++ ++ _renderProcess = 0; ++ _prevSelectedNode = 0; ++ connect(&_renderTimer, TQT_SIGNAL(timeout()), ++ this, TQT_SLOT(showRenderWarning())); ++} ++ ++CallGraphView::~CallGraphView() ++{ ++ delete _completeView; ++ delete _tip; ++ ++ if (_canvas) { ++ setCanvas(0); ++ delete _canvas; ++ } ++} ++ ++TQString CallGraphView::whatsThis() const ++{ ++ return i18n( "<b>Call Graph around active Function</b>" ++ "<p>Depending on configuration, this view shows " ++ "the call graph environment of the active function. " ++ "Note: the shown cost is <b>only</b> 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.</p>" ++ "<p>For cycles, blue call arrows indicate that this is an " ++ "artificial call added for correct drawing which " ++ "actually never happened.</p>" ++ "<p>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.<p>"); ++} ++ ++void CallGraphView::updateSizes(TQSize s) ++{ ++ if (!_canvas) return; ++ ++ if (s == TQSize(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); ++ ++ TQWMatrix 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; ++ TQPoint oldZoomPos = _completeView->pos(); ++ TQPoint newZoomPos = TQPoint(0,0); ++ ZoomPosition zp = _zoomPosition; ++ if (zp == Auto) { ++ TQPoint tl1Pos = viewportToContents(TQPoint(0,0)); ++ TQPoint tl2Pos = viewportToContents(TQPoint(cvW,cvH)); ++ TQPoint tr1Pos = viewportToContents(TQPoint(x,0)); ++ TQPoint tr2Pos = viewportToContents(TQPoint(x+cvW,cvH)); ++ TQPoint bl1Pos = viewportToContents(TQPoint(0,y)); ++ TQPoint bl2Pos = viewportToContents(TQPoint(cvW,y+cvH)); ++ TQPoint br1Pos = viewportToContents(TQPoint(x,y)); ++ TQPoint br2Pos = viewportToContents(TQPoint(x+cvW,y+cvH)); ++ int tlCols = _canvas->collisions(TQRect(tl1Pos,tl2Pos)).count(); ++ int trCols = _canvas->collisions(TQRect(tr1Pos,tr2Pos)).count(); ++ int blCols = _canvas->collisions(TQRect(bl1Pos,bl2Pos)).count(); ++ int brCols = _canvas->collisions(TQRect(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 = TQPoint(x,0); ++ break; ++ case BottomLeft: ++ newZoomPos = TQPoint(0,y); ++ break; ++ case BottomRight: ++ newZoomPos = TQPoint(x,y); ++ break; ++ default: ++ break; ++ } ++ if (newZoomPos != oldZoomPos) _completeView->move(newZoomPos); ++} ++ ++void CallGraphView::focusInEvent(TQFocusEvent*) ++{ ++ if (!_canvas) return; ++ ++ if (_selectedNode && _selectedNode->canvasNode()) { ++ _selectedNode->canvasNode()->setSelected(true); // requests item update ++ _canvas->update(); ++ } ++} ++ ++void CallGraphView::focusOutEvent(TQFocusEvent* e) ++{ ++ // trigger updates as in focusInEvent ++ focusInEvent(e); ++} ++ ++void CallGraphView::keyPressEvent(TQKeyEvent* 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(TQResizeEvent* e) ++{ ++ TQCanvasView::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; ++ } ++ ++ TQCanvasItemList l = _canvas->allItems(); ++ TQCanvasItemList::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(TQString s) ++{ ++ clear(); ++ _renderTimer.stop(); ++ ++ _canvas = new TQCanvas(TQApplication::desktop()->width(), ++ TQApplication::desktop()->height()); ++ ++ TQCanvasText* t = new TQCanvasText(s, _canvas); ++ t->move(5, 5); ++ t->show(); ++ center(0,0); ++ setCanvas(_canvas); ++ _canvas->update(); ++ _completeView->hide(); ++} ++ ++void CallGraphView::showRenderWarning() ++{ ++ TQString 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 = TQString(); ++ ++ _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 = TQPoint(-1,-1); ++ if (_selectedNode) { ++ TQPoint 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 TQProcess(TQT_TQOBJECT(this)); ++ if (_layout == GraphOptions::Circular) ++ _renderProcess->addArgument( "twopi" ); ++ else ++ _renderProcess->addArgument( "dot" ); ++ _renderProcess->addArgument(_exporter.filename()); ++ _renderProcess->addArgument( "-Tplain" ); ++ ++ connect( _renderProcess, TQT_SIGNAL(readyReadStdout()), ++ this, TQT_SLOT(readDotOutput()) ); ++ connect( _renderProcess, TQT_SIGNAL(processExited()), ++ this, TQT_SLOT(dotExited()) ); ++ ++ if (1) kdDebug() << "Running '" ++ << _renderProcess->arguments().join(" ") ++ << "'..." << endl; ++ ++ if ( !_renderProcess->start() ) { ++ TQString 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 = TQString(); ++ ++ // layouting of more than seconds is dubious ++ _renderTimer.start(1000, true); ++} ++ ++void CallGraphView::readDotOutput() ++{ ++ _unparsedOutput.append( _renderProcess->readStdout() ); ++} ++ ++void CallGraphView::dotExited() ++{ ++ TQString line, cmd; ++ CanvasNode *rItem; ++ TQCanvasEllipse* eItem; ++ CanvasEdge* sItem; ++ CanvasEdgeLabel* lItem; ++ TQTextStream* 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 TQTextStream(_unparsedOutput, IO_ReadOnly); ++ ++ int lineno = 0; ++ while (1) { ++ line = dotStream->readLine(); ++ if (line.isNull()) break; ++ lineno++; ++ if (line.isEmpty()) continue; ++ ++ TQTextStream 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") { ++ TQString 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 < TQApplication::desktop()->width()) ++ _xMargin += (TQApplication::desktop()->width()-w)/2; ++ ++ _yMargin = 50; ++ if (h < TQApplication::desktop()->height()) ++ _yMargin += (TQApplication::desktop()->height()-h)/2; ++ ++ _canvas = new TQCanvas(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 ++ TQString 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 TQCanvasEllipse(w, h, _canvas); ++ eItem->move(xx, yy); ++ eItem->setBrush(TQt::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 ++ ++ TQString node1Name, node2Name, label, edgeX, edgeY; ++ double x, y; ++ TQPointArray 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<points;i++) { ++ if (lineStream.atEnd()) break; ++ 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(" 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 ++ TQColor arrowColor = TQt::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 = TQt::blue; ++ ++ sItem = new CanvasEdge(e, _canvas); ++ e->setCanvasEdge(sItem); ++ sItem->setControlPoints(pa, false); ++ sItem->setPen(TQPen(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 ++ TQPoint 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) { ++ TQPoint 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() && (indexHead<points-2)) { ++ indexHead++; ++ arrowDir = pa.point(indexHead) - pa.point(indexHead+1); ++ } ++ } ++ } ++ ++ if (arrowDir.isNull()) { ++ indexHead = points; ++ // sometimes the last spline points from dot are the same... ++ while(arrowDir.isNull() && (indexHead>1)) { ++ 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())); ++ TQPointArray a(3); ++ a.setPoint(0, pa.point(indexHead) + arrowDir); ++ a.setPoint(1, pa.point(indexHead) + TQPoint(arrowDir.y()/2, ++ -arrowDir.x()/2)); ++ a.setPoint(2, pa.point(indexHead) + TQPoint(-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 ++ TQChar 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 TQCanvas(size().width(),size().height()); ++ TQString s = i18n("Error running the graph layouting tool.\n"); ++ s += i18n("Please check that 'dot' is installed (package GraphViz)."); ++ TQCanvasText* t = new TQCanvasText(s, _canvas); ++ t->move(5, 5); ++ t->show(); ++ center(0,0); ++ } ++ else if (!activeNode && !activeEdge) { ++ TQString s = i18n("There is no call graph available for function\n" ++ "\t'%1'\n" ++ "because it has no cost of the selected event type."); ++ TQCanvasText* t = new TQCanvasText(s.arg(_activeItem->name()), _canvas); ++ // t->setTextFlags(TQt::AlignHCenter | TQt::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) ++{ ++ TQRect 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(TQMouseEvent* e) ++{ ++ // clicking on the viewport sets focus ++ setFocus(); ++ ++ _isMoving = true; ++ ++ TQCanvasItemList l = canvas()->collisions(e->pos()); ++ if (l.count()>0) { ++ TQCanvasItem* 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(TQMouseEvent* 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(TQMouseEvent*) ++{ ++ _isMoving = false; ++ if (_zoomPosition == Auto) updateSizes(); ++} ++ ++void CallGraphView::contentsMouseDoubleClickEvent(TQMouseEvent* e) ++{ ++ TQCanvasItemList l = canvas()->collisions(e->pos()); ++ if (l.count() == 0) return; ++ TQCanvasItem* 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(TQContextMenuEvent* e) ++{ ++ TQCanvasItemList l = canvas()->collisions(e->pos()); ++ TQCanvasItem* i = (l.count() == 0) ? 0 : l.first(); ++ ++ TQPopupMenu 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(); ++ ++ TQString 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) { ++ TQString 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(); ++ ++ TQPopupMenu epopup; ++ epopup.insertItem(i18n("As PostScript"), 201); ++ epopup.insertItem(i18n("As Image ..."), 202); ++ ++ popup.insertItem(i18n("Export Graph"), &epopup, 200); ++ popup.insertSeparator(); ++ ++ TQPopupMenu 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; ++ } ++ ++ TQPopupMenu 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; ++ } ++ ++ TQPopupMenu 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; ++ ++ TQPopupMenu 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); ++ ++ TQPopupMenu 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); ++ ++ TQPopupMenu 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); ++ ++ TQPopupMenu 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(TraceItemView::data(), f, costType(), groupType(), ++ TQString("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; ++ ++ TQString fn = KFileDialog::getSaveFileName(":","*.png"); ++ ++ if (!fn.isEmpty()) { ++ TQPixmap pix(_canvas->size()); ++ TQPainter 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(TQString s) ++{ ++ if (s == TQString("TopLeft")) return TopLeft; ++ if (s == TQString("TopRight")) return TopRight; ++ if (s == TQString("BottomLeft")) return BottomLeft; ++ if (s == TQString("BottomRight")) return BottomRight; ++ if (s == TQString("Automatic")) return Auto; ++ ++ return DEFAULT_ZOOMPOS; ++} ++ ++TQString CallGraphView::zoomPosString(ZoomPosition p) ++{ ++ if (p == TopRight) return TQString("TopRight"); ++ if (p == BottomLeft) return TQString("BottomLeft"); ++ if (p == BottomRight) return TQString("BottomRight"); ++ if (p == Auto) return TQString("Automatic"); ++ ++ return TQString("TopLeft"); ++} ++ ++void CallGraphView::readViewConfig(KConfig* c, ++ TQString prefix, TQString 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, ++ TQString prefix, TQString 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/kdecachegrind/kdecachegrind/callgraphview.h b/kdecachegrind/kdecachegrind/callgraphview.h +new file mode 100644 +index 0000000..4db619d +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/callgraphview.h +@@ -0,0 +1,501 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Callgraph View ++ */ ++ ++#ifndef CALLGRAPHVIEW_H ++#define CALLGRAPHVIEW_H ++ ++#include <tqcanvas.h> ++#include <tqwidget.h> ++#include <tqmap.h> ++#include <tqtimer.h> ++ ++#include "treemap.h" // for DrawParams ++#include "tracedata.h" ++#include "traceitemview.h" ++ ++class TQProcess; ++ ++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 TQPtrList<GraphEdge> ++{ ++ public: ++ GraphEdgeList(); ++ void setSortCallerPos(bool b) { _sortCallerPos = b; } ++ ++ protected: ++ int compareItems ( Item item1, Item item2 ); ++ ++ private: ++ bool _sortCallerPos; ++}; ++ ++ ++typedef TQMap<GraphEdge*, int> 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 ++ TQString 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 TQMap<TraceFunction*, GraphNode> GraphNodeMap; ++typedef TQMap<TQPair<TraceFunction*, TraceFunction*>, 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 TQString layoutString(Layout); ++ static Layout layout(TQString); ++}; ++ ++/* 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, TQString filename = TQString()); ++ virtual ~GraphExporter(); ++ ++ void reset(TraceData*, TraceItem*, TraceCostType*, ++ TraceItem::CostType, TQString filename = TQString()); ++ ++ TQString 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 ++ ++ /* <toFunc> 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(TQString); ++ 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); ++ ++ TQString _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 TQCanvas ++ */ ++class PannerView: public TQCanvasView ++{ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ PannerView(TQWidget * parent = 0, const char * name = 0); ++ ++ void setZoomRect(TQRect r); ++ ++signals: ++ void zoomRectMoved(int dx, int dy); ++ void zoomRectMoveFinished(); ++ ++protected: ++ void contentsMousePressEvent(TQMouseEvent*); ++ void contentsMouseMoveEvent(TQMouseEvent*); ++ void contentsMouseReleaseEvent(TQMouseEvent*); ++ void drawContents(TQPainter * p, int clipx, int clipy, int clipw, int cliph); ++ ++ TQRect _zoomRect; ++ bool _movingZoomRect; ++ TQPoint _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 TQCanvasRectangle, public StoredDrawParams ++{ ++public: ++ CanvasNode(CallGraphView*,GraphNode*, int, int, int, int, TQCanvas*); ++ ++ void updateGroup(); ++ void setSelected(bool); ++ void drawShape(TQPainter&); ++ ++ GraphNode* node() { return _node; } ++ int rtti() const { return CANVAS_NODE; } ++ ++private: ++ GraphNode* _node; ++ CallGraphView* _view; ++}; ++ ++class CanvasEdgeLabel: public TQCanvasRectangle, public StoredDrawParams ++{ ++public: ++ CanvasEdgeLabel(CallGraphView*, CanvasEdge*, int, int, int, int, TQCanvas*); ++ ++ void drawShape(TQPainter&); ++ ++ CanvasEdge* canvasEdge() { return _ce; } ++ int rtti() const { return CANVAS_EDGELABEL; } ++ ++private: ++ CanvasEdge* _ce; ++ CallGraphView* _view; ++}; ++ ++class CanvasEdgeArrow: public TQCanvasPolygon ++{ ++public: ++ CanvasEdgeArrow(CanvasEdge*, TQCanvas*); ++ ++ void drawShape(TQPainter&); ++ ++ CanvasEdge* canvasEdge() { return _ce; } ++ int rtti() const { return CANVAS_EDGEARROW; } ++ ++private: ++ CanvasEdge* _ce; ++}; ++ ++ ++class CanvasEdge: public TQCanvasSpline ++{ ++public: ++ CanvasEdge(GraphEdge*, TQCanvas*); ++ ++ void setSelected(bool); ++ void drawShape(TQPainter&); ++ TQPointArray 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 TQCanvasRectangle ++{ ++public: ++ CanvasFrame( CanvasNode*, TQCanvas *canvas ); ++ int rtti () const { return CANVAS_FRAME; } ++ bool hit( const TQPoint&) const { return false; } ++protected: ++ void drawShape( TQPainter & ); ++private: ++ static TQPixmap* _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 TQCanvasView, public TraceItemView, ++ public StorableGraphOptions ++{ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ enum ZoomPosition { TopLeft, TopRight, BottomLeft, BottomRight, Auto }; ++ ++ CallGraphView(TraceItemView* parentView, ++ TQWidget* parent=0, const char* name=0); ++ ~CallGraphView(); ++ ++ void readViewConfig(KConfig*, TQString prefix, TQString postfix, bool); ++ void saveViewConfig(KConfig*, TQString prefix, TQString postfix, bool); ++ ++ TQWidget* widget() { return this; } ++ TQString whatsThis() const; ++ ++ ZoomPosition zoomPos() const { return _zoomPosition; } ++ static ZoomPosition zoomPos(TQString); ++ static TQString 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(TQResizeEvent*); ++ void contentsMousePressEvent(TQMouseEvent*); ++ void contentsMouseMoveEvent(TQMouseEvent*); ++ void contentsMouseReleaseEvent(TQMouseEvent*); ++ void contentsMouseDoubleClickEvent(TQMouseEvent*); ++ void contentsContextMenuEvent(TQContextMenuEvent*); ++ void keyPressEvent(TQKeyEvent*); ++ void focusInEvent(TQFocusEvent*); ++ void focusOutEvent(TQFocusEvent*); ++ ++private: ++ void updateSizes(TQSize s = TQSize(0,0)); ++ TraceItem* canShow(TraceItem*); ++ void doUpdate(int); ++ void refresh(); ++ void makeFrame(CanvasNode*, bool active); ++ void clear(); ++ void showText(TQString); ++ ++ TQCanvas *_canvas; ++ int _xMargin, _yMargin; ++ PannerView *_completeView; ++ double _cvZoom; ++ ++ CallGraphTip* _tip; ++ ++ bool _isMoving; ++ TQPoint _lastPos; ++ ++ GraphExporter _exporter; ++ ++ GraphNode* _selectedNode; ++ GraphEdge* _selectedEdge; ++ ++ // widget options ++ ZoomPosition _zoomPosition, _lastAutoPosition; ++ ++ // background rendering ++ TQProcess* _renderProcess; ++ TQTimer _renderTimer; ++ GraphNode* _prevSelectedNode; ++ TQPoint _prevSelectedPos; ++ TQString _unparsedOutput; ++}; ++ ++ ++ ++ ++#endif ++ ++ ++ +diff --git a/kdecachegrind/kdecachegrind/callitem.cpp b/kdecachegrind/kdecachegrind/callitem.cpp +new file mode 100644 +index 0000000..ebca490 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/callitem.cpp +@@ -0,0 +1,185 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002, 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Items for caller/callee view. ++ */ ++ ++#include <tqpixmap.h> ++#include <klocale.h> ++#include <kapplication.h> ++#include <kiconloader.h> ++ ++#include "configuration.h" ++#include "listutils.h" ++#include "callitem.h" ++#include "callview.h" ++#include "toplevel.h" ++ ++// CallItem ++ ++ ++CallItem::CallItem(CallView* view, TQListView* parent, TraceCall* c) ++ : TQListViewItem(parent) ++{ ++ _call = c; ++ _view = view; ++ ++ _active = _view->activeFunction(); ++ bool baseIsCycle = (_active && (_active == _active->cycle())); ++ ++ TQString 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() ++{ ++ TQColor 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; ++ ++ TQString 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) { ++ TQString str = "-"; ++ ++ setText(0, str); ++ setPixmap(0, TQPixmap()); ++ } ++ else { ++ double sum = 100.0 * _sum / total; ++ ++ if (_view->topLevel()->showPercentage()) ++ setText(0, TQString("%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) { ++ TQString str = "-"; ++ ++ setText(1, str); ++ setPixmap(1, TQPixmap()); ++ } ++ else { ++ double sum = 100.0 * _sum2 / total; ++ ++ if (_view->topLevel()->showPercentage()) ++ setText(1, TQString("%1") ++ .arg(sum, 0, 'f', Configuration::percentPrecision())); ++ else ++ setText(1, _call->prettySubCost(ct2)); ++ ++ setPixmap(1, costPixmap(ct2, _call, total, false)); ++ } ++ } ++ ++ TQPixmap p; ++ if (sameCycle && !selectedIsCycle && !shownIsCycle) { ++ ++ TQString icon = "undo"; ++ KIconLoader* loader = KApplication::kApplication()->iconLoader(); ++ p= loader->loadIcon(icon, KIcon::Small, 0, ++ KIcon::DefaultState, 0, true); ++ } ++ setPixmap(2, p); ++} ++ ++ ++int CallItem::compare(TQListViewItem * 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 TQListViewItem::compare(i, col, ascending); ++} ++ +diff --git a/kdecachegrind/kdecachegrind/callitem.h b/kdecachegrind/kdecachegrind/callitem.h +new file mode 100644 +index 0000000..94191b8 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/callitem.h +@@ -0,0 +1,50 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Items of call view. ++ */ ++ ++#ifndef CALLITEM_H ++#define CALLITEM_H ++ ++#include <tqlistview.h> ++#include "tracedata.h" ++ ++class CallView; ++ ++class CallItem: public TQListViewItem ++{ ++public: ++ CallItem(CallView*, TQListView*, TraceCall* c); ++ ++ int compare(TQListViewItem * 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/kdecachegrind/kdecachegrind/callmapview.cpp b/kdecachegrind/kdecachegrind/callmapview.cpp +new file mode 100644 +index 0000000..0e4d5e3 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/callmapview.cpp +@@ -0,0 +1,999 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Call Map View ++ */ ++ ++#include <tqwhatsthis.h> ++#include <tqpopupmenu.h> ++ ++#include <klocale.h> ++#include <kiconloader.h> ++#include <kapplication.h> ++#include <kconfig.h> ++ ++#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, ++ TQWidget* 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, ++ TQT_SIGNAL(doubleClicked(TreeMapItem*)), ++ TQT_SLOT(activatedSlot(TreeMapItem*))); ++ connect(this, ++ TQT_SIGNAL(returnPressed(TreeMapItem*)), ++ TQT_SLOT(activatedSlot(TreeMapItem*))); ++ connect(this, ++ TQT_SIGNAL(currentChanged(TreeMapItem*, bool)), ++ TQT_SLOT(selectedSlot(TreeMapItem*, bool))); ++ connect(this, ++ TQT_SIGNAL(contextMenuRequested(TreeMapItem*,const TQPoint &)), ++ TQT_SLOT(context(TreeMapItem*,const TQPoint &))); ++ ++ TQWhatsThis::add( this, whatsThis()); ++} ++ ++TQString CallMapView::whatsThis() const ++{ ++ TQString s = _showCallers ? ++ i18n( "<b>Caller Map</b>" ++ "<p>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).</p>") : ++ i18n("<b>Call Map</b>" ++ "<p>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).</p>"); ++ ++ s += i18n( "<p>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 " ++ "<em>very</em> 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 <em>before</em> drawing children. Note that " ++ "size proportions can get <em>heavily</em> wrong.</p>" ++ ++ "<p>This is a <em>TreeMap</em> 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. " ++ "<em>Return</em> activates the current item.</p>"); ++ ++ return s; ++} ++ ++void CallMapView::setData(TraceData* d) ++{ ++ TraceItemView::setData(d); ++ ++ ((CallMapBaseItem*)base())->setFunction(0); ++} ++ ++void CallMapView::context(TreeMapItem* i,const TQPoint & p) ++{ ++ if (!i) return; ++ ++ TQPopupMenu popup; ++ TQPopupMenu fpopup; // select function subpopup ++ TQPopupMenu vpopup; // visualisation subpopup ++ TQPopupMenu dpopup; // split direction ++ TQPopupMenu bpopup; // border subpopup ++ TQPopupMenu l1popup; // depth limit subpopup ++ TQPopupMenu l2popup; // function limit subpopup ++ TQPopupMenu l3popup; // area limit subpopup ++ ++ TreeMapItem* item = i; ++ int count; ++ ++ TQString 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 (count<Configuration::maxSymbolCount() && item) { ++ TQString name = item->text(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 (count<Configuration::maxSymbolCount() && item) { ++ TQString name = item->text(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(); ++ TQString 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); ++ ++ TQPopupMenu 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, TQString()); 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) { ++ TQString 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(); ++} ++ ++ ++ ++TQColor CallMapView::groupColor(TraceFunction* f) const ++{ ++ if (!f) ++ return colorGroup().button(); ++ ++ return Configuration::functionColor(_groupType, f); ++} ++ ++ ++TQString CallMapView::tipString(TreeMapItem* i) const ++{ ++ TQString tip, itemTip; ++ int count = 0; ++ ++ //qDebug("CallMapView::tipString for '%s'", i->text(0).ascii()); ++ ++ // first, SubPartItem's ++ while (i && count<Configuration::maxSymbolCount()) { ++ 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()) 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(); ++} ++ ++ ++TQString CallMapBaseItem::text(int textNo) const ++{ ++ if (textNo == 0) { ++ if (!_f) ++ return i18n("(no function)"); ++ ++ return _f->prettyName(); ++ } ++ ++ if (!_f) return TQString(); ++ ++ if (textNo == 2) return _f->prettyLocation(); ++ if (textNo == 3) return _f->calledCount().pretty(); ++ if (textNo != 1) return TQString(); ++ ++ 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 TQString("%1 %") ++ .arg(sum, 0, 'f', Configuration::percentPrecision()); ++ } ++ return _f->inclusive()->prettySubCost(ct); ++} ++ ++TQPixmap CallMapBaseItem::pixmap(int i) const ++{ ++ if ((i != 1) || !_f) return TQPixmap(); ++ ++ 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; ++} ++ ++TQColor 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 ++} ++ ++TQString 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 TQString(); ++ ++ 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 TQString("%1 %") ++ .arg(p, 0, 'f', Configuration::percentPrecision()); ++ } ++ return val.pretty(); ++} ++ ++TQPixmap CallMapCallingItem::pixmap(int i) const ++{ ++ if (i != 1) return TQPixmap(); ++ ++ // 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; ++} ++ ++ ++TQColor CallMapCallingItem::backColor() const ++{ ++ CallMapView* w = (CallMapView*)widget(); ++ return w->groupColor(_c->called()); ++} ++ ++ ++// CallMapCallerItem ++ ++CallMapCallerItem::CallMapCallerItem(double factor, TraceCall* c) ++{ ++ _factor = factor; ++ _c = c; ++} ++ ++TQString 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 TQString(); ++ ++ 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 TQString("%1 %") ++ .arg(p, 0, 'f', Configuration::percentPrecision()); ++ } ++ return val.pretty(); ++} ++ ++ ++TQPixmap CallMapCallerItem::pixmap(int i) const ++{ ++ if (i != 1) return TQPixmap(); ++ ++ // 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; ++} ++ ++TQColor CallMapCallerItem::backColor() const ++{ ++ CallMapView* w = (CallMapView*)widget(); ++ return w->groupColor(_c->caller()); ++} ++ ++void CallMapView::readViewConfig(KConfig* c, ++ TQString prefix, TQString 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, ++ TQString prefix, TQString 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/kdecachegrind/kdecachegrind/callmapview.h b/kdecachegrind/kdecachegrind/callmapview.h +new file mode 100644 +index 0000000..860743f +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/callmapview.h +@@ -0,0 +1,130 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * 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 ++ TQ_OBJECT ++ ++public: ++ ++ CallMapView(bool showCallers, TraceItemView* parentView, ++ TQWidget* parent=0, const char* name=0); ++ ++ TQWidget* widget() { return this; } ++ TQString whatsThis() const; ++ void setData(TraceData*); ++ ++ void readViewConfig(KConfig*, TQString prefix, TQString postfix, bool); ++ void saveViewConfig(KConfig*, TQString prefix, TQString postfix, bool); ++ ++ bool showCallers() const { return _showCallers; } ++ TraceCost* totalCost(); ++ TQString tipString(TreeMapItem*) const; ++ TQColor groupColor(TraceFunction*) const; ++ ++private slots: ++ void context(TreeMapItem*,const TQPoint &); ++ 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; ++ TQString text(int) const; ++ TQPixmap pixmap(int) const; ++ TreeMapItemList* children(); ++ TQColor 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; ++ TQString text(int) const; ++ TQPixmap pixmap(int) const; ++ TreeMapItemList* children(); ++ TQColor 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; ++ TQString text(int) const; ++ TQPixmap pixmap(int) const; ++ TreeMapItemList* children(); ++ TQColor backColor() const; ++ ++private: ++ TraceCall* _c; ++ double _factor; ++}; ++ ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/callview.cpp b/kdecachegrind/kdecachegrind/callview.cpp +new file mode 100644 +index 0000000..317d137 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/callview.cpp +@@ -0,0 +1,256 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Call Views ++ */ ++ ++#include <tqwhatsthis.h> ++#include <tqpopupmenu.h> ++#include <klocale.h> ++ ++#include "configuration.h" ++#include "callitem.h" ++#include "callview.h" ++ ++ ++ ++// ++// CallView ++// ++ ++ ++CallView::CallView(bool showCallers, TraceItemView* parentView, ++ TQWidget* parent, const char* name) ++ : TQListView(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, TQt::AlignRight); ++ setColumnAlignment(1, TQt::AlignRight); ++ setColumnAlignment(2, TQt::AlignRight); ++ setAllColumnsShowFocus(true); ++ setResizeMode(TQListView::LastColumn); ++ setMinimumHeight(50); ++ ++ connect( this, ++ TQT_SIGNAL( selectionChanged(TQListViewItem*) ), ++ TQT_SLOT( selectedSlot(TQListViewItem*) ) ); ++ ++ connect( this, ++ TQT_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint &, int)), ++ TQT_SLOT(context(TQListViewItem*, const TQPoint &, int))); ++ ++ connect(this, ++ TQT_SIGNAL(doubleClicked(TQListViewItem*)), ++ TQT_SLOT(activatedSlot(TQListViewItem*))); ++ ++ connect(this, ++ TQT_SIGNAL(returnPressed(TQListViewItem*)), ++ TQT_SLOT(activatedSlot(TQListViewItem*))); ++ ++ TQWhatsThis::add( this, whatsThis() ); ++} ++ ++TQString CallView::whatsThis() const ++{ ++ return _showCallers ? ++ i18n( "<b>List of direct Callers</b>" ++ "<p>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.</p>" ++ "<p>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.</p>" ++ "<p>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.</p>") : ++ i18n( "<b>List of direct Callees</b>" ++ "<p>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.</p>" ++ "<p>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.</p>"); ++} ++ ++ ++void CallView::context(TQListViewItem* i, const TQPoint & p, int col) ++{ ++ TQPopupMenu popup; ++ ++ // Menu entry: ++ TraceCall* c = i ? ((CallItem*) i)->call() : 0; ++ TraceFunction *f = 0, *cycle = 0; ++ ++ if (c) { ++ TQString 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(TQListViewItem * 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(TQListViewItem * 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*) TQListView::selectedItem(); ++ TraceCall* c; ++ TraceItem* ti; ++ if (ci) { ++ c = ci->call(); ++ ti = _showCallers ? c->caller() : c->called(); ++ if (ti == _selectedItem) return; ++ } ++ ++ TQListViewItem *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) { ++ TQListViewItem *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, TQListView::Maximum); ++ ++ for (call=l.first();call;call=l.next()) ++ if (call->subCost(_costType)>0) ++ new CallItem(this, this, call); ++ ++ if (!_costType2) { ++ setColumnWidthMode(1, TQListView::Manual); ++ setColumnWidth(1, 0); ++ } ++} ++ ++#include "callview.moc" +diff --git a/kdecachegrind/kdecachegrind/callview.h b/kdecachegrind/kdecachegrind/callview.h +new file mode 100644 +index 0000000..be644f9 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/callview.h +@@ -0,0 +1,56 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Call Views ++ */ ++ ++#ifndef CALLVIEW_H ++#define CALLVIEW_H ++ ++#include <tqlistview.h> ++#include "tracedata.h" ++#include "traceitemview.h" ++ ++class CallView: public TQListView, public TraceItemView ++{ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ CallView(bool showCallers, TraceItemView* parentView, ++ TQWidget* parent=0, const char* name=0); ++ ++ virtual TQWidget* widget() { return this; } ++ TQString whatsThis() const; ++ bool showCallers() const { return _showCallers; } ++ ++private slots: ++ void context(TQListViewItem*,const TQPoint &, int); ++ void selectedSlot(TQListViewItem*); ++ void activatedSlot(TQListViewItem*); ++ ++private: ++ TraceItem* canShow(TraceItem*); ++ void doUpdate(int); ++ void refresh(); ++ ++ bool _showCallers; ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/configdlg.cpp b/kdecachegrind/kdecachegrind/configdlg.cpp +new file mode 100644 +index 0000000..e0b4547 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/configdlg.cpp +@@ -0,0 +1,398 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002, 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Configuration Dialog for KCachegrind ++ */ ++ ++#include <tqcombobox.h> ++#include <tqcheckbox.h> ++#include <tqlineedit.h> ++#include <tqlistview.h> ++#include <tqdict.h> ++#include <tqmessagebox.h> ++ ++#include <kcolorbutton.h> ++#include <kfiledialog.h> ++#include <klocale.h> ++#include <knumvalidator.h> ++ ++#include "configdlg.h" ++#include "tracedata.h" ++#include "configuration.h" ++ ++ ++ConfigDlg::ConfigDlg(Configuration* c, TraceData* data, ++ TQWidget* 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 ++ TQListViewItem *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, TQT_SIGNAL(activated(const TQString &)), ++ this, TQT_SLOT(objectActivated(const TQString &))); ++ connect(objectCombo, TQT_SIGNAL(textChanged(const TQString &)), ++ this, TQT_SLOT(objectActivated(const TQString &))); ++ connect(objectCheck, TQT_SIGNAL(toggled(bool)), ++ this, TQT_SLOT(objectCheckChanged(bool))); ++ connect(objectColor, TQT_SIGNAL(changed(const TQColor &)), ++ this, TQT_SLOT(objectColorChanged(const TQColor &))); ++ ++ connect(classCombo, TQT_SIGNAL(activated(const TQString &)), ++ this, TQT_SLOT(classActivated(const TQString &))); ++ connect(classCombo, TQT_SIGNAL(textChanged(const TQString &)), ++ this, TQT_SLOT(classActivated(const TQString &))); ++ connect(classCheck, TQT_SIGNAL(toggled(bool)), ++ this, TQT_SLOT(classCheckChanged(bool))); ++ connect(classColor, TQT_SIGNAL(changed(const TQColor &)), ++ this, TQT_SLOT(classColorChanged(const TQColor &))); ++ ++ connect(fileCombo, TQT_SIGNAL(activated(const TQString &)), ++ this, TQT_SLOT(fileActivated(const TQString &))); ++ connect(fileCombo, TQT_SIGNAL(textChanged(const TQString &)), ++ this, TQT_SLOT(fileActivated(const TQString &))); ++ connect(fileCheck, TQT_SIGNAL(toggled(bool)), ++ this, TQT_SLOT(fileCheckChanged(bool))); ++ connect(fileColor, TQT_SIGNAL(changed(const TQColor &)), ++ this, TQT_SLOT(fileColorChanged(const TQColor &))); ++ ++ TQString objectPrefix = TraceCost::typeName(TraceCost::Object); ++ TQString classPrefix = TraceCost::typeName(TraceCost::Class); ++ TQString 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; ++ TQStringList oList; ++ for ( oit = data->objectMap().begin(); ++ oit != data->objectMap().end(); ++oit ) ++ oList.append((*oit).prettyName()); ++ ++ TraceClassMap::Iterator cit; ++ TQStringList cList; ++ for ( cit = data->classMap().begin(); ++ cit != data->classMap().end(); ++cit ) ++ cList.append((*cit).prettyName()); ++ ++ TraceFileMap::Iterator fit; ++ TQStringList fList; ++ for ( fit = data->fileMap().begin(); ++ fit != data->fileMap().end(); ++fit ) ++ fList.append((*fit).prettyName()); ++ ++ // then already defined colors (have to check for duplicates!) ++ TQDictIterator<Configuration::ColorSetting> it( c->_colors ); ++ for( ; it.current(); ++it ) { ++ if ((*it)->automatic) continue; ++ ++ TQString 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(TQString::number(c->_maxListCount)); ++ ++ _dirItem = 0; ++ ++ TQListViewItem* i = new TQListViewItem(dirList, i18n("(always)")); ++ i->setOpen(true); ++ TQStringList::Iterator sit = c->_generalSourceDirs.begin(); ++ for(; sit != c->_generalSourceDirs.end(); ++sit ) { ++ TQString d = (*sit); ++ if (d.isEmpty()) d = "/"; ++ new TQListViewItem(i, d); ++ } ++ for ( oit = data->objectMap().begin(); ++ oit != data->objectMap().end(); ++oit ) { ++ TQString n = (*oit).name(); ++ i = new TQListViewItem(dirList, n); ++ i->setOpen(true); ++ TQStringList* dirs = c->_objectSourceDirs[n]; ++ if (!dirs) continue; ++ ++ sit = dirs->begin(); ++ for(; sit != dirs->end(); ++sit ) { ++ TQString d = (*sit); ++ if (d.isEmpty()) d = "/"; ++ new TQListViewItem(i, d); ++ } ++ } ++ ++ connect(dirList, TQT_SIGNAL(selectionChanged(TQListViewItem*)), ++ this, TQT_SLOT(dirsItemChanged(TQListViewItem*))); ++ connect(addDirButton, TQT_SIGNAL(clicked()), ++ this, TQT_SLOT(dirsAddPressed())); ++ connect(deleteDirButton, TQT_SIGNAL(clicked()), ++ this, TQT_SLOT(dirsDeletePressed())); ++ dirList->setSelected(dirList->firstChild(), true); ++ ++ symbolCount->setText(TQString::number(c->_maxSymbolCount)); ++ symbolLength->setText(TQString::number(c->_maxSymbolLength)); ++ precisionEdit->setText(TQString::number(c->_percentPrecision)); ++ contextEdit->setText(TQString::number(c->_context)); ++} ++ ++ConfigDlg::~ConfigDlg() ++{ ++} ++ ++bool ConfigDlg::configure(Configuration* c, TraceData* d, TQWidget* 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 ++ TQMessageBox::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(TQString::number(c->_maxListCount)), ++ TQMessageBox::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 TQString & s) ++{ ++// qDebug("objectActivated: %s", s.ascii()); ++ ++ if (s.isEmpty()) { _objectCS=0; return; } ++ ++ TQString 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 TQColor & c) ++{ ++ if (_objectCS) _objectCS->color = c; ++} ++ ++void ConfigDlg::classActivated(const TQString & s) ++{ ++// qDebug("classActivated: %s", s.ascii()); ++ ++ if (s.isEmpty()) { _classCS=0; return; } ++ ++ TQString 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 TQColor & c) ++{ ++ if (_classCS) _classCS->color = c; ++} ++ ++ ++void ConfigDlg::fileActivated(const TQString & s) ++{ ++// qDebug("fileActivated: %s", s.ascii()); ++ ++ if (s.isEmpty()) { _fileCS=0; return; } ++ ++ TQString 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 TQColor & c) ++{ ++ if (_fileCS) _fileCS->color = c; ++} ++ ++ ++void ConfigDlg::dirsItemChanged(TQListViewItem* i) ++{ ++ _dirItem = i; ++ deleteDirButton->setEnabled(i->depth() == 1); ++ addDirButton->setEnabled(i->depth() == 0); ++} ++ ++void ConfigDlg::dirsDeletePressed() ++{ ++ if (!_dirItem || (_dirItem->depth() == 0)) return; ++ TQListViewItem* p = _dirItem->parent(); ++ if (!p) return; ++ ++ Configuration* c = Configuration::config(); ++ TQString objName = p->text(0); ++ ++ TQStringList* 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(); ++ TQString objName = _dirItem->text(0); ++ ++ TQStringList* dirs; ++ if (objName == i18n("(always)")) ++ dirs = &(c->_generalSourceDirs); ++ else { ++ dirs = c->_objectSourceDirs[objName]; ++ if (!dirs) { ++ dirs = new TQStringList; ++ c->_objectSourceDirs.insert(objName, dirs); ++ } ++ } ++ ++ TQString newDir; ++ newDir = KFileDialog::getExistingDirectory(TQString(), ++ 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 = TQString("/"); ++ new TQListViewItem(_dirItem, newDir); ++} ++ ++#include "configdlg.moc" +diff --git a/kdecachegrind/kdecachegrind/configdlg.h b/kdecachegrind/kdecachegrind/configdlg.h +new file mode 100644 +index 0000000..5ef6bab +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/configdlg.h +@@ -0,0 +1,65 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002, 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Configuration Dialog for KCachegrind ++ */ ++ ++#ifndef CONFIGDLG_H ++#define CONFIGDLG_H ++ ++#include "configdlgbase.h" ++#include "configuration.h" ++ ++class TraceData; ++ ++class ConfigDlg : public ConfigDlgBase ++{ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ ConfigDlg(Configuration*, TraceData*, ++ TQWidget* parent = 0, const char* name = 0); ++ ~ConfigDlg(); ++ ++ static bool configure(Configuration*, TraceData*, TQWidget*); ++ ++protected slots: ++ void objectActivated(const TQString &); ++ void objectCheckChanged(bool); ++ void objectColorChanged(const TQColor &); ++ void classActivated(const TQString &); ++ void classCheckChanged(bool); ++ void classColorChanged(const TQColor &); ++ void fileActivated(const TQString &); ++ void fileCheckChanged(bool); ++ void fileColorChanged(const TQColor &); ++ void dirsItemChanged(TQListViewItem*); ++ void dirsDeletePressed(); ++ void dirsAddPressed(); ++ ++private: ++ Configuration* _config; ++ TraceData* _data; ++ ++ Configuration::ColorSetting *_objectCS, *_classCS, *_fileCS; ++ TQListViewItem* _dirItem; ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/configdlgbase.ui b/kdecachegrind/kdecachegrind/configdlgbase.ui +new file mode 100644 +index 0000000..dc0ee9e +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/configdlgbase.ui +@@ -0,0 +1,653 @@ ++<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> ++<class>ConfigDlgBase</class> ++<widget class="TQDialog"> ++ <property name="name"> ++ <cstring>configDlgBase</cstring> ++ </property> ++ <property name="geometry"> ++ <rect> ++ <x>0</x> ++ <y>0</y> ++ <width>447</width> ++ <height>378</height> ++ </rect> ++ </property> ++ <property name="caption"> ++ <string>Configuration</string> ++ </property> ++ <vbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <property name="margin"> ++ <number>11</number> ++ </property> ++ <property name="spacing"> ++ <number>6</number> ++ </property> ++ <widget class="TQTabWidget"> ++ <property name="name"> ++ <cstring>tabWidget2</cstring> ++ </property> ++ <widget class="TQWidget"> ++ <property name="name"> ++ <cstring>tab</cstring> ++ </property> ++ <attribute name="title"> ++ <string>General</string> ++ </attribute> ++ <vbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <widget class="TQLayoutWidget"> ++ <property name="name"> ++ <cstring>layout10</cstring> ++ </property> ++ <grid> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <widget class="TQLineEdit" row="3" column="2" rowspan="1" colspan="2"> ++ <property name="name"> ++ <cstring>precisionEdit</cstring> ++ </property> ++ <property name="sizePolicy"> ++ <sizepolicy> ++ <hsizetype>7</hsizetype> ++ <vsizetype>0</vsizetype> ++ <horstretch>2</horstretch> ++ <verstretch>0</verstretch> ++ </sizepolicy> ++ </property> ++ </widget> ++ <widget class="TQLabel" row="2" column="1"> ++ <property name="name"> ++ <cstring>TextLabel2</cstring> ++ </property> ++ <property name="text"> ++ <string>Truncated when more/longer than:</string> ++ </property> ++ </widget> ++ <widget class="TQLabel" row="3" column="0" rowspan="1" colspan="2"> ++ <property name="name"> ++ <cstring>TextLabel4_3</cstring> ++ </property> ++ <property name="text"> ++ <string>Precision of percentage values:</string> ++ </property> ++ </widget> ++ <widget class="TQLabel" row="1" column="0" rowspan="1" colspan="3"> ++ <property name="name"> ++ <cstring>TextLabel3</cstring> ++ </property> ++ <property name="text"> ++ <string>Symbols in tooltips and context menus</string> ++ </property> ++ </widget> ++ <widget class="TQLineEdit" row="2" column="3"> ++ <property name="name"> ++ <cstring>symbolLength</cstring> ++ </property> ++ <property name="sizePolicy"> ++ <sizepolicy> ++ <hsizetype>4</hsizetype> ++ <vsizetype>0</vsizetype> ++ <horstretch>0</horstretch> ++ <verstretch>0</verstretch> ++ </sizepolicy> ++ </property> ++ </widget> ++ <spacer row="2" column="0"> ++ <property name="name"> ++ <cstring>Spacer6_2_2_2</cstring> ++ </property> ++ <property name="orientation"> ++ <enum>Horizontal</enum> ++ </property> ++ <property name="sizeType"> ++ <enum>Fixed</enum> ++ </property> ++ <property name="sizeHint"> ++ <size> ++ <width>16</width> ++ <height>20</height> ++ </size> ++ </property> ++ </spacer> ++ <widget class="TQLineEdit" row="0" column="2" rowspan="1" colspan="2"> ++ <property name="name"> ++ <cstring>maxListEdit</cstring> ++ </property> ++ </widget> ++ <widget class="TQLineEdit" row="2" column="2"> ++ <property name="name"> ++ <cstring>symbolCount</cstring> ++ </property> ++ <property name="sizePolicy"> ++ <sizepolicy> ++ <hsizetype>4</hsizetype> ++ <vsizetype>0</vsizetype> ++ <horstretch>0</horstretch> ++ <verstretch>0</verstretch> ++ </sizepolicy> ++ </property> ++ </widget> ++ <widget class="TQLabel" row="0" column="0" rowspan="1" colspan="2"> ++ <property name="name"> ++ <cstring>TextLabel5</cstring> ++ </property> ++ <property name="text"> ++ <string>Maximum number of items in lists:</string> ++ </property> ++ </widget> ++ </grid> ++ </widget> ++ <widget class="TQLabel"> ++ <property name="name"> ++ <cstring>TextLabel1</cstring> ++ </property> ++ <property name="font"> ++ <font> ++ <bold>1</bold> ++ </font> ++ </property> ++ <property name="frameShape"> ++ <enum>NoFrame</enum> ++ </property> ++ <property name="frameShadow"> ++ <enum>Plain</enum> ++ </property> ++ <property name="text"> ++ <string>Cost Item Colors</string> ++ </property> ++ </widget> ++ <widget class="TQLayoutWidget"> ++ <property name="name"> ++ <cstring>Layout9</cstring> ++ </property> ++ <grid> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <property name="margin"> ++ <number>0</number> ++ </property> ++ <property name="spacing"> ++ <number>6</number> ++ </property> ++ <spacer row="1" column="1"> ++ <property name="name"> ++ <cstring>Spacer9</cstring> ++ </property> ++ <property name="orientation"> ++ <enum>Vertical</enum> ++ </property> ++ <property name="sizeType"> ++ <enum>Fixed</enum> ++ </property> ++ <property name="sizeHint"> ++ <size> ++ <width>20</width> ++ <height>16</height> ++ </size> ++ </property> ++ </spacer> ++ <spacer row="0" column="0"> ++ <property name="name"> ++ <cstring>Spacer6</cstring> ++ </property> ++ <property name="orientation"> ++ <enum>Horizontal</enum> ++ </property> ++ <property name="sizeType"> ++ <enum>Fixed</enum> ++ </property> ++ <property name="sizeHint"> ++ <size> ++ <width>16</width> ++ <height>20</height> ++ </size> ++ </property> ++ </spacer> ++ <widget class="TQLayoutWidget" row="0" column="1"> ++ <property name="name"> ++ <cstring>Layout9</cstring> ++ </property> ++ <grid> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <property name="margin"> ++ <number>0</number> ++ </property> ++ <property name="spacing"> ++ <number>6</number> ++ </property> ++ <widget class="TQComboBox" row="1" column="1"> ++ <property name="name"> ++ <cstring>classCombo</cstring> ++ </property> ++ <property name="maximumSize"> ++ <size> ++ <width>300</width> ++ <height>32767</height> ++ </size> ++ </property> ++ <property name="editable"> ++ <bool>true</bool> ++ </property> ++ </widget> ++ <widget class="TQCheckBox" row="2" column="2"> ++ <property name="name"> ++ <cstring>fileCheck</cstring> ++ </property> ++ <property name="text"> ++ <string>Automatic</string> ++ </property> ++ </widget> ++ <widget class="TQLabel" row="0" column="0"> ++ <property name="name"> ++ <cstring>TextLabel4</cstring> ++ </property> ++ <property name="text"> ++ <string>Object:</string> ++ </property> ++ </widget> ++ <widget class="TQLabel" row="1" column="0"> ++ <property name="name"> ++ <cstring>TextLabel4_2_2</cstring> ++ </property> ++ <property name="text"> ++ <string>Class:</string> ++ </property> ++ </widget> ++ <widget class="KColorButton" row="2" column="3"> ++ <property name="name"> ++ <cstring>fileColor</cstring> ++ </property> ++ <property name="sizePolicy"> ++ <sizepolicy> ++ <hsizetype>0</hsizetype> ++ <vsizetype>0</vsizetype> ++ <horstretch>0</horstretch> ++ <verstretch>0</verstretch> ++ </sizepolicy> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ </widget> ++ <widget class="TQCheckBox" row="1" column="2"> ++ <property name="name"> ++ <cstring>classCheck</cstring> ++ </property> ++ <property name="text"> ++ <string>Automatic</string> ++ </property> ++ </widget> ++ <widget class="KColorButton" row="0" column="3"> ++ <property name="name"> ++ <cstring>objectColor</cstring> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ </widget> ++ <widget class="TQCheckBox" row="0" column="2"> ++ <property name="name"> ++ <cstring>objectCheck</cstring> ++ </property> ++ <property name="text"> ++ <string>Automatic</string> ++ </property> ++ </widget> ++ <widget class="TQLabel" row="2" column="0"> ++ <property name="name"> ++ <cstring>TextLabel4_2</cstring> ++ </property> ++ <property name="text"> ++ <string>File:</string> ++ </property> ++ </widget> ++ <widget class="KColorButton" row="1" column="3"> ++ <property name="name"> ++ <cstring>classColor</cstring> ++ </property> ++ <property name="sizePolicy"> ++ <sizepolicy> ++ <hsizetype>0</hsizetype> ++ <vsizetype>0</vsizetype> ++ <horstretch>0</horstretch> ++ <verstretch>0</verstretch> ++ </sizepolicy> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ </widget> ++ <widget class="TQComboBox" row="2" column="1"> ++ <property name="name"> ++ <cstring>fileCombo</cstring> ++ </property> ++ <property name="maximumSize"> ++ <size> ++ <width>300</width> ++ <height>32767</height> ++ </size> ++ </property> ++ <property name="editable"> ++ <bool>true</bool> ++ </property> ++ </widget> ++ <widget class="TQComboBox" row="0" column="1"> ++ <property name="name"> ++ <cstring>objectCombo</cstring> ++ </property> ++ <property name="sizePolicy"> ++ <sizepolicy> ++ <hsizetype>3</hsizetype> ++ <vsizetype>0</vsizetype> ++ <horstretch>0</horstretch> ++ <verstretch>0</verstretch> ++ </sizepolicy> ++ </property> ++ <property name="maximumSize"> ++ <size> ++ <width>300</width> ++ <height>32767</height> ++ </size> ++ </property> ++ <property name="editable"> ++ <bool>true</bool> ++ </property> ++ </widget> ++ </grid> ++ </widget> ++ </grid> ++ </widget> ++ </vbox> ++ </widget> ++ <widget class="TQWidget"> ++ <property name="name"> ++ <cstring>tab</cstring> ++ </property> ++ <attribute name="title"> ++ <string>Annotations</string> ++ </attribute> ++ <vbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <widget class="TQLayoutWidget"> ++ <property name="name"> ++ <cstring>layout8</cstring> ++ </property> ++ <hbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <widget class="TQLabel"> ++ <property name="name"> ++ <cstring>TextLabel4_3_2</cstring> ++ </property> ++ <property name="text"> ++ <string>Context lines in annotations:</string> ++ </property> ++ </widget> ++ <widget class="TQLineEdit"> ++ <property name="name"> ++ <cstring>contextEdit</cstring> ++ </property> ++ <property name="sizePolicy"> ++ <sizepolicy> ++ <hsizetype>7</hsizetype> ++ <vsizetype>0</vsizetype> ++ <horstretch>2</horstretch> ++ <verstretch>0</verstretch> ++ </sizepolicy> ++ </property> ++ </widget> ++ </hbox> ++ </widget> ++ <widget class="TQLabel"> ++ <property name="name"> ++ <cstring>TextLabel1_2</cstring> ++ </property> ++ <property name="font"> ++ <font> ++ <bold>1</bold> ++ </font> ++ </property> ++ <property name="text"> ++ <string>Source Folders</string> ++ </property> ++ </widget> ++ <widget class="TQLayoutWidget"> ++ <property name="name"> ++ <cstring>layout11</cstring> ++ </property> ++ <grid> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <spacer row="0" column="0"> ++ <property name="name"> ++ <cstring>Spacer6_2</cstring> ++ </property> ++ <property name="orientation"> ++ <enum>Horizontal</enum> ++ </property> ++ <property name="sizeType"> ++ <enum>Fixed</enum> ++ </property> ++ <property name="sizeHint"> ++ <size> ++ <width>16</width> ++ <height>20</height> ++ </size> ++ </property> ++ </spacer> ++ <widget class="TQListView" row="0" column="1"> ++ <column> ++ <property name="text"> ++ <string>Object / Related Source Base</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <property name="name"> ++ <cstring>dirList</cstring> ++ </property> ++ <property name="rootIsDecorated"> ++ <bool>true</bool> ++ </property> ++ </widget> ++ <widget class="TQLayoutWidget" row="0" column="2"> ++ <property name="name"> ++ <cstring>layout10</cstring> ++ </property> ++ <vbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <widget class="TQPushButton"> ++ <property name="name"> ++ <cstring>addDirButton</cstring> ++ </property> ++ <property name="text"> ++ <string>Add...</string> ++ </property> ++ </widget> ++ <spacer> ++ <property name="name"> ++ <cstring>Spacer5</cstring> ++ </property> ++ <property name="orientation"> ++ <enum>Vertical</enum> ++ </property> ++ <property name="sizeType"> ++ <enum>Expanding</enum> ++ </property> ++ <property name="sizeHint"> ++ <size> ++ <width>16</width> ++ <height>49</height> ++ </size> ++ </property> ++ </spacer> ++ <widget class="TQPushButton"> ++ <property name="name"> ++ <cstring>deleteDirButton</cstring> ++ </property> ++ <property name="text"> ++ <string>Delete</string> ++ </property> ++ </widget> ++ </vbox> ++ </widget> ++ <spacer row="1" column="1"> ++ <property name="name"> ++ <cstring>Spacer9_2</cstring> ++ </property> ++ <property name="orientation"> ++ <enum>Vertical</enum> ++ </property> ++ <property name="sizeType"> ++ <enum>Fixed</enum> ++ </property> ++ <property name="sizeHint"> ++ <size> ++ <width>20</width> ++ <height>16</height> ++ </size> ++ </property> ++ </spacer> ++ </grid> ++ </widget> ++ </vbox> ++ </widget> ++ </widget> ++ <widget class="Line"> ++ <property name="name"> ++ <cstring>Line1</cstring> ++ </property> ++ <property name="frameShape"> ++ <enum>HLine</enum> ++ </property> ++ <property name="frameShadow"> ++ <enum>Sunken</enum> ++ </property> ++ <property name="orientation"> ++ <enum>Horizontal</enum> ++ </property> ++ </widget> ++ <widget class="TQLayoutWidget"> ++ <property name="name"> ++ <cstring>Layout4</cstring> ++ </property> ++ <hbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <property name="margin"> ++ <number>0</number> ++ </property> ++ <property name="spacing"> ++ <number>6</number> ++ </property> ++ <spacer> ++ <property name="name"> ++ <cstring>Spacer2</cstring> ++ </property> ++ <property name="orientation"> ++ <enum>Horizontal</enum> ++ </property> ++ <property name="sizeType"> ++ <enum>Expanding</enum> ++ </property> ++ <property name="sizeHint"> ++ <size> ++ <width>210</width> ++ <height>0</height> ++ </size> ++ </property> ++ </spacer> ++ <widget class="TQPushButton"> ++ <property name="name"> ++ <cstring>PushButton2</cstring> ++ </property> ++ <property name="text"> ++ <string>&OK</string> ++ </property> ++ <property name="default"> ++ <bool>true</bool> ++ </property> ++ </widget> ++ <widget class="TQPushButton"> ++ <property name="name"> ++ <cstring>PushButton1</cstring> ++ </property> ++ <property name="text"> ++ <string>&Cancel</string> ++ </property> ++ </widget> ++ </hbox> ++ </widget> ++ </vbox> ++</widget> ++<connections> ++ <connection> ++ <sender>PushButton2</sender> ++ <signal>clicked()</signal> ++ <receiver>configDlgBase</receiver> ++ <slot>accept()</slot> ++ </connection> ++ <connection> ++ <sender>PushButton1</sender> ++ <signal>clicked()</signal> ++ <receiver>configDlgBase</receiver> ++ <slot>reject()</slot> ++ </connection> ++ <connection> ++ <sender>classCheck</sender> ++ <signal>toggled(bool)</signal> ++ <receiver>classColor</receiver> ++ <slot>setDisabled(bool)</slot> ++ </connection> ++ <connection> ++ <sender>fileCheck</sender> ++ <signal>toggled(bool)</signal> ++ <receiver>fileColor</receiver> ++ <slot>setDisabled(bool)</slot> ++ </connection> ++ <connection> ++ <sender>objectCheck</sender> ++ <signal>toggled(bool)</signal> ++ <receiver>objectColor</receiver> ++ <slot>setDisabled(bool)</slot> ++ </connection> ++</connections> ++<tabstops> ++ <tabstop>objectCombo</tabstop> ++ <tabstop>objectCheck</tabstop> ++ <tabstop>classCombo</tabstop> ++ <tabstop>classCheck</tabstop> ++ <tabstop>classColor</tabstop> ++ <tabstop>fileCombo</tabstop> ++ <tabstop>fileCheck</tabstop> ++ <tabstop>fileColor</tabstop> ++ <tabstop>maxListEdit</tabstop> ++ <tabstop>PushButton1</tabstop> ++ <tabstop>PushButton2</tabstop> ++</tabstops> ++<includes> ++ <include location="global" impldecl="in implementation">kcolorbutton.h</include> ++</includes> ++<pixmapinproject/> ++<layoutdefaults spacing="6" margin="11"/> ++</UI> +diff --git a/kdecachegrind/kdecachegrind/configuration.cpp b/kdecachegrind/kdecachegrind/configuration.cpp +new file mode 100644 +index 0000000..02d5c09 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/configuration.cpp +@@ -0,0 +1,490 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002, 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Configuration for KCachegrind ++ */ ++ ++#include <kconfig.h> ++#include <klocale.h> ++#include <kdebug.h> ++ ++#include "configuration.h" ++#include "tracedata.h" ++#include "configdlgbase.h" ++ ++#include "traceitemview.h" ++ ++// ++// Some predefined cost types... ++// ++ ++static TQStringList knownTypes() ++{ ++ TQStringList l; ++ ++ l << "Ir" << "Dr" << "Dw" ++ << "I1mr" << "D1mr" << "D1mw" ++ << "I2mr" << "D2mr" << "D2mw" ++ ++ << "Smp" << "Sys" << "User" ++ << "L1m" << "L2m" << "CEst"; ++ ++ return l; ++} ++ ++ ++static TQString knownFormula(TQString name) ++{ ++ if (name =="L1m") return TQString("I1mr + D1mr + D1mw"); ++ if (name =="L2m") return TQString("I2mr + D2mr + D2mw"); ++ if (name =="CEst") return TQString("Ir + 10 L1m + 100 L2m"); ++ ++ return TQString(); ++} ++ ++static TQString knownLongName(TQString 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 TQString(); ++} ++ ++ ++ ++ ++// ++// 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, TQCString("CostColors")); ++ TQDictIterator<ColorSetting> it( c->_colors ); ++ int count = 1; ++ for( ; it.current(); ++it ) { ++ if ( !(*it)->automatic ) { ++ colorConfig.writeEntry( TQString("Name%1").arg(count), ++ it.currentKey()); ++ colorConfig.writeEntry( TQString("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, TQCString("Source")); ++ sourceConfig.writeEntry("Dirs", c->_generalSourceDirs, ':'); ++ TQDictIterator<TQStringList> it2( c->_objectSourceDirs ); ++ count = 1; ++ for( ; it2.current(); ++it2 ) { ++ sourceConfig.writeEntry( TQString("Object%1").arg(count), ++ it2.currentKey()); ++ sourceConfig.writeEntry( TQString("Dirs%1").arg(count), ++ *(*it2), ':'); ++ count++; ++ } ++ sourceConfig.writeEntry( "Count", count-1); ++ ++ // general options ++ KConfigGroup generalConfig(kconfig, TQCString("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, TQCString("CostTypes")); ++ int ctCount = TraceCostType::knownTypeCount(); ++ ctConfig.writeEntry( "Count", ctCount); ++ for (int i=0; i<ctCount; i++) { ++ TraceCostType* t = TraceCostType::knownType(i); ++ ctConfig.writeEntry( TQString("Name%1").arg(i+1), t->name()); ++ ++ // Use localized key ++ TraceItemView::writeConfigEntry(&ctConfig, ++ TQString("Longname%1").arg(i+1).ascii(), ++ t->longName(), ++ knownLongName(t->name()).utf8().data() /*, true */ ); ++ TraceItemView::writeConfigEntry(&ctConfig, ++ TQString("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 = TQColor(240, 0, 0); ++ c->color("CostType-D2mr")->color = TQColor(180,40,40); ++ c->color("CostType-D2mw")->color = TQColor(120,80,80); ++ ++ c->color("CostType-I1mr")->color = TQColor(0, 240, 0); ++ c->color("CostType-D1mr")->color = TQColor(40,180,40); ++ c->color("CostType-D1mw")->color = TQColor(80,120,80); ++ ++ c->color("CostType-Ir")->color = TQColor(0, 0, 240); ++ c->color("CostType-Dr")->color = TQColor(40,40,180); ++ c->color("CostType-Dw")->color = TQColor(80,80,120); ++ ++ KConfigGroup colorConfig(kconfig, TQCString("CostColors")); ++ count = colorConfig.readNumEntry("Count", 0); ++ for (i=1;i<=count;i++) { ++ TQString n = colorConfig.readEntry(TQString("Name%1").arg(i)); ++ TQColor color = colorConfig.readColorEntry(TQString("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, TQCString("Source")); ++ TQStringList 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++) { ++ TQString n = sourceConfig.readEntry(TQString("Object%1").arg(i)); ++ dirs = sourceConfig.readListEntry(TQString("Dirs%1").arg(i), ':'); ++ ++ if (n.isEmpty() || (dirs.count()==0)) continue; ++ ++ c->_objectSourceDirs.insert(n, new TQStringList(dirs)); ++ } ++ ++ ++ // general options ++ KConfigGroup generalConfig(kconfig, TQCString("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, TQCString("CostTypes")); ++ int ctCount = ctConfig.readNumEntry("Count", 0); ++ if (ctCount>0) { ++ for (int i=1;i<=ctCount;i++) { ++ TQString n = ctConfig.readEntry(TQString("Name%1").arg(i)); ++ TQString l = ctConfig.readEntry(TQString("Longname%1").arg(i)); ++ if (l.isEmpty()) l = knownLongName(n); ++ TQString f = ctConfig.readEntry(TQString("Formula%1").arg(i)); ++ if (f.isEmpty()) f = knownFormula(n); ++ ++ TraceCostType::add(new TraceCostType(n, l, f)); ++ } ++ } ++ else { ++ // add default types ++ ++ TQString longName, formula; ++ TraceCostType* ct; ++ TQStringList l = knownTypes(); ++ for ( TQStringList::Iterator it = l.begin(); ++ it != l.end(); ++it ) { ++ longName = knownLongName(*it); ++ formula = knownFormula(*it); ++ ct = new TraceCostType(*it, longName, formula); ++ TraceCostType::add(ct); ++ } ++ } ++ } ++} ++ ++TQColor Configuration::groupColor(TraceItem* cost) ++{ ++ TQString n; ++ ++ if (!cost) ++ n = TQString("default"); ++ else ++ n = TraceCost::typeName(cost->type()) + "-" + cost->prettyName(); ++ ++ return color(n)->color; ++} ++ ++TQColor Configuration::costTypeColor(TraceCostType* t) ++{ ++ TQString n; ++ ++ if (!t) ++ n = TQString("CostType-default"); ++ else ++ n = TQString("CostType-%1").arg(t->name()); ++ ++ return color(n)->color; ++} ++ ++TQColor Configuration::functionColor(TraceCost::CostType gt, ++ TraceFunction* f) ++{ ++ TraceCost* group = f; ++ TQString 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(TQString 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); ++ TQColor color = TQColor(h, 64+s, 192, TQColor::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. ++ */ ++TQStringList Configuration::sourceDirs(TraceData* data, TraceObject* o) ++{ ++ TQStringList 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;i<ol->count();i++) ++ l.prepend( (*ol)[i] ); ++ } ++ if (ol2) { ++ for(unsigned int i=0;i<ol2->count();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; ++} ++ ++TQString Configuration::shortenSymbol(TQString 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/kdecachegrind/kdecachegrind/configuration.h b/kdecachegrind/kdecachegrind/configuration.h +new file mode 100644 +index 0000000..478f617 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/configuration.h +@@ -0,0 +1,101 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002, 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Configuration for KCachegrind ++ */ ++ ++#ifndef CONFIGURATION_H ++#define CONFIGURATION_H ++ ++#include <tqcolor.h> ++#include <tqstringlist.h> ++#include <tqdict.h> ++ ++#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 TQColor functionColor(TraceItem::CostType gt, TraceFunction*); ++ static TQColor groupColor(TraceItem*); ++ static TQColor costTypeColor(TraceCostType*); ++ static TQStringList 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 <maxSymbolLength> ++ static TQString shortenSymbol(TQString); ++ 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 { ++ TQString name; ++ TQColor color; ++ bool automatic; ++ }; ++ ++ static ColorSetting* color(TQString, bool createNew = true); ++ ++ TQDict<ColorSetting> _colors; ++ ++ TQStringList _generalSourceDirs; ++ TQDict<TQStringList> _objectSourceDirs; ++ ++ bool _showPercentage, _showExpanded, _showCycles; ++ double _cycleCut; ++ int _percentPrecision; ++ int _maxSymbolLength, _maxSymbolCount, _maxListCount; ++ int _context, _noCostInside; ++ ++ static Configuration* _config; ++}; ++ ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/costlistitem.cpp b/kdecachegrind/kdecachegrind/costlistitem.cpp +new file mode 100644 +index 0000000..1e777b0 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/costlistitem.cpp +@@ -0,0 +1,136 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002, 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++#include <math.h> ++ ++#include <tqpainter.h> ++#include <tqregexp.h> ++ ++#include <klocale.h> ++#include <kiconloader.h> ++#include <kapplication.h> ++ ++#include "listutils.h" ++#include "costlistitem.h" ++#include "coverage.h" ++#include "configuration.h" ++ ++// CostListItem ++ ++ ++CostListItem::CostListItem(TQListView* parent, TraceCostItem* costItem, ++ TraceCostType* ct, int size) ++ :TQListViewItem(parent) ++{ ++ _groupSize = size; ++ _skipped = 0; ++ _costItem = costItem; ++ setCostType(ct); ++ ++ if (costItem) { ++ updateName(); ++ setPixmap(1, colorPixmap(10, 10, ++ Configuration::groupColor(_costItem))); ++ } ++} ++ ++CostListItem::CostListItem(TQListView* parent, int skipped, ++ TraceCostItem* costItem, TraceCostType* ct) ++ :TQListViewItem(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; ++ ++ TQString n = _costItem->prettyName(); ++ if (_groupSize>=0) n += TQString(" (%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, TQString("---")); ++ setPixmap(0, TQPixmap()); ++ return; ++ } ++ ++ _pure = _costItem->subCost(_costType); ++ double pure = 100.0 * _pure / total; ++ TQString str; ++ if (Configuration::showPercentage()) ++ str = TQString("%1").arg(pure, 0, 'f', Configuration::percentPrecision()); ++ else ++ str = _costItem->prettySubCost(_costType); ++ ++ if (_skipped) { ++ // special handling for skip entries... ++ setText(0, TQString("< %1").arg(str)); ++ return; ++ } ++ ++ setText(0, str); ++ setPixmap(0, costPixmap(_costType, _costItem, total, false)); ++} ++ ++int CostListItem::compare(TQListViewItem * 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 TQListViewItem::compare(i, col, ascending); ++} +diff --git a/kdecachegrind/kdecachegrind/costlistitem.h b/kdecachegrind/kdecachegrind/costlistitem.h +new file mode 100644 +index 0000000..99f654e +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/costlistitem.h +@@ -0,0 +1,52 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++#ifndef COSTLISTITEM_H ++#define COSTLISTITEM_H ++ ++#include <tqlistview.h> ++#include "tracedata.h" ++ ++class CostListItem: public TQListViewItem ++{ ++public: ++ CostListItem(TQListView* parent, TraceCostItem* cost, ++ TraceCostType* ct, int size = -1); ++ // entry with multiple skipped items ++ CostListItem(TQListView* parent, int skipped, TraceCostItem* cost, ++ TraceCostType* ct); ++ ++ int compare(TQListViewItem * 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/kdecachegrind/kdecachegrind/costtypeitem.cpp b/kdecachegrind/kdecachegrind/costtypeitem.cpp +new file mode 100644 +index 0000000..dc35cb2 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/costtypeitem.cpp +@@ -0,0 +1,149 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Items of cost type view. ++ */ ++ ++#include <tqpixmap.h> ++#include <klocale.h> ++ ++#include "configuration.h" ++#include "listutils.h" ++#include "costtypeitem.h" ++ ++ ++// CostTypeItem ++ ++ ++CostTypeItem::CostTypeItem(TQListView* parent, TraceCostItem* costItem, ++ TraceCostType* ct, TraceCost::CostType gt) ++ :TQListViewItem(parent) ++{ ++ _costItem = costItem; ++ _costType = ct; ++ _groupType = gt; ++ ++ if (ct) { ++ setText(0, ct->longName()); ++ setText(3, ct->name()); ++ TQString 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, TQPixmap()); ++ setText(2, "-"); ++ setPixmap(2, TQPixmap()); ++ 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, TQString("%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, TQPixmap()); ++ return; ++ } ++ ++ _sum = f->inclusive()->subCost(_costType); ++ double sum = 100.0 * _sum / total; ++ if (Configuration::showPercentage()) { ++ setText(1, TQString("%1") ++ .arg(sum, 0, 'f', Configuration::percentPrecision())); ++ } ++ else ++ setText(1, _sum.pretty()); ++ ++ setPixmap(1, costPixmap(_costType, f->inclusive(), total, false)); ++} ++ ++ ++int CostTypeItem::compare(TQListViewItem * 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 TQListViewItem::compare(i, col, ascending); ++} ++ ++ +diff --git a/kdecachegrind/kdecachegrind/costtypeitem.h b/kdecachegrind/kdecachegrind/costtypeitem.h +new file mode 100644 +index 0000000..d34973d +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/costtypeitem.h +@@ -0,0 +1,50 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Items of cost type view. ++ */ ++ ++#ifndef COSTTYEPITEM_H ++#define COSTTYEPITEM_H ++ ++#include <tqlistview.h> ++#include "tracedata.h" ++ ++ ++class CostTypeItem: public TQListViewItem ++{ ++public: ++ CostTypeItem(TQListView* parent, TraceCostItem* costItem, ++ TraceCostType* ct, TraceCost::CostType gt); ++ ++ int compare(TQListViewItem * 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/kdecachegrind/kdecachegrind/costtypeview.cpp b/kdecachegrind/kdecachegrind/costtypeview.cpp +new file mode 100644 +index 0000000..3f5417e +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/costtypeview.cpp +@@ -0,0 +1,310 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Cost Type View ++ */ ++ ++#include <tqwhatsthis.h> ++#include <tqpopupmenu.h> ++#include <klocale.h> ++ ++#include "configuration.h" ++#include "costtypeitem.h" ++#include "costtypeview.h" ++#include "toplevel.h" ++ ++ ++// ++// CostTypeView ++// ++ ++ ++CostTypeView::CostTypeView(TraceItemView* parentView, ++ TQWidget* parent, const char* name) ++ : TQListView(parent, name), TraceItemView(parentView) ++{ ++ addColumn( i18n( "Event Type" ) ); ++ addColumn( i18n( "Incl." ) ); ++ addColumn( i18n( "Self" ) ); ++ addColumn( i18n( "Short" ) ); ++ addColumn( TQString() ); ++ addColumn( i18n( "Formula" ) ); ++ ++ setSorting(-1); ++ setAllColumnsShowFocus(true); ++ setColumnAlignment(1, TQt::AlignRight); ++ setColumnAlignment(2, TQt::AlignRight); ++ setColumnAlignment(3, TQt::AlignRight); ++ setMinimumHeight(50); ++ ++ connect( this, ++ TQT_SIGNAL( selectionChanged(TQListViewItem*) ), ++ TQT_SLOT( selectedSlot(TQListViewItem*) ) ); ++ ++ connect( this, ++ TQT_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint &, int)), ++ TQT_SLOT(context(TQListViewItem*, const TQPoint &, int))); ++ ++ connect(this, ++ TQT_SIGNAL(doubleClicked(TQListViewItem*)), ++ TQT_SLOT(activatedSlot(TQListViewItem*))); ++ ++ connect(this, ++ TQT_SIGNAL(returnPressed(TQListViewItem*)), ++ TQT_SLOT(activatedSlot(TQListViewItem*))); ++ ++ connect(this, ++ TQT_SIGNAL(itemRenamed(TQListViewItem*,int,const TQString&)), ++ TQT_SLOT(renamedSlot(TQListViewItem*,int,const TQString&))); ++ ++ TQWhatsThis::add( this, whatsThis() ); ++} ++ ++TQString CostTypeView::whatsThis() const ++{ ++ return i18n( "<b>Cost Types List</b>" ++ "<p>This list shows all cost types available " ++ "and what the self/inclusive cost of the " ++ "current selected function is for that cost type.</p>" ++ "<p>By choosing a cost type from the list, " ++ "you change the cost type of costs shown " ++ "all over KCachegrind to be the selected one.</p>"); ++} ++ ++ ++void CostTypeView::context(TQListViewItem* i, const TQPoint & p, int) ++{ ++ TQPopupMenu 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;i<m->realCount();i++) { ++ ct = m->realType(i); ++ if (ct) prev = ct; ++ } ++ for (int i=0;i<m->virtualCount();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 ++ TQString shortName = i18n("New%1").arg(i); ++ TQString 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(TQListViewItem * i) ++{ ++ TraceCostType* ct = i ? ((CostTypeItem*) i)->costType() : 0; ++ if (ct) ++ selectedCostType(ct); ++} ++ ++void CostTypeView::activatedSlot(TQListViewItem * 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) { ++ TQListViewItem *item; ++ for (item = firstChild();item;item = item->nextSibling()) ++ ((CostTypeItem*)item)->setGroupType(_groupType); ++ ++ return; ++ } ++ ++ if (changeType == costTypeChanged) { ++ TQListViewItem *item; ++ for (item = firstChild();item;item = item->nextSibling()) ++ if ( ((CostTypeItem*)item)->costType() == _costType) { ++ setSelected(item, true); ++ ensureItemVisible(item); ++ break; ++ } ++ ++ return; ++ } ++ ++ if (changeType == partsChanged) { ++ TQListViewItem *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 ; ++ TQListViewItem* item = 0; ++ TQString sumStr, pureStr; ++ TQListViewItem* 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(TQListViewItem* item,int c,const TQString& 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; i<knownCount; i++) { ++ known = TraceCostType::knownType(i); ++ if (known->name() == 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/kdecachegrind/kdecachegrind/costtypeview.h b/kdecachegrind/kdecachegrind/costtypeview.h +new file mode 100644 +index 0000000..ee9963e +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/costtypeview.h +@@ -0,0 +1,54 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Cost Type View ++ */ ++ ++#ifndef COSTTYPEVIEW_H ++#define COSTTYPEVIEW_H ++ ++#include <tqlistview.h> ++#include "tracedata.h" ++#include "traceitemview.h" ++ ++class CostTypeView: public TQListView, public TraceItemView ++{ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ CostTypeView(TraceItemView* parentView, ++ TQWidget* parent=0, const char* name=0); ++ ++ virtual TQWidget* widget() { return this; } ++ TQString whatsThis() const; ++ ++private slots: ++ void context(TQListViewItem*,const TQPoint &, int); ++ void selectedSlot(TQListViewItem*); ++ void activatedSlot(TQListViewItem*); ++ void renamedSlot(TQListViewItem*,int,const TQString&); ++ ++private: ++ TraceItem* canShow(TraceItem*); ++ void doUpdate(int); ++ void refresh(); ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/coverage.cpp b/kdecachegrind/kdecachegrind/coverage.cpp +new file mode 100644 +index 0000000..86e6f7f +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/coverage.cpp +@@ -0,0 +1,329 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * 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;i<maxHistogramDepth;i++) { ++ _selfHisto[i] = 0.0; ++ _inclHisto[i] = 0.0; ++ } ++ ++ _valid = true; ++} ++ ++int Coverage::inclusiveMedian() ++{ ++ double maxP = _inclHisto[0]; ++ int medD = 0; ++ for (int i = 1;i<maxHistogramDepth;i++) ++ if (_inclHisto[i]>maxP) { ++ maxP = _inclHisto[i]; ++ medD = i; ++ } ++ ++ return medD; ++} ++ ++int Coverage::selfMedian() ++{ ++ double maxP = _selfHisto[0]; ++ int medD = 0; ++ for (int i = 1;i<maxHistogramDepth;i++) ++ if (_selfHisto[i]>maxP) { ++ 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 (d<maxHistogramDepth) { ++ _inclHisto[d] += pBack; ++ } ++ else { ++ _inclHisto[maxHistogramDepth-1] += pBack; ++ } ++ ++#ifdef DEBUG_COVERAGE ++ qDebug("CallerCov: D %d, %s (now active, new incl %f): newP %f", ++ d, _function->prettyName().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 (d<maxHistogramDepth) { ++ _inclHisto[d] += pForward; ++ _selfHisto[d] += self; ++ } ++ else { ++ _inclHisto[maxHistogramDepth-1] += pForward; ++ _selfHisto[maxHistogramDepth-1] += self; ++ } ++ ++#ifdef DEBUG_COVERAGE ++ qDebug("CngCov:%s < %s (incl %f, self %f)", ++ spaces+strlen(spaces)-d, ++ _function->prettyName().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/kdecachegrind/kdecachegrind/coverage.h b/kdecachegrind/kdecachegrind/coverage.h +new file mode 100644 +index 0000000..50c5936 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/coverage.h +@@ -0,0 +1,102 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * 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/kdecachegrind/kdecachegrind/coverageitem.cpp b/kdecachegrind/kdecachegrind/coverageitem.cpp +new file mode 100644 +index 0000000..26e5b36 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/coverageitem.cpp +@@ -0,0 +1,343 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Items of coverage view. ++ */ ++ ++#include <tqpixmap.h> ++#include <klocale.h> ++ ++#include "configuration.h" ++#include "listutils.h" ++#include "coverage.h" ++#include "coverageitem.h" ++ ++ ++// CallerCoverageItem ++ ++ ++CallerCoverageItem::CallerCoverageItem(TQListView* parent, Coverage* c, ++ TraceFunction* base, ++ TraceCostType* ct, ++ TraceCost::CostType gt) ++ : TQListViewItem(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(TQListView* parent, int skipped, Coverage* c, ++ TraceFunction* base, ++ TraceCostType* ct, ++ TraceCost::CostType gt) ++ : TQListViewItem(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; ++ ++ TQColor 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, TQString()); ++ setText(1, TQString()); ++ return; ++ } ++ ++ _pSum = 100.0 * _coverage->inclusive(); ++ SubCost realSum = _base->inclusive()->subCost(_costType); ++ _sum = SubCost(realSum * _coverage->inclusive()); ++ TQString str; ++ if (Configuration::showPercentage()) ++ str = TQString("%1").arg(_pSum, 0, 'f', Configuration::percentPrecision()); ++ else ++ str = _sum.pretty(); ++ ++ if (_skipped) { ++ setText(0, TQString("< %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() : TQString("(0)")); ++ ++ // distance (min/max/median) ++ _distance = _coverage->inclusiveMedian(); ++ TQString distString; ++ if (_coverage->minDistance() == _coverage->maxDistance()) ++ distString = TQString::number(_distance); ++ else ++ distString = TQString("%1-%2 (%3)") ++ .arg(_coverage->minDistance()) ++ .arg(_coverage->maxDistance()) ++ .arg(_distance); ++ setText(1, distString); ++} ++ ++ ++int CallerCoverageItem::compare(TQListViewItem * 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 TQListViewItem::compare(i, col, ascending); ++} ++ ++ ++// CalleeCoverageItem ++ ++ ++CalleeCoverageItem::CalleeCoverageItem(TQListView* parent, Coverage* c, ++ TraceFunction* base, ++ TraceCostType* ct, ++ TraceCost::CostType gt) ++ : TQListViewItem(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(TQListView* parent, int skipped, Coverage* c, ++ TraceFunction* base, ++ TraceCostType* ct, ++ TraceCost::CostType gt) ++ : TQListViewItem(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; ++ ++ TQColor 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, TQString()); ++ setText(1, TQString()); ++ setText(2, TQString()); ++ 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()); ++ ++ ++ TQString str; ++ if (Configuration::showPercentage()) ++ str = TQString("%1").arg(_pSum, 0, 'f', Configuration::percentPrecision()); ++ else ++ str = _sum.pretty(); ++ ++ if (_skipped) { ++ str = TQString("< %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, TQString("%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() : TQString("(0)")); ++ ++ // for comparations ++ _distance = _coverage->inclusiveMedian(); ++ TQString distString; ++ if (_coverage->minDistance() == _coverage->maxDistance()) ++ distString = TQString::number(_distance); ++ else { ++ int sMed = _coverage->selfMedian(); ++ TQString med; ++ if (_distance == sMed) ++ med = TQString::number(_distance); ++ else ++ med = TQString("%1/%2").arg(_distance).arg(sMed); ++ ++ distString = TQString("%1-%2 (%3)") ++ .arg(_coverage->minDistance()) ++ .arg(_coverage->maxDistance()) ++ .arg(med); ++ } ++ setText(2, distString); ++} ++ ++ ++int CalleeCoverageItem::compare(TQListViewItem * 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 TQListViewItem::compare(i, col, ascending); ++} ++ ++ +diff --git a/kdecachegrind/kdecachegrind/coverageitem.h b/kdecachegrind/kdecachegrind/coverageitem.h +new file mode 100644 +index 0000000..ba442aa +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/coverageitem.h +@@ -0,0 +1,82 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Items of coverage view. ++ */ ++ ++#ifndef COVERAGEITEM_H ++#define COVERAGEITEM_H ++ ++#include <tqlistview.h> ++#include "tracedata.h" ++ ++class Coverage; ++ ++class CallerCoverageItem: public TQListViewItem ++{ ++public: ++ CallerCoverageItem(TQListView* parent, Coverage* c, TraceFunction* base, ++ TraceCostType* ct, TraceCost::CostType gt); ++ CallerCoverageItem(TQListView* parent, int skipped, Coverage* c, TraceFunction* base, ++ TraceCostType* ct, TraceCost::CostType gt); ++ ++ int compare(TQListViewItem * 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 TQListViewItem ++{ ++public: ++ CalleeCoverageItem(TQListView* parent, Coverage* c, TraceFunction* base, ++ TraceCostType* ct, TraceCost::CostType gt); ++ CalleeCoverageItem(TQListView* parent, int skipped, Coverage* c, TraceFunction* base, ++ TraceCostType* ct, TraceCost::CostType gt); ++ ++ int compare(TQListViewItem * 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/kdecachegrind/kdecachegrind/coverageview.cpp b/kdecachegrind/kdecachegrind/coverageview.cpp +new file mode 100644 +index 0000000..6657e92 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/coverageview.cpp +@@ -0,0 +1,321 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Coverage Views ++ */ ++ ++#include <tqwhatsthis.h> ++#include <tqpopupmenu.h> ++#include <klocale.h> ++ ++#include "configuration.h" ++#include "coverageitem.h" ++#include "coverage.h" ++#include "coverageview.h" ++ ++ ++ ++// ++// CoverageView ++// ++ ++ ++CoverageView::CoverageView(bool showCallers, TraceItemView* parentView, ++ TQWidget* parent, const char* name) ++ : TQListView(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, TQt::AlignRight); ++ } ++ ++ setSorting(0,false); ++ setColumnAlignment(0, TQt::AlignRight); ++ setColumnAlignment(1, TQt::AlignRight); ++ setColumnAlignment(2, TQt::AlignRight); ++ setAllColumnsShowFocus(true); ++ setResizeMode(TQListView::LastColumn); ++ setMinimumHeight(50); ++ ++ connect( this, ++ TQT_SIGNAL( selectionChanged(TQListViewItem*) ), ++ TQT_SLOT( selectedSlot(TQListViewItem*) ) ); ++ ++ connect( this, ++ TQT_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint &, int)), ++ TQT_SLOT(context(TQListViewItem*, const TQPoint &, int))); ++ ++ connect(this, ++ TQT_SIGNAL(doubleClicked(TQListViewItem*)), ++ TQT_SLOT(activatedSlot(TQListViewItem*))); ++ ++ connect(this, ++ TQT_SIGNAL(returnPressed(TQListViewItem*)), ++ TQT_SLOT(activatedSlot(TQListViewItem*))); ++ ++ TQWhatsThis::add( this, whatsThis() ); ++} ++ ++TQString CoverageView::whatsThis() const ++{ ++ return _showCallers ? ++ i18n( "<b>List of all Callers</b>" ++ "<p>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 <em>Distance</em> (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).</p>" ++ ++ "<p>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.</p>" ++ ++ "<p>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.</p>" ++ ++ "<p>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.</p>") : ++ ++ i18n( "<b>List of all Callees</b>" ++ "<p>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 <em>Distance</em> (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).</p>" ++ ++ "<p>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.</p>" ++ ++ "<p>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.</p>" ++ ++ "<p>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.</p>"); ++} ++ ++void CoverageView::context(TQListViewItem* i, const TQPoint & p, int c) ++{ ++ TQPopupMenu popup; ++ ++ TraceFunction* f = 0; ++ if (i) { ++ f = _showCallers ? ++ ((CallerCoverageItem*)i)->function() : ++ ((CalleeCoverageItem*)i)->function(); ++ } ++ ++ if (f) { ++ TQString 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(TQListViewItem * i) ++{ ++ TraceFunction* f = 0; ++ if (i) { ++ f = _showCallers ? ++ ((CallerCoverageItem*)i)->function() : ++ ((CalleeCoverageItem*)i)->function(); ++ } ++ ++ if (f) { ++ _selectedItem = f; ++ selected(f); ++ } ++} ++ ++void CoverageView::activatedSlot(TQListViewItem * 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; ++ TQListViewItem* i = TQListView::selectedItem(); ++ if (i) { ++ f = _showCallers ? ++ ((CallerCoverageItem*)i)->function() : ++ ((CalleeCoverageItem*)i)->function(); ++ } ++ if (f == _selectedItem) return; ++ ++ TQListViewItem *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) { ++ TQListViewItem *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/kdecachegrind/kdecachegrind/coverageview.h b/kdecachegrind/kdecachegrind/coverageview.h +new file mode 100644 +index 0000000..09c5de0 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/coverageview.h +@@ -0,0 +1,57 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Coverage Views ++ */ ++ ++#ifndef COVERAGEVIEW_H ++#define COVERAGEVIEW_H ++ ++#include <tqlistview.h> ++#include "tracedata.h" ++#include "traceitemview.h" ++#include "listutils.h" ++ ++class CoverageView: public TQListView, public TraceItemView ++{ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ CoverageView(bool showCallers, TraceItemView* parentView, ++ TQWidget* parent=0, const char* name=0); ++ ++ virtual TQWidget* widget() { return this; } ++ TQString whatsThis() const; ++ ++private slots: ++ void context(TQListViewItem*,const TQPoint &, int); ++ void selectedSlot(TQListViewItem*); ++ void activatedSlot(TQListViewItem*); ++ ++private: ++ TraceItem* canShow(TraceItem*); ++ void doUpdate(int); ++ void refresh(); ++ ++ HighestCostList _hc; ++ bool _showCallers; ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/dumpmanager.cpp b/kdecachegrind/kdecachegrind/dumpmanager.cpp +new file mode 100644 +index 0000000..2f0891a +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/dumpmanager.cpp +@@ -0,0 +1,50 @@ ++/** ++ * DumpManager ++ * Part of KCachegrind ++ * 2003, Josef Weidendorfer (GPL V2) ++ */ ++ ++#include "dumpmanager.h" ++ ++ ++// ++// Dump ++// ++ ++Dump::Dump(TQString 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/kdecachegrind/kdecachegrind/dumpmanager.h b/kdecachegrind/kdecachegrind/dumpmanager.h +new file mode 100644 +index 0000000..4925819 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/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 <tqstring.h> ++#include <tqptrlist.h> ++ ++class Dump; ++class TraceData; ++ ++typedef TQPtrList<Dump> DumpList; ++ ++ ++/** ++ * A loadable profile Dump ++ */ ++class Dump ++{ ++public: ++ Dump(TQString); ++ ++ TQString filename() { return _filename; } ++ ++private: ++ TQString _filename; ++}; ++ ++ ++/* ++ * TODO: ++ * - Everything ++ * ++ */ ++ ++class DumpManager ++{ ++public: ++ DumpManager(); ++ ++ DumpManager* self(); ++ ++ DumpList loadableDumps(); ++ TraceData* load(Dump*); ++ ++private: ++ static DumpManager* _self; ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/dumpselection.cpp b/kdecachegrind/kdecachegrind/dumpselection.cpp +new file mode 100644 +index 0000000..4d812ef +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/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, ++ TQWidget* parent, const char* name) ++ : DumpSelectionBase(parent, name), TraceItemView(0, top) ++{ ++} ++ ++DumpSelection::~DumpSelection() ++{} ++ ++ ++#include "dumpselection.moc" ++ +diff --git a/kdecachegrind/kdecachegrind/dumpselection.h b/kdecachegrind/kdecachegrind/dumpselection.h +new file mode 100644 +index 0000000..49ca532 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/dumpselection.h +@@ -0,0 +1,30 @@ ++/** ++ * 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 ++ TQ_OBJECT ++ ++public: ++ DumpSelection( TopLevel*, TQWidget* parent = 0, const char* name = 0); ++ virtual ~DumpSelection(); ++ ++ TQWidget* widget() { return this; } ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/dumpselectionbase.ui b/kdecachegrind/kdecachegrind/dumpselectionbase.ui +new file mode 100644 +index 0000000..b8ad1b0 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/dumpselectionbase.ui +@@ -0,0 +1,1082 @@ ++<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> ++<class>DumpSelectionBase</class> ++<widget class="TQWidget"> ++ <property name="name"> ++ <cstring>DumpSelectionBase</cstring> ++ </property> ++ <property name="geometry"> ++ <rect> ++ <x>0</x> ++ <y>0</y> ++ <width>349</width> ++ <height>832</height> ++ </rect> ++ </property> ++ <property name="caption"> ++ <string>Profile Dumps</string> ++ </property> ++ <vbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <widget class="TQSplitter"> ++ <property name="name"> ++ <cstring>splitter1</cstring> ++ </property> ++ <property name="orientation"> ++ <enum>Vertical</enum> ++ </property> ++ <widget class="TQListView"> ++ <column> ++ <property name="text"> ++ <string>Target</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string>Time</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string>Path</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <property name="name"> ++ <cstring>listView1</cstring> ++ </property> ++ </widget> ++ <widget class="TQTabWidget"> ++ <property name="name"> ++ <cstring>tabWidget2</cstring> ++ </property> ++ <widget class="TQWidget"> ++ <property name="name"> ++ <cstring>tab</cstring> ++ </property> ++ <attribute name="title"> ++ <string>Options</string> ++ </attribute> ++ <vbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <widget class="TQLabel"> ++ <property name="name"> ++ <cstring>textLabel1</cstring> ++ </property> ++ <property name="sizePolicy"> ++ <sizepolicy> ++ <hsizetype>5</hsizetype> ++ <vsizetype>5</vsizetype> ++ <horstretch>1</horstretch> ++ <verstretch>0</verstretch> ++ </sizepolicy> ++ </property> ++ <property name="text"> ++ <string>Target command:</string> ++ </property> ++ </widget> ++ <widget class="TQLineEdit"> ++ <property name="name"> ++ <cstring>lineEdit1</cstring> ++ </property> ++ </widget> ++ <widget class="TQLabel"> ++ <property name="name"> ++ <cstring>textLabel2</cstring> ++ </property> ++ <property name="text"> ++ <string>Profiler options:</string> ++ </property> ++ </widget> ++ <widget class="TQListView"> ++ <column> ++ <property name="text"> ++ <string>Option</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string>Value</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <item> ++ <property name="text"> ++ <string>Trace</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <item> ++ <property name="text"> ++ <string>Jumps</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Instructions</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Events</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <item> ++ <property name="text"> ++ <string>Full Cache</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Custom</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Collect</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <item> ++ <property name="text"> ++ <string>At Startup</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ <item> ++ <property name="text"> ++ <string>While In</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Skip</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <item> ++ <property name="text"> ++ <string>PLT</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Function</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Dump Profile</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <item> ++ <property name="text"> ++ <string>Every BBs</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ <item> ++ <property name="text"> ++ <string>On Entering</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ <item> ++ <property name="text"> ++ <string>On Leaving</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Zero Events</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <item> ++ <property name="text"> ++ <string>On Entering</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Separate</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <item> ++ <property name="text"> ++ <string>Threads</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Recursions</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Call Chain</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ </item> ++ <property name="name"> ++ <cstring>listView3</cstring> ++ </property> ++ </widget> ++ <widget class="TQLabel"> ++ <property name="name"> ++ <cstring>textLabel1_2</cstring> ++ </property> ++ <property name="sizePolicy"> ++ <sizepolicy> ++ <hsizetype>5</hsizetype> ++ <vsizetype>5</vsizetype> ++ <horstretch>1</horstretch> ++ <verstretch>0</verstretch> ++ </sizepolicy> ++ </property> ++ <property name="text"> ++ <string>Custom profiler options:</string> ++ </property> ++ </widget> ++ <widget class="TQLineEdit"> ++ <property name="name"> ++ <cstring>lineEdit1_2</cstring> ++ </property> ++ </widget> ++ <widget class="TQLayoutWidget"> ++ <property name="name"> ++ <cstring>layout3</cstring> ++ </property> ++ <hbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <spacer> ++ <property name="name"> ++ <cstring>spacer1</cstring> ++ </property> ++ <property name="orientation"> ++ <enum>Horizontal</enum> ++ </property> ++ <property name="sizeType"> ++ <enum>Expanding</enum> ++ </property> ++ <property name="sizeHint"> ++ <size> ++ <width>21</width> ++ <height>20</height> ++ </size> ++ </property> ++ </spacer> ++ <widget class="TQPushButton"> ++ <property name="name"> ++ <cstring>pushButton2</cstring> ++ </property> ++ <property name="text"> ++ <string>Run New Profile</string> ++ </property> ++ </widget> ++ </hbox> ++ </widget> ++ </vbox> ++ </widget> ++ <widget class="TQWidget"> ++ <property name="name"> ++ <cstring>tab</cstring> ++ </property> ++ <attribute name="title"> ++ <string>Info</string> ++ </attribute> ++ <vbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <widget class="TQLabel"> ++ <property name="name"> ++ <cstring>textLabel8</cstring> ++ </property> ++ <property name="text"> ++ <string>Dump reason:</string> ++ </property> ++ </widget> ++ <widget class="TQLineEdit"> ++ <property name="name"> ++ <cstring>lineEdit3</cstring> ++ </property> ++ </widget> ++ <widget class="TQLabel"> ++ <property name="name"> ++ <cstring>textLabel6</cstring> ++ </property> ++ <property name="text"> ++ <string>Event summary:</string> ++ </property> ++ </widget> ++ <widget class="TQListView"> ++ <column> ++ <property name="text"> ++ <string>Name</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string>Sum</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <property name="name"> ++ <cstring>listView4</cstring> ++ </property> ++ </widget> ++ <widget class="TQLabel"> ++ <property name="name"> ++ <cstring>textLabel7</cstring> ++ </property> ++ <property name="text"> ++ <string>Miscellaneous:</string> ++ </property> ++ </widget> ++ <widget class="TQTextEdit"> ++ <property name="name"> ++ <cstring>textEdit2</cstring> ++ </property> ++ </widget> ++ <widget class="TQLayoutWidget"> ++ <property name="name"> ++ <cstring>layout7</cstring> ++ </property> ++ <hbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <spacer> ++ <property name="name"> ++ <cstring>spacer3</cstring> ++ </property> ++ <property name="orientation"> ++ <enum>Horizontal</enum> ++ </property> ++ <property name="sizeType"> ++ <enum>Expanding</enum> ++ </property> ++ <property name="sizeHint"> ++ <size> ++ <width>50</width> ++ <height>20</height> ++ </size> ++ </property> ++ </spacer> ++ <widget class="TQPushButton"> ++ <property name="name"> ++ <cstring>pushButton6</cstring> ++ </property> ++ <property name="text"> ++ <string>Show</string> ++ </property> ++ </widget> ++ <widget class="TQPushButton"> ++ <property name="name"> ++ <cstring>pushButton5</cstring> ++ </property> ++ <property name="text"> ++ <string>Compare</string> ++ </property> ++ </widget> ++ </hbox> ++ </widget> ++ </vbox> ++ </widget> ++ <widget class="TQWidget"> ++ <property name="name"> ++ <cstring>tab</cstring> ++ </property> ++ <attribute name="title"> ++ <string>State</string> ++ </attribute> ++ <vbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <widget class="TQLayoutWidget"> ++ <property name="name"> ++ <cstring>layout2</cstring> ++ </property> ++ <hbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <widget class="TQPushButton"> ++ <property name="name"> ++ <cstring>pushButton1</cstring> ++ </property> ++ <property name="text"> ++ <string>Update</string> ++ </property> ++ </widget> ++ <widget class="TQCheckBox"> ++ <property name="name"> ++ <cstring>checkBox1</cstring> ++ </property> ++ <property name="text"> ++ <string>Every [s]:</string> ++ </property> ++ </widget> ++ <widget class="TQLineEdit"> ++ <property name="name"> ++ <cstring>lineEdit3_2</cstring> ++ </property> ++ </widget> ++ </hbox> ++ </widget> ++ <widget class="TQListView"> ++ <column> ++ <property name="text"> ++ <string>Counter</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string>Value</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <item> ++ <property name="text"> ++ <string>Dumps Done</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Is Collecting</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Executed</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <item> ++ <property name="text"> ++ <string>Basic Blocks</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Calls</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Jumps</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Events</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <item> ++ <property name="text"> ++ <string>Ir</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Distinct</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <item> ++ <property name="text"> ++ <string>ELF Objects</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Functions</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ <item> ++ <property name="text"> ++ <string>Contexts</string> ++ </property> ++ <property name="text"> ++ <string></string> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ <property name="pixmap"> ++ <pixmap></pixmap> ++ </property> ++ </item> ++ </item> ++ <property name="name"> ++ <cstring>listView4_3</cstring> ++ </property> ++ </widget> ++ <widget class="TQLayoutWidget"> ++ <property name="name"> ++ <cstring>layout4</cstring> ++ </property> ++ <hbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <widget class="TQLabel"> ++ <property name="name"> ++ <cstring>textLabel4</cstring> ++ </property> ++ <property name="sizePolicy"> ++ <sizepolicy> ++ <hsizetype>5</hsizetype> ++ <vsizetype>5</vsizetype> ++ <horstretch>1</horstretch> ++ <verstretch>0</verstretch> ++ </sizepolicy> ++ </property> ++ <property name="text"> ++ <string>Stack trace:</string> ++ </property> ++ </widget> ++ <widget class="TQCheckBox"> ++ <property name="name"> ++ <cstring>checkBox2</cstring> ++ </property> ++ <property name="text"> ++ <string>Sync.</string> ++ </property> ++ </widget> ++ </hbox> ++ </widget> ++ <widget class="TQListView"> ++ <column> ++ <property name="text"> ++ <string>#</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string>Incl.</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string>Called</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string>Function</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string>Location</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <property name="name"> ++ <cstring>listView7</cstring> ++ </property> ++ </widget> ++ <widget class="TQLayoutWidget"> ++ <property name="name"> ++ <cstring>layout6</cstring> ++ </property> ++ <hbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <widget class="TQPushButton"> ++ <property name="name"> ++ <cstring>pushButton7</cstring> ++ </property> ++ <property name="text"> ++ <string>Start</string> ++ </property> ++ </widget> ++ <spacer> ++ <property name="name"> ++ <cstring>spacer2</cstring> ++ </property> ++ <property name="orientation"> ++ <enum>Horizontal</enum> ++ </property> ++ <property name="sizeType"> ++ <enum>Expanding</enum> ++ </property> ++ <property name="sizeHint"> ++ <size> ++ <width>20</width> ++ <height>20</height> ++ </size> ++ </property> ++ </spacer> ++ <widget class="TQPushButton"> ++ <property name="name"> ++ <cstring>pushButton6_2</cstring> ++ </property> ++ <property name="text"> ++ <string>Zero</string> ++ </property> ++ </widget> ++ <widget class="TQPushButton"> ++ <property name="name"> ++ <cstring>pushButton4</cstring> ++ </property> ++ <property name="text"> ++ <string>Dump</string> ++ </property> ++ </widget> ++ </hbox> ++ </widget> ++ </vbox> ++ </widget> ++ <widget class="TQWidget"> ++ <property name="name"> ++ <cstring>tab</cstring> ++ </property> ++ <attribute name="title"> ++ <string>Messages</string> ++ </attribute> ++ <vbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <widget class="TQTextEdit"> ++ <property name="name"> ++ <cstring>textEdit2_2</cstring> ++ </property> ++ </widget> ++ <widget class="TQLayoutWidget"> ++ <property name="name"> ++ <cstring>layout6</cstring> ++ </property> ++ <hbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <widget class="TQPushButton"> ++ <property name="name"> ++ <cstring>pushButton9</cstring> ++ </property> ++ <property name="text"> ++ <string>Kill Run</string> ++ </property> ++ </widget> ++ <spacer> ++ <property name="name"> ++ <cstring>spacer4</cstring> ++ </property> ++ <property name="orientation"> ++ <enum>Horizontal</enum> ++ </property> ++ <property name="sizeType"> ++ <enum>Expanding</enum> ++ </property> ++ <property name="sizeHint"> ++ <size> ++ <width>21</width> ++ <height>20</height> ++ </size> ++ </property> ++ </spacer> ++ <widget class="TQPushButton"> ++ <property name="name"> ++ <cstring>pushButton8</cstring> ++ </property> ++ <property name="text"> ++ <string>Clear</string> ++ </property> ++ </widget> ++ </hbox> ++ </widget> ++ </vbox> ++ </widget> ++ </widget> ++ </widget> ++ </vbox> ++</widget> ++<layoutdefaults spacing="6" margin="11"/> ++</UI> +diff --git a/kdecachegrind/kdecachegrind/fixcost.cpp b/kdecachegrind/kdecachegrind/fixcost.cpp +new file mode 100644 +index 0000000..4102926 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/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(i<maxCount) { ++ if (!s.stripUInt64(_cost[i])) break; ++ i++; ++ } ++ _count = i; ++ ++ if (!pool->allocateReserved(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(i<maxCount) { ++ if (!s.stripUInt64(_cost[i])) break; ++ i++; ++ } ++ _count = i; ++ ++ if (!pool->allocateReserved(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/kdecachegrind/kdecachegrind/fixcost.h b/kdecachegrind/kdecachegrind/fixcost.h +new file mode 100644 +index 0000000..7e90fb4 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/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/kdecachegrind/kdecachegrind/functionitem.cpp b/kdecachegrind/kdecachegrind/functionitem.cpp +new file mode 100644 +index 0000000..3b694dd +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/functionitem.cpp +@@ -0,0 +1,236 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * List Item for the FunctionSelection list ++ */ ++ ++ ++//#include <math.h> ++ ++//#include <tqpainter.h> ++//#include <tqregexp.h> ++ ++#include <klocale.h> ++#include <kiconloader.h> ++#include <kapplication.h> ++ ++#include "listutils.h" ++#include "functionitem.h" ++#include "configuration.h" ++ ++ ++// FunctionItem ++ ++FunctionItem::FunctionItem(TQListView* parent, TraceFunction* f, ++ TraceCostType* ct, TraceCost::CostType gt) ++ :TQListViewItem(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(TQListView* parent, int skipped, ++ TraceFunction* f, TraceCostType* ct) ++ :TQListViewItem(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 TQPixmap* FunctionItem::pixmap(int column) const ++{ ++ if (column == 3) { ++ if (!_groupPixValid) { ++ TQColor 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 ++ TQColor 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); ++ TQString 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 = TQString("%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 = TQString("-"); ++ else ++ str = TQString("(0)"); ++ } ++ setText(2, str); ++ ++ // Incl. cost ++ _sum = _function->inclusive()->subCost(_costType); ++ if (inclTotal == 0.0) { ++ setPixmap(0, TQPixmap()); ++ setText(0, "-"); ++ } ++ else { ++ double incl = 100.0 * _sum / inclTotal; ++ if (Configuration::showPercentage()) ++ setText(0, TQString("%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, TQPixmap()); ++ setText(1, "-"); ++ } ++ else { ++ double self = 100.0 * _pure / selfTotal; ++ ++ if (Configuration::showPercentage()) ++ setText(1, TQString("%1") ++ .arg(self, 0, 'f', Configuration::percentPrecision())); ++ else ++ setText(1, _function->prettySubCost(_costType)); ++ ++ setPixmap(1, costPixmap(_costType, _function, selfTotal, false)); ++ } ++} ++ ++ ++int FunctionItem::compare(TQListViewItem * 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 TQListViewItem::compare(i, col, ascending); ++} ++ +diff --git a/kdecachegrind/kdecachegrind/functionitem.h b/kdecachegrind/kdecachegrind/functionitem.h +new file mode 100644 +index 0000000..d8f98f4 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/functionitem.h +@@ -0,0 +1,58 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * List Item for the FunctionSelection list ++ */ ++ ++#ifndef FUNCTIONITEM_H ++#define FUNCTIONITEM_H ++ ++#include <tqlistview.h> ++#include "tracedata.h" ++ ++class FunctionItem: public TQListViewItem ++{ ++public: ++ FunctionItem(TQListView* parent, TraceFunction* function, ++ TraceCostType* ct, TraceCost::CostType gt); ++ // constructor for a "Skipped ... " entry ++ FunctionItem(TQListView* parent, int skipped, ++ TraceFunction* function, TraceCostType* ct); ++ ++ int compare(TQListViewItem * 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 TQPixmap* pixmap (int column) const; ++ bool _costPixValid, _groupPixValid; ++ TQPixMap _costPix, _groupPix; ++#endif ++ ++private: ++ SubCost _sum, _pure; ++ TraceCostType* _costType; ++ TraceCost::CostType _groupType; ++ TraceFunction* _function; ++ int _skipped; ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/functionselection.cpp b/kdecachegrind/kdecachegrind/functionselection.cpp +new file mode 100644 +index 0000000..c5646dd +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/functionselection.cpp +@@ -0,0 +1,871 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002, 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * For function selection, to be put into a TQDockWindow ++ */ ++ ++#include <tqtimer.h> ++#include <tqlistview.h> ++#include <tqlabel.h> ++#include <tqpushbutton.h> ++#include <tqcombobox.h> ++#include <tqlineedit.h> ++#include <tqregexp.h> ++#include <tqpopupmenu.h> ++ ++#include <klocale.h> ++ ++#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, ++ TQWidget* parent, const char* name) ++ : FunctionSelectionBase(parent, name), TraceItemView(0, top) ++{ ++ _group = 0; ++ _inSetGroup = false; ++ _inSetFunction = false; ++ ++ TQStringList 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, TQT_SIGNAL(activated(int)), ++ top, TQT_SLOT(groupTypeSelected(int))); ++ ++ // search while typing... ++ connect(searchEdit, TQT_SIGNAL(textChanged(const TQString&)), ++ this, TQT_SLOT(searchChanged(const TQString&))); ++ connect(&_searchTimer, TQT_SIGNAL(timeout()), ++ this, TQT_SLOT(queryDelayed())); ++ // select first matching group/function on return ++ connect(searchEdit, TQT_SIGNAL(returnPressed()), ++ this, TQT_SLOT(searchReturnPressed())); ++ searchEdit->setMinimumWidth(50); ++ ++ // we start with desending cost sorting ++ functionList->setSorting(0,false); ++ functionList->setColumnAlignment(0, TQt::AlignRight); ++ functionList->setColumnAlignment(1, TQt::AlignRight); ++ functionList->setColumnAlignment(2, TQt::AlignRight); ++ functionList->setAllColumnsShowFocus(true); ++ // functionList->setShowSortIndicator(true); ++ // we can have very long function and location names ++ functionList->setColumnWidthMode(3, TQListView::Manual); ++ functionList->setColumnWidth(3, 200); ++ functionList->setColumnWidthMode(4, TQListView::Manual); ++ functionList->setColumnWidth(4, 200); ++ ++ groupList->setSorting(0,false); ++ groupList->setColumnAlignment(0, TQt::AlignRight); ++ groupList->setAllColumnsShowFocus(true); ++ // groupList->setShowSortIndicator(true); ++ groupList->setResizeMode(TQListView::LastColumn); ++ ++#if 0 ++ // single click press activation ++ connect(functionList, TQT_SIGNAL(selectionChanged(TQListViewItem*)), ++ this, TQT_SLOT(functionActivated(TQListViewItem*))); ++ connect(functionList, ++ TQT_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint &, int)), ++ this, TQT_SLOT(functionContext(TQListViewItem*, const TQPoint &, int))); ++#else ++ // single click release activation ++ connect(functionList, TQT_SIGNAL(selectionChanged(TQListViewItem*)), ++ this, TQT_SLOT(functionSelected(TQListViewItem*))); ++ connect(functionList, TQT_SIGNAL(clicked(TQListViewItem*)), ++ this, TQT_SLOT(functionActivated(TQListViewItem*))); ++ connect(functionList, TQT_SIGNAL(returnPressed(TQListViewItem*)), ++ this, TQT_SLOT(functionActivated(TQListViewItem*))); ++ connect(functionList, ++ TQT_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint &, int)), ++ this, TQT_SLOT(functionContext(TQListViewItem*, const TQPoint &, int))); ++#endif ++ ++ connect(groupList, TQT_SIGNAL(selectionChanged(TQListViewItem*)), ++ this, TQT_SLOT(groupSelected(TQListViewItem*))); ++ connect(groupList, TQT_SIGNAL(doubleClicked(TQListViewItem*)), ++ this, TQT_SLOT(groupDoubleClicked(TQListViewItem*))); ++ connect(groupList, TQT_SIGNAL(returnPressed(TQListViewItem*)), ++ this, TQT_SLOT(groupDoubleClicked(TQListViewItem*))); ++ connect(groupList, ++ TQT_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint &, int)), ++ this, TQT_SLOT(groupContext(TQListViewItem*, const TQPoint &, int))); ++ ++ // start hidden ++ groupList->hide(); ++} ++ ++FunctionSelection::~FunctionSelection() ++{ ++} ++ ++void FunctionSelection::searchReturnPressed() ++{ ++ query(searchEdit->text()); ++ ++ TQListViewItem* 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 TQString& 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(TQListViewItem* i, ++ const TQPoint & p, int c) ++{ ++ TQPopupMenu 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(TQListViewItem* /*i*/, ++ const TQPoint & p, int c) ++{ ++ TQPopupMenu 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(TQPopupMenu* popup) ++{ ++ TQPopupMenu *popup1 = new TQPopupMenu(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,TQT_SIGNAL(activated(int)), ++ _topLevel,TQT_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 ++ TQListViewItem* i = functionList->currentItem(); ++ if (i && (((FunctionItem*)i)->function() == f)) { ++ functionList->setSelected(i,true); ++ return; ++ } ++ ++ // reset searchEdit (as not activated from this view) ++ _searchString = TQString(); ++ query(TQString()); ++ ++ // 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; ++ } ++ ++ TQListViewItem* 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 = TQString(); ++ query(TQString()); ++ ++ 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; ++ ++ TQListViewItem* 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; ++ TQListViewItem *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: ++ { ++ TQListViewItem* 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 TQListView 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(TQListViewItem* 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 TQListView 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 ++ ++ TQRegExp 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(TQListViewItem* 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(TQString s) ++{ ++ TQListViewItem *item; ++ item = groupList->firstChild(); ++ for(;item;item = item->nextSibling()) ++ if (((CostListItem*)item)->costItem()->name() == s) ++ return ((CostListItem*)item)->costItem(); ++ ++ return 0; ++} ++ ++ ++ ++void FunctionSelection::functionSelected(TQListViewItem* 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(TQListViewItem* i) ++{ ++ if (!i) return; ++ if (!_data) return; ++ TraceFunction* f = ((FunctionItem*) i)->function(); ++ ++ if (!f) return; ++ ++ if (!_inSetFunction) ++ activated(f); ++} ++ ++void FunctionSelection::updateGroupSizes(bool hideEmpty) ++{ ++ TQListViewItem* 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(TQString 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; ++ ++ TQRegExp 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() ++{ ++ TQListViewItem* 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, TQListView::Maximum); ++ functionList->setColumnWidth(0,50); ++ functionList->setColumnWidthMode(2, TQListView::Maximum); ++ functionList->setColumnWidth(2,50); ++ } ++ else { ++ functionList->setColumnWidthMode(0, TQListView::Manual); ++ functionList->setColumnWidth(0,0); ++ functionList->setColumnWidthMode(2, TQListView::Manual); ++ functionList->setColumnWidth(2,0); ++ } ++ ++ functionList->setColumnWidth(1, 50); ++} ++ ++ ++ ++#include "functionselection.moc" +diff --git a/kdecachegrind/kdecachegrind/functionselection.h b/kdecachegrind/kdecachegrind/functionselection.h +new file mode 100644 +index 0000000..c5f7810 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/functionselection.h +@@ -0,0 +1,86 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002, 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * For function selection, to be put into a TQDockWindow ++ */ ++ ++#ifndef FUNCTIONSELECTION_H ++#define FUNCTIONSELECTION_H ++ ++#include "functionselectionbase.h" ++#include "traceitemview.h" ++#include "tracedata.h" ++#include "listutils.h" ++ ++class TQPopupMenu; ++ ++class TraceFunction; ++class TraceData; ++class StackBrowser; ++class NestedAreaItem; ++ ++class FunctionSelection : public FunctionSelectionBase, public TraceItemView ++{ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ FunctionSelection( TopLevel*, TQWidget* parent = 0, const char* name = 0); ++ ~FunctionSelection(); ++ ++ TraceCostItem* group(TQString); ++ void setGroup(TraceCostItem*); ++ void query(TQString); ++ bool setTopFunction(); ++ ++ TQWidget* widget() { return this; } ++ ++ void addGroupMenu(TQPopupMenu*); ++ ++public slots: ++ void searchReturnPressed(); ++ void searchChanged(const TQString&); ++ void queryDelayed(); ++ void groupDoubleClicked( TQListViewItem* ); ++ void functionActivated( TQListViewItem* ); ++ void groupSelected( TQListViewItem* ); ++ void functionSelected( TQListViewItem* ); ++ void functionContext(TQListViewItem*, const TQPoint &, int); ++ void groupContext(TQListViewItem*, const TQPoint &, int); ++ ++private: ++ TraceItem* canShow(TraceItem* i); ++ void doUpdate(int); ++ void selectFunction(); ++ void refresh(); ++ void setCostColumnWidths(); ++ void updateGroupSizes(bool hideEmpty); ++ ++ TraceCostItem* _group; ++ ++ TQString _searchString, _searchDelayed; ++ TQTimer _searchTimer; ++ TQMap<TraceCostItem*,int> _groupSize; ++ ++ HighestCostList _hc; ++ // when setting a ++ bool _inSetGroup, _inSetFunction; ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/functionselectionbase.ui b/kdecachegrind/kdecachegrind/functionselectionbase.ui +new file mode 100644 +index 0000000..eec019d +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/functionselectionbase.ui +@@ -0,0 +1,163 @@ ++<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> ++<class>FunctionSelectionBase</class> ++<widget class="TQWidget"> ++ <property name="name"> ++ <cstring>FunctionSelectionBase</cstring> ++ </property> ++ <property name="geometry"> ++ <rect> ++ <x>0</x> ++ <y>0</y> ++ <width>223</width> ++ <height>485</height> ++ </rect> ++ </property> ++ <property name="caption"> ++ <string>Function Profile</string> ++ </property> ++ <vbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <property name="margin"> ++ <number>3</number> ++ </property> ++ <property name="spacing"> ++ <number>6</number> ++ </property> ++ <widget class="TQLayoutWidget"> ++ <property name="name"> ++ <cstring>layout1</cstring> ++ </property> ++ <hbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <widget class="TQLabel"> ++ <property name="name"> ++ <cstring>searchLabel</cstring> ++ </property> ++ <property name="text"> ++ <string>&Search:</string> ++ </property> ++ <property name="buddy" stdset="0"> ++ <cstring>searchEdit</cstring> ++ </property> ++ </widget> ++ <widget class="TQLineEdit"> ++ <property name="name"> ++ <cstring>searchEdit</cstring> ++ </property> ++ </widget> ++ <widget class="TQComboBox"> ++ <property name="name"> ++ <cstring>groupBox</cstring> ++ </property> ++ </widget> ++ </hbox> ++ </widget> ++ <widget class="TQListView"> ++ <column> ++ <property name="text"> ++ <string>Self</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string>Group</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <property name="name"> ++ <cstring>groupList</cstring> ++ </property> ++ <property name="sizePolicy"> ++ <sizepolicy> ++ <hsizetype>7</hsizetype> ++ <vsizetype>5</vsizetype> ++ <horstretch>0</horstretch> ++ <verstretch>0</verstretch> ++ </sizepolicy> ++ </property> ++ <property name="maximumSize"> ++ <size> ++ <width>32767</width> ++ <height>150</height> ++ </size> ++ </property> ++ </widget> ++ <widget class="TQListView"> ++ <column> ++ <property name="text"> ++ <string>Incl.</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string>Self</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string>Called</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string>Function</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string>Location</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <property name="name"> ++ <cstring>functionList</cstring> ++ </property> ++ </widget> ++ </vbox> ++</widget> ++<layoutdefaults spacing="6" margin="11"/> ++</UI> +diff --git a/kdecachegrind/kdecachegrind/hi32-app-kcachegrind.png b/kdecachegrind/kdecachegrind/hi32-app-kcachegrind.png +new file mode 100644 +index 0000000..bd41dae +Binary files /dev/null and b/kdecachegrind/kdecachegrind/hi32-app-kcachegrind.png differ +diff --git a/kdecachegrind/kdecachegrind/hi48-app-kcachegrind.png b/kdecachegrind/kdecachegrind/hi48-app-kcachegrind.png +new file mode 100644 +index 0000000..58c2efd +Binary files /dev/null and b/kdecachegrind/kdecachegrind/hi48-app-kcachegrind.png differ +diff --git a/kdecachegrind/kdecachegrind/instritem.cpp b/kdecachegrind/kdecachegrind/instritem.cpp +new file mode 100644 +index 0000000..ce5e81b +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/instritem.cpp +@@ -0,0 +1,469 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Items of instruction view. ++ */ ++ ++#include <tqpixmap.h> ++#include <tqpainter.h> ++ ++#include <klocale.h> ++#include <kapplication.h> ++#include <kiconloader.h> ++#include <kdebug.h> ++ ++#include "configuration.h" ++#include "listutils.h" ++#include "instritem.h" ++#include "instrview.h" ++ ++ ++// InstrItem ++ ++// for messages ++InstrItem::InstrItem(InstrView* iv, TQListView* parent, ++ Addr addr, const TQString& msg) ++ : TQListViewItem(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, TQListView* parent, ++ Addr addr, bool inside, ++ const TQString& code, const TQString& cmd, ++ const TQString& args, TraceInstr* instr) ++ : TQListViewItem(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, TQListViewItem* parent, Addr addr, ++ TraceInstr* instr, TraceInstrCall* instrCall) ++ : TQListViewItem(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(); ++ TQString templ = " "; ++ if (cc==0) ++ templ += i18n("Active call to '%1'"); ++ else ++ templ += i18n("%n call to '%1'", "%n calls to '%1'", cc); ++ ++ TQString 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, TQListViewItem* parent, Addr addr, ++ TraceInstr* instr, TraceInstrJump* instrJump) ++ : TQListViewItem(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()); ++ ++ TQString 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(); ++ TQColor 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))) { ++ TQString str; ++ TQPixmap p; ++ ++ TQString 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, TQString()); ++ setPixmap(1, TQPixmap()); ++ } ++ else { ++ double total = totalCost->subCost(ct); ++ double pure = 100.0 * _pure / total; ++ ++ if (Configuration::showPercentage()) ++ setText(1, TQString("%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, TQString()); ++ setPixmap(2, TQPixmap()); ++ } ++ else { ++ double total = totalCost->subCost(ct2); ++ double pure = 100.0 * _pure2 / total; ++ ++ if (Configuration::showPercentage()) ++ setText(2, TQString("%1") ++ .arg(pure, 0, 'f', Configuration::percentPrecision())); ++ else ++ setText(2, _pure2.pretty()); ++ ++ setPixmap(2, costPixmap(ct2, instrCost, total, false)); ++ } ++} ++ ++ ++int InstrItem::compare(TQListViewItem * 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 TQListViewItem::compare(i, col, ascending); ++} ++ ++void InstrItem::paintCell( TQPainter *p, const TQColorGroup &cg, ++ int column, int width, int alignment ) ++{ ++ TQColorGroup _cg( cg ); ++ ++ if ( !_inside || ((column==1) || column==2)) ++ _cg.setColor( TQColorGroup::Base, cg.button() ); ++ else if ((_instrCall || _instrJump) && column>2) ++ _cg.setColor( TQColorGroup::Base, cg.midlight() ); ++ ++ if (column == 3) ++ paintArrows(p, _cg, width); ++ else ++ TQListViewItem::paintCell( p, _cg, column, width, alignment ); ++} ++ ++void InstrItem::setJumpArray(const TQMemArray<TraceInstrJump*>& a) ++{ ++ _jump.duplicate(a); ++} ++ ++void InstrItem::paintArrows(TQPainter *p, const TQColorGroup &cg, int width) ++{ ++ TQListView *lv = listView(); ++ if ( !lv ) return; ++ InstrView* iv = (InstrView*) lv; ++ ++ const BackgroundMode bgmode = lv->viewport()->backgroundMode(); ++ const TQColorGroup::ColorRole crole ++ = TQPalette::backgroundRoleFromMode( bgmode ); ++ if ( cg.brush( crole ) != lv->colorGroup().brush( crole ) ) ++ p->fillRect( 0, 0, width, height(), cg.brush( crole ) ); ++ else ++ iv->paintEmptyArea( p, TQRect( 0, 0, width, height() ) ); ++ ++ if ( isSelected() && lv->allColumnsShowFocus() ) ++ p->fillRect( 0, 0, width, height(), cg.brush( TQColorGroup::Highlight ) ); ++ ++ int marg = lv->itemMargin(); ++ int yy = height()/2, y1, y2; ++ TQColor 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( TQColorGroup::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; ++ ++ TQPointArray 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 TQFontMetrics& fm, ++ const TQListView* lv, int c ) const ++{ ++ if (c != 3) return TQListViewItem::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/kdecachegrind/kdecachegrind/instritem.h b/kdecachegrind/kdecachegrind/instritem.h +new file mode 100644 +index 0000000..2bbce71 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/instritem.h +@@ -0,0 +1,86 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Items of instruction view. ++ */ ++ ++#ifndef INSTRITEM_H ++#define INSTRITEM_H ++ ++#include <tqlistview.h> ++#include "tracedata.h" ++ ++class InstrView; ++ ++class InstrItem: public TQListViewItem ++{ ++ ++public: ++ // for messages ++ InstrItem(InstrView* iv, TQListView* parent, ++ Addr addr, const TQString&); ++ ++ // for instruction lines ++ InstrItem(InstrView* iv, TQListView* parent, ++ Addr addr, bool inside, ++ const TQString&, const TQString&, const TQString&, ++ TraceInstr* instr); ++ ++ // for call instr ++ InstrItem(InstrView* iv, TQListViewItem* parent, Addr addr, ++ TraceInstr* instr, TraceInstrCall* instrCall); ++ ++ // for jump lines ++ InstrItem(InstrView* iv, TQListViewItem* 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(TQListViewItem * i, int col, bool ascending ) const; ++ ++ void paintCell(TQPainter *p, const TQColorGroup &cg, ++ int column, int width, int alignment ); ++ int width( const TQFontMetrics& fm, ++ const TQListView* lv, int c ) const; ++ ++ void updateGroup(); ++ void updateCost(); ++ ++ // arrow lines ++ void setJumpArray(const TQMemArray<TraceInstrJump*>& a); ++ ++protected: ++ void paintArrows(TQPainter *p, const TQColorGroup &cg, int width); ++ TQMemArray<TraceInstrJump*> _jump; ++ ++private: ++ InstrView* _view; ++ SubCost _pure, _pure2; ++ Addr _addr; ++ TraceInstr* _instr; ++ TraceInstrJump* _instrJump; ++ TraceInstrCall* _instrCall; ++ bool _inside; ++}; ++ ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/instrview.cpp b/kdecachegrind/kdecachegrind/instrview.cpp +new file mode 100644 +index 0000000..3df1679 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/instrview.cpp +@@ -0,0 +1,949 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Instruction View ++ */ ++ ++#include <tqfile.h> ++#include <tqregexp.h> ++#include <tqwhatsthis.h> ++#include <tqpopupmenu.h> ++#include <klocale.h> ++#include <kconfig.h> ++#include <kdebug.h> ++ ++#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: <space>* <hex address> ":" <space>* ++ 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: <space>* <hex address> ":" <space>* ++ ++ 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, ++ TQWidget* parent, const char* name) ++ : TQListView(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, TQt::AlignRight); ++ setColumnAlignment(2, TQt::AlignRight); ++ ++ connect(this, ++ TQT_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint &, int)), ++ TQT_SLOT(context(TQListViewItem*, const TQPoint &, int))); ++ ++ connect(this, TQT_SIGNAL(selectionChanged(TQListViewItem*)), ++ TQT_SLOT(selectedSlot(TQListViewItem*))); ++ ++ connect(this, ++ TQT_SIGNAL(doubleClicked(TQListViewItem*)), ++ TQT_SLOT(activatedSlot(TQListViewItem*))); ++ ++ connect(this, ++ TQT_SIGNAL(returnPressed(TQListViewItem*)), ++ TQT_SLOT(activatedSlot(TQListViewItem*))); ++ ++ TQWhatsThis::add( this, whatsThis()); ++} ++ ++void InstrView::paintEmptyArea( TQPainter * p, const TQRect & r) ++{ ++ TQListView::paintEmptyArea(p, r); ++} ++ ++TQString InstrView::whatsThis() const ++{ ++ return i18n( "<b>Annotated Assembler</b>" ++ "<p>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.</p>" ++ "<p>The disassembler output shown is generated with " ++ "the 'objdump' utility from the 'binutils' package.</p>" ++ "<p>Select a line with call information to " ++ "make the destination function of this call current.</p>"); ++} ++ ++void InstrView::context(TQListViewItem* i, const TQPoint & p, int c) ++{ ++ TQPopupMenu 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) { ++ TQString 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(TQListViewItem * 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(TQListViewItem * 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*)TQListView::selectedItem(); ++ if (ii) { ++ if ((ii->instr() == _selectedItem) || ++ (ii->instr() && (ii->instr()->line() == _selectedItem))) return; ++ } ++ ++ TQListViewItem *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) { ++ TQListViewItem *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, TQListView::Maximum); ++ setColumnWidth(4, _lastHexCodeWidth); ++ } ++ else { ++ setColumnWidthMode(4, TQListView::Manual); ++ setColumnWidth(4, 0); ++ } ++} ++ ++void InstrView::refresh() ++{ ++ _arrowLevels = 0; ++ ++ // reset to automatic sizing to get column width ++ setColumnWidthMode(4, TQListView::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, TQListView::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, TQListView::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 ++ TQString popencmd, objfile; ++ objfile = function->object()->name(); ++ objfile = objfile.replace(TQRegExp("[\"']"), ""); // security... ++ popencmd = TQString("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(TQFile::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; ++ } ++ TQFile 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; ++ TQString 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 ((objAddr<dumpStartAddr) || (objAddr>dumpEndAddr)) ++ 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 = TQString(buf + pos1); ++ cmd = TQString(buf + pos2); ++ args = TQString(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 = TQString(); ++ 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 = TQString(); ++ args = TQString("..."); ++ } ++ 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(); ++ TQListViewItem *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, TQString(" %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; ++ TQListViewItem* item = firstChild(); ++ for (;item;item = item->nextSibling()) { ++ ii = (InstrItem*)item; ++ TraceInstr* instr = ii->instr(); ++ if (!instr) continue; ++ ++ ii->updateCost(); ++ ++ TQListViewItem *next, *i = ii->firstChild(); ++ for (;i;i = next) { ++ next = i->nextSibling(); ++ ((InstrItem*)i)->updateCost(); ++ } ++ } ++} ++ ++void InstrView::readViewConfig(KConfig* c, ++ TQString prefix, TQString 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, ++ TQString prefix, TQString postfix, bool) ++{ ++ KConfigGroup g(c, (prefix+postfix).ascii()); ++ ++ writeConfigEntry(&g, "ShowHexCode", _showHexCode, DEFAULT_SHOWHEXCODE); ++} ++ ++#include "instrview.moc" +diff --git a/kdecachegrind/kdecachegrind/instrview.h b/kdecachegrind/kdecachegrind/instrview.h +new file mode 100644 +index 0000000..79d3d76 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/instrview.h +@@ -0,0 +1,83 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Instruction View ++ */ ++ ++#ifndef INSTRVIEW_H ++#define INSTRVIEW_H ++ ++#include <tqlistview.h> ++#include "traceitemview.h" ++ ++class InstrItem; ++ ++class InstrView : public TQListView, public TraceItemView ++{ ++ friend class InstrItem; ++ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ InstrView(TraceItemView* parentView, ++ TQWidget* parent = 0, const char* name = 0); ++ ++ virtual TQWidget* widget() { return this; } ++ TQString whatsThis() const; ++ ++ void readViewConfig(KConfig*, TQString prefix, TQString postfix, bool); ++ void saveViewConfig(KConfig*, TQString prefix, TQString postfix, bool); ++ ++protected: ++ int arrowLevels() { return _arrowLevels; } ++ void paintEmptyArea( TQPainter *, const TQRect & ); ++ ++private slots: ++ void context(TQListViewItem*, const TQPoint &, int); ++ void selectedSlot(TQListViewItem *); ++ void activatedSlot(TQListViewItem *); ++ ++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... ++ TQMemArray<TraceInstrJump*> _jump; ++ TraceInstrJumpList _lowList, _highList; ++ ++ // remember width of hex code column if hidden ++ int _lastHexCodeWidth; ++ ++ // widget options ++ bool _showHexCode; ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/listutils.cpp b/kdecachegrind/kdecachegrind/listutils.cpp +new file mode 100644 +index 0000000..0053646 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/listutils.cpp +@@ -0,0 +1,266 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Some helper functions for TQListViewItem derivates ++ */ ++ ++#include <tqpainter.h> ++#include "listutils.h" ++ ++#define COSTPIX_WIDTH 25 ++ ++TQPixmap colorPixmap(int w, int h, TQColor c) ++{ ++ static TQPixmap* pixs[37]; ++ static TQColor 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]; ++ } ++ ++ ++ TQPixmap* pix = new TQPixmap(w, h); ++ pix->fill(c); ++ TQPainter 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. ++ */ ++TQPixmap percentagePixmap(int w, int h, int percent, TQColor 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 TQPixmap(); ++ ++ TQPixmap pix(w, h); ++ pix.fill(TQt::white); ++ TQPainter p(&pix); ++ p.setPen(TQt::black); ++ if (framed) ++ p.drawRect(0, 0, w, h); ++ ++ // inside ++ p.setPen(TQt::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 TQColor partitionColor(int d, int max) ++{ ++ return TQColor( (720*d/max) % 360, ++ 255-(128*d/max), 192, TQColor::Hsv); ++} ++ ++ ++TQPixmap partitionPixmap(int w, int h, ++ double* hist, TQColor* 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;d<maxIndex;d++) ++ if (hist[d]>0.0) { ++ sum += hist[d]; ++ if (dmin>d) dmin = d; ++ if (dmax<d) dmax = d; ++ } ++ ++ // inner rectangle to fill with bar ++ int iw, ix1, ix2, ih, iy1, iy2; ++ 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; ++ } ++ ++ int filled = (int)(iw*sum+1); ++ if (!framed && (filled < w)) w=filled; ++ if (w<3) return TQPixmap(); ++ ++ TQPixmap pix(w, h); ++ pix.fill(TQt::white); ++ TQPainter p(&pix); ++ p.setPen(TQt::black); ++ if (framed) ++ p.drawRect(0, 0, w, h); ++ ++ //qDebug("Sum %f, dw %d", sum,dw); ++ ++ TQColor c, cLast; ++ bool leftDrawn = false; ++ int x1, x2=0; ++ int lastDiff=0, diff; ++ d=dmin; ++ while (d<dmax+1) { ++ val += hist[d]; ++ nextPos = (int)(filled * val/sum); ++ ++ //qDebug(" hist[%d] %f, val %f, nextPos %d", d, hist[d], val, nextPos); ++ ++ diff = nextPos-lastPos; ++ if (diff==0) { d++; continue; } ++ ++ c = cArray ? cArray[d] : partitionColor(d,maxIndex); ++ ++ x1 = ix1+lastPos; ++ x2 = ix1+nextPos; ++ if (x2>=iw) x2=iw-1; ++ ++ // inside ++ p.setPen(TQt::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; ++} ++ ++ ++TQPixmap costPixmap(TraceCostType* ct, TraceCost* cost, double total, bool framed) ++{ ++ if (ct->isReal()) { ++ TQColor 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]; ++ TQColor* cs = ct->mapping()->realColors(); ++ maxIndex = ct->histCost(cost, total, h); ++ ++ if (maxIndex ==0) return TQPixmap(); ++ 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/kdecachegrind/kdecachegrind/listutils.h b/kdecachegrind/kdecachegrind/listutils.h +new file mode 100644 +index 0000000..e3e13fb +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/listutils.h +@@ -0,0 +1,65 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Some helper functions for TQListViewItem derivates ++ */ ++ ++#ifndef LISTUTILS_H ++#define LISTUTILS_H ++ ++#include <tqpixmap.h> ++#include <tqstring.h> ++#include <tqcolor.h> ++#include "tracedata.h" ++ ++TQString bigNum(SubCost); ++TQPixmap colorPixmap(int w, int h, TQColor c); ++TQPixmap percentagePixmap(int w, int h, int percent, TQColor c, bool framed); ++TQPixmap partitionPixmap(int w, int h, double* hist, TQColor*, ++ int maxIndex, bool framed); ++TQPixmap costPixmap(TraceCostType* ct, TraceCost* cost, double total, bool framed); ++ ++/** ++ * A class to calculate the <maxSize> 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; ++ TQMemArray<TraceCost*> _item; ++ TQMemArray<SubCost> _cost; ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/lo16-app-kcachegrind.png b/kdecachegrind/kdecachegrind/lo16-app-kcachegrind.png +new file mode 100644 +index 0000000..0985586 +Binary files /dev/null and b/kdecachegrind/kdecachegrind/lo16-app-kcachegrind.png differ +diff --git a/kdecachegrind/kdecachegrind/lo32-app-kcachegrind.png b/kdecachegrind/kdecachegrind/lo32-app-kcachegrind.png +new file mode 100644 +index 0000000..12542c8 +Binary files /dev/null and b/kdecachegrind/kdecachegrind/lo32-app-kcachegrind.png differ +diff --git a/kdecachegrind/kdecachegrind/loader.cpp b/kdecachegrind/kdecachegrind/loader.cpp +new file mode 100644 +index 0000000..a4aecf5 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/loader.cpp +@@ -0,0 +1,85 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Base class for loaders of profiling data. ++ */ ++ ++#include "loader.h" ++ ++ ++/// Loader ++ ++LoaderList Loader::_loaderList; ++ ++Loader::Loader(TQString name, TQString desc) ++{ ++ _name = name; ++ _description = desc; ++} ++ ++Loader::~Loader() ++{} ++ ++bool Loader::canLoadTrace(TQFile*) ++{ ++ return false; ++} ++ ++bool Loader::loadTrace(TracePart*) ++{ ++ return false; ++} ++ ++Loader* Loader::matchingLoader(TQFile* file) ++{ ++ Loader* l; ++ for (l=_loaderList.first(); l; l = _loaderList.next()) ++ if (l->canLoadTrace(file)) ++ return l; ++ ++ return 0; ++} ++ ++Loader* Loader::loader(TQString 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/kdecachegrind/kdecachegrind/loader.h b/kdecachegrind/kdecachegrind/loader.h +new file mode 100644 +index 0000000..f79f13d +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/loader.h +@@ -0,0 +1,80 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Base class for loaders of profiling data. ++ */ ++ ++#ifndef LOADER_H ++#define LOADER_H ++ ++#include <tqobject.h> ++#include <tqptrlist.h> ++#include <tqstring.h> ++ ++class TQFile; ++class TraceData; ++class TracePart; ++class Loader; ++ ++ ++typedef TQPtrList<Loader> 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 TQObject ++{ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ Loader(TQString name, TQString desc); ++ virtual ~Loader(); ++ ++ virtual bool canLoadTrace(TQFile* file); ++ virtual bool loadTrace(TracePart*); ++ ++ static Loader* matchingLoader(TQFile* file); ++ static Loader* loader(TQString name); ++ static void initLoaders(); ++ static void deleteLoaders(); ++ ++ TQString name() const { return _name; } ++ TQString description() const { return _description; } ++ ++signals: ++ void updateStatus(TQString, int); ++ ++private: ++ TQString _name, _description; ++ ++ static LoaderList _loaderList; ++}; ++ ++ ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/main.cpp b/kdecachegrind/kdecachegrind/main.cpp +new file mode 100644 +index 0000000..fd9df1b +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/main.cpp +@@ -0,0 +1,95 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * KCachegrind startup ++ */ ++ ++// for KCACHEGRIND_VERSION ++#include "../version.h" ++ ++#include <tqfile.h> ++#include <kapplication.h> ++#include <kcmdlineargs.h> ++#include <kaboutdata.h> ++#include <klocale.h> ++ ++#include "toplevel.h" ++#include "tracedata.h" ++#include "loader.h" ++ ++static KCmdLineOptions options[] = ++{ ++ { "r <exec>", I18N_NOOP("Run <exec> under cachegrind"), 0 }, ++ { "+[trace]", I18N_NOOP("Show information of this trace"), 0 }, ++ KCmdLineLastOption // End of options. ++}; ++ ++int main( int argc, char ** argv ) ++{ ++ KAboutData aboutData("kdecachegrind", ++ I18N_NOOP("KCachegrind"), ++ KCACHEGRIND_VERSION, ++ I18N_NOOP("KDE Frontend for Cachegrind"), ++ KAboutData::License_GPL, ++ I18N_NOOP("(C) 2002, 2003, 2004"), 0, ++ "http://kdecachegrind.sf.net"); ++ aboutData.addAuthor("Josef Weidendorfer", ++ I18N_NOOP("Author/Maintainer"), ++ "[email protected]"); ++ ++ 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(TQFile::decodeName(args->arg(i))); ++ } ++ } ++ else { ++ // load trace in current dir ++ t = new TopLevel(); ++ t->show(); ++ t->loadDelayed("."); ++ } ++ } ++ ++ a.connect( &a, TQT_SIGNAL( lastWindowClosed() ), &a, TQT_SLOT( quit() ) ); ++ int res = a.exec(); ++ ++ // to make leak checking in valgrind happy... ++ Loader::deleteLoaders(); ++ TraceItem::cleanup(); ++ ++ return res; ++} +diff --git a/kdecachegrind/kdecachegrind/multiview.cpp b/kdecachegrind/kdecachegrind/multiview.cpp +new file mode 100644 +index 0000000..4288e2d +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/multiview.cpp +@@ -0,0 +1,224 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * 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 <tqobjectlist.h> ++#include <kconfig.h> ++#include <kdebug.h> ++ ++#include "multiview.h" ++#include "tabview.h" ++ ++// ++// MultiView ++// ++ ++MultiView::MultiView(TopLevel* top, TQWidget* parent, const char* name) ++ : TQSplitter(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, ++ TQString("TabView-%1").arg(n).ascii()); ++ connect(tv, TQT_SIGNAL(activated(TabView*)), ++ this, TQT_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, ++ TQString prefix, TQString postfix, ++ bool withOptions) ++{ ++ if (0) qDebug("%s::readConfig(%s%s)", name(), ++ prefix.ascii(), postfix.ascii()); ++ ++ TQString active; ++ KConfigGroup* g = configGroup(c, prefix, postfix); ++ int n = g->readNumEntry("Panels", 1); ++ setChildCount(n); ++ setOrientation( (g->readEntry("Orientation") == TQString("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, TQString("%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, ++ TQString prefix, TQString 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, TQString("%1-%2").arg(prefix).arg(tv->name()), ++ postfix, withOptions); ++} ++ ++ ++#include "multiview.moc" +diff --git a/kdecachegrind/kdecachegrind/multiview.h b/kdecachegrind/kdecachegrind/multiview.h +new file mode 100644 +index 0000000..9d77101 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/multiview.h +@@ -0,0 +1,67 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * 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 <tqsplitter.h> ++#include <tqptrlist.h> ++#include "traceitemview.h" ++#include "tabview.h" // because of TQPtrList<TabView> ++ ++class MultiView : public TQSplitter, public TraceItemView ++{ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ MultiView(TopLevel* top, TQWidget* parent = 0, const char* name = 0); ++ ++ TQWidget* 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*, TQString prefix, TQString postfix, bool); ++ void saveViewConfig(KConfig*, TQString prefix, TQString postfix, bool); ++ ++public slots: ++ void tabActivated(TabView*); ++ ++ private: ++ void doUpdate(int); ++ ++ TabView* _active; ++ TQPtrList<TabView> _views; ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/partgraph.cpp b/kdecachegrind/kdecachegrind/partgraph.cpp +new file mode 100644 +index 0000000..a20f53d +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/partgraph.cpp +@@ -0,0 +1,534 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * TracePart as Nested Area ++ */ ++ ++#include <klocale.h> ++ ++#include "partgraph.h" ++#include "configuration.h" ++#include "listutils.h" ++ ++ ++// PartAreaWidget ++ ++PartAreaWidget::PartAreaWidget(TQWidget* 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); ++} ++ ++TQColor PartAreaWidget::groupColor(TraceFunction* f) const ++{ ++ if (!f) ++ return colorGroup().button(); ++ ++ return Configuration::functionColor(_groupType, f); ++} ++ ++TQString PartAreaWidget::tipString(TreeMapItem* i) const ++{ ++ TQString tip, itemTip; ++ int count = 0; ++ ++ //qDebug("PartAreaWidget::tipString for '%s'", i->name().ascii()); ++ ++ // first, SubPartItem's ++ while (i && count<Configuration::maxSymbolCount() && i->rtti() == 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; ++} ++ ++TQString BasePartItem::text(int textNo) const ++{ ++ if (textNo == 0) { ++ if (!_data) ++ return i18n("(no trace)"); ++ ++ if (_data->parts().count() == 0) ++ return i18n("(no part)"); ++ } ++ return TQString(); ++} ++ ++ ++TQColor 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; ++} ++ ++TQString PartItem::text(int textNo) const ++{ ++ if (textNo == 0) ++ return _p->prettyName(); ++ ++ if (textNo != 1) ++ return TQString(); ++ ++ 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 TQString("%1 %") ++ .arg(p, 0, 'f', Configuration::percentPrecision()); ++ } ++ return v.pretty(); ++} ++ ++ ++TQPixmap PartItem::pixmap(int i) const ++{ ++ if (i != 1) return TQPixmap(); ++ ++ // 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; ++} ++ ++ ++TQColor PartItem::backColor() const ++{ ++ PartAreaWidget* w = (PartAreaWidget*)widget(); ++ return w->groupColor(0); ++} ++ ++ ++// SubPartItem ++ ++SubPartItem::SubPartItem(TraceCost* c) ++{ ++ _partCostItem = c; ++ _factor=1; ++} ++ ++TQString SubPartItem::text(int textNo) const ++{ ++ if (textNo == 0) { ++ if (!_partCostItem) ++ return i18n("(unknown)"); ++ ++ return _partCostItem->dependant()->prettyName(); ++ } ++ ++ if (textNo != 1) ++ return TQString(); ++ ++ 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 TQString("%1 %") ++ .arg(p, 0, 'f', Configuration::percentPrecision()); ++ } ++ return v.pretty(); ++} ++ ++TQPixmap SubPartItem::pixmap(int i) const ++{ ++ if (i != 1) return TQPixmap(); ++ ++ // 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; ++} ++ ++ ++TQColor 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/kdecachegrind/kdecachegrind/partgraph.h b/kdecachegrind/kdecachegrind/partgraph.h +new file mode 100644 +index 0000000..f28f12e +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/partgraph.h +@@ -0,0 +1,132 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * TracePart Graph ++ */ ++ ++#ifndef PARTGRAPH_H ++#define PARTGRAPH_H ++ ++#include "treemap.h" ++#include "tracedata.h" ++ ++class PartAreaWidget: public TreeMapWidget ++{ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ // Visualisation inside of trace parts ++ enum VisualisationMode { NoVisualisation, Partitioning, Inclusive }; ++ ++ PartAreaWidget(TQWidget* 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; } ++ ++ TQColor groupColor(TraceFunction*) const; ++ TQString 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; ++ TQString text(int) const; ++ int borderWidth() const { return 0; } ++ TreeMapItemList* children(); ++ TQColor 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; } ++ TQString text(int) const; ++ TQPixmap pixmap(int) const; ++ TreeMapItemList* children(); ++ TQColor 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; } ++ TQString text(int) const; ++ TQPixmap pixmap(int) const; ++ TreeMapItemList* children(); ++ TQColor backColor() const; ++ ++private: ++ TraceCost* _partCostItem; ++ unsigned int _factor; ++}; ++ ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/partlistitem.cpp b/kdecachegrind/kdecachegrind/partlistitem.cpp +new file mode 100644 +index 0000000..40c2db3 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/partlistitem.cpp +@@ -0,0 +1,189 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++#include <math.h> ++ ++#include <tqpainter.h> ++#include <tqregexp.h> ++ ++#include <klocale.h> ++#include <kiconloader.h> ++#include <kapplication.h> ++ ++#include "listutils.h" ++#include "partlistitem.h" ++#include "coverage.h" ++#include "configuration.h" ++ ++ ++// PartListItem ++ ++PartListItem::PartListItem(TQListView* parent, TraceCostItem* costItem, ++ TraceCostType* ct, TraceCost::CostType gt, ++ TracePart* part) ++ :TQListViewItem(parent) ++{ ++ _partCostItem = costItem->findDepFromPart(part); ++ _part = part; ++ _groupType = gt; ++ _costType = ct; ++ ++#if 0 ++ TQString partName = TQString::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, TQString("-")); ++ setPixmap(2, TQPixmap()); ++ } ++ else { ++ double pure = 100.0 * _pure / selfTotal; ++ if (Configuration::showPercentage()) { ++ setText(2, TQString("%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, TQString("-")); ++ setPixmap(1, TQPixmap()); ++ } ++ else { ++ double sum = 100.0 * _sum / total; ++ if (Configuration::showPercentage()) { ++ setText(1, TQString("%1") ++ .arg(sum, 0, 'f', Configuration::percentPrecision())); ++ } ++ else ++ setText(1, _sum.pretty()); ++ ++ setPixmap(1, costPixmap(_costType, pf->inclusive(), total, false)); ++ } ++ ++ if (!pf) { ++ setText(3, TQString("-")); ++ _callers = 0; ++ return; ++ } ++ ++ TracePartCall* pc; ++ TracePartCallList pl; ++ SubCost callers, callees; ++ TQString 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(TQListViewItem * 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 TQListViewItem::compare(i, col, ascending); ++} +diff --git a/kdecachegrind/kdecachegrind/partlistitem.h b/kdecachegrind/kdecachegrind/partlistitem.h +new file mode 100644 +index 0000000..0ab99a9 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/partlistitem.h +@@ -0,0 +1,54 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++#ifndef PARTLISTITEM_H ++#define PARTLISTITEM_H ++ ++#include <tqlistview.h> ++#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 TQListViewItem ++{ ++public: ++ PartListItem(TQListView* parent, TraceCostItem* costItem, ++ TraceCostType* ct, TraceCost::CostType gt, TracePart* part); ++ ++ int compare(TQListViewItem * 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/kdecachegrind/kdecachegrind/partselection.cpp b/kdecachegrind/kdecachegrind/partselection.cpp +new file mode 100644 +index 0000000..703dd75 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/partselection.cpp +@@ -0,0 +1,567 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * For part file selection, to be put into a TQDockWindow ++ */ ++ ++#include <tqtimer.h> ++#include <tqlistview.h> ++#include <tqlabel.h> ++#include <tqpushbutton.h> ++#include <tqcombobox.h> ++#include <tqlineedit.h> ++#include <tqpopupmenu.h> ++#include <tqlayout.h> ++ ++#include <klocale.h> ++#include <kconfig.h> ++#include <kdebug.h> ++ ++#include "partselection.h" ++#include "partgraph.h" ++ ++PartSelection::PartSelection( TQWidget* 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, TQT_SIGNAL(selectionChanged()), ++ this, TQT_SLOT(selectionChanged())); ++ connect(partAreaWidget, TQT_SIGNAL(currentChanged(TreeMapItem*, bool)), ++ this, TQT_SLOT(currentChangedSlot(TreeMapItem*, bool))); ++ connect(partAreaWidget, TQT_SIGNAL(doubleClicked(TreeMapItem*)), ++ this, TQT_SLOT(doubleClicked(TreeMapItem*))); ++ connect(partAreaWidget, ++ TQT_SIGNAL(contextMenuRequested(TreeMapItem*,const TQPoint &)), ++ this, ++ TQT_SLOT(contextMenuRequested(TreeMapItem*,const TQPoint &))); ++ ++ _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; ++ ++ TQString str = i->text(0); ++ if (!i->text(1).isEmpty()) ++ str += " (" + i->text(1) + ")"; ++ TQString 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 TQPoint & p) ++{ ++ if (!i) return; ++ ++ TQPopupMenu popup; ++ TQPopupMenu ppopup; ++ TQPopupMenu vpopup; ++ ++ TQString 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; ++ ++ TQString 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) ++{ ++ TQString 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; ++ } ++ ++ TQString 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/kdecachegrind/kdecachegrind/partselection.h b/kdecachegrind/kdecachegrind/partselection.h +new file mode 100644 +index 0000000..b8a195f +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/partselection.h +@@ -0,0 +1,96 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * PartSelection for KCachegrind ++ * For part file selection, to be put into a TQDockWindow ++ */ ++ ++#ifndef PARTSELECTION_H ++#define PARTSELECTION_H ++ ++#include <tqobject.h> ++ ++#include "partselectionbase.h" ++#include "partgraph.h" ++#include "tracedata.h" ++ ++class KConfigGroup; ++class TraceFunction; ++class TraceData; ++class TreeMapItem; ++ ++class PartSelection: public PartSelectionBase ++{ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ PartSelection( TQWidget* 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 TQString&, int); ++ void goBack(); ++ ++public slots: ++ void selectionChanged(); ++ void doubleClicked(TreeMapItem*); ++ void contextMenuRequested(TreeMapItem*, const TQPoint &); ++ 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/kdecachegrind/kdecachegrind/partselectionbase.ui b/kdecachegrind/kdecachegrind/partselectionbase.ui +new file mode 100644 +index 0000000..53320d5 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/partselectionbase.ui +@@ -0,0 +1,89 @@ ++<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> ++<class>PartSelectionBase</class> ++<widget class="TQWidget"> ++ <property name="name"> ++ <cstring>PartSelectionBase</cstring> ++ </property> ++ <property name="geometry"> ++ <rect> ++ <x>0</x> ++ <y>0</y> ++ <width>460</width> ++ <height>402</height> ++ </rect> ++ </property> ++ <property name="caption"> ++ <string>Parts Overview</string> ++ </property> ++ <vbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <property name="margin"> ++ <number>6</number> ++ </property> ++ <property name="spacing"> ++ <number>6</number> ++ </property> ++ <widget class="PartAreaWidget"> ++ <property name="name"> ++ <cstring>partAreaWidget</cstring> ++ </property> ++ <property name="sizePolicy"> ++ <sizepolicy> ++ <hsizetype>5</hsizetype> ++ <vsizetype>7</vsizetype> ++ <horstretch>0</horstretch> ++ <verstretch>0</verstretch> ++ </sizepolicy> ++ </property> ++ <property name="minimumSize"> ++ <size> ++ <width>0</width> ++ <height>50</height> ++ </size> ++ </property> ++ </widget> ++ <widget class="TQLabel"> ++ <property name="name"> ++ <cstring>rangeLabel</cstring> ++ </property> ++ <property name="sizePolicy"> ++ <sizepolicy> ++ <hsizetype>7</hsizetype> ++ <vsizetype>5</vsizetype> ++ <horstretch>0</horstretch> ++ <verstretch>0</verstretch> ++ </sizepolicy> ++ </property> ++ <property name="text"> ++ <string>(no trace parts)</string> ++ </property> ++ </widget> ++ </vbox> ++</widget> ++<customwidgets> ++ <customwidget> ++ <class>PartAreaWidget</class> ++ <header location="local">partgraph.h</header> ++ <sizehint> ++ <width>-1</width> ++ <height>-1</height> ++ </sizehint> ++ <container>0</container> ++ <sizepolicy> ++ <hordata>5</hordata> ++ <verdata>7</verdata> ++ <horstretch>0</horstretch> ++ <verstretch>0</verstretch> ++ </sizepolicy> ++ <pixmap>image0</pixmap> ++ </customwidget> ++</customwidgets> ++<images> ++ <image name="image0"> ++ <data format="XPM.GZ" length="5230">789c9597db4e1d4b0e86eff31428be8b46b5fb54dd551acd051020211c4320c0682eecaa5e9ccf90005bf3ee53cbbfe9d9c9c548a38ea27c2977b5cbfe6dd7fae3c3c2e1cee6c2873fde3d3cf2e3595a48a77cbff0213f5d5dbdfcf35ffff8f3ddfba65998fff161a179ffb777ef771f17d2c2d6cdf53807c705a889cdac6ee72c47736eebd637b5f2b672d70ecd30e744ca7d9b9a4ad7579587363433e527e5d072abefbb03636978ceb4a31c5bc13a91716ad41f89e0aec2fe74abcc6d6ad5f9e4945357375ef75f997357751dd61d19c71afb61bdef421b951f274ebaffb6716cb3f2b2f230f18d3183dd95b1b441d7d59f2e94757ccf1b27eceff69563618d0f5dcfd9d7beeb7a5dd7f3fac6fb4ee32b87c619f1168d976f7d6ff6df26d6ef4b301e1bac7f54f63e747a5e87f5c173276aff605c22a8fefc540e5e3af5972f956359577bf2c6d978cb78d6697edc9eb2f8b143fed7945359d778b0e6c3e7be81bf7465dce23cf4ac3c16567f19fecefaae53bd081b7baccbb131b7d05f679ca03f27c6b9d1f388c6bbaffab7f80dca4d1fed7bfafdbeedb9533d490f1e2ae8d5693ccacb83d77cd027e3d4416faa97beefa3d7f8f086f168f9d9020f43a3f5c5aa877e28eb88f74fe319becfaa873e0c15de775f95639f3dec37c143d361bf2be3cef4b76bdcb7d84ff5d2cbd0227facf1ecd360fa67d54b9f078ffcd10f636e11ff53e3847853ad3c1b668837e9fe43358c2df45a81430b3db2e6676842d368fd48abdc068b87e0fd2e788bf792b21f861e7afd0e0e5ce37dcdd7d0872218e5d789f57b3218a71ae7bb501eca3af67b510e21c05fd27a1b6218ac3ef17d0ed68fe8cb1be3fc6e5159863ca0bfed824340fe687d62c4838c23f677f0370de380fcdd2be7e03dfab1d6cf300bcd80fa80bfb358dbf9547fa18abe413d68fe421d63ade7737aded094cf63fdcc38f4d0bfee17dac2d08380a3a0dff24cb90bdc23ffaa87e0636c912fed87612e38d4df1e982bc4dbdd29872001f14aca319413eb793e83cbfbe8af27606e4c0f5f8d3bcb7763ec6b8da73b57e69083d61f9d82b9b5fada31f635fcaf8d7b7b5ffb41f16e34ff34fe214dfc0c8e1ef1a22330bfcdc345e31e4c33e3c1e6e181b1609e3ac43f8759803e8cd9f2e7e0ff18ab80ef5d8239e2fc0ef129f90ff0bf0773347fee8d13ce279bc6b34af5c61abf58157bf4b765b05435f4abfec726968c2b67e536fa80fe388279067f680d2c750dff75fe9766da07e44bc02521ba4e1d584c1fb261dcc23fd97f63f8c3af136bfc687362c4e76962ed8fac7a2ecdb7853f7c63ec6bf467bd3fc421c680780c60b6f9275fc045aea84fadaf18a2e0fc04ff0237bd9e8fefc0d236e8ef3f8d83c57fdd38daf94edeb886be74fec618b3ed7f3731e66b0fe67a403e76c0454ef8de9231dbfe9f8dc5fc47be38a6887ad2f950964d2fdc1a27d3c7b6710693f6ef9864b4fdb4ffc6cc5540fea0972c339c87afc1c9f4443a5fe2c84d40bd2f82cb3ae6c7b1716dbc37b1e68bf5be118b7e3dead78125d87d0bfa99bd7d8fb78c1bdb4fcfcfd51bcb9d716bebda3fb8966cf7cf57709103f2e327c6fe5a2fdc4ab4fb01deef5283fa65d50ffb54c13fd67ec37d61e845fb070f8571df7c341eec7e7a691c6ae453fb771937d1fa999ebfa43358bf573d3327c6fdd0a9de59b87414e59f60911ef3ff07380d16bf0b63b6fbb8ce0f4e523a80ee8ff8a5f21fe83f384f2e8cf9d419db7d4bb4bff32cb1dd871ae38c79417962dd9f8f2786bfaa3fa992ddbf68d5d8d6797d62dc27b4bf4a9bdeee5be7c619efbb169c2b9bb7aa67e972857a67d59bf8943cf4a6f5237dae3b9c3f4f8cef5713637e6a3c6548e546a7eb87e0b28e7cf7c68ddd2fb15f28acf166bd2f484c19f921f8cbc57de84ff52c397b8f7e178d7bbb7f7e9f18f743d593ccd210d12f9e8c05fd8417c1e5e704e2afefa7aa5cf850ff4fc6a1473debfc4d7561c4ebd3c48897f69fd4e4d8eb7e4efb736a0b23fe9adfd465bb3f388d6ff22933fa1df6f7597ad483c62371f6e6ff8671b6f89e198fb8cfb0ea3f49619c6f6562cc27d5474a7906e6b589d1df549f29e78479450fe0b1c2fc67d5731a0ba33ffc97a167fd7d92ebb1b1f9a87acdcdd8a23ff2e3c4a81fed0fb91bbb88f8e9f772f9b9c6b8bfac187bd41febbcc8c35831f4bb6edc227eeec8b847bc58ef0f398d91a1179d6f391746bdebfd60ac4716b0180bd8a97e473f8ea2dfdb7d9c3f8e1cff8f8760057b27e57f92cb6e743377e24efff29cb97377e12edd55b190c99eddb5bb71b7eeceddbb07f7e89edc0ff7d33dbb17f7ea16dd925b761fdd8a63d8174f52b15e756bee93fbecd6dd17b7e136dd96db763b6ed77d2dd67bee9bdb77078ed49e8b27b7c5fabb3b7447eed855ae768d6bcbd339ef7a37b8e022392a2734fbd1dd119350a24c23cde8844ee98ccee9c2f9c2977445d77443b793fd8ceee89e1ee8919ee807fdb4e7995ee8b5d82fd252b15ffe8bfd097da4155aa535fa34597fa6f5f2f717da28f69bb445dbb433d99fd22e7da53dfa3659efd3017da7c3f2af233aa68aeadfec1b6aa9236fd63d0d14ca15c0719913737b965fedcbf0c83cc29a677cc2a77cc6e77cc1977cc5d7c5fee637fbdbb2db9d5adff343b17ee427fec1737ee69762fffa9bfd222ff17259fdc82bbcca6bfc893ff33a7fe10ddee42ddee69ddfec77f92beff137dee703fece877cc4c7aee58a6b6ee8b8fc10ed7eb13f635f9a4bc5e54719477152ba671977a505c84845c57222a7bfd89fcb999ccb855cca955cbfc5546ee456ee68b14cf67b799047799aec2fdc92fc28999cef567224cff222afb2c85b32e36d599265f9282bb23ad95fba6559934f2593f3e798b74acc8fd5f6b3accb17d9904dd9926db3a7520d1fdd37d9915df95a72599e1291566df7e49beccb817c974339829eb55e56dc7e51ec0d2dcbb1545297a791563af1c5eff2d3beb4faf8562f568f0744745b9e9d5f1f7992d5e412a186ffef7afff7dfdffd077c99ae99</data> ++ </image> ++</images> ++<layoutdefaults spacing="6" margin="11"/> ++</UI> +diff --git a/kdecachegrind/kdecachegrind/partview.cpp b/kdecachegrind/kdecachegrind/partview.cpp +new file mode 100644 +index 0000000..3f344bb +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/partview.cpp +@@ -0,0 +1,235 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Part View ++ */ ++ ++#include <tqwhatsthis.h> ++#include <tqpopupmenu.h> ++#include <tqheader.h> ++#include <klocale.h> ++ ++#include "configuration.h" ++#include "partlistitem.h" ++#include "toplevel.h" ++#include "partview.h" ++ ++ ++ ++// ++// PartView ++// ++ ++ ++PartView::PartView(TraceItemView* parentView, ++ TQWidget* parent, const char* name) ++ : TQListView(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, TQt::AlignRight); ++ setColumnAlignment(2, TQt::AlignRight); ++ setColumnAlignment(3, TQt::AlignRight); ++ setMinimumHeight(50); ++ setSelectionMode(Extended); ++ ++ connect( this, ++ TQT_SIGNAL( selectionChanged() ), ++ TQT_SLOT( selectionChangedSlot() ) ); ++ ++ connect( this, ++ TQT_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint &, int)), ++ TQT_SLOT(context(TQListViewItem*, const TQPoint &, int))); ++ ++ TQWhatsThis::add( this, whatsThis() ); ++} ++ ++TQString PartView::whatsThis() const ++{ ++ return i18n( "<b>Trace Part List</b>" ++ "<p>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 <em>of the part</em> (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.</p>" ++ "<p>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.</p>" ++ "<p>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.</p>" ++ "<p>Note that the list is hidden if only one trace " ++ "part is loaded.</p>"); ++} ++ ++ ++void PartView::context(TQListViewItem* i, const TQPoint & pos, int) ++{ ++ TQPopupMenu 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; ++ TQListViewItem* item = firstChild(); ++ for(;item;item = item->nextSibling()) ++ if (item->isSelected()) ++ l.append( ((PartListItem*)item)->part() ); ++ ++ selected(l); ++} ++ ++ ++TraceItem* PartView::canShow(TraceItem* i) ++{ ++ if (!TraceItemView::data()) return 0; ++ if (TraceItemView::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) { ++ TQListViewItem *item; ++ for (item = firstChild();item;item = item->nextSibling()) ++ ((PartListItem*)item)->setGroupType(_groupType); ++ ++ return; ++ } ++ ++ if (changeType == costTypeChanged) { ++ TQListViewItem *item; ++ for (item = firstChild();item;item = item->nextSibling()) ++ ((PartListItem*)item)->setCostType(_costType); ++ ++ return; ++ } ++ ++ if (changeType == partsChanged) { ++ ++ TracePart* part; ++ ++ TQListViewItem* 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; ++ ++ TQListViewItem* 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/kdecachegrind/kdecachegrind/partview.h b/kdecachegrind/kdecachegrind/partview.h +new file mode 100644 +index 0000000..92761cc +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/partview.h +@@ -0,0 +1,55 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Part View ++ */ ++ ++#ifndef PARTVIEW_H ++#define PARTVIEW_H ++ ++#include <tqlistview.h> ++#include "tracedata.h" ++#include "traceitemview.h" ++ ++class PartView: public TQListView, public TraceItemView ++{ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ PartView(TraceItemView* parentView, ++ TQWidget* parent=0, const char* name=0); ++ ++ virtual TQWidget* widget() { return this; } ++ TQString whatsThis() const; ++ ++ void refresh(); ++ ++private slots: ++ void context(TQListViewItem*,const TQPoint &, int); ++ void selectionChangedSlot(); ++ ++private: ++ TraceItem* canShow(TraceItem*); ++ void doUpdate(int); ++ ++ bool _inSelectionUpdate; ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/pool.cpp b/kdecachegrind/kdecachegrind/pool.cpp +new file mode 100644 +index 0000000..d4a89a7 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/pool.cpp +@@ -0,0 +1,258 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002-2004 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++#include <string.h> ++#include <stdlib.h> ++#include <tqglobal.h> ++#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;i<CHUNK_SIZE;i++) { ++ p.allocate(bufs+i, 10+i%10); ++ if (((i%3)==0) && (i>20)) ++ p.free(bufs+i-20); ++ } ++ ++ for(i=0;i<CHUNK_SIZE;i++) { ++ if ((bufs[i]==0) || ((i%7)==0)) continue; ++ p.free(bufs+i); ++ } ++ ++ for(i=0;i<CHUNK_SIZE;i++) { ++ if (bufs[i]) continue; ++ p.allocate(bufs+i, 10+i%10); ++ } ++} ++*/ +diff --git a/kdecachegrind/kdecachegrind/pool.h b/kdecachegrind/kdecachegrind/pool.h +new file mode 100644 +index 0000000..c9d70c1 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/pool.h +@@ -0,0 +1,107 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002-2004 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++#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 <size> 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 <size> 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 <size> 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/kdecachegrind/kdecachegrind/sourceitem.cpp b/kdecachegrind/kdecachegrind/sourceitem.cpp +new file mode 100644 +index 0000000..305b824 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/sourceitem.cpp +@@ -0,0 +1,444 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Items of source view. ++ */ ++ ++#include <tqpixmap.h> ++#include <tqregexp.h> ++#include <tqpainter.h> ++ ++#include <klocale.h> ++#include <kiconloader.h> ++#include <kapplication.h> ++ ++#include "configuration.h" ++#include "listutils.h" ++#include "sourceview.h" ++#include "sourceitem.h" ++ ++ ++// SourceItem ++ ++// for source lines ++SourceItem::SourceItem(SourceView* sv, TQListView* parent, ++ int fileno, unsigned int lineno, ++ bool inside, const TQString& src, ++ TraceLine* line) ++ : TQListViewItem(parent) ++{ ++ _view = sv; ++ _lineno = lineno; ++ _fileno = fileno; ++ _inside = inside; ++ _line = line; ++ _lineCall = 0; ++ _lineJump = 0; ++ ++ if (src == "...") ++ setText(0, src); ++ else ++ setText(0, TQString::number(lineno)); ++ ++ TQString s = src; ++ setText(4, s.replace( TQRegExp("\t"), " " )); ++ ++ updateGroup(); ++ updateCost(); ++} ++ ++// for call lines ++SourceItem::SourceItem(SourceView* sv, TQListViewItem* parent, ++ int fileno, unsigned int lineno, ++ TraceLine* line, TraceLineCall* lineCall) ++ : TQListViewItem(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(); ++ TQString templ = " "; ++ if (cc==0) ++ templ += i18n("Active call to '%1'"); ++ else ++ templ += i18n("%n call to '%1'", "%n calls to '%1'", cc); ++ ++ TQString 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, TQListViewItem* parent, ++ int fileno, unsigned int lineno, ++ TraceLine* line, TraceLineJump* lineJump) ++ : TQListViewItem(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()); ++ ++ TQString to; ++ if (_lineJump->lineTo()->functionSource() == _line->functionSource()) ++ to = _lineJump->lineTo()->name(); ++ else ++ to = _lineJump->lineTo()->prettyName(); ++ ++ TQString 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(); ++ TQColor 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))) { ++ TQString str; ++ TQPixmap p; ++ ++ TQString 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, TQString()); ++ setPixmap(1, TQPixmap()); ++ } ++ else { ++ double total = totalCost->subCost(ct); ++ double pure = 100.0 * _pure / total; ++ ++ if (Configuration::showPercentage()) ++ setText(1, TQString("%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, TQString()); ++ setPixmap(2, TQPixmap()); ++ } ++ else { ++ double total = totalCost->subCost(ct2); ++ double pure2 = 100.0 * _pure2 / total; ++ ++ if (Configuration::showPercentage()) ++ setText(2, TQString("%1") ++ .arg(pure2, 0, 'f', Configuration::percentPrecision())); ++ else ++ setText(2, _pure2.pretty()); ++ ++ setPixmap(2, costPixmap(ct2, lineCost, total, false)); ++ } ++} ++ ++ ++int SourceItem::compare(TQListViewItem * 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 TQListViewItem::compare(i, col, ascending); ++} ++ ++void SourceItem::paintCell( TQPainter *p, const TQColorGroup &cg, ++ int column, int width, int alignment ) ++{ ++ TQColorGroup _cg( cg ); ++ ++ if ( !_inside || ((column==1) || (column==2))) ++ _cg.setColor( TQColorGroup::Base, cg.button() ); ++ else if ((_lineCall || _lineJump) && column>2) ++ _cg.setColor( TQColorGroup::Base, cg.midlight() ); ++ ++ if (column == 3) ++ paintArrows(p, _cg, width); ++ else ++ TQListViewItem::paintCell( p, _cg, column, width, alignment ); ++} ++ ++void SourceItem::setJumpArray(const TQMemArray<TraceLineJump*>& a) ++{ ++ _jump.duplicate(a); ++} ++ ++void SourceItem::paintArrows(TQPainter *p, const TQColorGroup &cg, int width) ++{ ++ TQListView *lv = listView(); ++ if ( !lv ) return; ++ SourceView* sv = (SourceView*) lv; ++ ++ const BackgroundMode bgmode = lv->viewport()->backgroundMode(); ++ const TQColorGroup::ColorRole crole ++ = TQPalette::backgroundRoleFromMode( bgmode ); ++ if ( cg.brush( crole ) != lv->colorGroup().brush( crole ) ) ++ p->fillRect( 0, 0, width, height(), cg.brush( crole ) ); ++ else ++ sv->paintEmptyArea( p, TQRect( 0, 0, width, height() ) ); ++ ++ if ( isSelected() && lv->allColumnsShowFocus() ) ++ p->fillRect( 0, 0, width, height(), cg.brush( TQColorGroup::Highlight ) ); ++ ++ int marg = lv->itemMargin(); ++ int yy = height()/2, y1, y2; ++ TQColor 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; ++ ++ TQPointArray 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 TQFontMetrics& fm, ++ const TQListView* lv, int c ) const ++{ ++ if (c != 3) return TQListViewItem::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/kdecachegrind/kdecachegrind/sourceitem.h b/kdecachegrind/kdecachegrind/sourceitem.h +new file mode 100644 +index 0000000..925e575 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/sourceitem.h +@@ -0,0 +1,84 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Items of source view. ++ */ ++ ++#ifndef SOURCEITEM_H ++#define SOURCEITEM_H ++ ++#include <tqlistview.h> ++#include "tracedata.h" ++ ++class SourceView; ++ ++class SourceItem: public TQListViewItem ++{ ++public: ++ // for source lines ++ SourceItem(SourceView* sv, TQListView* parent, ++ int fileno, unsigned int lineno, ++ bool inside, const TQString& src, ++ TraceLine* line = 0); ++ ++ // for call lines ++ SourceItem(SourceView* sv, TQListViewItem* parent, ++ int fileno, unsigned int lineno, ++ TraceLine* line, TraceLineCall* lineCall); ++ ++ // for jump lines ++ SourceItem(SourceView* sv, TQListViewItem* 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(TQListViewItem * i, int col, bool ascending ) const; ++ ++ void paintCell( TQPainter *p, const TQColorGroup &cg, ++ int column, int width, int alignment ); ++ int width( const TQFontMetrics& fm, ++ const TQListView* lv, int c ) const; ++ void updateGroup(); ++ void updateCost(); ++ ++ // arrow lines ++ void setJumpArray(const TQMemArray<TraceLineJump*>& a); ++ ++protected: ++ void paintArrows(TQPainter *p, const TQColorGroup &cg, int width); ++ TQMemArray<TraceLineJump*> _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/kdecachegrind/kdecachegrind/sourceview.cpp b/kdecachegrind/kdecachegrind/sourceview.cpp +new file mode 100644 +index 0000000..dde291e +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/sourceview.cpp +@@ -0,0 +1,813 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Source View ++ */ ++ ++#include <tqdir.h> ++#include <tqfile.h> ++#include <tqwhatsthis.h> ++#include <tqpopupmenu.h> ++#include <klocale.h> ++#include <kdebug.h> ++ ++#include "configuration.h" ++#include "sourceitem.h" ++#include "sourceview.h" ++ ++ ++// ++// SourceView ++// ++ ++ ++SourceView::SourceView(TraceItemView* parentView, ++ TQWidget* parent, const char* name) ++ : TQListView(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, TQt::AlignRight); ++ setColumnAlignment(1, TQt::AlignRight); ++ setColumnAlignment(2, TQt::AlignRight); ++ setResizeMode(TQListView::LastColumn); ++ ++ connect(this, ++ TQT_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint &, int)), ++ TQT_SLOT(context(TQListViewItem*, const TQPoint &, int))); ++ ++ connect(this, ++ TQT_SIGNAL(selectionChanged(TQListViewItem*)), ++ TQT_SLOT(selectedSlot(TQListViewItem*))); ++ ++ connect(this, ++ TQT_SIGNAL(doubleClicked(TQListViewItem*)), ++ TQT_SLOT(activatedSlot(TQListViewItem*))); ++ ++ connect(this, ++ TQT_SIGNAL(returnPressed(TQListViewItem*)), ++ TQT_SLOT(activatedSlot(TQListViewItem*))); ++ ++ TQWhatsThis::add( this, whatsThis()); ++} ++ ++void SourceView::paintEmptyArea( TQPainter * p, const TQRect & r) ++{ ++ TQListView::paintEmptyArea(p, r); ++} ++ ++ ++TQString SourceView::whatsThis() const ++{ ++ return i18n( "<b>Annotated Source</b>" ++ "<p>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.</p>" ++ "<p>Select a inserted call information line to " ++ "make the destination function current.</p>"); ++} ++ ++void SourceView::context(TQListViewItem* i, const TQPoint & p, int c) ++{ ++ TQPopupMenu 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) { ++ TQString 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(TQListViewItem * 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(TQListViewItem * 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*)TQListView::selectedItem(); ++ if (si) { ++ if (si->line() == sLine) return; ++ if (si->lineCall() && ++ (si->lineCall()->call()->called() == _selectedItem)) return; ++ } ++ ++ TQListViewItem *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) { ++ TQListViewItem *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, TQListView::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, TQListView::Manual); ++ setColumnWidth(2, 0); ++ } ++} ++ ++ ++// helper for fillSourceList: ++// search recursive for a file, starting from a base dir ++static bool checkFileExistance(TQString& dir, const TQString& name) ++{ ++ // we leave this in... ++ qDebug("Checking %s/%s", dir.ascii(), name.ascii()); ++ ++ if (TQFile::exists(dir + "/" + name)) return true; ++ ++ // check in subdirectories ++ TQDir d(dir); ++ d.setFilter( TQDir::Dirs | TQDir::NoSymLinks ); ++ d.setSorting( TQDir::Unsorted ); ++ TQStringList subdirs = d.entryList(); ++ TQStringList::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;iStart<asize;iStart++) ++ if (_jump[iStart] && ++ (_jump[iStart]->lineTo() == lj->lineTo())) break; ++#else ++ iStart = asize; ++#endif ++ ++ if (iStart == asize) { ++ for(iStart=0;iStart<asize;iStart++) ++ if (_jump[iStart] == 0) break; ++ ++ if (iStart== asize) { ++ asize++; ++ _jump.resize(asize); ++ if (asize > _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, ++ TQString(" '%1'").arg(sf->function()->prettyName())); ++ new SourceItem(this, this, fileno, 3, false, ++ i18n("Thus, no annotated source can be shown.")); ++ return; ++ } ++ } ++ ++ TQString filename = sf->file()->shortName(); ++ TQString dir = sf->file()->directory(); ++ if (!dir.isEmpty()) ++ filename = dir + "/" + filename; ++ ++ if (nextCostLineno>0) { ++ // we have debug info... search for source file ++ if (!TQFile::exists(filename)) { ++ TQStringList list = Configuration::sourceDirs(_data, ++ sf->function()->object()); ++ TQStringList::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, ++ TQString(" '%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, ++ TQString(" '%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, ++ TQString(" '%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; ++ TQFile 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, TQString(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(); ++ TQListViewItem *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, TQListView::Maximum); ++ ++ if (_costType) ++ setColumnText(1, _costType->name()); ++ if (_costType2) ++ setColumnText(2, _costType2->name()); ++ ++ SourceItem* si; ++ TQListViewItem* item = firstChild(); ++ for (;item;item = item->nextSibling()) { ++ si = (SourceItem*)item; ++ TraceLine* l = si->line(); ++ if (!l) continue; ++ ++ si->updateCost(); ++ ++ TQListViewItem *next, *i = si->firstChild(); ++ for (;i;i = next) { ++ next = i->nextSibling(); ++ ((SourceItem*)i)->updateCost(); ++ } ++ } ++ ++ if (!_costType2) { ++ setColumnWidthMode(2, TQListView::Manual); ++ setColumnWidth(2, 0); ++ } ++} ++ ++#include "sourceview.moc" +diff --git a/kdecachegrind/kdecachegrind/sourceview.h b/kdecachegrind/kdecachegrind/sourceview.h +new file mode 100644 +index 0000000..b72fc7a +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/sourceview.h +@@ -0,0 +1,71 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Source View ++ */ ++ ++#ifndef SOURCEVIEW_H ++#define SOURCEVIEW_H ++ ++#include <tqlistview.h> ++#include "traceitemview.h" ++ ++class SourceItem; ++ ++class SourceView : public TQListView, public TraceItemView ++{ ++ friend class SourceItem; ++ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ SourceView(TraceItemView* parentView, ++ TQWidget* parent = 0, const char* name = 0); ++ ++ TQWidget* widget() { return this; } ++ TQString whatsThis() const; ++ ++protected: ++ int arrowLevels() { return _arrowLevels; } ++ void paintEmptyArea( TQPainter *, const TQRect & ); ++ ++private slots: ++ void context(TQListViewItem*, const TQPoint &, int); ++ void selectedSlot(TQListViewItem *); ++ void activatedSlot(TQListViewItem *); ++ ++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... ++ TQMemArray<TraceLineJump*> _jump; ++ TraceLineJumpList _lowList, _highList; ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/stackbrowser.cpp b/kdecachegrind/kdecachegrind/stackbrowser.cpp +new file mode 100644 +index 0000000..78095eb +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/stackbrowser.cpp +@@ -0,0 +1,417 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++#include <tqlistview.h> ++ ++#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 ); ++} ++ ++TQString Stack::toString() ++{ ++ TQString 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/kdecachegrind/kdecachegrind/stackbrowser.h b/kdecachegrind/kdecachegrind/stackbrowser.h +new file mode 100644 +index 0000000..e7d6b80 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/stackbrowser.h +@@ -0,0 +1,109 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++#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); ++ ++ TQString 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/kdecachegrind/kdecachegrind/stackitem.cpp b/kdecachegrind/kdecachegrind/stackitem.cpp +new file mode 100644 +index 0000000..e3763ab +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/stackitem.cpp +@@ -0,0 +1,116 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Items of stack dockable. ++ */ ++ ++#include <tqpixmap.h> ++#include <klocale.h> ++ ++#include "configuration.h" ++#include "listutils.h" ++#include "stackitem.h" ++#include "stackselection.h" ++ ++// StackItem ++ ++StackItem::StackItem(StackSelection* ss, ++ TQListView* parent, TraceFunction* f) ++ :TQListViewItem(parent) ++{ ++ _view = ss; ++ _function = f; ++ _call = 0; ++ ++ updateGroup(); ++ updateCost(); ++ ++ setText(2, TQString("-- ")); ++ setText(3, f->prettyName()); ++} ++ ++StackItem::StackItem(StackSelection* ss, ++ TQListView* parent, TraceCall* call) ++ :TQListViewItem(parent) ++{ ++ _view = ss; ++ _call = call; ++ _function = call->called(); ++ ++ updateGroup(); ++ updateCost(); ++ ++ setText(3, _function->prettyName()); ++} ++ ++ ++void StackItem::updateGroup() ++{ ++ TQColor 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, TQPixmap()); ++ } ++ else { ++ double sum = 100.0 * _sum / total; ++ ++ if (Configuration::showPercentage()) ++ setText(0, TQString("%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, TQPixmap()); ++ } ++ else { ++ double sum = 100.0 * _sum / total; ++ ++ if (Configuration::showPercentage()) ++ setText(1, TQString("%1") ++ .arg(sum, 0, 'f', Configuration::percentPrecision())); ++ else ++ setText(1, _call->prettySubCost(ct2)); ++ ++ setPixmap(1, costPixmap(ct2, _call, total, false)); ++ } ++} +diff --git a/kdecachegrind/kdecachegrind/stackitem.h b/kdecachegrind/kdecachegrind/stackitem.h +new file mode 100644 +index 0000000..250e9f6 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/stackitem.h +@@ -0,0 +1,56 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003, 2004 ++ Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Items of stack dockable. ++ */ ++ ++#ifndef STACKITEM_H ++#define STACKITEM_H ++ ++#include <tqlistview.h> ++#include "tracedata.h" ++ ++class StackSelection; ++ ++ ++// for the stack browser ++ ++class StackItem: public TQListViewItem ++{ ++public: ++ // for top ++ StackItem(StackSelection* ss, TQListView* parent, TraceFunction* f); ++ StackItem(StackSelection* ss, TQListView* 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/kdecachegrind/kdecachegrind/stackselection.cpp b/kdecachegrind/kdecachegrind/stackselection.cpp +new file mode 100644 +index 0000000..5909475 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/stackselection.cpp +@@ -0,0 +1,230 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * StackSelection for KCachegrind ++ * For function selection of a most expected stack, ++ * to be put into a TQDockWindow ++ */ ++ ++#include <tqtimer.h> ++#include <tqlistview.h> ++#include <tqlabel.h> ++#include <tqpushbutton.h> ++#include <tqcombobox.h> ++#include <tqlineedit.h> ++ ++#include <kdebug.h> ++ ++#include "stackbrowser.h" ++#include "stackselection.h" ++#include "stackitem.h" ++ ++StackSelection::StackSelection( TQWidget* 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(TQListView::LastColumn); ++ stackList->setColumnAlignment(0, TQt::AlignRight); ++ stackList->setColumnAlignment(1, TQt::AlignRight); ++ stackList->setColumnAlignment(2, TQt::AlignRight); ++ stackList->setColumnWidth(0, 50); ++ // 2nd cost column hidden at first (_costType2 == 0) ++ stackList->setColumnWidth(1, 0); ++ stackList->setColumnWidth(2, 50); ++ ++ connect(stackList, TQT_SIGNAL(selectionChanged(TQListViewItem*)), ++ this, TQT_SLOT(stackSelected(TQListViewItem*))); ++} ++ ++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, TQListView::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 ++ TQListViewItem* 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, TQListView::Manual); ++ stackList->setColumnWidth(1, 0); ++ } ++} ++ ++void StackSelection::stackSelected(TQListViewItem* 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() ++{ ++ TQListViewItem* 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()); ++ ++ TQListViewItem* 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, TQListView::Maximum); ++ if (_costType2) ++ stackList->setColumnText(1, _costType2->name()); ++ ++ TQListViewItem* item = stackList->firstChild(); ++ for(;item;item = item->nextSibling()) ++ ((StackItem*)item)->updateCost(); ++ ++ if (!_costType2) { ++ stackList->setColumnWidthMode(1, TQListView::Manual); ++ stackList->setColumnWidth(1, 0); ++ } ++} ++ ++void StackSelection::setGroupType(TraceItem::CostType gt) ++{ ++ if (_groupType == gt) return; ++ _groupType = gt; ++ ++ TQListViewItem* item = stackList->firstChild(); ++ for(;item;item = item->nextSibling()) ++ ((StackItem*)item)->updateGroup(); ++} ++ ++#include "stackselection.moc" +diff --git a/kdecachegrind/kdecachegrind/stackselection.h b/kdecachegrind/kdecachegrind/stackselection.h +new file mode 100644 +index 0000000..2bb3a75 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/stackselection.h +@@ -0,0 +1,81 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * StackSelection for KCachegrind ++ * For function selection of a most expected stack, ++ * to be put into a TQDockWindow ++ */ ++ ++#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 ++ TQ_OBJECT ++ ++public: ++ StackSelection( TQWidget* 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( TQListViewItem* ); ++ void browserBack(); ++ void browserForward(); ++ void browserUp(); ++ void browserDown(); ++ void refresh(); ++ void rebuildStackList(); ++ ++private: ++ void selectFunction(); ++ ++ TraceData* _data; ++ StackBrowser* _browser; ++ TQListViewItem* _item; ++ TraceFunction* _function; ++ TraceCostType* _costType; ++ TraceCostType* _costType2; ++ TraceItem::CostType _groupType; ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/stackselectionbase.ui b/kdecachegrind/kdecachegrind/stackselectionbase.ui +new file mode 100644 +index 0000000..c61010f +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/stackselectionbase.ui +@@ -0,0 +1,80 @@ ++<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> ++<class>StackSelectionBase</class> ++<widget class="TQWidget"> ++ <property name="name"> ++ <cstring>StackSelectionBase</cstring> ++ </property> ++ <property name="geometry"> ++ <rect> ++ <x>0</x> ++ <y>0</y> ++ <width>168</width> ++ <height>108</height> ++ </rect> ++ </property> ++ <property name="caption"> ++ <string>Stack Selection</string> ++ </property> ++ <vbox> ++ <property name="name"> ++ <cstring>unnamed</cstring> ++ </property> ++ <property name="margin"> ++ <number>3</number> ++ </property> ++ <property name="spacing"> ++ <number>6</number> ++ </property> ++ <widget class="TQListView"> ++ <column> ++ <property name="text"> ++ <string>Cost</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizeable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string>Cost2</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizeable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string>Calls</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizeable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <column> ++ <property name="text"> ++ <string>Function</string> ++ </property> ++ <property name="clickable"> ++ <bool>true</bool> ++ </property> ++ <property name="resizeable"> ++ <bool>true</bool> ++ </property> ++ </column> ++ <property name="name"> ++ <cstring>stackList</cstring> ++ </property> ++ </widget> ++ </vbox> ++</widget> ++<layoutdefaults spacing="6" margin="11"/> ++</UI> +diff --git a/kdecachegrind/kdecachegrind/subcost.cpp b/kdecachegrind/kdecachegrind/subcost.cpp +new file mode 100644 +index 0000000..7b5034e +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/subcost.cpp +@@ -0,0 +1,62 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2004 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++#include <tqstring.h> ++ ++#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; ++} ++ ++TQString SubCost::pretty() ++{ ++ unsigned long long n = v; ++ ++ if (n==0) return TQString(" 0"); ++ ++ int i = 0; ++ TQString res = ""; ++ ++ while (n) { ++ if ((i>0) && !(i%3)) res = " " + res; ++ i++; ++ res = TQChar('0'+int(n%10)) + res; ++ n /= 10; ++ } ++ res = " " + res; ++ return res; ++} ++ ++ +diff --git a/kdecachegrind/kdecachegrind/subcost.h b/kdecachegrind/kdecachegrind/subcost.h +new file mode 100644 +index 0000000..8169280 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/subcost.h +@@ -0,0 +1,66 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002-2004 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++#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 TQString, ++ * spaced every 3 digits. ++ */ ++ TQString pretty(); ++ ++ uint64 v; ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/tabview.cpp b/kdecachegrind/kdecachegrind/tabview.cpp +new file mode 100644 +index 0000000..0049d1e +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/tabview.cpp +@@ -0,0 +1,890 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Tab View, enclosing detailed views for one trace item in ++ * two tab widgets, separated by a splitter ++ */ ++ ++#include <tqobjectlist.h> ++#include <tqsplitter.h> ++#include <tqtabwidget.h> ++#include <tqlayout.h> ++#include <tqwhatsthis.h> ++#include <tqpopupmenu.h> ++ ++#include <klocale.h> ++#include <kconfig.h> ++ ++#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, TQTabWidget* parent, const char *name) ++ : TQTabBar(parent, name) ++{ ++ _tabWidget = parent; ++ _tabView = v; ++} ++ ++void TabBar::mousePressEvent(TQMouseEvent *e) ++{ ++ if (e->button() == Qt::RightButton) { ++ TQTab *tab = selectTab( e->pos() ); ++ TQWidget* page; ++ page = tab ? _tabWidget->page( indexOf( tab->identifier() ) ) :0; ++ ++ TQPopupMenu 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); ++ } ++ ++ TQTabBar::mousePressEvent( e ); ++} ++ ++ ++// ++// Splitter ++// ++ ++Splitter::Splitter(Qt::Orientation o, TQWidget* parent, const char* name) ++ : TQSplitter(o, parent, name) ++{} ++ ++void Splitter::moveEvent(TQMoveEvent* e) ++{ ++ TQSplitter::moveEvent(e); ++ ++ if (0) qDebug("Splitter %s: Move", name()); ++ checkVisiblity(); ++} ++ ++void Splitter::checkVisiblity() ++{ ++ const TQObjectList l = childrenListObject(); ++ TQObjectListIt it( l ); ++ TQObject *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, TQWidget* parent, ++ const char* name, WFlags f) ++ : TQTabWidget(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(TQResizeEvent *e) ++{ ++ TQTabWidget::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(TQShowEvent* e) ++{ ++ TQTabWidget::showEvent(e); ++ ++ if (0) qDebug("TabWidget %s: Show", name()); ++ checkVisibility(); ++} ++ ++void TabWidget::hideEvent(TQHideEvent* e) ++{ ++ TQTabWidget::hideEvent(e); ++ ++ if (0) qDebug("TabWidget %s: Hide", name()); ++ checkVisibility(); ++} ++ ++void TabWidget::moveEvent(TQMoveEvent* e) ++{ ++ TQTabWidget::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, ++ TQWidget* parent, const char* name) ++ : TQWidget(parent, name), TraceItemView(parentView) ++{ ++ setFocusPolicy(TQ_StrongFocus); ++ ++ _isCollapsed = true; ++ ++ TQVBoxLayout* vbox = new TQVBoxLayout( this, 6, 6); ++ ++ _nameLabel = new KSqueezedTextLabel( this, "nameLabel" ); ++ _nameLabel->setText(i18n("(No profile data file loaded)")); ++ vbox->addWidget( _nameLabel ); ++ ++ _mainSplitter = new TQSplitter(Qt::Horizontal, this); ++ _leftSplitter = new Splitter(Qt::Vertical, _mainSplitter, "Left"); ++ vbox->addWidget( _mainSplitter ); ++ ++ _rightTW = new TabWidget(this, _mainSplitter, "Right"); ++ connect(_rightTW, TQT_SIGNAL(currentChanged(TQWidget*)), ++ this, TQT_SLOT(tabChanged(TQWidget*))); ++ connect(_rightTW, TQT_SIGNAL(visibleRectChanged(TabWidget*)), ++ this, TQT_SLOT(visibleRectChangedSlot(TabWidget*))); ++ ++ _topTW = new TabWidget(this, _leftSplitter, "Top"); ++ connect(_topTW, TQT_SIGNAL(currentChanged(TQWidget*)), ++ this, TQT_SLOT(tabChanged(TQWidget*))); ++ connect(_topTW, TQT_SIGNAL(visibleRectChanged(TabWidget*)), ++ this, TQT_SLOT(visibleRectChangedSlot(TabWidget*))); ++ ++ _bottomSplitter = new Splitter(Qt::Horizontal, ++ _leftSplitter, "Bottom"); ++ ++ _leftTW = new TabWidget(this, _bottomSplitter, "Left"); ++ _leftTW->setTabPosition(TQTabWidget::Bottom); ++ connect(_leftTW, TQT_SIGNAL(currentChanged(TQWidget*)), ++ this, TQT_SLOT(tabChanged(TQWidget*))); ++ connect(_leftTW, TQT_SIGNAL(visibleRectChanged(TabWidget*)), ++ this, TQT_SLOT(visibleRectChangedSlot(TabWidget*))); ++ ++ _bottomTW = new TabWidget(this, _bottomSplitter, "Bottom"); ++ _bottomTW->setTabPosition(TQTabWidget::Bottom); ++ connect(_bottomTW, TQT_SIGNAL(currentChanged(TQWidget*)), ++ this, TQT_SLOT(tabChanged(TQWidget*))); ++ connect(_bottomTW, TQT_SIGNAL(visibleRectChanged(TabWidget*)), ++ this, TQT_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(); ++ ++ TQWhatsThis::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(TQString 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(TQWidget* 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); ++ ++ TQValueList<int> 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(TQWidget* 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); ++ ++ TQPtrList<TraceItemView> 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(); ++} ++ ++ ++TQString TabView::whatsThis() const ++{ ++ return i18n( "<b>Information Tabs</b>" ++ "<p>This widget shows information for the " ++ "current selected function in different tabs: " ++ "<ul>" ++ "<li>The Costs tab shows a list of available event types " ++ "and the inclusive and self costs regarding to these types.</li>" ++ "<li>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.</li>" ++ "<li>The Call Lists tab shows direct callers and " ++ "callees of the function in more detail.</li>" ++ "<li>The Coverage tab shows the same is the Call " ++ "Lists tab, but not only direct callers and callees " ++ "but also indirect ones.</li>" ++ "<li>The Call Graph tab shows a graphical " ++ "visualization of the calls done by this function.</li>" ++ "<li>The Source tab presents annotated source code " ++ "if debugging information and the source file " ++ "is available.</li>" ++ "<li>The Assembler tab presents annotated assembler code " ++ "if trace information on instruction level " ++ "is available.</li></ul>" ++ "For more information, see the <em>What's This?</em> " ++ "help of the corresponding tab widget</p>"); ++} ++ ++void TabView::installFocusFilters() ++{ ++ TQObjectList *l = queryList(TQWIDGET_OBJECT_NAME_STRING); ++ TQObjectListIt it( *l ); ++ TQObject *obj; ++ ++ while ( (obj = it.current()) != 0 ) { ++ ++it; ++ if ( ((TQWidget*)obj)->isFocusEnabled() ) ++ obj->installEventFilter(this); ++ } ++ delete l; ++} ++ ++ ++bool TabView::eventFilter(TQObject* o, TQEvent* e) ++{ ++ if (e->type() == TQEvent::FocusIn) { ++ _lastFocus = o->isWidgetType() ? (TQWidget*) o : 0; ++ setActive(_lastFocus != 0); ++ } ++ return TQWidget::eventFilter(o,e); ++} ++ ++void TabView::mousePressEvent(TQMouseEvent*) ++{ ++ if (_lastFocus) ++ _lastFocus->setFocus(); ++ setActive(true); ++} ++ ++void TabView::setActive(bool a) ++{ ++ if (a == _active) return; ++ _active = a; ++ ++ TQFont 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; ++ TQPtrListIterator<TraceItemView> 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(TQWidget* 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(TQResizeEvent* e) ++{ ++ TQWidget::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, ++ TQString prefix, TQString 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")); ++ ++ TQString activeT = g->readEntry("ActiveTop", "CallerView"); ++ TQString activeB = g->readEntry("ActiveBottom", "CalleeView"); ++ TQString activeL = g->readEntry("ActiveLeft", ""); ++ TQString activeR = g->readEntry("ActiveRight", ""); ++ ++ TQStringList topTabs = g->readListEntry("TopTabs"); ++ TQStringList bottomTabs = g->readListEntry("BottomTabs"); ++ TQStringList leftTabs = g->readListEntry("LeftTabs"); ++ TQStringList 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; ++ TQPtrListIterator<TraceItemView> it( _tabs ); ++ while ( (v=it.current()) != 0) { ++ ++it; ++ ++ TQString n = TQString(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, TQString("%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()); ++ ++ TQString activeType = g->readEntry("ActiveItemType", ""); ++ TQString activeName = g->readEntry("ActiveItemName", ""); ++ TQString selectedType = g->readEntry("SelectedItemType", ""); ++ TQString 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, ++ TQString prefix, TQString postfix, ++ bool withOptions) ++{ ++ KConfigGroup g(c, (prefix+postfix).ascii()); ++ ++ g.writeEntry("MainSizes", _mainSplitter->sizes()); ++ g.writeEntry("LeftSizes", _leftSplitter->sizes()); ++ g.writeEntry("BottomSizes", _bottomSplitter->sizes()); ++ ++ TQString a; ++ if ((_topTW->count()>0) && ++ (_topTW->isTabEnabled(_topTW->currentPage()))) ++ a = TQString(_topTW->currentPage()->name()); ++ g.writeEntry("ActiveTop", a); ++ ++ a.setLength(0); ++ if ((_bottomTW->count()>0) && ++ (_bottomTW->isTabEnabled(_bottomTW->currentPage()))) ++ a = TQString(_bottomTW->currentPage()->name()); ++ g.writeEntry("ActiveBottom", a); ++ ++ a.setLength(0); ++ if ((_leftTW->count()>0) && ++ (_leftTW->isTabEnabled(_leftTW->currentPage()))) ++ a = TQString(_leftTW->currentPage()->name()); ++ g.writeEntry("ActiveLeft", a); ++ ++ a.setLength(0); ++ if ((_rightTW->count()>0) && ++ (_rightTW->isTabEnabled(_rightTW->currentPage()))) ++ a = TQString(_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()); ++ } ++ } ++ ++ TQStringList topList, bottomList, leftList, rightList; ++ TraceItemView *v; ++ for (v=_tabs.first();v;v=_tabs.next()) { ++ switch(v->position()) { ++ case TraceItemView::Top: ++ topList << TQString(v->widget()->name()); ++ break; ++ ++ case TraceItemView::Bottom: ++ bottomList << TQString(v->widget()->name()); ++ break; ++ ++ case TraceItemView::Left: ++ leftList << TQString(v->widget()->name()); ++ break; ++ ++ case TraceItemView::Right: ++ rightList << TQString(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, TQString("%1-%2").arg(prefix) ++ .arg(v->widget()->name()), postfix, true); ++} ++ ++#include "tabview.moc" +diff --git a/kdecachegrind/kdecachegrind/tabview.h b/kdecachegrind/kdecachegrind/tabview.h +new file mode 100644 +index 0000000..b9b4026 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/tabview.h +@@ -0,0 +1,174 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Tab View, enclosing detailed views for one trace item in ++ * two tab widgets, separated by a splitter ++ */ ++ ++#ifndef TABVIEW_H ++#define TABVIEW_H ++ ++#include <tqptrlist.h> ++#include <tqwidget.h> ++#include <tqtabwidget.h> ++#include <tqtabbar.h> ++#include <ksqueezedtextlabel.h> ++#include "traceitemview.h" ++ ++class TQSplitter; ++class TabView; ++ ++/** ++ * Subclass of TQTabBar to enable context menu on tabs ++ */ ++class TabBar : public TQTabBar ++{ ++ Q_OBJECT ++ TQ_OBJECT ++ ++ public: ++ TabBar(TabView*, TQTabWidget* parent, const char *name = 0); ++ protected: ++ void mousePressEvent(TQMouseEvent *e); ++ ++ private: ++ TQTabWidget* _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 TQSplitter ++{ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ Splitter(Qt::Orientation o, TQWidget* parent = 0, const char* name = 0); ++ void checkVisiblity(); ++ ++protected: ++ void moveEvent(TQMoveEvent *); ++}; ++ ++ ++/** ++ * Own TabView: ++ * - A TQTabWidget able to track its visible rect via resizeEvents. ++ * This is needed to track if this widget is collapsed in a TQSplitter. ++ * - Use own TabBar for context menu ++ */ ++class TabWidget: public TQTabWidget ++{ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ ++ TabWidget(TabView*, TQWidget* parent = 0, ++ const char* name = 0, WFlags f = 0); ++ ++ bool hasVisibleRect() { return _hasVisibleRect; } ++ void checkVisibility(); ++ ++signals: ++ void visibleRectChanged(TabWidget*); ++ ++protected: ++ void resizeEvent(TQResizeEvent *); ++ void showEvent(TQShowEvent *); ++ void hideEvent(TQHideEvent *); ++ void moveEvent(TQMoveEvent *); ++ ++private: ++ bool _hasVisibleRect; ++}; ++ ++ ++ ++class TabView : public TQWidget, public TraceItemView ++{ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ ++ TabView(TraceItemView* parentView, ++ TQWidget* parent = 0, const char* name = 0); ++ ++ virtual TQWidget* widget() { return this; } ++ TQString 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 <w> == 0, move hidden tabs ++ */ ++ void moveTab(TQWidget* w, Position, bool wholeArea = false); ++ ++ Position tabPosition(TQWidget*); ++ int visibleTabs(); ++ int visibleAreas(); ++ ++ void readViewConfig(KConfig*, TQString prefix, TQString postfix, bool); ++ void saveViewConfig(KConfig*, TQString prefix, TQString postfix, bool); ++ ++public slots: ++ void tabChanged(TQWidget*); ++ void visibleRectChangedSlot(TabWidget*); ++ ++signals: ++ void activated(TabView*); ++ ++protected: ++ void resizeEvent(TQResizeEvent *); ++ bool eventFilter(TQObject*, TQEvent*); ++ void mousePressEvent(TQMouseEvent*); ++ ++private: ++ TraceItemView* addTab(TQString, 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; ++ TQSplitter *_mainSplitter, *_leftSplitter, *_bottomSplitter; ++ TabWidget *_topTW, *_leftTW, *_bottomTW, *_rightTW; ++ TQPtrList<TraceItemView> _tabs; ++ ++ TQWidget* _lastFocus; ++ bool _active; ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/kdecachegrind.desktop b/kdecachegrind/kdecachegrind/kdecachegrind.desktop +new file mode 100644 +index 0000000..7089370 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/kdecachegrind.desktop +@@ -0,0 +1,103 @@ ++# KDE Config File ++[Desktop Entry] ++Type=Application ++Exec=kdecachegrind -caption "%c" %i %m %u ++MimeType=application/x-kcachegrind; ++Icon=kdecachegrind ++DocPath=kdecachegrind/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/kdecachegrind/kdecachegrind/kdecachegrindui.rc b/kdecachegrind/kdecachegrind/kdecachegrindui.rc +new file mode 100644 +index 0000000..9531829 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/kdecachegrindui.rc +@@ -0,0 +1,57 @@ ++<!DOCTYPE kpartgui> ++<kpartgui name="kdecachegrind" version="4"> ++ <MenuBar> ++ <Menu name="file"><text>&File</text> ++ <Action name="file_add" append="open_merge"/> ++ <Action name="reload" append="revert_merge"/> ++ <Action name="dump" append="revert_merge"/> ++ <Action name="export"/> ++ </Menu> ++ <Menu name="view"><text>&View</text> ++ <Action name="view_cost_type"/> ++ <Action name="view_cost_type2"/> ++ <Action name="view_group_type"/> ++ <Separator/> ++ <Menu name="layouts"><text>&Layout</text> ++ <Action name="layout_next"/> ++ <Action name="layout_previous"/> ++ <Action name="layout_duplicate"/> ++ <Action name="layout_remove"/> ++ <Separator/> ++ <Action name="layout_restore"/> ++ <Action name="layout_save"/> ++ </Menu> ++ <Action name="view_split"/> ++ <Action name="view_split_dir"/> ++ <Separator/> ++ <Action name="view_percentage"/> ++ <Action name="view_expanded"/> ++ <Action name="view_cycles"/> ++ </Menu> ++ <Menu name="settings"> ++ <Menu append="show_toolbar_merge"><text>Sidebars</text> ++ <Action name="settings_show_dumpdock"/> ++ <Action name="settings_show_partdock"/> ++ <Action name="settings_show_stackdock"/> ++ <Action name="settings_show_profiledock"/> ++ </Menu> ++ </Menu> ++ </MenuBar> ++ ++ <ToolBar name="mainToolBar" noMerge="1"><text>Main Toolbar</text> ++ <Action name="file_open"/> ++ <Action name="reload"/> ++ <Action name="dump"/> ++ <Separator/> ++ <Action name="go_up"/> ++ <Action name="go_back"/> ++ <Action name="go_forward"/> ++ <Separator/> ++ <Action name="view_percentage"/> ++ <Action name="view_expanded"/> ++ <Action name="view_cycles"/> ++ </ToolBar> ++ <ToolBar name="stateToolBar" noMerge="1"><text>State Toolbar</text> ++ <Action name="view_cost_type"/> ++ </ToolBar> ++</kpartgui> +diff --git a/kdecachegrind/kdecachegrind/tips b/kdecachegrind/kdecachegrind/tips +new file mode 100644 +index 0000000..1f555c0 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/tips +@@ -0,0 +1,141 @@ ++<tip category="KCachegrind|Help"> ++<html> ++<p>...that the <em>What's This?</em> 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 <em>What's This?</em> help by pressing ++Shift+F1 and clicking on the widget.</p> ++</html> ++</tip> ++ ++<tip category="KCachegrind|Explanation"> ++<html> ++<p>...that you can get profile information at instruction level ++with Calltree when you provide the option <em>--dump-instr=yes</em>? ++Use the Assembler View for the instruction annotations. ++</p> ++</html> ++</tip> ++ ++<tip category="KCachegrind|Keyboard"> ++<html> ++<p>...that you can use Alt-Left/Right keys of your keyboard to go ++back/forward in the active object history ?</p> ++</html> ++</tip> ++ ++<tip category="KCachegrind|Keyboard"> ++<html> ++<p>...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. ++</p> ++</html> ++</tip> ++ ++<tip category="KCachegrind|Keyboard"> ++<html> ++<p>...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. ++</p> ++</html> ++</tip> ++ ++<tip category="KCachegrind|Filters"> ++<html> ++<p>...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?</p> ++</html> ++</tip> ++ ++<tip category="KCachegrind|Appearance"> ++<html> ++<p>...that you can assign custom colors to ++ELF objects/C++ Classes/Source Files for graph coloring ++in <em>Settings->Configure KCachegrind...</em>?</p> ++</html> ++</tip> ++ ++<tip category="KCachegrind|Configuration"> ++<html> ++<p>...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?</p> ++<p>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 ++<em>Source Directories</em> list in the configuration. ++</html> ++</tip> ++ ++<tip category="KCachegrind|Appearance"> ++<html> ++<p>...that you can configure whether KCachgrind should ++show absolute event counts or relative ones (percentage display)?</p> ++</html> ++</tip> ++ ++<tip category="KCachegrind|Appearance"> ++<html> ++<p>...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.</p> ++<p>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.</p> ++</html> ++</tip> ++ ++<tip category="KCachegrind|Explanation"> ++<html> ++<p>...that the Coverage tab - in contrast to the Call Lists tab - ++shows <em>all</em> 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?</p> ++<p>Examples:</p> ++<p>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().</p> ++<p>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().</p> ++</html> ++</tip> ++ ++<tip category="KCachegrind|Explanation"> ++<html> ++<p>...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?</p> ++<p>Items from this list can be selected by pressing the right ++mouse button.</p> ++</html> ++</tip> ++ ++<tip category="KCachegrind|Explanation"> ++<html> ++<p>...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?</p> ++<p>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).</p> ++</html> ++</tip> ++ ++<tip category="KCachegrind|Explanation"> ++<p>...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?</p> ++</html> ++</tip> ++ +diff --git a/kdecachegrind/kdecachegrind/toplevel.cpp b/kdecachegrind/kdecachegrind/toplevel.cpp +new file mode 100644 +index 0000000..5a2e1de +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/toplevel.cpp +@@ -0,0 +1,2389 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002, 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * KCachegrind top level window ++ */ ++ ++#define TRACE_UPDATES 0 ++#define ENABLE_DUMPDOCK 0 ++ ++#include <stdlib.h> // for system() ++ ++#include <tqvbox.h> ++#include <tqtimer.h> ++#include <tqwhatsthis.h> ++#include <tqlineedit.h> ++#include <tqtextstream.h> ++#include <tqsizepolicy.h> ++#include <tqprogressbar.h> ++#include <tqfile.h> ++#include <tqeventloop.h> ++ ++#include <kapplication.h> ++#include <klocale.h> ++#include <kstatusbar.h> ++#include <kstdaccel.h> ++#include <kstdaction.h> ++#include <kaction.h> ++#include <kurl.h> ++#include <kfiledialog.h> ++#include <kio/netaccess.h> ++#include <kedittoolbar.h> ++#include <kkeydialog.h> ++#include <ktip.h> ++#include <kpopupmenu.h> ++#include <kdebug.h> ++ ++#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, TQT_SIGNAL(visibilityChanged(bool)), ++ TQT_TQOBJECT(this), TQT_SLOT(partVisibilityChanged(bool))); ++ connect(_stackDock, TQT_SIGNAL(visibilityChanged(bool)), ++ TQT_TQOBJECT(this), TQT_SLOT(stackVisibilityChanged(bool))); ++ connect(_functionDock, TQT_SIGNAL(visibilityChanged(bool)), ++ TQT_TQOBJECT(this), TQT_SLOT(functionVisibilityChanged(bool))); ++ ++#if ENABLE_DUMPDOCK ++ _dumpDockShown->setChecked(!_dumpDock->isHidden()); ++ connect(_dumpDock, TQT_SIGNAL(visibilityChanged(bool)), ++ TQT_TQOBJECT(this), TQT_SLOT(dumpVisibilityChanged(bool))); ++#endif ++ ++ _statusbar = statusBar(); ++ _statusLabel = new TQLabel(_statusbar); ++#if 0 ++ // how to do avoid main window resizing on large statusbar label? ++ TQSizePolicy p(TQSizePolicy::Fixed, TQSizePolicy::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(TQString()); ++ ++ // if this is the first toplevel, show tip of day ++ if (memberList->count() == 1) ++ TQTimer::singleShot( 200, TQT_TQOBJECT(this), TQT_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, TQT_SIGNAL(activePartsChanged(const TracePartList&)), ++ TQT_TQOBJECT(this), TQT_SLOT(activePartsChangedSlot(const TracePartList&))); ++ connect(ps, TQT_SIGNAL(groupChanged(TraceCostItem*)), ++ TQT_TQOBJECT(this), TQT_SLOT(setGroupDelayed(TraceCostItem*))); ++ connect(ps, TQT_SIGNAL(functionChanged(TraceItem*)), ++ TQT_TQOBJECT(this), TQT_SLOT(setTraceItemDelayed(TraceItem*))); ++ ++ connect(ps, TQT_SIGNAL(goBack()), ++ _stackSelection, TQT_SLOT(browserBack())); ++ ++ connect(ps, TQT_SIGNAL(partsHideSelected()), ++ TQT_TQOBJECT(this), TQT_SLOT(partsHideSelectedSlotDelayed())); ++ connect(ps, TQT_SIGNAL(partsUnhideAll()), ++ TQT_TQOBJECT(this), TQT_SLOT(partsUnhideAllSlotDelayed())); ++ ++ connect(ps, TQT_SIGNAL(showMessage(const TQString&, int)), ++ _statusbar, TQT_SLOT(message(const TQString&, 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(TQString postfix) ++{ ++ KConfig* kconfig = KGlobal::config(); ++ TQCString pf = postfix.ascii(); ++ ++ KConfigGroup psConfig(kconfig, TQCString("PartOverview")+pf); ++ _partSelection->saveVisualisationConfig(&psConfig); ++ ++ KConfigGroup stateConfig(kconfig, TQCString("CurrentState")+pf); ++ stateConfig.writeEntry("CostType", ++ _costType ? _costType->name() : TQString("?")); ++ stateConfig.writeEntry("CostType2", ++ _costType2 ? _costType2->name() : TQString("?")); ++ stateConfig.writeEntry("GroupType", TraceItem::typeName(_groupType)); ++ ++ _multiView->saveViewConfig(kconfig, TQString("MainView"), postfix, true); ++} ++ ++/** ++ * This function is called when a trace is closed. ++ * Save browsing position for later restoring ++ */ ++void TopLevel::saveTraceSettings() ++{ ++ TQString key = traceKey(); ++ ++ KConfigGroup pConfig(KGlobal::config(), TQCString("TracePositions")); ++ pConfig.writeEntry(TQString("CostType%1").arg(key), ++ _costType ? _costType->name() : TQString("?")); ++ pConfig.writeEntry(TQString("CostType2%1").arg(key), ++ _costType2 ? _costType2->name() : TQString("?")); ++ pConfig.writeEntry(TQString("GroupType%1").arg(key), ++ TraceItem::typeName(_groupType)); ++ ++ if (!_data) return; ++ ++ KConfigGroup aConfig(KGlobal::config(), TQCString("Layouts")); ++ aConfig.writeEntry(TQString("Count%1").arg(key), _layoutCount); ++ aConfig.writeEntry(TQString("Current%1").arg(key), _layoutCurrent); ++ ++ saveCurrentState(key); ++ pConfig.writeEntry(TQString("Group%1").arg(key), ++ _group ? _group->name() : TQString()); ++} ++ ++/** ++ * 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(TQString postfix) ++{ ++ KConfig* kconfig = KGlobal::config(); ++ TQStringList gList = kconfig->groupList(); ++ TQCString pf = postfix.ascii(); ++ ++ // dock properties (not position, this should be have done before) ++ TQCString group = TQCString("PartOverview"); ++ if (gList.contains(group+pf)) group += pf; ++ KConfigGroup psConfig(kconfig, group); ++ _partSelection->readVisualisationConfig(&psConfig); ++ ++ _multiView->readViewConfig(kconfig, TQString("MainView"), postfix, true); ++ _taSplit->setChecked(_multiView->childCount()>1); ++ _taSplitDir->setEnabled(_multiView->childCount()>1); ++ _taSplitDir->setChecked(_multiView->orientation() == Qt::Horizontal); ++} ++ ++ ++void TopLevel::createDocks() ++{ ++ _partDock = new TQDockWindow(TQDockWindow::InDock, this); ++ _partDock->setCaption(i18n("Parts Overview")); ++ _partDock->setCloseMode( TQDockWindow::Always ); ++ _partSelection = new PartSelection(_partDock, "partSelection"); ++ _partDock->setWidget(_partSelection); ++ _partDock->setResizeEnabled(true); ++ _partDock->setFixedExtentWidth(200); ++ TQWhatsThis::add( _partSelection, i18n( ++ "<b>The Parts Overview</b>" ++ "<p>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." ++ "</p>" ++ "<p>The parts are further subdivided: there is a " ++ "partitioning and an callee split mode: " ++ "<ul><li>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.</li>" ++ "<li>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.</li></ul></p>")); ++ ++ _stackDock = new TQDockWindow(TQDockWindow::InDock, this); ++ _stackDock->setResizeEnabled(true); ++ // Why is the caption only correct with a close button? ++ _stackDock->setCloseMode( TQDockWindow::Always ); ++ _stackSelection = new StackSelection(_stackDock, "stackSelection"); ++ _stackDock->setWidget(_stackSelection); ++ _stackDock->setFixedExtentWidth(200); ++ _stackDock->setCaption(i18n("Top Cost Call Stack")); ++ TQWhatsThis::add( _stackSelection, i18n( ++ "<b>The Top Cost Call Stack</b>" ++ "<p>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.</p>" ++ "<p>The <b>Cost</b> and <b>Calls</b> columns show the " ++ "cost used for all calls from the function in the line " ++ "above.</p>")); ++ ++ connect(_stackSelection, TQT_SIGNAL(functionSelected(TraceItem*)), ++ TQT_TQOBJECT(this), TQT_SLOT(setTraceItemDelayed(TraceItem*))); ++ ++ _functionDock = new TQDockWindow(TQDockWindow::InDock, this); ++ _functionDock->setCaption(i18n("Flat Profile")); ++ _functionDock->setCloseMode( TQDockWindow::Always ); ++ _functionSelection = new FunctionSelection(this, _functionDock, ++ "functionSelection"); ++ _functionSelection->setTopLevel(this); ++ ++ _functionDock->setWidget(_functionSelection); ++ _functionDock->setResizeEnabled(true); ++ _functionDock->setFixedExtentWidth(200); ++ TQWhatsThis::add( _functionSelection, i18n( ++ "<b>The Flat Profile</b>" ++ "<p>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.<p>" ++ "<p>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.</p>")); ++ ++#if ENABLE_DUMPDOCK ++ _dumpDock = new TQDockWindow(TQDockWindow::InDock, this); ++ _dumpDock->setCaption(i18n("Profile Dumps")); ++ _dumpDock->setCloseMode( TQDockWindow::Always ); ++ _dumpSelection = new DumpSelection(this, _dumpDock, ++ "dumpSelection"); ++ _dumpSelection->setTopLevel(this); ++ ++ _dumpDock->setWidget(_dumpSelection); ++ _dumpDock->setResizeEnabled(true); ++ _dumpDock->setFixedExtentWidth(200); ++ TQWhatsThis::add( _dumpSelection, i18n( ++ "<b>Profile Dumps</b>" ++ "<p>This dockable shows in the top part the list of " ++ "loadable profile dumps in all subdirectories of: " ++ "<ul><li>current working directory of KCachegrind, " ++ "i.e. where it was started from, and " ++ "<li>the default profile dump directory given in the " ++ "configuration.</ul> " ++ "The list is sorted according the the target command " ++ "profiled in the corresponding dump.</p>" ++ "<p>On selecting a profile dump, information for it " ++ "is shown in the bottom area of the dockable: " ++ "<ul><li><b>Options</b> 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 <b>Run Profile</b> to start a" ++ "profile run with these options in the background. " ++ "<li><b>Info</b> gives detailed info on the selected " ++ "dump like event cost summary and properties of the " ++ "simulated cache. " ++ "<li><b>State</b> is only available for current happening " ++ "profiles runs. Press <b>Update</b> to see different " ++ "counters of the run, and a stack trace of the current " ++ "position in the program profiled. Check the <b>Every</b> " ++ "option to let KCachegrind regularly poll these data. " ++ "Check the <b>Sync</b> option to let the dockable activate " ++ "the top function in the current loaded dump.</ul></p>")); ++#endif ++ ++ // Restore QT Dock positions... ++ KConfigGroup dockConfig(KGlobal::config(), TQCString("Docks")); ++ TQString str = dockConfig.readEntry("Position", TQString()); ++ 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 { ++ TQTextStream 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, TQT_SIGNAL(contextMenuRequested(const TQPoint &)), ++ TQT_TQOBJECT(this), TQT_SLOT(showDockMenu(const TQPoint &))); ++#endif ++} ++ ++ ++TopLevel::~TopLevel() ++{ ++ delete _data; ++} ++ ++ ++void TopLevel::saveProperties(KConfig* c) ++{ ++ c->writeEntry("TraceName", _data->traceName()); ++} ++ ++void TopLevel::readProperties(KConfig* c) ++{ ++ TQString traceName = c->readEntry("TraceName"); ++ if (!traceName.isEmpty()) { ++ TraceData* d = new TraceData(this); ++ d->load(traceName); ++ setData(d); ++ } ++} ++ ++void TopLevel::createLayoutActions() ++{ ++ TQString hint; ++ KAction* action; ++ ++ action = new KAction( i18n( "&Duplicate" ), ++ KShortcut(KKey("Ctrl+Plus")), ++ TQT_TQOBJECT(this), TQT_SLOT(layoutDuplicate()), ++ actionCollection(), "layout_duplicate" ); ++ hint = i18n("<b>Duplicate Current Layout</b>" ++ "<p>Make a copy of the current layout.</p>"); ++ action->setWhatsThis( hint ); ++ ++ action = new KAction( i18n( "&Remove" ), KShortcut(), ++ TQT_TQOBJECT(this), TQT_SLOT(layoutRemove()), ++ actionCollection(), "layout_remove" ); ++ hint = i18n("<b>Remove Current Layout</b>" ++ "<p>Delete current layout and make the previous active.</p>"); ++ action->setWhatsThis( hint ); ++ ++ action = new KAction( i18n( "&Go to Next" ), ++ KShortcut(KKey("Ctrl+Right")), ++ TQT_TQOBJECT(this), TQT_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")), ++ TQT_TQOBJECT(this), TQT_SLOT(layoutPrevious()), ++ actionCollection(), "layout_previous" ); ++ hint = i18n("Go to Previous Layout"); ++ action->setWhatsThis( hint ); ++ ++ action = new KAction( i18n( "&Restore to Default" ), KShortcut(), ++ TQT_TQOBJECT(this), TQT_SLOT(layoutRestore()), ++ actionCollection(), "layout_restore" ); ++ hint = i18n("Restore Layouts to Default"); ++ action->setWhatsThis( hint ); ++ ++ action = new KAction( i18n( "&Save as Default" ), KShortcut(), ++ TQT_TQOBJECT(this), TQT_SLOT(layoutSave()), ++ actionCollection(), "layout_save" ); ++ hint = i18n("Save Layouts as Default"); ++ action->setWhatsThis( hint ); ++} ++ ++// TODO: split this up... ++void TopLevel::createMiscActions() ++{ ++ TQString hint; ++ KAction* action; ++ ++ action = KStdAction::openNew(TQT_TQOBJECT(this), TQT_SLOT(newWindow()), actionCollection()); ++ hint = i18n("<b>New</b><p>Open new empty KCachegrind window.</p>"); ++ action->setWhatsThis( hint ); ++ ++ action = new KAction( i18n( "&Add..." ), KShortcut(), ++ TQT_TQOBJECT(this), TQT_SLOT(addTrace()), ++ actionCollection(), "file_add" ); ++ hint = i18n("<b>Add Profile Data</b>" ++ "<p>This opens an additional profile data file in the current window.</p>"); ++ 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 ++ TQT_TQOBJECT(this), TQT_SLOT( reload() ), actionCollection(), "reload" ); ++ hint = i18n("<b>Reload Profile Data</b>" ++ "<p>This loads any new created parts, too.</p>"); ++ action->setWhatsThis( hint ); ++ ++ action = new KAction( i18n( "&Export Graph" ), KShortcut(), ++ TQT_TQOBJECT(this), TQT_SLOT(exportGraph()), ++ actionCollection(), "export" ); ++ ++ hint = i18n("<b>Export Call Graph</b>" ++ "<p>Generates a file with extension .dot for the tools " ++ "of the GraphViz package.</p>"); ++ 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 ++ TQT_TQOBJECT(this), TQT_SLOT( forceTrace() ), ++ actionCollection(), "dump" ); ++ hint = i18n("<b>Force Dump</b>" ++ "<p>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.</p>" ++ "<p>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 <em>no</em> Callgrind " ++ "running, press 'Force Dump' again to cancel the dump " ++ "request. This deletes 'callgrind.cmd' itself and " ++ "stops polling for a new dump.</p>" ++ "<p>Note: A Callgrind run <em>only</em> detects " ++ "existence of 'callgrind.cmd' when actively running " ++ "a few milliseconds, i.e. " ++ "<em>not</em> sleeping. Tip: For a profiled GUI program, " ++ "you can awake Callgrind e.g. by resizing a window " ++ "of the program.</p>"); ++ _taDump->setWhatsThis( hint ); ++ ++ action = KStdAction::open(TQT_TQOBJECT(this), TQT_SLOT(loadTrace()), actionCollection()); ++ hint = i18n("<b>Open Profile Data</b>" ++ "<p>This opens a profile data file, with possible multiple parts</p>"); ++ action->setToolTip( hint ); ++ action->setWhatsThis( hint ); ++ ++ _openRecent = KStdAction::openRecent(TQT_TQOBJECT(this), TQT_SLOT(loadTrace(const KURL&)), ++ actionCollection()); ++ ++ KStdAction::showStatusbar(TQT_TQOBJECT(this), ++ TQT_SLOT(toggleStatusBar()), actionCollection()); ++ ++ _partDockShown = new KToggleAction(i18n("Parts Overview"), KShortcut(), ++ TQT_TQOBJECT(this), TQT_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(), ++ TQT_TQOBJECT(this), TQT_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(), ++ TQT_TQOBJECT(this), TQT_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(), ++ TQT_TQOBJECT(this), TQT_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(), ++ TQT_TQOBJECT(this), TQT_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(), ++ TQT_TQOBJECT(this), TQT_SLOT(toggleExpanded()), ++ actionCollection(), ++ "view_expanded"); ++ ++ hint = i18n("Show percentage costs relative to parent"); ++ _taExpanded->setToolTip( hint ); ++ _taExpanded->setWhatsThis( hint ); ++ ++ hint = i18n("<b>Show percentage costs relative to parent</b>" ++ "<p>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." ++ "<ul><table>" ++ "<tr><td><b>Cost Type</td><td><b>Parent Cost</td></tr>" ++ "<tr><td>Function Cumulative</td><td>Total</td></tr>" ++ "<tr><td>Function Self</td><td>Function Group (*) / Total</td></tr>" ++ "<tr><td>Call</td><td>Function Cumulative</td></tr>" ++ "<tr><td>Source Line</td><td>Function Cumulative</td></tr>" ++ "</table>" ++ "<p>(*) Only if function grouping is switched on (e.g. ELF object grouping)."); ++ _taExpanded->setWhatsThis( hint ); ++ ++ _taCycles = new KToggleAction( i18n( "Do Cycle Detection" ), "undo", ++ KShortcut(), ++ TQT_TQOBJECT(this), TQT_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("<b>Detect recursive cycles</b>" ++ "<p>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)." ++ "<p>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(TQT_TQOBJECT(this), TQT_SLOT(close()), actionCollection()); ++ KStdAction::preferences(TQT_TQOBJECT(this), TQT_SLOT(configure()), actionCollection()); ++ KStdAction::keyBindings(TQT_TQOBJECT(this), TQT_SLOT(configureKeys()), actionCollection()); ++ KStdAction::configureToolbars(TQT_TQOBJECT(this),TQT_SLOT(configureToolbars()), ++ actionCollection()); ++#if 0 ++ action = KStdAction::back(_stackSelection, TQT_SLOT(browserBack()), ++ actionCollection()); ++ hint = i18n("Go back in function selection history"); ++ action->setToolTip( hint ); ++ action->setWhatsThis( hint ); ++ ++ action = KStdAction::forward(_stackSelection, TQT_SLOT(browserForward()), ++ actionCollection()); ++ hint = i18n("Go forward in function selection history"); ++ action->setToolTip( hint ); ++ action->setWhatsThis( hint ); ++ ++ action = KStdAction::up(_stackSelection, TQT_SLOT(browserUp()), ++ actionCollection()); ++ hint = i18n("<b>Go Up</b>" ++ "<p>Go to last selected caller of current function. " ++ "If no caller was visited, use that with highest cost.</p>"); ++ action->setToolTip( hint ); ++ action->setWhatsThis( hint ); ++#else ++ _paUp = new KToolBarPopupAction( i18n( "&Up" ), "up", ++ ALT+Key_Up, ++ TQT_TQOBJECT(_stackSelection), TQT_SLOT( browserUp() ), ++ actionCollection(), "go_up" ); ++ connect( _paUp->popupMenu(), TQT_SIGNAL( aboutToShow() ), ++ TQT_TQOBJECT(this), TQT_SLOT( upAboutToShow() ) ); ++ connect( _paUp->popupMenu(), TQT_SIGNAL( activated( int ) ), ++ TQT_TQOBJECT(this), TQT_SLOT( upActivated( int ) ) ); ++ hint = i18n("<b>Go Up</b>" ++ "<p>Go to last selected caller of current function. " ++ "If no caller was visited, use that with highest cost.</p>"); ++ _paUp->setToolTip( hint ); ++ _paUp->setWhatsThis( hint ); ++ ++ TQPair< KGuiItem, KGuiItem > backForward = KStdGuiItem::backAndForward(); ++ _paBack = new KToolBarPopupAction( backForward.first, ALT+Key_Left, ++ TQT_TQOBJECT(_stackSelection), TQT_SLOT(browserBack()), ++ actionCollection(), "go_back" ); ++ connect( _paBack->popupMenu(), TQT_SIGNAL( aboutToShow() ), ++ TQT_TQOBJECT(this), TQT_SLOT( backAboutToShow() ) ); ++ connect( _paBack->popupMenu(), TQT_SIGNAL( activated( int ) ), ++ TQT_TQOBJECT(this), TQT_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, ++ TQT_TQOBJECT(_stackSelection), ++ TQT_SLOT(browserForward()), ++ actionCollection(), "go_forward" ); ++ connect( _paForward->popupMenu(), TQT_SIGNAL( aboutToShow() ), ++ this, TQT_SLOT( forwardAboutToShow() ) ); ++ connect( _paForward->popupMenu(), TQT_SIGNAL( activated( int ) ), ++ this, TQT_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, TQT_SIGNAL(activated(const TQString&)), ++ TQT_TQOBJECT(this), TQT_SLOT(costTypeSelected(const TQString&))); ++ ++ _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, TQT_SIGNAL(activated(const TQString&)), ++ TQT_TQOBJECT(this), TQT_SLOT(costType2Selected(const TQString&))); ++ ++ 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 ); ++ ++ TQStringList 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, TQT_SIGNAL(activated(int)), ++ TQT_TQOBJECT(this), TQT_SLOT(groupTypeSelected(int))); ++ ++ _taSplit = new KToggleAction(i18n("Split"), "view_left_right", KShortcut(), ++ TQT_TQOBJECT(this), TQT_SLOT(splitSlot()), ++ actionCollection(), "view_split"); ++ ++ hint = i18n("Show two information panels"); ++ _taSplit->setToolTip( hint ); ++ _taSplit->setWhatsThis( hint ); ++ ++ _taSplitDir = new KToggleAction(i18n("SplitQt::Horizontal"), ++ "view_left_right", KShortcut(), ++ TQT_TQOBJECT(this), TQT_SLOT(splitDirSlot()), ++ actionCollection(), "view_split_dir"); ++ ++ hint = i18n("Change Split Qt::Orientation when main window is split."); ++ _taSplitDir->setToolTip( hint ); ++ _taSplitDir->setWhatsThis( hint ); ++ ++ // copied from KMail... ++#if KDE_VERSION >= 308 // KDE 3.1 ++ KStdAction::tipOfDay( TQT_TQOBJECT(this), TQT_SLOT( slotShowTip() ), actionCollection() ); ++#else ++ (void) new KAction( KGuiItem( i18n("Tip of the &Day..."), "idea", ++ i18n("Show \"Tip of the Day\"") ), ++ 0, TQT_TQOBJECT(this), TQT_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 ++ TQString 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(TQString 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(TQString(), ++ 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 ++ TQString 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(TQString 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(TQString file) ++{ ++ _loadTraceDelayed = file; ++ TQTimer::singleShot(0, TQT_TQOBJECT(this), TQT_SLOT(loadTraceDelayed())); ++} ++ ++void TopLevel::loadTraceDelayed() ++{ ++ if (_loadTraceDelayed.isEmpty()) return; ++ ++ loadTrace(_loadTraceDelayed); ++ _loadTraceDelayed = TQString(); ++} ++ ++ ++void TopLevel::reload() ++{ ++ TQString 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; ++ ++ TQString n = TQString("callgraph.dot"); ++ GraphExporter ge(_data, _function, _costType, _groupType, n); ++ ge.writeDot(); ++ ++ TQString cmd = TQString("(dot %1 -Tps > %2.ps; kghostview %3.ps)&") ++ .arg(n).arg(n).arg(n); ++ system(TQFile::encodeName( cmd )); ++} ++ ++ ++bool TopLevel::setCostType(TQString 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(TQString s) ++{ ++ TraceCostType* ct; ++ ++ // Special type i18n("(Hidden)") gives 0 ++ ct = (_data) ? _data->mapping()->type(s) : 0; ++ ++ return setCostType2(ct); ++} ++ ++void TopLevel::costTypeSelected(const TQString& s) ++{ ++ TraceCostType* ct; ++ ++ ct = (_data) ? _data->mapping()->typeForLong(s) : 0; ++ setCostType(ct); ++} ++ ++void TopLevel::costType2Selected(const TQString& 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; ++ TQStringList l = _saCost->items(); ++ for (TQStringList::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; ++ ++ TQString longName = ct ? ct->longName() : i18n("(Hidden)"); ++ ++ int idx=0; ++ TQStringList l = _saCost2->items(); ++ for (TQStringList::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(TQString 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(TQString 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(TQString 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; ++ TQTimer::singleShot (0, TQT_TQOBJECT(this), TQT_SLOT(setCostTypeDelayed())); ++} ++ ++void TopLevel::setCostType2Delayed(TraceCostType* ct) ++{ ++ _costType2Delayed = ct; ++ TQTimer::singleShot (0, TQT_TQOBJECT(this), TQT_SLOT(setCostType2Delayed())); ++} ++ ++void TopLevel::setCostTypeDelayed() ++{ ++ setCostType(_costTypeDelayed); ++} ++ ++void TopLevel::setCostType2Delayed() ++{ ++ setCostType2(_costType2Delayed); ++} ++ ++void TopLevel::setGroupTypeDelayed(TraceItem::CostType gt) ++{ ++ _groupTypeDelayed = gt; ++ TQTimer::singleShot (0, TQT_TQOBJECT(this), TQT_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; ++ TQTimer::singleShot (0, TQT_TQOBJECT(this), TQT_SLOT(setGroupDelayed())); ++} ++ ++void TopLevel::setGroupDelayed() ++{ ++ setGroup(_groupDelayed); ++} ++ ++void TopLevel::setDirectionDelayed(TraceItemView::Direction d) ++{ ++ _directionDelayed = d; ++ TQTimer::singleShot (0, TQT_TQOBJECT(this), TQT_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 = TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>(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 ++ ++ TQTimer::singleShot (0, TQT_TQOBJECT(this), TQT_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 ++ TQStringList 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;i<m->realCount();i++) ++ types << m->realType(i)->longName(); ++ for (int i=0;i<m->virtualCount();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(); ++ ++ TQString 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(TQPopupMenu* popup, bool withCost2) ++{ ++ if (_data) { ++ TQPopupMenu *popup1 = new TQPopupMenu(popup); ++ TQPopupMenu *popup2 = 0; ++ popup1->setCheckable(true); ++ ++ if (withCost2) { ++ popup2 = new TQPopupMenu(popup); ++ popup2->setCheckable(true); ++ ++ if (_costType2) { ++ popup2->insertItem(i18n("Hide"),199); ++ popup2->insertSeparator(); ++ } ++ } ++ ++ TraceCostMapping* m = _data->mapping(); ++ TraceCostType* ct; ++ for (int i=0;i<m->realCount();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;i<m->virtualCount();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,TQT_SIGNAL(activated(int)),this,TQT_SLOT(setCostType(int))); ++ if (popup2) { ++ popup->insertItem(i18n("Secondary Event Type"), popup2); ++ connect(popup2,TQT_SIGNAL(activated(int)),this,TQT_SLOT(setCostType2(int))); ++ } ++ } ++ if (_showPercentage) ++ popup->insertItem(i18n("Show Absolute Cost"), ++ TQT_TQOBJECT(this), TQT_SLOT(setAbsoluteCost())); ++ else ++ popup->insertItem(i18n("Show Relative Cost"), ++ TQT_TQOBJECT(this), TQT_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(TQPopupMenu* popup) ++{ ++ popup->insertItem(i18n("Go Back"), TQT_TQOBJECT(this), TQT_SLOT(goBack())); ++ popup->insertItem(i18n("Go Forward"), TQT_TQOBJECT(this), TQT_SLOT(goForward())); ++ popup->insertItem(i18n("Go Up"), TQT_TQOBJECT(this), TQT_SLOT(goUp())); ++} ++ ++void TopLevel::goBack() ++{ ++ setDirectionDelayed(TraceItemView::Back); ++} ++ ++void TopLevel::goForward() ++{ ++ setDirectionDelayed(TraceItemView::Forward); ++} ++ ++void TopLevel::goUp() ++{ ++ setDirectionDelayed(TraceItemView::Up); ++} ++ ++TQString TopLevel::traceKey() ++{ ++ if (!_data || _data->command().isEmpty()) return TQString(); ++ ++ TQString name = _data->command(); ++ TQString key; ++ for (unsigned int l=0;l<name.length();l++) ++ if (name[l].isLetterOrNumber()) key += name[l]; ++ ++ return TQString("-") + key; ++} ++ ++ ++void TopLevel::restoreTraceTypes() ++{ ++ TQString key = traceKey(); ++ ++ KConfigGroup cConfig(KGlobal::config(), TQCString("CurrentState")); ++ KConfigGroup pConfig(KGlobal::config(), TQCString("TracePositions")); ++ ++ TQString groupType, costType, costType2; ++ groupType = pConfig.readEntry(TQString("GroupType%1").arg(key)); ++ costType = pConfig.readEntry(TQString("CostType%1").arg(key)); ++ costType2 = pConfig.readEntry(TQString("CostType2%1").arg(key)); ++ ++ if (groupType.isEmpty()) groupType = cConfig.readEntry("GroupType"); ++ if (costType.isEmpty()) costType = cConfig.readEntry("CostType"); ++ if (costType2.isEmpty()) costType2 = cConfig.readEntry("CostType2"); ++ ++ setGroupType(groupType); ++ setCostType(costType); ++ setCostType2(costType2); ++ ++ // if still no cost type set, use first available ++ if (!_costType && !_saCost->items().isEmpty()) ++ costTypeSelected(_saCost->items().first()); ++ ++ KConfigGroup aConfig(KGlobal::config(), TQCString("Layouts")); ++ _layoutCount = aConfig.readNumEntry(TQString("Count%1").arg(key), 0); ++ _layoutCurrent = aConfig.readNumEntry(TQString("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; ++ ++ TQString key = traceKey(); ++ ++ KConfigGroup pConfig(KGlobal::config(), TQCString("TracePositions")); ++ TQString group = pConfig.readEntry(TQString("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(), ++ TQString("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(), ++ TQString("Layout%1-MainView").arg(from), ++ traceKey(), false); ++ _layoutCount--; ++ ++ updateLayoutActions(); ++} ++ ++void TopLevel::layoutNext() ++{ ++ if (_layoutCount <2) return; ++ ++ KConfig* config = KGlobal::config(); ++ TQString key = traceKey(); ++ ++ _multiView->saveViewConfig(config, ++ TQString("Layout%1-MainView").arg(_layoutCurrent), ++ key, false); ++ _layoutCurrent++; ++ if (_layoutCurrent == _layoutCount) _layoutCurrent = 0; ++ ++ _multiView->readViewConfig(config, ++ TQString("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(); ++ TQString key = traceKey(); ++ ++ _multiView->saveViewConfig(config, ++ TQString("Layout%1-MainView").arg(_layoutCurrent), ++ key, false); ++ _layoutCurrent--; ++ if (_layoutCurrent <0) _layoutCurrent = _layoutCount-1; ++ ++ _multiView->readViewConfig(config, ++ TQString("Layout%1-MainView").arg(_layoutCurrent), ++ key, false); ++ ++ if (0) kdDebug() << "TopLevel::layoutPrevious: current " ++ << _layoutCurrent << endl; ++} ++ ++void TopLevel::layoutSave() ++{ ++ KConfig* config = KGlobal::config(); ++ TQString key = traceKey(); ++ ++ _multiView->saveViewConfig(config, ++ TQString("Layout%1-MainView").arg(_layoutCurrent), ++ key, false); ++ ++ for(int i=0;i<_layoutCount;i++) { ++ _multiView->readViewConfig(config, ++ TQString("Layout%1-MainView").arg(i), ++ key, false); ++ _multiView->saveViewConfig(config, ++ TQString("Layout%1-MainView").arg(i), ++ TQString(), false); ++ } ++ ++ _multiView->readViewConfig(config, ++ TQString("Layout%1-MainView").arg(_layoutCurrent), ++ key, false); ++ ++ KConfigGroup aConfig(config, TQCString("Layouts")); ++ aConfig.writeEntry("DefaultCount", _layoutCount); ++ aConfig.writeEntry("DefaultCurrent", _layoutCurrent); ++} ++ ++void TopLevel::layoutRestore() ++{ ++ KConfig* config = KGlobal::config(); ++ KConfigGroup aConfig(config, TQCString("Layouts")); ++ _layoutCount = aConfig.readNumEntry("DefaultCount", 0); ++ _layoutCurrent = aConfig.readNumEntry("DefaultCurrent", 0); ++ if (_layoutCount == 0) { ++ _layoutCount++; ++ return; ++ } ++ ++ TQString key = traceKey(); ++ for(int i=0;i<_layoutCount;i++) { ++ _multiView->readViewConfig(config, ++ TQString("Layout%1-MainView").arg(i), ++ TQString(), false); ++ _multiView->saveViewConfig(config, ++ TQString("Layout%1-MainView").arg(i), ++ key, false); ++ } ++ ++ _multiView->readViewConfig(config, ++ TQString("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; ++ } ++ ++ TQString status = TQString("%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 += TQString(" - %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(TQString()); ++ ++ // save QT dock positions... ++ ++ // We don't want to save the KToolbar position here. ++ // Its already stored. ++ delete toolBar(); ++ ++ KConfigGroup dockConfig(KGlobal::config(), TQCString("Docks")); ++ TQString str; ++ TQTextStream 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, TQString(), 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() ++{ ++ TQTimer::singleShot( 0, TQT_TQOBJECT(this), TQT_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() ++{ ++ TQTimer::singleShot( 0, TQT_TQOBJECT(this), TQT_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... ++ TQFile cmd("callgrind.cmd"); ++ if (!cmd.exists()) { ++ cmd.open(IO_WriteOnly); ++ cmd.writeBlock("DUMP\n", 5); ++ cmd.close(); ++ } ++ if (_taDump->isChecked()) ++ TQTimer::singleShot( 1000, TQT_TQOBJECT(this), TQT_SLOT(forceTraceReload()) ); ++ else { ++ // cancel request ++ cmd.remove(); ++ } ++ ++} ++ ++void TopLevel::forceTraceReload() ++{ ++// qDebug("forceTraceReload"); ++ ++ TQFile cmd("callgrind.cmd"); ++ if (cmd.exists()) { ++ if (_taDump->isChecked()) ++ TQTimer::singleShot( 1000, TQT_TQOBJECT(this), TQT_SLOT(forceTraceReload()) ); ++ return; ++ } ++ _taDump->setChecked(false); ++ reload(); ++} ++ ++void TopLevel::forwardAboutToShow() ++{ ++ TQPopupMenu *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 (count<Configuration::maxSymbolCount() && hi) { ++ f = hi->function(); ++ if (!f) break; ++ ++ TQString 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() ++{ ++ TQPopupMenu *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 (count<Configuration::maxSymbolCount() && hi) { ++ f = hi->function(); ++ if (!f) break; ++ ++ TQString 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() ++{ ++ TQPopupMenu *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 (count<Configuration::maxSymbolCount() && f) { ++ TQString name = f->prettyName(); ++ 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 TQString& msg, int ms) ++{ ++ if (_statusbar) ++ _statusbar->message(msg, ms); ++} ++ ++void TopLevel::showStatus(TQString 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 TQProgressBar(_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 ++ TQEventLoop* l = tqApp->eventLoop(); ++ if (l) l->processEvents(TQEventLoop::ExcludeUserInput); ++} ++ ++#include "toplevel.moc" +diff --git a/kdecachegrind/kdecachegrind/toplevel.h b/kdecachegrind/kdecachegrind/toplevel.h +new file mode 100644 +index 0000000..10e7cde +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/toplevel.h +@@ -0,0 +1,275 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002, 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * KCachegrind top level window ++ */ ++ ++#ifndef TOPLEVEL_H ++#define TOPLEVEL_H ++ ++#include <tqdatetime.h> ++ ++#include <dcopobject.h> ++#include <kmainwindow.h> ++ ++#include "traceitemview.h" ++#include "tracedata.h" ++ ++class MultiView; ++class TQLineEdit; ++class TQDockWidget; ++class TQLabel; ++class TQProgressBar; ++class TQPopupMenu; ++ ++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 ++ TQ_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(TQPopupMenu*,bool); ++ void addGoMenu(TQPopupMenu*); ++ ++public slots: ++ void newTrace(); ++ void loadTrace(); ++ void loadTrace(const KURL&); ++ void loadTrace(TQString); ++ void addTrace(); ++ void addTrace(const KURL&); ++ void addTrace(TQString); ++ ++ // for quick showing the main window... ++ void loadDelayed(TQString); ++ ++ 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 TQString&); ++ void costType2Selected(const TQString&); ++ 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(TQString); ++ bool setCostType2(TQString); ++ bool setCostType(int); ++ bool setCostType2(int); ++ bool setGroupType(TraceItem::CostType); ++ bool setGroupType(TQString); ++ bool setGroup(TraceCostItem*); ++ bool setGroup(TQString); ++ bool setFunction(TraceFunction*); ++ bool setFunction(TQString); ++ 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(TQString msg, int progress); ++ void showMessage(const TQString&, int msec); ++ ++private: ++ void init(); ++ void createLayoutActions(); ++ void createMiscActions(); ++ void setupMainWidget(MainWidget*); ++ void setupPartSelection(PartSelection*); ++ void restoreCurrentState(TQString postfix); ++ void saveCurrentState(TQString postfix); ++ void saveTraceSettings(); ++ TQString traceKey(); ++ void restoreTraceTypes(); ++ void restoreTraceSettings(); ++ ++ KStatusBar* _statusbar; ++ TQLabel* _statusLabel; ++ KRecentFilesAction* _openRecent; ++ bool _twoMainWidgets; ++ Qt::Orientation _spOrientation; ++ ++ MultiView* _multiView; ++ FunctionSelection* _functionSelection; ++ DumpSelection* _dumpSelection; ++ PartSelection* _partSelection; ++ StackSelection* _stackSelection; ++ TQLineEdit* queryLineEdit; ++ ++ TQDockWindow *_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 TQObject* _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; ++ TQString _loadTraceDelayed; ++ TraceItemView::Direction _directionDelayed; ++ ++ // for status progress display ++ TQString _progressMsg; ++ TQTime _progressStart; ++ TQProgressBar* _progressBar; ++ ++ // toplevel configuration options ++ bool _showPercentage, _showExpanded, _showCycles; ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/tracedata.cpp b/kdecachegrind/kdecachegrind/tracedata.cpp +new file mode 100644 +index 0000000..f129c2e +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/tracedata.cpp +@@ -0,0 +1,5068 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002, 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++ ++#include <stdlib.h> ++ ++#include <tqfile.h> ++#include <tqdir.h> ++#include <tqfileinfo.h> ++#include <tqregexp.h> ++ ++#include <klocale.h> ++#include <kdebug.h> ++ ++#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; ++} ++ ++ ++TQString Addr::toString() const ++{ ++ if (_v == 0) return TQString("0"); ++ ++ uint64 n = _v; ++ TQString hex; ++ hex.reserve(16); ++ ++ while(n>0) { ++ int d = (n & 15); ++ hex = TQChar((d<10) ? ('0'+d) : ('A'-10+d)) + hex; ++ n /= 16; ++ } ++ ++ return hex; ++} ++ ++TQString Addr::pretty() const ++{ ++ if (_v == 0) return TQString("0"); ++ ++ uint64 n = _v; ++ int p = 0; ++ TQString hex; ++ hex.reserve(20); ++ ++ while(n>0) { ++ int d = (n & 15); ++ if ((p>0) && ((p%4)==0)) hex = " " + hex; ++ hex = TQChar((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 ++ ++TQString* TraceItem::_typeName = 0; ++TQString* 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; ++ } ++} ++ ++TQString TraceItem::typeName(CostType t) ++{ ++ if (!_typeName) { ++ _typeName = new TQString [MaxCostType+1]; ++ TQString* strs = _typeName; ++ for(int i=0;i<=MaxCostType;i++) ++ strs[i] = TQString("?"); ++ ++ 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(TQString s) ++{ ++ // This is the default cost Type ++ if (s.isEmpty()) return Function; ++ ++ CostType type; ++ for (int i=0; i<MaxCostType;i++) { ++ type = (CostType) i; ++ if (typeName(type) == s) ++ return type; ++ } ++ return NoCostType; ++} ++ ++// all strings of typeName() are translatable because of I18N_NOOP there ++TQString TraceItem::i18nTypeName(CostType t) ++{ ++ if (!_i18nTypeName) { ++ _i18nTypeName = new TQString [MaxCostType+1]; ++ for(int i=0;i<=MaxCostType;i++) ++ _i18nTypeName[i] = i18n(typeName((CostType)i).utf8().data()); ++ } ++ if (t<0 || t> MaxCostType) t = MaxCostType; ++ return _i18nTypeName[t]; ++} ++ ++TraceItem::CostType TraceItem::i18nCostType(TQString s) ++{ ++ // This is the default cost Type ++ if (s.isEmpty()) return Function; ++ ++ CostType type; ++ for (int i=0; i<MaxCostType;i++) { ++ type = (CostType) i; ++ if (i18nTypeName(type) == s) ++ return type; ++ } ++ return NoCostType; ++} ++ ++ ++void TraceItem::clear() ++{ ++ invalidate(); ++} ++ ++ ++TQString TraceItem::costString(TraceCostMapping*) ++{ ++ return TQString("(no cost)"); ++} ++ ++TQString TraceItem::name() const ++{ ++ if (part()) { ++ return i18n("%1 from %2") ++ .arg(_dep->name()) ++ .arg(part()->name()); ++ } ++ ++ if (_dep) ++ return _dep->name(); ++ ++ return i18n("(unknown)"); ++} ++ ++TQString TraceItem::prettyName() const ++{ ++ if (name().isEmpty()) return i18n("(unknown)"); ++ return name(); ++} ++ ++ ++TQString TraceItem::fullName() const ++{ ++ return TQString("%1 %2") ++ .arg(typeName(type())).arg(prettyName()); ++} ++ ++TQString TraceItem::toString() ++{ ++ return TQString("%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(i<sm->count()) { ++ if (!_cost[i].set(&s)) break; ++ i++; ++ } ++ _count = i; ++ } ++ else { ++ int i = 0, maxIndex = 0, index; ++ while(1) { ++ index = sm->realIndex(i); ++ if (maxIndex<index) maxIndex=index; ++ if (index == TraceCost::InvalidIndex) break; ++ if (!_cost[index].set(&s)) break; ++ i++; ++ } ++ // we have to set all costs of unused indexes till maxIndex to zero ++ for(i=sm->firstUnused(); 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(i<sm->count()) { ++ if (!s.stripUInt64(_cost[i])) break; ++ i++; ++ } ++ _count = i; ++ } ++ else { ++ int i = 0, maxIndex = 0, index; ++ while(1) { ++ index = sm->realIndex(i); ++ if (maxIndex<index) maxIndex=index; ++ if (index == TraceCost::InvalidIndex) break; ++ if (!s.stripUInt64(_cost[index])) break; ++ i++; ++ } ++ // we have to set all costs of unused indexes till maxIndex to zero ++ for(i=sm->firstUnused(); 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(i<sm->count()) { ++ 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<index) maxIndex=index; ++ if (index == TraceCost::InvalidIndex) break; ++ if (index<_count) ++ _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; ++ } ++ } ++ ++ // 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(i<sm->count()) { ++ 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<index) maxIndex=index; ++ if (index == TraceCost::InvalidIndex) break; ++ if (index<_count) ++ _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 ++} ++ ++ ++// 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(i<sm->count()) { ++ 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<index) maxIndex=index; ++ if (index == TraceCost::InvalidIndex) break; ++ if (index<_count) { ++ if (v>_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<item->_count; i++) ++ _cost[i] += item->_cost[i]; ++ } ++ else { ++ for (i = 0; i<_count; i++) ++ _cost[i] += item->_cost[i]; ++ for (; i<item->_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<item->_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<item->_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<type;i++) ++ _cost[i] = 0; ++ _cost[type] = value; ++ _count = type+1; ++ } ++ ++ // a cost change has to be propagated (esp. in subclasses) ++ invalidate(); ++} ++ ++void TraceCost::maxCost(int type, SubCost value) ++{ ++ if (type<0 || type>=MaxRealIndex) return; ++ if (type<_count) { ++ if (value>_cost[type]) _cost[type] = value; ++ } ++ else { ++ for(int i=_count;i<type;i++) ++ _cost[i] = 0; ++ _cost[type] = value; ++ _count = type+1; ++ } ++ ++ // a cost change has to be propagated (esp. in subclasses) ++ invalidate(); ++} ++ ++ ++TraceCost TraceCost::diff(TraceCost* item) ++{ ++ TraceCost res; ++ ++ // we have to update the other item if needed ++ // because we access the item costs directly ++ if (item->_dirty) item->update(); ++ ++ int maxCount = (item->_count > _count) ? item->_count : _count; ++ ++ res._count = maxCount; ++ for (int i=0; i<maxCount;i++) ++ res._cost[i] = item->subCost(i) - subCost(i); ++ ++ return res; ++} ++ ++TQString TraceCost::costString(TraceCostMapping* m) ++{ ++ TQString res; ++ ++ if (_dirty) update(); ++ ++ int maxIndex = m ? m->realCount() : TraceCost::MaxRealIndex; ++ for (int i = 0; i<maxIndex; i++) { ++ if (!res.isEmpty()) res += ", "; ++ if (m) res += m->type(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; ++} ++ ++TQString 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; ++} ++ ++TQString TraceJumpCost::costString(TraceCostMapping*) ++{ ++ if (_dirty) update(); ++ ++ return TQString("%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 ++ ++TQPtrList<TraceCostType>* TraceCostType::_knownTypes = 0; ++ ++TraceCostType::TraceCostType(TQString name, TQString longName, TQString formula) ++{ ++ _name = name; ++ _longName = longName; ++ _formula = formula; ++ _mapping = 0; ++ _realIndex = TraceCost::InvalidIndex; ++ _parsed = false; ++ _inParsing = false; ++ ++ for (int i=0; i<TraceCost::MaxRealIndex;i++) ++ _coefficient[i] = 0; ++} ++ ++void TraceCostType::setFormula(TQString formula) ++{ ++ _formula = formula; ++ _realIndex = TraceCost::InvalidIndex; ++ _parsed = false; ++} ++ ++void TraceCostType::setMapping(TraceCostMapping* m) ++{ ++ _parsed = false; ++ _mapping = m; ++} ++ ++// setting the index to TraceCost::MaxRealIndex makes it a ++// real type with unspecified index ++void TraceCostType::setRealIndex(int i) ++{ ++ if (i<0 || i>TraceCost::MaxRealIndex) ++ i=TraceCost::InvalidIndex; ++ ++ _realIndex = i; ++ _formula = TQString(); ++} ++ ++// 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; i<TraceCost::MaxRealIndex;i++) ++ _coefficient[i] = 0; ++ ++ TQRegExp rx( "((?:\\+|\\-)?)\\s*(\\d*)\\s*\\*?\\s*(\\w+)" ); ++ ++ int factor, pos; ++ TQString costName; ++ TraceCostType* costType; ++ ++ pos = 0; ++ while (1) { ++ pos = rx.search(_formula, pos); ++ if (pos<0) break; ++ pos += rx.matchedLength(); ++ if (rx.cap(0).isEmpty()) break; ++ ++ //qDebug("parseFormula: matched '%s','%s','%s'", ++ // rx.cap(1).ascii(), rx.cap(2).ascii(), rx.cap(3).ascii()); ++ ++ costName = rx.cap(3); ++ costType = _mapping->type(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<TraceCost::MaxRealIndex;i++) ++ _coefficient[i] += factor * costType->_coefficient[i]; ++ } ++ } ++ ++ _inParsing = false; ++ _parsed = true; ++ ++ return true; ++} ++ ++TQString TraceCostType::parsedFormula() ++{ ++ TQString res; ++ ++ if (!parseFormula()) return res; ++ ++ for (int i=0; i<TraceCost::MaxRealIndex;i++) { ++ int c = _coefficient[i]; ++ if (c == 0) continue; ++ ++ if (!res.isEmpty()) { ++ res += " "; ++ if (c>0) res += "+ "; ++ } ++ if (c<0) { res += "- "; c = -c; } ++ res += TQString::number(c); ++ ++ TraceCostType* t = _mapping->type(i); ++ if (!t) continue; ++ ++ if (!t->name().isEmpty()) ++ res += TQString(" * %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;i<rc;i++) ++ if (_coefficient[i] != 0) ++ res += _coefficient[i] * c->subCost(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;i<rc;i++) { ++ if (_coefficient[i] != 0) ++ hist[i] = _coefficient[i] * c->subCost(i) / total; ++ else ++ hist[i] = 0.0; ++ } ++ ++ return rc; ++} ++ ++ ++ ++ ++TraceCostType* TraceCostType::knownRealType(TQString 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(TQString 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 TQPtrList<TraceCostType>; ++ ++ /* 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(TQString 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); ++} ++ ++TQColor TraceCostType::color() ++{ ++ if (!_mapping) return TQColor(); ++ return _mapping->realColors()[_realIndex]; ++} ++ ++ ++//--------------------------------------------------- ++// TraceCostMapping ++ ++TraceCostMapping::TraceCostMapping() ++{ ++ _realCount = 0; ++ _virtualCount = 0; ++ for (int i=0;i<TraceCost::MaxRealIndex;i++) _real[i] = 0; ++ for (int i=0;i<TraceCost::MaxRealIndex;i++) _virtual[i] = 0; ++} ++ ++TraceCostMapping::~TraceCostMapping() ++{ ++ for (int i=0;i<TraceCost::MaxRealIndex;i++) ++ if (_real[i]) delete _real[i]; ++ ++ for (int i=0;i<TraceCost::MaxRealIndex;i++) ++ if (_virtual[i]) delete _virtual[i]; ++} ++ ++TraceSubMapping* TraceCostMapping::subMapping(TQString types, bool create) ++{ ++ // first check if there's enough space in the mapping ++ int newCount = 0; ++ int pos = 0, pos2, len = types.length(); ++ ++ while (1) { ++ // skip space ++ while((pos<len) && types[pos].isSpace()) pos++; ++ ++ pos2 = pos; ++ while((pos2<len) && !types[pos2].isSpace()) pos2++; ++ if (pos2 == pos) break; ++ ++ if (realIndex(types.mid(pos,pos2-pos)) == TraceCost::InvalidIndex) ++ newCount++; ++ ++ pos = pos2; ++ } ++ ++ if (!create && (newCount>0)) 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((pos<len) && types[pos].isSpace()) pos++; ++ ++ pos2 = pos; ++ while((pos2<len) && !types[pos2].isSpace()) pos2++; ++ if (pos2 == pos) break; ++ ++ sm->append(addReal(types.mid(pos,pos2-pos))); ++ ++ pos = pos2; ++ } ++ ++ return sm; ++} ++ ++ ++int TraceCostMapping::addReal(TQString 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(TQString 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(TQString 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(TQString name) ++{ ++ for (int i=0;i<_realCount;i++) ++ if (_real[i] && (_real[i]->name() == name)) ++ return i; ++ ++ return TraceCost::InvalidIndex; ++} ++ ++int TraceCostMapping::index(TQString 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; i<knownCount; i++) { ++ TraceCostType* t = TraceCostType::knownType(i); ++ if (t->isReal()) 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;i<TraceCost::MaxRealIndex;i++) { ++ _realIndex[i] = TraceCost::InvalidIndex; ++ _nextUnused[i] = i+1; ++ } ++} ++ ++bool TraceSubMapping::append(TQString type, bool create) ++{ ++ if (!_mapping) return false; ++ int index = create ? _mapping->addReal(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<type;i++) ++ if (_nextUnused[i] == type) ++ _nextUnused[i]=_nextUnused[type]; ++ ++ _count++; ++ return true; ++} ++ ++ ++//--------------------------------------------------- ++// TraceCallCost ++ ++TraceCallCost::TraceCallCost() ++{ ++ _callCount = 0; ++} ++ ++TraceCallCost::~TraceCallCost() ++{} ++ ++ ++TQString TraceCallCost::costString(TraceCostMapping* m) ++{ ++ return TQString("%1, Calls %2") ++ .arg(TraceCost::costString(m)) ++ .arg(_callCount.pretty()); ++} ++ ++TQString TraceCallCost::prettyCallCount() ++{ ++ return _callCount.pretty(); ++} ++ ++void TraceCallCost::clear() ++{ ++ _callCount = 0; ++ TraceCost::clear(); ++} ++ ++SubCost TraceCallCost::callCount() ++{ ++ if (_dirty) update(); ++ ++ return _callCount; ++} ++ ++void TraceCallCost::addCallCount(SubCost c) ++{ ++ _callCount += c; ++ ++ invalidate(); ++} ++ ++ ++//--------------------------------------------------- ++// TraceInclusiveCost ++ ++TraceInclusiveCost::TraceInclusiveCost() ++{} ++ ++TraceInclusiveCost::~TraceInclusiveCost() ++{} ++ ++TQString TraceInclusiveCost::costString(TraceCostMapping* m) ++{ ++ return TQString("%1, Inclusive %2") ++ .arg(TraceCost::costString(m)) ++ .arg(_inclusive.costString(m)); ++} ++ ++void TraceInclusiveCost::clear() ++{ ++ _inclusive.clear(); ++ TraceCost::clear(); ++} ++ ++TraceCost* TraceInclusiveCost::inclusive() ++{ ++ if (_dirty) update(); ++ ++ return &_inclusive; ++} ++ ++void TraceInclusiveCost::addInclusive(TraceCost* c) ++{ ++ _inclusive.addCost(c); ++ ++ invalidate(); ++} ++ ++ ++//--------------------------------------------------- ++// TraceListCost ++ ++TraceListCost::TraceListCost() ++{ ++ _lastDep = 0; ++} ++ ++TraceListCost::~TraceListCost() ++{} ++ ++void TraceListCost::addDep(TraceCost* 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 ++} ++ ++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() ++{} ++ ++TQString TracePartFunction::prettyCalledCount() ++{ ++ return _calledCount.pretty(); ++} ++ ++TQString TracePartFunction::prettyCallingCount() ++{ ++ return _callingCount.pretty(); ++} ++ ++TQString TracePartFunction::costString(TraceCostMapping* m) ++{ ++ update(); ++ ++ TQString res = TraceInclusiveCost::costString(m); ++ res += TQString(", called from %1: %2") ++ .arg(_calledContexts).arg(prettyCalledCount()); ++ res += TQString(", 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() ++{} ++ ++TQString TracePartClass::prettyName() const ++{ ++ return TQString("%1 from %2") ++ .arg( _dep->name().isEmpty() ? TQString("(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 ++} ++ ++TQString TraceInstrJump::name() const ++{ ++ return TQString("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; ++} ++ ++ ++TQString TraceLineJump::name() const ++{ ++ return TQString("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; ++} ++ ++ ++TQString TraceInstrCall::name() const ++{ ++ return TQString("%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; ++} ++ ++ ++TQString TraceLineCall::name() const ++{ ++ return TQString("%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(); ++} ++ ++ ++TQString TraceCall::name() const ++{ ++ return TQString("%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; ++} ++ ++TQString 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)) { ++ TQString via = _called->prettyName(); ++ return i18n("%1 via %2").arg(_caller->prettyName()).arg(via); ++ } ++ } ++ ++ return _caller->prettyName(); ++} ++ ++TQString 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); ++ TQString 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 ++} ++ ++ ++TQString TraceInstr::name() const ++{ ++ return TQString("0x%1").arg(_addr.toString()); ++} ++ ++TQString TraceInstr::prettyName() const ++{ ++ return TQString("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 ++} ++ ++ ++TQString TraceLine::name() const ++{ ++ TQString fileShortName = _sourceFile->file()->shortName(); ++ if (fileShortName.isEmpty()) ++ return i18n("(unknown)"); ++ ++ return TQString("%1:%2") ++ .arg(fileShortName).arg(_lineno); ++} ++ ++TQString TraceLine::prettyName() const ++{ ++ return TQString("%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; ++} ++ ++TQString TraceFunctionSource::name() const ++{ ++ return TQString("%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(TQString 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; ++} ++ ++ ++TQString TraceFunction::prettyName() const ++{ ++ TQString 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 = TQString("%1 <cycle %2>").arg(res).arg(_cycle->cycleNo()); ++ else ++ res = TQString("<cycle %2>").arg(_cycle->cycleNo()); ++ } ++ ++ ++ return res; ++} ++ ++/* ++ * Returns location string: ELF object and source file(s). ++ */ ++TQString TraceFunction::location(int maxFiles) const ++{ ++ TQString 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 += TQString(" (0x%1)").arg(to, 0, 16); ++ else ++ loc += TQString(" (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 += TQString(" (%1)").arg(to); ++ else ++ loc += TQString(" (%1-%2)").arg(from).arg(to); ++ } ++#endif ++ } ++ ++ return loc; ++} ++ ++// pretty version is allowed to mangle the string... ++TQString TraceFunction::prettyLocation(int maxFiles) const ++{ ++ TQString l = location(maxFiles); ++ if (l.isEmpty()) return i18n("(unknown)"); ++ ++ return l; ++} ++ ++void TraceFunction::addPrettyLocation(TQString& s, int maxFiles) const ++{ ++ TQString l = location(maxFiles); ++ if (l.isEmpty()) return; ++ ++ s += TQString(" (%1)").arg(l); ++} ++ ++TQString TraceFunction::prettyNameWithLocation(int maxFiles) const ++{ ++ TQString l = location(maxFiles); ++ if (l.isEmpty()) return prettyName(); ++ ++ return TQString("%1 (%2)").arg(prettyName()).arg(l); ++} ++ ++TQString TraceFunction::info() const ++{ ++ TQString l = location(); ++ if (l.isEmpty()) ++ return TQString("Function %1").arg(name()); ++ ++ return TQString("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; ++} ++ ++TQString TraceFunction::prettyCalledCount() ++{ ++ return _calledCount.pretty(); ++} ++ ++TQString 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)", ++ TQString().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", ++ TQString().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)", ++ TQString().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)", ++ TQString().fill(' ', d+1).ascii(), d+1, ++ called->prettyName().ascii(), called->_cycleLow); ++ } ++ else { ++ if (0) ++ qDebug("%s D%02d - %s (%d) [Not on stack]", ++ TQString().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)", ++ TQString().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(TQString("<cycle %1>").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() ++{} ++ ++TQString TraceClass::prettyName() const ++{ ++ if (_name.isEmpty()) ++ return TQString("(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 TQString& dir) ++{ ++ if (dir.endsWith("/")) ++ _dir = dir.left(dir.length()-1); ++ else ++ _dir = dir; ++} ++ ++TQString TraceFile::directory() ++{ ++ if (!_dir.isEmpty()) return _dir; ++ ++ int lastIndex = 0, index; ++ while ( (index=_name.find("/", lastIndex)) >=0) ++ lastIndex = index+1; ++ ++ if (lastIndex==0) return TQString(); ++ ++ // without ending "/" ++ return _name.left(lastIndex-1); ++} ++ ++ ++TQString TraceFile::shortName() const ++{ ++ int lastIndex = 0, index; ++ while ( (index=_name.find("/", lastIndex)) >=0) ++ lastIndex = index+1; ++ ++ return _name.mid(lastIndex); ++} ++ ++TQString TraceFile::prettyName() const ++{ ++ TQString sn = shortName(); ++ ++ if (sn.isEmpty()) ++ return i18n("(unknown)"); ++ ++ return sn; ++} ++ ++TQString 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 TQString& name) ++{ ++ _name = name; ++ ++ int lastIndex = 0, index; ++ while ( (index=_name.find("/", lastIndex)) >=0) ++ lastIndex = index+1; ++ ++ _shortName = _name.mid(lastIndex); ++} ++ ++TQString TraceObject::prettyName() const ++{ ++ if (_shortName.isEmpty()) ++ return i18n("(unknown)"); ++ ++ return _shortName; ++} ++ ++//--------------------------------------------------- ++// TracePart ++ ++TracePart::TracePart(TraceData* data, TQFile* 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() <n) data()->setMaxPartNumber(n); ++ _number = n; ++} ++ ++void TracePart::setThreadID(int tid) ++{ ++ if (data()->maxThreadID() <tid) data()->setMaxThreadID(tid); ++ _tid = tid; ++} ++ ++void TracePart::setProcessID(int pid) ++{ ++ _pid = pid; ++} ++ ++ ++ ++// strip path ++TQString TracePart::shortName() const ++{ ++ int lastIndex = 0, index; ++ while ( (index=_name.find("/", lastIndex)) >=0) ++ lastIndex = index+1; ++ ++ return _name.mid(lastIndex); ++} ++ ++TQString TracePart::prettyName() const ++{ ++ TQString name = TQString("%1.%2").arg(_pid).arg(_number); ++ if (data()->maxThreadID()>1) ++ name += TQString("-%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()); ++} ++ ++TQString TracePartList::names() const ++{ ++ TQString 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 TQString& 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; ++} ++ ++TQString 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: ++ * ++ * - <base> is a directory: Load first profile data file available ++ * - <base> is a file name without part/thread suffixes ++ */ ++void TraceData::load(const TQString& base) ++{ ++ bool baseExisting = true; ++ ++ _traceName = base; ++ TQFileInfo finfo(base); ++ TQString file = finfo.fileName(); ++ TQDir dir = finfo.dir(); ++ ++ if (!finfo.exists()) { ++ baseExisting = false; ++ } ++ else if (finfo.isDir()) { ++ // search for first profile data file in directory ++ dir = TQDir(base); ++ ++ TQStringList prefixList; ++ prefixList << "callgrind.out" << "cachegrind.out"; ++ for ( TQStringList::Iterator it = prefixList.begin(); ++ it != prefixList.end(); ++it ) { ++ file = *it; ++ ++ // search for ".pid" ++ TQStringList strList = dir.entryList(file+".*", TQDir::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; ++ } ++ ++ TQStringList strList; ++ strList += dir.entryList(file+".*", TQDir::Files); ++ strList += dir.entryList(file+"-*", TQDir::Files); ++ ++ baseExisting = TQFile::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; ++ } ++ ++ TQStringList::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 TQString& 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 ((int)str.at(pos) < '0' || (int)str.at(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 ((int)str.at(pos) < '0' || (int)str.at(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(TQString(), 0); ++} ++ ++TracePart* TraceData::addPart(const TQString& dir, const TQString& name) ++{ ++ TQString filename = TQString("%1/%2").arg(dir).arg(name); ++#if TRACE_DEBUG ++ qDebug("TraceData::addPart('%s')", filename.ascii()); ++#endif ++ ++ TQFile* file = new TQFile(filename); ++ ++ Loader* l = Loader::matchingLoader(file); ++ if (!l) return 0; ++ ++ if (_topLevel) ++ _topLevel->connect(l, TQT_SIGNAL(updateStatus(TQString, int)), ++ TQT_SLOT(showStatus(TQString, 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(TQString& name) ++{ ++ TracePart* part; ++ for (part=_parts.first();part;part=_parts.next()) ++ if (part->name() == name) ++ return part; ++ return 0; ++} ++ ++TQString TraceData::activePartRange() ++{ ++ TQString 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 += TQString::number(r1); ++ else res += TQString("%1-%2").arg(r1).arg(r2); ++ r1 = r2 = count; ++ } ++ } ++ if (r1>=0) { ++ if (!res.isEmpty()) res += ";"; ++ if (r1==r2) res += TQString::number(r1); ++ else res += TQString("%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 TQString& 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 TQString& 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 TQString& fnName, TQString& 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 && pIndex<index) break; ++ lastIndex = index+2; ++ } ++ ++ TQString clsName = (lastIndex < 3) ? TQString() : ++ fnName.left(lastIndex-2); ++ shortName = fnName.mid(lastIndex); ++ ++ TraceClass& c = _classMap[clsName]; ++ if (!c.data()) { ++ // was created ++ c.setPosition(this); ++ c.setName(clsName); ++ ++#if TRACE_DEBUG ++ qDebug("Created %s [TraceData::cls]", ++ c.fullName().ascii()); ++#endif ++ } ++ return &c; ++} ++ ++ ++// name is inclusive class/namespace prefix ++TraceFunction* TraceData::function(const TQString& name, ++ TraceFile* file, TraceObject* object) ++{ ++ // strip class name ++ TQString shortName; ++ TraceClass* c = cls(name, shortName); ++ ++ if (!file || !object || !c) { ++ qDebug("ERROR - no file/object/class for %s ?!", name.ascii()); ++ return 0; ++ } ++ ++ // Don't use file in key: A function can go over many files ++ // (inlined parts), but still is ONE function. ++ TQString key = name + object->shortName(); ++ ++ 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 !! ++ TQString 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, TQString 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; ++ TQString 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(TQString(),0); ++#endif ++} ++ ++void TraceData::updateObjectCycles() ++{ ++} ++ ++ ++void TraceData::updateClassCycles() ++{ ++} ++ ++ ++void TraceData::updateFileCycles() ++{ ++} ++ ++ +diff --git a/kdecachegrind/kdecachegrind/tracedata.h b/kdecachegrind/kdecachegrind/tracedata.h +new file mode 100644 +index 0000000..8fab2b6 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/tracedata.h +@@ -0,0 +1,1967 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002, 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Classes holding profiling data for ++ * multiple tracefiles for one command. ++ * See class TraceData first. ++ */ ++ ++#ifndef TRACEDATA_H ++#define TRACEDATA_H ++ ++#include <tqstring.h> ++#include <tqstringlist.h> ++#include <tqptrlist.h> ++#include <tqmap.h> ++#include <tqptrvector.h> ++#include <tqcolor.h> ++ ++#include "subcost.h" ++#include "utils.h" ++ ++class TQFile; ++ ++/** ++ * 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 TQPtrList<TraceCost> TraceCostList; ++typedef TQPtrList<TraceJumpCost> TraceJumpCostList; ++typedef TQPtrList<TraceCallCost> TraceCallCostList; ++typedef TQPtrList<TraceInclusiveCost> TraceInclusiveCostList; ++ ++typedef TQPtrList<TracePartCall> TracePartCallList; ++typedef TQPtrList<TracePartInstr> TracePartInstrList; ++typedef TQPtrList<TracePartLine> TracePartLineList; ++typedef TQPtrList<TracePartLineRegion> TracePartLineRegionList; ++typedef TQPtrList<TracePartFunction> TracePartFunctionList; ++typedef TQPtrList<TracePartInstrCall> TracePartInstrCallList; ++typedef TQPtrList<TracePartLineCall> TracePartLineCallList; ++ ++ ++typedef TQPtrList<TraceInstr> TraceInstrList; ++typedef TQPtrList<TraceLine> TraceLineList; ++typedef TQPtrList<TraceInstrCall> TraceInstrCallList; ++typedef TQPtrList<TraceLineCall> TraceLineCallList; ++typedef TQPtrList<TraceCall> TraceCallList; ++typedef TQPtrList<TraceFile> TraceFileList; ++typedef TQPtrList<TraceLineRegion> TraceLineRegionList; ++typedef TQPtrList<TraceFunctionSource> TraceFunctionSourceList; ++typedef TQPtrList<TraceFunction> TraceFunctionList; ++typedef TQPtrList<TraceFunctionCycle> TraceFunctionCycleList; ++typedef TQMap<TQString, TraceObject> TraceObjectMap; ++typedef TQMap<TQString, TraceClass> TraceClassMap; ++typedef TQMap<TQString, TraceFile> TraceFileMap; ++typedef TQMap<TQString, TraceFunction> TraceFunctionMap; ++typedef TQMap<uint, TraceLine> 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); ++ TQString toString() const; ++ // similar to toString(), but adds a space every 4 digits ++ TQString 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 TQMap<Addr, TraceInstr> 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 TQString typeName(CostType); ++ static CostType costType(TQString); ++ // the versions below should be used for user visible strings, as ++ // these use localisation settings ++ static TQString i18nTypeName(CostType); ++ static CostType i18nCostType(TQString); ++ // clean up some static data ++ static void cleanup(); ++ ++ /** ++ * Returns dynamic name info (without type) ++ */ ++ virtual TQString name() const; ++ ++ /** ++ * Same as name, but sometimes nicer for humans :-) ++ */ ++ virtual TQString prettyName() const; ++ ++ /** ++ * Returns text of all cost metrics ++ */ ++ virtual TQString costString(TraceCostMapping*); ++ ++ /** ++ * Returns type name + dynamic name ++ */ ++ TQString fullName() const; ++ ++ /** ++ * Returns full name + cost text ++ */ ++ TQString 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 TQString *_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 TQString 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) ++ */ ++ TQString 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: ++ ++ /** ++ * <name> is a short (non-localized) identifier for the cost type, ++ * e.g. "l1rm". ++ * <longName> is a long localized string, e.g. "L1 Read Miss" ++ * <formula> uses short names to reference other types ++ */ ++ TraceCostType(TQString name, ++ TQString longName = TQString(), ++ TQString formula = TQString()); ++ ++ void setName(TQString n) { _name = n; } ++ void setLongName(TQString n) { _longName = n; } ++ void setMapping(TraceCostMapping* m); ++ void setFormula(TQString); ++ // default arg is for specifying a real type, but index unknown ++ void setRealIndex(int r = TraceCost::MaxRealIndex); ++ ++ const TQString& name() { return _name; } ++ const TQString& longName() { return _longName; } ++ const TQString& formula() { return _formula; } ++ TraceCostMapping* mapping() { return _mapping; } ++ int realIndex() { return _realIndex; } ++ bool isReal() { return _formula.isEmpty(); } ++ TQColor color(); ++ ++ /* ++ * returns true if all cost type names can be resolved in formula ++ */ ++ bool parseFormula(); ++ TQString 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(TQString); ++ static TraceCostType* knownVirtualType(TQString); ++ static void add(TraceCostType*); ++ static bool remove(TQString); ++ static int knownTypeCount(); ++ static TraceCostType* knownType(int); ++ ++private: ++ ++ TQString _name, _longName, _formula; ++ TraceCostMapping* _mapping; ++ bool _parsed, _inParsing; ++ // index MaxRealIndex is for constant addition ++ int _coefficient[MaxRealIndexValue]; ++ int _realIndex; ++ ++ static TQPtrList<TraceCostType>* _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 <create> is false, checks if this is a existing sub mapping. ++ */ ++ TraceSubMapping* subMapping(TQString types, bool create = true); ++ ++ // "knows" about some real types ++ int addReal(TQString); ++ 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(TQString); ++ TraceCostType* typeForLong(TQString); ++ int realIndex(TQString); ++ int index(TQString); ++ TQColor* 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]; ++ TQColor _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(TQString, 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 TQString 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 TQString costString(TraceCostMapping* m); ++ virtual void clear(); ++ ++ // additional cost metric ++ SubCost callCount(); ++ TQString 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 TQString 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 TQString 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(); ++ TQString prettyCalledCount(); ++ TQString 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; } ++ TQString 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*, TQFile* file); ++ virtual ~TracePart(); ++ ++ virtual CostType type() const { return Part; } ++ virtual TracePart* part() { return this; } ++ virtual const TracePart* part() const { return this; } ++ ++ TQString shortName() const; ++ TQString prettyName() const; ++ TQFile* file() const { return _file; } ++ TQString name() const { return _name; } ++ TQString description() const { return _descr; } ++ TQString trigger() const { return _trigger; } ++ TQString timeframe() const { return _timeframe; } ++ TQString version() const { return _version; } ++ int partNumber() { return _number; } ++ int threadID() { return _tid; } ++ int processID() { return _pid; } ++ void setDescription(const TQString& d) { _descr = d; } ++ void setTrigger(const TQString& t) { _trigger = t; } ++ void setTimeframe(const TQString& t) { _timeframe = t; } ++ void setVersion(const TQString& 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: ++ TQFile* _file; ++ TQString _name; ++ TQString _descr; ++ TQString _trigger; ++ TQString _timeframe; ++ TQString _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 TQPtrList<TracePart> ++{ ++ public: ++ TQString 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 TQString 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 TQPtrList<TraceInstrJump> ++{ ++ 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 TQString 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 TQPtrList<TraceLineJump> ++{ ++ 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 TQString 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 TQString 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 TQString 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; ++ TQString callerName(bool skipCycle=false) const; ++ TQString 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 TQString name() const; ++ TQString 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 TQString name() const; ++ TQString 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 TQString name() const { return _name; } ++ virtual void setName(const TQString& name) { _name = name; } ++ ++ protected: ++ bool onlyActiveParts() { return true; } ++ ++ protected: ++ TQString _name; ++}; ++ ++ ++/** ++ * Cost of a source region. ++ */ ++class TraceLineRegion: public TraceInclusiveListCost ++{ ++public: ++ TraceLineRegion(uint from, uint to, TQString name); ++ virtual ~TraceLineRegion(); ++ ++ virtual CostType type() const { return LineRegion; } ++ virtual void update(); ++ ++ uint from() const { return _from; } ++ uint to() const { return _to; } ++ TQString name() const { return _name; } ++ ++ // factories ++ TracePartLine* partLineRegion(TracePart* part, ++ TracePartFunction* partFunction); ++ private: ++ uint _from, _to; ++ TQString _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 TQString 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, TQString 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 TQPtrList<TraceAssoziation> TraceAssoziationList; ++typedef TQMap<TraceFunction*, TraceCall*> 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 TQString& 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 ++ */ ++ TQString location(int maxFiles = 0) const; ++ ++ TQString prettyName() const; ++ TQString prettyLocation(int maxFiles = 0) const; ++ TQString prettyNameWithLocation(int maxFiles = 1) const; ++ void addPrettyLocation(TQString&, int maxFiles = 1) const; ++ // type + name + location ++ TQString 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(); ++ TQString prettyCalledCount(); ++ TQString 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(TQString) 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 TQMap<TraceFunction*,int> 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 TQString 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 TQString& dir); ++ void resetDirectory() { _dir = TQString(); } ++ TQString directory(); ++ ++ void addFunction(TraceFunction*); ++ void addSourceFile(TraceFunctionSource*); ++ ++ // without path ++ TQString shortName() const; ++ TQString prettyName() const; ++ TQString prettyLongName() const; ++ const TraceFunctionList& functions() const { return _functions; } ++ const TraceFunctionSourceList& sourceFiles() const ++ { return _sourceFiles; } ++ ++ // part factory ++ TracePartFile* partFile(TracePart*); ++ ++ private: ++ TraceFunctionList _functions; ++ TraceFunctionSourceList _sourceFiles; ++ TQString _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 TQString& name); ++ TQString shortName() const { return _shortName; } ++ TQString prettyName() const; ++ const TraceFunctionList& functions() const { return _functions; } ++ ++ // part factory ++ TracePartObject* partObject(TracePart*); ++ ++ private: ++ TraceFunctionList _functions; ++ TQString _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 TQString& 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 TQString&); ++ ++ /** 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(TQString& name); ++ ++ // with path ++ TQString traceName() const { return _traceName; } ++ ++ // without path ++ TQString shortTraceName() const; ++ TQString activePartRange(); ++ ++ TraceCostMapping* mapping() { return &_mapping; } ++ ++ // memory pools ++ FixPool* fixPool(); ++ DynPool* dynPool(); ++ ++ // factories for object/file/class/function/line instances ++ TraceObject* object(const TQString& name); ++ TraceFile* file(const TQString& name); ++ TraceClass* cls(const TQString& fnName, TQString& shortName); ++ // function creation involves class creation if needed ++ TraceFunction* function(const TQString& 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, TQString, ++ 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 TQString& command) { _command = command; } ++ TQString 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 TQString& dir, const TQString& 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; ++ TQString _command; ++ TQString _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/kdecachegrind/kdecachegrind/traceitemview.cpp b/kdecachegrind/kdecachegrind/traceitemview.cpp +new file mode 100644 +index 0000000..d11f02b +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/traceitemview.cpp +@@ -0,0 +1,443 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Trace Item View ++ */ ++ ++#include <tqwidget.h> ++#include <kconfig.h> ++#include <klocale.h> ++#include <kdebug.h> ++ ++#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; ++} ++ ++TQString TraceItemView::whatsThis() const ++{ ++ return i18n("No description available"); ++} ++ ++void TraceItemView::select(TraceItem* i) ++{ ++ _newSelectedItem = i; ++} ++ ++KConfigGroup* TraceItemView::configGroup(KConfig* c, ++ TQString group, TQString post) ++{ ++ TQStringList gList = c->groupList(); ++ if (gList.contains((group+post).ascii()) ) group += post; ++ return new KConfigGroup(c, group); ++} ++ ++void TraceItemView::writeConfigEntry(KConfigBase* c, const char* pKey, ++ TQString value, const char* def, bool bNLS) ++{ ++ if (!c) return; ++ if ((value.isEmpty() && ((def == 0) || (*def == 0))) || ++ (value == TQString(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*, TQString, TQString, bool) ++{} ++ ++#if 1 ++void TraceItemView::saveViewConfig(KConfig*, TQString, TQString, bool) ++{} ++#else ++void TraceItemView::saveViewConfig(KConfig* c, ++ TQString prefix, TQString 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() ++{ ++ TQWidget* 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(TQPopupMenu* p, bool withCost2) ++{ ++ if (_topLevel) _topLevel->addCostMenu(p, withCost2); ++} ++ ++void TraceItemView::addGoMenu(TQPopupMenu* p) ++{ ++ if (_topLevel) _topLevel->addGoMenu(p); ++} +diff --git a/kdecachegrind/kdecachegrind/traceitemview.h b/kdecachegrind/kdecachegrind/traceitemview.h +new file mode 100644 +index 0000000..f83aa89 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/traceitemview.h +@@ -0,0 +1,206 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Trace Item View ++ */ ++ ++#ifndef TRACEITEMVIEW_H ++#define TRACEITEMVIEW_H ++ ++#include "tracedata.h" ++ ++class TQWidget; ++class TQPopupMenu; ++ ++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 ++ * - <dataChanged> 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 TQString whatsThis() const; ++ ++ static KConfigGroup* configGroup(KConfig*, TQString prefix, TQString postfix); ++ static void writeConfigEntry(KConfigBase*, const char* pKey, TQString 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*, TQString prefix, TQString postfix, ++ bool withOptions); ++ virtual void saveViewConfig(KConfig*, TQString prefix, TQString 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(TQString t) { _title = t; } ++ TQString title() const { return _title; } ++ ++ // We depend on derived class to be a widget. ++ // Force overiding by making this abstract. ++ virtual TQWidget* 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(TQPopupMenu*,bool withCost2 = true); ++ void addGoMenu(TQPopupMenu*); ++ ++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; ++ ++ TQString _title; ++ int _status; ++ bool _inUpdate; ++ Position _pos; ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/treemap.cpp b/kdecachegrind/kdecachegrind/treemap.cpp +new file mode 100644 +index 0000000..0d4b8dc +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/treemap.cpp +@@ -0,0 +1,3214 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002, 2003 Josef Weidendorfer <[email protected]> ++ ++ 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 TQListView. ++ */ ++ ++#include <math.h> ++ ++#include <tqpainter.h> ++#include <tqtooltip.h> ++#include <tqregexp.h> ++#include <tqstyle.h> ++#include <tqpopupmenu.h> ++ ++#include <klocale.h> ++#include <kconfig.h> ++#include <kdebug.h> ++ ++#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 = TQt::white; ++ ++ // field array has size 0 ++} ++ ++StoredDrawParams::StoredDrawParams(TQColor c, ++ bool selected, bool current) ++{ ++ _backColor = c; ++ ++ _selected = selected; ++ _current = current; ++ _shaded = true; ++ _rotated = false; ++ _drawFrame = true; ++ ++ // field array has size 0 ++} ++ ++TQString StoredDrawParams::text(int f) const ++{ ++ if ((f<0) || (f >= (int)_field.size())) ++ return TQString(); ++ ++ return _field[f].text; ++} ++ ++TQPixmap StoredDrawParams::pixmap(int f) const ++{ ++ if ((f<0) || (f >= (int)_field.size())) ++ return TQPixmap(); ++ ++ 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 TQFont& StoredDrawParams::font() const ++{ ++ static TQFont* f = 0; ++ if (!f) f = new TQFont(TQApplication::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 TQString& t, TQPixmap 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 TQString& t) ++{ ++ if (f<0 || f>=MAX_FIELD) return; ++ ensureField(f); ++ ++ _field[f].text = t; ++} ++ ++void StoredDrawParams::setPixmap(int f, const TQPixmap& 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(TQRect 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(TQRect r) ++{ ++ _rect = r; ++ ++ _usedTopLeft = 0; ++ _usedTopCenter = 0; ++ _usedTopRight = 0; ++ _usedBottomLeft = 0; ++ _usedBottomCenter = 0; ++ _usedBottomRight = 0; ++ ++ _fontHeight = 0; ++} ++ ++TQRect 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(TQPainter* p, DrawParams* dp) ++{ ++ if (!dp) dp = drawParams(); ++ if (_rect.width()<=0 || _rect.height()<=0) return; ++ ++ TQRect r = _rect; ++ TQColor normal = dp->backColor(); ++ if (dp->selected()) normal = normal.light(); ++ bool isCurrent = dp->current(); ++ ++ if (dp->drawFrame() || isCurrent) { ++ // 3D raised/sunken frame effect... ++ TQColor high = normal.light(); ++ TQColor 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 = tqGray(normal.rgb())>128; ++ int rBase, gBase, bBase; ++ normal.rgb(&rBase, &gBase, &bBase); ++ p->setBrush(TQBrush::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; ++ ++ TQColor 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(TQPen::NoPen); ++ p->setBrush(normal); ++ p->drawRect(r); ++} ++ ++ ++bool RectDrawing::drawField(TQPainter* p, int f, DrawParams* dp) ++{ ++ if (!dp) dp = drawParams(); ++ ++ if (!_fm) { ++ _fm = new TQFontMetrics(dp->font()); ++ _fontHeight = _fm->height(); ++ } ++ ++ TQRect 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 (<unused>) ++ 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) ++ TQString name = dp->text(f); ++ if (name.isEmpty()) return 0; ++ TQPixmap 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( (tqGray(dp->backColor().rgb())>100) ? TQt::black : TQt::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) ++ */ ++ TQString remaining; ++ int origLines = lines; ++ while (lines>0) { ++ ++ if (w>width && lines>1) { ++ int lastBreakPos = name.length(), lastWidth = w; ++ int len = name.length(); ++ TQChar::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 == TQChar::Letter_Uppercase && ++ caOld == TQChar::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() == TQChar::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 == TQChar::Letter_Uppercase && ++ ca == TQChar::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() == TQChar::Separator_Space) ++ name = name.right(lastBreakPos-1); ++ else ++ name = name.right(lastBreakPos); ++ } ++ } ++ else ++ remaining = TQString(); ++ ++ /* 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, ++ TQt::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 && (pixY<y)) y = pixY; ++ if (!isBottom && (pixY>y)) 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, ++ TQString text1, TQString text2, ++ TQString text3, TQString 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(); ++} ++ ++ ++TQStringList TreeMapItem::path(int textNo) const ++{ ++ TQStringList list(text(textNo)); ++ ++ TreeMapItem* i = _parent; ++ while (i) { ++ TQString 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 TQFont& 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 = TQRect(); ++ clearFreeRects(); ++} ++ ++void TreeMapItem::clearFreeRects() ++{ ++ if (_freeRects) _freeRects->clear(); ++} ++ ++void TreeMapItem::addFreeRect(const TQRect& r) ++{ ++ // don't add invalid rects ++ if ((r.width() < 1) || (r.height() < 1)) return; ++ ++ if (!_freeRects) { ++ _freeRects = new TQPtrList<TQRect>; ++ _freeRects->setAutoDelete(true); ++ } ++ ++ if (0) kdDebug(90100) << "addFree(" << path(0).join("/") << ", " ++ << r.x() << "/" << r.y() << "-" ++ << r.width() << "x" << r.height() << ")" << endl; ++ ++ TQRect* last = _freeRects->last(); ++ if (!last) { ++ _freeRects->append(new TQRect(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 TQRect(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 TQToolTip ++{ ++public: ++ TreeMapTip( TQWidget* p ):TQToolTip(p) {} ++ ++protected: ++ void maybeTip( const TQPoint & ); ++}; ++ ++void TreeMapTip::maybeTip( const TQPoint& pos ) ++{ ++ if ( !parentWidget()->inherits( "TreeMapWidget" ) ) ++ return; ++ ++ TreeMapWidget* p = (TreeMapWidget*)parentWidget(); ++ TreeMapItem* i; ++ i = p->item(pos.x(), pos.y()); ++ TQPtrList<TQRect>* rList = i ? i->freeRects() : 0; ++ if (rList) { ++ TQRect* r; ++ for(r=rList->first();r;r=rList->next()) ++ if (r->contains(pos)) ++ tip(*r, p->tipString(i)); ++ } ++} ++ ++ ++ ++// TreeMapWidget ++ ++TreeMapWidget::TreeMapWidget(TreeMapItem* base, ++ TQWidget* parent, const char* name) ++ : TQWidget(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(TQt::NoBackground); ++ setFocusPolicy(TQ_StrongFocus); ++ _tip = new TreeMapTip(this); ++} ++ ++TreeMapWidget::~TreeMapWidget() ++{ ++ delete _base; ++ delete _tip; ++} ++ ++const TQFont& 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(TQString 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; ++} ++ ++TQString TreeMapWidget::splitModeString() const ++{ ++ TQString 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(); ++} ++ ++TQString TreeMapWidget::defaultFieldType(int f) const ++{ ++ return i18n("Text %1").arg(f+1); ++} ++ ++TQString TreeMapWidget::defaultFieldStop(int) const ++{ ++ return TQString(); ++} ++ ++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<size) { ++ _attr[oldSize].type = defaultFieldType(oldSize); ++ _attr[oldSize].stop = defaultFieldStop(oldSize); ++ _attr[oldSize].visible = defaultFieldVisible(oldSize); ++ _attr[oldSize].forced = defaultFieldForced(oldSize); ++ _attr[oldSize].pos = defaultFieldPosition(oldSize); ++ oldSize++; ++ } ++ } ++ return true; ++} ++ ++void TreeMapWidget::setFieldType(int f, TQString type) ++{ ++ if (((int)_attr.size() < f+1) && ++ (type == defaultFieldType(f))) return; ++ if (resizeAttr(f+1)) _attr[f].type = type; ++ ++ // no need to redraw: the type string is not visible in the TreeMap ++} ++ ++TQString TreeMapWidget::fieldType(int f) const ++{ ++ if (f<0 || (int)_attr.size()<f+1) return defaultFieldType(f); ++ return _attr[f].type; ++} ++ ++void TreeMapWidget::setFieldStop(int f, TQString stop) ++{ ++ if (((int)_attr.size() < f+1) && ++ (stop == defaultFieldStop(f))) return; ++ if (resizeAttr(f+1)) { ++ _attr[f].stop = stop; ++ redraw(); ++ } ++} ++ ++TQString TreeMapWidget::fieldStop(int f) const ++{ ++ if (f<0 || (int)_attr.size()<f+1) return defaultFieldStop(f); ++ return _attr[f].stop; ++} ++ ++void TreeMapWidget::setFieldVisible(int f, bool enable) ++{ ++ if (((int)_attr.size() < f+1) && ++ (enable == defaultFieldVisible(f))) return; ++ ++ if (resizeAttr(f+1)) { ++ _attr[f].visible = enable; ++ redraw(); ++ } ++} ++ ++bool TreeMapWidget::fieldVisible(int f) const ++{ ++ if (f<0 || (int)_attr.size()<f+1) ++ return defaultFieldVisible(f); ++ ++ return _attr[f].visible; ++} ++ ++void TreeMapWidget::setFieldForced(int f, bool enable) ++{ ++ if (((int)_attr.size() < f+1) && ++ (enable == defaultFieldForced(f))) return; ++ ++ if (resizeAttr(f+1)) { ++ _attr[f].forced = enable; ++ if (_attr[f].visible) redraw(); ++ } ++} ++ ++bool TreeMapWidget::fieldForced(int f) const ++{ ++ if (f<0 || (int)_attr.size()<f+1) ++ return defaultFieldForced(f); ++ ++ return _attr[f].forced; ++} ++ ++void TreeMapWidget::setFieldPosition(int f, TreeMapItem::Position pos) ++{ ++ if (((int)_attr.size() < f+1) && ++ (pos == defaultFieldPosition(f))) return; ++ ++ if (resizeAttr(f+1)) { ++ _attr[f].pos = pos; ++ if (_attr[f].visible) redraw(); ++ } ++} ++ ++DrawParams::Position TreeMapWidget::fieldPosition(int f) const ++{ ++ if (f<0 || (int)_attr.size()<f+1) ++ return defaultFieldPosition(f); ++ ++ return _attr[f].pos; ++} ++ ++void TreeMapWidget::setFieldPosition(int f, TQString pos) ++{ ++ if (pos == "TopLeft") ++ setFieldPosition(f, DrawParams::TopLeft); ++ else if (pos == "TopCenter") ++ setFieldPosition(f, DrawParams::TopCenter); ++ else if (pos == "TopRight") ++ setFieldPosition(f, DrawParams::TopRight); ++ else if (pos == "BottomLeft") ++ setFieldPosition(f, DrawParams::BottomLeft); ++ else if (pos == "BottomCenter") ++ setFieldPosition(f, DrawParams::BottomCenter); ++ else if (pos == "BottomRight") ++ setFieldPosition(f, DrawParams::BottomRight); ++ else if (pos == "Default") ++ setFieldPosition(f, DrawParams::Default); ++} ++ ++TQString TreeMapWidget::fieldPositionString(int f) const ++{ ++ TreeMapItem::Position pos = fieldPosition(f); ++ if (pos == DrawParams::TopLeft) return TQString("TopLeft"); ++ if (pos == DrawParams::TopCenter) return TQString("TopCenter"); ++ if (pos == DrawParams::TopRight) return TQString("TopRight"); ++ if (pos == DrawParams::BottomLeft) return TQString("BottomLeft"); ++ if (pos == DrawParams::BottomCenter) return TQString("BottomCenter"); ++ if (pos == DrawParams::BottomRight) return TQString("BottomRight"); ++ if (pos == DrawParams::Default) return TQString("Default"); ++ return TQString("unknown"); ++} ++ ++void TreeMapWidget::setMinimalArea(int area) ++{ ++ if (_minimalArea == area) return; ++ ++ _minimalArea = area; ++ redraw(); ++} ++ ++ ++void TreeMapWidget::deletingItem(TreeMapItem* i) ++{ ++ // remove any references to the item to be deleted ++ while(_selection.findRef(i) > -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(); ++ } ++} ++ ++ ++TQString TreeMapWidget::tipString(TreeMapItem* i) const ++{ ++ TQString 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 (!TQT_TQRECT_OBJECT(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("") : TQString("(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( TQContextMenuEvent* e ) ++{ ++ //kdDebug(90100) << "TreeMapWidget::contextMenuEvent" << endl; ++ ++ if ( receivers( TQT_SIGNAL(contextMenuRequested(TreeMapItem*, const TQPoint &)) ) ) ++ e->accept(); ++ ++ if ( e->reason() == TQContextMenuEvent::Keyboard ) { ++ TQRect r = (_current) ? _current->itemRect() : _base->itemRect(); ++ TQPoint p = TQPoint(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( TQMouseEvent* 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() == Qt::RightButton) { ++ TreeMapItem* changed2 = setTmpSelected(item, true); ++ if (changed2) changed = changed2->commonParent(changed); ++ } ++ ++ setCurrent(_pressed); ++ ++ if (changed) ++ redraw(changed); ++ ++ if (e->button() == Qt::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( TQMouseEvent* 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( TQMouseEvent* ) ++{ ++ //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( TQMouseEvent* 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++; ++ TQRect 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--; ++ TQRect r = p->children()->at(idx)->itemRect(); ++ if (r.width()>1 && r.height()>1) ++ return idx; ++ } ++ return -1; ++} ++ ++ ++ ++ ++void TreeMapWidget::keyPressEvent( TQKeyEvent* 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 TQFont& ) ++{ ++ redraw(); ++} ++ ++ ++void TreeMapWidget::resizeEvent( TQResizeEvent * ) ++{ ++ // this automatically redraws (as size is changed) ++ drawTreeMap(); ++} ++ ++void TreeMapWidget::paintEvent( TQPaintEvent * ) ++{ ++ drawTreeMap(); ++} ++ ++void TreeMapWidget::showEvent( TQShowEvent * ) ++{ ++ // 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 = TQPixmap(size()); ++ _pixmap.fill(backgroundColor()); ++ } ++ TQPainter p(&_pixmap); ++ if (_needsRefresh == _base) { ++ p.setPen(black); ++ p.drawRect(TQRect(2, 2, TQWidget::width()-4, TQWidget::height()-4)); ++ _base->setItemRect(TQRect(3, 3, TQWidget::width()-6, TQWidget::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( TQT_TQPAINTDEVICE(this), 0, 0, TQT_TQPAINTDEVICE(&_pixmap), 0, 0, ++ TQWidget::width(), TQWidget::height(), CopyROP, true); ++ ++ if (hasFocus()) { ++ TQPainter p(this); ++ style().tqdrawPrimitive( TQStyle::PE_FocusRect, &p, ++ TQRect(0, 0, TQWidget::width(), TQWidget::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(TQPainter* 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 TQRect& 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(TQPainter* 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(); ++ ++ TQRect origRect = item->itemRect(); ++ int bw = item->borderWidth(); ++ TQRect r = TQRect(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++) { ++ TQString 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; ++ } ++ ++ TQRect 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(TQRect(orig.x(), orig.y(), ++ orig.width(), orig.height()-r.height())); ++ } ++ else { ++ // Strings on the left ++ item->addFreeRect(TQRect(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 ++ TQRect 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); ++ TQRect firstRect = TQRect(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); ++ TQRect firstRect = TQRect(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, TQPainter* p, TQRect& r) ++{ ++ p->setBrush(TQt::Dense4Pattern); ++ p->setPen(TQt::NoPen); ++ p->drawRect(r); ++ i->addFreeRect(r); ++} ++ ++// fills area with a pattern if to small to draw children ++void TreeMapWidget::drawFill(TreeMapItem* i, TQPainter* p, TQRect& 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(TQt::Dense4Pattern); ++ p->setPen(TQt::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(TQPainter* p, TreeMapItem* item, ++ TQRect& 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 && valSum<user_sum/2) { ++ while (lenLeft>len/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); ++ TQRect firstRect = TQRect(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); ++ TQRect firstRect = TQRect(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; ++ } ++ ++ TQRect 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 && (nextPos<lastPos)) { ++ p->setPen(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(TQPopupMenu* popup, int id) ++{ ++ _splitID = id; ++ popup->setCheckable(true); ++ ++ connect(popup, TQT_SIGNAL(activated(int)), ++ this, TQT_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(TQPopupMenu* popup, int id) ++{ ++ _visID = id; ++ ++ popup->setCheckable(true); ++ ++ TQPopupMenu* bpopup = new TQPopupMenu(); ++ bpopup->setCheckable(true); ++ ++ connect(popup, TQT_SIGNAL(activated(int)), ++ this, TQT_SLOT(visualizationActivated(int))); ++ connect(bpopup, TQT_SIGNAL(activated(int)), ++ this, TQT_SLOT(visualizationActivated(int))); ++ ++ TQPopupMenu* spopup = new TQPopupMenu(); ++ 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; ++ TQPopupMenu* tpopup; ++ id += 20; ++ for (f=0;f<(int)_attr.size();f++, id+=10) { ++ tpopup = new TQPopupMenu(); ++ 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, TQT_SIGNAL(activated(int)), ++ this, TQT_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(TQPopupMenu* popup, ++ int id, TreeMapItem* i) ++{ ++ if (!i) return; ++ ++ _selectionID = id; ++ _menuItem = i; ++ ++ connect(popup, TQT_SIGNAL(activated(int)), ++ this, TQT_SLOT(selectionActivated(int))); ++ ++ while (i) { ++ TQString 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, TQString()); ++ 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(TQPopupMenu* popup, ++ int id, TreeMapItem* i) ++{ ++ _fieldStopID = id; ++ ++ connect(popup, TQT_SIGNAL(activated(int)), ++ this, TQT_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++; ++ TQString 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(TQPopupMenu* popup, ++ int id, TreeMapItem* i) ++{ ++ _areaStopID = id; ++ _menuItem = i; ++ ++ connect(popup, TQT_SIGNAL(activated(int)), ++ this, TQT_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(TQPopupMenu* popup, ++ int id, TreeMapItem* i) ++{ ++ _depthStopID = id; ++ _menuItem = i; ++ ++ connect(popup, TQT_SIGNAL(activated(int)), ++ this, TQT_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, TQString 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;f<fCount;f++) { ++ config->writeEntry(TQString(prefix+"FieldVisible%1").arg(f), ++ _attr[f].visible); ++ config->writeEntry(TQString(prefix+"FieldForced%1").arg(f), ++ _attr[f].forced); ++ config->writeEntry(TQString(prefix+"FieldStop%1").arg(f), ++ _attr[f].stop); ++ config->writeEntry(TQString(prefix+"FieldPosition%1").arg(f), ++ fieldPositionString(f)); ++ } ++} ++ ++ ++void TreeMapWidget::restoreOptions(KConfigGroup* config, TQString prefix) ++{ ++ bool enabled; ++ int num; ++ TQString 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;f<num;f++) { ++ str = TQString(prefix+"FieldVisible%1").arg(f); ++ if (config->hasKey(str)) ++ setFieldVisible(f, config->readBoolEntry(str)); ++ ++ str = TQString(prefix+"FieldForced%1").arg(f); ++ if (config->hasKey(str)) ++ setFieldForced(f, config->readBoolEntry(str)); ++ ++ str = config->readEntry(TQString(prefix+"FieldStop%1").arg(f)); ++ setFieldStop(f, str); ++ ++ str = config->readEntry(TQString(prefix+"FieldPosition%1").arg(f)); ++ if (!str.isEmpty()) setFieldPosition(f, str); ++ } ++} ++ ++#include "treemap.moc" +diff --git a/kdecachegrind/kdecachegrind/treemap.h b/kdecachegrind/kdecachegrind/treemap.h +new file mode 100644 +index 0000000..422cd35 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/treemap.h +@@ -0,0 +1,759 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2002, 2003 Josef Weidendorfer <[email protected]> ++ ++ 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 TQListView. ++ * ++ * 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 <tqstring.h> ++#include <tqwidget.h> ++#include <tqpixmap.h> ++#include <tqptrlist.h> ++#include <tqvaluevector.h> ++#include <tqcolor.h> ++#include <tqapplication.h> ++#include <tqstringlist.h> ++ ++class TQPopupMenu; ++class TreeMapTip; ++class TreeMapWidget; ++class TreeMapItem; ++class TreeMapItemList; ++class TQString; ++ ++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 TQString text(int) const = 0; ++ virtual TQPixmap 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 TQColor backColor() const { return TQt::white; } ++ virtual const TQFont& 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(TQColor c, ++ bool selected = false, bool current = false); ++ ++ // getters ++ TQString text(int) const; ++ TQPixmap pixmap(int) const; ++ Position position(int) const; ++ int maxLines(int) const; ++ int fieldCount() const { return _field.size(); } ++ ++ TQColor 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 TQFont& font() const; ++ ++ // attribute setters ++ void setField(int f, const TQString& t, TQPixmap pm = TQPixmap(), ++ Position p = Default, int maxLines = 0); ++ void setText(int f, const TQString&); ++ void setPixmap(int f, const TQPixmap&); ++ void setPosition(int f, Position); ++ void setMaxLines(int f, int); ++ void setBackColor(const TQColor& 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: ++ TQColor _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 <f> ++ void ensureField(int f); ++ ++ struct Field { ++ TQString text; ++ TQPixmap pix; ++ Position pos; ++ int maxLines; ++ }; ++ ++ TQValueVector<Field> _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(TQRect); ++ ~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 TQPainter, use this class as info provider per default ++ void drawBack(TQPainter*, DrawParams* dp = 0); ++ /* Draw field at position() from pixmap()/text() with maxLines(). ++ * Returns true if something was drawn ++ */ ++ bool drawField(TQPainter*, int f, DrawParams* dp = 0); ++ ++ // resets rectangle for free space ++ void setRect(TQRect); ++ ++ // Returns the rectangle area still free of text/pixmaps after ++ // a number of drawText() calls. ++ TQRect remainingRect(DrawParams* dp = 0); ++ ++private: ++ int _usedTopLeft, _usedTopCenter, _usedTopRight; ++ int _usedBottomLeft, _usedBottomCenter, _usedBottomRight; ++ TQRect _rect; ++ ++ // temporary ++ int _fontHeight; ++ TQFontMetrics* _fm; ++ DrawParams* _dp; ++}; ++ ++ ++class TreeMapItemList: public TQPtrList<TreeMapItem> ++{ ++public: ++ TreeMapItem* commonParent(); ++protected: ++ int compareItems ( Item item1, Item item2 ); ++}; ++ ++typedef TQPtrListIterator<TreeMapItem> 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:Qt::Horizontal at top; alternate direction on depth step ++ * VAlternate:Qt::Vertical at top; alternate direction on depth step ++ * Qt::Horizontal: Always horizontal split direction ++ * Qt::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, ++ TQString text1, TQString text2 = TQString(), ++ TQString text3 = TQString(), TQString text4 = TQString()); ++ 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. ++ */ ++ TQStringList 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 TQRect& r) { _rect = r; } ++ void clearItemRect(); ++ const TQRect& 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(); ++ TQPtrList<TQRect>* freeRects() const { return _freeRects; } ++ void addFreeRect(const TQRect& 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 TQFont& 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: <textNo> = -1 ++ * For value() sorting, use <textNo> = -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 ++ TQRect _rect; ++ TQPtrList<TQRect>* _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 TQWidget ++{ ++ Q_OBJECT ++ TQ_OBJECT ++ ++public: ++ ++ /** ++ * Same as in TQListBox/TQListView ++ */ ++ enum SelectionMode { Single, Multi, Extended, NoSelection }; ++ ++ /* The widget becomes owner of the base item */ ++ TreeMapWidget(TreeMapItem* base, TQWidget* 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 TQFont& 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 <markNo>. 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(<markNo>). ++ * 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(TQString); ++ TQString 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 <width>, 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 <reuseSpace> 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 */ ++ TQString defaultFieldType(int) const; ++ TQString 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, TQString); ++ TQString fieldType(int) const; ++ ++ /** ++ * Stop drawing at item with name ++ */ ++ void setFieldStop(int, TQString); ++ TQString 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, TQString); ++ TQString 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*, TQString prefix = TQString()); ++ void restoreOptions(KConfigGroup*, TQString prefix = TQString()); ++ ++ /** ++ * 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(TQPopupMenu*, int); ++ void addSelectionItems(TQPopupMenu*, int, TreeMapItem*); ++ void addFieldStopItems(TQPopupMenu*, int, TreeMapItem*); ++ void addAreaStopItems(TQPopupMenu*, int, TreeMapItem*); ++ void addDepthStopItems(TQPopupMenu*, int, TreeMapItem*); ++ void addVisualizationItems(TQPopupMenu*, 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 TQString 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 <kbd> is set to true ++ */ ++ void currentChanged(TreeMapItem*, bool keyboard); ++ void clicked(TreeMapItem*); ++ void returnPressed(TreeMapItem*); ++ void doubleClicked(TreeMapItem*); ++ void rightButtonPressed(TreeMapItem*, const TQPoint &); ++ void contextMenuRequested(TreeMapItem*, const TQPoint &); ++ ++protected: ++ void mousePressEvent( TQMouseEvent * ); ++ void contextMenuEvent( TQContextMenuEvent * ); ++ void mouseReleaseEvent( TQMouseEvent * ); ++ void mouseMoveEvent( TQMouseEvent * ); ++ void mouseDoubleClickEvent( TQMouseEvent * ); ++ void keyPressEvent( TQKeyEvent* ); ++ void paintEvent( TQPaintEvent * ); ++ void resizeEvent( TQResizeEvent * ); ++ void showEvent( TQShowEvent * ); ++ void fontChange( const TQFont& ); ++ ++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(TQPainter* p, TreeMapItem*); ++ void drawItems(TQPainter* p, TreeMapItem*); ++ bool horizontal(TreeMapItem* i, const TQRect& r); ++ void drawFill(TreeMapItem*,TQPainter* p, TQRect& r); ++ void drawFill(TreeMapItem*,TQPainter* p, TQRect& r, ++ TreeMapItemListIterator it, int len, bool goBack); ++ bool drawItemArray(TQPainter* p, TreeMapItem*, TQRect& 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 { ++ TQString type, stop; ++ bool visible, forced; ++ DrawParams::Position pos; ++ }; ++ TQValueVector<FieldAttr> _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 ++ TQFont _font; ++ int _fontHeight; ++ ++ // back buffer pixmap ++ TQPixmap _pixmap; ++}; ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/utils.cpp b/kdecachegrind/kdecachegrind/utils.cpp +new file mode 100644 +index 0000000..65c7e34 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/utils.cpp +@@ -0,0 +1,483 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Utility classes for KCachegrind ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include <config.h> ++#endif ++ ++#ifdef HAVE_MMAP ++#include <unistd.h> ++#include <sys/mman.h> ++#endif ++ ++#include <tqfile.h> ++#include <errno.h> ++ ++#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 (!TQChar(*_str).isLetter() && (*_str != '_')) return false; ++ ++ int newLen = 1; ++ const char* newStr = _str; ++ ++ _str++; ++ _len--; ++ ++ while(_len>0) { ++ if (!TQChar(*_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(TQFile* 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*) TQFile::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<count;i++) ++ delete _first[i]; ++ } ++} ++ ++#endif +diff --git a/kdecachegrind/kdecachegrind/utils.h b/kdecachegrind/kdecachegrind/utils.h +new file mode 100644 +index 0000000..7256f05 +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/utils.h +@@ -0,0 +1,164 @@ ++/* This file is part of KCachegrind. ++ Copyright (C) 2003 Josef Weidendorfer <[email protected]> ++ ++ 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. ++*/ ++ ++/* ++ * Utility classes for KCachegrind ++ */ ++ ++#ifndef UTILS_H ++#define UTILS_H ++ ++#include <tqstring.h> ++ ++class TQFile; ++ ++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 <c> 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 TQString() const ++ { return TQString::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(TQFile*); ++ ~FixFile(); ++ ++ /** ++ * Read next line into <str>. 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; ++ TQByteArray _data; ++ unsigned _len, _currentLeft; ++ bool _used_mmap, _openError; ++ TQString _filename; ++}; ++ ++ ++/** ++ * A list of pointers, only able to append items. ++ * Optimized for speed, not space. ++ */ ++template<class type> ++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/kdecachegrind/kdecachegrind/x-kcachegrind.desktop b/kdecachegrind/kdecachegrind/x-kcachegrind.desktop +new file mode 100644 +index 0000000..b9bf93a +--- /dev/null ++++ b/kdecachegrind/kdecachegrind/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=kdecachegrind ++Icon=kdecachegrind ++Type=MimeType ++MimeType=application/x-kcachegrind ++Patterns=cachegrind.out*;callgrind.out* +diff --git a/kdecachegrind/tests/cg-badcompression1 b/kdecachegrind/tests/cg-badcompression1 +new file mode 100644 +index 0000000..6076bf9 +--- /dev/null ++++ b/kdecachegrind/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/kdecachegrind/tests/cg-badcostline1 b/kdecachegrind/tests/cg-badcostline1 +new file mode 100644 +index 0000000..224ff67 +--- /dev/null ++++ b/kdecachegrind/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/kdecachegrind/tests/cg-badposition b/kdecachegrind/tests/cg-badposition +new file mode 100644 +index 0000000..1be582c +--- /dev/null ++++ b/kdecachegrind/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/kdecachegrind/version.h.in b/kdecachegrind/version.h.in +new file mode 100644 +index 0000000..d88081b +--- /dev/null ++++ b/kdecachegrind/version.h.in +@@ -0,0 +1 @@ ++#define KCACHEGRIND_VERSION "@KCACHEGRIND_VERSION@" |