diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 114a878c64ce6f8223cfd22d76a20eb16d177e5e (patch) | |
tree | acaf47eb0fa12142d3896416a69e74cbf5a72242 /lib | |
download | tdevelop-114a878c64ce6f8223cfd22d76a20eb16d177e5e.tar.gz tdevelop-114a878c64ce6f8223cfd22d76a20eb16d177e5e.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdevelop@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'lib')
333 files changed, 55688 insertions, 0 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 00000000..e75b5d04 --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,23 @@ +# Here resides the library of general KDevelop-related utilities, +# which may be linked by any plugin or part. This is installed +# as a shared library, including header files + +INCLUDES = $(all_includes) + +lib_LTLIBRARIES = libkdevelop.la +libkdevelop_la_LDFLAGS = -no-undefined $(all_libraries) -version-info 1:0 +libkdevelop_la_LIBADD = $(top_builddir)/lib/interfaces/libkdevinterfaces.la \ + $(top_builddir)/lib/widgets/propeditor/libkdevpropertyeditor.la $(LIB_KIO) + +# $(top_builddir)/lib/util/libkdevutil.la $(top_builddir)/lib/widgets/propeditor/libkdevpropertyeditor.la \ +# $(top_builddir)/lib/interfaces/external/libkinterfacedesigner.la $(top_builddir)/lib/interfaces/extensions/libkdevextensions.la \ +# -lktexteditor $(LIB_KPARTS) $(LIB_KIO) -lkscript +libkdevelop_la_SOURCES = dummy.cpp + + + +SUBDIRS = util interfaces widgets antlr catalog \ + cppparser astyle + +DOXYGEN_EMPTY = YES +include ../Doxyfile.am diff --git a/lib/antlr/AUTHORS b/lib/antlr/AUTHORS new file mode 100644 index 00000000..7bdc4852 --- /dev/null +++ b/lib/antlr/AUTHORS @@ -0,0 +1,2 @@ +Author: + Peter Wells <pete@yamuna.demon.co.uk> diff --git a/lib/antlr/COPYING b/lib/antlr/COPYING new file mode 100644 index 00000000..ce9ec595 --- /dev/null +++ b/lib/antlr/COPYING @@ -0,0 +1,32 @@ + +SOFTWARE RIGHTS + +ANTLR MageLang Institute, 1989-1999 +http://www.ANTLR.org + +We reserve no legal rights to the ANTLR--it is fully in the +public domain. An individual or company may do whatever +they wish with source code distributed with ANTLR or the +code generated by ANTLR, including the incorporation of +ANTLR, or its output, into commerical software. + +We encourage users to develop software with ANTLR. However, +we do ask that credit is given to us for developing +ANTLR. By "credit", we mean that if you use ANTLR or +incorporate any source code into one of your programs +(commercial product, research project, or otherwise) that +you acknowledge this fact somewhere in the documentation, +research report, etc... If you like ANTLR and have +developed a nice tool with the output, please mention that +you developed it using ANTLR. In addition, we ask that the +headers remain intact in our source code. As long as these +guidelines are kept, we expect to continue enhancing this +system and expect to make other tools available as they are +completed. + +The primary ANTLR guy: + +Terence Parr +MageLang Institute; http://www.MageLang.com +parrt@jguru.com +parrt@magelang.com diff --git a/lib/antlr/ChangeLog b/lib/antlr/ChangeLog new file mode 100644 index 00000000..735046e5 --- /dev/null +++ b/lib/antlr/ChangeLog @@ -0,0 +1,293 @@ +Not 100% complete. Changes from develtree are not listed yet. + +Change 400 on 2000/09/27 by klaren@klaren.hawking.main + + Made little TCL script to pretty print a ChangeLog with C++ stuff. + + +Change 399 on 2000/09/27 by klaren@klaren.hawking.main + + Fixed generating too many ASTNULL checks in wrong places. + + +Change 397 on 2000/09/27 by klaren@klaren.hawking.main + + Some *UGLY* fixes for the last typecasting problems in Cpp codegen. It + now works. In 2.7.2 or later I'll fix this in a nice way. + + +Change 397 on 2000/09/27 by klaren@klaren.hawking.main + + Some *UGLY* fixes for the last typecasting problems in Cpp codegen. It + now works. In 2.7.2 or later I'll fix this in a nice way. + + +Change 394 on 2000/09/26 by klaren@klaren.hawking.main + + Prefixed Unicode optimization checks with a ASTNULL check. + + +Change 393 on 2000/09/25 by klaren@klaren.hawking.main + + Bumped up the version no from 2.7.1a4 to 2.7.1. + + +Change 380 on 2000/09/24 by parrt@parrt.foggy + + integrating ric's stuff into main + + +Change 380 on 2000/09/24 by parrt@parrt.foggy + + integrating ric's stuff into main + + +Change 380 on 2000/09/24 by parrt@parrt.foggy + + integrating ric's stuff into main + + +Change 348 on 2000/09/07 by klaren@klaren.hawking.main + + Small improvement in constructor of CommonAST. + + +Change 344 on 2000/09/06 by klaren@klaren.hawking.main + + Fixed missing namespace in generated TreeParsers as reported by Ross + Bencina. + + +Change 341 on 2000/09/06 by klaren@klaren.hawking.main + + Miniscule fix for Borland C++Builder 4.0/C++ 5.4. (extra parens) + + +Change 317 on 2000/08/22 by klaren@klaren.hawking.main + + Updated changelog for a5 (or was it 2.7.1) release.. + + +Change 316 on 2000/08/22 by klaren@klaren.hawking.main + + All kinds of small Makefile/configure tweaks. All gcc-isms should be + gone now. + + +Change 309 on 2000/08/15 by klaren@klaren.hawking.main + + Integrate bugfixes from klaren.dev to MismatchedChar/TokenException. + + +Change 297 on 2000/08/07 by klaren@klaren.kronecker.main + + Fixes for namespace/namespaceAntlr/namespaceStd/genHashLines options. + + +Change 296 on 2000/08/07 by klaren@klaren.kronecker.main + + Virtualized all functions that someone should want to override. Probably + necessary for heteroAST stuff. + + +Change 291 on 2000/08/07 by klaren@klaren.kronecker.main + + Some tweaks to configure.in and Makefile.am's. Fix for CXXFLAGS being + set incorrectly when not using gcc. + + +Change 290 on 2000/08/05 by klaren@klaren.kronecker.main + + Updated prototype of toLower to definition in cpp file. It seems I + messed them up a while back. + + +Change 289 on 2000/08/05 by klaren@klaren.kronecker.main + + Added namespace macro to out_of_range exception. + + +Change 288 on 2000/07/28 by parrt@parrt.foggy + + re-added toLower return type fix + + +Change 285 on 2000/07/19 by klaren@klaren.kronecker.main + + Fixed thinko. + + +Change 284 on 2000/07/19 by klaren@klaren.kronecker.main + + Dumped output of p4 changes -l into it... + + +Change 283 on 2000/07/19 by klaren@klaren.kronecker.main + + Fix for bug found by Michael Ebner. Bitset size was not increased in add + method. + + +Change 280 on 2000/07/19 by klaren@klaren.kronecker.main + + Made namespaceAntlr, namespaceStd and genHashlines options file-level + options. Removed nameSpace member from Tool class all is now handled in + CppCodegenerator.java. + + +Change 276 on 2000/07/18 by klaren@klaren.kronecker.main + + C++ Changes for the indented traceXXXX output as invented by Monty Zukowski + + +Change 275 on 2000/07/18 by klaren@klaren.kronecker.main + + Added missing initializer in generated code for TreeParser + + +Change 272 on 2000/07/17 by klaren@klaren.kronecker.main + + Another workspace for MSVC6 has support for dll's (for version 2.6.1). + + +Change 271 on 2000/07/17 by klaren@klaren.kronecker.main + + New autoconf/automake stuff for the C++ support library. + + +Change 270 on 2000/07/17 by klaren@klaren.kronecker.main + + Fixed error within the NO_STATIC_CONSTS #ifdef + + +Change 269 on 2000/07/17 by klaren@klaren.kronecker.main + + Move C++ files to lib/cpp/src as first step for autoconf setup + + +Change 268 on 2000/07/17 by klaren@klaren.kronecker.main + + Add contrib dir and Microsoft Visual C++ 6.0 projects supplied by John + Millaway + + +Change 260 on 2000/07/14 by klaren@klaren.kronecker.main + + Fixed crashbugs/typos in constructors of Mismatched[Token|Char]Exception + + +Change 258 on 2000/07/10 by parrt@parrt.foggy + + fixes per klaren + + +Change 258 on 2000/07/10 by parrt@parrt.foggy + + fixes per klaren + + +Change 248 on 2000/07/04 by parrt@parrt.foggy + + Ric Klaren's changes to C++ lib + + +Change 247 on 2000/07/04 by parrt@parrt.foggy + + Ric Klaren's changes for namespaces + + +Change 239 on 2000/06/03 by parrt@parrt.foggy + + adjusted so it works; header actions got converted to Token objects from + Strings; lots of cast problems and then null ptr exceptions. + +Change 235 on 2000/05/31 by pete@pete.linux + + More changes to support #line generation in C++ (from Ric Klaren) + +Change 220 on 2000/05/29 by parrt@parrt.foggy + + changed char to int for toLower + + +Change 219 on 2000/05/28 by pete@pete.linux + + Mirroring Java changes + + +Change 218 on 2000/05/28 by pete@pete.linux + + Cleaned up the #line generator a little. + + +Change 211 on 2000/05/27 by parrt@parrt.foggy + + had same bug as JavaCodeGenerator related to ~(A|B) + + +Change 205 on 2000/05/24 by pete@pete.linux + + Add support for Metrowerks Codewarrior + + +Change 203 on 2000/05/22 by pete@pete.linux + + Fix for multithreading from Jan Mikkelsen + + +Change 202 on 2000/05/21 by pete@pete.linux + + Merged in some fixes from Ric Klaren for tracing TreeParsers, cleaner + namespace code, and #line generation. + + +Change 202 on 2000/05/21 by pete@pete.linux + + Merged in some fixes from Ric Klaren for tracing TreeParsers, cleaner + namespace code, and #line generation. + +Change 201 on 2000/05/21 by pete@pete.linux + + Added destructors with empty throw specs, as suggested by Dan Field. + + +Change 200 on 2000/05/21 by pete@pete.linux + + Various performance improvements, mostly from Eric Dumas. + + +Change 183 on 2000/02/08 by pete@pete.linux + + Added support for Sun CC 5.0 (from Michael Schmitt) + + +Change 182 on 2000/02/08 by pete@pete.linux + + Fix a couple of minor problems with C++ generation (noted by Michael + Schmitt) + +Change 132 on 2000/01/18 by parrt@parrt.foggy + + setting type to ktext for everything + + +Change 132 on 2000/01/18 by parrt@parrt.foggy + + setting type to ktext for everything + + +Change 131 on 2000/01/18 by parrt@parrt.foggy + + from dev back to main + + +Change 131 on 2000/01/18 by parrt@parrt.foggy + + from dev back to main + + +Change 1 on 1999/12/13 by parrt@parrt.foggy + + adding 2.6.0 from antlr site as initial main line + + diff --git a/lib/antlr/INSTALL b/lib/antlr/INSTALL new file mode 100644 index 00000000..30dd4d49 --- /dev/null +++ b/lib/antlr/INSTALL @@ -0,0 +1,183 @@ +Basic Installation +================== + + These are generic installation instructions. Check out the README for +additional info. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/lib/antlr/Makefile.am b/lib/antlr/Makefile.am new file mode 100644 index 00000000..2aa39c30 --- /dev/null +++ b/lib/antlr/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src antlr diff --git a/lib/antlr/NEWS b/lib/antlr/NEWS new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/lib/antlr/NEWS diff --git a/lib/antlr/README b/lib/antlr/README new file mode 100644 index 00000000..6ca0913b --- /dev/null +++ b/lib/antlr/README @@ -0,0 +1,72 @@ +C++ support libraries + +Original GNU autconf stuff contributed by Braden N. McDaniel. Slightly +hacked up by me (Ric Klaren (klaren@cs.utwente.nl)) for who it's the first +autoconf/automake/aclocal stuff ever, so suggestions additions welcome. + +HOW TO INSTALL + +In theory do: + +./configure --prefix=<basedirectory where you want it installed> +make +make install + +Now libantlr.a should reside under <basedir>/lib/libantlr.a and the +includes should be at <basedir>/include/antlr. + +NOTE: this only installs the C++ library and header files. + +In the contrib directory a dsp/dsw project for Microsoft Visual C++ can be +found. + +In general this library needs runtime typing information (RTTI) make sure +you enable this in whatever compiler you are using. + +SUPPORTED COMPILERS + +Pasted from the FAQ entry on: http://www.jguru.com/jguru/faq/view.jsp?EID=121 + +Compiler OS Version +------------------ --------------------- ---------- +Sun Workshop 4.2 Solaris 2.6, 7 2.7.1a2 +Sun Workshop 5.0 Solaris 2.7 2.7.1a2 +Sun Workshop 6.0 Solaris 2.7 2.7.1a2 +egcs-1.1.2 Solaris 2.6,7 2.7.1a2 +egcs-1.1.2 Linux 2.2, Solaris 2.6 2.7.1a2 +gcc-2.95.2 Linux 2.2, Solaris 2.6,7 2.7.1a2 +gcc-2.96 (20000527) Solaris 2.6 2.7.1a2 +aCC A.01.21 HP-UX 10.20 2.7.0 no! +Visual C++ 6.0 PC 2.7.1a2 (warnings) +Intel C++ 4.0 NT 4.0 2.7.0 +Borland 5.0 NT 4.0 2.7.0 + +IT DOESN'T WORK!? + +Check out the faq: http://www.jguru.com/jguru/faq/view.jsp?EID=120 + +The text of that entry (by Peter Wells): + +The ANTLR code uses some relatively new features of C++ which not all +compilers support yet (such as namespaces, and new style standard headers). + +There is work currently in progress to provide a compatibility mode for +ANTLR, to enable older compilers to handle this. + +At the moment, you may be able to work around the problem with a few nasty +tricks: + +Try creating some header files like 'iostream' just containing: + +#include <iostream.h> + +and compile with an option to define away the word 'std', such as + +CC .... -Dstd= .... + +Also in the antlr subdirectory there's a file config.hpp. Tweak this one to +enable/disable the different bells and whistles used in the rest of the code. +Don't forget to submit those changes back to us (along with compiler info) +so we can incorporate them in our next release! + +Thanks! diff --git a/lib/antlr/TODO b/lib/antlr/TODO new file mode 100644 index 00000000..51d104c3 --- /dev/null +++ b/lib/antlr/TODO @@ -0,0 +1,34 @@ +* Improve configure scripts => KICK OUT automake! + +* Add allocators to the objects + +* Look more at exception handling + +* TreeParser.cpp around line 76 the MismatchedTokenException here does not + use ttype to improve it's errormessage. Would require changing a bit in + MismatchedTokenException.cpp + +* On Thu, Sep 21, 2000 at 12:33:48AM -0700, John Lambert <JohnL@jBASE.com> wrote: + > 1) The literal EOF is not defined and causes the define of EOF_CHAR in + > CharScanner.hpp to fail. + + ANTLR with STL Port. Changing the EOF define to char_traits<char>::eof() + breaks things for gcc-2.95.2. Fix this in next release portably. + http://www.egroups.com/message/antlr-interest/2520 + +* John Millaway requested some mechanism to add code to the constructor + of the parser/lexer/treewalker. This can be usefull. + http://www.egroups.com/message/antlr-interest/2501 + +* Fix heterogeneous AST stuff. It boils down to adding a method to AST + types that knows how to duplicate the sucker. Atm duptree cannot work + because of this. Knowing one factory is not enough. Also look at having + to set the astfactory by hand (this is not 100% necessary). + http://www.egroups.com/message/antlr-interest/2496 + +* Look at messageLog stuff Ross Bencina proposed. Looks good at first glance. + http://www.egroups.com/message/antlr-interest/2555 + +* Add RW_STL & CC 4.2 patch from Ulrich Teichert: + See my mailbox.. and these comments from Ross Bencina: + http://www.egroups.com/message/antlr-interest/2494 diff --git a/lib/antlr/antlr/ANTLRException.hpp b/lib/antlr/antlr/ANTLRException.hpp new file mode 100644 index 00000000..426595a2 --- /dev/null +++ b/lib/antlr/antlr/ANTLRException.hpp @@ -0,0 +1,59 @@ +#ifndef INC_ANTLRException_hpp__ +#define INC_ANTLRException_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <string> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +class ANTLR_API ANTLRException +{ +public: + /// Create ANTLR base exception without error message + ANTLRException() : text("") + { + } + /// Create ANTLR base exception with error message + ANTLRException(const ANTLR_USE_NAMESPACE(std)string& s) + : text(s) + { + } + virtual ~ANTLRException() throw() + { + } + + /** Return complete error message with line/column number info (if present) + * @note for your own exceptions override this one. Call getMessage from + * here to get the 'clean' error message stored in the text attribute. + */ + virtual ANTLR_USE_NAMESPACE(std)string toString() const + { + return text; + } + + /** Return error message without additional info (if present) + * @note when making your own exceptions classes override toString + * and call in toString getMessage which relays the text attribute + * from here. + */ + virtual ANTLR_USE_NAMESPACE(std)string getMessage() const + { + return text; + } +private: + ANTLR_USE_NAMESPACE(std)string text; +}; +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_ANTLRException_hpp__ diff --git a/lib/antlr/antlr/ANTLRUtil.hpp b/lib/antlr/antlr/ANTLRUtil.hpp new file mode 100644 index 00000000..4732588c --- /dev/null +++ b/lib/antlr/antlr/ANTLRUtil.hpp @@ -0,0 +1,53 @@ +#ifndef INC_ANTLRUtil_hpp__ +#define INC_ANTLRUtil_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <iostream> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** Eat whitespace from the input stream + * @param is the stream to read from + */ +ANTLR_USE_NAMESPACE(std)istream& eatwhite( ANTLR_USE_NAMESPACE(std)istream& is ); + +/** Read a string enclosed by '"' from a stream. Also handles escaping of \". + * Skips leading whitespace. + * @param in the istream to read from. + * @returns the string read from file exclusive the '"' + * @throws ios_base::failure if string is badly formatted + */ +ANTLR_USE_NAMESPACE(std)string read_string( ANTLR_USE_NAMESPACE(std)istream& in ); + +/* Read a ([A-Z][0-9][a-z]_)* kindoff thing. Skips leading whitespace. + * @param in the istream to read from. + */ +ANTLR_USE_NAMESPACE(std)string read_identifier( ANTLR_USE_NAMESPACE(std)istream& in ); + +/** Read a attribute="value" thing. Leading whitespace is skipped. + * Between attribute and '=' no whitespace is allowed. After the '=' it is + * permitted. + * @param in the istream to read from. + * @param attribute string the attribute name is put in + * @param value string the value of the attribute is put in + * @throws ios_base::failure if something is fishy. E.g. malformed quoting + * or missing '=' + */ +void read_AttributeNValue( ANTLR_USE_NAMESPACE(std)istream& in, + ANTLR_USE_NAMESPACE(std)string& attribute, + ANTLR_USE_NAMESPACE(std)string& value ); + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif diff --git a/lib/antlr/antlr/AST.hpp b/lib/antlr/antlr/AST.hpp new file mode 100644 index 00000000..b01a60a9 --- /dev/null +++ b/lib/antlr/antlr/AST.hpp @@ -0,0 +1,166 @@ +#ifndef INC_AST_hpp__ +#define INC_AST_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/ASTRefCount.hpp> +#include <antlr/Token.hpp> +#include <vector> +#include <string> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +struct ASTRef; + +class ANTLR_API AST { +public: + AST() : ref(0) {} + AST(const AST&) : ref(0) {} + virtual ~AST() {} + + /// Return the type name for this AST node. (for XML output) + virtual const char* typeName( void ) const = 0; + /// Clone this AST node. + virtual RefAST clone( void ) const = 0; + /// Is node t equal to this in terms of token type and text? + virtual bool equals(RefAST t) const = 0; + /** Is t an exact structural and equals() match of this tree. The + * 'this' reference is considered the start of a sibling list. + */ + virtual bool equalsList(RefAST t) const = 0; + + /** Is 't' a subtree of this list? The siblings of the root are NOT ignored. + */ + virtual bool equalsListPartial(RefAST t) const = 0; + /** Is tree rooted at 'this' equal to 't'? The siblings of 'this' are + * ignored. + */ + virtual bool equalsTree(RefAST t) const = 0; + /** Is 't' a subtree of the tree rooted at 'this'? The siblings of + * 'this' are ignored. + */ + virtual bool equalsTreePartial(RefAST t) const = 0; + + /** Walk the tree looking for all exact subtree matches. Return + * a vector of RefAST that lets the caller walk the list + * of subtree roots found herein. + */ + virtual ANTLR_USE_NAMESPACE(std)vector<RefAST> findAll(RefAST t) = 0; + + /** Walk the tree looking for all subtrees. Return + * a vector of RefAST that lets the caller walk the list + * of subtree roots found herein. + */ + virtual ANTLR_USE_NAMESPACE(std)vector<RefAST> findAllPartial(RefAST t) = 0; + + /// Add a node to the end of the child list for this node + virtual void addChild(RefAST c) = 0; + /// Get the number of children. Returns 0 if the node is a leaf + virtual size_t getNumberOfChildren() const = 0; + + /// Get the first child of this node; null if no children + virtual RefAST getFirstChild() const = 0; + /// Get the next sibling in line after this one + virtual RefAST getNextSibling() const = 0; + + /// Get the token text for this node + virtual ANTLR_USE_NAMESPACE(std)string getText() const = 0; + /// Get the token type for this node + virtual int getType() const = 0; + + /** Various initialization routines. Used by several factories to initialize + * an AST element. + */ + virtual void initialize(int t, const ANTLR_USE_NAMESPACE(std)string& txt) = 0; + virtual void initialize(RefAST t) = 0; + virtual void initialize(RefToken t) = 0; + +#ifdef ANTLR_SUPPORT_XML + /** initialize this node from the contents of a stream. + * @param in the stream to read the AST attributes from. + */ + virtual void initialize( ANTLR_USE_NAMESPACE(std)istream& in ) = 0; +#endif + + /// Set the first child of a node. + virtual void setFirstChild(RefAST c) = 0; + /// Set the next sibling after this one. + virtual void setNextSibling(RefAST n) = 0; + + /// Set the token text for this node + virtual void setText(const ANTLR_USE_NAMESPACE(std)string& txt) = 0; + /// Set the token type for this node + virtual void setType(int type) = 0; + + /// Return this AST node as a string + virtual ANTLR_USE_NAMESPACE(std)string toString() const = 0; + + /// Print out a child-sibling tree in LISP notation + virtual ANTLR_USE_NAMESPACE(std)string toStringList() const = 0; + virtual ANTLR_USE_NAMESPACE(std)string toStringTree() const = 0; + +#ifdef ANTLR_SUPPORT_XML + /** get attributes of this node to 'out'. Override to customize XML + * output. + * @param out the stream to write the AST attributes to. + * @returns if a explicit closetag should be written + */ + virtual bool attributesToStream( ANTLR_USE_NAMESPACE(std)ostream& out ) const = 0; + + /** Print a symbol over ostream. Overload this one to customize the XML + * output for AST derived AST-types + * @param output stream + */ + virtual void toStream( ANTLR_USE_NAMESPACE(std)ostream &out ) const = 0; + + /** Dump AST contents in XML format to output stream. + * Works in conjunction with to_stream method. Overload that one is + * derived classes to customize behaviour. + * @param output stream to write to string to put the stuff in. + * @param ast RefAST object to write. + */ + friend ANTLR_USE_NAMESPACE(std)ostream& operator<<( ANTLR_USE_NAMESPACE(std)ostream& output, const RefAST& ast ); +#endif + +private: + friend struct ASTRef; + ASTRef* ref; + + AST(RefAST other); + AST& operator=(const AST& other); + AST& operator=(RefAST other); +}; + +#ifdef ANTLR_SUPPORT_XML +inline ANTLR_USE_NAMESPACE(std)ostream& operator<<( ANTLR_USE_NAMESPACE(std)ostream& output, const RefAST& ast ) +{ + ast->toStream(output); + return output; +} +#endif + +extern ANTLR_API RefAST nullAST; +extern ANTLR_API AST* const nullASTptr; + +#ifdef NEEDS_OPERATOR_LESS_THAN +// RK: apparently needed by MSVC and a SUN CC, up to and including +// 2.7.2 this was undefined ? +inline bool operator<( RefAST l, RefAST r ) +{ + return nullAST == l ? ( nullAST == r ? false : true ) : l->getType() < r->getType(); +} +#endif + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_AST_hpp__ diff --git a/lib/antlr/antlr/ASTArray.hpp b/lib/antlr/antlr/ASTArray.hpp new file mode 100644 index 00000000..2d224d32 --- /dev/null +++ b/lib/antlr/antlr/ASTArray.hpp @@ -0,0 +1,45 @@ +#ifndef INC_ASTArray_hpp__ +#define INC_ASTArray_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/AST.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** ASTArray is a class that allows ANTLR to + * generate code that can create and initialize an array + * in one expression, like: + * (new ASTArray(3))->add(x)->add(y)->add(z) + */ +class ANTLR_API ASTArray { +public: + int size; // = 0; + ANTLR_USE_NAMESPACE(std)vector<RefAST> array; + + ASTArray(int capacity) + : size(0) + , array(capacity) + { + } + + ASTArray* add(RefAST node) + { + array[size++] = node; + return this; + } +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_ASTArray_hpp__ diff --git a/lib/antlr/antlr/ASTFactory.hpp b/lib/antlr/antlr/ASTFactory.hpp new file mode 100644 index 00000000..968ab6c5 --- /dev/null +++ b/lib/antlr/antlr/ASTFactory.hpp @@ -0,0 +1,165 @@ +#ifndef INC_ASTFactory_hpp__ +#define INC_ASTFactory_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/AST.hpp> +#include <antlr/ASTArray.hpp> +#include <antlr/ASTPair.hpp> + +#include <istream> +#include <utility> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +// Using these extra types to appease MSVC +typedef RefAST (*factory_type_)(); +typedef ANTLR_USE_NAMESPACE(std)pair< const char*, factory_type_ > factory_descriptor_; +typedef ANTLR_USE_NAMESPACE(std)vector< factory_descriptor_* > factory_descriptor_list_; + +/** AST Super Factory shared by TreeParser and Parser. + * This super factory maintains a map of all AST node types to their respective + * AST factories. One instance should be shared among a parser/treeparser + * chain. + * + * @todo check all this code for possible use of references in + * stead of RefAST's. + */ +class ANTLR_API ASTFactory { +public: + typedef factory_type_ factory_type; + typedef factory_descriptor_ factory_descriptor; + typedef factory_descriptor_list_ factory_descriptor_list; +protected: + /* The mapping of AST node type to factory.. + */ + factory_descriptor default_factory_descriptor; + factory_descriptor_list nodeFactories; +public: + /// Make new factory. Per default (Ref)CommonAST instances are generated. + ASTFactory(); + /** Initialize factory with a non default node type. + * factory_node_name should be the name of the AST node type the factory + * generates. (should exist during the existance of this ASTFactory + * instance) + */ + ASTFactory( const char* factory_node_name, factory_type factory ); + /// Destroy factory + virtual ~ASTFactory(); + + /// Register a node factory for the node type type with name ast_name + void registerFactory( int type, const char* ast_name, factory_type factory ); + /// Set the maximum node (AST) type this factory may encounter + void setMaxNodeType( int type ); + + /// Add a child to the current AST + void addASTChild(ASTPair& currentAST, RefAST child); + /// Create new empty AST node. The right default type shou + virtual RefAST create(); + /// Create AST node of the right type for 'type' + RefAST create(int type); + /// Create AST node of the right type for 'type' and initialize with txt + RefAST create(int type, const ANTLR_USE_NAMESPACE(std)string& txt); + /// Create duplicate of tr + RefAST create(RefAST tr); + /// Create new AST node and initialize contents from a token. + RefAST create(RefToken tok); + /// Create new AST node and initialize contents from a stream. + RefAST create(const ANTLR_USE_NAMESPACE(std)string& txt, ANTLR_USE_NAMESPACE(std)istream& infile ); + /** Deep copy a single node. This function the new clone() methods in the + * AST interface. Returns a new RefAST(nullASTptr) if t is null. + */ + RefAST dup(RefAST t); + /// Duplicate tree including siblings of root. + RefAST dupList(RefAST t); + /** Duplicate a tree, assuming this is a root node of a tree-- + * duplicate that node and what's below; ignore siblings of root node. + */ + RefAST dupTree(RefAST t); + /** Make a tree from a list of nodes. The first element in the + * array is the root. If the root is null, then the tree is + * a simple list not a tree. Handles null children nodes correctly. + * For example, make(a, b, null, c) yields tree (a b c). make(null,a,b) + * yields tree (nil a b). + */ + RefAST make(ANTLR_USE_NAMESPACE(std)vector<RefAST>& nodes); + /** Make a tree from a list of nodes, where the nodes are contained + * in an ASTArray object. The ASTArray is deleted after use. + * @todo FIXME! I have a feeling we can get rid of this ugly ASTArray thing + */ + RefAST make(ASTArray* nodes); + /// Make an AST the root of current AST + void makeASTRoot(ASTPair& currentAST, RefAST root); + + /** Set a new default AST type. + * factory_node_name should be the name of the AST node type the factory + * generates. (should exist during the existance of this ASTFactory + * instance). + * Only change factory between parser runs. You might get unexpected results + * otherwise. + */ + void setASTNodeFactory( const char* factory_node_name, factory_type factory ); + +#ifdef ANTLR_SUPPORT_XML + /** Load a XML AST from stream. Make sure you have all the factories + * registered before use. + * @note this 'XML' stuff is quite rough still. YMMV. + */ + RefAST LoadAST( ANTLR_USE_NAMESPACE(std)istream& infile ); +#endif +protected: + void loadChildren( ANTLR_USE_NAMESPACE(std)istream& infile, RefAST current ); + void loadSiblings( ANTLR_USE_NAMESPACE(std)istream& infile, RefAST current ); + bool checkCloseTag( ANTLR_USE_NAMESPACE(std)istream& infile ); + +#ifdef ANTLR_VECTOR_HAS_AT + /// construct a node of 'type' + inline RefAST getNodeOfType( unsigned int type ) + { + return RefAST(nodeFactories.at(type)->second()); + } + /// get the name of the node 'type' + const char* getASTNodeType( unsigned int type ) + { + return nodeFactories.at(type)->first; + } + /// get the factory used for node 'type' + factory_type getASTNodeFactory( unsigned int type ) + { + return nodeFactories.at(type)->second; + } +#else + inline RefAST getNodeOfType( unsigned int type ) + { + return RefAST(nodeFactories[type]->second()); + } + /// get the name of the node 'type' + const char* getASTNodeType( unsigned int type ) + { + return nodeFactories[type]->first; + } + factory_type getASTNodeFactory( unsigned int type ) + { + return nodeFactories[type]->second; + } +#endif + +private: + // no copying and such.. + ASTFactory( const ASTFactory& ); + ASTFactory& operator=( const ASTFactory& ); +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_ASTFactory_hpp__ diff --git a/lib/antlr/antlr/ASTNULLType.hpp b/lib/antlr/antlr/ASTNULLType.hpp new file mode 100644 index 00000000..867f5e5b --- /dev/null +++ b/lib/antlr/antlr/ASTNULLType.hpp @@ -0,0 +1,64 @@ +#ifndef INC_ASTNULLType_hpp__ +#define INC_ASTNULLType_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/AST.hpp> +#include <iostream> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** There is only one instance of this class **/ +class ANTLR_API ASTNULLType : public AST { +public: + const char* typeName( void ) const; + RefAST clone( void ) const; + + void addChild(RefAST c); + size_t getNumberOfChildren() const; + void setFirstChild(RefAST c); + void setNextSibling(RefAST n); + + bool equals(RefAST t) const; + bool equalsList(RefAST t) const; + bool equalsListPartial(RefAST t) const; + bool equalsTree(RefAST t) const; + bool equalsTreePartial(RefAST t) const; + + ANTLR_USE_NAMESPACE(std)vector<RefAST> findAll(RefAST tree); + ANTLR_USE_NAMESPACE(std)vector<RefAST> findAllPartial(RefAST subtree); + + RefAST getFirstChild() const; + RefAST getNextSibling() const; + + ANTLR_USE_NAMESPACE(std)string getText() const; + int getType() const; + + void initialize(int t, const ANTLR_USE_NAMESPACE(std)string& txt); + void initialize(RefAST t); + void initialize(RefToken t); + void initialize(ANTLR_USE_NAMESPACE(std)istream& infile); + + void setText(const ANTLR_USE_NAMESPACE(std)string& text); + void setType(int ttype); + ANTLR_USE_NAMESPACE(std)string toString() const; + ANTLR_USE_NAMESPACE(std)string toStringList() const; + ANTLR_USE_NAMESPACE(std)string toStringTree() const; + + bool attributesToStream( ANTLR_USE_NAMESPACE(std)ostream &out ) const; + void toStream( ANTLR_USE_NAMESPACE(std)ostream &out ) const; +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_ASTNULLType_hpp__ diff --git a/lib/antlr/antlr/ASTPair.hpp b/lib/antlr/antlr/ASTPair.hpp new file mode 100644 index 00000000..d6237902 --- /dev/null +++ b/lib/antlr/antlr/ASTPair.hpp @@ -0,0 +1,57 @@ +#ifndef INC_ASTPair_hpp__ +#define INC_ASTPair_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/AST.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** ASTPair: utility class used for manipulating a pair of ASTs + * representing the current AST root and current AST sibling. + * This exists to compensate for the lack of pointers or 'var' + * arguments in Java. + * + * OK, so we can do those things in C++, but it seems easier + * to stick with the Java way for now. + */ +class ANTLR_API ASTPair { +public: + RefAST root; // current root of tree + RefAST child; // current child to which siblings are added + + /** Make sure that child is the last sibling */ + void advanceChildToEnd() { + if (child) { + while (child->getNextSibling()) { + child = child->getNextSibling(); + } + } + } +// /** Copy an ASTPair. Don't call it clone() because we want type-safety */ +// ASTPair copy() { +// ASTPair tmp = new ASTPair(); +// tmp.root = root; +// tmp.child = child; +// return tmp; +// } + ANTLR_USE_NAMESPACE(std)string toString() const { + ANTLR_USE_NAMESPACE(std)string r = !root ? ANTLR_USE_NAMESPACE(std)string("null") : root->getText(); + ANTLR_USE_NAMESPACE(std)string c = !child ? ANTLR_USE_NAMESPACE(std)string("null") : child->getText(); + return "["+r+","+c+"]"; + } +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_ASTPair_hpp__ diff --git a/lib/antlr/antlr/ASTRefCount.hpp b/lib/antlr/antlr/ASTRefCount.hpp new file mode 100644 index 00000000..f5ef4422 --- /dev/null +++ b/lib/antlr/antlr/ASTRefCount.hpp @@ -0,0 +1,98 @@ +#ifndef INC_ASTRefCount_hpp__ +# define INC_ASTRefCount_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +# include <antlr/config.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + + class AST; + +struct ANTLR_API ASTRef +{ + AST* const ptr; + unsigned int count; + + ASTRef(AST* p); + ~ASTRef(); + ASTRef* increment() + { + ++count; + return this; + } + bool decrement() + { + return (--count==0); + } + + static ASTRef* getRef(const AST* p); +private: + ASTRef( const ASTRef& ); + ASTRef& operator=( const ASTRef& ); +}; + +template<class T> + class ANTLR_API ASTRefCount +{ +private: + ASTRef* ref; + +public: + ASTRefCount(const AST* p=0) + : ref(p ? ASTRef::getRef(p) : 0) + { + } + ASTRefCount(const ASTRefCount<T>& other) + : ref(other.ref ? other.ref->increment() : 0) + { + } + ~ASTRefCount() + { + if (ref && ref->decrement()) + delete ref; + } + ASTRefCount<T>& operator=(AST* other) + { + ASTRef* tmp = ASTRef::getRef(other); + + if (ref && ref->decrement()) + delete ref; + + ref=tmp; + + return *this; + } + ASTRefCount<T>& operator=(const ASTRefCount<T>& other) + { + if( other.ref != ref ) + { + ASTRef* tmp = other.ref ? other.ref->increment() : 0; + + if (ref && ref->decrement()) + delete ref; + + ref=tmp; + } + return *this; + } + + operator T* () const { return ref ? static_cast<T*>(ref->ptr) : 0; } + T* operator->() const { return ref ? static_cast<T*>(ref->ptr) : 0; } + T* get() const { return ref ? static_cast<T*>(ref->ptr) : 0; } +}; + +typedef ASTRefCount<AST> RefAST; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_ASTRefCount_hpp__ diff --git a/lib/antlr/antlr/BaseAST.hpp b/lib/antlr/antlr/BaseAST.hpp new file mode 100644 index 00000000..1bfc4e56 --- /dev/null +++ b/lib/antlr/antlr/BaseAST.hpp @@ -0,0 +1,193 @@ +#ifndef INC_BaseAST_hpp__ +#define INC_BaseAST_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/AST.hpp> + +#include <iostream> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +class ANTLR_API BaseAST; +typedef ASTRefCount<BaseAST> RefBaseAST; + +class ANTLR_API BaseAST : public AST { +public: + BaseAST() : AST() + { + } + BaseAST(const BaseAST& other) + : AST(other) + { + } + virtual ~BaseAST() + { + } + + /// Return the class name + virtual const char* typeName( void ) const = 0; + + /// Clone this AST node. + virtual RefAST clone( void ) const = 0; + + /// Is node t equal to this in terms of token type and text? + virtual bool equals(RefAST t) const; + + /** Is t an exact structural and equals() match of this tree. The + * 'this' reference is considered the start of a sibling list. + */ + virtual bool equalsList(RefAST t) const; + + /** Is 't' a subtree of this list? The siblings of the root are NOT ignored. + */ + virtual bool equalsListPartial(RefAST t) const; + + /** Is tree rooted at 'this' equal to 't'? The siblings of 'this' are + * ignored. + */ + virtual bool equalsTree(RefAST t) const; + + /** Is 't' a subtree of the tree rooted at 'this'? The siblings of + * 'this' are ignored. + */ + virtual bool equalsTreePartial(RefAST t) const; + + /** Walk the tree looking for all exact subtree matches. Return + * an ASTEnumerator that lets the caller walk the list + * of subtree roots found herein. + */ + virtual ANTLR_USE_NAMESPACE(std)vector<RefAST> findAll(RefAST t); + + /** Walk the tree looking for all subtrees. Return + * an ASTEnumerator that lets the caller walk the list + * of subtree roots found herein. + */ + virtual ANTLR_USE_NAMESPACE(std)vector<RefAST> findAllPartial(RefAST t); + + /// Add a node to the end of the child list for this node + virtual void addChild(RefAST c) + { + if( !c ) + return; + + RefBaseAST tmp = down; + + if (tmp) + { + while (tmp->right) + tmp = tmp->right; + tmp->right = c; + } + else + down = c; + } + + /** Get the number of child nodes of this node (shallow e.g. not of the + * whole tree it spans). + */ + virtual size_t getNumberOfChildren() const; + + /// Get the first child of this node; null if no children + virtual RefAST getFirstChild() const + { + return RefAST(down); + } + /// Get the next sibling in line after this one + virtual RefAST getNextSibling() const + { + return RefAST(right); + } + + /// Get the token text for this node + virtual ANTLR_USE_NAMESPACE(std)string getText() const + { + return ""; + } + /// Get the token type for this node + virtual int getType() const + { + return 0; + } + + /// Remove all children + virtual void removeChildren() + { + down = static_cast<BaseAST*>(static_cast<AST*>(nullAST)); + } + + /// Set the first child of a node. + virtual void setFirstChild(RefAST c) + { + down = static_cast<BaseAST*>(static_cast<AST*>(c)); + } + + /// Set the next sibling after this one. + virtual void setNextSibling(RefAST n) + { + right = static_cast<BaseAST*>(static_cast<AST*>(n)); + } + + /// Set the token text for this node + virtual void setText(const ANTLR_USE_NAMESPACE(std)string& txt) + { + } + + /// Set the token type for this node + virtual void setType(int type) + { + } + +#ifdef ANTLR_SUPPORT_XML + /** print attributes of this node to 'out'. Override to customize XML + * output. + * @param out the stream to write the AST attributes to. + */ + virtual bool attributesToStream( ANTLR_USE_NAMESPACE(std)ostream& out ) const; + /** Write this subtree to a stream. Overload this one to customize the XML + * output for AST derived AST-types + * @param output stream + */ + virtual void toStream( ANTLR_USE_NAMESPACE(std)ostream &out ) const; +#endif + + /// Return string representation for the AST + virtual ANTLR_USE_NAMESPACE(std)string toString() const + { + return getText(); + } + + /// Print out a child sibling tree in LISP notation + virtual ANTLR_USE_NAMESPACE(std)string toStringList() const; + virtual ANTLR_USE_NAMESPACE(std)string toStringTree() const; +protected: + RefBaseAST down; + RefBaseAST right; +private: + void doWorkForFindAll(ANTLR_USE_NAMESPACE(std)vector<RefAST>& v, + RefAST target, + bool partialMatch); +}; + +/** Is node t equal to this in terms of token type and text? + */ +inline bool BaseAST::equals(RefAST t) const +{ + if (!t) + return false; + return ((getType() == t->getType()) && (getText() == t->getText())); +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_BaseAST_hpp__ diff --git a/lib/antlr/antlr/BitSet.hpp b/lib/antlr/antlr/BitSet.hpp new file mode 100644 index 00000000..f7619232 --- /dev/null +++ b/lib/antlr/antlr/BitSet.hpp @@ -0,0 +1,60 @@ +#ifndef INC_BitSet_hpp__ +#define INC_BitSet_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <vector> +#include <stdexcept> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** A BitSet to replace java.util.BitSet. + * Primary differences are that most set operators return new sets + * as opposed to oring and anding "in place". Further, a number of + * operations were added. I cannot contain a BitSet because there + * is no way to access the internal bits (which I need for speed) + * and, because it is final, I cannot subclass to add functionality. + * Consider defining set degree. Without access to the bits, I must + * call a method n times to test the ith bit...ack! + * + * Also seems like or() from util is wrong when size of incoming set is bigger + * than this.length. + * + * This is a C++ version of the Java class described above, with only + * a handful of the methods implemented, because we don't need the + * others at runtime. It's really just a wrapper around vector<bool>, + * which should probably be changed to a wrapper around bitset, once + * bitset is more widely available. + * + * @author Terence Parr, MageLang Institute + * @author <br><a href="mailto:pete@yamuna.demon.co.uk">Pete Wells</a> + */ +class ANTLR_API BitSet { +private: + ANTLR_USE_NAMESPACE(std)vector<bool> storage; + +public: + BitSet( unsigned int nbits=64 ); + BitSet( const unsigned long* bits_, unsigned int nlongs); + ~BitSet(); + + void add( unsigned int el ); + + bool member( unsigned int el ) const; + + ANTLR_USE_NAMESPACE(std)vector<unsigned int> toArray() const; +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_BitSet_hpp__ diff --git a/lib/antlr/antlr/CharBuffer.hpp b/lib/antlr/antlr/CharBuffer.hpp new file mode 100644 index 00000000..fe2b1bc5 --- /dev/null +++ b/lib/antlr/antlr/CharBuffer.hpp @@ -0,0 +1,56 @@ +#ifndef INC_CharBuffer_hpp__ +#define INC_CharBuffer_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> + +#include <istream> + +#include <antlr/InputBuffer.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/**A Stream of characters fed to the lexer from a InputStream that can + * be rewound via mark()/rewind() methods. + * <p> + * A dynamic array is used to buffer up all the input characters. Normally, + * "k" characters are stored in the buffer. More characters may be stored + * during guess mode (testing syntactic predicate), or when LT(i>k) is + * referenced. + * Consumption of characters is deferred. In other words, reading the next + * character is not done by consume(), but deferred until needed by LA or LT. + * <p> + * + * @see antlr.CharQueue + */ + +class ANTLR_API CharBuffer : public InputBuffer { +public: + /// Create a character buffer + CharBuffer( ANTLR_USE_NAMESPACE(std)istream& input ); + /// Get the next character from the stream + int getChar(); + +protected: + // character source + ANTLR_USE_NAMESPACE(std)istream& input; + +private: + // NOTE: Unimplemented + CharBuffer(const CharBuffer& other); + CharBuffer& operator=(const CharBuffer& other); +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_CharBuffer_hpp__ diff --git a/lib/antlr/antlr/CharInputBuffer.hpp b/lib/antlr/antlr/CharInputBuffer.hpp new file mode 100644 index 00000000..543972a3 --- /dev/null +++ b/lib/antlr/antlr/CharInputBuffer.hpp @@ -0,0 +1,77 @@ +#ifndef INC_CharInputBuffer_hpp__ +# define INC_CharInputBuffer_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +# include <antlr/config.hpp> +# include <antlr/InputBuffer.hpp> + +# ifdef HAS_NOT_CCTYPE_H +# include <ctype.h> +# else +# include <cctype> +# endif + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** CharInputBuffer.hpp provides an InputBuffer for plain character arrays (buffers). + */ +class CharInputBuffer : public InputBuffer +{ +public: + /** Construct a CharInputBuffer.hpp object with a char* buffer of 'size' + * if 'owner' is true, then the buffer will be delete[]-ed on destruction. + * @note it is assumed the buffer was allocated with new[]! + */ + CharInputBuffer( unsigned char* buf, size_t size, bool owner = false ) + : buffer(buf) + , ptr(buf) + , end(buf + size) + , delete_buffer(owner) + { + } + + /** Destructor + * @note If you're using malloced data, then you probably need to change + * this destructor. Or better use this class as template for your own. + */ + ~CharInputBuffer( void ) + { + if( delete_buffer && buffer ) + delete [] buffer; + } + + /** Reset the CharInputBuffer to initial state + * Called from LexerInputState::reset. + * @see LexerInputState + */ + virtual inline void reset( void ) + { + InputBuffer::reset(); + ptr = buffer; + } + + virtual int getChar( void ) + { + return (ptr < end) ? *ptr++ : EOF; + } + +protected: + unsigned char* buffer; ///< the buffer with data + unsigned char* ptr; ///< position ptr into the buffer + unsigned char* end; ///< end sentry for buffer + bool delete_buffer; ///< flag signifying if we have to delete the buffer +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif diff --git a/lib/antlr/antlr/CharScanner.hpp b/lib/antlr/antlr/CharScanner.hpp new file mode 100644 index 00000000..c426eba2 --- /dev/null +++ b/lib/antlr/antlr/CharScanner.hpp @@ -0,0 +1,575 @@ +#ifndef INC_CharScanner_hpp__ +#define INC_CharScanner_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> + +#include <map> + +#ifdef HAS_NOT_CCTYPE_H +#include <ctype.h> +#else +#include <cctype> +#endif +#include <cstring> + +#if ( _MSC_VER == 1200 ) +// VC6 seems to need this +// note that this is not a standard C++ include file. +# include <stdio.h> +#endif + +#include <antlr/TokenStream.hpp> +#include <antlr/RecognitionException.hpp> +#include <antlr/SemanticException.hpp> +#include <antlr/MismatchedCharException.hpp> +#include <antlr/InputBuffer.hpp> +#include <antlr/BitSet.hpp> +#include <antlr/LexerSharedInputState.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +class ANTLR_API CharScanner; + +ANTLR_C_USING(tolower) + +#ifdef ANTLR_REALLY_NO_STRCASECMP +// Apparently, neither strcasecmp nor stricmp is standard, and Codewarrior +// on the mac has neither... +inline int strcasecmp(const char *s1, const char *s2) +{ + while (true) + { + char c1 = tolower(*s1++), + c2 = tolower(*s2++); + if (c1 < c2) return -1; + if (c1 > c2) return 1; + if (c1 == 0) return 0; + } +} +#else +#ifdef NO_STRCASECMP +ANTLR_C_USING(stricmp) +#else +ANTLR_C_USING(strcasecmp) +#endif +#endif + +/** Functor for the literals map + */ +class ANTLR_API CharScannerLiteralsLess : public ANTLR_USE_NAMESPACE(std)binary_function<ANTLR_USE_NAMESPACE(std)string,ANTLR_USE_NAMESPACE(std)string,bool> { +private: + const CharScanner* scanner; +public: +#ifdef NO_TEMPLATE_PARTS + CharScannerLiteralsLess() {} // not really used, definition to appease MSVC +#endif + CharScannerLiteralsLess(const CharScanner* theScanner) + : scanner(theScanner) + { + } + bool operator() (const ANTLR_USE_NAMESPACE(std)string& x,const ANTLR_USE_NAMESPACE(std)string& y) const; +// defaults are good enough.. + // CharScannerLiteralsLess(const CharScannerLiteralsLess&); + // CharScannerLiteralsLess& operator=(const CharScannerLiteralsLess&); +}; + +/** Superclass of generated lexers + */ +class ANTLR_API CharScanner : public TokenStream { +protected: + typedef RefToken (*factory_type)(); +public: + CharScanner(InputBuffer& cb, bool case_sensitive ); + CharScanner(InputBuffer* cb, bool case_sensitive ); + CharScanner(const LexerSharedInputState& state, bool case_sensitive ); + + virtual ~CharScanner() + { + } + + virtual int LA(unsigned int i); + + virtual void append(char c) + { + if (saveConsumedInput) + { + size_t l = text.length(); + + if ((l%256) == 0) + text.reserve(l+256); + + text.replace(l,0,&c,1); + } + } + + virtual void append(const ANTLR_USE_NAMESPACE(std)string& s) + { + if( saveConsumedInput ) + text += s; + } + + virtual void commit() + { + inputState->getInput().commit(); + } + + /** called by the generated lexer to do error recovery, override to + * customize the behaviour. + */ + virtual void recover(const RecognitionException& ex, const BitSet& tokenSet) + { + consume(); + consumeUntil(tokenSet); + } + + virtual void consume() + { + if (inputState->guessing == 0) + { + int c = LA(1); + if (caseSensitive) + { + append(c); + } + else + { + // use input.LA(), not LA(), to get original case + // CharScanner.LA() would toLower it. + append(inputState->getInput().LA(1)); + } + + // RK: in a sense I don't like this automatic handling. + if (c == '\t') + tab(); + else + inputState->column++; + } + inputState->getInput().consume(); + } + + /** Consume chars until one matches the given char */ + virtual void consumeUntil(int c) + { + for(;;) + { + int la_1 = LA(1); + if( la_1 == EOF_CHAR || la_1 == c ) + break; + consume(); + } + } + + /** Consume chars until one matches the given set */ + virtual void consumeUntil(const BitSet& set) + { + for(;;) + { + int la_1 = LA(1); + if( la_1 == EOF_CHAR || set.member(la_1) ) + break; + consume(); + } + } + + /// Mark the current position and return a id for it + virtual unsigned int mark() + { + return inputState->getInput().mark(); + } + /// Rewind the scanner to a previously marked position + virtual void rewind(unsigned int pos) + { + inputState->getInput().rewind(pos); + } + + /// See if input contains character 'c' throw MismatchedCharException if not + virtual void match(int c) + { + int la_1 = LA(1); + if ( la_1 != c ) + throw MismatchedCharException(la_1, c, false, this); + consume(); + } + + /** See if input contains element from bitset b + * throw MismatchedCharException if not + */ + virtual void match(const BitSet& b) + { + int la_1 = LA(1); + + if ( !b.member(la_1) ) + throw MismatchedCharException( la_1, b, false, this ); + consume(); + } + + /** See if input contains string 's' throw MismatchedCharException if not + * @note the string cannot match EOF + */ + virtual void match( const char* s ) + { + while( *s != '\0' ) + { + // the & 0xFF is here to prevent sign extension lateron + int la_1 = LA(1), c = (*s++ & 0xFF); + + if ( la_1 != c ) + throw MismatchedCharException(la_1, c, false, this); + + consume(); + } + } + /** See if input contains string 's' throw MismatchedCharException if not + * @note the string cannot match EOF + */ + virtual void match(const ANTLR_USE_NAMESPACE(std)string& s) + { + size_t len = s.length(); + + for (size_t i = 0; i < len; i++) + { + // the & 0xFF is here to prevent sign extension lateron + int la_1 = LA(1), c = (s[i] & 0xFF); + + if ( la_1 != c ) + throw MismatchedCharException(la_1, c, false, this); + + consume(); + } + } + /** See if input does not contain character 'c' + * throw MismatchedCharException if not + */ + virtual void matchNot(int c) + { + int la_1 = LA(1); + + if ( la_1 == c ) + throw MismatchedCharException(la_1, c, true, this); + + consume(); + } + /** See if input contains character in range c1-c2 + * throw MismatchedCharException if not + */ + virtual void matchRange(int c1, int c2) + { + int la_1 = LA(1); + + if ( la_1 < c1 || la_1 > c2 ) + throw MismatchedCharException(la_1, c1, c2, false, this); + + consume(); + } + + virtual bool getCaseSensitive() const + { + return caseSensitive; + } + + virtual void setCaseSensitive(bool t) + { + caseSensitive = t; + } + + virtual bool getCaseSensitiveLiterals() const=0; + + /// Get the line the scanner currently is in (starts at 1) + virtual int getLine() const + { + return inputState->line; + } + + /// set the line number + virtual void setLine(int l) + { + inputState->line = l; + } + + /// Get the column the scanner currently is in (starts at 1) + virtual int getColumn() const + { + return inputState->column; + } + /// set the column number + virtual void setColumn(int c) + { + inputState->column = c; + } + + /// get the filename for the file currently used + virtual const ANTLR_USE_NAMESPACE(std)string& getFilename() const + { + return inputState->filename; + } + /// Set the filename the scanner is using (used in error messages) + virtual void setFilename(const ANTLR_USE_NAMESPACE(std)string& f) + { + inputState->filename = f; + } + + virtual bool getCommitToPath() const + { + return commitToPath; + } + + virtual void setCommitToPath(bool commit) + { + commitToPath = commit; + } + + /** return a copy of the current text buffer */ + virtual const ANTLR_USE_NAMESPACE(std)string& getText() const + { + return text; + } + + virtual void setText(const ANTLR_USE_NAMESPACE(std)string& s) + { + text = s; + } + + virtual void resetText() + { + text = ""; + inputState->tokenStartColumn = inputState->column; + inputState->tokenStartLine = inputState->line; + } + + virtual RefToken getTokenObject() const + { + return _returnToken; + } + + /** Used to keep track of line breaks, needs to be called from + * within generated lexers when a \n \r is encountered. + */ + virtual void newline() + { + ++inputState->line; + inputState->column = 1; + } + + /** Advance the current column number by an appropriate amount according + * to the tabsize. This method needs to be explicitly called from the + * lexer rules encountering tabs. + */ + virtual void tab() + { + int c = getColumn(); + int nc = ( ((c-1)/tabsize) + 1) * tabsize + 1; // calculate tab stop + setColumn( nc ); + } + /// set the tabsize. Returns the old tabsize + int setTabsize( int size ) + { + int oldsize = tabsize; + tabsize = size; + return oldsize; + } + /// Return the tabsize used by the scanner + int getTabSize() const + { + return tabsize; + } + + /** Report exception errors caught in nextToken() */ + virtual void reportError(const RecognitionException& e); + + /** Parser error-reporting function can be overridden in subclass */ + virtual void reportError(const ANTLR_USE_NAMESPACE(std)string& s); + + /** Parser warning-reporting function can be overridden in subclass */ + virtual void reportWarning(const ANTLR_USE_NAMESPACE(std)string& s); + + virtual InputBuffer& getInputBuffer() + { + return inputState->getInput(); + } + + virtual LexerSharedInputState getInputState() + { + return inputState; + } + + /** set the input state for the lexer. + * @note state is a reference counted object, hence no reference */ + virtual void setInputState(LexerSharedInputState state) + { + inputState = state; + } + + /// Set the factory for created tokens + virtual void setTokenObjectFactory(factory_type factory) + { + tokenFactory = factory; + } + + /** Test the token text against the literals table + * Override this method to perform a different literals test + */ + virtual int testLiteralsTable(int ttype) const + { + ANTLR_USE_NAMESPACE(std)map<ANTLR_USE_NAMESPACE(std)string,int,CharScannerLiteralsLess>::const_iterator i = literals.find(text); + if (i != literals.end()) + ttype = (*i).second; + return ttype; + } + + /** Test the text passed in against the literals table + * Override this method to perform a different literals test + * This is used primarily when you want to test a portion of + * a token + */ + virtual int testLiteralsTable(const ANTLR_USE_NAMESPACE(std)string& txt,int ttype) const + { + ANTLR_USE_NAMESPACE(std)map<ANTLR_USE_NAMESPACE(std)string,int,CharScannerLiteralsLess>::const_iterator i = literals.find(txt); + if (i != literals.end()) + ttype = (*i).second; + return ttype; + } + + /// Override this method to get more specific case handling + virtual int toLower(int c) const + { + // test on EOF_CHAR for buggy (?) STLPort tolower (or HPUX tolower?) + // also VC++ 6.0 does this. (see fix 422 (is reverted by this fix) + // this one is more structural. Maybe make this configurable. + return (c == EOF_CHAR ? EOF_CHAR : tolower(c)); + } + + /** This method is called by YourLexer::nextToken() when the lexer has + * hit EOF condition. EOF is NOT a character. + * This method is not called if EOF is reached during + * syntactic predicate evaluation or during evaluation + * of normal lexical rules, which presumably would be + * an IOException. This traps the "normal" EOF condition. + * + * uponEOF() is called after the complete evaluation of + * the previous token and only if your parser asks + * for another token beyond that last non-EOF token. + * + * You might want to throw token or char stream exceptions + * like: "Heh, premature eof" or a retry stream exception + * ("I found the end of this file, go back to referencing file"). + */ + virtual void uponEOF() + { + } + + /// Methods used to change tracing behavior + virtual void traceIndent(); + virtual void traceIn(const char* rname); + virtual void traceOut(const char* rname); + +#ifndef NO_STATIC_CONSTS + static const int EOF_CHAR = EOF; +#else + enum { + EOF_CHAR = EOF + }; +#endif +protected: + ANTLR_USE_NAMESPACE(std)string text; ///< Text of current token + /// flag indicating wether consume saves characters + bool saveConsumedInput; + factory_type tokenFactory; ///< Factory for tokens + bool caseSensitive; ///< Is this lexer case sensitive + ANTLR_USE_NAMESPACE(std)map<ANTLR_USE_NAMESPACE(std)string,int,CharScannerLiteralsLess> literals; // set by subclass + + RefToken _returnToken; ///< used to return tokens w/o using return val + + /// Input state, gives access to input stream, shared among different lexers + LexerSharedInputState inputState; + + /** Used during filter mode to indicate that path is desired. + * A subsequent scan error will report an error as usual + * if acceptPath=true; + */ + bool commitToPath; + + int tabsize; ///< tab size the scanner uses. + + /// Create a new RefToken of type t + virtual RefToken makeToken(int t) + { + RefToken tok = tokenFactory(); + tok->setType(t); + tok->setColumn(inputState->tokenStartColumn); + tok->setLine(inputState->tokenStartLine); + return tok; + } + + /** Tracer class, used when -traceLexer is passed to antlr + */ + class Tracer { + private: + CharScanner* parser; + const char* text; + + Tracer(const Tracer& other); // undefined + Tracer& operator=(const Tracer& other); // undefined + public: + Tracer( CharScanner* p,const char* t ) + : parser(p), text(t) + { + parser->traceIn(text); + } + ~Tracer() + { + parser->traceOut(text); + } + }; + + int traceDepth; +private: + CharScanner( const CharScanner& other ); // undefined + CharScanner& operator=( const CharScanner& other ); // undefined + +#ifndef NO_STATIC_CONSTS + static const int NO_CHAR = 0; +#else + enum { + NO_CHAR = 0 + }; +#endif +}; + +inline int CharScanner::LA(unsigned int i) +{ + int c = inputState->getInput().LA(i); + + if ( caseSensitive ) + return c; + else + return toLower(c); // VC 6 tolower bug caught in toLower. +} + +inline bool CharScannerLiteralsLess::operator() (const ANTLR_USE_NAMESPACE(std)string& x,const ANTLR_USE_NAMESPACE(std)string& y) const +{ + if (scanner->getCaseSensitiveLiterals()) + return ANTLR_USE_NAMESPACE(std)less<ANTLR_USE_NAMESPACE(std)string>()(x,y); + else + { +#ifdef NO_STRCASECMP + return (stricmp(x.c_str(),y.c_str())<0); +#else + return (strcasecmp(x.c_str(),y.c_str())<0); +#endif + } +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_CharScanner_hpp__ diff --git a/lib/antlr/antlr/CharStreamException.hpp b/lib/antlr/antlr/CharStreamException.hpp new file mode 100644 index 00000000..a49e3bbb --- /dev/null +++ b/lib/antlr/antlr/CharStreamException.hpp @@ -0,0 +1,29 @@ +#ifndef INC_CharStreamException_hpp__ +#define INC_CharStreamException_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/ANTLRException.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +class ANTLR_API CharStreamException : public ANTLRException { +public: + CharStreamException(const ANTLR_USE_NAMESPACE(std)string& s) + : ANTLRException(s) {} + ~CharStreamException() throw() {} +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_CharStreamException_hpp__ diff --git a/lib/antlr/antlr/CharStreamIOException.hpp b/lib/antlr/antlr/CharStreamIOException.hpp new file mode 100644 index 00000000..e3e3111e --- /dev/null +++ b/lib/antlr/antlr/CharStreamIOException.hpp @@ -0,0 +1,31 @@ +#ifndef INC_CharStreamIOException_hpp__ +#define INC_CharStreamIOException_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/CharStreamException.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +class ANTLR_API CharStreamIOException : public CharStreamException { +public: + ANTLR_USE_NAMESPACE(std)exception io; + + CharStreamIOException(ANTLR_USE_NAMESPACE(std)exception& e) + : CharStreamException(e.what()), io(e) {} + ~CharStreamIOException() throw() {} +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_CharStreamIOException_hpp__ diff --git a/lib/antlr/antlr/CircularQueue.hpp b/lib/antlr/antlr/CircularQueue.hpp new file mode 100644 index 00000000..506b4f56 --- /dev/null +++ b/lib/antlr/antlr/CircularQueue.hpp @@ -0,0 +1,100 @@ +#ifndef INC_CircularQueue_hpp__ +#define INC_CircularQueue_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/Token.hpp> +#include <vector> +#include <cassert> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +// Resize every 5000 items +#define OFFSET_MAX_RESIZE 5000 + +template <class T> +class ANTLR_API CircularQueue { +public: + CircularQueue() + : storage() + , m_offset(0) + { + } + ~CircularQueue() + { + } + + /// Clear the queue + inline void clear( void ) + { + m_offset = 0; + storage.clear(); + } + + /// @todo this should use at or should have a check + inline T elementAt( size_t idx ) const + { + return storage[idx+m_offset]; + } + void removeFirst() + { + if (m_offset >= OFFSET_MAX_RESIZE) + { + storage.erase( storage.begin(), storage.begin() + m_offset + 1 ); + m_offset = 0; + } + else + ++m_offset; + } + inline void removeItems( size_t nb ) + { + // it would be nice if we would not get called with nb > entries + // (or to be precise when entries() == 0) + // This case is possible when lexer/parser::recover() calls + // consume+consumeUntil when the queue is empty. + // In recover the consume says to prepare to read another + // character/token. Then in the subsequent consumeUntil the + // LA() call will trigger + // syncConsume which calls this method *before* the same queue + // has been sufficiently filled. + if( nb > entries() ) + nb = entries(); + + if (m_offset >= OFFSET_MAX_RESIZE) + { + storage.erase( storage.begin(), storage.begin() + m_offset + nb ); + m_offset = 0; + } + else + m_offset += nb; + } + inline void append(const T& t) + { + storage.push_back(t); + } + inline size_t entries() const + { + return storage.size() - m_offset; + } + +private: + ANTLR_USE_NAMESPACE(std)vector<T> storage; + size_t m_offset; + + CircularQueue(const CircularQueue&); + const CircularQueue& operator=(const CircularQueue&); +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_CircularQueue_hpp__ diff --git a/lib/antlr/antlr/CommonAST.hpp b/lib/antlr/antlr/CommonAST.hpp new file mode 100644 index 00000000..091d7b6e --- /dev/null +++ b/lib/antlr/antlr/CommonAST.hpp @@ -0,0 +1,110 @@ +#ifndef INC_CommonAST_hpp__ +#define INC_CommonAST_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/BaseAST.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +class ANTLR_API CommonAST : public BaseAST { +public: + CommonAST() + : BaseAST() + , ttype( Token::INVALID_TYPE ) + , text() + { + } + + CommonAST( RefToken t ) + : BaseAST() + , ttype( t->getType() ) + , text( t->getText() ) + { + } + + CommonAST( const CommonAST& other ) + : BaseAST(other) + , ttype(other.ttype) + , text(other.text) + { + } + + virtual ~CommonAST() + { + } + + virtual const char* typeName( void ) const + { + return CommonAST::TYPE_NAME; + } + + /// Clone this AST node. + virtual RefAST clone( void ) const + { + CommonAST *ast = new CommonAST( *this ); + return RefAST(ast); + } + + virtual ANTLR_USE_NAMESPACE(std)string getText() const + { + return text; + } + virtual int getType() const + { + return ttype; + } + + virtual void initialize( int t, const ANTLR_USE_NAMESPACE(std)string& txt ) + { + setType(t); + setText(txt); + } + + virtual void initialize( RefAST t ) + { + setType(t->getType()); + setText(t->getText()); + } + virtual void initialize( RefToken t ) + { + setType(t->getType()); + setText(t->getText()); + } + +#ifdef ANTLR_SUPPORT_XML + virtual void initialize( ANTLR_USE_NAMESPACE(std)istream& in ); +#endif + + virtual void setText( const ANTLR_USE_NAMESPACE(std)string& txt ) + { + text = txt; + } + virtual void setType( int type ) + { + ttype = type; + } + + static RefAST factory(); + + static const char* const TYPE_NAME; +protected: + int ttype; + ANTLR_USE_NAMESPACE(std)string text; +}; + +typedef ASTRefCount<CommonAST> RefCommonAST; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_CommonAST_hpp__ diff --git a/lib/antlr/antlr/CommonASTWithHiddenTokens.hpp b/lib/antlr/antlr/CommonASTWithHiddenTokens.hpp new file mode 100644 index 00000000..cac7c83e --- /dev/null +++ b/lib/antlr/antlr/CommonASTWithHiddenTokens.hpp @@ -0,0 +1,60 @@ +#ifndef INC_CommonASTWithHiddenTokens_hpp__ +#define INC_CommonASTWithHiddenTokens_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/CommonAST.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** A CommonAST whose initialization copies hidden token + * information from the Token used to create a node. + */ +class ANTLR_API CommonASTWithHiddenTokens : public CommonAST { +public: + CommonASTWithHiddenTokens(); + virtual ~CommonASTWithHiddenTokens(); + virtual const char* typeName( void ) const + { + return CommonASTWithHiddenTokens::TYPE_NAME; + } + /// Clone this AST node. + virtual RefAST clone( void ) const; + + // Borland C++ builder seems to need the decl's of the first two... + virtual void initialize(int t,const ANTLR_USE_NAMESPACE(std)string& txt); + virtual void initialize(RefAST t); + virtual void initialize(RefToken t); + + virtual RefToken getHiddenAfter() const + { + return hiddenAfter; + } + + virtual RefToken getHiddenBefore() const + { + return hiddenBefore; + } + + static RefAST factory(); + + static const char* const TYPE_NAME; +protected: + RefToken hiddenBefore,hiddenAfter; // references to hidden tokens +}; + +typedef ASTRefCount<CommonASTWithHiddenTokens> RefCommonASTWithHiddenTokens; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_CommonASTWithHiddenTokens_hpp__ diff --git a/lib/antlr/antlr/CommonHiddenStreamToken.hpp b/lib/antlr/antlr/CommonHiddenStreamToken.hpp new file mode 100644 index 00000000..f3b78c44 --- /dev/null +++ b/lib/antlr/antlr/CommonHiddenStreamToken.hpp @@ -0,0 +1,41 @@ +#ifndef INC_CommonHiddenStreamToken_hpp__ +#define INC_CommonHiddenStreamToken_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/CommonToken.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +class ANTLR_API CommonHiddenStreamToken : public CommonToken { +protected: + RefToken hiddenBefore; + RefToken hiddenAfter; + +public: + CommonHiddenStreamToken(); + CommonHiddenStreamToken(int t, const ANTLR_USE_NAMESPACE(std)string& txt); + CommonHiddenStreamToken(const ANTLR_USE_NAMESPACE(std)string& s); + + RefToken getHiddenAfter(); + RefToken getHiddenBefore(); + + static RefToken factory(); + + void setHiddenAfter(RefToken t); + void setHiddenBefore(RefToken t); +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_CommonHiddenStreamToken_hpp__ diff --git a/lib/antlr/antlr/CommonToken.hpp b/lib/antlr/antlr/CommonToken.hpp new file mode 100644 index 00000000..51d17b42 --- /dev/null +++ b/lib/antlr/antlr/CommonToken.hpp @@ -0,0 +1,83 @@ +#ifndef INC_CommonToken_hpp__ +#define INC_CommonToken_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/Token.hpp> +#include <string> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +class ANTLR_API CommonToken : public Token { +public: + CommonToken(); + CommonToken(int t, const ANTLR_USE_NAMESPACE(std)string& txt); + CommonToken(const ANTLR_USE_NAMESPACE(std)string& s); + + /// return contents of token + virtual ANTLR_USE_NAMESPACE(std)string getText() const + { + return text; + } + + /// set contents of token + virtual void setText(const ANTLR_USE_NAMESPACE(std)string& s) + { + text = s; + } + + /** get the line the token is at (starting at 1) + * @see CharScanner::newline() + * @see CharScanner::tab() + */ + virtual int getLine() const + { + return line; + } + /** gt the column the token is at (starting at 1) + * @see CharScanner::newline() + * @see CharScanner::tab() + */ + virtual int getColumn() const + { + return col; + } + + /// set line for token + virtual void setLine(int l) + { + line = l; + } + /// set column for token + virtual void setColumn(int c) + { + col = c; + } + + virtual ANTLR_USE_NAMESPACE(std)string toString() const; + static RefToken factory(); + +protected: + // most tokens will want line and text information + int line; + int col; + ANTLR_USE_NAMESPACE(std)string text; + +private: + CommonToken(const CommonToken&); + const CommonToken& operator=(const CommonToken&); +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_CommonToken_hpp__ diff --git a/lib/antlr/antlr/IOException.hpp b/lib/antlr/antlr/IOException.hpp new file mode 100644 index 00000000..22566ecc --- /dev/null +++ b/lib/antlr/antlr/IOException.hpp @@ -0,0 +1,45 @@ +#ifndef INC_IOException_hpp__ +#define INC_IOException_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/ANTLRException.hpp> +#include <exception> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** Generic IOException used inside support code. (thrown by XML I/O routs) + * basically this is something I'm using since a lot of compilers don't + * support ios_base::failure. + */ +class ANTLR_API IOException : public ANTLRException +{ +public: + ANTLR_USE_NAMESPACE(std)exception io; + + IOException( ANTLR_USE_NAMESPACE(std)exception& e ) + : ANTLRException(e.what()) + { + } + IOException( const ANTLR_USE_NAMESPACE(std)string& mesg ) + : ANTLRException(mesg) + { + } + virtual ~IOException() throw() + { + } +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_IOException_hpp__ diff --git a/lib/antlr/antlr/InputBuffer.hpp b/lib/antlr/antlr/InputBuffer.hpp new file mode 100644 index 00000000..b979ef8c --- /dev/null +++ b/lib/antlr/antlr/InputBuffer.hpp @@ -0,0 +1,146 @@ +#ifndef INC_InputBuffer_hpp__ +#define INC_InputBuffer_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/CircularQueue.hpp> +#include <string> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** A Stream of characters fed to the lexer from a InputStream that can + * be rewound via mark()/rewind() methods. + * <p> + * A dynamic array is used to buffer up all the input characters. Normally, + * "k" characters are stored in the buffer. More characters may be stored during + * guess mode (testing syntactic predicate), or when LT(i>k) is referenced. + * Consumption of characters is deferred. In other words, reading the next + * character is not done by conume(), but deferred until needed by LA or LT. + * <p> + * + * @see antlr.CharQueue + */ +class ANTLR_API InputBuffer { +public: + /** Create a character buffer */ + InputBuffer() + : nMarkers(0) + , markerOffset(0) + , numToConsume(0) + { + } + + virtual ~InputBuffer() + { + } + + /// Reset the input buffer to empty state + virtual inline void reset( void ) + { + nMarkers = 0; + markerOffset = 0; + numToConsume = 0; + queue.clear(); + } + + /** This method updates the state of the input buffer so that + * the text matched since the most recent mark() is no longer + * held by the buffer. So, you either do a mark/rewind for + * failed predicate or mark/commit to keep on parsing without + * rewinding the input. + */ + inline void commit( void ) + { + nMarkers--; + } + + /** Mark another character for deferred consumption */ + virtual inline void consume() + { + numToConsume++; + } + + /** Ensure that the character buffer is sufficiently full */ + virtual void fill(unsigned int amount); + + /** Override this in subclasses to get the next character */ + virtual int getChar()=0; + + /** Get a lookahead character */ + virtual inline int LA(unsigned int i) + { + fill(i); + return queue.elementAt(markerOffset + i - 1); + } + + /** Return an integer marker that can be used to rewind the buffer to + * its current state. + */ + virtual unsigned int mark(); + /// Are there any marks active in the InputBuffer + virtual inline bool isMarked() const + { + return (nMarkers != 0); + } + /** Rewind the character buffer to a marker. + * @param mark Marker returned previously from mark() + */ + virtual void rewind(unsigned int mark); + + /** Get the number of non-consumed characters + */ + virtual unsigned int entries() const; + + ANTLR_USE_NAMESPACE(std)string getLAChars() const; + + ANTLR_USE_NAMESPACE(std)string getMarkedChars() const; + +protected: + // char source + // leave to subclasses + + // Number of active markers + unsigned int nMarkers; // = 0; + + // Additional offset used when markers are active + unsigned int markerOffset; // = 0; + + // Number of calls to consume() since last LA() or LT() call + unsigned int numToConsume; // = 0; + + // Circular queue + CircularQueue<int> queue; + + /** Sync up deferred consumption */ + void syncConsume(); + +private: + InputBuffer(const InputBuffer& other); + InputBuffer& operator=(const InputBuffer& other); +}; + +/** Sync up deferred consumption */ +inline void InputBuffer::syncConsume() { + if (numToConsume > 0) + { + if (nMarkers > 0) + markerOffset += numToConsume; + else + queue.removeItems( numToConsume ); + numToConsume = 0; + } +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_InputBuffer_hpp__ diff --git a/lib/antlr/antlr/LLkParser.hpp b/lib/antlr/antlr/LLkParser.hpp new file mode 100644 index 00000000..32acd3dd --- /dev/null +++ b/lib/antlr/antlr/LLkParser.hpp @@ -0,0 +1,67 @@ +#ifndef INC_LLkParser_hpp__ +#define INC_LLkParser_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/Parser.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/**An LL(k) parser. + * + * @see antlr.Token + * @see antlr.TokenBuffer + * @see antlr.LL1Parser + */ +class ANTLR_API LLkParser : public Parser { +public: + LLkParser(const ParserSharedInputState& lexer, int k_); + + LLkParser(TokenBuffer& tokenBuf, int k_); + + LLkParser(TokenStream& lexer, int k_); + + /** Consume another token from the input stream. Can only write sequentially! + * If you need 3 tokens ahead, you must consume() 3 times. + * <p> + * Note that it is possible to overwrite tokens that have not been matched. + * For example, calling consume() 3 times when k=2, means that the first token + * consumed will be overwritten with the 3rd. + */ + virtual inline void consume() + { + inputState->getInput().consume(); + } + + virtual inline int LA(unsigned int i) + { + return inputState->getInput().LA(i); + } + + virtual inline RefToken LT(unsigned int i) + { + return inputState->getInput().LT(i); + } +protected: + /// the lookahead this LL(k) parser is using. + int k; +private: + void trace(const char* ee, const char* rname); +public: + virtual void traceIn(const char* rname); + virtual void traceOut(const char* rname); +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_LLkParser_hpp__ diff --git a/lib/antlr/antlr/LexerSharedInputState.hpp b/lib/antlr/antlr/LexerSharedInputState.hpp new file mode 100644 index 00000000..2571bda3 --- /dev/null +++ b/lib/antlr/antlr/LexerSharedInputState.hpp @@ -0,0 +1,156 @@ +#ifndef INC_LexerSharedInputState_hpp__ +#define INC_LexerSharedInputState_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/InputBuffer.hpp> +#include <antlr/RefCount.hpp> +#include <antlr/CharBuffer.hpp> +#include <string> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** This object contains the data associated with an + * input stream of characters. Multiple lexers + * share a single LexerSharedInputState to lex + * the same input stream. + */ +class ANTLR_API LexerInputState { +public: + /** Construct a new LexerInputState + * @param inbuf the InputBuffer to read from. The object is deleted together + * with the LexerInputState object. + */ + LexerInputState(InputBuffer* inbuf) + : column(1) + , line(1) + , tokenStartColumn(1) + , tokenStartLine(1) + , guessing(0) + , filename("") + , input(inbuf) + , inputResponsible(true) + { + } + + /** Construct a new LexerInputState + * @param inbuf the InputBuffer to read from. + */ + LexerInputState(InputBuffer& inbuf) + : column(1) + , line(1) + , tokenStartColumn(1) + , tokenStartLine(1) + , guessing(0) + , filename("") + , input(&inbuf) + , inputResponsible(false) + { + } + + /** Construct a new LexerInputState + * @param in an istream to read from. + * @see antlr.CharBuffer + */ + LexerInputState(ANTLR_USE_NAMESPACE(std)istream& in) + : column(1) + , line(1) + , tokenStartColumn(1) + , tokenStartLine(1) + , guessing(0) + , filename("") + , input(new CharBuffer(in)) + , inputResponsible(true) + { + } + + /** Reset the LexerInputState with a specified stream and filename. + * This method is a hack, dunno what I was thinking when I added it. + * This should actually be done in a subclass. + * @deprecated + */ + virtual void initialize( ANTLR_USE_NAMESPACE(std)istream& in, const char* file = "" ) + { + column = 1; + line = 1; + tokenStartColumn = 1; + tokenStartLine = 1; + guessing = 0; + filename = file; + + if( input && inputResponsible ) + delete input; + + input = new CharBuffer(in); + inputResponsible = true; + } + + /** Reset the LexerInputState to initial state. + * The underlying InputBuffer is also reset. + */ + virtual void reset( void ) + { + column = 1; + line = 1; + tokenStartColumn = 1; + tokenStartLine = 1; + guessing = 0; + input->reset(); + } + + /** Set the file position of the SharedLexerInputState. + * @param line_ line number to be set + * @param column_ column number to be set + */ + void setPosition( int line_, int column_ ) + { + line = line_; + column = column_; + } + + virtual ~LexerInputState() + { + if (inputResponsible) + delete input; + } + + int column; + int line; + int tokenStartColumn; + int tokenStartLine; + int guessing; + /** What file (if known) caused the problem? */ + ANTLR_USE_NAMESPACE(std)string filename; + InputBuffer& getInput(); +private: + /// Input buffer we use + InputBuffer* input; + /// Who is responsible for cleaning up the InputBuffer? + bool inputResponsible; + + // we don't want these: + LexerInputState(const LexerInputState&); + LexerInputState& operator=(const LexerInputState&); +}; + +inline InputBuffer& LexerInputState::getInput() +{ + return *input; +} + +/// A reference counted LexerInputState object +typedef RefCount<LexerInputState> LexerSharedInputState; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_LexerSharedInputState_hpp__ diff --git a/lib/antlr/antlr/Makefile.am b/lib/antlr/antlr/Makefile.am new file mode 100644 index 00000000..717c43c0 --- /dev/null +++ b/lib/antlr/antlr/Makefile.am @@ -0,0 +1,2 @@ + +noinst_HEADERS = ANTLRException.hpp ANTLRUtil.hpp AST.hpp ASTArray.hpp ASTFactory.hpp ASTNULLType.hpp ASTPair.hpp ASTRefCount.hpp BaseAST.hpp BitSet.hpp CharBuffer.hpp CharInputBuffer.hpp CharScanner.hpp CharStreamException.hpp CharStreamIOException.hpp CircularQueue.hpp CommonAST.hpp CommonASTWithHiddenTokens.hpp CommonHiddenStreamToken.hpp CommonToken.hpp InputBuffer.hpp IOException.hpp LLkParser.hpp LexerSharedInputState.hpp MismatchedCharException.hpp MismatchedTokenException.hpp NoViableAltException.hpp NoViableAltForCharException.hpp Parser.hpp ParserSharedInputState.hpp RecognitionException.hpp RefCount.hpp SemanticException.hpp String.hpp Token.hpp TokenBuffer.hpp TokenStream.hpp TokenStreamBasicFilter.hpp TokenStreamException.hpp TokenStreamHiddenTokenFilter.hpp TokenStreamIOException.hpp TokenStreamRecognitionException.hpp TokenStreamRetryException.hpp TokenStreamSelector.hpp TreeParser.hpp TreeParserSharedInputState.hpp config.hpp diff --git a/lib/antlr/antlr/MismatchedCharException.hpp b/lib/antlr/antlr/MismatchedCharException.hpp new file mode 100644 index 00000000..cc77f9e4 --- /dev/null +++ b/lib/antlr/antlr/MismatchedCharException.hpp @@ -0,0 +1,102 @@ +#ifndef INC_MismatchedCharException_hpp__ +#define INC_MismatchedCharException_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/RecognitionException.hpp> +#include <antlr/BitSet.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +class CharScanner; + +class ANTLR_API MismatchedCharException : public RecognitionException { +public: + // Types of chars +#ifndef NO_STATIC_CONSTS + static const int CHAR = 1; + static const int NOT_CHAR = 2; + static const int RANGE = 3; + static const int NOT_RANGE = 4; + static const int SET = 5; + static const int NOT_SET = 6; +#else + enum { + CHAR = 1, + NOT_CHAR = 2, + RANGE = 3, + NOT_RANGE = 4, + SET = 5, + NOT_SET = 6 + }; +#endif + +public: + // One of the above + int mismatchType; + + // what was found on the input stream + int foundChar; + + // For CHAR/NOT_CHAR and RANGE/NOT_RANGE + int expecting; + + // For RANGE/NOT_RANGE (expecting is lower bound of range) + int upper; + + // For SET/NOT_SET + BitSet set; + +protected: + // who knows...they may want to ask scanner questions + CharScanner* scanner; + +public: + MismatchedCharException(); + + // Expected range / not range + MismatchedCharException( + int c, + int lower, + int upper_, + bool matchNot, + CharScanner* scanner_ + ); + + // Expected token / not token + MismatchedCharException( + int c, + int expecting_, + bool matchNot, + CharScanner* scanner_ + ); + + // Expected BitSet / not BitSet + MismatchedCharException( + int c, + BitSet set_, + bool matchNot, + CharScanner* scanner_ + ); + + ~MismatchedCharException() throw() {} + + /** + * Returns a clean error message (no line number/column information) + */ + ANTLR_USE_NAMESPACE(std)string getMessage() const; +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_MismatchedCharException_hpp__ diff --git a/lib/antlr/antlr/MismatchedTokenException.hpp b/lib/antlr/antlr/MismatchedTokenException.hpp new file mode 100644 index 00000000..47fb44e6 --- /dev/null +++ b/lib/antlr/antlr/MismatchedTokenException.hpp @@ -0,0 +1,144 @@ +#ifndef INC_MismatchedTokenException_hpp__ +#define INC_MismatchedTokenException_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/RecognitionException.hpp> +#include <antlr/BitSet.hpp> +#include <antlr/Token.hpp> +#include <antlr/AST.hpp> +#include <vector> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +class ANTLR_API MismatchedTokenException : public RecognitionException { +public: + MismatchedTokenException(); + + /// Expected range / not range + MismatchedTokenException( + const char* const* tokenNames_, + const int numTokens_, + RefAST node_, + int lower, + int upper_, + bool matchNot + ); + + // Expected token / not token + MismatchedTokenException( + const char* const* tokenNames_, + const int numTokens_, + RefAST node_, + int expecting_, + bool matchNot + ); + + // Expected BitSet / not BitSet + MismatchedTokenException( + const char* const* tokenNames_, + const int numTokens_, + RefAST node_, + BitSet set_, + bool matchNot + ); + + // Expected range / not range + MismatchedTokenException( + const char* const* tokenNames_, + const int numTokens_, + RefToken token_, + int lower, + int upper_, + bool matchNot, + const ANTLR_USE_NAMESPACE(std)string& fileName_ + ); + + // Expected token / not token + MismatchedTokenException( + const char* const* tokenNames_, + const int numTokens_, + RefToken token_, + int expecting_, + bool matchNot, + const ANTLR_USE_NAMESPACE(std)string& fileName_ + ); + + // Expected BitSet / not BitSet + MismatchedTokenException( + const char* const* tokenNames_, + const int numTokens_, + RefToken token_, + BitSet set_, + bool matchNot, + const ANTLR_USE_NAMESPACE(std)string& fileName_ + ); + ~MismatchedTokenException() throw() {} + + /** + * Returns a clean error message (no line number/column information) + */ + ANTLR_USE_NAMESPACE(std)string getMessage() const; + +public: + /// The token that was encountered + const RefToken token; + /// The offending AST node if tree walking + const RefAST node; + /// taken from node or token object + ANTLR_USE_NAMESPACE(std)string tokenText; + + /// Types of tokens +#ifndef NO_STATIC_CONSTS + static const int TOKEN = 1; + static const int NOT_TOKEN = 2; + static const int RANGE = 3; + static const int NOT_RANGE = 4; + static const int SET = 5; + static const int NOT_SET = 6; +#else + enum { + TOKEN = 1, + NOT_TOKEN = 2, + RANGE = 3, + NOT_RANGE = 4, + SET = 5, + NOT_SET = 6 + }; +#endif + +public: + /// One of the above + int mismatchType; + + /// For TOKEN/NOT_TOKEN and RANGE/NOT_RANGE + int expecting; + + /// For RANGE/NOT_RANGE (expecting is lower bound of range) + int upper; + + /// For SET/NOT_SET + BitSet set; + +private: + /// Token names array for formatting + const char* const* tokenNames; + /// Max number of tokens in tokenNames + const int numTokens; + /// Return token name for tokenType + ANTLR_USE_NAMESPACE(std)string tokenName(int tokenType) const; +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_MismatchedTokenException_hpp__ diff --git a/lib/antlr/antlr/NoViableAltException.hpp b/lib/antlr/antlr/NoViableAltException.hpp new file mode 100644 index 00000000..22236c90 --- /dev/null +++ b/lib/antlr/antlr/NoViableAltException.hpp @@ -0,0 +1,40 @@ +#ifndef INC_NoViableAltException_hpp__ +#define INC_NoViableAltException_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/RecognitionException.hpp> +#include <antlr/Token.hpp> +#include <antlr/AST.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +class ANTLR_API NoViableAltException : public RecognitionException { +public: + const RefToken token; + const RefAST node; // handles parsing and treeparsing + + NoViableAltException(RefAST t); + NoViableAltException(RefToken t,const ANTLR_USE_NAMESPACE(std)string& fileName_); + + ~NoViableAltException() throw() {} + + /** + * Returns a clean error message (no line number/column information) + */ + ANTLR_USE_NAMESPACE(std)string getMessage() const; +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_NoViableAltException_hpp__ diff --git a/lib/antlr/antlr/NoViableAltForCharException.hpp b/lib/antlr/antlr/NoViableAltForCharException.hpp new file mode 100644 index 00000000..0b16a0ea --- /dev/null +++ b/lib/antlr/antlr/NoViableAltForCharException.hpp @@ -0,0 +1,41 @@ +#ifndef INC_NoViableAltForCharException_hpp__ +# define INC_NoViableAltForCharException_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +# include <antlr/config.hpp> +# include <antlr/RecognitionException.hpp> +# include <antlr/CharScanner.hpp> + +# ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr +{ +# endif + +class ANTLR_API NoViableAltForCharException : public RecognitionException +{ +public: + NoViableAltForCharException(int c, CharScanner* scanner); + NoViableAltForCharException(int c, const ANTLR_USE_NAMESPACE(std)string& fileName_, + int line_, int column_); + + virtual ~NoViableAltForCharException() throw() + { + } + + /// Returns a clean error message (no line number/column information) + ANTLR_USE_NAMESPACE(std)string getMessage() const; +protected: + int foundChar; +}; + +# ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +# endif + +#endif //INC_NoViableAltForCharException_hpp__ diff --git a/lib/antlr/antlr/Parser.hpp b/lib/antlr/antlr/Parser.hpp new file mode 100644 index 00000000..f74be2a8 --- /dev/null +++ b/lib/antlr/antlr/Parser.hpp @@ -0,0 +1,319 @@ +#ifndef INC_Parser_hpp__ +#define INC_Parser_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> + +#include <iostream> +#include <exception> + +#include <antlr/BitSet.hpp> +#include <antlr/TokenBuffer.hpp> +#include <antlr/RecognitionException.hpp> +#include <antlr/MismatchedTokenException.hpp> +#include <antlr/ASTFactory.hpp> +#include <antlr/ParserSharedInputState.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +extern bool DEBUG_PARSER; + +/** A generic ANTLR parser (LL(k) for k>=1) containing a bunch of + * utility routines useful at any lookahead depth. We distinguish between + * the LL(1) and LL(k) parsers because of efficiency. This may not be + * necessary in the near future. + * + * Each parser object contains the state of the parse including a lookahead + * cache (the form of which is determined by the subclass), whether or + * not the parser is in guess mode, where tokens come from, etc... + * + * <p> + * During <b>guess</b> mode, the current lookahead token(s) and token type(s) + * cache must be saved because the token stream may not have been informed + * to save the token (via <tt>mark</tt>) before the <tt>try</tt> block. + * Guessing is started by: + * <ol> + * <li>saving the lookahead cache. + * <li>marking the current position in the TokenBuffer. + * <li>increasing the guessing level. + * </ol> + * + * After guessing, the parser state is restored by: + * <ol> + * <li>restoring the lookahead cache. + * <li>rewinding the TokenBuffer. + * <li>decreasing the guessing level. + * </ol> + * + * @see antlr.Token + * @see antlr.TokenBuffer + * @see antlr.TokenStream + * @see antlr.LL1Parser + * @see antlr.LLkParser + * + * @todo add constructors with ASTFactory. + */ +class ANTLR_API Parser { +protected: + Parser(TokenBuffer& input) + : inputState(new ParserInputState(input)), astFactory(0), traceDepth(0) + { + } + Parser(TokenBuffer* input) + : inputState(new ParserInputState(input)), astFactory(0), traceDepth(0) + { + } + Parser(const ParserSharedInputState& state) + : inputState(state), astFactory(0), traceDepth(0) + { + } +public: + virtual ~Parser() + { + } + + /** Return the token type of the ith token of lookahead where i=1 + * is the current token being examined by the parser (i.e., it + * has not been matched yet). + */ + virtual int LA(unsigned int i)=0; + + /// Return the i-th token of lookahead + virtual RefToken LT(unsigned int i)=0; + + /** DEPRECATED! Specify the factory to be used during tree building. (Compulsory) + * Setting the factory is nowadays compulsory. + * @see setASTFactory + */ + virtual void setASTNodeFactory( ASTFactory *factory ) + { + astFactory = factory; + } + /** Specify the factory to be used during tree building. (Compulsory) + * Setting the factory is nowadays compulsory. + */ + virtual void setASTFactory( ASTFactory *factory ) + { + astFactory = factory; + } + /** Return a pointer to the ASTFactory used. + * So you might use it in subsequent treewalkers or to reload AST's + * from disk. + */ + virtual ASTFactory* getASTFactory() + { + return astFactory; + } + /** Get the root AST node of the generated AST. When using a custom AST type + * or heterogenous AST's, you'll have to convert it to the right type + * yourself. + */ + virtual RefAST getAST() = 0; + + /// Return the filename of the input file. + virtual inline ANTLR_USE_NAMESPACE(std)string getFilename() const + { + return inputState->filename; + } + /// Set the filename of the input file (used for error reporting). + virtual void setFilename(const ANTLR_USE_NAMESPACE(std)string& f) + { + inputState->filename = f; + } + + virtual void setInputState(ParserSharedInputState state) + { + inputState = state; + } + virtual inline ParserSharedInputState getInputState() const + { + return inputState; + } + + /// Get another token object from the token stream + virtual void consume()=0; + /// Consume tokens until one matches the given token + virtual void consumeUntil(int tokenType) + { + while (LA(1) != Token::EOF_TYPE && LA(1) != tokenType) + consume(); + } + + /// Consume tokens until one matches the given token set + virtual void consumeUntil(const BitSet& set) + { + while (LA(1) != Token::EOF_TYPE && !set.member(LA(1))) + consume(); + } + + /** Make sure current lookahead symbol matches token type <tt>t</tt>. + * Throw an exception upon mismatch, which is catch by either the + * error handler or by the syntactic predicate. + */ + virtual void match(int t) + { + if ( DEBUG_PARSER ) + { + traceIndent(); + ANTLR_USE_NAMESPACE(std)cout << "enter match(" << t << ") with LA(1)=" << LA(1) << ANTLR_USE_NAMESPACE(std)endl; + } + if ( LA(1) != t ) + { + if ( DEBUG_PARSER ) + { + traceIndent(); + ANTLR_USE_NAMESPACE(std)cout << "token mismatch: " << LA(1) << "!=" << t << ANTLR_USE_NAMESPACE(std)endl; + } + throw MismatchedTokenException(getTokenNames(), getNumTokens(), LT(1), t, false, getFilename()); + } + else + { + // mark token as consumed -- fetch next token deferred until LA/LT + consume(); + } + } + + virtual void matchNot(int t) + { + if ( LA(1)==t ) + { + // Throws inverted-sense exception + throw MismatchedTokenException(getTokenNames(), getNumTokens(), LT(1), t, true, getFilename()); + } + else + { + // mark token as consumed -- fetch next token deferred until LA/LT + consume(); + } + } + + /** Make sure current lookahead symbol matches the given set + * Throw an exception upon mismatch, which is catch by either the + * error handler or by the syntactic predicate. + */ + virtual void match(const BitSet& b) + { + if ( DEBUG_PARSER ) + { + traceIndent(); + ANTLR_USE_NAMESPACE(std)cout << "enter match(" << "bitset" /*b.toString()*/ + << ") with LA(1)=" << LA(1) << ANTLR_USE_NAMESPACE(std)endl; + } + if ( !b.member(LA(1)) ) + { + if ( DEBUG_PARSER ) + { + traceIndent(); + ANTLR_USE_NAMESPACE(std)cout << "token mismatch: " << LA(1) << " not member of " + << "bitset" /*b.toString()*/ << ANTLR_USE_NAMESPACE(std)endl; + } + throw MismatchedTokenException(getTokenNames(), getNumTokens(), LT(1), b, false, getFilename()); + } + else + { + // mark token as consumed -- fetch next token deferred until LA/LT + consume(); + } + } + + /** Mark a spot in the input and return the position. + * Forwarded to TokenBuffer. + */ + virtual inline unsigned int mark() + { + return inputState->getInput().mark(); + } + /// rewind to a previously marked position + virtual inline void rewind(unsigned int pos) + { + inputState->getInput().rewind(pos); + } + /** called by the generated parser to do error recovery, override to + * customize the behaviour. + */ + virtual void recover(const RecognitionException& ex, const BitSet& tokenSet) + { + consume(); + consumeUntil(tokenSet); + } + + /// Parser error-reporting function can be overridden in subclass + virtual void reportError(const RecognitionException& ex); + /// Parser error-reporting function can be overridden in subclass + virtual void reportError(const ANTLR_USE_NAMESPACE(std)string& s); + /// Parser warning-reporting function can be overridden in subclass + virtual void reportWarning(const ANTLR_USE_NAMESPACE(std)string& s); + + /// get the token name for the token number 'num' + virtual const char* getTokenName(int num) const = 0; + /// get a vector with all token names + virtual const char* const* getTokenNames() const = 0; + /** Get the number of tokens defined. + * This one should be overridden in subclasses. + */ + virtual int getNumTokens(void) const = 0; + + /** Set or change the input token buffer */ +// void setTokenBuffer(TokenBuffer<Token>* t); + + virtual void traceIndent(); + virtual void traceIn(const char* rname); + virtual void traceOut(const char* rname); +protected: +// void setTokenNames(const char** tokenNames_); + + ParserSharedInputState inputState; + +// /// AST return value for a rule is squirreled away here +// RefAST returnAST; + + /// AST support code; parser and treeparser delegate to this object + ASTFactory *astFactory; + + // used to keep track of the indentation for the trace + int traceDepth; + + /** Utility class which allows tracing to work even when exceptions are + * thrown. + */ + class Tracer { /*{{{*/ + private: + Parser* parser; + const char* text; + public: + Tracer(Parser* p,const char * t) + : parser(p), text(t) + { + parser->traceIn(text); + } + ~Tracer() + { +#ifdef ANTLR_CXX_SUPPORTS_UNCAUGHT_EXCEPTION + // Only give trace if there's no uncaught exception.. + if(!ANTLR_USE_NAMESPACE(std)uncaught_exception()) +#endif + parser->traceOut(text); + } + private: + Tracer(const Tracer&); // undefined + const Tracer& operator=(const Tracer&); // undefined + /*}}}*/ + }; +private: + Parser(const Parser&); // undefined + const Parser& operator=(const Parser&); // undefined +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_Parser_hpp__ diff --git a/lib/antlr/antlr/ParserSharedInputState.hpp b/lib/antlr/antlr/ParserSharedInputState.hpp new file mode 100644 index 00000000..b011bc15 --- /dev/null +++ b/lib/antlr/antlr/ParserSharedInputState.hpp @@ -0,0 +1,92 @@ +#ifndef INC_ParserSharedInputState_hpp__ +#define INC_ParserSharedInputState_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/TokenBuffer.hpp> +#include <antlr/RefCount.hpp> +#include <string> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** This object contains the data associated with an + * input stream of tokens. Multiple parsers + * share a single ParserSharedInputState to parse + * the same stream of tokens. + */ +class ANTLR_API ParserInputState { +public: + /** Construct a new ParserInputState + * @param in the TokenBuffer to read from. The object is deleted together + * with the ParserInputState object. + */ + ParserInputState( TokenBuffer* in ) + : guessing(0) + , filename() + , input(in) + , inputResponsible(true) + { + } + /** Construct a new ParserInputState + * @param in the TokenBuffer to read from. + */ + ParserInputState( TokenBuffer& in ) + : guessing(0) + , filename("") + , input(&in) + , inputResponsible(false) + { + } + + virtual ~ParserInputState() + { + if (inputResponsible) + delete input; + } + + TokenBuffer& getInput( void ) + { + return *input; + } + + /// Reset the ParserInputState and the underlying TokenBuffer + void reset( void ) + { + input->reset(); + guessing = 0; + } + +public: + /** Are we guessing (guessing>0)? */ + int guessing; + /** What file (if known) caused the problem? + * @todo wrap this one.. + */ + ANTLR_USE_NAMESPACE(std)string filename; +private: + /** Where to get token objects */ + TokenBuffer* input; + /// Do we need to free the TokenBuffer or is it owned by another.. + bool inputResponsible; + + // we don't want these: + ParserInputState(const ParserInputState&); + ParserInputState& operator=(const ParserInputState&); +}; + +/// A reference counted ParserInputState +typedef RefCount<ParserInputState> ParserSharedInputState; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_ParserSharedInputState_hpp__ diff --git a/lib/antlr/antlr/RecognitionException.hpp b/lib/antlr/antlr/RecognitionException.hpp new file mode 100644 index 00000000..e61bc620 --- /dev/null +++ b/lib/antlr/antlr/RecognitionException.hpp @@ -0,0 +1,66 @@ +#ifndef INC_RecognitionException_hpp__ +# define INC_RecognitionException_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +# include <antlr/config.hpp> +# include <antlr/ANTLRException.hpp> + +# ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr +{ +# endif + class ANTLR_API RecognitionException : public ANTLRException + { + public: + RecognitionException(); + RecognitionException(const ANTLR_USE_NAMESPACE(std)string& s); + RecognitionException(const ANTLR_USE_NAMESPACE(std)string& s, + const ANTLR_USE_NAMESPACE(std)string& fileName, + int line, int column ); + + virtual ~RecognitionException() throw() + { + } + + /// Return file where mishap occurred. + virtual ANTLR_USE_NAMESPACE(std)string getFilename() const throw() + { + return fileName; + } + /** + * @return the line number that this exception happened on. + */ + virtual int getLine() const throw() + { + return line; + } + /** + * @return the column number that this exception happened on. + */ + virtual int getColumn() const throw() + { + return column; + } + + /// Return complete error message with line/column number info (if present) + virtual ANTLR_USE_NAMESPACE(std)string toString() const; + + /// See what file/line/column info is present and return it as a string + virtual ANTLR_USE_NAMESPACE(std)string getFileLineColumnString() const; + protected: + ANTLR_USE_NAMESPACE(std)string fileName; // not used by treeparsers + int line; // not used by treeparsers + int column; // not used by treeparsers + }; + +# ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +# endif + +#endif //INC_RecognitionException_hpp__ diff --git a/lib/antlr/antlr/RefCount.hpp b/lib/antlr/antlr/RefCount.hpp new file mode 100644 index 00000000..8546a049 --- /dev/null +++ b/lib/antlr/antlr/RefCount.hpp @@ -0,0 +1,80 @@ +#ifndef INC_RefCount_hpp__ +#define INC_RefCount_hpp__ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +template<class T> +class ANTLR_API RefCount { +private: + struct Ref { + T* const ptr; + unsigned int count; + + Ref(T* p) : ptr(p), count(1) {} + ~Ref() {delete ptr;} + Ref* increment() {++count;return this;} + bool decrement() {return (--count==0);} + private: + Ref(const Ref&); + Ref& operator=(const Ref&); + }* ref; + +public: + explicit RefCount(T* p = 0) + : ref(p ? new Ref(p) : 0) + { + } + RefCount(const RefCount<T>& other) + : ref(other.ref ? other.ref->increment() : 0) + { + } + ~RefCount() + { + if (ref && ref->decrement()) + delete ref; + } + RefCount<T>& operator=(const RefCount<T>& other) + { + Ref* tmp = other.ref ? other.ref->increment() : 0; + if (ref && ref->decrement()) + delete ref; + ref = tmp; + return *this; + } + + operator T* () const + { + return ref ? ref->ptr : 0; + } + + T* operator->() const + { + return ref ? ref->ptr : 0; + } + + T* get() const + { + return ref ? ref->ptr : 0; + } + + template<class newType> operator RefCount<newType>() + { + return RefCount<newType>(ref); + } +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_RefCount_hpp__ diff --git a/lib/antlr/antlr/SemanticException.hpp b/lib/antlr/antlr/SemanticException.hpp new file mode 100644 index 00000000..dc68d5a9 --- /dev/null +++ b/lib/antlr/antlr/SemanticException.hpp @@ -0,0 +1,40 @@ +#ifndef INC_SemanticException_hpp__ +#define INC_SemanticException_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/RecognitionException.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +class ANTLR_API SemanticException : public RecognitionException { +public: + SemanticException(const ANTLR_USE_NAMESPACE(std)string& s) + : RecognitionException(s) + { + } + SemanticException(const ANTLR_USE_NAMESPACE(std)string& s, + const ANTLR_USE_NAMESPACE(std)string& fileName_, + int line_,int column_) + : RecognitionException(s,fileName_,line_,column_) + { + } + + ~SemanticException() throw() + { + } +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_SemanticException_hpp__ diff --git a/lib/antlr/antlr/String.hpp b/lib/antlr/antlr/String.hpp new file mode 100644 index 00000000..7e6dde5a --- /dev/null +++ b/lib/antlr/antlr/String.hpp @@ -0,0 +1,27 @@ +#ifndef INC_String_hpp__ +#define INC_String_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <string> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +ANTLR_API ANTLR_USE_NAMESPACE(std)string operator+( const ANTLR_USE_NAMESPACE(std)string& lhs, const int rhs ); +ANTLR_API ANTLR_USE_NAMESPACE(std)string operator+( const ANTLR_USE_NAMESPACE(std)string& lhs, size_t rhs ); + +ANTLR_API ANTLR_USE_NAMESPACE(std)string charName( int ch ); + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_String_hpp__ diff --git a/lib/antlr/antlr/Token.hpp b/lib/antlr/antlr/Token.hpp new file mode 100644 index 00000000..cd56fc3c --- /dev/null +++ b/lib/antlr/antlr/Token.hpp @@ -0,0 +1,108 @@ +#ifndef INC_Token_hpp__ +#define INC_Token_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/TokenRefCount.hpp> +#include <string> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +struct TokenRef; + +/** A token is minimally a token type. Subclasses can add the text matched + * for the token and line info. + */ +class ANTLR_API Token +{ +public: + // constants +#ifndef NO_STATIC_CONSTS + static const int MIN_USER_TYPE = 4; + static const int NULL_TREE_LOOKAHEAD = 3; + static const int INVALID_TYPE = 0; + static const int EOF_TYPE = 1; + static const int SKIP = -1; +#else + enum { + MIN_USER_TYPE = 4, + NULL_TREE_LOOKAHEAD = 3, + INVALID_TYPE = 0, + EOF_TYPE = 1, + SKIP = -1 + }; +#endif + + Token() + : ref(0) + , type(INVALID_TYPE) + { + } + Token(int t) + : ref(0) + , type(t) + { + } + Token(int t, const ANTLR_USE_NAMESPACE(std)string& txt) + : ref(0) + , type(t) + { + setText(txt); + } + virtual ~Token() + { + } + + virtual int getColumn() const; + virtual int getLine() const; + virtual ANTLR_USE_NAMESPACE(std)string getText() const; + virtual const ANTLR_USE_NAMESPACE(std)string& getFilename() const; + virtual int getType() const; + + virtual void setColumn(int c); + + virtual void setLine(int l); + virtual void setText(const ANTLR_USE_NAMESPACE(std)string& t); + virtual void setType(int t); + + virtual void setFilename( const std::string& file ); + + virtual ANTLR_USE_NAMESPACE(std)string toString() const; + +private: + friend struct TokenRef; + TokenRef* ref; + + int type; ///< the type of the token + + Token(RefToken other); + Token& operator=(const Token& other); + Token& operator=(RefToken other); + + Token(const Token&); +}; + +extern ANTLR_API RefToken nullToken; + +#ifdef NEEDS_OPERATOR_LESS_THAN +// RK: Added after 2.7.2 previously it was undefined. +// AL: what to return if l and/or r point to nullToken??? +inline bool operator<( RefToken l, RefToken r ) +{ + return nullToken == l ? ( nullToken == r ? false : true ) : l->getType() < r->getType(); +} +#endif + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_Token_hpp__ diff --git a/lib/antlr/antlr/TokenBuffer.hpp b/lib/antlr/antlr/TokenBuffer.hpp new file mode 100644 index 00000000..3077eead --- /dev/null +++ b/lib/antlr/antlr/TokenBuffer.hpp @@ -0,0 +1,121 @@ +#ifndef INC_TokenBuffer_hpp__ +#define INC_TokenBuffer_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/TokenStream.hpp> +#include <antlr/CircularQueue.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/**A Stream of Token objects fed to the parser from a TokenStream that can + * be rewound via mark()/rewind() methods. + * <p> + * A dynamic array is used to buffer up all the input tokens. Normally, + * "k" tokens are stored in the buffer. More tokens may be stored during + * guess mode (testing syntactic predicate), or when LT(i>k) is referenced. + * Consumption of tokens is deferred. In other words, reading the next + * token is not done by conume(), but deferred until needed by LA or LT. + * <p> + * + * @todo: see if we can integrate this one with InputBuffer into one template + * or so. + * + * @see antlr.Token + * @see antlr.TokenStream + * @see antlr.TokenQueue + */ +class ANTLR_API TokenBuffer { +public: + /** Create a token buffer */ + TokenBuffer(TokenStream& input_); + virtual ~TokenBuffer(); + + /// Reset the input buffer to empty state + inline void reset( void ) + { + nMarkers = 0; + markerOffset = 0; + numToConsume = 0; + queue.clear(); + } + + /** Get a lookahead token value */ + int LA( unsigned int i ); + + /** Get a lookahead token */ + RefToken LT( unsigned int i ); + + /** Return an integer marker that can be used to rewind the buffer to + * its current state. + */ + unsigned int mark(); + + /**Rewind the token buffer to a marker. + * @param mark Marker returned previously from mark() + */ + void rewind(unsigned int mark); + + /** Mark another token for deferred consumption */ + inline void consume() + { + numToConsume++; + } + + /// Return the number of entries in the TokenBuffer + virtual unsigned int entries() const; + +private: + /** Ensure that the token buffer is sufficiently full */ + void fill(unsigned int amount); + /** Sync up deferred consumption */ + void syncConsume(); + +protected: + /// Token source + TokenStream& input; + + /// Number of active markers + unsigned int nMarkers; + + /// Additional offset used when markers are active + unsigned int markerOffset; + + /// Number of calls to consume() since last LA() or LT() call + unsigned int numToConsume; + + /// Circular queue with Tokens + CircularQueue<RefToken> queue; + +private: + TokenBuffer(const TokenBuffer& other); + const TokenBuffer& operator=(const TokenBuffer& other); +}; + +/** Sync up deferred consumption */ +inline void TokenBuffer::syncConsume() +{ + if (numToConsume > 0) + { + if (nMarkers > 0) + markerOffset += numToConsume; + else + queue.removeItems( numToConsume ); + + numToConsume = 0; + } +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_TokenBuffer_hpp__ diff --git a/lib/antlr/antlr/TokenRefCount.hpp b/lib/antlr/antlr/TokenRefCount.hpp new file mode 100644 index 00000000..9ccbb98c --- /dev/null +++ b/lib/antlr/antlr/TokenRefCount.hpp @@ -0,0 +1,98 @@ +#ifndef INC_TokenRefCount_hpp__ +# define INC_TokenRefCount_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id:$ + */ + +# include <antlr/config.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +class Token; + +struct ANTLR_API TokenRef +{ + Token* const ptr; + unsigned int count; + + TokenRef(Token* p); + ~TokenRef(); + TokenRef* increment() + { + ++count; + return this; + } + bool decrement() + { + return (--count==0); + } + + static TokenRef* getRef(const Token* p); +private: + TokenRef( const TokenRef& ); + TokenRef& operator=( const TokenRef& ); +}; + +template<class T> + class ANTLR_API TokenRefCount +{ +private: + TokenRef* ref; + +public: + TokenRefCount(const Token* p=0) + : ref(p ? TokenRef::getRef(p) : 0) + { + } + TokenRefCount(const TokenRefCount<T>& other) + : ref(other.ref ? other.ref->increment() : 0) + { + } + ~TokenRefCount() + { + if (ref && ref->decrement()) + delete ref; + } + TokenRefCount<T>& operator=(Token* other) + { + TokenRef* tmp = TokenRef::getRef(other); + + if (ref && ref->decrement()) + delete ref; + + ref=tmp; + + return *this; + } + TokenRefCount<T>& operator=(const TokenRefCount<T>& other) + { + if( other.ref != ref ) + { + TokenRef* tmp = other.ref ? other.ref->increment() : 0; + + if (ref && ref->decrement()) + delete ref; + + ref=tmp; + } + return *this; + } + + operator T* () const { return ref ? static_cast<T*>(ref->ptr) : 0; } + T* operator->() const { return ref ? static_cast<T*>(ref->ptr) : 0; } + T* get() const { return ref ? static_cast<T*>(ref->ptr) : 0; } +}; + +typedef TokenRefCount<Token> RefToken; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_TokenRefCount_hpp__ diff --git a/lib/antlr/antlr/TokenStream.hpp b/lib/antlr/antlr/TokenStream.hpp new file mode 100644 index 00000000..dbf83a8c --- /dev/null +++ b/lib/antlr/antlr/TokenStream.hpp @@ -0,0 +1,34 @@ +#ifndef INC_TokenStream_hpp__ +#define INC_TokenStream_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/Token.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** This interface allows any object to pretend it is a stream + * of tokens. + * @author Terence Parr, MageLang Institute + */ +class ANTLR_API TokenStream { +public: + virtual RefToken nextToken()=0; + virtual ~TokenStream() + { + } +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_TokenStream_hpp__ diff --git a/lib/antlr/antlr/TokenStreamBasicFilter.hpp b/lib/antlr/antlr/TokenStreamBasicFilter.hpp new file mode 100644 index 00000000..bcdb131c --- /dev/null +++ b/lib/antlr/antlr/TokenStreamBasicFilter.hpp @@ -0,0 +1,46 @@ +#ifndef INC_TokenStreamBasicFilter_hpp__ +#define INC_TokenStreamBasicFilter_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/BitSet.hpp> +#include <antlr/TokenStream.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** This object is a TokenStream that passes through all + * tokens except for those that you tell it to discard. + * There is no buffering of the tokens. + */ +class ANTLR_API TokenStreamBasicFilter : public TokenStream { + /** The set of token types to discard */ +protected: + BitSet discardMask; + + /** The input stream */ +protected: + TokenStream* input; + +public: + TokenStreamBasicFilter(TokenStream& input_); + + void discard(int ttype); + + void discard(const BitSet& mask); + + RefToken nextToken(); +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_TokenStreamBasicFilter_hpp__ diff --git a/lib/antlr/antlr/TokenStreamException.hpp b/lib/antlr/antlr/TokenStreamException.hpp new file mode 100644 index 00000000..dbddb442 --- /dev/null +++ b/lib/antlr/antlr/TokenStreamException.hpp @@ -0,0 +1,41 @@ +#ifndef INC_TokenStreamException_hpp__ +#define INC_TokenStreamException_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/ANTLRException.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** Baseclass for exceptions thrown by classes implementing the TokenStream + * interface. + * @see TokenStream + */ +class ANTLR_API TokenStreamException : public ANTLRException { +public: + TokenStreamException() + : ANTLRException() + { + } + TokenStreamException(const ANTLR_USE_NAMESPACE(std)string& s) + : ANTLRException(s) + { + } + virtual ~TokenStreamException() throw() + { + } +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_TokenStreamException_hpp__ diff --git a/lib/antlr/antlr/TokenStreamHiddenTokenFilter.hpp b/lib/antlr/antlr/TokenStreamHiddenTokenFilter.hpp new file mode 100644 index 00000000..ed007a7a --- /dev/null +++ b/lib/antlr/antlr/TokenStreamHiddenTokenFilter.hpp @@ -0,0 +1,95 @@ +#ifndef INC_TokenStreamHiddenTokenFilter_hpp__ +#define INC_TokenStreamHiddenTokenFilter_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/TokenStreamBasicFilter.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/**This object filters a token stream coming from a lexer + * or another TokenStream so that only certain token channels + * get transmitted to the parser. + * + * Any of the channels can be filtered off as "hidden" channels whose + * tokens can be accessed from the parser. + */ +class ANTLR_API TokenStreamHiddenTokenFilter : public TokenStreamBasicFilter { + // protected BitSet discardMask; +protected: + BitSet hideMask; + +private: + RefToken nextMonitoredToken; + +protected: + /** track tail of hidden list emanating from previous + * monitored token + */ + RefToken lastHiddenToken; + + RefToken firstHidden; // = null; + +public: + TokenStreamHiddenTokenFilter(TokenStream& input); + +protected: + void consume(); + +private: + void consumeFirst(); + +public: + BitSet getDiscardMask() const; + + /** Return a ptr to the hidden token appearing immediately after + * token t in the input stream. + */ + RefToken getHiddenAfter(RefToken t); + + /** Return a ptr to the hidden token appearing immediately before + * token t in the input stream. + */ + RefToken getHiddenBefore(RefToken t); + + BitSet getHideMask() const; + + /** Return the first hidden token if one appears + * before any monitored token. + */ + RefToken getInitialHiddenToken(); + + void hide(int m); + + void hide(const BitSet& mask); + +protected: + RefToken LA(int i); + +public: +/** Return the next monitored token. + * Test the token following the monitored token. + * If following is another monitored token, save it + * for the next invocation of nextToken (like a single + * lookahead token) and return it then. + * If following is unmonitored, nondiscarded (hidden) + * channel token, add it to the monitored token. + * + * Note: EOF must be a monitored Token. + */ + RefToken nextToken(); +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_TokenStreamHiddenTokenFilter_hpp__ diff --git a/lib/antlr/antlr/TokenStreamIOException.hpp b/lib/antlr/antlr/TokenStreamIOException.hpp new file mode 100644 index 00000000..fff72ced --- /dev/null +++ b/lib/antlr/antlr/TokenStreamIOException.hpp @@ -0,0 +1,40 @@ +#ifndef INC_TokenStreamIOException_hpp__ +#define INC_TokenStreamIOException_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/TokenStreamException.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +class TokenStreamIOException : public TokenStreamException { +public: + TokenStreamIOException() + : TokenStreamException() + { + } + TokenStreamIOException(const ANTLR_USE_NAMESPACE(std)exception& e) + : TokenStreamException(e.what()) + , io(e) + { + } + ~TokenStreamIOException() throw() + { + } +private: + ANTLR_USE_NAMESPACE(std)exception io; +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_TokenStreamIOException_hpp__ diff --git a/lib/antlr/antlr/TokenStreamRecognitionException.hpp b/lib/antlr/antlr/TokenStreamRecognitionException.hpp new file mode 100644 index 00000000..687d7814 --- /dev/null +++ b/lib/antlr/antlr/TokenStreamRecognitionException.hpp @@ -0,0 +1,57 @@ +#ifndef INC_TokenStreamRecognitionException_hpp__ +#define INC_TokenStreamRecognitionException_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/TokenStreamException.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** Exception thrown from generated lexers when there's no default error + * handler specified. + * @see TokenStream + */ +class TokenStreamRecognitionException : public TokenStreamException { +public: + TokenStreamRecognitionException(RecognitionException& re) + : TokenStreamException(re.getMessage()) + , recog(re) + { + } + virtual ~TokenStreamRecognitionException() throw() + { + } + virtual ANTLR_USE_NAMESPACE(std)string toString() const + { + return recog.getFileLineColumnString()+getMessage(); + } + + virtual ANTLR_USE_NAMESPACE(std)string getFilename() const throw() + { + return recog.getFilename(); + } + virtual int getLine() const throw() + { + return recog.getLine(); + } + virtual int getColumn() const throw() + { + return recog.getColumn(); + } +private: + RecognitionException recog; +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_TokenStreamRecognitionException_hpp__ diff --git a/lib/antlr/antlr/TokenStreamRetryException.hpp b/lib/antlr/antlr/TokenStreamRetryException.hpp new file mode 100644 index 00000000..5af21cde --- /dev/null +++ b/lib/antlr/antlr/TokenStreamRetryException.hpp @@ -0,0 +1,28 @@ +#ifndef INC_TokenStreamRetryException_hpp__ +#define INC_TokenStreamRetryException_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/TokenStreamException.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +class TokenStreamRetryException : public TokenStreamException { +public: + TokenStreamRetryException() {} + ~TokenStreamRetryException() throw() {} +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_TokenStreamRetryException_hpp__ diff --git a/lib/antlr/antlr/TokenStreamRewriteEngine.hpp b/lib/antlr/antlr/TokenStreamRewriteEngine.hpp new file mode 100644 index 00000000..9fab08c2 --- /dev/null +++ b/lib/antlr/antlr/TokenStreamRewriteEngine.hpp @@ -0,0 +1,439 @@ +#ifndef INC_TokenStreamRewriteEngine_hpp__ +#define INC_TokenStreamRewriteEngine_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + */ + +#include <string> +#include <list> +#include <vector> +#include <map> +#include <utility> +#include <iostream> +#include <iterator> +#include <cassert> +#include <algorithm> + +#include <antlr/config.hpp> + +#include <antlr/TokenStream.hpp> +#include <antlr/TokenWithIndex.hpp> +#include <antlr/BitSet.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** This token stream tracks the *entire* token stream coming from + * a lexer, but does not pass on the whitespace (or whatever else + * you want to discard) to the parser. + * + * This class can then be asked for the ith token in the input stream. + * Useful for dumping out the input stream exactly after doing some + * augmentation or other manipulations. Tokens are index from 0..n-1 + * + * You can insert stuff, replace, and delete chunks. Note that the + * operations are done lazily--only if you convert the buffer to a + * String. This is very efficient because you are not moving data around + * all the time. As the buffer of tokens is converted to strings, the + * toString() method(s) check to see if there is an operation at the + * current index. If so, the operation is done and then normal String + * rendering continues on the buffer. This is like having multiple Turing + * machine instruction streams (programs) operating on a single input tape. :) + * + * Since the operations are done lazily at toString-time, operations do not + * screw up the token index values. That is, an insert operation at token + * index i does not change the index values for tokens i+1..n-1. + * + * Because operations never actually alter the buffer, you may always get + * the original token stream back without undoing anything. Since + * the instructions are queued up, you can easily simulate transactions and + * roll back any changes if there is an error just by removing instructions. + * For example, + * + * TokenStreamRewriteEngine rewriteEngine = + * new TokenStreamRewriteEngine(lexer); + * JavaRecognizer parser = new JavaRecognizer(rewriteEngine); + * ... + * rewriteEngine.insertAfter("pass1", t, "foobar");} + * rewriteEngine.insertAfter("pass2", u, "start");} + * System.out.println(rewriteEngine.toString("pass1")); + * System.out.println(rewriteEngine.toString("pass2")); + * + * You can also have multiple "instruction streams" and get multiple + * rewrites from a single pass over the input. Just name the instruction + * streams and use that name again when printing the buffer. This could be + * useful for generating a C file and also its header file--all from the + * same buffer. + * + * If you don't use named rewrite streams, a "default" stream is used. + * + * Terence Parr, parrt@cs.usfca.edu + * University of San Francisco + * February 2004 + */ +class TokenStreamRewriteEngine : public TokenStream +{ +public: + typedef ANTLR_USE_NAMESPACE(std)vector<antlr::RefTokenWithIndex> token_list; + static const char* DEFAULT_PROGRAM_NAME; +#ifndef NO_STATIC_CONSTS + static const size_t MIN_TOKEN_INDEX; + static const int PROGRAM_INIT_SIZE; +#else + enum { + MIN_TOKEN_INDEX = 0, + PROGRAM_INIT_SIZE = 100 + }; +#endif + + struct tokenToStream { + tokenToStream( ANTLR_USE_NAMESPACE(std)ostream& o ) : out(o) {} + template <typename T> void operator() ( const T& t ) { + out << t->getText(); + } + ANTLR_USE_NAMESPACE(std)ostream& out; + }; + + class RewriteOperation { + protected: + RewriteOperation( size_t idx, const ANTLR_USE_NAMESPACE(std)string& txt ) + : index(idx), text(txt) + { + } + public: + virtual ~RewriteOperation() + { + } + /** Execute the rewrite operation by possibly adding to the buffer. + * Return the index of the next token to operate on. + */ + virtual size_t execute( ANTLR_USE_NAMESPACE(std)ostream& /* out */ ) { + return index; + } + virtual size_t getIndex() const { + return index; + } + virtual const char* type() const { + return "RewriteOperation"; + } + protected: + size_t index; + ANTLR_USE_NAMESPACE(std)string text; + }; + + struct executeOperation { + ANTLR_USE_NAMESPACE(std)ostream& out; + executeOperation( ANTLR_USE_NAMESPACE(std)ostream& s ) : out(s) {} + void operator () ( RewriteOperation* t ) { + t->execute(out); + } + }; + + /// list of rewrite operations + typedef ANTLR_USE_NAMESPACE(std)list<RewriteOperation*> operation_list; + /// map program name to <program counter,program> tuple + typedef ANTLR_USE_NAMESPACE(std)map<ANTLR_USE_NAMESPACE(std)string,operation_list> program_map; + + class InsertBeforeOp : public RewriteOperation + { + public: + InsertBeforeOp( size_t index, const ANTLR_USE_NAMESPACE(std)string& text ) + : RewriteOperation(index, text) + { + } + virtual ~InsertBeforeOp() {} + virtual size_t execute( ANTLR_USE_NAMESPACE(std)ostream& out ) + { + out << text; + return index; + } + virtual const char* type() const { + return "InsertBeforeOp"; + } + }; + + class ReplaceOp : public RewriteOperation + { + public: + ReplaceOp(size_t from, size_t to, ANTLR_USE_NAMESPACE(std)string text) + : RewriteOperation(from,text) + , lastIndex(to) + { + } + virtual ~ReplaceOp() {} + virtual size_t execute( ANTLR_USE_NAMESPACE(std)ostream& out ) { + out << text; + return lastIndex+1; + } + virtual const char* type() const { + return "ReplaceOp"; + } + protected: + size_t lastIndex; + }; + + class DeleteOp : public ReplaceOp { + public: + DeleteOp(size_t from, size_t to) + : ReplaceOp(from,to,"") + { + } + virtual const char* type() const { + return "DeleteOp"; + } + }; + + TokenStreamRewriteEngine(TokenStream& upstream); + + TokenStreamRewriteEngine(TokenStream& upstream, size_t initialSize); + + RefToken nextToken( void ); + + void rollback(size_t instructionIndex) { + rollback(DEFAULT_PROGRAM_NAME, instructionIndex); + } + + /** Rollback the instruction stream for a program so that + * the indicated instruction (via instructionIndex) is no + * longer in the stream. UNTESTED! + */ + void rollback(const ANTLR_USE_NAMESPACE(std)string& programName, + size_t instructionIndex ); + + void deleteProgram() { + deleteProgram(DEFAULT_PROGRAM_NAME); + } + + /** Reset the program so that no instructions exist */ + void deleteProgram(const ANTLR_USE_NAMESPACE(std)string& programName) { + rollback(programName, MIN_TOKEN_INDEX); + } + + void insertAfter( RefTokenWithIndex t, + const ANTLR_USE_NAMESPACE(std)string& text ) + { + insertAfter(DEFAULT_PROGRAM_NAME, t, text); + } + + void insertAfter(size_t index, const ANTLR_USE_NAMESPACE(std)string& text) { + insertAfter(DEFAULT_PROGRAM_NAME, index, text); + } + + void insertAfter( const ANTLR_USE_NAMESPACE(std)string& programName, + RefTokenWithIndex t, + const ANTLR_USE_NAMESPACE(std)string& text ) + { + insertAfter(programName, t->getIndex(), text); + } + + void insertAfter( const ANTLR_USE_NAMESPACE(std)string& programName, + size_t index, + const ANTLR_USE_NAMESPACE(std)string& text ) + { + // to insert after, just insert before next index (even if past end) + insertBefore(programName,index+1, text); + } + + void insertBefore( RefTokenWithIndex t, + const ANTLR_USE_NAMESPACE(std)string& text ) + { + // std::cout << "insertBefore index " << t->getIndex() << " " << text << std::endl; + insertBefore(DEFAULT_PROGRAM_NAME, t, text); + } + + void insertBefore(size_t index, const ANTLR_USE_NAMESPACE(std)string& text) { + insertBefore(DEFAULT_PROGRAM_NAME, index, text); + } + + void insertBefore( const ANTLR_USE_NAMESPACE(std)string& programName, + RefTokenWithIndex t, + const ANTLR_USE_NAMESPACE(std)string& text ) + { + insertBefore(programName, t->getIndex(), text); + } + + void insertBefore( const ANTLR_USE_NAMESPACE(std)string& programName, + size_t index, + const ANTLR_USE_NAMESPACE(std)string& text ) + { + addToSortedRewriteList(programName, new InsertBeforeOp(index,text)); + } + + void replace(size_t index, const ANTLR_USE_NAMESPACE(std)string& text) + { + replace(DEFAULT_PROGRAM_NAME, index, index, text); + } + + void replace( size_t from, size_t to, + const ANTLR_USE_NAMESPACE(std)string& text) + { + replace(DEFAULT_PROGRAM_NAME, from, to, text); + } + + void replace( RefTokenWithIndex indexT, + const ANTLR_USE_NAMESPACE(std)string& text ) + { + replace(DEFAULT_PROGRAM_NAME, indexT->getIndex(), indexT->getIndex(), text); + } + + void replace( RefTokenWithIndex from, + RefTokenWithIndex to, + const ANTLR_USE_NAMESPACE(std)string& text ) + { + replace(DEFAULT_PROGRAM_NAME, from, to, text); + } + + void replace(const ANTLR_USE_NAMESPACE(std)string& programName, + size_t from, size_t to, + const ANTLR_USE_NAMESPACE(std)string& text ) + { + addToSortedRewriteList(programName,new ReplaceOp(from, to, text)); + } + + void replace( const ANTLR_USE_NAMESPACE(std)string& programName, + RefTokenWithIndex from, + RefTokenWithIndex to, + const ANTLR_USE_NAMESPACE(std)string& text ) + { + replace(programName, + from->getIndex(), + to->getIndex(), + text); + } + + void remove(size_t index) { + remove(DEFAULT_PROGRAM_NAME, index, index); + } + + void remove(size_t from, size_t to) { + remove(DEFAULT_PROGRAM_NAME, from, to); + } + + void remove(RefTokenWithIndex indexT) { + remove(DEFAULT_PROGRAM_NAME, indexT, indexT); + } + + void remove(RefTokenWithIndex from, RefTokenWithIndex to) { + remove(DEFAULT_PROGRAM_NAME, from, to); + } + + void remove( const ANTLR_USE_NAMESPACE(std)string& programName, + size_t from, size_t to) + { + replace(programName,from,to,""); + } + + void remove( const ANTLR_USE_NAMESPACE(std)string& programName, + RefTokenWithIndex from, RefTokenWithIndex to ) + { + replace(programName,from,to,""); + } + + void discard(int ttype) { + discardMask.add(ttype); + } + + RefToken getToken( size_t i ) + { + return RefToken(tokens.at(i)); + } + + size_t getTokenStreamSize() const { + return tokens.size(); + } + + void originalToStream( ANTLR_USE_NAMESPACE(std)ostream& out ) const { + ANTLR_USE_NAMESPACE(std)for_each( tokens.begin(), tokens.end(), tokenToStream(out) ); + } + + void originalToStream( ANTLR_USE_NAMESPACE(std)ostream& out, + size_t start, size_t end ) const; + + void toStream( ANTLR_USE_NAMESPACE(std)ostream& out ) const { + toStream( out, MIN_TOKEN_INDEX, getTokenStreamSize()); + } + + void toStream( ANTLR_USE_NAMESPACE(std)ostream& out, + const ANTLR_USE_NAMESPACE(std)string& programName ) const + { + toStream( out, programName, MIN_TOKEN_INDEX, getTokenStreamSize()); + } + + void toStream( ANTLR_USE_NAMESPACE(std)ostream& out, + size_t start, size_t end ) const + { + toStream(out, DEFAULT_PROGRAM_NAME, start, end); + } + + void toStream( ANTLR_USE_NAMESPACE(std)ostream& out, + const ANTLR_USE_NAMESPACE(std)string& programName, + size_t firstToken, size_t lastToken ) const; + + void toDebugStream( ANTLR_USE_NAMESPACE(std)ostream& out ) const { + toDebugStream( out, MIN_TOKEN_INDEX, getTokenStreamSize()); + } + + void toDebugStream( ANTLR_USE_NAMESPACE(std)ostream& out, + size_t start, size_t end ) const; + + size_t getLastRewriteTokenIndex() const { + return getLastRewriteTokenIndex(DEFAULT_PROGRAM_NAME); + } + + /** Return the last index for the program named programName + * return 0 if the program does not exist or the program is empty. + * (Note this is different from the java implementation that returns -1) + */ + size_t getLastRewriteTokenIndex(const ANTLR_USE_NAMESPACE(std)string& programName) const { + program_map::const_iterator rewrites = programs.find(programName); + + if( rewrites == programs.end() ) + return 0; + + const operation_list& prog = rewrites->second; + if( !prog.empty() ) + { + operation_list::const_iterator last = prog.end(); + --last; + return (*last)->getIndex(); + } + return 0; + } + +protected: + /** If op.index > lastRewriteTokenIndexes, just add to the end. + * Otherwise, do linear */ + void addToSortedRewriteList(RewriteOperation* op) { + addToSortedRewriteList(DEFAULT_PROGRAM_NAME, op); + } + + void addToSortedRewriteList( const ANTLR_USE_NAMESPACE(std)string& programName, + RewriteOperation* op ); + +protected: + /** Who do we suck tokens from? */ + TokenStream& stream; + /** track index of tokens */ + size_t index; + + /** Track the incoming list of tokens */ + token_list tokens; + + /** You may have multiple, named streams of rewrite operations. + * I'm calling these things "programs." + * Maps String (name) -> rewrite (List) + */ + program_map programs; + + /** Which (whitespace) token(s) to throw out */ + BitSet discardMask; +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif diff --git a/lib/antlr/antlr/TokenStreamSelector.hpp b/lib/antlr/antlr/TokenStreamSelector.hpp new file mode 100644 index 00000000..1dab8797 --- /dev/null +++ b/lib/antlr/antlr/TokenStreamSelector.hpp @@ -0,0 +1,87 @@ +#ifndef INC_TokenStreamSelector_hpp__ +#define INC_TokenStreamSelector_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/TokenStream.hpp> +#include <map> +#include <stack> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** A token stream MUX (multiplexor) knows about n token streams + * and can multiplex them onto the same channel for use by token + * stream consumer like a parser. This is a way to have multiple + * lexers break up the same input stream for a single parser. + * Or, you can have multiple instances of the same lexer handle + * multiple input streams; this works great for includes. + */ +class ANTLR_API TokenStreamSelector : public TokenStream { +protected: + /** The set of inputs to the MUX */ +#ifdef OS_NO_ALLOCATOR + typedef ANTLR_USE_NAMESPACE(std)less<ANTLR_USE_NAMESPACE(std)string> lessp; + typedef ANTLR_USE_NAMESPACE(std)map<ANTLR_USE_NAMESPACE(std)string,TokenStream*,lessp> inputStreamNames_coll; +#else + typedef ANTLR_USE_NAMESPACE(std)map<ANTLR_USE_NAMESPACE(std)string,TokenStream*> inputStreamNames_coll; +#endif + inputStreamNames_coll inputStreamNames; + + /** The currently-selected token stream input */ + TokenStream* input; + + /** Used to track stack of input streams */ +#ifdef OS_NO_ALLOCATOR + typedef ANTLR_USE_NAMESPACE(std)stack<TokenStream*, ANTLR_USE_NAMESPACE(std)deque<TokenStream*> > streamStack_coll; +#else + typedef ANTLR_USE_NAMESPACE(std)stack<TokenStream*> streamStack_coll; +#endif + streamStack_coll streamStack; + +public: + TokenStreamSelector(); + ~TokenStreamSelector(); + + void addInputStream(TokenStream* stream, const ANTLR_USE_NAMESPACE(std)string& key); + + /// Return the stream from which tokens are being pulled at the moment. + TokenStream* getCurrentStream() const; + + TokenStream* getStream(const ANTLR_USE_NAMESPACE(std)string& sname) const; + + RefToken nextToken(); + + TokenStream* pop(); + + void push(TokenStream* stream); + + void push(const ANTLR_USE_NAMESPACE(std)string& sname); + + /** Abort recognition of current Token and try again. + * A stream can push a new stream (for include files + * for example, and then retry(), which will cause + * the current stream to abort back to this.nextToken(). + * this.nextToken() then asks for a token from the + * current stream, which is the new "substream." + */ + void retry(); + + /** Set the stream without pushing old stream */ + void select(TokenStream* stream); + + void select(const ANTLR_USE_NAMESPACE(std)string& sname); +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_TokenStreamSelector_hpp__ diff --git a/lib/antlr/antlr/TokenWithIndex.hpp b/lib/antlr/antlr/TokenWithIndex.hpp new file mode 100644 index 00000000..e4a3e37e --- /dev/null +++ b/lib/antlr/antlr/TokenWithIndex.hpp @@ -0,0 +1,84 @@ +#ifndef INC_TokenWithIndex_hpp__ +#define INC_TokenWithIndex_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id:$ + */ + +#include <antlr/config.hpp> +#include <antlr/CommonToken.hpp> +#include <antlr/String.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +class ANTLR_API TokenWithIndex : public ANTLR_USE_NAMESPACE(antlr)CommonToken { +public: + // static size_t count; + TokenWithIndex() : CommonToken(), index(0) + { + // std::cout << __PRETTY_FUNCTION__ << std::endl; + // count++; + } + TokenWithIndex(int t, const ANTLR_USE_NAMESPACE(std)string& txt) + : CommonToken(t,txt) + , index(0) + { + // std::cout << __PRETTY_FUNCTION__ << std::endl; + // count++; + } + TokenWithIndex(const ANTLR_USE_NAMESPACE(std)string& s) + : CommonToken(s) + , index(0) + { + // std::cout << __PRETTY_FUNCTION__ << std::endl; + // count++; + } + ~TokenWithIndex() + { + // count--; + } + void setIndex( size_t idx ) + { + index = idx; + } + size_t getIndex( void ) const + { + return index; + } + + ANTLR_USE_NAMESPACE(std)string toString() const + { + return ANTLR_USE_NAMESPACE(std)string("[")+ + index+ + ":\""+ + getText()+"\",<"+ + getType()+">,line="+ + getLine()+",column="+ + getColumn()+"]"; + } + + static RefToken factory() + { + return RefToken(new TokenWithIndex()); + } + +protected: + size_t index; + +private: + TokenWithIndex(const TokenWithIndex&); + const TokenWithIndex& operator=(const TokenWithIndex&); +}; + +typedef TokenRefCount<TokenWithIndex> RefTokenWithIndex; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_CommonToken_hpp__ diff --git a/lib/antlr/antlr/TreeParser.hpp b/lib/antlr/antlr/TreeParser.hpp new file mode 100644 index 00000000..aeee3f9d --- /dev/null +++ b/lib/antlr/antlr/TreeParser.hpp @@ -0,0 +1,155 @@ +#ifndef INC_TreeParser_hpp__ +#define INC_TreeParser_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/AST.hpp> +#include <antlr/ASTFactory.hpp> +#include <antlr/BitSet.hpp> +#include <antlr/RecognitionException.hpp> +#include <antlr/MismatchedTokenException.hpp> +#include <antlr/TreeParserSharedInputState.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +class ANTLR_API TreeParser { +public: + TreeParser() + : astFactory(0) + , inputState(new TreeParserInputState()) + , traceDepth(0) + { + } + + TreeParser(const TreeParserSharedInputState& state) + : astFactory(0) + , inputState(state) + , traceDepth(0) + { + } + + virtual ~TreeParser() + { + } + + /// Get the AST return value squirreled away in the parser + virtual RefAST getAST() = 0; + + /** Make sure current lookahead symbol matches the given set + * Throw an exception upon mismatch, which is caught by either the + * error handler or by a syntactic predicate. + */ + virtual void match(RefAST t, const BitSet& b) + { + if ( !t || t==ASTNULL || !b.member(t->getType()) ) + throw MismatchedTokenException( getTokenNames(), getNumTokens(), + t, b, false ); + } + + /** Specify the AST factory to be used during tree building. (Compulsory) + * Setting the factory is compulsory (if you intend to modify + * the tree in the treeparser). The AST Factory is shared between + * parser (who builds the initial AST) and treeparser. + * @see Parser::getASTFactory() + */ + virtual void setASTFactory(ASTFactory* factory) + { + astFactory = factory; + } + /// Return pointer to ASTFactory + virtual ASTFactory* getASTFactory() const + { + return astFactory; + } + /// Get the name for token 'num' + virtual const char* getTokenName(int num) const = 0; + /// Return the number of tokens defined + virtual int getNumTokens() const = 0; + /// Return an array of getNumTokens() token names + virtual const char* const* getTokenNames() const = 0; + + /// Parser error-reporting function can be overridden in subclass + virtual void reportError(const RecognitionException& ex); + /// Parser error-reporting function can be overridden in subclass + virtual void reportError(const ANTLR_USE_NAMESPACE(std)string& s); + /// Parser warning-reporting function can be overridden in subclass + virtual void reportWarning(const ANTLR_USE_NAMESPACE(std)string& s); + + /// These are used during when traceTreeParser commandline option is passed. + virtual void traceIndent(); + virtual void traceIn(const char* rname, RefAST t); + virtual void traceOut(const char* rname, RefAST t); + + /** The AST Null object; the parsing cursor is set to this when + * it is found to be null. This way, we can test the + * token type of a node without having to have tests for 0 + * everywhere. + */ + static RefAST ASTNULL; + +protected: + virtual void match(RefAST t, int ttype) + { + if (!t || t == ASTNULL || t->getType() != ttype ) + throw MismatchedTokenException( getTokenNames(), getNumTokens(), + t, ttype, false ); + } + + virtual void matchNot(RefAST t, int ttype) + { + if ( !t || t == ASTNULL || t->getType() == ttype ) + throw MismatchedTokenException( getTokenNames(), getNumTokens(), + t, ttype, true ); + } + + /** AST support code; parser and treeparser delegate to this object */ + ASTFactory* astFactory; + + /// The input state of this tree parser. + TreeParserSharedInputState inputState; + + /** Used to keep track of indent depth with -traceTreeParser */ + int traceDepth; + + /** Utility class which allows tracing to work even when exceptions are + * thrown. + */ + class Tracer { + private: + TreeParser* parser; + const char* text; + RefAST tree; + public: + Tracer(TreeParser* p, const char* t, RefAST a) + : parser(p), text(t), tree(a) + { + parser->traceIn(text,tree); + } + ~Tracer() + { + parser->traceOut(text,tree); + } + private: + Tracer(const Tracer&); // undefined + const Tracer& operator=(const Tracer&); // undefined + }; + +private: + // no copying of treeparser instantiations... + TreeParser(const TreeParser& other); + TreeParser& operator=(const TreeParser& other); +}; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_TreeParser_hpp__ diff --git a/lib/antlr/antlr/TreeParserSharedInputState.hpp b/lib/antlr/antlr/TreeParserSharedInputState.hpp new file mode 100644 index 00000000..76ec6840 --- /dev/null +++ b/lib/antlr/antlr/TreeParserSharedInputState.hpp @@ -0,0 +1,45 @@ +#ifndef INC_TreeParserSharedInputState_hpp__ +#define INC_TreeParserSharedInputState_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/RefCount.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** This object contains the data associated with an + * input AST. Multiple parsers + * share a single TreeParserSharedInputState to parse + * the same tree or to have the parser walk multiple + * trees. + */ +class ANTLR_API TreeParserInputState { +public: + TreeParserInputState() : guessing(0) {} + virtual ~TreeParserInputState() {} + +public: + /** Are we guessing (guessing>0)? */ + int guessing; //= 0; + +private: + // we don't want these: + TreeParserInputState(const TreeParserInputState&); + TreeParserInputState& operator=(const TreeParserInputState&); +}; + +typedef RefCount<TreeParserInputState> TreeParserSharedInputState; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +#endif //INC_TreeParserSharedInputState_hpp__ diff --git a/lib/antlr/antlr/config.hpp b/lib/antlr/antlr/config.hpp new file mode 100644 index 00000000..db8fb28a --- /dev/null +++ b/lib/antlr/antlr/config.hpp @@ -0,0 +1,290 @@ +#ifndef INC_config_hpp__ +#define INC_config_hpp__ + +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +/* + * Just a simple configuration file to differentiate between the + * various compilers used and reconfigure stuff for any oddities of the + * compiler in question. + * + * These are the defaults. Per compiler these are amended. + */ +#define ANTLR_USE_NAMESPACE(_x_) _x_:: +#define ANTLR_USING_NAMESPACE(_x_) using namespace _x_; +#define ANTLR_CXX_SUPPORTS_NAMESPACE 1 +#define ANTLR_C_USING(_x_) +#define ANTLR_API +#ifndef CUSTOM_API +# define CUSTOM_API +#endif +#define ANTLR_IOS_BASE ios_base +/** define if cctype functions/macros need a std:: prefix. A lot of compilers + * define these as macros, in which case something barfs. + */ +#define ANTLR_CCTYPE_NEEDS_STD + +/// Define if C++ compiler supports std::uncaught_exception +#define ANTLR_CXX_SUPPORTS_UNCAUGHT_EXCEPTION + +#define ANTLR_ATOI_IN_STD + +/******************************************************************************/ +/*{{{ Microsoft Visual C++ */ +// NOTE: If you provide patches for a specific MSVC version guard them for +// the specific version!!!! +// _MSC_VER == 1100 for Microsoft Visual C++ 5.0 +// _MSC_VER == 1200 for Microsoft Visual C++ 6.0 +// _MSC_VER == 1300 for Microsoft Visual C++ 7.0 +#if defined(_MSC_VER) + +# if _MSC_VER < 1300 +# define NOMINMAX +# pragma warning(disable : 4786) +# define min _cpp_min +# endif + +// This warning really gets on my nerves. +// It's the one about symbol longer than 256 chars, and it happens +// all the time with STL. +# pragma warning( disable : 4786 4231 ) +// this shuts up some DLL interface warnings for STL +# pragma warning( disable : 4251 ) + +# ifdef ANTLR_CXX_USE_STLPORT +# undef ANTLR_CXX_SUPPORTS_UNCAUGHT_EXCEPTION +# endif + +# if ( _MSC_VER < 1300 ) && ( defined(ANTLR_EXPORTS) || defined(ANTLR_IMPORTS) ) +# error "DLL Build not supported on these MSVC versions." +// see comment in lib/cpp/src/dll.cpp +# endif + +// For the DLL support originally contributed by Stephen Naughton +// If you are building statically leave ANTLR_EXPORTS/ANTLR_IMPORTS undefined +// If you are building the DLL define ANTLR_EXPORTS +// If you are compiling code to be used with the DLL define ANTLR_IMPORTS +# ifdef ANTLR_EXPORTS +# undef ANTLR_API +# define ANTLR_API __declspec(dllexport) +# endif + +# ifdef ANTLR_IMPORTS +# undef ANTLR_API +# define ANTLR_API __declspec(dllimport) +# endif + +# if ( _MSC_VER < 1200 ) +// supposedly only for MSVC5 and before... +// Using vector<XXX> requires operator<(X,X) to be defined +# define NEEDS_OPERATOR_LESS_THAN +# endif + +// VC6 +# if ( _MSC_VER == 1200 ) +# undef ANTLR_ATOI_IN_STD +# endif + +# if ( _MSC_VER < 1310 ) +// Supposedly only for MSVC7 and before... +// Not allowed to put 'static const int XXX=20;' in a class definition +# define NO_STATIC_CONSTS +# define NO_TEMPLATE_PARTS +# endif + +// No strcasecmp in the C library (so use stricmp instead) +// - Anyone know which is in which standard? +# define NO_STRCASECMP +# undef ANTLR_CCTYPE_NEEDS_STD +# define NO_STATIC_CONSTS +#endif // End of Microsoft Visual C++ + +/*}}}*/ +/******************************************************************************/ +/*{{{ SunPro Compiler (Using OBJECTSPACE STL) + *****************************************************************************/ +#ifdef __SUNPRO_CC + +# if (__SUNPRO_CC >= 0x500) + +# define NEEDS_OPERATOR_LESS_THAN +# define NO_TEMPLATE_PARTS + +# else + +# undef namespace +# define namespace + +# if (__SUNPRO_CC == 0x420) + +/* This code is specif to SunWspro Compiler 4.2, and will compile with + the objectspace 2.1 toolkit for Solaris2.6 */ +# define HAS_NOT_CASSERT_H +# define HAS_NOT_CSTRING_H +# define HAS_NOT_CCTYPE_H +# define HAS_NOT_CSTDIO_H +# define HAS_OSTREAM_H + +/* #define OS_SOLARIS_2_6 + #define OS_NO_WSTRING + #define OS_NO_ALLOCATORS + #define OS_MULTI_THREADED + #define OS_SOLARIS_NATIVE + #define OS_REALTIME + #define __OSVERSION__=5 + #define SVR4 + */ + +// ObjectSpace + some specific templates constructions with stl. +/* #define OS_NO_ALLOCATOR */ + +// This great compiler does not have the namespace feature. +# undef ANTLR_USE_NAMESPACE +# define ANTLR_USE_NAMESPACE(_x_) +# undef ANTLR_USING_NAMESPACE +# define ANTLR_USING_NAMESPACE(_x_) +# undef ANTLR_CXX_SUPPORTS_NAMESPACE +# endif // End __SUNPRO_CC == 0x420 + +# undef explicit +# define explicit + +# define exception os_exception +# define bad_exception os_bad_exception + +// Not allowed to put 'static const int XXX=20;' in a class definition +# define NO_STATIC_CONSTS +// Using vector<XXX> requires operator<(X,X) to be defined +# define NEEDS_OPERATOR_LESS_THAN + +# endif + +# undef ANTLR_CCTYPE_NEEDS_STD + +#endif // end __SUNPRO_CC +/*}}}*/ +/*****************************************************************************/ +/*{{{ Inprise C++ Builder 3.0 + *****************************************************************************/ +#ifdef __BCPLUSPLUS__ +# define NO_TEMPLATE_PARTS +# define NO_STRCASECMP +# undef ANTLR_CCTYPE_NEEDS_STD +#endif // End of C++ Builder 3.0 +/*}}}*/ +/*****************************************************************************/ +/*{{{ IBM VisualAge C++ ( which includes the Dinkumware C++ Library ) + *****************************************************************************/ +#ifdef __IBMCPP__ + +// No strcasecmp in the C library (so use stricmp instead) +// - Anyone know which is in which standard? +#if (defined(_AIX) && (__IBMCPP__ >= 600)) +# define NO_STATIC_CONSTS +#else +# define NO_STRCASECMP +# undef ANTLR_CCTYPE_NEEDS_STD +#endif + +#endif // end IBM VisualAge C++ +/*}}}*/ +/*****************************************************************************/ +/*{{{ Metrowerks Codewarrior + *****************************************************************************/ +#ifdef __MWERKS__ +# if (__MWERKS__ <= 0x2201) +# define NO_TEMPLATE_PARTS +# endif + +// CW 6.0 and 7.0 still do not have it. +# define ANTLR_REALLY_NO_STRCASECMP + +# undef ANTLR_C_USING +# define ANTLR_C_USING(_x_) using std:: ## _x_; + +# define ANTLR_CCTYPE_NEEDS_STD +# undef ANTLR_CXX_SUPPORTS_UNCAUGHT_EXCEPTION + +#endif // End of Metrowerks Codewarrior +/*}}}*/ +/*****************************************************************************/ +/*{{{ SGI Irix 6.5.10 MIPSPro compiler + *****************************************************************************/ +// (contributed by Anna Winkler) +// Note: you can't compile ANTLR with the MIPSPro compiler on +// anything < 6.5.10 because SGI just fixed a big bug dealing with +// namespaces in that release. +#ifdef __sgi +# define HAS_NOT_CCTYPE_H +# define HAS_NOT_CSTRING_H +# define HAS_NOT_CSTDIO_H +# undef ANTLR_CCTYPE_NEEDS_STD +#endif // End IRIX MIPSPro +/*}}}*/ +/*****************************************************************************/ +/*{{{ G++ in various incarnations + *****************************************************************************/ +// With the gcc-2.95 and 3.0 being in the near future we should start handling +// incompatabilities between the various libstdc++'s. +#if defined(__GNUC__) || defined(__GNUG__) +// gcc 2 branch.. +# if (__GNUC__ == 2 ) +# if (__GNUC_MINOR__ <= 8 ) +# undef ANTLR_USE_NAMESPACE +# define ANTLR_USE_NAMESPACE(_x_) +# undef ANTLR_USING_NAMESPACE +# define ANTLR_USING_NAMESPACE(_x_) +# undef ANTLR_CXX_SUPPORTS_NAMESPACE +# endif +# if (__GNUC_MINOR__ > 8 && __GNUC_MINOR__ <= 95 ) +# undef ANTLR_IOS_BASE +# define ANTLR_IOS_BASE ios +# undef ANTLR_CCTYPE_NEEDS_STD +// compiling with -ansi ? +# ifdef __STRICT_ANSI__ +# undef ANTLR_REALLY_NO_STRCASECMP +# define ANTLR_REALLY_NO_STRCASECMP +# endif +# else +// experimental .96 .97 branches.. +# undef ANTLR_CCTYPE_NEEDS_STD +# endif +# endif +#endif // ! __GNUC__ +/*}}}*/ +/*****************************************************************************/ +/*{{{ Digital CXX (Tru64) + *****************************************************************************/ +#ifdef __DECCXX +#define __USE_STD_IOSTREAM +#endif +/*}}}*/ +/*****************************************************************************/ +#ifdef __BORLANDC__ +# if __BORLANDC__ >= 560 +# include <ctype> +# include <stdlib> +# define ANTLR_CCTYPE_NEEDS_STD +# else +# error "sorry, compiler is too old - consider an update." +# endif +#endif + +// Redefine these for backwards compatability.. +#undef ANTLR_BEGIN_NAMESPACE +#undef ANTLR_END_NAMESPACE + +#if ANTLR_CXX_SUPPORTS_NAMESPACE == 1 +# define ANTLR_BEGIN_NAMESPACE(_x_) namespace _x_ { +# define ANTLR_END_NAMESPACE } +#else +# define ANTLR_BEGIN_NAMESPACE(_x_) +# define ANTLR_END_NAMESPACE +#endif + +#endif //INC_config_hpp__ diff --git a/lib/antlr/src/ANTLRUtil.cpp b/lib/antlr/src/ANTLRUtil.cpp new file mode 100644 index 00000000..30e7ba21 --- /dev/null +++ b/lib/antlr/src/ANTLRUtil.cpp @@ -0,0 +1,163 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <antlr/config.hpp> +#include <antlr/IOException.hpp> + +#include <iostream> +#include <cctype> +#include <string> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** Eat whitespace from the input stream + * @param is the stream to read from + */ +ANTLR_USE_NAMESPACE(std)istream& eatwhite( ANTLR_USE_NAMESPACE(std)istream& is ) +{ + char c; + while( is.get(c) ) + { +#ifdef ANTLR_CCTYPE_NEEDS_STD + if( !ANTLR_USE_NAMESPACE(std)isspace(c) ) +#else + if( !isspace(c) ) +#endif + { + is.putback(c); + break; + } + } + return is; +} + +/** Read a string enclosed by '"' from a stream. Also handles escaping of \". + * Skips leading whitespace. + * @param in the istream to read from. + * @returns the string read from file exclusive the '"' + * @throws IOException if string is badly formatted + */ +ANTLR_USE_NAMESPACE(std)string read_string( ANTLR_USE_NAMESPACE(std)istream& in ) +{ + char ch; + ANTLR_USE_NAMESPACE(std)string ret(""); + // States for a simple state machine... + enum { START, READING, ESCAPE, FINISHED }; + int state = START; + + eatwhite(in); + + while( state != FINISHED && in.get(ch) ) + { + switch( state ) + { + case START: + // start state: check wether starting with " then switch to READING + if( ch != '"' ) + throw IOException("string must start with '\"'"); + state = READING; + continue; + case READING: + // reading state: look out for escape sequences and closing " + if( ch == '\\' ) // got escape sequence + { + state = ESCAPE; + continue; + } + if( ch == '"' ) // close quote -> stop + { + state = FINISHED; + continue; + } + ret += ch; // else append... + continue; + case ESCAPE: + switch(ch) + { + case '\\': + ret += ch; + state = READING; + continue; + case '"': + ret += ch; + state = READING; + continue; + case '0': + ret += '\0'; + state = READING; + continue; + default: // unrecognized escape is not mapped + ret += '\\'; + ret += ch; + state = READING; + continue; + } + } + } + if( state != FINISHED ) + throw IOException("badly formatted string: "+ret); + + return ret; +} + +/* Read a ([A-Z][0-9][a-z]_)* kindoff thing. Skips leading whitespace. + * @param in the istream to read from. + */ +ANTLR_USE_NAMESPACE(std)string read_identifier( ANTLR_USE_NAMESPACE(std)istream& in ) +{ + char ch; + ANTLR_USE_NAMESPACE(std)string ret(""); + + eatwhite(in); + + while( in.get(ch) ) + { +#ifdef ANTLR_CCTYPE_NEEDS_STD + if( ANTLR_USE_NAMESPACE(std)isupper(ch) || + ANTLR_USE_NAMESPACE(std)islower(ch) || + ANTLR_USE_NAMESPACE(std)isdigit(ch) || + ch == '_' ) +#else + if( isupper(ch) || islower(ch) || isdigit(ch) || ch == '_' ) +#endif + ret += ch; + else + { + in.putback(ch); + break; + } + } + return ret; +} + +/** Read a attribute="value" thing. Leading whitespace is skipped. + * Between attribute and '=' no whitespace is allowed. After the '=' it is + * permitted. + * @param in the istream to read from. + * @param attribute string the attribute name is put in + * @param value string the value of the attribute is put in + * @throws IOException if something is fishy. E.g. malformed quoting + * or missing '=' + */ +void read_AttributeNValue( ANTLR_USE_NAMESPACE(std)istream& in, + ANTLR_USE_NAMESPACE(std)string& attribute, + ANTLR_USE_NAMESPACE(std)string& value ) +{ + attribute = read_identifier(in); + + char ch; + if( in.get(ch) && ch == '=' ) + value = read_string(in); + else + throw IOException("invalid attribute=value thing "+attribute); +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif diff --git a/lib/antlr/src/ASTFactory.cpp b/lib/antlr/src/ASTFactory.cpp new file mode 100644 index 00000000..98ce6b7a --- /dev/null +++ b/lib/antlr/src/ASTFactory.cpp @@ -0,0 +1,504 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include "antlr/CommonAST.hpp" +#include "antlr/ANTLRException.hpp" +#include "antlr/IOException.hpp" +#include "antlr/ASTFactory.hpp" +#include "antlr/ANTLRUtil.hpp" + +#include <iostream> +#include <istream> + +using namespace std; + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** AST Support code shared by TreeParser and Parser. + * We use delegation to share code (and have only one + * bit of code to maintain) rather than subclassing + * or superclassing (forces AST support code to be + * loaded even when you don't want to do AST stuff). + * + * This class collects all factories of AST types used inside the code. + * New AST node types are registered with the registerFactory method. + * On creation of an ASTFactory object a default AST node factory may be + * specified. + * + * When registering types gaps between different types are filled with entries + * for the default factory. + */ + +/// Initialize factory +ASTFactory::ASTFactory() +: default_factory_descriptor(ANTLR_USE_NAMESPACE(std)make_pair(CommonAST::TYPE_NAME,&CommonAST::factory)) +{ + nodeFactories.resize( Token::MIN_USER_TYPE, &default_factory_descriptor ); +} + +/** Initialize factory with a non default node type. + * factory_node_name should be the name of the AST node type the factory + * generates. (should exist during the existance of this ASTFactory instance) + */ +ASTFactory::ASTFactory( const char* factory_node_name, factory_type fact ) +: default_factory_descriptor(ANTLR_USE_NAMESPACE(std)make_pair(factory_node_name, fact)) +{ + nodeFactories.resize( Token::MIN_USER_TYPE, &default_factory_descriptor ); +} + +/// Delete ASTFactory +ASTFactory::~ASTFactory() +{ + factory_descriptor_list::iterator i = nodeFactories.begin(); + + while( i != nodeFactories.end() ) + { + if( *i != &default_factory_descriptor ) + delete *i; + i++; + } +} + +/// Register a factory for a given AST type +void ASTFactory::registerFactory( int type, const char* ast_name, factory_type factory ) +{ + // check validity of arguments... + if( type < Token::MIN_USER_TYPE ) + throw ANTLRException("Internal parser error invalid type passed to RegisterFactory"); + if( factory == 0 ) + throw ANTLRException("Internal parser error 0 factory passed to RegisterFactory"); + + // resize up to and including 'type' and initalize any gaps to default + // factory. + if( nodeFactories.size() < (static_cast<unsigned int>(type)+1) ) + nodeFactories.resize( type+1, &default_factory_descriptor ); + + // And add new thing.. + nodeFactories[type] = new ANTLR_USE_NAMESPACE(std)pair<const char*, factory_type>( ast_name, factory ); +} + +void ASTFactory::setMaxNodeType( int type ) +{ + if( nodeFactories.size() < (static_cast<unsigned int>(type)+1) ) + nodeFactories.resize( type+1, &default_factory_descriptor ); +} + +/** Create a new empty AST node; if the user did not specify + * an AST node type, then create a default one: CommonAST. + */ +RefAST ASTFactory::create() +{ + RefAST node = nodeFactories[0]->second(); + node->setType(Token::INVALID_TYPE); + return node; +} + +RefAST ASTFactory::create(int type) +{ + RefAST t = nodeFactories[type]->second(); + t->initialize(type,""); + return t; +} + +RefAST ASTFactory::create(int type, const ANTLR_USE_NAMESPACE(std)string& txt) +{ + RefAST t = nodeFactories[type]->second(); + t->initialize(type,txt); + return t; +} + +#ifdef ANTLR_SUPPORT_XML +RefAST ASTFactory::create(const ANTLR_USE_NAMESPACE(std)string& type_name, ANTLR_USE_NAMESPACE(std)istream& infile ) +{ + factory_descriptor_list::iterator fact = nodeFactories.begin(); + + while( fact != nodeFactories.end() ) + { + if( type_name == (*fact)->first ) + { + RefAST t = (*fact)->second(); + t->initialize(infile); + return t; + } + fact++; + } + + string error = "ASTFactory::create: Unknown AST type '" + type_name + "'"; + throw ANTLRException(error); +} +#endif + +/** Create a new empty AST node; if the user did not specify + * an AST node type, then create a default one: CommonAST. + */ +RefAST ASTFactory::create(RefAST tr) +{ + if (!tr) + return nullAST; + +// cout << "create(tr)" << endl; + + RefAST t = nodeFactories[tr->getType()]->second(); + t->initialize(tr); + return t; +} + +RefAST ASTFactory::create(RefToken tok) +{ +// cout << "create( tok="<< tok->getType() << ", " << tok->getText() << ")" << nodeFactories.size() << endl; + RefAST t = nodeFactories[tok->getType()]->second(); + t->initialize(tok); + return t; +} + +/** Add a child to the current AST */ +void ASTFactory::addASTChild(ASTPair& currentAST, RefAST child) +{ + if (child) + { + if (!currentAST.root) + { + // Make new child the current root + currentAST.root = child; + } + else + { + if (!currentAST.child) + { + // Add new child to current root + currentAST.root->setFirstChild(child); + } + else + { + currentAST.child->setNextSibling(child); + } + } + // Make new child the current child + currentAST.child = child; + currentAST.advanceChildToEnd(); + } +} + +/** Deep copy a single node. This function the new clone() methods in the AST + * interface. Returns nullAST if t is null. + */ +RefAST ASTFactory::dup(RefAST t) +{ + if( t ) + return t->clone(); + else + return RefAST(nullASTptr); +} + +/** Duplicate tree including siblings of root. */ +RefAST ASTFactory::dupList(RefAST t) +{ + RefAST result = dupTree(t); // if t == null, then result==null + RefAST nt = result; + + while( t ) + { // for each sibling of the root + t = t->getNextSibling(); + nt->setNextSibling(dupTree(t)); // dup each subtree, building new tree + nt = nt->getNextSibling(); + } + return result; +} + +/** Duplicate a tree, assuming this is a root node of a tree + * duplicate that node and what's below; ignore siblings of root node. + */ +RefAST ASTFactory::dupTree(RefAST t) +{ + RefAST result = dup(t); // make copy of root + // copy all children of root. + if( t ) + result->setFirstChild( dupList(t->getFirstChild()) ); + return result; +} + +/** Make a tree from a list of nodes. The first element in the + * array is the root. If the root is null, then the tree is + * a simple list not a tree. Handles null children nodes correctly. + * For example, make(a, b, null, c) yields tree (a b c). make(null,a,b) + * yields tree (nil a b). + */ +RefAST ASTFactory::make(ANTLR_USE_NAMESPACE(std)vector<RefAST>& nodes) +{ + if ( nodes.size() == 0 ) + return RefAST(nullASTptr); + + RefAST root = nodes[0]; + RefAST tail = RefAST(nullASTptr); + + if( root ) + root->setFirstChild(RefAST(nullASTptr)); // don't leave any old pointers set + + // link in children; + for( unsigned int i = 1; i < nodes.size(); i++ ) + { + if ( nodes[i] == 0 ) // ignore null nodes + continue; + + if ( root == 0 ) // Set the root and set it up for a flat list + root = tail = nodes[i]; + else if ( tail == 0 ) + { + root->setFirstChild(nodes[i]); + tail = root->getFirstChild(); + } + else + { + tail->setNextSibling(nodes[i]); + tail = tail->getNextSibling(); + } + + if( tail ) // RK: I cannot fathom why this missing check didn't bite anyone else... + { + // Chase tail to last sibling + while (tail->getNextSibling()) + tail = tail->getNextSibling(); + } + } + + return root; +} + +/** Make a tree from a list of nodes, where the nodes are contained + * in an ASTArray object + */ +RefAST ASTFactory::make(ASTArray* nodes) +{ + RefAST ret = make(nodes->array); + delete nodes; + return ret; +} + +/// Make an AST the root of current AST +void ASTFactory::makeASTRoot( ASTPair& currentAST, RefAST root ) +{ + if (root) + { + // Add the current root as a child of new root + root->addChild(currentAST.root); + // The new current child is the last sibling of the old root + currentAST.child = currentAST.root; + currentAST.advanceChildToEnd(); + // Set the new root + currentAST.root = root; + } +} + +void ASTFactory::setASTNodeFactory( const char* factory_node_name, + factory_type factory ) +{ + default_factory_descriptor.first = factory_node_name; + default_factory_descriptor.second = factory; +} + +#ifdef ANTLR_SUPPORT_XML +bool ASTFactory::checkCloseTag( ANTLR_USE_NAMESPACE(std)istream& in ) +{ + char ch; + + if( in.get(ch) ) + { + if( ch == '<' ) + { + char ch2; + if( in.get(ch2) ) + { + if( ch2 == '/' ) + { + in.putback(ch2); + in.putback(ch); + return true; + } + in.putback(ch2); + in.putback(ch); + return false; + } + } + in.putback(ch); + return false; + } + return false; +} + +void ASTFactory::loadChildren( ANTLR_USE_NAMESPACE(std)istream& infile, + RefAST current ) +{ + char ch; + + for(;;) // for all children of this node.... + { + eatwhite(infile); + + infile.get(ch); // '<' + if( ch != '<' ) + { + string error = "Invalid XML file... no '<' found ("; + error += ch + ")"; + throw IOException(error); + } + + infile.get(ch); // / or text.... + + if( ch == '/' ) // check for close tag... + { + string temp; + + // read until '>' and see if it matches the open tag... if not trouble + temp = read_identifier( infile ); + + if( strcmp(temp.c_str(), current->typeName() ) != 0 ) + { + string error = "Invalid XML file... close tag does not match start tag: "; + error += current->typeName(); + error += " closed by " + temp; + throw IOException(error); + } + + infile.get(ch); // must be a '>' + + if( ch != '>' ) + { + string error = "Invalid XML file... no '>' found ("; + error += ch + ")"; + throw IOException(error); + } + // close tag => exit loop + break; + } + + // put our 'look ahead' back where it came from + infile.putback(ch); + infile.putback('<'); + + // and recurse into the tree... + RefAST child = LoadAST(infile); + + current->addChild( child ); + } +} + +void ASTFactory::loadSiblings(ANTLR_USE_NAMESPACE(std)istream& infile, + RefAST current ) +{ + for(;;) + { + eatwhite(infile); + + if( infile.eof() ) + break; + + if( checkCloseTag(infile) ) + break; + + RefAST sibling = LoadAST(infile); + current->setNextSibling(sibling); + } +} + +RefAST ASTFactory::LoadAST( ANTLR_USE_NAMESPACE(std)istream& infile ) +{ + RefAST current = nullAST; + char ch; + + eatwhite(infile); + + if( !infile.get(ch) ) + return nullAST; + + if( ch != '<' ) + { + string error = "Invalid XML file... no '<' found ("; + error += ch + ")"; + throw IOException(error); + } + + string ast_type = read_identifier(infile); + + // create the ast of type 'ast_type' + current = create( ast_type, infile ); + if( current == nullAST ) + { + string error = "Unsuported AST type: " + ast_type; + throw IOException(error); + } + + eatwhite(infile); + + infile.get(ch); + + // now if we have a '/' here it's a single node. If it's a '>' we get + // a tree with children + + if( ch == '/' ) + { + infile.get(ch); // get the closing '>' + if( ch != '>' ) + { + string error = "Invalid XML file... no '>' found after '/' ("; + error += ch + ")"; + throw IOException(error); + } + + // get the rest on this level + loadSiblings( infile, current ); + + return current; + } + + // and finaly see if we got the close tag... + if( ch != '>' ) + { + string error = "Invalid XML file... no '>' found ("; + error += ch + ")"; + throw IOException(error); + } + + // handle the ones below this level.. + loadChildren( infile, current ); + + // load the rest on this level... + loadSiblings( infile, current ); + + return current; +} +#endif // ANTLR_SUPPORT_XML + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + +/* Heterogeneous AST/XML-I/O ramblings... + * + * So there is some heterogeneous AST support.... + * basically in the code generators a new custom ast is generated without + * going throug the factory. It also expects the RefXAST to be defined. + * + * Is it maybe better to register all AST types with the ASTFactory class + * together with the respective factory methods. + * + * More and more I get the impression that hetero ast was a kindoff hack + * on top of ANTLR's normal AST system. + * + * The heteroast stuff will generate trouble for all astFactory.create( ... ) + * invocations. Most of this is handled via getASTCreateString methods in the + * codegenerator. At the moment getASTCreateString(GrammarAtom, String) has + * slightly to little info to do it's job (ok the hack that is in now + * works, but it's an ugly hack) + * + * An extra caveat is the 'nice' action.g thing. Which also judiciously calls + * getASTCreateString methods because it handles the #( ... ) syntax. + * And converts that to ASTFactory calls. + * + * + */ diff --git a/lib/antlr/src/ASTNULLType.cpp b/lib/antlr/src/ASTNULLType.cpp new file mode 100644 index 00000000..7dd62724 --- /dev/null +++ b/lib/antlr/src/ASTNULLType.cpp @@ -0,0 +1,157 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include "antlr/config.hpp" +#include "antlr/AST.hpp" +#include "antlr/ASTNULLType.hpp" + +#include <iostream> + +ANTLR_USING_NAMESPACE(std) + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +RefAST ASTNULLType::clone( void ) const +{ + return RefAST(this); +} + +void ASTNULLType::addChild( RefAST ) +{ +} + +size_t ASTNULLType::getNumberOfChildren() const +{ + return 0; +} + +bool ASTNULLType::equals( RefAST ) const +{ + return false; +} + +bool ASTNULLType::equalsList( RefAST ) const +{ + return false; +} + +bool ASTNULLType::equalsListPartial( RefAST ) const +{ + return false; +} + +bool ASTNULLType::equalsTree( RefAST ) const +{ + return false; +} + +bool ASTNULLType::equalsTreePartial( RefAST ) const +{ + return false; +} + +vector<RefAST> ASTNULLType::findAll( RefAST ) +{ + return vector<RefAST>(); +} + +vector<RefAST> ASTNULLType::findAllPartial( RefAST ) +{ + return vector<RefAST>(); +} + +RefAST ASTNULLType::getFirstChild() const +{ + return this; +} + +RefAST ASTNULLType::getNextSibling() const +{ + return this; +} + +string ASTNULLType::getText() const +{ + return "<ASTNULL>"; +} + +int ASTNULLType::getType() const +{ + return Token::NULL_TREE_LOOKAHEAD; +} + +void ASTNULLType::initialize( int, const string& ) +{ +} + +void ASTNULLType::initialize( RefAST ) +{ +} + +void ASTNULLType::initialize( RefToken ) +{ +} + +#ifdef ANTLR_SUPPORT_XML +void ASTNULLType::initialize( istream& ) +{ +} +#endif + +void ASTNULLType::setFirstChild( RefAST ) +{ +} + +void ASTNULLType::setNextSibling( RefAST ) +{ +} + +void ASTNULLType::setText( const string& ) +{ +} + +void ASTNULLType::setType( int ) +{ +} + +string ASTNULLType::toString() const +{ + return getText(); +} + +string ASTNULLType::toStringList() const +{ + return getText(); +} + +string ASTNULLType::toStringTree() const +{ + return getText(); +} + +#ifdef ANTLR_SUPPORT_XML +bool ASTNULLType::attributesToStream( ostream& ) const +{ + return false; +} + +void ASTNULLType::toStream( ostream& out ) const +{ + out << "</ASTNULL>" << endl; +} +#endif + +const char* ASTNULLType::typeName( void ) const +{ + return "ASTNULLType"; +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif diff --git a/lib/antlr/src/ASTRefCount.cpp b/lib/antlr/src/ASTRefCount.cpp new file mode 100644 index 00000000..0ca54df0 --- /dev/null +++ b/lib/antlr/src/ASTRefCount.cpp @@ -0,0 +1,41 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ +#include "antlr/ASTRefCount.hpp" +#include "antlr/AST.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +ASTRef::ASTRef(AST* p) +: ptr(p), count(1) +{ + if (p && !p->ref) + p->ref = this; +} + +ASTRef::~ASTRef() +{ + delete ptr; +} + +ASTRef* ASTRef::getRef(const AST* p) +{ + if (p) { + AST* pp = const_cast<AST*>(p); + if (pp->ref) + return pp->ref->increment(); + else + return new ASTRef(pp); + } else + return 0; +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + diff --git a/lib/antlr/src/BaseAST.cpp b/lib/antlr/src/BaseAST.cpp new file mode 100644 index 00000000..f10f1e16 --- /dev/null +++ b/lib/antlr/src/BaseAST.cpp @@ -0,0 +1,281 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include "antlr/config.hpp" + +#include <iostream> + +#include "antlr/AST.hpp" +#include "antlr/BaseAST.hpp" + +ANTLR_USING_NAMESPACE(std) +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +size_t BaseAST::getNumberOfChildren() const +{ + RefBaseAST t = this->down; + size_t n = 0; + if( t ) + { + n = 1; + while( t->right ) + { + t = t->right; + n++; + } + return n; + } + return n; +} + +void BaseAST::doWorkForFindAll( + ANTLR_USE_NAMESPACE(std)vector<RefAST>& v, + RefAST target,bool partialMatch) +{ + // Start walking sibling lists, looking for matches. + for (RefAST sibling=this; + sibling; + sibling=sibling->getNextSibling()) + { + if ( (partialMatch && sibling->equalsTreePartial(target)) || + (!partialMatch && sibling->equalsTree(target)) ) { + v.push_back(sibling); + } + // regardless of match or not, check any children for matches + if ( sibling->getFirstChild() ) { + RefBaseAST(sibling->getFirstChild())->doWorkForFindAll(v, target, partialMatch); + } + } +} + +/** Is t an exact structural and equals() match of this tree. The + * 'this' reference is considered the start of a sibling list. + */ +bool BaseAST::equalsList(RefAST t) const +{ + // the empty tree is not a match of any non-null tree. + if (!t) + return false; + + // Otherwise, start walking sibling lists. First mismatch, return false. + RefAST sibling=this; + for (;sibling && t; + sibling=sibling->getNextSibling(), t=t->getNextSibling()) { + // as a quick optimization, check roots first. + if (!sibling->equals(t)) + return false; + // if roots match, do full list match test on children. + if (sibling->getFirstChild()) { + if (!sibling->getFirstChild()->equalsList(t->getFirstChild())) + return false; + } + // sibling has no kids, make sure t doesn't either + else if (t->getFirstChild()) + return false; + } + + if (!sibling && !t) + return true; + + // one sibling list has more than the other + return false; +} + +/** Is 'sub' a subtree of this list? + * The siblings of the root are NOT ignored. + */ +bool BaseAST::equalsListPartial(RefAST sub) const +{ + // the empty tree is always a subset of any tree. + if (!sub) + return true; + + // Otherwise, start walking sibling lists. First mismatch, return false. + RefAST sibling=this; + for (;sibling && sub; + sibling=sibling->getNextSibling(), sub=sub->getNextSibling()) { + // as a quick optimization, check roots first. + if (!sibling->equals(sub)) + return false; + // if roots match, do partial list match test on children. + if (sibling->getFirstChild()) + if (!sibling->getFirstChild()->equalsListPartial(sub->getFirstChild())) + return false; + } + + if (!sibling && sub) + // nothing left to match in this tree, but subtree has more + return false; + + // either both are null or sibling has more, but subtree doesn't + return true; +} + +/** Is tree rooted at 'this' equal to 't'? The siblings + * of 'this' are ignored. + */ +bool BaseAST::equalsTree(RefAST t) const +{ + // check roots first + if (!equals(t)) + return false; + // if roots match, do full list match test on children. + if (getFirstChild()) { + if (!getFirstChild()->equalsList(t->getFirstChild())) + return false; + } + // sibling has no kids, make sure t doesn't either + else if (t->getFirstChild()) + return false; + + return true; +} + +/** Is 'sub' a subtree of the tree rooted at 'this'? The siblings + * of 'this' are ignored. + */ +bool BaseAST::equalsTreePartial(RefAST sub) const +{ + // the empty tree is always a subset of any tree. + if (!sub) + return true; + + // check roots first + if (!equals(sub)) + return false; + // if roots match, do full list partial match test on children. + if (getFirstChild()) + if (!getFirstChild()->equalsListPartial(sub->getFirstChild())) + return false; + + return true; +} + +/** Walk the tree looking for all exact subtree matches. Return + * an ASTEnumerator that lets the caller walk the list + * of subtree roots found herein. + */ +ANTLR_USE_NAMESPACE(std)vector<RefAST> BaseAST::findAll(RefAST target) +{ + ANTLR_USE_NAMESPACE(std)vector<RefAST> roots; + + // the empty tree cannot result in an enumeration + if (target) { + doWorkForFindAll(roots,target,false); // find all matches recursively + } + + return roots; +} + +/** Walk the tree looking for all subtrees. Return + * an ASTEnumerator that lets the caller walk the list + * of subtree roots found herein. + */ +ANTLR_USE_NAMESPACE(std)vector<RefAST> BaseAST::findAllPartial(RefAST target) +{ + ANTLR_USE_NAMESPACE(std)vector<RefAST> roots; + + // the empty tree cannot result in an enumeration + if (target) + doWorkForFindAll(roots,target,true); // find all matches recursively + + return roots; +} + +ANTLR_USE_NAMESPACE(std)string BaseAST::toStringList() const +{ + ANTLR_USE_NAMESPACE(std)string ts=""; + + if (getFirstChild()) + { + ts+=" ( "; + ts+=toString(); + ts+=getFirstChild()->toStringList(); + ts+=" )"; + } + else + { + ts+=" "; + ts+=toString(); + } + + if (getNextSibling()) + ts+=getNextSibling()->toStringList(); + + return ts; +} + +ANTLR_USE_NAMESPACE(std)string BaseAST::toStringTree() const +{ + ANTLR_USE_NAMESPACE(std)string ts = ""; + + if (getFirstChild()) + { + ts+=" ( "; + ts+=toString(); + ts+=getFirstChild()->toStringList(); + ts+=" )"; + } + else + { + ts+=" "; + ts+=toString(); + } + return ts; +} + +#ifdef ANTLR_SUPPORT_XML +/* This whole XML output stuff needs a little bit more thought + * I'd like to store extra XML data in the node. e.g. for custom ast's + * with for instance symboltable references. This + * should be more pluggable.. + * @returns boolean value indicating wether a closetag should be produced. + */ +bool BaseAST::attributesToStream( ANTLR_USE_NAMESPACE(std)ostream& out ) const +{ + out << "text=\"" << this->getText() + << "\" type=\"" << this->getType() << "\""; + + return false; +} + +void BaseAST::toStream( ANTLR_USE_NAMESPACE(std)ostream& out ) const +{ + for( RefAST node = this; node != 0; node = node->getNextSibling() ) + { + out << "<" << this->typeName() << " "; + + // Write out attributes and if there is extra data... + bool need_close_tag = node->attributesToStream( out ); + + if( need_close_tag ) + { + // got children so write them... + if( node->getFirstChild() != 0 ) + node->getFirstChild()->toStream( out ); + + // and a closing tag.. + out << "</" << node->typeName() << ">" << endl; + } + } +} +#endif + +// this is nasty, but it makes the code generation easier +ANTLR_API RefAST nullAST; + +#if defined(_MSC_VER) && !defined(__ICL) // Microsoft Visual C++ +extern ANTLR_API AST* const nullASTptr = 0; +#else +ANTLR_API AST* const nullASTptr = 0; +#endif + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif diff --git a/lib/antlr/src/BitSet.cpp b/lib/antlr/src/BitSet.cpp new file mode 100644 index 00000000..2a32404a --- /dev/null +++ b/lib/antlr/src/BitSet.cpp @@ -0,0 +1,62 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ +#include "antlr/BitSet.hpp" +#include <string> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +BitSet::BitSet(unsigned int nbits) +: storage(nbits) +{ + for (unsigned int i = 0; i < nbits ; i++ ) + storage[i] = false; +} + +BitSet::BitSet( const unsigned long* bits_, unsigned int nlongs ) +: storage(nlongs*32) +{ + for ( unsigned int i = 0 ; i < (nlongs * 32); i++) + storage[i] = (bits_[i>>5] & (1UL << (i&31))) ? true : false; +} + +BitSet::~BitSet() +{ +} + +void BitSet::add(unsigned int el) +{ + if( el >= storage.size() ) + storage.resize( el+1, false ); + + storage[el] = true; +} + +bool BitSet::member(unsigned int el) const +{ + if ( el >= storage.size()) + return false; + + return storage[el]; +} + +ANTLR_USE_NAMESPACE(std)vector<unsigned int> BitSet::toArray() const +{ + ANTLR_USE_NAMESPACE(std)vector<unsigned int> elems; + for (unsigned int i = 0; i < storage.size(); i++) + { + if (storage[i]) + elems.push_back(i); + } + + return elems; +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif diff --git a/lib/antlr/src/CharBuffer.cpp b/lib/antlr/src/CharBuffer.cpp new file mode 100644 index 00000000..a0e11eae --- /dev/null +++ b/lib/antlr/src/CharBuffer.cpp @@ -0,0 +1,52 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include "antlr/CharBuffer.hpp" +#include <iostream> + +//#include <ios> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/* RK: Per default istream does not throw exceptions. This can be + * enabled with: + * stream.exceptions(ios_base::badbit|ios_base::failbit|ios_base::eofbit); + * + * We could try catching the bad/fail stuff. But handling eof via this is + * not a good idea. EOF is best handled as a 'normal' character. + * + * So this does not work yet with gcc... Comment it until I get to a platform + * that does.. + */ + +/** Create a character buffer. Enable fail and bad exceptions, if supported + * by platform. */ +CharBuffer::CharBuffer(ANTLR_USE_NAMESPACE(std)istream& input_) +: input(input_) +{ +// input.exceptions(ANTLR_USE_NAMESPACE(std)ios_base::badbit| +// ANTLR_USE_NAMESPACE(std)ios_base::failbit); +} + +/** Get the next character from the stream. May throw CharStreamIOException + * when something bad happens (not EOF) (if supported by platform). + */ +int CharBuffer::getChar() +{ +// try { + return input.get(); +// } +// catch (ANTLR_USE_NAMESPACE(std)ios_base::failure& e) { +// throw CharStreamIOException(e); +// } +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif diff --git a/lib/antlr/src/CharScanner.cpp b/lib/antlr/src/CharScanner.cpp new file mode 100644 index 00000000..d5b1f753 --- /dev/null +++ b/lib/antlr/src/CharScanner.cpp @@ -0,0 +1,108 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include <iostream> + +#include "antlr/CharScanner.hpp" +#include "antlr/CommonToken.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif +ANTLR_C_USING(exit) + +CharScanner::CharScanner(InputBuffer& cb, bool case_sensitive ) + : saveConsumedInput(true) //, caseSensitiveLiterals(true) + , caseSensitive(case_sensitive) + , literals(CharScannerLiteralsLess(this)) + , inputState(new LexerInputState(cb)) + , commitToPath(false) + , tabsize(8) + , traceDepth(0) +{ + setTokenObjectFactory(&CommonToken::factory); +} + +CharScanner::CharScanner(InputBuffer* cb, bool case_sensitive ) + : saveConsumedInput(true) //, caseSensitiveLiterals(true) + , caseSensitive(case_sensitive) + , literals(CharScannerLiteralsLess(this)) + , inputState(new LexerInputState(cb)) + , commitToPath(false) + , tabsize(8) + , traceDepth(0) +{ + setTokenObjectFactory(&CommonToken::factory); +} + +CharScanner::CharScanner( const LexerSharedInputState& state, bool case_sensitive ) + : saveConsumedInput(true) //, caseSensitiveLiterals(true) + , caseSensitive(case_sensitive) + , literals(CharScannerLiteralsLess(this)) + , inputState(state) + , commitToPath(false) + , tabsize(8) + , traceDepth(0) +{ + setTokenObjectFactory(&CommonToken::factory); +} + +/** Report exception errors caught in nextToken() */ +void CharScanner::reportError(const RecognitionException& ex) +{ + ANTLR_USE_NAMESPACE(std)cerr << ex.toString().c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +/** Parser error-reporting function can be overridden in subclass */ +void CharScanner::reportError(const ANTLR_USE_NAMESPACE(std)string& s) +{ + if (getFilename() == "") + ANTLR_USE_NAMESPACE(std)cerr << "error: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; + else + ANTLR_USE_NAMESPACE(std)cerr << getFilename().c_str() << ": error: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +/** Parser warning-reporting function can be overridden in subclass */ +void CharScanner::reportWarning(const ANTLR_USE_NAMESPACE(std)string& s) +{ + if (getFilename() == "") + ANTLR_USE_NAMESPACE(std)cerr << "warning: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; + else + ANTLR_USE_NAMESPACE(std)cerr << getFilename().c_str() << ": warning: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +void CharScanner::traceIndent() +{ + for( int i = 0; i < traceDepth; i++ ) + ANTLR_USE_NAMESPACE(std)cout << " "; +} + +void CharScanner::traceIn(const char* rname) +{ + traceDepth++; + traceIndent(); + ANTLR_USE_NAMESPACE(std)cout << "> lexer " << rname + << "; c==" << LA(1) << ANTLR_USE_NAMESPACE(std)endl; +} + +void CharScanner::traceOut(const char* rname) +{ + traceIndent(); + ANTLR_USE_NAMESPACE(std)cout << "< lexer " << rname + << "; c==" << LA(1) << ANTLR_USE_NAMESPACE(std)endl; + traceDepth--; +} + +#ifndef NO_STATIC_CONSTS +const int CharScanner::NO_CHAR; +const int CharScanner::EOF_CHAR; +#endif + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + diff --git a/lib/antlr/src/CommonAST.cpp b/lib/antlr/src/CommonAST.cpp new file mode 100644 index 00000000..e132c0aa --- /dev/null +++ b/lib/antlr/src/CommonAST.cpp @@ -0,0 +1,49 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ +#include "antlr/config.hpp" + +#include <cstdlib> +#include <iostream> + +#include "antlr/CommonAST.hpp" +#include "antlr/ANTLRUtil.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +const char* const CommonAST::TYPE_NAME = "CommonAST"; + +#ifdef ANTLR_SUPPORT_XML +void CommonAST::initialize( ANTLR_USE_NAMESPACE(std)istream& in ) +{ + ANTLR_USE_NAMESPACE(std)string t1, t2, text; + + // text + read_AttributeNValue( in, t1, text ); + + read_AttributeNValue( in, t1, t2 ); +#ifdef ANTLR_ATOI_IN_STD + int type = ANTLR_USE_NAMESPACE(std)atoi(t2.c_str()); +#else + int type = atoi(t2.c_str()); +#endif + + // initialize first part of AST. + this->initialize( type, text ); +} +#endif + +RefAST CommonAST::factory() +{ + return RefAST(new CommonAST); +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + diff --git a/lib/antlr/src/CommonASTWithHiddenTokens.cpp b/lib/antlr/src/CommonASTWithHiddenTokens.cpp new file mode 100644 index 00000000..b0e1a3dd --- /dev/null +++ b/lib/antlr/src/CommonASTWithHiddenTokens.cpp @@ -0,0 +1,64 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ +#include "antlr/config.hpp" +#include "antlr/AST.hpp" +#include "antlr/BaseAST.hpp" +#include "antlr/CommonAST.hpp" +#include "antlr/CommonASTWithHiddenTokens.hpp" +#include "antlr/CommonHiddenStreamToken.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +const char* const CommonASTWithHiddenTokens::TYPE_NAME = "CommonASTWithHiddenTokens"; +// RK: Do not put constructor and destructor into the header file here.. +// this triggers something very obscure in gcc 2.95.3 (and 3.0) +// missing vtables and stuff. +// Although this may be a problem with with binutils. +CommonASTWithHiddenTokens::CommonASTWithHiddenTokens() +: CommonAST() +{ +} + +CommonASTWithHiddenTokens::~CommonASTWithHiddenTokens() +{ +} + +void CommonASTWithHiddenTokens::initialize(int t,const ANTLR_USE_NAMESPACE(std)string& txt) +{ + CommonAST::initialize(t,txt); +} + +void CommonASTWithHiddenTokens::initialize(RefAST t) +{ + CommonAST::initialize(t); + hiddenBefore = RefCommonASTWithHiddenTokens(t)->getHiddenBefore(); + hiddenAfter = RefCommonASTWithHiddenTokens(t)->getHiddenAfter(); +} + +void CommonASTWithHiddenTokens::initialize(RefToken t) +{ + CommonAST::initialize(t); + hiddenBefore = static_cast<CommonHiddenStreamToken*>(t.get())->getHiddenBefore(); + hiddenAfter = static_cast<CommonHiddenStreamToken*>(t.get())->getHiddenAfter(); +} + +RefAST CommonASTWithHiddenTokens::factory() +{ + return RefAST(new CommonASTWithHiddenTokens); +} + +RefAST CommonASTWithHiddenTokens::clone( void ) const +{ + CommonASTWithHiddenTokens *ast = new CommonASTWithHiddenTokens( *this ); + return RefAST(ast); +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif diff --git a/lib/antlr/src/CommonHiddenStreamToken.cpp b/lib/antlr/src/CommonHiddenStreamToken.cpp new file mode 100644 index 00000000..9396a43b --- /dev/null +++ b/lib/antlr/src/CommonHiddenStreamToken.cpp @@ -0,0 +1,56 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ +#include "antlr/CommonHiddenStreamToken.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +CommonHiddenStreamToken::CommonHiddenStreamToken() +: CommonToken() +{ +} + +CommonHiddenStreamToken::CommonHiddenStreamToken(int t, const ANTLR_USE_NAMESPACE(std)string& txt) +: CommonToken(t,txt) +{ +} + +CommonHiddenStreamToken::CommonHiddenStreamToken(const ANTLR_USE_NAMESPACE(std)string& s) +: CommonToken(s) +{ +} + +RefToken CommonHiddenStreamToken::getHiddenAfter() +{ + return hiddenAfter; +} + +RefToken CommonHiddenStreamToken::getHiddenBefore() +{ + return hiddenBefore; +} + +RefToken CommonHiddenStreamToken::factory() +{ + return RefToken(new CommonHiddenStreamToken); +} + +void CommonHiddenStreamToken::setHiddenAfter(RefToken t) +{ + hiddenAfter = t; +} + +void CommonHiddenStreamToken::setHiddenBefore(RefToken t) +{ + hiddenBefore = t; +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + diff --git a/lib/antlr/src/CommonToken.cpp b/lib/antlr/src/CommonToken.cpp new file mode 100644 index 00000000..a223b239 --- /dev/null +++ b/lib/antlr/src/CommonToken.cpp @@ -0,0 +1,45 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include "antlr/CommonToken.hpp" +#include "antlr/String.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +CommonToken::CommonToken() : Token(), line(1), col(1), text("") +{} + +CommonToken::CommonToken(int t, const ANTLR_USE_NAMESPACE(std)string& txt) +: Token(t) +, line(1) +, col(1) +, text(txt) +{} + +CommonToken::CommonToken(const ANTLR_USE_NAMESPACE(std)string& s) +: Token() +, line(1) +, col(1) +, text(s) +{} + +ANTLR_USE_NAMESPACE(std)string CommonToken::toString() const +{ + return "[\""+getText()+"\",<"+getType()+">,line="+getLine()+",column="+getColumn()+"]"; +} + +RefToken CommonToken::factory() +{ + return RefToken(new CommonToken); +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + diff --git a/lib/antlr/src/InputBuffer.cpp b/lib/antlr/src/InputBuffer.cpp new file mode 100644 index 00000000..c9eced7d --- /dev/null +++ b/lib/antlr/src/InputBuffer.cpp @@ -0,0 +1,81 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include "antlr/config.hpp" +#include "antlr/InputBuffer.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** Ensure that the character buffer is sufficiently full */ +void InputBuffer::fill(unsigned int amount) +{ + syncConsume(); + // Fill the buffer sufficiently to hold needed characters + while (queue.entries() < amount + markerOffset) + { + // Append the next character + queue.append(getChar()); + } +} + +/** get the current lookahead characters as a string + * @warning it may treat 0 and EOF values wrong + */ +ANTLR_USE_NAMESPACE(std)string InputBuffer::getLAChars( void ) const +{ + ANTLR_USE_NAMESPACE(std)string ret; + + for(unsigned int i = markerOffset; i < queue.entries(); i++) + ret += queue.elementAt(i); + + return ret; +} + +/** get the current marked characters as a string + * @warning it may treat 0 and EOF values wrong + */ +ANTLR_USE_NAMESPACE(std)string InputBuffer::getMarkedChars( void ) const +{ + ANTLR_USE_NAMESPACE(std)string ret; + + for(unsigned int i = 0; i < markerOffset; i++) + ret += queue.elementAt(i); + + return ret; +} + +/** Return an integer marker that can be used to rewind the buffer to + * its current state. + */ +unsigned int InputBuffer::mark() +{ + syncConsume(); + nMarkers++; + return markerOffset; +} + +/** Rewind the character buffer to a marker. + * @param mark Marker returned previously from mark() + */ +void InputBuffer::rewind(unsigned int mark) +{ + syncConsume(); + markerOffset = mark; + nMarkers--; +} + +unsigned int InputBuffer::entries() const +{ + //assert(queue.entries() >= markerOffset); + return queue.entries() - markerOffset; +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif diff --git a/lib/antlr/src/LLkParser.cpp b/lib/antlr/src/LLkParser.cpp new file mode 100644 index 00000000..ab0d672a --- /dev/null +++ b/lib/antlr/src/LLkParser.cpp @@ -0,0 +1,85 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include "antlr/LLkParser.hpp" +#include <iostream> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +ANTLR_USING_NAMESPACE(std) + +/**An LL(k) parser. + * + * @see antlr.Token + * @see antlr.TokenBuffer + * @see antlr.LL1Parser + */ + +// LLkParser(int k_); + +LLkParser::LLkParser(const ParserSharedInputState& state, int k_) +: Parser(state), k(k_) +{ +} + +LLkParser::LLkParser(TokenBuffer& tokenBuf, int k_) +: Parser(tokenBuf), k(k_) +{ +} + +LLkParser::LLkParser(TokenStream& lexer, int k_) +: Parser(new TokenBuffer(lexer)), k(k_) +{ +} + +void LLkParser::trace(const char* ee, const char* rname) +{ + traceIndent(); + + cout << ee << rname << ((inputState->guessing>0)?"; [guessing]":"; "); + + for (int i = 1; i <= k; i++) + { + if (i != 1) { + cout << ", "; + } + cout << "LA(" << i << ")=="; + + string temp; + + try { + temp = LT(i)->getText().c_str(); + } + catch( ANTLRException& ae ) + { + temp = "[error: "; + temp += ae.toString(); + temp += ']'; + } + cout << temp; + } + + cout << endl; +} + +void LLkParser::traceIn(const char* rname) +{ + traceDepth++; + trace("> ",rname); +} + +void LLkParser::traceOut(const char* rname) +{ + trace("< ",rname); + traceDepth--; +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif diff --git a/lib/antlr/src/Makefile.am b/lib/antlr/src/Makefile.am new file mode 100644 index 00000000..b1af9fca --- /dev/null +++ b/lib/antlr/src/Makefile.am @@ -0,0 +1,10 @@ + +# Make #include <antlr/xxx> work.. +INCLUDES = -I$(srcdir)/.. +KDE_CXXFLAGS = $(USE_EXCEPTIONS) + +noinst_LTLIBRARIES = libantlr.la + +libantlr_la_LDFLAGS = -no-undefined + +libantlr_la_SOURCES = ANTLRUtil.cpp ASTFactory.cpp ASTNULLType.cpp ASTRefCount.cpp BaseAST.cpp BitSet.cpp CharBuffer.cpp CharScanner.cpp CommonAST.cpp CommonASTWithHiddenTokens.cpp CommonHiddenStreamToken.cpp CommonToken.cpp InputBuffer.cpp LLkParser.cpp MismatchedCharException.cpp MismatchedTokenException.cpp NoViableAltException.cpp NoViableAltForCharException.cpp Parser.cpp RecognitionException.cpp String.cpp Token.cpp TokenBuffer.cpp TokenRefCount.cpp TokenStreamBasicFilter.cpp TokenStreamHiddenTokenFilter.cpp TokenStreamRewriteEngine.cpp TokenStreamSelector.cpp TreeParser.cpp diff --git a/lib/antlr/src/MismatchedCharException.cpp b/lib/antlr/src/MismatchedCharException.cpp new file mode 100644 index 00000000..e73dbf36 --- /dev/null +++ b/lib/antlr/src/MismatchedCharException.cpp @@ -0,0 +1,120 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include "antlr/CharScanner.hpp" +#include "antlr/MismatchedCharException.hpp" +#include "antlr/String.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +MismatchedCharException::MismatchedCharException() + : RecognitionException("Mismatched char") +{} + +// Expected range / not range +MismatchedCharException::MismatchedCharException( + int c, + int lower, + int upper_, + bool matchNot, + CharScanner* scanner_ +) : RecognitionException("Mismatched char", + scanner_->getFilename(), + scanner_->getLine(), scanner_->getColumn()) + , mismatchType(matchNot ? NOT_RANGE : RANGE) + , foundChar(c) + , expecting(lower) + , upper(upper_) + , scanner(scanner_) +{ +} + +// Expected token / not token +MismatchedCharException::MismatchedCharException( + int c, + int expecting_, + bool matchNot, + CharScanner* scanner_ +) : RecognitionException("Mismatched char", + scanner_->getFilename(), + scanner_->getLine(), scanner_->getColumn()) + , mismatchType(matchNot ? NOT_CHAR : CHAR) + , foundChar(c) + , expecting(expecting_) + , scanner(scanner_) +{ +} + +// Expected BitSet / not BitSet +MismatchedCharException::MismatchedCharException( + int c, + BitSet set_, + bool matchNot, + CharScanner* scanner_ +) : RecognitionException("Mismatched char", + scanner_->getFilename(), + scanner_->getLine(), scanner_->getColumn()) + , mismatchType(matchNot ? NOT_SET : SET) + , foundChar(c) + , set(set_) + , scanner(scanner_) +{ +} + +ANTLR_USE_NAMESPACE(std)string MismatchedCharException::getMessage() const +{ + ANTLR_USE_NAMESPACE(std)string s; + + switch (mismatchType) { + case CHAR : + s += "expecting '" + charName(expecting) + "', found '" + charName(foundChar) + "'"; + break; + case NOT_CHAR : + s += "expecting anything but '" + charName(expecting) + "'; got it anyway"; + break; + case RANGE : + s += "expecting token in range: '" + charName(expecting) + "'..'" + charName(upper) + "', found '" + charName(foundChar) + "'"; + break; + case NOT_RANGE : + s += "expecting token NOT in range: " + charName(expecting) + "'..'" + charName(upper) + "', found '" + charName(foundChar) + "'"; + break; + case SET : + case NOT_SET : + { + s += ANTLR_USE_NAMESPACE(std)string("expecting ") + (mismatchType == NOT_SET ? "NOT " : "") + "one of ("; + ANTLR_USE_NAMESPACE(std)vector<unsigned int> elems = set.toArray(); + for ( unsigned int i = 0; i < elems.size(); i++ ) + { + s += " '"; + s += charName(elems[i]); + s += "'"; + } + s += "), found '" + charName(foundChar) + "'"; + } + break; + default : + s += RecognitionException::getMessage(); + break; + } + + return s; +} + +#ifndef NO_STATIC_CONSTS +const int MismatchedCharException::CHAR; +const int MismatchedCharException::NOT_CHAR; +const int MismatchedCharException::RANGE; +const int MismatchedCharException::NOT_RANGE; +const int MismatchedCharException::SET; +const int MismatchedCharException::NOT_SET; +#endif + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif diff --git a/lib/antlr/src/MismatchedTokenException.cpp b/lib/antlr/src/MismatchedTokenException.cpp new file mode 100644 index 00000000..7fc349f7 --- /dev/null +++ b/lib/antlr/src/MismatchedTokenException.cpp @@ -0,0 +1,196 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include "antlr/MismatchedTokenException.hpp" +#include "antlr/String.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +MismatchedTokenException::MismatchedTokenException() + : RecognitionException("Mismatched Token: expecting any AST node","<AST>",-1,-1) + , token(0) + , node(nullASTptr) + , tokenNames(0) + , numTokens(0) +{ +} + +// Expected range / not range +MismatchedTokenException::MismatchedTokenException( + const char* const* tokenNames_, + const int numTokens_, + RefAST node_, + int lower, + int upper_, + bool matchNot +) : RecognitionException("Mismatched Token","<AST>",-1,-1) + , token(0) + , node(node_) + , tokenText( (node_ ? node_->toString(): ANTLR_USE_NAMESPACE(std)string("<empty tree>")) ) + , mismatchType(matchNot ? NOT_RANGE : RANGE) + , expecting(lower) + , upper(upper_) + , tokenNames(tokenNames_) + , numTokens(numTokens_) +{ +} + +// Expected token / not token +MismatchedTokenException::MismatchedTokenException( + const char* const* tokenNames_, + const int numTokens_, + RefAST node_, + int expecting_, + bool matchNot +) : RecognitionException("Mismatched Token","<AST>",-1,-1) + , token(0) + , node(node_) + , tokenText( (node_ ? node_->toString(): ANTLR_USE_NAMESPACE(std)string("<empty tree>")) ) + , mismatchType(matchNot ? NOT_TOKEN : TOKEN) + , expecting(expecting_) + , tokenNames(tokenNames_) + , numTokens(numTokens_) +{ +} + +// Expected BitSet / not BitSet +MismatchedTokenException::MismatchedTokenException( + const char* const* tokenNames_, + const int numTokens_, + RefAST node_, + BitSet set_, + bool matchNot +) : RecognitionException("Mismatched Token","<AST>",-1,-1) + , token(0) + , node(node_) + , tokenText( (node_ ? node_->toString(): ANTLR_USE_NAMESPACE(std)string("<empty tree>")) ) + , mismatchType(matchNot ? NOT_SET : SET) + , set(set_) + , tokenNames(tokenNames_) + , numTokens(numTokens_) +{ +} + +// Expected range / not range +MismatchedTokenException::MismatchedTokenException( + const char* const* tokenNames_, + const int numTokens_, + RefToken token_, + int lower, + int upper_, + bool matchNot, + const ANTLR_USE_NAMESPACE(std)string& fileName_ +) : RecognitionException("Mismatched Token",fileName_,token_->getLine(),token_->getColumn()) + , token(token_) + , node(nullASTptr) + , tokenText(token_->getText()) + , mismatchType(matchNot ? NOT_RANGE : RANGE) + , expecting(lower) + , upper(upper_) + , tokenNames(tokenNames_) + , numTokens(numTokens_) +{ +} + +// Expected token / not token +MismatchedTokenException::MismatchedTokenException( + const char* const* tokenNames_, + const int numTokens_, + RefToken token_, + int expecting_, + bool matchNot, + const ANTLR_USE_NAMESPACE(std)string& fileName_ +) : RecognitionException("Mismatched Token",fileName_,token_->getLine(),token_->getColumn()) + , token(token_) + , node(nullASTptr) + , tokenText(token_->getText()) + , mismatchType(matchNot ? NOT_TOKEN : TOKEN) + , expecting(expecting_) + , tokenNames(tokenNames_) + , numTokens(numTokens_) +{ +} + +// Expected BitSet / not BitSet +MismatchedTokenException::MismatchedTokenException( + const char* const* tokenNames_, + const int numTokens_, + RefToken token_, + BitSet set_, + bool matchNot, + const ANTLR_USE_NAMESPACE(std)string& fileName_ +) : RecognitionException("Mismatched Token",fileName_,token_->getLine(),token_->getColumn()) + , token(token_) + , node(nullASTptr) + , tokenText(token_->getText()) + , mismatchType(matchNot ? NOT_SET : SET) + , set(set_) + , tokenNames(tokenNames_) + , numTokens(numTokens_) +{ +} + +ANTLR_USE_NAMESPACE(std)string MismatchedTokenException::getMessage() const +{ + ANTLR_USE_NAMESPACE(std)string s; + switch (mismatchType) { + case TOKEN: + s += "expecting " + tokenName(expecting) + ", found '" + tokenText + "'"; + break; + case NOT_TOKEN: + s += "expecting anything but " + tokenName(expecting) + "; got it anyway"; + break; + case RANGE: + s += "expecting token in range: " + tokenName(expecting) + ".." + tokenName(upper) + ", found '" + tokenText + "'"; + break; + case NOT_RANGE: + s += "expecting token NOT in range: " + tokenName(expecting) + ".." + tokenName(upper) + ", found '" + tokenText + "'"; + break; + case SET: + case NOT_SET: + { + s += ANTLR_USE_NAMESPACE(std)string("expecting ") + (mismatchType == NOT_SET ? "NOT " : "") + "one of ("; + ANTLR_USE_NAMESPACE(std)vector<unsigned int> elems = set.toArray(); + for ( unsigned int i = 0; i < elems.size(); i++ ) + { + s += " "; + s += tokenName(elems[i]); + } + s += "), found '" + tokenText + "'"; + } + break; + default: + s = RecognitionException::getMessage(); + break; + } + return s; +} + +ANTLR_USE_NAMESPACE(std)string MismatchedTokenException::tokenName(int tokenType) const +{ + if (tokenType == Token::INVALID_TYPE) + return "<Set of tokens>"; + else if (tokenType < 0 || tokenType >= numTokens) + return ANTLR_USE_NAMESPACE(std)string("<") + tokenType + ">"; + else + return tokenNames[tokenType]; +} + +#ifndef NO_STATIC_CONSTS +const int MismatchedTokenException::TOKEN; +const int MismatchedTokenException::NOT_TOKEN; +const int MismatchedTokenException::RANGE; +const int MismatchedTokenException::NOT_RANGE; +const int MismatchedTokenException::SET; +const int MismatchedTokenException::NOT_SET; +#endif + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif diff --git a/lib/antlr/src/NoViableAltException.cpp b/lib/antlr/src/NoViableAltException.cpp new file mode 100644 index 00000000..94da2cc4 --- /dev/null +++ b/lib/antlr/src/NoViableAltException.cpp @@ -0,0 +1,52 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include "antlr/NoViableAltException.hpp" +#include "antlr/String.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +ANTLR_USING_NAMESPACE(std) + +NoViableAltException::NoViableAltException(RefAST t) + : RecognitionException("NoViableAlt","<AST>",-1,-1), + token(0), node(t) +{ +} + +NoViableAltException::NoViableAltException( + RefToken t, + const ANTLR_USE_NAMESPACE(std)string& fileName_ +) : RecognitionException("NoViableAlt",fileName_,t->getLine(),t->getColumn()), + token(t), node(nullASTptr) +{ +} + +ANTLR_USE_NAMESPACE(std)string NoViableAltException::getMessage() const +{ + if (token) + { + if( token->getType() == Token::EOF_TYPE ) + return string("unexpected end of file"); + else if( token->getType() == Token::NULL_TREE_LOOKAHEAD ) + return string("unexpected end of tree"); + else + return string("unexpected token: ")+token->getText(); + } + + // must a tree parser error if token==null + if (!node) + return "unexpected end of subtree"; + + return string("unexpected AST node: ")+node->toString(); +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif diff --git a/lib/antlr/src/NoViableAltForCharException.cpp b/lib/antlr/src/NoViableAltForCharException.cpp new file mode 100644 index 00000000..10d9447a --- /dev/null +++ b/lib/antlr/src/NoViableAltForCharException.cpp @@ -0,0 +1,39 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include "antlr/NoViableAltForCharException.hpp" +#include "antlr/String.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +NoViableAltForCharException::NoViableAltForCharException(int c, CharScanner* scanner) + : RecognitionException("NoViableAlt", + scanner->getFilename(), + scanner->getLine(),scanner->getColumn()), + foundChar(c) +{ +} + +NoViableAltForCharException::NoViableAltForCharException( + int c, + const ANTLR_USE_NAMESPACE(std)string& fileName_, + int line_, int column_) + : RecognitionException("NoViableAlt",fileName_,line_,column_), + foundChar(c) +{ +} + +ANTLR_USE_NAMESPACE(std)string NoViableAltForCharException::getMessage() const +{ + return ANTLR_USE_NAMESPACE(std)string("unexpected char: ")+charName(foundChar); +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif diff --git a/lib/antlr/src/Parser.cpp b/lib/antlr/src/Parser.cpp new file mode 100644 index 00000000..640da7b6 --- /dev/null +++ b/lib/antlr/src/Parser.cpp @@ -0,0 +1,113 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include "antlr/Parser.hpp" + +#include <iostream> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** A generic ANTLR parser (LL(k) for k>=1) containing a bunch of + * utility routines useful at any lookahead depth. We distinguish between + * the LL(1) and LL(k) parsers because of efficiency. This may not be + * necessary in the near future. + * + * Each parser object contains the state of the parse including a lookahead + * cache (the form of which is determined by the subclass), whether or + * not the parser is in guess mode, where tokens come from, etc... + * + * <p> + * During <b>guess</b> mode, the current lookahead token(s) and token type(s) + * cache must be saved because the token stream may not have been informed + * to save the token (via <tt>mark</tt>) before the <tt>try</tt> block. + * Guessing is started by: + * <ol> + * <li>saving the lookahead cache. + * <li>marking the current position in the TokenBuffer. + * <li>increasing the guessing level. + * </ol> + * + * After guessing, the parser state is restored by: + * <ol> + * <li>restoring the lookahead cache. + * <li>rewinding the TokenBuffer. + * <li>decreasing the guessing level. + * </ol> + * + * @see antlr.Token + * @see antlr.TokenBuffer + * @see antlr.TokenStream + * @see antlr.LL1Parser + * @see antlr.LLkParser + */ + +bool DEBUG_PARSER = false; + +/** Parser error-reporting function can be overridden in subclass */ +void Parser::reportError(const RecognitionException& ex) +{ + ANTLR_USE_NAMESPACE(std)cerr << ex.toString().c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +/** Parser error-reporting function can be overridden in subclass */ +void Parser::reportError(const ANTLR_USE_NAMESPACE(std)string& s) +{ + if ( getFilename()=="" ) + ANTLR_USE_NAMESPACE(std)cerr << "error: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; + else + ANTLR_USE_NAMESPACE(std)cerr << getFilename().c_str() << ": error: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +/** Parser warning-reporting function can be overridden in subclass */ +void Parser::reportWarning(const ANTLR_USE_NAMESPACE(std)string& s) +{ + if ( getFilename()=="" ) + ANTLR_USE_NAMESPACE(std)cerr << "warning: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; + else + ANTLR_USE_NAMESPACE(std)cerr << getFilename().c_str() << ": warning: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +/** Set or change the input token buffer */ +// void setTokenBuffer(TokenBuffer<Token>* t); + +void Parser::traceIndent() +{ + for( int i = 0; i < traceDepth; i++ ) + ANTLR_USE_NAMESPACE(std)cout << " "; +} + +void Parser::traceIn(const char* rname) +{ + traceDepth++; + + for( int i = 0; i < traceDepth; i++ ) + ANTLR_USE_NAMESPACE(std)cout << " "; + + ANTLR_USE_NAMESPACE(std)cout << "> " << rname + << "; LA(1)==" << LT(1)->getText().c_str() + << ((inputState->guessing>0)?" [guessing]":"") + << ANTLR_USE_NAMESPACE(std)endl; +} + +void Parser::traceOut(const char* rname) +{ + for( int i = 0; i < traceDepth; i++ ) + ANTLR_USE_NAMESPACE(std)cout << " "; + + ANTLR_USE_NAMESPACE(std)cout << "< " << rname + << "; LA(1)==" << LT(1)->getText().c_str() + << ((inputState->guessing>0)?" [guessing]":"") + << ANTLR_USE_NAMESPACE(std)endl; + + traceDepth--; +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif diff --git a/lib/antlr/src/RecognitionException.cpp b/lib/antlr/src/RecognitionException.cpp new file mode 100644 index 00000000..9c185ccc --- /dev/null +++ b/lib/antlr/src/RecognitionException.cpp @@ -0,0 +1,71 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include "antlr/RecognitionException.hpp" +#include "antlr/String.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +RecognitionException::RecognitionException() +: ANTLRException("parsing error") +, line(-1) +, column(-1) +{ +} + +RecognitionException::RecognitionException(const ANTLR_USE_NAMESPACE(std)string& s) +: ANTLRException(s) +, line(-1) +, column(-1) +{ +} + +RecognitionException::RecognitionException(const ANTLR_USE_NAMESPACE(std)string& s, + const ANTLR_USE_NAMESPACE(std)string& fileName_, + int line_,int column_) +: ANTLRException(s) +, fileName(fileName_) +, line(line_) +, column(column_) +{ +} + +ANTLR_USE_NAMESPACE(std)string RecognitionException::getFileLineColumnString() const +{ + ANTLR_USE_NAMESPACE(std)string fileLineColumnString; + + if ( fileName.length() > 0 ) + fileLineColumnString = fileName + ":"; + + if ( line != -1 ) + { + if ( fileName.length() == 0 ) + fileLineColumnString = fileLineColumnString + "line "; + + fileLineColumnString = fileLineColumnString + line; + + if ( column != -1 ) + fileLineColumnString = fileLineColumnString + ":" + column; + + fileLineColumnString = fileLineColumnString + ":"; + } + + fileLineColumnString = fileLineColumnString + " "; + + return fileLineColumnString; +} + +ANTLR_USE_NAMESPACE(std)string RecognitionException::toString() const +{ + return getFileLineColumnString()+getMessage(); +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif diff --git a/lib/antlr/src/String.cpp b/lib/antlr/src/String.cpp new file mode 100644 index 00000000..ae70f479 --- /dev/null +++ b/lib/antlr/src/String.cpp @@ -0,0 +1,90 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include "antlr/String.hpp" + +#include <cctype> + +#ifdef HAS_NOT_CSTDIO_H +#include <stdio.h> +#else +#include <cstdio> +#endif + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +// wh: hack for Borland C++ 5.6 +#if __BORLANDC__ + using std::sprintf; +#endif + + +// RK: should be using snprintf actually... (or stringstream) +ANTLR_C_USING(sprintf) + +ANTLR_USE_NAMESPACE(std)string operator+( const ANTLR_USE_NAMESPACE(std)string& lhs, const int rhs ) +{ + char tmp[100]; + sprintf(tmp,"%d",rhs); + return lhs+tmp; +} + +ANTLR_USE_NAMESPACE(std)string operator+( const ANTLR_USE_NAMESPACE(std)string& lhs, size_t rhs ) +{ + char tmp[100]; + sprintf(tmp,"%u",rhs); + return lhs+tmp; +} + +/** Convert character to readable string + */ +ANTLR_USE_NAMESPACE(std)string charName(int ch) +{ + if (ch == EOF) + return "EOF"; + else + { + ANTLR_USE_NAMESPACE(std)string s; + + // when you think you've seen it all.. an isprint that crashes... + ch = ch & 0xFF; +#ifdef ANTLR_CCTYPE_NEEDS_STD + if( ANTLR_USE_NAMESPACE(std)isprint( ch ) ) +#else + if( isprint( ch ) ) +#endif + { + s.append("'"); + s += ch; + s.append("'"); +// s += "'"+ch+"'"; + } + else + { + s += "0x"; + + unsigned int t = ch >> 4; + if( t < 10 ) + s += t | 0x30; + else + s += t + 0x37; + t = ch & 0xF; + if( t < 10 ) + s += t | 0x30; + else + s += t + 0x37; + } + return s; + } +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + diff --git a/lib/antlr/src/Token.cpp b/lib/antlr/src/Token.cpp new file mode 100644 index 00000000..f8181634 --- /dev/null +++ b/lib/antlr/src/Token.cpp @@ -0,0 +1,80 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include "antlr/Token.hpp" +#include "antlr/String.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +int Token::getColumn() const +{ + return 0; +} + +int Token::getLine() const +{ + return 0; +} + +ANTLR_USE_NAMESPACE(std)string Token::getText() const +{ + return "<no text>"; +} + +int Token::getType() const +{ + return type; +} + +void Token::setColumn(int) +{ +} + +void Token::setLine(int) +{ +} + +void Token::setText(const ANTLR_USE_NAMESPACE(std)string&) +{ +} + +void Token::setType(int t) +{ + type = t; +} + +void Token::setFilename(const ANTLR_USE_NAMESPACE(std)string&) +{ +} + +ANTLR_USE_NAMESPACE(std)string emptyString(""); + +const ANTLR_USE_NAMESPACE(std)string& Token::getFilename() const +{ + return emptyString; +} + +ANTLR_USE_NAMESPACE(std)string Token::toString() const +{ + return "[\""+getText()+"\",<"+type+">]"; +} + +ANTLR_API RefToken nullToken; + +#ifndef NO_STATIC_CONSTS +const int Token::MIN_USER_TYPE; +const int Token::NULL_TREE_LOOKAHEAD; +const int Token::INVALID_TYPE; +const int Token::EOF_TYPE; +const int Token::SKIP; +#endif + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif diff --git a/lib/antlr/src/TokenBuffer.cpp b/lib/antlr/src/TokenBuffer.cpp new file mode 100644 index 00000000..ed69d6e0 --- /dev/null +++ b/lib/antlr/src/TokenBuffer.cpp @@ -0,0 +1,96 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include "antlr/TokenBuffer.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/**A Stream of Token objects fed to the parser from a TokenStream that can + * be rewound via mark()/rewind() methods. + * <p> + * A dynamic array is used to buffer up all the input tokens. Normally, + * "k" tokens are stored in the buffer. More tokens may be stored during + * guess mode (testing syntactic predicate), or when LT(i>k) is referenced. + * Consumption of tokens is deferred. In other words, reading the next + * token is not done by conume(), but deferred until needed by LA or LT. + * <p> + * + * @see antlr.Token + * @see antlr.TokenStream + * @see antlr.TokenQueue + */ + +/** Create a token buffer */ +TokenBuffer::TokenBuffer( TokenStream& inp ) +: input(inp) +, nMarkers(0) +, markerOffset(0) +, numToConsume(0) +{ +} + +TokenBuffer::~TokenBuffer( void ) +{ +} + +/** Ensure that the token buffer is sufficiently full */ +void TokenBuffer::fill(unsigned int amount) +{ + syncConsume(); + // Fill the buffer sufficiently to hold needed tokens + while (queue.entries() < (amount + markerOffset)) + { + // Append the next token + queue.append(input.nextToken()); + } +} + +/** Get a lookahead token value */ +int TokenBuffer::LA(unsigned int i) +{ + fill(i); + return queue.elementAt(markerOffset+i-1)->getType(); +} + +/** Get a lookahead token */ +RefToken TokenBuffer::LT(unsigned int i) +{ + fill(i); + return queue.elementAt(markerOffset+i-1); +} + +/** Return an integer marker that can be used to rewind the buffer to + * its current state. + */ +unsigned int TokenBuffer::mark() +{ + syncConsume(); + nMarkers++; + return markerOffset; +} + +/**Rewind the token buffer to a marker. + * @param mark Marker returned previously from mark() + */ +void TokenBuffer::rewind(unsigned int mark) +{ + syncConsume(); + markerOffset=mark; + nMarkers--; +} + +/// Get number of non-consumed tokens +unsigned int TokenBuffer::entries() const +{ + return queue.entries() - markerOffset; +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE + } +#endif diff --git a/lib/antlr/src/TokenRefCount.cpp b/lib/antlr/src/TokenRefCount.cpp new file mode 100644 index 00000000..0afb0f84 --- /dev/null +++ b/lib/antlr/src/TokenRefCount.cpp @@ -0,0 +1,41 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id:$ + */ +#include "antlr/TokenRefCount.hpp" +#include "antlr/Token.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +TokenRef::TokenRef(Token* p) +: ptr(p), count(1) +{ + if (p && !p->ref) + p->ref = this; +} + +TokenRef::~TokenRef() +{ + delete ptr; +} + +TokenRef* TokenRef::getRef(const Token* p) +{ + if (p) { + Token* pp = const_cast<Token*>(p); + if (pp->ref) + return pp->ref->increment(); + else + return new TokenRef(pp); + } else + return 0; +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + diff --git a/lib/antlr/src/TokenStreamBasicFilter.cpp b/lib/antlr/src/TokenStreamBasicFilter.cpp new file mode 100644 index 00000000..982e8645 --- /dev/null +++ b/lib/antlr/src/TokenStreamBasicFilter.cpp @@ -0,0 +1,44 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ +#include "antlr/TokenStreamBasicFilter.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** This object is a TokenStream that passes through all + * tokens except for those that you tell it to discard. + * There is no buffering of the tokens. + */ +TokenStreamBasicFilter::TokenStreamBasicFilter(TokenStream& input_) +: input(&input_) +{ +} + +void TokenStreamBasicFilter::discard(int ttype) +{ + discardMask.add(ttype); +} + +void TokenStreamBasicFilter::discard(const BitSet& mask) +{ + discardMask = mask; +} + +RefToken TokenStreamBasicFilter::nextToken() +{ + RefToken tok = input->nextToken(); + while ( tok && discardMask.member(tok->getType()) ) { + tok = input->nextToken(); + } + return tok; +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + diff --git a/lib/antlr/src/TokenStreamHiddenTokenFilter.cpp b/lib/antlr/src/TokenStreamHiddenTokenFilter.cpp new file mode 100644 index 00000000..431df0c3 --- /dev/null +++ b/lib/antlr/src/TokenStreamHiddenTokenFilter.cpp @@ -0,0 +1,156 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ +#include "antlr/TokenStreamHiddenTokenFilter.hpp" +#include "antlr/CommonHiddenStreamToken.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/**This object filters a token stream coming from a lexer + * or another TokenStream so that only certain token channels + * get transmitted to the parser. + * + * Any of the channels can be filtered off as "hidden" channels whose + * tokens can be accessed from the parser. + */ + +TokenStreamHiddenTokenFilter::TokenStreamHiddenTokenFilter(TokenStream& input) +: TokenStreamBasicFilter(input) +{ +} + +void TokenStreamHiddenTokenFilter::consume() +{ + nextMonitoredToken = input->nextToken(); +} + +void TokenStreamHiddenTokenFilter::consumeFirst() +{ + consume(); + + // Handle situation where hidden or discarded tokens + // appear first in input stream + RefToken p; + // while hidden or discarded scarf tokens + while ( hideMask.member(LA(1)->getType()) || discardMask.member(LA(1)->getType()) ) { + if ( hideMask.member(LA(1)->getType()) ) { + if ( !p ) { + p = LA(1); + } + else { + static_cast<CommonHiddenStreamToken*>(p.get())->setHiddenAfter(LA(1)); + static_cast<CommonHiddenStreamToken*>(LA(1).get())->setHiddenBefore(p); // double-link + p = LA(1); + } + lastHiddenToken = p; + if (!firstHidden) + firstHidden = p; // record hidden token if first + } + consume(); + } +} + +BitSet TokenStreamHiddenTokenFilter::getDiscardMask() const +{ + return discardMask; +} + +/** Return a ptr to the hidden token appearing immediately after + * token t in the input stream. + */ +RefToken TokenStreamHiddenTokenFilter::getHiddenAfter(RefToken t) +{ + return static_cast<CommonHiddenStreamToken*>(t.get())->getHiddenAfter(); +} + +/** Return a ptr to the hidden token appearing immediately before + * token t in the input stream. + */ +RefToken TokenStreamHiddenTokenFilter::getHiddenBefore(RefToken t) +{ + return static_cast<CommonHiddenStreamToken*>(t.get())->getHiddenBefore(); +} + +BitSet TokenStreamHiddenTokenFilter::getHideMask() const +{ + return hideMask; +} + +/** Return the first hidden token if one appears + * before any monitored token. + */ +RefToken TokenStreamHiddenTokenFilter::getInitialHiddenToken() +{ + return firstHidden; +} + +void TokenStreamHiddenTokenFilter::hide(int m) +{ + hideMask.add(m); +} + +void TokenStreamHiddenTokenFilter::hide(const BitSet& mask) +{ + hideMask = mask; +} + +RefToken TokenStreamHiddenTokenFilter::LA(int) +{ + return nextMonitoredToken; +} + +/** Return the next monitored token. +* Test the token following the monitored token. +* If following is another monitored token, save it +* for the next invocation of nextToken (like a single +* lookahead token) and return it then. +* If following is unmonitored, nondiscarded (hidden) +* channel token, add it to the monitored token. +* +* Note: EOF must be a monitored Token. +*/ +RefToken TokenStreamHiddenTokenFilter::nextToken() +{ + // handle an initial condition; don't want to get lookahead + // token of this splitter until first call to nextToken + if ( !LA(1) ) { + consumeFirst(); + } + + // we always consume hidden tokens after monitored, thus, + // upon entry LA(1) is a monitored token. + RefToken monitored = LA(1); + // point to hidden tokens found during last invocation + static_cast<CommonHiddenStreamToken*>(monitored.get())->setHiddenBefore(lastHiddenToken); + lastHiddenToken = nullToken; + + // Look for hidden tokens, hook them into list emanating + // from the monitored tokens. + consume(); + RefToken p = monitored; + // while hidden or discarded scarf tokens + while ( hideMask.member(LA(1)->getType()) || discardMask.member(LA(1)->getType()) ) { + if ( hideMask.member(LA(1)->getType()) ) { + // attach the hidden token to the monitored in a chain + // link forwards + static_cast<CommonHiddenStreamToken*>(p.get())->setHiddenAfter(LA(1)); + // link backwards + if (p != monitored) { //hidden cannot point to monitored tokens + static_cast<CommonHiddenStreamToken*>(LA(1).get())->setHiddenBefore(p); + } + p = lastHiddenToken = LA(1); + } + consume(); + } + return monitored; +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + diff --git a/lib/antlr/src/TokenStreamRewriteEngine.cpp b/lib/antlr/src/TokenStreamRewriteEngine.cpp new file mode 100644 index 00000000..2f171eb6 --- /dev/null +++ b/lib/antlr/src/TokenStreamRewriteEngine.cpp @@ -0,0 +1,214 @@ +#include <antlr/config.hpp> + +#include <string> +#include <list> +#include <vector> +#include <map> +#include <utility> +#include <iostream> +#include <iterator> +#include <sstream> +#include <cassert> + +#include <antlr/TokenStream.hpp> +#include <antlr/TokenWithIndex.hpp> +#include <antlr/BitSet.hpp> +#include <antlr/TokenStreamRewriteEngine.hpp> + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +#ifndef NO_STATIC_CONSTS +const size_t TokenStreamRewriteEngine::MIN_TOKEN_INDEX = 0; +const int TokenStreamRewriteEngine::PROGRAM_INIT_SIZE = 100; +#endif + +const char* TokenStreamRewriteEngine::DEFAULT_PROGRAM_NAME = "default"; + +namespace { + + struct compareOperationIndex { + typedef TokenStreamRewriteEngine::RewriteOperation RewriteOperation; + bool operator() ( const RewriteOperation* a, const RewriteOperation* b ) const + { + return a->getIndex() < b->getIndex(); + } + }; + struct dumpTokenWithIndex { + dumpTokenWithIndex( ANTLR_USE_NAMESPACE(std)ostream& o ) : out(o) {} + void operator() ( const RefTokenWithIndex& t ) { + out << "[txt='" << t->getText() << "' tp=" << t->getType() << " idx=" << t->getIndex() << "]\n"; + } + ANTLR_USE_NAMESPACE(std)ostream& out; + }; +} + +TokenStreamRewriteEngine::TokenStreamRewriteEngine(TokenStream& upstream) +: stream(upstream) +, index(MIN_TOKEN_INDEX) +, tokens() +, programs() +, discardMask() +{ +} + +TokenStreamRewriteEngine::TokenStreamRewriteEngine(TokenStream& upstream, size_t initialSize ) +: stream(upstream) +, index(MIN_TOKEN_INDEX) +, tokens(initialSize) +, programs() +, discardMask() +{ +} + +RefToken TokenStreamRewriteEngine::nextToken( void ) +{ + RefTokenWithIndex t; + // suck tokens until end of stream or we find a non-discarded token + do { + t = RefTokenWithIndex(stream.nextToken()); + if ( t ) + { + t->setIndex(index); // what is t's index in list? + if ( t->getType() != Token::EOF_TYPE ) { + tokens.push_back(t); // track all tokens except EOF + } + index++; // move to next position + } + } while ( t && discardMask.member(t->getType()) ); + return RefToken(t); +} + +void TokenStreamRewriteEngine::rollback( const std::string& programName, + size_t instructionIndex ) +{ + program_map::iterator rewrite = programs.find(programName); + if( rewrite != programs.end() ) + { + operation_list& prog = rewrite->second; + operation_list::iterator + j = prog.begin(), + end = prog.end(); + + std::advance(j,instructionIndex); + if( j != end ) + prog.erase(j, end); + } +} + +void TokenStreamRewriteEngine::originalToStream( std::ostream& out, + size_t start, + size_t end ) const +{ + token_list::const_iterator s = tokens.begin(); + std::advance( s, start ); + token_list::const_iterator e = s; + std::advance( e, end-start ); + std::for_each( s, e, tokenToStream(out) ); +} + +void TokenStreamRewriteEngine::toStream( std::ostream& out, + const std::string& programName, + size_t firstToken, + size_t lastToken ) const +{ + if( tokens.size() == 0 ) + return; + + program_map::const_iterator rewriter = programs.find(programName); + + if ( rewriter == programs.end() ) + return; + + // get the prog and some iterators in it... + const operation_list& prog = rewriter->second; + operation_list::const_iterator + rewriteOpIndex = prog.begin(), + rewriteOpEnd = prog.end(); + + size_t tokenCursor = firstToken; + // make sure we don't run out of the tokens we have... + if( lastToken > (tokens.size() - 1) ) + lastToken = tokens.size() - 1; + + while ( tokenCursor <= lastToken ) + { +// std::cout << "tokenCursor = " << tokenCursor << " first prog index = " << (*rewriteOpIndex)->getIndex() << std::endl; + + if( rewriteOpIndex != rewriteOpEnd ) + { + size_t up_to_here = std::min(lastToken,(*rewriteOpIndex)->getIndex()); + while( tokenCursor < up_to_here ) + out << tokens[tokenCursor++]->getText(); + } + while ( rewriteOpIndex != rewriteOpEnd && + tokenCursor == (*rewriteOpIndex)->getIndex() && + tokenCursor <= lastToken ) + { + tokenCursor = (*rewriteOpIndex)->execute(out); + ++rewriteOpIndex; + } + if( tokenCursor <= lastToken ) + out << tokens[tokenCursor++]->getText(); + } + // std::cout << "Handling tail operations # left = " << std::distance(rewriteOpIndex,rewriteOpEnd) << std::endl; + // now see if there are operations (append) beyond last token index + std::for_each( rewriteOpIndex, rewriteOpEnd, executeOperation(out) ); + rewriteOpIndex = rewriteOpEnd; +} + +void TokenStreamRewriteEngine::toDebugStream( std::ostream& out, + size_t start, + size_t end ) const +{ + token_list::const_iterator s = tokens.begin(); + std::advance( s, start ); + token_list::const_iterator e = s; + std::advance( e, end-start ); + std::for_each( s, e, dumpTokenWithIndex(out) ); +} + +void TokenStreamRewriteEngine::addToSortedRewriteList( const std::string& programName, + RewriteOperation* op ) +{ + program_map::iterator rewrites = programs.find(programName); + // check if we got the program already.. + if ( rewrites == programs.end() ) + { + // no prog make a new one... + operation_list ops; + ops.push_back(op); + programs.insert(std::make_pair(programName,ops)); + return; + } + operation_list& prog = rewrites->second; + + if( prog.empty() ) + { + prog.push_back(op); + return; + } + + operation_list::iterator i, end = prog.end(); + i = end; + --i; + // if at or beyond last op's index, just append + if ( op->getIndex() >= (*i)->getIndex() ) { + prog.push_back(op); // append to list of operations + return; + } + i = prog.begin(); + + if( i != end ) + { + operation_list::iterator pos = std::upper_bound( i, end, op, compareOperationIndex() ); + prog.insert(pos,op); + } + else + prog.push_back(op); +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif diff --git a/lib/antlr/src/TokenStreamSelector.cpp b/lib/antlr/src/TokenStreamSelector.cpp new file mode 100644 index 00000000..602e50dc --- /dev/null +++ b/lib/antlr/src/TokenStreamSelector.cpp @@ -0,0 +1,107 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ +#include "antlr/TokenStreamSelector.hpp" +#include "antlr/TokenStreamRetryException.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** A token stream MUX (multiplexor) knows about n token streams + * and can multiplex them onto the same channel for use by token + * stream consumer like a parser. This is a way to have multiple + * lexers break up the same input stream for a single parser. + * Or, you can have multiple instances of the same lexer handle + * multiple input streams; this works great for includes. + */ + +TokenStreamSelector::TokenStreamSelector() +: input(0) +{ +} + +TokenStreamSelector::~TokenStreamSelector() +{ +} + +void TokenStreamSelector::addInputStream(TokenStream* stream, const ANTLR_USE_NAMESPACE(std)string& key) +{ + inputStreamNames[key] = stream; +} + +TokenStream* TokenStreamSelector::getCurrentStream() const +{ + return input; +} + +TokenStream* TokenStreamSelector::getStream(const ANTLR_USE_NAMESPACE(std)string& sname) const +{ + inputStreamNames_coll::const_iterator i = inputStreamNames.find(sname); + if (i == inputStreamNames.end()) { + throw ANTLR_USE_NAMESPACE(std)string("TokenStream ")+sname+" not found"; + } + return (*i).second; +} + +RefToken TokenStreamSelector::nextToken() +{ + // keep looking for a token until you don't + // get a retry exception + for (;;) { + try { + return input->nextToken(); + } + catch (TokenStreamRetryException&) { + // just retry "forever" + } + } +} + +TokenStream* TokenStreamSelector::pop() +{ + TokenStream* stream = streamStack.top(); + streamStack.pop(); + select(stream); + return stream; +} + +void TokenStreamSelector::push(TokenStream* stream) +{ + streamStack.push(input); + select(stream); +} + +void TokenStreamSelector::push(const ANTLR_USE_NAMESPACE(std)string& sname) +{ + streamStack.push(input); + select(sname); +} + +void TokenStreamSelector::retry() +{ + throw TokenStreamRetryException(); +} + +/** Set the stream without pushing old stream */ +void TokenStreamSelector::select(TokenStream* stream) +{ + input = stream; +} + +void TokenStreamSelector::select(const ANTLR_USE_NAMESPACE(std)string& sname) +{ + inputStreamNames_coll::const_iterator i = inputStreamNames.find(sname); + if (i == inputStreamNames.end()) { + throw ANTLR_USE_NAMESPACE(std)string("TokenStream ")+sname+" not found"; + } + input = (*i).second; +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif + diff --git a/lib/antlr/src/TreeParser.cpp b/lib/antlr/src/TreeParser.cpp new file mode 100644 index 00000000..6b3f2ca1 --- /dev/null +++ b/lib/antlr/src/TreeParser.cpp @@ -0,0 +1,72 @@ +/* ANTLR Translator Generator + * Project led by Terence Parr at http://www.jGuru.com + * Software rights: http://www.antlr.org/license.html + * + * $Id$ + */ + +#include "antlr/TreeParser.hpp" +#include "antlr/ASTNULLType.hpp" + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +namespace antlr { +#endif + +/** The AST Null object; the parsing cursor is set to this when + * it is found to be null. This way, we can test the + * token type of a node without having to have tests for null + * everywhere. + */ +RefAST TreeParser::ASTNULL(new ASTNULLType); + +/** Parser error-reporting function can be overridden in subclass */ +void TreeParser::reportError(const RecognitionException& ex) +{ + ANTLR_USE_NAMESPACE(std)cerr << ex.toString().c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +/** Parser error-reporting function can be overridden in subclass */ +void TreeParser::reportError(const ANTLR_USE_NAMESPACE(std)string& s) +{ + ANTLR_USE_NAMESPACE(std)cerr << "error: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +/** Parser warning-reporting function can be overridden in subclass */ +void TreeParser::reportWarning(const ANTLR_USE_NAMESPACE(std)string& s) +{ + ANTLR_USE_NAMESPACE(std)cerr << "warning: " << s.c_str() << ANTLR_USE_NAMESPACE(std)endl; +} + +/** Procedure to write out an indent for traceIn and traceOut */ +void TreeParser::traceIndent() +{ + for( int i = 0; i < traceDepth; i++ ) + ANTLR_USE_NAMESPACE(std)cout << " "; +} + +void TreeParser::traceIn(const char* rname, RefAST t) +{ + traceDepth++; + traceIndent(); + + ANTLR_USE_NAMESPACE(std)cout << "> " << rname + << "(" << (t ? t->toString().c_str() : "null") << ")" + << ((inputState->guessing>0)?" [guessing]":"") + << ANTLR_USE_NAMESPACE(std)endl; +} + +void TreeParser::traceOut(const char* rname, RefAST t) +{ + traceIndent(); + + ANTLR_USE_NAMESPACE(std)cout << "< " << rname + << "(" << (t ? t->toString().c_str() : "null") << ")" + << ((inputState->guessing>0)?" [guessing]":"") + << ANTLR_USE_NAMESPACE(std)endl; + + traceDepth--; +} + +#ifdef ANTLR_CXX_SUPPORTS_NAMESPACE +} +#endif diff --git a/lib/astyle/ASBeautifier.cpp b/lib/astyle/ASBeautifier.cpp new file mode 100644 index 00000000..432d58a9 --- /dev/null +++ b/lib/astyle/ASBeautifier.cpp @@ -0,0 +1,1933 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * ASBeautifier.cpp + * + * This file is a part of "Artistic Style" - an indentation and + * reformatting tool for C, C++, C# and Java source files. + * http://astyle.sourceforge.net + * + * The "Artistic Style" project, including all files needed to + * compile it, is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later + * version. + * + * This 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this project; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + +#include "astyle.h" + +#include <algorithm> +#include <iostream> + + +#define INIT_CONTAINER(container, value) {if ( (container) != NULL ) delete (container); (container) = (value); } +#define DELETE_CONTAINER(container) {if ( (container) != NULL ) delete (container); } + + +namespace astyle +{ +vector<const string*> ASBeautifier::headers; +vector<const string*> ASBeautifier::nonParenHeaders; +vector<const string*> ASBeautifier::preBlockStatements; +vector<const string*> ASBeautifier::assignmentOperators; +vector<const string*> ASBeautifier::nonAssignmentOperators; + + +/* + * initialize the static vars + */ +void ASBeautifier::initStatic() +{ + static int beautifierFileType = 9; // initialized with an invalid type + + if (fileType == beautifierFileType) // don't build unless necessary + return; + + beautifierFileType = fileType; + + headers.clear(); + nonParenHeaders.clear(); + assignmentOperators.clear(); + nonAssignmentOperators.clear(); + preBlockStatements.clear(); + + ASResource::buildHeaders(headers, fileType, true); + ASResource::buildNonParenHeaders(nonParenHeaders, fileType, true); + ASResource::buildAssignmentOperators(assignmentOperators); + ASResource::buildNonAssignmentOperators(nonAssignmentOperators); + ASResource::buildPreBlockStatements(preBlockStatements); + +// cout << "beaut" << endl; +} + +/** + * ASBeautifier's constructor + */ +ASBeautifier::ASBeautifier() +{ + waitingBeautifierStack = NULL; + activeBeautifierStack = NULL; + waitingBeautifierStackLengthStack = NULL; + activeBeautifierStackLengthStack = NULL; + + headerStack = NULL; + tempStacks = NULL; + blockParenDepthStack = NULL; + blockStatementStack = NULL; + parenStatementStack = NULL; + bracketBlockStateStack = NULL; + inStatementIndentStack = NULL; + inStatementIndentStackSizeStack = NULL; + parenIndentStack = NULL; + sourceIterator = NULL; + + isMinimalConditinalIndentSet = false; + shouldForceTabIndentation = false; + + setSpaceIndentation(4); + setMaxInStatementIndentLength(40); + setClassIndent(false); + setSwitchIndent(false); + setCaseIndent(false); + setBlockIndent(false); + setBracketIndent(false); + setNamespaceIndent(false); + setLabelIndent(false); + setEmptyLineFill(false); + fileType = C_TYPE; + setCStyle(); + setPreprocessorIndent(false); +} + +/** + * ASBeautifier's copy constructor + */ +ASBeautifier::ASBeautifier(const ASBeautifier &other) +{ + waitingBeautifierStack = NULL; + activeBeautifierStack = NULL; + waitingBeautifierStackLengthStack = NULL; + activeBeautifierStackLengthStack = NULL; + + headerStack = new vector<const string*>; + *headerStack = *other.headerStack; + + tempStacks = new vector<vector<const string*>*>; + vector<vector<const string*>*>::iterator iter; + for (iter = other.tempStacks->begin(); + iter != other.tempStacks->end(); + ++iter) + { + vector<const string*> *newVec = new vector<const string*>; + *newVec = **iter; + tempStacks->push_back(newVec); + } + blockParenDepthStack = new vector<int>; + *blockParenDepthStack = *other.blockParenDepthStack; + + blockStatementStack = new vector<bool>; + *blockStatementStack = *other.blockStatementStack; + + parenStatementStack = new vector<bool>; + *parenStatementStack = *other.parenStatementStack; + + bracketBlockStateStack = new vector<bool>; + *bracketBlockStateStack = *other.bracketBlockStateStack; + + inStatementIndentStack = new vector<int>; + *inStatementIndentStack = *other.inStatementIndentStack; + + inStatementIndentStackSizeStack = new vector<int>; + *inStatementIndentStackSizeStack = *other.inStatementIndentStackSizeStack; + + parenIndentStack = new vector<int>; + *parenIndentStack = *other.parenIndentStack; + + sourceIterator = other.sourceIterator; + + // protected variables + fileType = other.fileType; + isCStyle = other.isCStyle; + isJavaStyle = other.isJavaStyle; + isSharpStyle = other.isSharpStyle; + + // variables set by ASFormatter + // must also be updated in preprocessor + inLineNumber = other.inLineNumber; + outLineNumber = other.outLineNumber; + lineCommentNoBeautify = other.lineCommentNoBeautify; + isNonInStatementArray = other.isNonInStatementArray; + + // private variables + indentString = other.indentString; + currentHeader = other.currentHeader; + previousLastLineHeader = other.previousLastLineHeader; + immediatelyPreviousAssignmentOp = other.immediatelyPreviousAssignmentOp; + probationHeader = other.probationHeader; + isInQuote = other.isInQuote; + isInComment = other.isInComment; + isInCase = other.isInCase; + isInQuestion = other.isInQuestion; + isInStatement = other.isInStatement; + isInHeader = other.isInHeader; + isInOperator = other.isInOperator; + isInTemplate = other.isInTemplate; + isInDefine = other.isInDefine; + isInDefineDefinition = other.isInDefineDefinition; + classIndent = other.classIndent; + isInClassHeader = other.isInClassHeader; + isInClassHeaderTab = other.isInClassHeaderTab; + switchIndent = other.switchIndent; + caseIndent = other.caseIndent; + namespaceIndent = other.namespaceIndent; + bracketIndent = other.bracketIndent; + blockIndent = other.blockIndent; + labelIndent = other.labelIndent; + preprocessorIndent = other.preprocessorIndent; + isInConditional = other.isInConditional; + isMinimalConditinalIndentSet = other.isMinimalConditinalIndentSet; + shouldForceTabIndentation = other.shouldForceTabIndentation; + emptyLineFill = other.emptyLineFill; + backslashEndsPrevLine = other.backslashEndsPrevLine; + blockCommentNoIndent = other.blockCommentNoIndent; + blockCommentNoBeautify = other.blockCommentNoBeautify; + previousLineProbationTab = other.previousLineProbationTab; + minConditionalIndent = other.minConditionalIndent; + parenDepth = other.parenDepth; + indentLength = other.indentLength; + blockTabCount = other.blockTabCount; + leadingWhiteSpaces = other.leadingWhiteSpaces; + maxInStatementIndent = other.maxInStatementIndent; + templateDepth = other.templateDepth; + prevFinalLineSpaceTabCount = other.prevFinalLineSpaceTabCount; + prevFinalLineTabCount = other.prevFinalLineTabCount; + defineTabCount = other.defineTabCount; + quoteChar = other.quoteChar; + prevNonSpaceCh = other.prevNonSpaceCh; + currentNonSpaceCh = other.currentNonSpaceCh; + currentNonLegalCh = other.currentNonLegalCh; + prevNonLegalCh = other.prevNonLegalCh; +} + +/** + * ASBeautifier's destructor + */ +ASBeautifier::~ASBeautifier() +{ + DELETE_CONTAINER(headerStack); + DELETE_CONTAINER(tempStacks); + DELETE_CONTAINER(blockParenDepthStack); + DELETE_CONTAINER(blockStatementStack); + DELETE_CONTAINER(parenStatementStack); + DELETE_CONTAINER(bracketBlockStateStack); + DELETE_CONTAINER(inStatementIndentStack); + DELETE_CONTAINER(inStatementIndentStackSizeStack); + DELETE_CONTAINER(parenIndentStack); +} + +/** + * initialize the ASBeautifier. + * + * init() should be called every time a ABeautifier object is to start + * beautifying a NEW source file. + * init() recieves a pointer to a DYNAMICALLY CREATED ASSourceIterator object + * that will be used to iterate through the source code. This object will be + * deleted during the ASBeautifier's destruction, and thus should not be + * deleted elsewhere. + * + * @param iter a pointer to the DYNAMICALLY CREATED ASSourceIterator object. + */ +void ASBeautifier::init(ASSourceIterator *iter) +{ + sourceIterator = iter; + init(); +} + +/** + * initialize the ASBeautifier. + */ +void ASBeautifier::init() +{ + initStatic(); + + INIT_CONTAINER(waitingBeautifierStack, new vector<ASBeautifier*>); + INIT_CONTAINER(activeBeautifierStack, new vector<ASBeautifier*>); + + INIT_CONTAINER(waitingBeautifierStackLengthStack, new vector<int>); + INIT_CONTAINER(activeBeautifierStackLengthStack, new vector<int>); + + INIT_CONTAINER(headerStack, new vector<const string*>); + INIT_CONTAINER(tempStacks, new vector<vector<const string*>*>); + tempStacks->push_back(new vector<const string*>); + + INIT_CONTAINER(blockParenDepthStack, new vector<int>); + INIT_CONTAINER(blockStatementStack, new vector<bool>); + INIT_CONTAINER(parenStatementStack, new vector<bool>); + + INIT_CONTAINER(bracketBlockStateStack, new vector<bool>); + bracketBlockStateStack->push_back(true); + + INIT_CONTAINER(inStatementIndentStack, new vector<int>); + INIT_CONTAINER(inStatementIndentStackSizeStack, new vector<int>); + inStatementIndentStackSizeStack->push_back(0); + INIT_CONTAINER(parenIndentStack, new vector<int>); + + immediatelyPreviousAssignmentOp = NULL; + previousLastLineHeader = NULL; + currentHeader = NULL; + + isInQuote = false; + isInComment = false; + isInStatement = false; + isInCase = false; + isInQuestion = false; + isInClassHeader = false; + isInClassHeaderTab = false; + isInHeader = false; + isInOperator = false; + isInTemplate = false; + isInConditional = false; + templateDepth = 0; + parenDepth = 0; + blockTabCount = 0; + leadingWhiteSpaces = 0; + prevNonSpaceCh = '{'; + currentNonSpaceCh = '{'; + prevNonLegalCh = '{'; + currentNonLegalCh = '{'; + quoteChar = ' '; + prevFinalLineSpaceTabCount = 0; + prevFinalLineTabCount = 0; + probationHeader = NULL; + backslashEndsPrevLine = false; + isInDefine = false; + isInDefineDefinition = false; + defineTabCount = 0; + lineCommentNoBeautify = false; + blockCommentNoIndent = false; + blockCommentNoBeautify = false; + previousLineProbationTab = false; + isNonInStatementArray = false; + inLineNumber = -1; // for debugging + outLineNumber = 0; // for debugging +} + +/** + * set indentation style to C/C++. + */ +void ASBeautifier::setCStyle() +{ + fileType = C_TYPE; + isCStyle = true; + isJavaStyle = false; + isSharpStyle = false; +} + +/** + * set indentation style to Java. + */ +void ASBeautifier::setJavaStyle() +{ + fileType = JAVA_TYPE; + isJavaStyle = true; + isCStyle = false; + isSharpStyle = false; +} + +/** + * set indentation style to C#. + */ +void ASBeautifier::setSharpStyle() +{ + fileType = SHARP_TYPE; + isSharpStyle = true; + isCStyle = false; + isJavaStyle = false; +} + +/** + * indent using one tab per indentation + */ +void ASBeautifier::setTabIndentation(int length, bool forceTabs) +{ + indentString = "\t"; + indentLength = length; + shouldForceTabIndentation = forceTabs; + + if (!isMinimalConditinalIndentSet) + minConditionalIndent = indentLength * 2; +} + +/** + * indent using a number of spaces per indentation. + * + * @param length number of spaces per indent. + */ +void ASBeautifier::setSpaceIndentation(int length) +{ + indentString = string(length, ' '); + indentLength = length; + + if (!isMinimalConditinalIndentSet) + minConditionalIndent = indentLength * 2; +} + +/** + * set the maximum indentation between two lines in a multi-line statement. + * + * @param max maximum indentation length. + */ +void ASBeautifier::setMaxInStatementIndentLength(int max) +{ + maxInStatementIndent = max; +} + +/** + * set the minimum indentation between two lines in a multi-line condition. + * + * @param min minimal indentation length. + */ +void ASBeautifier::setMinConditionalIndentLength(int min) +{ + minConditionalIndent = min; + isMinimalConditinalIndentSet = true; +} + +/** + * set the state of the bracket indentation option. If true, brackets will + * be indented one additional indent. + * + * @param state state of option. + */ +void ASBeautifier::setBracketIndent(bool state) +{ + bracketIndent = state; +} + +/** + * set the state of the block indentation option. If true, entire blocks + * will be indented one additional indent, similar to the GNU indent style. + * + * @param state state of option. + */ +void ASBeautifier::setBlockIndent(bool state) +{ + if (state) + setBracketIndent(false); // so that we don't have both bracket and block indent + blockIndent = state; +} + +/** + * set the state of the class indentation option. If true, C++ class + * definitions will be indented one additional indent. + * + * @param state state of option. + */ +void ASBeautifier::setClassIndent(bool state) +{ + classIndent = state; +} + +/** + * set the state of the switch indentation option. If true, blocks of 'switch' + * statements will be indented one additional indent. + * + * @param state state of option. + */ +void ASBeautifier::setSwitchIndent(bool state) +{ + switchIndent = state; +} + +/** + * set the state of the case indentation option. If true, lines of 'case' + * statements will be indented one additional indent. + * + * @param state state of option. + */ +void ASBeautifier::setCaseIndent(bool state) +{ + caseIndent = state; +} + +/** + * set the state of the namespace indentation option. + * If true, blocks of 'namespace' statements will be indented one + * additional indent. Otherwise, NO indentation will be added. + * + * @param state state of option. + */ +void ASBeautifier::setNamespaceIndent(bool state) +{ + namespaceIndent = state; +} + +/** + * set the state of the label indentation option. + * If true, labels will be indented one indent LESS than the + * current indentation level. + * If false, labels will be flushed to the left with NO + * indent at all. + * + * @param state state of option. + */ +void ASBeautifier::setLabelIndent(bool state) +{ + labelIndent = state; +} + +/** + * set the state of the preprocessor indentation option. + * If true, multiline #define statements will be indented. + * + * @param state state of option. + */ +void ASBeautifier::setPreprocessorIndent(bool state) +{ + preprocessorIndent = state; +} + +/** + * set the state of the empty line fill option. + * If true, empty lines will be filled with the whitespace. + * of their previous lines. + * If false, these lines will remain empty. + * + * @param state state of option. + */ +void ASBeautifier::setEmptyLineFill(bool state) +{ + emptyLineFill = state; +} + +/** + * get the number of spaces per indent + * + * @return value of indentLength option. +*/ +int ASBeautifier::getIndentLength(void) +{ + return indentLength; +} + +/** + * get the char used for indentation, space or tab + * + * @return the char used for indentation. + */ +string ASBeautifier::getIndentString(void) +{ + return indentString; +} + +/** + * get the state of the case indentation option. If true, lines of 'case' + * statements will be indented one additional indent. + * + * @return state of caseIndent option. + */ +bool ASBeautifier::getCaseIndent(void) +{ + return caseIndent; +} + +/** + * get C style identifier. + * If true, a C source is being indented. + * + * @return state of isCStyle option. + */ +bool ASBeautifier::getCStyle(void) +{ + return isCStyle; +} + +/** + * get Java style identifier. + * If true, a Java source is being indented. + * + * @return state of isJavaStyle option. + */ +bool ASBeautifier::getJavaStyle(void) +{ + return isJavaStyle; +} + +/** + * get C# style identifier. + * If true, a C# source is being indented. + * + * @return state of isSharpStyle option. + */ +bool ASBeautifier::getSharpStyle(void) +{ + return isSharpStyle; +} + +/** + * get the state of the empty line fill option. + * If true, empty lines will be filled with the whitespace. + * of their previous lines. + * If false, these lines will remain empty. + * + * @return state of emptyLineFill option. + */ +bool ASBeautifier::getEmptyLineFill(void) +{ + return emptyLineFill; +} + +/** + * check if there are any indented lines ready to be read by nextLine() + * + * @return are there any indented lines ready? + */ +bool ASBeautifier::hasMoreLines() const +{ + return sourceIterator->hasMoreLines(); +} + +/** + * get the next indented line. + * + * @return indented line. + */ +string ASBeautifier::nextLine() +{ + return beautify(sourceIterator->nextLine()); +} + +/** + * beautify a line of source code. + * every line of source code in a source code file should be sent + * one after the other to the beautify method. + * + * @return the indented line. + * @param originalLine the original unindented line. + */ +string ASBeautifier::beautify(const string &originalLine) +{ + string line; + bool isInLineComment = false; + bool lineStartsInComment = false; + bool isInClass = false; + bool isInSwitch = false; + bool isImmediatelyAfterConst = false; + bool isSpecialChar = false; + char ch = ' '; + char prevCh; + string outBuffer; // the newly idented line is bufferd here + int tabCount = 0; + const string *lastLineHeader = NULL; + bool closingBracketReached = false; + int spaceTabCount = 0; + char tempCh; + size_t headerStackSize = headerStack->size(); + bool shouldIndentBrackettedLine = true; + int lineOpeningBlocksNum = 0; + int lineClosingBlocksNum = 0; + bool previousLineProbation = (probationHeader != NULL); + int i; + + currentHeader = NULL; + lineStartsInComment = isInComment; + blockCommentNoBeautify = blockCommentNoIndent; + previousLineProbationTab = false; + outLineNumber++; + + // handle and remove white spaces around the line: + // If not in comment, first find out size of white space before line, + // so that possible comments starting in the line continue in + // relation to the preliminary white-space. + if (!isInComment) + { + int strlen = originalLine.length(); + leadingWhiteSpaces = 0; + + for (int j = 0; j < strlen && isWhiteSpace(originalLine[j]); j++) + { + if (originalLine[j] == '\t') + leadingWhiteSpaces += indentLength; + else + leadingWhiteSpaces++; + } + line = trim(originalLine); + } + else + { + // convert leading tabs to spaces + string spaceTabs(indentLength, ' '); + string newLine = originalLine; + int strlen = newLine.length(); + + for (int j=0; j < leadingWhiteSpaces && j < strlen; j++) + { + if (newLine[j] == '\t') + { + newLine.replace(j, 1, spaceTabs); + strlen = newLine.length(); + } + } + + // trim the comment leaving the new leading whitespace + int trimSize = 0; + strlen = newLine.length(); + + while (trimSize < strlen + && trimSize < leadingWhiteSpaces + && isWhiteSpace(newLine[trimSize])) + trimSize++; + + + while (trimSize < strlen && isWhiteSpace(newLine[strlen-1])) + strlen--; + + line = newLine.substr(trimSize, strlen); + size_t trimEnd = line.find_last_not_of(" \t"); + if (trimEnd != string::npos) + { + int spacesToDelete = line.length() - 1 - trimEnd; + if (spacesToDelete > 0) + line.erase(trimEnd + 1, spacesToDelete); + } + } + + + if (line.length() == 0) + { + if (backslashEndsPrevLine) // must continue to clear variables + line = ' '; + else if (emptyLineFill) + return preLineWS(prevFinalLineSpaceTabCount, prevFinalLineTabCount); + else + return line; + } + + // handle preprocessor commands + + if (isCStyle && !isInComment && (line[0] == '#' || backslashEndsPrevLine)) + { + if (line[0] == '#') + { + string preproc = trim(string(line.c_str() + 1)); + + // When finding a multi-lined #define statement, the original beautifier + // 1. sets its isInDefineDefinition flag + // 2. clones a new beautifier that will be used for the actual indentation + // of the #define. This clone is put into the activeBeautifierStack in order + // to be called for the actual indentation. + // The original beautifier will have isInDefineDefinition = true, isInDefine = false + // The cloned beautifier will have isInDefineDefinition = true, isInDefine = true + if (preprocessorIndent && preproc.compare(0, 6, "define") == 0 && line[line.length() - 1] == '\\') + { + if (!isInDefineDefinition) + { + ASBeautifier *defineBeautifier; + + // this is the original beautifier + isInDefineDefinition = true; + + // push a new beautifier into the active stack + // this beautifier will be used for the indentation of this define + defineBeautifier = new ASBeautifier(*this); + activeBeautifierStack->push_back(defineBeautifier); + } + else + { + // the is the cloned beautifier that is in charge of indenting the #define. + isInDefine = true; + } + } + else if (preproc.compare(0, 2, "if") == 0) + { + // push a new beautifier into the stack + waitingBeautifierStackLengthStack->push_back(waitingBeautifierStack->size()); + activeBeautifierStackLengthStack->push_back(activeBeautifierStack->size()); + waitingBeautifierStack->push_back(new ASBeautifier(*this)); + } + else if (preproc.compare(0, 4/*2*/, "else") == 0) + { + if (waitingBeautifierStack && !waitingBeautifierStack->empty()) + { + // MOVE current waiting beautifier to active stack. + activeBeautifierStack->push_back(waitingBeautifierStack->back()); + waitingBeautifierStack->pop_back(); + } + } + else if (preproc.compare(0, 4, "elif") == 0) + { + if (waitingBeautifierStack && !waitingBeautifierStack->empty()) + { + // append a COPY current waiting beautifier to active stack, WITHOUT deleting the original. + activeBeautifierStack->push_back(new ASBeautifier(*(waitingBeautifierStack->back()))); + } + } + else if (preproc.compare(0, 5, "endif") == 0) + { + int stackLength; + ASBeautifier *beautifier; + + if (waitingBeautifierStackLengthStack && !waitingBeautifierStackLengthStack->empty()) + { + stackLength = waitingBeautifierStackLengthStack->back(); + waitingBeautifierStackLengthStack->pop_back(); + while ((int) waitingBeautifierStack->size() > stackLength) + { + beautifier = waitingBeautifierStack->back(); + waitingBeautifierStack->pop_back(); + delete beautifier; + } + } + + if (!activeBeautifierStackLengthStack->empty()) + { + stackLength = activeBeautifierStackLengthStack->back(); + activeBeautifierStackLengthStack->pop_back(); + while ((int) activeBeautifierStack->size() > stackLength) + { + beautifier = activeBeautifierStack->back(); + activeBeautifierStack->pop_back(); + delete beautifier; + } + } + } + } + + // check if the last char is a backslash + if (line.length() > 0) + backslashEndsPrevLine = (line[line.length() - 1] == '\\'); + else + backslashEndsPrevLine = false; + + // check if this line ends a multi-line #define + // if so, use the #define's cloned beautifier for the line's indentation + // and then remove it from the active beautifier stack and delete it. + if (!backslashEndsPrevLine && isInDefineDefinition && !isInDefine) + { + string beautifiedLine; + ASBeautifier *defineBeautifier; + + isInDefineDefinition = false; + defineBeautifier = activeBeautifierStack->back(); + activeBeautifierStack->pop_back(); + + beautifiedLine = defineBeautifier->beautify(line); + delete defineBeautifier; + return beautifiedLine; + } + + // unless this is a multi-line #define, return this precompiler line as is. + if (!isInDefine && !isInDefineDefinition) + return originalLine; + } + + // if there exists any worker beautifier in the activeBeautifierStack, + // then use it instead of me to indent the current line. + // variables set by ASFormatter must be updated. + if (!isInDefine && activeBeautifierStack != NULL && !activeBeautifierStack->empty()) + { + activeBeautifierStack->back()->inLineNumber = inLineNumber; + activeBeautifierStack->back()->outLineNumber = outLineNumber; + activeBeautifierStack->back()->lineCommentNoBeautify = lineCommentNoBeautify; + activeBeautifierStack->back()->isNonInStatementArray = isNonInStatementArray; + return activeBeautifierStack->back()->beautify(line); + } + + // calculate preliminary indentation based on data from past lines + if (!inStatementIndentStack->empty()) + spaceTabCount = inStatementIndentStack->back(); + + + for (i = 0; i < (int) headerStackSize; i++) + { + isInClass = false; + + if (blockIndent || (!(i > 0 && (*headerStack)[i-1] != &AS_OPEN_BRACKET + && (*headerStack)[i] == &AS_OPEN_BRACKET))) + ++tabCount; + + if (!isJavaStyle && !namespaceIndent && i >= 1 + && (*headerStack)[i-1] == &AS_NAMESPACE + && (*headerStack)[i] == &AS_OPEN_BRACKET) + --tabCount; + + if (isCStyle && i >= 1 + && (*headerStack)[i-1] == &AS_CLASS + && (*headerStack)[i] == &AS_OPEN_BRACKET) + { + if (classIndent) + ++tabCount; + isInClass = true; + } + + // is the switchIndent option is on, indent switch statements an additional indent. + else if (switchIndent && i > 1 && + (*headerStack)[i-1] == &AS_SWITCH && + (*headerStack)[i] == &AS_OPEN_BRACKET + ) + { + ++tabCount; + isInSwitch = true; + } + + } + + if (!lineStartsInComment + && isCStyle + && isInClass + && classIndent + && headerStackSize >= 2 + && (*headerStack)[headerStackSize-2] == &AS_CLASS + && (*headerStack)[headerStackSize-1] == &AS_OPEN_BRACKET + && line[0] == '}') + --tabCount; + + else if (!lineStartsInComment + && isInSwitch + && switchIndent + && headerStackSize >= 2 + && (*headerStack)[headerStackSize-2] == &AS_SWITCH + && (*headerStack)[headerStackSize-1] == &AS_OPEN_BRACKET + && line[0] == '}') + --tabCount; + + if (isInClassHeader) + { + isInClassHeaderTab = true; + tabCount += 2; + } + + if (isInConditional) + { + --tabCount; + } + + + // parse characters in the current line. + + for (i = 0; i < (int) line.length(); i++) + { + tempCh = line[i]; + + prevCh = ch; + ch = tempCh; + + outBuffer.append(1, ch); + + if (isWhiteSpace(ch)) + continue; + + // check for utf8 characters + // isalnum() will display an assert message in debug if not bypassed here + if (ch < 0) + continue; + + // handle special characters (i.e. backslash+character such as \n, \t, ...) + if (isSpecialChar) + { + isSpecialChar = false; + continue; + } + if (!(isInComment || isInLineComment) && line.compare(i, 2, "\\\\") == 0) + { + outBuffer.append(1, '\\'); + i++; + continue; + } + if (!(isInComment || isInLineComment) && ch == '\\') + { + isSpecialChar = true; + continue; + } + + // handle quotes (such as 'x' and "Hello Dolly") + if (!(isInComment || isInLineComment) && (ch == '"' || ch == '\'')) + if (!isInQuote) + { + quoteChar = ch; + isInQuote = true; + } + else if (quoteChar == ch) + { + isInQuote = false; + isInStatement = true; + continue; + } + if (isInQuote) + continue; + + // handle comments + + if (!(isInComment || isInLineComment) && line.compare(i, 2, "//") == 0) + { + isInLineComment = true; + outBuffer.append(1, '/'); + i++; + continue; + } + else if (!(isInComment || isInLineComment) && line.compare(i, 2, "/*") == 0) + { + isInComment = true; + outBuffer.append(1, '*'); + i++; + size_t j = line.find_first_not_of(" \t"); + if (!line.compare(j, 2, "/*") == 0) // does line start with comment? + blockCommentNoIndent = true; // if no, cannot indent continuation lines + continue; + } + else if ((isInComment || isInLineComment) && line.compare(i, 2, "*/") == 0) + { + isInComment = false; + outBuffer.append(1, '/'); + i++; + blockCommentNoIndent = false; // ok to indent next comment + continue; + } + + if (isInComment || isInLineComment) + continue; + + // if we have reached this far then we are NOT in a comment or string of special character... + + if (probationHeader != NULL) + { + if (((probationHeader == &AS_STATIC || probationHeader == &AS_CONST) && ch == '{') + || (probationHeader == &AS_SYNCHRONIZED && ch == '(')) + { + // insert the probation header as a new header + isInHeader = true; + headerStack->push_back(probationHeader); + + // handle the specific probation header + isInConditional = (probationHeader == &AS_SYNCHRONIZED); + if (probationHeader == &AS_CONST) + isImmediatelyAfterConst = true; + + isInStatement = false; + // if the probation comes from the previous line, then indent by 1 tab count. + if (previousLineProbation && ch == '{') + { + tabCount++; + previousLineProbationTab = true; + } + previousLineProbation = false; + } + + // dismiss the probation header + probationHeader = NULL; + } + + prevNonSpaceCh = currentNonSpaceCh; + currentNonSpaceCh = ch; + if (!isLegalNameChar(ch) && ch != ',' && ch != ';') + { + prevNonLegalCh = currentNonLegalCh; + currentNonLegalCh = ch; + } + + if (isInHeader) + { + isInHeader = false; + currentHeader = headerStack->back(); + } + else + currentHeader = NULL; + + if (isCStyle && isInTemplate + && (ch == '<' || ch == '>') + && findHeader(line, i, nonAssignmentOperators) == NULL) + { + if (ch == '<') + { + ++templateDepth; + } + else if (ch == '>') + { + if (--templateDepth <= 0) + { + if (isInTemplate) + ch = ';'; + else + ch = 't'; + isInTemplate = false; + templateDepth = 0; + } + } + } + + // handle parenthesies + if (ch == '(' || ch == '[' || ch == ')' || ch == ']') + { + if (ch == '(' || ch == '[') + { + if (parenDepth == 0) + { + parenStatementStack->push_back(isInStatement); + isInStatement = true; + } + parenDepth++; + + inStatementIndentStackSizeStack->push_back(inStatementIndentStack->size()); + + if (currentHeader != NULL) + registerInStatementIndent(line, i, spaceTabCount, minConditionalIndent/*indentLength*2*/, true); + else + registerInStatementIndent(line, i, spaceTabCount, 0, true); + } + else if (ch == ')' || ch == ']') + { + parenDepth--; + if (parenDepth == 0) + { + isInStatement = parenStatementStack->back(); + parenStatementStack->pop_back(); + ch = ' '; + + isInConditional = false; + } + + if (!inStatementIndentStackSizeStack->empty()) + { + int previousIndentStackSize = inStatementIndentStackSizeStack->back(); + inStatementIndentStackSizeStack->pop_back(); + while (previousIndentStackSize < (int) inStatementIndentStack->size()) + inStatementIndentStack->pop_back(); + + if (!parenIndentStack->empty()) + { + int poppedIndent = parenIndentStack->back(); + parenIndentStack->pop_back(); + + if (i == 0) + spaceTabCount = poppedIndent; + } + } + } + + continue; + } + + + if (ch == '{') + { + bool isBlockOpener; + // first, check if '{' is a block-opener or an static-array opener + isBlockOpener = ((prevNonSpaceCh == '{' && bracketBlockStateStack->back()) + || prevNonSpaceCh == '}' + || prevNonSpaceCh == ')' + || prevNonSpaceCh == ';' + || peekNextChar(line, i) == '{' + || isNonInStatementArray + || isInClassHeader +// || isBlockOpener + || isImmediatelyAfterConst + || (isInDefine && + (prevNonSpaceCh == '(' + || prevNonSpaceCh == '_' + || isalnum(prevNonSpaceCh)))); + + isInClassHeader = false; + if (!isBlockOpener && currentHeader != NULL) + { + for (size_t n = 0; n < nonParenHeaders.size(); n++) + if (currentHeader == nonParenHeaders[n]) + { + isBlockOpener = true; + break; + } + } + bracketBlockStateStack->push_back(isBlockOpener); + if (!isBlockOpener) + { + inStatementIndentStackSizeStack->push_back(inStatementIndentStack->size()); + registerInStatementIndent(line, i, spaceTabCount, 0, true); + parenDepth++; + if (i == 0) + shouldIndentBrackettedLine = false; + + continue; + } + + // this bracket is a block opener... + + ++lineOpeningBlocksNum; +// if (isInClassHeader) +// isInClassHeader = false; + + if (isInClassHeaderTab) + { + isInClassHeaderTab = false; + // decrease tab count if bracket is broken + size_t firstChar = line.find_first_not_of(" \t"); + if (firstChar != string::npos) + if (line[firstChar] == '{' && (int) firstChar == i) + tabCount -= 2; + } + + // do not allow inStatementIndent - should occur for Java files only + if (inStatementIndentStack->size() > 0) + { + spaceTabCount = 0; + inStatementIndentStack->back() = 0; + } + + blockParenDepthStack->push_back(parenDepth); + blockStatementStack->push_back(isInStatement); + + inStatementIndentStackSizeStack->push_back(inStatementIndentStack->size()); + if (inStatementIndentStack->size() > 0) + inStatementIndentStack->back() = 0; + + blockTabCount += isInStatement ? 1 : 0; + parenDepth = 0; + isInStatement = false; + + tempStacks->push_back(new vector<const string*>); + headerStack->push_back(&AS_OPEN_BRACKET); + lastLineHeader = &AS_OPEN_BRACKET; + + continue; + } + + //check if a header has been reached + if (isWhiteSpace(prevCh)) + { + bool isIndentableHeader = true; + const string *newHeader = findHeader(line, i, headers); + if (newHeader != NULL) + { + // if we reached here, then this is a header... + isInHeader = true; + + vector<const string*> *lastTempStack; + if (tempStacks->empty()) + lastTempStack = NULL; + else + lastTempStack = tempStacks->back(); + + // if a new block is opened, push a new stack into tempStacks to hold the + // future list of headers in the new block. + + // take care of the special case: 'else if (...)' + if (newHeader == &AS_IF && lastLineHeader == &AS_ELSE) + { + headerStack->pop_back(); + } + + // take care of 'else' + else if (newHeader == &AS_ELSE) + { + if (lastTempStack != NULL) + { + int indexOfIf = indexOf(*lastTempStack, &AS_IF); + if (indexOfIf != -1) + { + // recreate the header list in headerStack up to the previous 'if' + // from the temporary snapshot stored in lastTempStack. + int restackSize = lastTempStack->size() - indexOfIf - 1; + for (int r = 0; r < restackSize; r++) + { + headerStack->push_back(lastTempStack->back()); + lastTempStack->pop_back(); + } + if (!closingBracketReached) + tabCount += restackSize; + } + /* + * If the above if is not true, i.e. no 'if' before the 'else', + * then nothing beautiful will come out of this... + * I should think about inserting an Exception here to notify the caller of this... + */ + } + } + + // check if 'while' closes a previous 'do' + else if (newHeader == &AS_WHILE) + { + if (lastTempStack != NULL) + { + int indexOfDo = indexOf(*lastTempStack, &AS_DO); + if (indexOfDo != -1) + { + // recreate the header list in headerStack up to the previous 'do' + // from the temporary snapshot stored in lastTempStack. + int restackSize = lastTempStack->size() - indexOfDo - 1; + for (int r = 0; r < restackSize; r++) + { + headerStack->push_back(lastTempStack->back()); + lastTempStack->pop_back(); + } + if (!closingBracketReached) + tabCount += restackSize; + } + } + } + // check if 'catch' closes a previous 'try' or 'catch' + else if (newHeader == &AS_CATCH || newHeader == &AS_FINALLY) + { + if (lastTempStack != NULL) + { + int indexOfTry = indexOf(*lastTempStack, &AS_TRY); + if (indexOfTry == -1) + indexOfTry = indexOf(*lastTempStack, &AS_CATCH); + if (indexOfTry != -1) + { + // recreate the header list in headerStack up to the previous 'try' + // from the temporary snapshot stored in lastTempStack. + int restackSize = lastTempStack->size() - indexOfTry - 1; + for (int r = 0; r < restackSize; r++) + { + headerStack->push_back(lastTempStack->back()); + lastTempStack->pop_back(); + } + + if (!closingBracketReached) + tabCount += restackSize; + } + } + } + else if (newHeader == &AS_CASE) + { + isInCase = true; + --tabCount; + } + else if (newHeader == &AS_DEFAULT) + { + isInCase = true; + --tabCount; + } + else if (newHeader == &AS_STATIC + || newHeader == &AS_SYNCHRONIZED + || (newHeader == &AS_CONST && isCStyle)) + { + if (!headerStack->empty() && + (headerStack->back() == &AS_STATIC + || headerStack->back() == &AS_SYNCHRONIZED + || headerStack->back() == &AS_CONST)) + { + isIndentableHeader = false; + } + else + { + isIndentableHeader = false; + probationHeader = newHeader; + } + } + else if (newHeader == &AS_CONST) + { + isIndentableHeader = false; + } + else if (newHeader == &AS_TEMPLATE) + { + if (isCStyle) + isInTemplate = true; + isIndentableHeader = false; + } + + + if (isIndentableHeader) + { + headerStack->push_back(newHeader); + isInStatement = false; + if (indexOf(nonParenHeaders, newHeader) == -1) + { + isInConditional = true; + } + lastLineHeader = newHeader; + } + else + isInHeader = false; + + outBuffer.append(newHeader->substr(1)); + i += newHeader->length() - 1; + + continue; + } + } + + if (isCStyle && !isalpha(prevCh) + && line.compare(i, 8, "operator") == 0 && !isalnum(line[i+8])) + { + isInOperator = true; + outBuffer.append(AS_OPERATOR.substr(1)); + i += 7; + continue; + } + + // "new" operator is a pointer, not a calculation + if (!isalpha(prevCh) + && line.compare(i, 3, "new") == 0 && !isalnum(line[i+3])) + { + if (prevNonSpaceCh == '=' && isInStatement && !inStatementIndentStack->empty()) + inStatementIndentStack->back() = 0; + } + + if (ch == '?') + isInQuestion = true; + + + // special handling of 'case' statements + if (ch == ':') + { + if ((int) line.length() > i + 1 && line[i+1] == ':') // look for :: + { + ++i; + outBuffer.append(1, ':'); + ch = ' '; + continue; + } + + else if (isInQuestion) + { + isInQuestion = false; + } + + else if (isCStyle && isInClass && prevNonSpaceCh != ')') + { + --tabCount; + // found a 'private:' or 'public:' inside a class definition + // so do nothing special + } + + else if (!isJavaStyle && isInClassHeader) + { + // found a 'class A : public B' definition + // so do nothing special + } + + else if (isJavaStyle && lastLineHeader == &AS_FOR) + { + // found a java for-each statement + // so do nothing special + } + + else if (isCStyle && prevNonSpaceCh == ')') + { + isInClassHeader = true; + if (i == 0) + tabCount += 2; + } + else + { + currentNonSpaceCh = ';'; // so that brackets after the ':' will appear as block-openers + if (isInCase) + { + isInCase = false; + ch = ';'; // from here on, treat char as ';' + } + + + else // is in a label (e.g. 'label1:') + { + if (labelIndent) + --tabCount; // unindent label by one indent + else + tabCount = 0; // completely flush indent to left + } + + + + } + } + + if ((ch == ';' || (parenDepth > 0 && ch == ',')) && !inStatementIndentStackSizeStack->empty()) + while ((int) inStatementIndentStackSizeStack->back() + (parenDepth > 0 ? 1 : 0) + < (int) inStatementIndentStack->size()) + inStatementIndentStack->pop_back(); + + + // handle ends of statements + if ((ch == ';' && parenDepth == 0) || ch == '}'/* || (ch == ',' && parenDepth == 0)*/) + { + if (ch == '}') + { + // first check if this '}' closes a previous block, or a static array... + if (!bracketBlockStateStack->empty()) + { + bool bracketBlockState = bracketBlockStateStack->back(); + bracketBlockStateStack->pop_back(); + if (!bracketBlockState) + { + if (!inStatementIndentStackSizeStack->empty()) + { + // this bracket is a static array + + int previousIndentStackSize = inStatementIndentStackSizeStack->back(); + inStatementIndentStackSizeStack->pop_back(); + while (previousIndentStackSize < (int) inStatementIndentStack->size()) + inStatementIndentStack->pop_back(); + parenDepth--; + if (i == 0) + shouldIndentBrackettedLine = false; + + if (!parenIndentStack->empty()) + { + int poppedIndent = parenIndentStack->back(); + parenIndentStack->pop_back(); + if (i == 0) + spaceTabCount = poppedIndent; + } + } + continue; + } + } + + // this bracket is block closer... + + ++lineClosingBlocksNum; + + if (!inStatementIndentStackSizeStack->empty()) + inStatementIndentStackSizeStack->pop_back(); + + if (!blockParenDepthStack->empty()) + { + parenDepth = blockParenDepthStack->back(); + blockParenDepthStack->pop_back(); + isInStatement = blockStatementStack->back(); + blockStatementStack->pop_back(); + + if (isInStatement) + blockTabCount--; + } + + closingBracketReached = true; + int headerPlace = indexOf(*headerStack, &AS_OPEN_BRACKET); + if (headerPlace != -1) + { + const string *popped = headerStack->back(); + while (popped != &AS_OPEN_BRACKET) + { + headerStack->pop_back(); + popped = headerStack->back(); + } + headerStack->pop_back(); + + if (!tempStacks->empty()) + { + vector<const string*> *temp = tempStacks->back(); + tempStacks->pop_back(); + delete temp; + } + } + + + ch = ' '; // needed due to cases such as '}else{', so that headers ('else' tn tih case) will be identified... + } + + /* + * Create a temporary snapshot of the current block's header-list in the + * uppermost inner stack in tempStacks, and clear the headerStack up to + * the begining of the block. + * Thus, the next future statement will think it comes one indent past + * the block's '{' unless it specifically checks for a companion-header + * (such as a previous 'if' for an 'else' header) within the tempStacks, + * and recreates the temporary snapshot by manipulating the tempStacks. + */ + if (!tempStacks->back()->empty()) + while (!tempStacks->back()->empty()) + tempStacks->back()->pop_back(); + while (!headerStack->empty() && headerStack->back() != &AS_OPEN_BRACKET) + { + tempStacks->back()->push_back(headerStack->back()); + headerStack->pop_back(); + } + + if (parenDepth == 0 && ch == ';') + isInStatement = false; + + previousLastLineHeader = NULL; + isInClassHeader = false; + isInQuestion = false; + + continue; + } + + + // check for preBlockStatements ONLY if not within parenthesies + // (otherwise 'struct XXX' statements would be wrongly interpreted...) + if (isWhiteSpace(prevCh) && !isInTemplate && parenDepth == 0) + { + const string *newHeader = findHeader(line, i, preBlockStatements); + if (newHeader != NULL) + { + isInClassHeader = true; + outBuffer.append(newHeader->substr(1)); + i += newHeader->length() - 1; + headerStack->push_back(newHeader); + } + } + + // Handle operators + + immediatelyPreviousAssignmentOp = NULL; + + // Check if an operator has been reached. + const string *foundAssignmentOp = findHeader(line, i, assignmentOperators, false); + if (foundAssignmentOp == &AS_RETURN) + foundAssignmentOp = findHeader(line, i, assignmentOperators, true); + const string *foundNonAssignmentOp = findHeader(line, i, nonAssignmentOperators, false); + + // Since findHeader's boundry checking was not used above, it is possible + // that both an assignment op and a non-assignment op where found, + // e.g. '>>' and '>>='. If this is the case, treat the LONGER one as the + // found operator. + if (foundAssignmentOp != NULL && foundNonAssignmentOp != NULL) + if (foundAssignmentOp->length() < foundNonAssignmentOp->length()) + foundAssignmentOp = NULL; + else + foundNonAssignmentOp = NULL; + + if (foundNonAssignmentOp != NULL) + { + if (foundNonAssignmentOp->length() > 1) + { + outBuffer.append(foundNonAssignmentOp->substr(1)); + i += foundNonAssignmentOp->length() - 1; + } + } + + else if (foundAssignmentOp != NULL) + { + if (foundAssignmentOp->length() > 1) + { + outBuffer.append(foundAssignmentOp->substr(1)); + i += foundAssignmentOp->length() - 1; + } + + if (!isInOperator && !isInTemplate && !isNonInStatementArray) + { + registerInStatementIndent(line, i, spaceTabCount, 0, false); + immediatelyPreviousAssignmentOp = foundAssignmentOp; + isInStatement = true; + } + } + + if (isInOperator) + isInOperator = false; + } + + // handle special cases of unindentation: + + /* + * if '{' doesn't follow an immediately previous '{' in the headerStack + * (but rather another header such as "for" or "if", then unindent it + * by one indentation relative to its block. + */ + + if (!lineStartsInComment + && !blockIndent + && outBuffer.length() > 0 + && outBuffer[0] == '{' + && !(lineOpeningBlocksNum > 0 && lineOpeningBlocksNum == lineClosingBlocksNum) + && !(headerStack->size() > 1 && (*headerStack)[headerStack->size()-2] == &AS_OPEN_BRACKET) + && shouldIndentBrackettedLine) + --tabCount; + + else if (!lineStartsInComment + && outBuffer.length() > 0 + && outBuffer[0] == '}' + && shouldIndentBrackettedLine) + --tabCount; + + // correctly indent one-line-blocks... + else if (!lineStartsInComment + && outBuffer.length() > 0 + && lineOpeningBlocksNum > 0 + && lineOpeningBlocksNum == lineClosingBlocksNum + && previousLineProbationTab) + --tabCount; //lineOpeningBlocksNum - (blockIndent ? 1 : 0); + + if (tabCount < 0) + tabCount = 0; + + // take care of extra bracket indentatation option... + if (bracketIndent && outBuffer.length() > 0 && shouldIndentBrackettedLine) + if (outBuffer[0] == '{' || outBuffer[0] == '}') + tabCount++; + + + if (isInDefine) + { + if (outBuffer[0] == '#') + { + string preproc = trim(string(outBuffer.c_str() + 1)); + if (preproc.compare(0, 6, "define") == 0) + { + if (!inStatementIndentStack->empty() + && inStatementIndentStack->back() > 0) + { + defineTabCount = tabCount; + } + else + { + defineTabCount = tabCount - 1; + tabCount--; + } + } + } + + tabCount -= defineTabCount; + } + + if (tabCount < 0) + tabCount = 0; + if (lineCommentNoBeautify || blockCommentNoBeautify) + tabCount = spaceTabCount = 0; + + // finally, insert indentations into begining of line + + prevFinalLineSpaceTabCount = spaceTabCount; + prevFinalLineTabCount = tabCount; + + if (shouldForceTabIndentation) + { + tabCount += spaceTabCount / indentLength; + spaceTabCount = spaceTabCount % indentLength; + } + + outBuffer = preLineWS(spaceTabCount, tabCount) + outBuffer; + + if (lastLineHeader != NULL) + previousLastLineHeader = lastLineHeader; + + return outBuffer; +} + + +string ASBeautifier::preLineWS(int spaceTabCount, int tabCount) +{ + string ws; + + for (int i = 0; i < tabCount; i++) + ws += indentString; + + while ((spaceTabCount--) > 0) + ws += string(" "); + + return ws; + +} + +/** + * register an in-statement indent. + */ +void ASBeautifier::registerInStatementIndent(const string &line, int i, int spaceTabCount, + int minIndent, bool updateParenStack) +{ + int inStatementIndent; + int remainingCharNum = line.length() - i; + int nextNonWSChar = getNextProgramCharDistance(line, i); + + // if indent is around the last char in the line, indent instead 2 spaces from the previous indent + if (nextNonWSChar == remainingCharNum) + { + int previousIndent = spaceTabCount; + if (!inStatementIndentStack->empty()) + previousIndent = inStatementIndentStack->back(); + + inStatementIndentStack->push_back(/*2*/ indentLength + previousIndent); + if (updateParenStack) + parenIndentStack->push_back(previousIndent); + return; + } + + if (updateParenStack) + parenIndentStack->push_back(i + spaceTabCount); + + inStatementIndent = i + nextNonWSChar + spaceTabCount; + + if (i + nextNonWSChar < minIndent) + inStatementIndent = minIndent + spaceTabCount; + + if (i + nextNonWSChar > maxInStatementIndent) + inStatementIndent = indentLength * 2 + spaceTabCount; + + if (!inStatementIndentStack->empty() && + inStatementIndent < inStatementIndentStack->back()) + inStatementIndent = inStatementIndentStack->back(); + + if (isNonInStatementArray) + inStatementIndent = 0; + + inStatementIndentStack->push_back(inStatementIndent); +} + +/** + * get distance to the next non-white sspace, non-comment character in the line. + * if no such character exists, return the length remaining to the end of the line. + */ +int ASBeautifier::getNextProgramCharDistance(const string &line, int i) +{ + bool inComment = false; + int remainingCharNum = line.length() - i; + int charDistance; + char ch; + + for (charDistance = 1; charDistance < remainingCharNum; charDistance++) + { + ch = line[i + charDistance]; + if (inComment) + { + if (line.compare(i + charDistance, 2, "*/") == 0) + { + charDistance++; + inComment = false; + } + continue; + } + else if (isWhiteSpace(ch)) + continue; + else if (ch == '/') + { + if (line.compare(i + charDistance, 2, "//") == 0) + return remainingCharNum; + else if (line.compare(i + charDistance, 2, "/*") == 0) + { + charDistance++; + inComment = true; + } + } + else + return charDistance; + } + + return charDistance; +} + + +/** + * check if a specific line position contains a header, out of several possible headers. + * + * @return a pointer to the found header. if no header was found then return NULL. + */ +const string *ASBeautifier::findHeader(const string &line, int i, const vector<const string*> &possibleHeaders, bool checkBoundry) +{ + int maxHeaders = possibleHeaders.size(); + // const string *header = NULL; + int p; + + for (p = 0; p < maxHeaders; p++) + { + const string *header = possibleHeaders[p]; + + if (line.compare(i, header->length(), header->c_str()) == 0) + { + // check that this is a header and not a part of a longer word + // (e.g. not at its begining, not at its middle...) + + int lineLength = line.length(); + int headerEnd = i + header->length(); + char startCh = (*header)[0]; // first char of header + char endCh = 0; // char just after header + char prevCh = 0; // char just before header + + if (headerEnd < lineLength) + { + endCh = line[headerEnd]; + } + if (i > 0) + { + prevCh = line[i-1]; + } + + if (!checkBoundry) + { + return header; + } + else if (prevCh != 0 + && isLegalNameChar(startCh) + && isLegalNameChar(prevCh)) + { + return NULL; + } + else if (headerEnd >= lineLength + || !isLegalNameChar(startCh) + || !isLegalNameChar(endCh)) + { + return header; + } + else + { + return NULL; + } + } + } + + return NULL; +} + +/** + * find the index number of a string element in a container of strings + * + * @return the index number of element in the ocntainer. -1 if element not found. + * @param container a vector of strings. + * @param element the element to find . + */ +int ASBeautifier::indexOf(vector<const string*> &container, const string *element) +{ + vector<const string*>::const_iterator where; + + where = find(container.begin(), container.end(), element); + if (where == container.end()) + return -1; + else + return (int) (where - container.begin()); +} + +/** + * trim removes the white space surrounding a line. + * + * @return the trimmed line. + * @param str the line to trim. + */ +string ASBeautifier::trim(const string &str) +{ + + int start = 0; + int end = str.length() - 1; + + while (start < end && isWhiteSpace(str[start])) + start++; + + while (start <= end && isWhiteSpace(str[end])) + end--; + + string returnStr(str, start, end + 1 - start); + return returnStr; +} + +/** +* peek at the next unread character. +* +* @return the next unread character. +* @param line the line to check. +* @param i the current char position on the line. +*/ +char ASBeautifier::peekNextChar(string &line, int i) +{ + char ch = ' '; + size_t peekNum = line.find_first_not_of(" \t", i + 1); + + if (peekNum == string::npos) + return ch; + + ch = line[peekNum]; + + return ch; +} + + +} // end namespace astyle + diff --git a/lib/astyle/ASEnhancer.cpp b/lib/astyle/ASEnhancer.cpp new file mode 100644 index 00000000..6fea5970 --- /dev/null +++ b/lib/astyle/ASEnhancer.cpp @@ -0,0 +1,483 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * ASEnhancer.cpp + * + * This file is a part of "Artistic Style" - an indentation and + * reformatting tool for C, C++, C# and Java source files. + * http://astyle.sourceforge.net + * + * The "Artistic Style" project, including all files needed to + * compile it, is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later + * version. + * + * This 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this project; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + +// can trace only if NDEBUG is not defined +#ifndef NDEBUG +// #define TRACEswitch +// #define TRACEcase +// #define TRACEmisc +#endif + +#include "astyle.h" + +#include <iostream> +#include <fstream> +#include <sstream> + +#ifdef TRACEswitch +#define TRswitch(a,b) *traceOut << lineNumber << a << b << endl; +#else +#define TRswitch(a,b) ((void)0) +#endif // TRACEswitch +#ifdef TRACEcase +#define TRcase(a,b) *traceOut << lineNumber << a << b << endl; +#else +#define TRcase(a,b) ((void)0) +#endif // TRACEcase +#ifdef TRACEmisc +#define TRmisc(a) *traceOut << lineNumber << a << endl; +#else +#define TRmisc(a) ((void)0) +#endif // TRACEmisc + + +namespace astyle +{ + +// ---------------------------- functions for ASEnhancer Class ------------------------------------- + +/** + * ASEnhancer constructor + */ +ASEnhancer::ASEnhancer() +{ + // variables are initialized by init() + traceOut = new stringstream; +} + +/** + * Destructor of ASEnhancer + * Display the TRACE entries. + */ +ASEnhancer::~ASEnhancer() +{ +#if defined(TRACEswitch) || defined(TRACEcase) || defined(TRACEmisc) + string line; + string msg = "TRACE Entries\n\n"; + char countLine[50]; + int count = 0; + + while (getline(*traceOut, line)) + { + msg += line + '\n'; + count++; + } + sprintf(countLine, "\n%d Entries", count); + msg += countLine; + // write a text file to "My Documents" (Windows) + char filename [_MAX_PATH + _MAX_FNAME + _MAX_EXT + 1]; // full path and filename + strcpy(filename, getenv("USERPROFILE")); + strcat(filename, "\\My Documents\\tracee.txt"); + ofstream outfile(filename); + outfile << msg; + outfile.close(); +#endif + delete traceOut; +} + +/** + * initialize the ASEnhancer. + * + * init() is called each time an ASFormatter object is initialized. + */ +void ASEnhancer::init(int _indentLength, + string _indentString, + bool _isCStyle, + bool _isJavaStyle, + bool _isSharpStyle, + bool _caseIndent, + bool _emptyLineFill) +{ + // formatting variables from ASFormatter and ASBeautifier + indentLength = _indentLength; + if (_indentString.compare(0, 1, "\t") == 0) + useTabs = true; + else + useTabs = false; + isCStyle = _isCStyle; + isJavaStyle = _isJavaStyle; + isSharpStyle = _isSharpStyle; + caseIndent = _caseIndent; + emptyLineFill = _emptyLineFill; + + // unindent variables + lineNumber = 0; + bracketCount = 0; + isInComment = false; + isInQuote = false; + switchDepth = 0; + lookingForCaseBracket = false; + unindentNextLine = false; + +#if defined(TRACEswitch) || defined(TRACEcase) || defined(TRACEmisc) + *traceOut << "New file -------------" << endl; +#endif +} + +/** + * additional formatting for line of source code. + * every line of source code in a source code file should be sent + * one after the other to this function. + * indents event tables + * unindents the case blocks + * + * @param line the original formatted line will be updated if necessary. + */ +void ASEnhancer::enhance(string &line) +{ + static vector<switchVariables> swVector; // stack vector of switch variables + static switchVariables sw; // switch variables struct + + static bool nextLineIsEventTable; // begin event table is reached + static bool isInEventTable; // need to indent an event table + + bool isSpecialChar = false; + size_t lineLength; // length of the line being parsed + + lineNumber++; + lineLength = line.length(); + + // check for beginning of event table + if (nextLineIsEventTable) + { + isInEventTable = true; + nextLineIsEventTable = false; + } + + if (lineLength == 0 + && ! isInEventTable + && ! emptyLineFill) + return; + + // test for unindent on attached brackets + if (unindentNextLine) + { + sw.unindentDepth++; + sw.unindentCase = true; + unindentNextLine = false; + TRcase(" unindent case ", sw.unindentDepth); + } + + // parse characters in the current line. + + for (size_t i = 0; i < lineLength; i++) + { + char ch = line[i]; + + // bypass whitespace + if (isWhiteSpaceX(ch)) + continue; + + // handle special characters (i.e. backslash+character such as \n, \t, ...) + if (isSpecialChar) + { + isSpecialChar = false; + continue; + } + if (!(isInComment) && line.compare(i, 2, "\\\\") == 0) + { + i++; + continue; + } + if (!(isInComment) && ch == '\\') + { + isSpecialChar = true; + continue; + } + + // handle quotes (such as 'x' and "Hello Dolly") + if (!(isInComment) && (ch == '"' || ch == '\'')) + if (!isInQuote) + { + quoteChar = ch; + isInQuote = true; + } + else if (quoteChar == ch) + { + isInQuote = false; + continue; + } + + if (isInQuote) + continue; + + // handle comments + + if (!(isInComment) && line.compare(i, 2, "//") == 0) + { + // check for windows line markers + if (line.compare(i + 2, 1, "\xf0") > 0) + lineNumber--; + break; // finished with the line + } + else if (!(isInComment) && line.compare(i, 2, "/*") == 0) + { + isInComment = true; + i++; + continue; + } + else if ((isInComment) && line.compare(i, 2, "*/") == 0) + { + isInComment = false; + i++; + continue; + } + + if (isInComment) + continue; + + // if we have reached this far then we are NOT in a comment or string of special characters + + if (line[i] == '{') // if open bracket + bracketCount++; + + if (line[i] == '}') // if close bracket + bracketCount--; + + // ---------------- process event tables -------------------------------------- + + // check for event table begin + if (findKeyword(line, i, "BEGIN_EVENT_TABLE") + || findKeyword(line, i, "BEGIN_MESSAGE_MAP")) + nextLineIsEventTable = true; + + // check for event table end + if (findKeyword(line, i, "END_EVENT_TABLE") + || findKeyword(line, i, "END_MESSAGE_MAP")) + isInEventTable = false; + + // ---------------- process switch statements --------------------------------- + + if (findKeyword(line, i, "switch")) // if switch statement + { + switchDepth++; // bump switch depth + TRswitch(" switch ", switchDepth); + swVector.push_back(sw); // save current variables + sw.switchBracketCount = 0; + sw.unindentCase = false; // don't clear case until end of switch + i += 5; // bypass switch statement + continue; + } + + // just want switch statements from this point + + if (caseIndent || switchDepth == 0) // from here just want switch statements + continue; // get next char + + if (line[i] == '{') // if open bracket + { + sw.switchBracketCount++; + if (lookingForCaseBracket) // if 1st after case statement + { + sw.unindentCase = true; // unindenting this case + sw.unindentDepth++; // bump depth + lookingForCaseBracket = false; // not looking now + TRcase(" unindent case ", sw.unindentDepth); + } + continue; + } + + lookingForCaseBracket = false; // no opening bracket, don't indent + + if (line[i] == '}') // if close bracket + { + sw.switchBracketCount--; + if (sw.switchBracketCount == 0) // if end of switch statement + { + TRswitch(" endsw ", switchDepth); + switchDepth--; // one less switch + sw = swVector.back(); // restore sw struct + swVector.pop_back(); // remove last entry from stack + } + continue; + } + + // look for case or default header + + if (findKeyword(line, i, "case") || findKeyword(line, i, "default")) + { + if (sw.unindentCase) // if unindented last case + { + sw.unindentCase = false; // stop unindenting previous case + sw.unindentDepth--; // reduce depth + } + for (; i < lineLength; i++) // bypass colon + { + if (line[i] == ':') + if ((i + 1 < lineLength) && (line[i + 1] == ':')) + i++; // bypass scope resolution operator + else + break; + } + i++; + for (; i < lineLength; i++) // bypass whitespace + { + if (!(isWhiteSpaceX(line[i]))) + break; + } + if (i < lineLength) // check for bracket + { + if (line[i] == '{') // if bracket found + { + sw.switchBracketCount++; + unindentNextLine = true; // start unindenting on next line + continue; + } + } + lookingForCaseBracket = true; // bracket must be on next line + i--; // need to check for comments + continue; + } + } // end of for loop + + if (isInEventTable) // if need to indent + indentLine(line, 1); // do it + + if (sw.unindentDepth > 0) // if need to unindent + unindentLine(line, sw.unindentDepth); // do it +} + +/** + * indent a line by a given number of tabsets + * by inserting leading whitespace to the line argument. + * + * @param line a pointer to the line to indent. + * @param unindent the number of tabsets to insert. + * @return the number of characters inserted. + */ +int ASEnhancer::indentLine(string &line, const int indent) const +{ + if (line.length() == 0 + && ! emptyLineFill) + return 0; + + size_t charsToInsert; // number of chars to insert + + if (useTabs) // if formatted with tabs + { + charsToInsert = indent; // tabs to insert + line.insert((size_t) 0, charsToInsert, '\t'); // insert the tabs + } + else + { + charsToInsert = indent * indentLength; // compute chars to insert + line.insert((size_t)0, charsToInsert, ' '); // insert the spaces + } + + return charsToInsert; +} + +/** + * unindent a line by a given number of tabsets + * by erasing the leading whitespace from the line argument. + * + * @param line a pointer to the line to unindent. + * @param unindent the number of tabsets to erase. + * @return the number of characters erased. + */ +int ASEnhancer::unindentLine(string &line, const int unindent) const +{ + size_t whitespace = line.find_first_not_of(" \t"); + + if (whitespace == string::npos) // if line is blank + whitespace = line.length(); // must remove padding, if any + + if (whitespace == 0) + return 0; + + size_t charsToErase; // number of chars to erase + + if (useTabs) // if formatted with tabs + { + charsToErase = unindent; // tabs to erase + if (charsToErase <= whitespace) // if there is enough whitespace + line.erase(0, charsToErase); // erase the tabs + else + charsToErase = 0; + } + else + { + charsToErase = unindent * indentLength; // compute chars to erase + if (charsToErase <= whitespace) // if there is enough whitespace + line.erase(0, charsToErase); // erase the spaces + else + charsToErase = 0; + } + + return charsToErase; +} + +/** + * check if a specific line position contains a keyword. + * + * @return true if the word was found. false if the word was not found. + */ +bool ASEnhancer::findKeyword(const string &line, int i, const char *keyword) const +{ + if (line.compare(i, strlen(keyword), keyword) == 0) + { + // check that this is a header and not a part of a longer word + // (e.g. not at its begining, not at its middle...) + + int lineLength = line.length(); + int wordEnd = i + strlen(keyword); + char startCh = keyword[0]; // first char of header + char endCh = 0; // char just after header + char prevCh = 0; // char just before header + + if (wordEnd < lineLength) + { + endCh = line[wordEnd]; + } + if (i > 0) + { + prevCh = line[i-1]; + } + + if (prevCh != 0 + && isLegalNameCharX(startCh) + && isLegalNameCharX(prevCh)) + { + return false; + } + else if (wordEnd >= lineLength + || !isLegalNameCharX(startCh) + || !isLegalNameCharX(endCh)) + { + return true; + } + else + { + return false; + } + } + + return false; +} + +} // end namespace astyle diff --git a/lib/astyle/ASFormatter.cpp b/lib/astyle/ASFormatter.cpp new file mode 100644 index 00000000..eb418760 --- /dev/null +++ b/lib/astyle/ASFormatter.cpp @@ -0,0 +1,2197 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * ASFormatter.cpp + * + * This file is a part of "Artistic Style" - an indentation and + * reformatting tool for C, C++, C# and Java source files. + * http://astyle.sourceforge.net + * + * The "Artistic Style" project, including all files needed to + * compile it, is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later + * version. + * + * This 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this project; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + +#include "astyle.h" + +#include <algorithm> +#include <fstream> +#include <iostream> +#ifdef __VMS +#include <assert> +#else +#include <cassert> +#endif + +// can trace only if NDEBUG is not defined +#ifndef NDEBUG +// #define TRACEunpad +// #define TRACEcomment +// #define TRACEheader +// #define TRACEbracket +// #define TRACEarray +#if defined(TRACEunpad) || defined(TRACEcomment) || defined(TRACEheader) \ +|| defined(TRACEbracket) || defined(TRACEarray) +ofstream *traceOutF; +#define TRACEF +#endif +#endif + +#ifdef TRACEunpad +#define TRunpad(a,b,c) if(b > 0 || c > 0) *traceOutF << outLineNumber << " " << b << a << c << endl +#else +#define TRunpad(a,b,c) ((void)0) +#endif + +#ifdef TRACEcomment +#define TRcomment(a) *traceOutF << outLineNumber << " " << a << endl +#else +#define TRcomment(a) ((void)0) +#endif + +#ifdef TRACEheader +#define TRxtra(a) *traceOutF << outLineNumber << " " << a << endl +#else +#define TRxtra(a) ((void)0) +#endif + +#ifdef TRACEbracket +#define TRbracket(a) *traceOutF << outLineNumber << " " << a << endl +#else +#define TRbracket(a) ((void)0) +#endif + +#ifdef TRACEarray +#define TRarray(a) *traceOutF << outLineNumber << " " << a << endl +#else +#define TRarray(a) ((void)0) +#endif + +#define INIT_CONTAINER(container, value) {if ( (container) != NULL ) delete (container); (container) = (value); } +#define DELETE_CONTAINER(container) {if ( (container) != NULL ) delete (container); } +#define IS_A(a,b) ( ((a) & (b)) == (b)) + +using namespace std; + +namespace astyle +{ +vector<const string*> ASFormatter::headers; +vector<const string*> ASFormatter::nonParenHeaders; +vector<const string*> ASFormatter::preDefinitionHeaders; +vector<const string*> ASFormatter::preCommandHeaders; +vector<const string*> ASFormatter::operators; +vector<const string*> ASFormatter::assignmentOperators; +vector<const string*> ASFormatter::castOperators; + +/** + * Constructor of ASFormatter + */ +ASFormatter::ASFormatter() +{ + preBracketHeaderStack = NULL; + bracketTypeStack = NULL; + parenStack = NULL; + lineCommentNoIndent = false; + sourceIterator = NULL; + bracketFormatMode = NONE_MODE; + shouldPadOperators = false; + shouldPadParensOutside = false; + shouldPadParensInside = false; + shouldUnPadParens = false; + shouldBreakOneLineBlocks = true; + shouldBreakOneLineStatements = true; + shouldConvertTabs = false; + shouldBreakBlocks = false; + shouldBreakClosingHeaderBlocks = false; + shouldBreakClosingHeaderBrackets = false; + shouldBreakElseIfs = false; +#ifdef TRACEF + // create a trace text file + string filename = "tracef.txt"; + char* env = getenv("HOME"); + if (env != NULL) + filename = string(env) + string("/tracef.txt"); + else + { + env = getenv("USERPROFILE"); + if (env != NULL) + filename = string(env) + string("\\My Documents\\tracef.txt"); + else + { + cout << "\nCould not open tracef.txt\n" << endl; + exit(1); + } + } + traceOutF = new ofstream(filename.c_str()); +#endif +} + +/** + * Destructor of ASFormatter + */ +ASFormatter::~ASFormatter() +{ + DELETE_CONTAINER(preBracketHeaderStack); +#ifdef TRACEF + delete traceOutF; +#endif +} + +/** + * initialization of static data of ASFormatter. + */ +void ASFormatter::staticInit() +{ + static int formatterFileType = 9; // initialized with an invalid type + + if (fileType == formatterFileType) // don't build unless necessary + return; + + formatterFileType = fileType; + + headers.clear(); + nonParenHeaders.clear(); + assignmentOperators.clear(); + operators.clear(); + preDefinitionHeaders.clear(); + preCommandHeaders.clear(); + castOperators.clear(); + + ASResource::buildHeaders(headers, fileType); + ASResource::buildNonParenHeaders(nonParenHeaders, fileType); + ASResource::buildAssignmentOperators(assignmentOperators); + ASResource::buildOperators(operators); + ASResource::buildPreDefinitionHeaders(preDefinitionHeaders); + ASResource::buildPreCommandHeaders(preCommandHeaders); + ASResource::buildCastOperators(castOperators); +} + +/** + * initialize the ASFormatter. + * + * init() should be called every time a ASFormatter object is to start + * formatting a NEW source file. + * init() recieves a pointer to a DYNAMICALLY CREATED ASSourceIterator object + * that will be used to iterate through the source code. This object will be + * deleted during the ASFormatter's destruction, and thus should not be + * deleted elsewhere. + * + * @param iter a pointer to the DYNAMICALLY CREATED ASSourceIterator object. + */ +void ASFormatter::init(ASSourceIterator *si) +{ + staticInit(); + + ASBeautifier::init(si); + ASEnhancer::init(ASBeautifier::getIndentLength(), + ASBeautifier::getIndentString(), + ASBeautifier::getCStyle(), + ASBeautifier::getJavaStyle(), + ASBeautifier::getSharpStyle(), + ASBeautifier::getCaseIndent(), + ASBeautifier::getEmptyLineFill()); + sourceIterator = si; + + INIT_CONTAINER(preBracketHeaderStack, new vector<const string*>); + INIT_CONTAINER(bracketTypeStack, new vector<BracketType>); + bracketTypeStack->push_back(NULL_TYPE); + INIT_CONTAINER(parenStack, new vector<int>); + parenStack->push_back(0); + + currentHeader = NULL; + currentLine = string(""); + readyFormattedLine = string(""); + formattedLine = ""; + currentChar = ' '; + previousChar = ' '; + previousCommandChar = ' '; + previousNonWSChar = ' '; + quoteChar = '"'; + charNum = 0; + spacePadNum = 0; + previousReadyFormattedLineLength = string::npos; + templateDepth = 0; + previousBracketType = NULL_TYPE; + previousOperator = NULL; + + isVirgin = true; + isInLineComment = false; + isInComment = false; + isInPreprocessor = false; + doesLineStartComment = false; + isInQuote = false; + isSpecialChar = false; + isNonParenHeader = true; + foundNamespaceHeader = false; + foundClassHeader = false; + foundPreDefinitionHeader = false; + foundPreCommandHeader = false; + foundCastOperator = false; + foundQuestionMark = false; + isInLineBreak = false; + endOfCodeReached = false; + isLineReady = false; + isPreviousBracketBlockRelated = true; + isInPotentialCalculation = false; + shouldReparseCurrentChar = false; + passedSemicolon = false; + passedColon = false; + isInTemplate = false; + isInBlParen = false; + shouldBreakLineAfterComments = false; + isImmediatelyPostComment = false; + isImmediatelyPostLineComment = false; + isImmediatelyPostEmptyBlock = false; + isImmediatelyPostPreprocessor = false; + + isPrependPostBlockEmptyLineRequested = false; + isAppendPostBlockEmptyLineRequested = false; + prependEmptyLine = false; + appendOpeningBracket = false; + + foundClosingHeader = false; + previousReadyFormattedLineLength = 0; + + isImmediatelyPostHeader = false; + isInHeader = false; +#ifdef TRACEF + // fileName will be empty if ASTYLE_LIB is defined + if (fileName.empty()) + *traceOutF << "new file" << endl; + else + *traceOutF << fileName << endl; +#endif +} + +/** + * get the next formatted line. + * + * @return formatted line. + */ + +string ASFormatter::nextLine() +{ + // these are reset with each new line + const string *newHeader; + bool isInVirginLine = isVirgin; + isCharImmediatelyPostComment = false; + isPreviousCharPostComment = false; + isCharImmediatelyPostLineComment = false; + isCharImmediatelyPostOpenBlock = false; + isCharImmediatelyPostCloseBlock = false; + isCharImmediatelyPostTemplate = false; + + while (!isLineReady) + { + if (shouldReparseCurrentChar) + shouldReparseCurrentChar = false; + else if (!getNextChar()) + { + breakLine(); + return beautify(readyFormattedLine); + } + else // stuff to do when reading a new character... + { + // make sure that a virgin '{' at the begining ofthe file will be treated as a block... + if (isInVirginLine && currentChar == '{') + previousCommandChar = '{'; + isPreviousCharPostComment = isCharImmediatelyPostComment; + isCharImmediatelyPostComment = false; + isCharImmediatelyPostTemplate = false; + } + + //if (inLineNumber >= 185) + // int x = 1; + + if (isInLineComment) + { + appendCurrentChar(); + + // explicitely break a line when a line comment's end is found. + if (charNum + 1 == (int) currentLine.length()) + { + isInLineBreak = true; + isInLineComment = false; + isImmediatelyPostLineComment = true; + currentChar = 0; //make sure it is a neutral char. + } + continue; + } + else if (isInComment) + { + if (isSequenceReached("*/")) + { + isInComment = false; + isImmediatelyPostComment = true; + appendSequence(AS_CLOSE_COMMENT); + goForward(1); + } + else + appendCurrentChar(); + + continue; + } + + // not in line comment or comment + + else if (isInQuote) + { + if (isSpecialChar) + { + isSpecialChar = false; + appendCurrentChar(); + } + else if (currentChar == '\\') + { + isSpecialChar = true; + appendCurrentChar(); + } + else if (quoteChar == currentChar) + { + isInQuote = false; + appendCurrentChar(); + } + else + { + appendCurrentChar(); + } + + continue; + } + + // handle white space - needed to simplify the rest. + if (isWhiteSpace(currentChar) || isInPreprocessor) + { + appendCurrentChar(); + continue; + } + + /* not in MIDDLE of quote or comment or white-space of any type ... */ + + if (isSequenceReached("//")) + { + if (currentLine[charNum+2] == '\xf2') // check for windows line marker + isAppendPostBlockEmptyLineRequested = false; + isInLineComment = true; + // do not indent if in column 1 or 2 + if (lineCommentNoIndent == false) + { + if (charNum == 0) + lineCommentNoIndent = true; + else if (charNum == 1 && currentLine[0] == ' ') + lineCommentNoIndent = true; + } + // move comment if spaces were added or deleted + if (lineCommentNoIndent == false && spacePadNum != 0) + adjustComments(); + formattedLineCommentNum = formattedLine.length(); + appendSequence(AS_OPEN_LINE_COMMENT); + goForward(1); + // explicitely break a line when a line comment's end is found. + if (charNum + 1 == (int) currentLine.length()) + { + isInLineBreak = true; + isInLineComment = false; + isImmediatelyPostLineComment = true; + currentChar = 0; //make sure it is a neutral char. + } + continue; + } + else if (isSequenceReached("/*")) + { + isInComment = true; + if (spacePadNum != 0) + adjustComments(); + formattedLineCommentNum = formattedLine.length(); + appendSequence(AS_OPEN_COMMENT); + goForward(1); + continue; + } + else if (currentChar == '"' || currentChar == '\'') + { + isInQuote = true; + quoteChar = currentChar; + appendCurrentChar(); + continue; + } + + /* not in quote or comment or white-space of any type ... */ + + // check if in preprocessor + // ** isInPreprocessor will be automatically reset at the begining + // of a new line in getnextChar() + if (currentChar == '#') + { + isInPreprocessor = true; + appendCurrentChar(); + continue; + } + + /* not in preprocessor ... */ + + if (isImmediatelyPostComment) + { + isImmediatelyPostComment = false; + isCharImmediatelyPostComment = true; + } + + if (isImmediatelyPostLineComment) + { + isImmediatelyPostLineComment = false; + isCharImmediatelyPostLineComment = true; + } + + if (shouldBreakLineAfterComments) + { + shouldBreakLineAfterComments = false; + shouldReparseCurrentChar = true; + breakLine(); + continue; + } + + // reset isImmediatelyPostHeader information + if (isImmediatelyPostHeader) + { + isImmediatelyPostHeader = false; + + // Make sure headers are broken from their succeeding blocks + // (e.g. + // if (isFoo) DoBar(); + // should become + // if (isFoo) + // DoBar; + // ) + // But treat else if() as a special case which should not be broken! + if (shouldBreakOneLineStatements) + { + // if may break 'else if()'s, then simply break the line + + if (shouldBreakElseIfs) + isInLineBreak = true; + } + } + + if (passedSemicolon) // need to break the formattedLine + { + passedSemicolon = false; + if (parenStack->back() == 0 && currentChar != ';') // allow ;; + { + // does a one-line statement have ending comments? + if (IS_A(bracketTypeStack->back(), SINGLE_LINE_TYPE)) + { + size_t blockEnd = currentLine.rfind(AS_CLOSE_BRACKET); + assert(blockEnd != string::npos); + // move ending comments to this formattedLine + if (isBeforeLineEndComment(blockEnd)) + { + size_t commentStart = currentLine.find_first_not_of(" \t", blockEnd + 1); + assert(commentStart != string::npos); + assert((currentLine.compare(commentStart, 2, "//") == 0) + || (currentLine.compare(commentStart, 2, "/*") == 0)); + size_t commentLength = currentLine.length() - commentStart; + int tabCount = getIndentLength(); + appendSpacePad(); + for (int i=1; i<tabCount; i++) + formattedLine.append(1, ' '); + formattedLine.append(currentLine, commentStart, commentLength); + currentLine.erase(commentStart, commentLength); + } + } + shouldReparseCurrentChar = true; + isInLineBreak = true; + continue; + } + } + + if (passedColon) + { + passedColon = false; + if (parenStack->back() == 0 && !isBeforeComment()) + { + shouldReparseCurrentChar = true; + isInLineBreak = true; + continue; + } + } + + // Check if in template declaration, e.g. foo<bar> or foo<bar,fig> + // If so, set isInTemplate to true + if (!isInTemplate && currentChar == '<') + { + int maxTemplateDepth = 0; + templateDepth = 0; + const string *oper; + for (size_t i = charNum; + i < currentLine.length(); + i += (oper ? oper->length() : 1)) + { + oper = ASBeautifier::findHeader(currentLine, i, operators); + + if (oper == &AS_LS) + { + templateDepth++; + maxTemplateDepth++; + } + else if (oper == &AS_GR) + { + templateDepth--; + if (templateDepth == 0) + { + // this is a template! + isInTemplate = true; + templateDepth = maxTemplateDepth; + break; + } + } + else if (oper == &AS_COMMA // comma, e.g. A<int, char> + || oper == &AS_BIT_AND // reference, e.g. A<int&> + || oper == &AS_MULT // pointer, e.g. A<int*> + || oper == &AS_COLON_COLON) // ::, e.g. std::string + { + continue; + } + else if (!isLegalNameChar(currentLine[i]) && !isWhiteSpace(currentLine[i])) + { + // this is not a template -> leave... + isInTemplate = false; + break; + } + } + } + + // handle parenthesies + if (currentChar == '(' || currentChar == '[' || (isInTemplate && currentChar == '<')) + { + parenStack->back()++; + if (currentChar == '[') + isInBlParen = true; + } + else if (currentChar == ')' || currentChar == ']' || (isInTemplate && currentChar == '>')) + { + parenStack->back()--; + if (isInTemplate && currentChar == '>') + { + templateDepth--; + if (templateDepth == 0) + { + isInTemplate = false; + isCharImmediatelyPostTemplate = true; + } + } + + // check if this parenthesis closes a header, e.g. if (...), while (...) + if (isInHeader && parenStack->back() == 0) + { + isInHeader = false; + isImmediatelyPostHeader = true; + } + if (currentChar == ']') + isInBlParen = false; + if (currentChar == ')') + foundCastOperator = false; + } + + // handle brackets + if (currentChar == '{' || currentChar == '}') + { + if (currentChar == '{') + { + BracketType newBracketType = getBracketType(); + foundNamespaceHeader = false; + foundClassHeader = false; + foundPreDefinitionHeader = false; + foundPreCommandHeader = false; + isInPotentialCalculation = false; + + bracketTypeStack->push_back(newBracketType); + preBracketHeaderStack->push_back(currentHeader); + currentHeader = NULL; + + isPreviousBracketBlockRelated = !IS_A(newBracketType, ARRAY_TYPE); + } + + // this must be done before the bracketTypeStack is popped + BracketType bracketType = bracketTypeStack->back(); + bool isOpeningArrayBracket = (IS_A(bracketType, ARRAY_TYPE) + && bracketTypeStack->size() >= 2 + && !IS_A((*bracketTypeStack)[bracketTypeStack->size()-2], ARRAY_TYPE) + ); + + if (currentChar == '}') + { + // if a request has been made to append a post block empty line, + // but the block exists immediately before a closing bracket, + // then there is not need for the post block empty line. + // + isAppendPostBlockEmptyLineRequested = false; + + if (!bracketTypeStack->empty()) + { + previousBracketType = bracketTypeStack->back(); + bracketTypeStack->pop_back(); + isPreviousBracketBlockRelated = !IS_A(bracketType, ARRAY_TYPE); + } + + if (!preBracketHeaderStack->empty()) + { + currentHeader = preBracketHeaderStack->back(); + preBracketHeaderStack->pop_back(); + } + else + currentHeader = NULL; + } + + // format brackets + if (IS_A(bracketType, ARRAY_TYPE)) + formatArrayBrackets(bracketType, isOpeningArrayBracket); + else + formatBrackets(bracketType); + continue; + } + + if (((previousCommandChar == '{' && isPreviousBracketBlockRelated) + || (previousCommandChar == '}' + && bracketFormatMode != NONE_MODE + && !isImmediatelyPostEmptyBlock + && isPreviousBracketBlockRelated + && !isPreviousCharPostComment // Fixes wrongly appended newlines after '}' immediately after comments + && peekNextChar() != ' ' + && !IS_A(previousBracketType, DEFINITION_TYPE) + && !(ASBeautifier::isJavaStyle && currentChar == ')')) + && !IS_A(bracketTypeStack->back(), DEFINITION_TYPE)) + && (shouldBreakOneLineBlocks + || !IS_A(bracketTypeStack->back(), SINGLE_LINE_TYPE))) + { + isCharImmediatelyPostOpenBlock = (previousCommandChar == '{'); + isCharImmediatelyPostCloseBlock = (previousCommandChar == '}'); + + //if (bracketFormatMode != NONE_MODE) + //{ + previousCommandChar = ' '; + isInLineBreak = true; + //} + } + + // reset block handling flags + isImmediatelyPostEmptyBlock = false; + + // look for headers + if (!isInTemplate) + { + if ((newHeader = findHeader(headers)) != NULL) + { + foundClosingHeader = false; + const string *previousHeader; + + // recognize closing headers of do..while, if..else, try..catch..finally + if ((newHeader == &AS_ELSE && currentHeader == &AS_IF) + || (newHeader == &AS_WHILE && currentHeader == &AS_DO) + || (newHeader == &AS_CATCH && currentHeader == &AS_TRY) + || (newHeader == &AS_CATCH && currentHeader == &AS_CATCH) + || (newHeader == &AS_FINALLY && currentHeader == &AS_TRY) + || (newHeader == &AS_FINALLY && currentHeader == &AS_CATCH)) + foundClosingHeader = true; + + previousHeader = currentHeader; + currentHeader = newHeader; + + // If in ATTACH or LINUX bracket modes, attach closing headers (e.g. 'else', 'catch') + // to their preceding bracket, + // But do not perform the attachment if the shouldBreakClosingHeaderBrackets is set! + if (!shouldBreakClosingHeaderBrackets + && foundClosingHeader + && (bracketFormatMode == ATTACH_MODE || bracketFormatMode == BDAC_MODE) + && (shouldBreakOneLineBlocks || !IS_A(previousBracketType, SINGLE_LINE_TYPE)) + && previousNonWSChar == '}') + { + spacePadNum = 0; // don't count as padding + + size_t firstChar = formattedLine.find_first_not_of(" \t"); + if (firstChar != string::npos) // if a blank line does not preceed this + { + isInLineBreak = false; + appendSpacePad(); + } + + if (shouldBreakBlocks) + isAppendPostBlockEmptyLineRequested = false; + } + + // If NONE bracket mode, leave closing headers as they are (e.g. 'else', 'catch') + if (foundClosingHeader && bracketFormatMode == NONE_MODE && previousCommandChar == '}') + { + if (lineBeginsWith('}')) // is closing bracket broken? + { + isInLineBreak = false; + appendSpacePad(); + } + + if (shouldBreakBlocks) + isAppendPostBlockEmptyLineRequested = false; + } + + if (foundClosingHeader && bracketFormatMode == BREAK_MODE && previousCommandChar == '}') + breakLine(); + + //Check if a template definition as been reached, e.g. template<class A> + //if (newHeader == &AS_TEMPLATE) + //{ + // isInTemplate = true; + //} + + // check if the found header is non-paren header + isNonParenHeader = (find(nonParenHeaders.begin(), nonParenHeaders.end(), + newHeader) != nonParenHeaders.end()); + + appendSequence(*currentHeader); + goForward(currentHeader->length() - 1); + // if a paren-header is found add a space after it, if needed + // this checks currentLine, appendSpacePad() checks formattedLine + if (!isNonParenHeader && charNum < (int) currentLine.length() && !isWhiteSpace(currentLine[charNum+1])) + appendSpacePad(); + + // Signal that a header has been reached + // *** But treat a closing while() (as in do...while) + // as if it where NOT a header since a closing while() + // should never have a block after it! + if (!(foundClosingHeader && currentHeader == &AS_WHILE)) + { + isInHeader = true; + if (isNonParenHeader) + { + isImmediatelyPostHeader = true; + isInHeader = false; + } + } + + if (currentHeader == &AS_IF && previousHeader == &AS_ELSE) + isInLineBreak = false; + + if (shouldBreakBlocks) + { + if (previousHeader == NULL + && !foundClosingHeader + && !isCharImmediatelyPostOpenBlock) + { + isPrependPostBlockEmptyLineRequested = true; + } + + if (currentHeader == &AS_ELSE + || currentHeader == &AS_CATCH + || currentHeader == &AS_FINALLY + || foundClosingHeader) + { + isPrependPostBlockEmptyLineRequested = false; + } + + if (shouldBreakClosingHeaderBlocks + && isCharImmediatelyPostCloseBlock) + { + isPrependPostBlockEmptyLineRequested = true; + } + + } + + continue; + } + else if ((newHeader = findHeader(preDefinitionHeaders)) != NULL + && parenStack->back() == 0) + { + if (newHeader == &AS_NAMESPACE) + foundNamespaceHeader = true; + if (newHeader == &AS_CLASS) + foundClassHeader = true; + foundPreDefinitionHeader = true; + appendSequence(*newHeader); + goForward(newHeader->length() - 1); + + if (shouldBreakBlocks) + isPrependPostBlockEmptyLineRequested = true; + + continue; + } + else if ((newHeader = findHeader(preCommandHeaders)) != NULL) + { + if (ASBeautifier::isJavaStyle + || (*newHeader == AS_CONST && previousCommandChar == ')') // 'const' member functions is a command bracket + || *newHeader == AS_EXTERN) + foundPreCommandHeader = true; + appendSequence(*newHeader); + goForward(newHeader->length() - 1); + + continue; + } + else if ((newHeader = findHeader(castOperators)) != NULL) + { + foundCastOperator = true; + appendSequence(*newHeader); + goForward(newHeader->length() - 1); + + continue; + } + + } + + if (isInLineBreak) // OK to break line here + breakLine(); + + if (previousNonWSChar == '}' || currentChar == ';') + { + if (shouldBreakOneLineStatements && currentChar == ';' + && (shouldBreakOneLineBlocks || !IS_A(bracketTypeStack->back(), SINGLE_LINE_TYPE)) + //&& (! bracketFormatMode == NONE_MODE) + ) + { + passedSemicolon = true; + } + + if (shouldBreakBlocks && currentHeader != NULL && parenStack->back() == 0) + { + isAppendPostBlockEmptyLineRequested = true; + } + + if (currentChar != ';') + currentHeader = NULL; + + foundQuestionMark = false; + foundNamespaceHeader = false; + foundClassHeader = false; + foundPreDefinitionHeader = false; + foundPreCommandHeader = false; + foundCastOperator = false; + isInPotentialCalculation = false; + isNonInStatementArray = false; + } + + if (currentChar == ':' + && shouldBreakOneLineStatements + && !foundQuestionMark // not in a ... ? ... : ... sequence + && !foundPreDefinitionHeader // not in a definition block (e.g. class foo : public bar + && previousCommandChar != ')' // not immediately after closing paren of a method header, e.g. ASFormatter::ASFormatter(...) : ASBeautifier(...) + && previousChar != ':' // not part of '::' + && peekNextChar() != ':') // not part of '::' + { + passedColon = true; + if (shouldBreakBlocks) + isPrependPostBlockEmptyLineRequested = true; + } + + if (currentChar == '?') + foundQuestionMark = true; + + // determine if this is a potential calculation + newHeader = findHeader(operators); + + if (newHeader != NULL) + { + if (!isInPotentialCalculation) + { + if (find(assignmentOperators.begin(), assignmentOperators.end(), newHeader) + != assignmentOperators.end()) + { + char peekedChar = peekNextChar(); + isInPotentialCalculation = (newHeader != &AS_RETURN + && !(newHeader == &AS_EQUAL && peekedChar == '*') + && !(newHeader == &AS_EQUAL && peekedChar == '&')); + } + } + } + else + { + // the following are not calculations + if (currentLine.compare(charNum, 3, "new") == 0 && !isLegalNameChar(currentLine[charNum+3])) + isInPotentialCalculation = false; + } + + if (shouldPadOperators && newHeader != NULL) + { + padOperators(newHeader); + continue; + } + + if ((shouldPadParensOutside || shouldPadParensInside || shouldUnPadParens) + && (currentChar == '(' || currentChar == ')')) + { + padParens(); + continue; + } + + appendCurrentChar(); + } // end of while loop * end of while loop * end of while loop * end of while loop + + // return a beautified (i.e. correctly indented) line. + + string beautifiedLine; + size_t readyFormattedLineLength = trim(readyFormattedLine).length(); + + if (prependEmptyLine // prepend a blank line before this formatted line + && readyFormattedLineLength > 0 + && previousReadyFormattedLineLength > 0) + { + isLineReady = true; // signal that a readyFormattedLine is still waiting + beautifiedLine = beautify(""); + previousReadyFormattedLineLength = 0; + } + else // format the current formatted line + { + isLineReady = false; + beautifiedLine = beautify(readyFormattedLine); + previousReadyFormattedLineLength = readyFormattedLineLength; + lineCommentNoBeautify = lineCommentNoIndent; + lineCommentNoIndent = false; + if (appendOpeningBracket) // insert bracket after this formatted line + { + appendOpeningBracket = false; + isLineReady = true; // signal that a readyFormattedLine is still waiting + readyFormattedLine = "{"; + isPrependPostBlockEmptyLineRequested = false; // next line should not be empty + } + } + + prependEmptyLine = false; + enhance(beautifiedLine); // call the enhancer function + return beautifiedLine; +} + + +/** +* check if there are any indented lines ready to be read by nextLine() +* +* @return are there any indented lines ready? +*/ +bool ASFormatter::hasMoreLines() const +{ + return !endOfCodeReached; +} + +/** + * set the bracket formatting mode. + * options: + * astyle::NONE_MODE no formatting of brackets. + * astyle::ATTACH_MODE Java, K&R style bracket placement. + * astyle::BREAK_MODE ANSI C/C++ style bracket placement. + * + * @param mode the bracket formatting mode. + */ +void ASFormatter::setBracketFormatMode(BracketMode mode) +{ + bracketFormatMode = mode; +} + +/** + * set closing header bracket breaking mode + * options: + * true brackets just before closing headers (e.g. 'else', 'catch') + * will be broken, even if standard brackets are attached. + * false closing header brackets will be treated as standard brackets. + * + * @param state the closing header bracket breaking mode. + */ +void ASFormatter::setBreakClosingHeaderBracketsMode(bool state) +{ + shouldBreakClosingHeaderBrackets = state; +} + +/** + * set 'else if()' breaking mode + * options: + * true 'else' headers will be broken from their succeeding 'if' headers. + * false 'else' headers will be attached to their succeeding 'if' headers. + * + * @param state the 'else if()' breaking mode. + */ +void ASFormatter::setBreakElseIfsMode(bool state) +{ + shouldBreakElseIfs = state; +} + +/** + * set operator padding mode. + * options: + * true statement operators will be padded with spaces around them. + * false statement operators will not be padded. + * + * @param state the padding mode. + */ +void ASFormatter::setOperatorPaddingMode(bool state) +{ + shouldPadOperators = state; +} + +/** +* set parenthesis outside padding mode. +* options: +* true statement parenthesiss will be padded with spaces around them. +* false statement parenthesiss will not be padded. +* +* @param state the padding mode. +*/ +void ASFormatter::setParensOutsidePaddingMode(bool state) +{ + shouldPadParensOutside = state; +} + +/** +* set parenthesis inside padding mode. +* options: +* true statement parenthesis will be padded with spaces around them. +* false statement parenthesis will not be padded. +* +* @param state the padding mode. +*/ +void ASFormatter::setParensInsidePaddingMode(bool state) +{ + shouldPadParensInside = state; +} + +/** +* set parenthesis unpadding mode. +* options: +* true statement parenthesis will be unpadded with spaces removed around them. +* false statement parenthesis will not be unpadded. +* +* @param state the padding mode. +*/ +void ASFormatter::setParensUnPaddingMode(bool state) +{ + shouldUnPadParens = state; +} + +/** + * set option to break/not break one-line blocks + * + * @param state true = break, false = don't break. + */ +void ASFormatter::setBreakOneLineBlocksMode(bool state) +{ + shouldBreakOneLineBlocks = state; +} + +/** + * set option to break/not break lines consisting of multiple statements. + * + * @param state true = break, false = don't break. + */ +void ASFormatter::setSingleStatementsMode(bool state) +{ + shouldBreakOneLineStatements = state; +} + +/** + * set option to convert tabs to spaces. + * + * @param state true = convert, false = don't convert. + */ +void ASFormatter::setTabSpaceConversionMode(bool state) +{ + shouldConvertTabs = state; +} + + +/** + * set option to break unrelated blocks of code with empty lines. + * + * @param state true = convert, false = don't convert. + */ +void ASFormatter::setBreakBlocksMode(bool state) +{ + shouldBreakBlocks = state; +} + +/** + * set option to break closing header blocks of code (such as 'else', 'catch', ...) with empty lines. + * + * @param state true = convert, false = don't convert. + */ +void ASFormatter::setBreakClosingHeaderBlocksMode(bool state) +{ + shouldBreakClosingHeaderBlocks = state; +} + +/** + * jump over several characters. + * + * @param i the number of characters to jump over. + */ +void ASFormatter::goForward(int i) +{ + while (--i >= 0) + getNextChar(); +} + +/** +* peek at the next unread character. +* +* @return the next unread character. +*/ +char ASFormatter::peekNextChar() const +{ + char ch = ' '; + size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1); + + if (peekNum == string::npos) + return ch; + + ch = currentLine[peekNum]; + +// if (shouldConvertTabs && ch == '\t') +// ch = ' '; + + return ch; +} + +/** +* check if current placement is before a comment or line-comment +* +* @return is before a comment or line-comment. +*/ +bool ASFormatter::isBeforeComment() const +{ + bool foundComment = false; + size_t peekNum = currentLine.find_first_not_of(" \t", charNum + 1); + + if (peekNum == string::npos) + return foundComment; + + foundComment = (currentLine.compare(peekNum, 2, "/*") == 0 + || currentLine.compare(peekNum, 2, "//") == 0); + + return foundComment; +} + +/** +* check if current placement is before a comment or line-comment +* if a block comment it must be at the end of the line +* +* @return is before a comment or line-comment. +*/ +bool ASFormatter::isBeforeLineEndComment(int startPos) const +{ + bool foundLineEndComment = false; + size_t peekNum = currentLine.find_first_not_of(" \t", startPos + 1); + + if (peekNum != string::npos) + { + if (currentLine.compare(peekNum, 2, "//") == 0) + foundLineEndComment = true; + else if (currentLine.compare(peekNum, 2, "/*") == 0) + { + // comment must be closed on this line with nothing after it + size_t endNum = currentLine.find("*/", peekNum + 2); + if (endNum != string::npos) + if (currentLine.find_first_not_of(" \t", endNum + 2) == string::npos) + foundLineEndComment = true; + } + } + return foundLineEndComment; +} + + +/** +* get the next character, increasing the current placement in the process. +* the new character is inserted into the variable currentChar. +* +* @return whether succeded to recieve the new character. +*/ +bool ASFormatter::getNextChar() +{ + isInLineBreak = false; + previousChar = currentChar; + + if (!isWhiteSpace(currentChar)) + { + previousNonWSChar = currentChar; + if (!isInComment && !isInLineComment && !isInQuote + && !isImmediatelyPostComment + && !isImmediatelyPostLineComment + && !isSequenceReached("/*") + && !isSequenceReached("//")) + previousCommandChar = previousNonWSChar; + } + + int currentLineLength = currentLine.length(); + + if (charNum + 1 < currentLineLength + && (!isWhiteSpace(peekNextChar()) || isInComment || isInLineComment)) + { + currentChar = currentLine[++charNum]; + + if (shouldConvertTabs && currentChar == '\t') + currentChar = ' '; + + return true; + } + else // end of line has been reached + { + if (sourceIterator->hasMoreLines()) + { + currentLine = sourceIterator->nextLine(); + spacePadNum = 0; + inLineNumber++; + + if (currentLine.length() == 0) + { + currentLine = string(" "); // a null is inserted if this is not done + } + + // unless reading in the first line of the file, + // break a new line. + if (!isVirgin) + isInLineBreak = true; + else + isVirgin = false; + + if (isInLineComment) + isImmediatelyPostLineComment = true; + isInLineComment = false; + + // check if is in preprocessor before line trimming + isImmediatelyPostPreprocessor = isInPreprocessor; + if (previousNonWSChar != '\\') + isInPreprocessor = false; + + trimNewLine(); + currentChar = currentLine[charNum]; + + if (shouldConvertTabs && currentChar == '\t') + currentChar = ' '; + + return true; + } + else + { + endOfCodeReached = true; + return false; + } + } +} + +/** +* jump over the leading white space in the current line, +* IF the line does not begin a comment or is in a preprocessor definition. +*/ +void ASFormatter::trimNewLine() +{ + int len = currentLine.length(); + charNum = 0; + + if (isInComment || isInPreprocessor) + return; + + while (isWhiteSpace(currentLine[charNum]) && charNum + 1 < len) + ++charNum; + + doesLineStartComment = false; + if (isSequenceReached("/*")) + { + charNum = 0; + doesLineStartComment = true; + } +} + +/** + * append a character to the current formatted line. + * Unless disabled (via canBreakLine == false), first check if a + * line-break has been registered, and if so break the + * formatted line, and only then append the character into + * the next formatted line. + * + * @param ch the character to append. + * @param canBreakLine if true, a registered line-break + */ +void ASFormatter::appendChar(char ch, bool canBreakLine) +{ + if (canBreakLine && isInLineBreak) + breakLine(); + formattedLine.append(1, ch); +} + +/** + * append a string sequence to the current formatted line. + * Unless disabled (via canBreakLine == false), first check if a + * line-break has been registered, and if so break the + * formatted line, and only then append the sequence into + * the next formatted line. + * + * @param sequence the sequence to append. + * @param canBreakLine if true, a registered line-break + */ +void ASFormatter::appendSequence(const string &sequence, bool canBreakLine) +{ + if (canBreakLine && isInLineBreak) + breakLine(); + formattedLine.append(sequence); +} + +/** + * append a space to the current formattedline, UNLESS the + * last character is already a white-space character. + */ +void ASFormatter::appendSpacePad() +{ + int len = formattedLine.length(); + if (len > 0 && !isWhiteSpace(formattedLine[len-1])) + { + formattedLine.append(1, ' '); + spacePadNum++; + } +} + +/** + * append a space to the current formattedline, UNLESS the + * next character is already a white-space character. + */ +void ASFormatter::appendSpaceAfter() +{ + int len = currentLine.length(); + if (charNum + 1 < len && !isWhiteSpace(currentLine[charNum+1])) + { + formattedLine.append(1, ' '); + spacePadNum++; + } +} + +/** + * register a line break for the formatted line. + */ +void ASFormatter::breakLine() +{ + isLineReady = true; + isInLineBreak = false; + spacePadNum = 0; + formattedLineCommentNum = string::npos; + + // queue an empty line prepend request if one exists + prependEmptyLine = isPrependPostBlockEmptyLineRequested; + + readyFormattedLine = formattedLine; + if (isAppendPostBlockEmptyLineRequested) + { + isAppendPostBlockEmptyLineRequested = false; + isPrependPostBlockEmptyLineRequested = true; + } + else + { + isPrependPostBlockEmptyLineRequested = false; + } + + formattedLine = ""; +} + +/** + * check if the currently reached open-bracket (i.e. '{') + * opens a: + * - a definition type block (such as a class or namespace), + * - a command block (such as a method block) + * - a static array + * this method takes for granted that the current character + * is an opening bracket. + * + * @return the type of the opened block. + */ +BracketType ASFormatter::getBracketType() const +{ + BracketType returnVal; + + if (foundPreDefinitionHeader) + { + returnVal = DEFINITION_TYPE; + if (foundNamespaceHeader) + returnVal = (BracketType)(returnVal | NAMESPACE_TYPE); + else if (foundClassHeader) + returnVal = (BracketType)(returnVal | CLASS_TYPE); + } + else + { + bool isCommandType = false; + + if (previousNonWSChar != '=') + isCommandType = (foundPreCommandHeader + || (currentHeader != NULL && isNonParenHeader) + || (previousCommandChar == ')') + || (previousCommandChar == ':' && !foundQuestionMark) + || (previousCommandChar == ';') + || ((previousCommandChar == '{' || previousCommandChar == '}') + && isPreviousBracketBlockRelated)); + + returnVal = (isCommandType ? COMMAND_TYPE : ARRAY_TYPE); + } + + if (isOneLineBlockReached()) + returnVal = (BracketType)(returnVal | SINGLE_LINE_TYPE); + + TRbracket(returnVal); + return returnVal; +} + +/** + * check if the currently reached '*' or '&' character is + * a pointer-or-reference symbol, or another operator. + * this method takes for granted that the current character + * is either a '*' or '&'. + * + * @return whether current character is a reference-or-pointer + */ +bool ASFormatter::isPointerOrReference() const +{ + bool isPR; + isPR = (!isInPotentialCalculation + || IS_A(bracketTypeStack->back(), DEFINITION_TYPE) + || (!isLegalNameChar(previousNonWSChar) + && previousNonWSChar != ')' + && previousNonWSChar != ']') + ); + + if (!isPR) + { + char nextChar = peekNextChar(); + isPR |= (!isWhiteSpace(nextChar) + && nextChar != '-' + && nextChar != '(' + && nextChar != '[' + && !isLegalNameChar(nextChar)); + } + + return isPR; +} + + +/** + * check if the currently reached '-' character is + * a unary minus + * this method takes for granted that the current character + * is a '-'. + * + * @return whether the current '-' is a unary minus. + */ +bool ASFormatter::isUnaryMinus() const +{ + return ((previousOperator == &AS_RETURN || !isalnum(previousCommandChar)) + && previousCommandChar != '.' + && previousCommandChar != ')' + && previousCommandChar != ']'); +} + + +/** + * check if the currently reached '-' or '+' character is + * part of an exponent, i.e. 0.2E-5. + * this method takes for granted that the current character + * is a '-' or '+'. + * + * @return whether the current '-' is in an exponent. + */ +bool ASFormatter::isInExponent() const +{ + int formattedLineLength = formattedLine.length(); + if (formattedLineLength >= 2) + { + char prevPrevFormattedChar = formattedLine[formattedLineLength - 2]; + char prevFormattedChar = formattedLine[formattedLineLength - 1]; + + return ((prevFormattedChar == 'e' || prevFormattedChar == 'E') + && (prevPrevFormattedChar == '.' || isdigit(prevPrevFormattedChar))); + } + else + return false; +} + +/** + * check if a one-line bracket has been reached, + * i.e. if the currently reached '{' character is closed + * with a complimentry '}' elsewhere on the current line, + *. + * @return has a one-line bracket been reached? + */ +bool ASFormatter::isOneLineBlockReached() const +{ + bool isInComment = false; + bool isInQuote = false; + int bracketCount = 1; + int currentLineLength = currentLine.length(); + char quoteChar = ' '; + + for (int i = charNum + 1; i < currentLineLength; ++i) + { + char ch = currentLine[i]; + + if (isInComment) + { + if (currentLine.compare(i, 2, "*/") == 0) + { + isInComment = false; + ++i; + } + continue; + } + + if (ch == '\\') + { + ++i; + continue; + } + + if (isInQuote) + { + if (ch == quoteChar) + isInQuote = false; + continue; + } + + if (ch == '"' || ch == '\'') + { + isInQuote = true; + quoteChar = ch; + continue; + } + + if (currentLine.compare(i, 2, "//") == 0) + break; + + if (currentLine.compare(i, 2, "/*") == 0) + { + isInComment = true; + ++i; + continue; + } + + if (ch == '{') + ++bracketCount; + else if (ch == '}') + --bracketCount; + + if (bracketCount == 0) + return true; + } + + return false; +} + +/** + * check if one of a set of headers has been reached in the + * current position of the current line. + * + * @return a pointer to the found header. Or a NULL if no header has been reached. + * @param headers a vector of headers. + * @param checkBoundry + */ +const string *ASFormatter::findHeader(const vector<const string*> &headers, bool checkBoundry) +{ + return ASBeautifier::findHeader(currentLine, charNum, headers, checkBoundry); +} + +/** + * check if a line begins with the specified character + * i.e. if the current line begins with a open bracket. + * + * @return true or false + */ +bool ASFormatter::lineBeginsWith(char charToCheck) const +{ + bool beginsWith = false; + size_t i = currentLine.find_first_not_of(" \t"); + + if (i != string::npos) + if (currentLine[i] == charToCheck && (int) i == charNum) + beginsWith = true; + + return beginsWith; +} + +/** + * adjust comment position because of adding or deleting spaces + * the spaces are added or deleted to formattedLine + * spacePadNum contains the adjustment + */ +void ASFormatter::adjustComments(void) +{ + assert(spacePadNum != 0); + assert(currentLine.compare(charNum, 2, "//") == 0 + || currentLine.compare(charNum, 2, "/*") == 0); + + + // block comment must be closed on this line with nothing after it + if (currentLine.compare(charNum, 2, "/*") == 0) + { + size_t endNum = currentLine.find("*/", charNum + 2); + if (endNum == string::npos) + return; + if (currentLine.find_first_not_of(" \t", endNum + 2) != string::npos) + return; + } + + size_t len = formattedLine.length(); + // if spaces were removed, need to add spaces before the comment + if (spacePadNum < 0) + { + int adjust = -spacePadNum; // make the number positive + if (formattedLine[len-1] != '\t') // don't adjust if a tab + formattedLine.append(adjust, ' '); +// else // comment out to avoid compiler warning +// adjust = 0; +// TRcomment(adjust); // trace macro + } + // if spaces were added, need to delete spaces before the comment, if possible + else if (spacePadNum > 0) + { + int adjust = spacePadNum; + if (formattedLine.find_last_not_of(' ') < len - adjust - 1 + && formattedLine[len-1] != '\t') // don't adjust a tab + formattedLine.resize(len - adjust); + // the following are commented out to avoid a Borland compiler warning + //else + // adjust = 0; + TRcomment(-adjust); // trace macro + } +} + +/** + * append the current bracket inside the end of line comments + * currentChar contains the bracket, it will be appended to formattedLine + * formattedLineCommentNum is the comment location on formattedLine + */ +void ASFormatter::appendCharInsideComments(void) +{ + if (formattedLineCommentNum == string::npos // does the comment start on the previous line? + || isBeforeComment()) // does a comment follow on this line? + { + appendCurrentChar(true); // don't attach + return; + } + assert(formattedLine.compare(formattedLineCommentNum, 2, "//") == 0 + || formattedLine.compare(formattedLineCommentNum, 2, "/*") == 0); + + // find the previous non space char + size_t end = formattedLineCommentNum; + size_t beg = formattedLine.find_last_not_of(" \t", end-1); + if (beg == string::npos) // is the previous line comment only? + { + appendCurrentChar(true); // don't attach + return; + } + beg++; + + // insert the bracket + if (end - beg < 3) // is there room to insert? + formattedLine.insert(beg, 3-end+beg, ' '); + if (formattedLine[beg] == '\t') // don't pad with a tab + formattedLine.insert(beg, 1, ' '); + formattedLine[beg+1] = currentChar; +} + +/** + * add or remove space padding to operators + * currentChar contains the paren + * the operators and necessary padding will be appended to formattedLine + * the calling function should have a continue statement after calling this method + * + * @param *newOperator the operator to be padded + */ +void ASFormatter::padOperators(const string *newOperator) +{ + assert (shouldPadOperators); + assert(newOperator != NULL); + + bool shouldPad = (newOperator != &AS_COLON_COLON + && newOperator != &AS_PAREN_PAREN + && newOperator != &AS_BLPAREN_BLPAREN + && newOperator != &AS_PLUS_PLUS + && newOperator != &AS_MINUS_MINUS + && newOperator != &AS_NOT + && newOperator != &AS_BIT_NOT + && newOperator != &AS_ARROW + && newOperator != &AS_OPERATOR + && newOperator != &AS_RETURN + && !(newOperator == &AS_MINUS && isInExponent()) + && !(newOperator == &AS_MINUS // check for negative number + && (previousNonWSChar == '(' + || previousNonWSChar == '=' + || previousNonWSChar == ',')) + && !(newOperator == &AS_PLUS && isInExponent()) + && previousOperator != &AS_OPERATOR + && !((newOperator == &AS_MULT || newOperator == &AS_BIT_AND) + && isPointerOrReference()) + && !(newOperator == &AS_MULT + && (previousNonWSChar == '.' + || previousNonWSChar == '>')) // check for -> + && !((isInTemplate || isCharImmediatelyPostTemplate) + && (newOperator == &AS_LS || newOperator == &AS_GR)) + ); + // pad before operator + if (shouldPad + && !isInBlParen + && !(newOperator == &AS_COLON && !foundQuestionMark) + && newOperator != &AS_SEMICOLON + && newOperator != &AS_COMMA) + appendSpacePad(); + appendSequence(*newOperator); + goForward(newOperator->length() - 1); + + // since this block handles '()' and '[]', + // the parenStack must be updated here accordingly! + if (newOperator == &AS_PAREN_PAREN + || newOperator == &AS_BLPAREN_BLPAREN) + parenStack->back()--; + + currentChar = (*newOperator)[newOperator->length() - 1]; + // pad after operator + // but do not pad after a '-' that is a unary-minus. + if (shouldPad + && !isInBlParen + && !isBeforeComment() + && !(newOperator == &AS_MINUS && isUnaryMinus()) + && !(currentLine.compare(charNum + 1, 1, ";") == 0) + && !(currentLine.compare(charNum + 1, 2, "::") == 0)) + appendSpaceAfter(); + + previousOperator = newOperator; + return; +} + +/** + * add or remove space padding to parens + * currentChar contains the paren + * the parens and necessary padding will be appended to formattedLine + * the calling function should have a continue statement after calling this method + */ +void ASFormatter::padParens(void) +{ + assert(shouldPadParensOutside || shouldPadParensInside || shouldUnPadParens); + assert (currentChar == '(' || currentChar == ')'); + + if (currentChar == '(') + { + int spacesOutsideToDelete = formattedLine.length() - 1; + int spacesInsideToDelete = 0; + + // compute spaces outside the opening paren to delete + if (shouldUnPadParens) + { + char lastChar = ' '; + bool prevIsParenHeader = false; + size_t i = formattedLine.find_last_not_of(" \t"); + if (i != string::npos) + { + size_t end = i; + spacesOutsideToDelete -= i; + lastChar = formattedLine[i]; + // was last word a paren header? + int start; // start of the previous word + for (start = i; start > 0; start--) + { + if (isLegalNameChar(formattedLine[start]) || formattedLine[start] == '*') + continue; + start++; + break; + } + string prevWord = formattedLine.substr(start, end-start+1); + // if previous word is a header, it will be a paren header + const string *prevWordH = ASBeautifier::findHeader(formattedLine, start, headers); + if (prevWordH != NULL) + { + prevIsParenHeader = true; + TRxtra(*prevWordH); // trace macro + } + else if (prevWord == "return" // don't unpad return statements + || prevWord == "*") // don't unpad multiply or pointer + { + prevIsParenHeader = true; + TRxtra(prevWord); // trace macro + } + // don't unpad variables + else if (prevWord == "bool" + || prevWord == "int" + || prevWord == "void" + || prevWord == "void*" + || (prevWord.length() >= 6 // check end of word for _t + && prevWord.compare(prevWord.length()-2, 2, "_t") == 0) + || prevWord == "BOOL" + || prevWord == "DWORD" + || prevWord == "HWND" + || prevWord == "INT" + || prevWord == "LPSTR" + || prevWord == "VOID" + || prevWord == "LPVOID" + ) + { + prevIsParenHeader = true; + TRxtra(prevWord); // trace macro + } + } + // do not unpad operators, but leave them if already padded + if (shouldPadParensOutside || prevIsParenHeader) + spacesOutsideToDelete--; + else if (lastChar == '|' // check for || + || lastChar == '&' // check for && + || lastChar == ',' + || (lastChar == '>' && !foundCastOperator) + || lastChar == '<' + || lastChar == '?' + || lastChar == ':' + || lastChar == ';' + || lastChar == '=' + || lastChar == '+' + || lastChar == '-' + || (lastChar == '*' && isInPotentialCalculation) + || lastChar == '/' + || lastChar == '%') + spacesOutsideToDelete--; + + if (spacesOutsideToDelete > 0) + { + formattedLine.erase(i + 1, spacesOutsideToDelete); + spacePadNum -= spacesOutsideToDelete; + } + } + + // pad open paren outside + char peekedCharOutside = peekNextChar(); + if (shouldPadParensOutside) + if (!(currentChar == '(' && peekedCharOutside == ')')) + appendSpacePad(); + + appendCurrentChar(); + + // unpad open paren inside + if (shouldUnPadParens) + { + size_t j = currentLine.find_first_not_of(" \t", charNum + 1); + if (j != string::npos) + spacesInsideToDelete = j - charNum - 1; + if (shouldPadParensInside) + spacesInsideToDelete--; + if (spacesInsideToDelete > 0) + { + currentLine.erase(charNum + 1, spacesInsideToDelete); + spacePadNum -= spacesInsideToDelete; + } + } + + // pad open paren inside + char peekedCharInside = peekNextChar(); + if (shouldPadParensInside) + if (!(currentChar == '(' && peekedCharInside == ')')) + appendSpaceAfter(); + + TRunpad('(', spacesOutsideToDelete, spacesInsideToDelete); // trace macro + } + else if (currentChar == ')' /*|| currentChar == ']'*/) + { + int spacesOutsideToDelete = 0; + int spacesInsideToDelete = formattedLine.length(); + + // unpad close paren inside + if (shouldUnPadParens) + { + size_t i = formattedLine.find_last_not_of(" \t"); + if (i != string::npos) + spacesInsideToDelete = formattedLine.length() - 1 - i; + if (shouldPadParensInside) + spacesInsideToDelete--; + if (spacesInsideToDelete > 0) + { + formattedLine.erase(i + 1, spacesInsideToDelete); + spacePadNum -= spacesInsideToDelete; + } + } + + // pad close paren inside + if (shouldPadParensInside) + if (!(previousChar == '(' && currentChar == ')')) + appendSpacePad(); + + appendCurrentChar(); + + // unpad close paren outside + if (shouldUnPadParens) + { + // may have end of line comments + size_t j = currentLine.find_first_not_of(" \t", charNum + 1); + if (j != string::npos) + if (currentLine[j] == '[' || currentLine[j] == ']') + spacesOutsideToDelete = j - charNum - 1; + if (shouldPadParensOutside) + spacesOutsideToDelete--; +// spacesOutsideToDelete--; // always leave 1 space + + if (spacesOutsideToDelete > 0) + { + currentLine.erase(charNum + 1, spacesOutsideToDelete); + spacePadNum -= spacesOutsideToDelete; + } + } + + // pad close paren outside + char peekedCharOutside = peekNextChar(); + if (shouldPadParensOutside) + if (peekedCharOutside != ';' + && peekedCharOutside != ',' + && peekedCharOutside != '.' + && peekedCharOutside != '-') // check for -> +// && !(currentChar == ']' && peekedCharOutside == '[')) + appendSpaceAfter(); + + TRunpad(')', spacesInsideToDelete, 0 /*spacesOutsideToDelete*/); // trace macro + } + return; +} + +/** + * format brackets as attached or broken + * currentChar contains the bracket + * the brackets will be appended to the current formattedLine or a new formattedLine as necessary + * the calling function should have a continue statement after calling this method + * + * @param bracketType the type of bracket to be formatted. + */ +void ASFormatter::formatBrackets(BracketType bracketType) +{ + assert(!IS_A(bracketType, ARRAY_TYPE)); + assert (currentChar == '{' || currentChar == '}'); + + if (currentChar == '{') + { + parenStack->push_back(0); + } + else if (currentChar == '}') + { + if (!parenStack->empty()) + { + parenStack->pop_back(); + } + } + + if (currentChar == '{') + { + bool bdacBreak = false; + // should a Linux bracket be broken? + if (bracketFormatMode == BDAC_MODE) + { + // always break a class + if (IS_A((*bracketTypeStack)[bracketTypeStack->size()-1], CLASS_TYPE)) + bdacBreak = true; + // break a namespace and the first bracket if a function + else if (bracketTypeStack->size() <= 2) + { + if (IS_A((*bracketTypeStack)[bracketTypeStack->size()-1], NAMESPACE_TYPE) + || IS_A((*bracketTypeStack)[bracketTypeStack->size()-1], COMMAND_TYPE)) + bdacBreak = true; + } + // break the first bracket after a namespace if a function + else if (IS_A((*bracketTypeStack)[bracketTypeStack->size()-2], NAMESPACE_TYPE)) + { + if (IS_A((*bracketTypeStack)[bracketTypeStack->size()-1], COMMAND_TYPE)) + bdacBreak = true; + } + // if not C style then break the first bracket after a class if a function + else if (!ASBeautifier::isCStyle) + { + if (IS_A((*bracketTypeStack)[bracketTypeStack->size()-2], CLASS_TYPE) + && IS_A((*bracketTypeStack)[bracketTypeStack->size()-1], COMMAND_TYPE)) + bdacBreak = true; + } + } + if (bracketFormatMode == ATTACH_MODE + || (bracketFormatMode == BDAC_MODE && !bdacBreak)) + { + // are there comments before the bracket? + if (isCharImmediatelyPostComment || isCharImmediatelyPostLineComment) + { + if ((shouldBreakOneLineBlocks || !IS_A(bracketType, SINGLE_LINE_TYPE)) + && peekNextChar() != '}') + appendCharInsideComments(); + else + appendCurrentChar(true); // don't attach + } + else if (previousCommandChar == '{' + || previousCommandChar == '}' + || previousCommandChar == ';') // '}' , ';' chars added for proper handling of '{' immediately after a '}' or ';' + { + appendCurrentChar(true); // don't attach + } + else + { + size_t firstChar = formattedLine.find_first_not_of(" \t"); + if (firstChar == string::npos) // if a blank line preceeds this + appendCurrentChar(true); // don't attach + else if (shouldBreakOneLineBlocks + || !IS_A(bracketType, SINGLE_LINE_TYPE) + || peekNextChar() == '}') + { + appendSpacePad(); + appendCurrentChar(false); // OK to attach + } + else + appendCurrentChar(true); // don't attach + } + } + else if (bracketFormatMode == BREAK_MODE + || (bracketFormatMode == BDAC_MODE && bdacBreak)) + { + if (isBeforeComment()) + { + // do not break unless comment is at line end + if (isBeforeLineEndComment(charNum)) + { + currentChar = ' '; // remove bracket from current line + appendOpeningBracket = true; // append bracket to following line + } + } + else if (!IS_A(bracketType, SINGLE_LINE_TYPE)) + breakLine(); + else if (shouldBreakOneLineBlocks && peekNextChar() != '}') + breakLine(); + + appendCurrentChar(); + } + else if (bracketFormatMode == NONE_MODE) + { + if (lineBeginsWith('{')) // is opening bracket broken? + appendCurrentChar(true); + else + appendCurrentChar(false); + } + } + else if (currentChar == '}') + { + // mark state of immediately after empty block + // this state will be used for locating brackets that appear immedately AFTER an empty block (e.g. '{} \n}'). + if (previousCommandChar == '{') + isImmediatelyPostEmptyBlock = true; + + if ((!(previousCommandChar == '{' && isPreviousBracketBlockRelated)) // this '{' does not close an empty block + && (shouldBreakOneLineBlocks || !IS_A(bracketType, SINGLE_LINE_TYPE)) // astyle is allowed to break on line blocks + && (!(bracketFormatMode == NONE_MODE && IS_A(bracketType, SINGLE_LINE_TYPE))) + && !isImmediatelyPostEmptyBlock) // this '}' does not immediately follow an empty block + { + breakLine(); + appendCurrentChar(); + } + else + { + if (!isCharImmediatelyPostComment + && !bracketFormatMode == NONE_MODE + && !isImmediatelyPostEmptyBlock) + isInLineBreak = false; + + appendCurrentChar(); + + //if (!bracketFormatMode == NONE_MODE) + // if ((shouldBreakOneLineBlocks || !IS_A(bracketType, SINGLE_LINE_TYPE)) + // && !(currentChar == '}' && peekNextChar() == ';')) // fixes }; placed on separate lines + // shouldBreakLineAfterComments = true; + } + + if (shouldBreakBlocks) + { + isAppendPostBlockEmptyLineRequested = true; + } + } + return; +} + +/** + * format array brackets as attached or broken + * determine if the brackets can have an inStatement indent + * currentChar contains the bracket + * the brackets will be appended to the current formattedLine or a new formattedLine as necessary + * the calling function should have a continue statement after calling this method + * + * @param bracketType the type of bracket to be formatted, must be an ARRAY_TYPE. + * @param isOpeningArrayBracket indicates if this is the opening bracket for the array block. + */ +void ASFormatter::formatArrayBrackets(BracketType bracketType, bool isOpeningArrayBracket) +{ + assert(IS_A(bracketType, ARRAY_TYPE)); + assert (currentChar == '{' || currentChar == '}'); + + if (currentChar == '{') + { + // is this the first opening bracket in the array? + if (isOpeningArrayBracket) + { + if (bracketFormatMode == ATTACH_MODE || bracketFormatMode == BDAC_MODE) + { + // don't attach to a preprocessor directive + if (isImmediatelyPostPreprocessor) + appendCurrentChar(true); // don't attach + // are there comments before the bracket? + else if (isCharImmediatelyPostComment || isCharImmediatelyPostLineComment) + { + appendCharInsideComments(); + } + else + { + // if bracket is broken or not an assignment + if (lineBeginsWith('{') || previousNonWSChar != '=') + appendSpacePad(); + appendCurrentChar(false); // OK to attach + } + } + else if (bracketFormatMode == BREAK_MODE) + { + if (isWhiteSpace(peekNextChar())) + breakLine(); + else if (isBeforeComment()) + { + // do not break unless comment is at line end + if (isBeforeLineEndComment(charNum)) + { + currentChar = ' '; // remove bracket from current line + appendOpeningBracket = true; // append bracket to following line + } + } + appendCurrentChar(); + } + else if (bracketFormatMode == NONE_MODE) + { + if (lineBeginsWith('{')) // is opening bracket broken? + appendCurrentChar(); + else + appendCurrentChar(false); + } + } + else + appendCurrentChar(); // not the first opening bracket - don't change + + // if an opening bracket ends the line there will be no inStatement indent + char nextChar = peekNextChar(); + if (isWhiteSpace(nextChar) + || isBeforeLineEndComment(charNum) + || nextChar == '{') + isNonInStatementArray = true; + if (isNonInStatementArray) + TRarray('x'); + else + TRarray(' '); + + } + else if (currentChar == '}') + { + // does this close the first opening bracket in the array? + if (isOpeningArrayBracket && !IS_A(bracketType, SINGLE_LINE_TYPE) ) + { + breakLine(); + appendCurrentChar(); + } + else + appendCurrentChar(); + } +} + + +} // end namespace astyle diff --git a/lib/astyle/ASResource.cpp b/lib/astyle/ASResource.cpp new file mode 100644 index 00000000..0c554ac0 --- /dev/null +++ b/lib/astyle/ASResource.cpp @@ -0,0 +1,389 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * ASResource.cpp + * + * This file is a part of "Artistic Style" - an indentation and + * reformatting tool for C, C++, C# and Java source files. + * http://astyle.sourceforge.net + * + * The "Artistic Style" project, including all files needed to + * compile it, is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later + * version. + * + * This 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this project; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + +#include "astyle.h" + + +namespace astyle +{ +const string ASResource::AS_IF = string("if"); +const string ASResource::AS_ELSE = string("else"); +const string ASResource::AS_FOR = string("for"); +const string ASResource::AS_DO = string("do"); +const string ASResource::AS_WHILE = string("while"); +const string ASResource::AS_SWITCH = string("switch"); +const string ASResource::AS_CASE = string("case"); +const string ASResource::AS_DEFAULT = string("default"); +const string ASResource::AS_CLASS = string("class"); +const string ASResource::AS_STRUCT = string("struct"); +const string ASResource::AS_UNION = string("union"); +const string ASResource::AS_INTERFACE = string("interface"); +const string ASResource::AS_NAMESPACE = string("namespace"); +const string ASResource::AS_EXTERN = string("extern"); +const string ASResource::AS_PUBLIC = string("public"); +const string ASResource::AS_PROTECTED = string("protected"); +const string ASResource::AS_PRIVATE = string("private"); +const string ASResource::AS_STATIC = string("static"); +const string ASResource::AS_SYNCHRONIZED = string("synchronized"); +const string ASResource::AS_OPERATOR = string("operator"); +const string ASResource::AS_TEMPLATE = string("template"); +const string ASResource::AS_TRY = string("try"); +const string ASResource::AS_CATCH = string("catch"); +const string ASResource::AS_FINALLY = string("finally"); +const string ASResource::AS_THROWS = string("throws"); +const string ASResource::AS_CONST = string("const"); + +const string ASResource::AS_ASM = string("asm"); + +const string ASResource::AS_BAR_DEFINE = string("#define"); +const string ASResource::AS_BAR_INCLUDE = string("#include"); +const string ASResource::AS_BAR_IF = string("#if"); +const string ASResource::AS_BAR_EL = string("#el"); +const string ASResource::AS_BAR_ENDIF = string("#endif"); + +const string ASResource::AS_OPEN_BRACKET = string("{"); +const string ASResource::AS_CLOSE_BRACKET = string("}"); +const string ASResource::AS_OPEN_LINE_COMMENT = string("//"); +const string ASResource::AS_OPEN_COMMENT = string("/*"); +const string ASResource::AS_CLOSE_COMMENT = string("*/"); + +const string ASResource::AS_ASSIGN = string("="); +const string ASResource::AS_PLUS_ASSIGN = string("+="); +const string ASResource::AS_MINUS_ASSIGN = string("-="); +const string ASResource::AS_MULT_ASSIGN = string("*="); +const string ASResource::AS_DIV_ASSIGN = string("/="); +const string ASResource::AS_MOD_ASSIGN = string("%="); +const string ASResource::AS_OR_ASSIGN = string("|="); +const string ASResource::AS_AND_ASSIGN = string("&="); +const string ASResource::AS_XOR_ASSIGN = string("^="); +const string ASResource::AS_GR_GR_ASSIGN = string(">>="); +const string ASResource::AS_LS_LS_ASSIGN = string("<<="); +const string ASResource::AS_GR_GR_GR_ASSIGN = string(">>>="); +const string ASResource::AS_LS_LS_LS_ASSIGN = string("<<<="); +const string ASResource::AS_RETURN = string("return"); + +const string ASResource::AS_EQUAL = string("=="); +const string ASResource::AS_PLUS_PLUS = string("++"); +const string ASResource::AS_MINUS_MINUS = string("--"); +const string ASResource::AS_NOT_EQUAL = string("!="); +const string ASResource::AS_GR_EQUAL = string(">="); +const string ASResource::AS_GR_GR = string(">>"); +const string ASResource::AS_GR_GR_GR = string(">>>"); +const string ASResource::AS_LS_EQUAL = string("<="); +const string ASResource::AS_LS_LS = string("<<"); +const string ASResource::AS_LS_LS_LS = string("<<<"); +const string ASResource::AS_ARROW = string("->"); +const string ASResource::AS_AND = string("&&"); +const string ASResource::AS_OR = string("||"); +const string ASResource::AS_COLON_COLON = string("::"); +const string ASResource::AS_PAREN_PAREN = string("()"); +const string ASResource::AS_BLPAREN_BLPAREN = string("[]"); + +const string ASResource::AS_PLUS = string("+"); +const string ASResource::AS_MINUS = string("-"); +const string ASResource::AS_MULT = string("*"); +const string ASResource::AS_DIV = string("/"); +const string ASResource::AS_MOD = string("%"); +const string ASResource::AS_GR = string(">"); +const string ASResource::AS_LS = string("<"); +const string ASResource::AS_NOT = string("!"); +const string ASResource::AS_BIT_OR = string("|"); +const string ASResource::AS_BIT_AND = string("&"); +const string ASResource::AS_BIT_NOT = string("~"); +const string ASResource::AS_BIT_XOR = string("^"); +const string ASResource::AS_QUESTION = string("?"); +const string ASResource::AS_COLON = string(":"); +const string ASResource::AS_COMMA = string(","); +const string ASResource::AS_SEMICOLON = string(";"); + +const string ASResource::AS_FOREACH = string("foreach"); +const string ASResource::AS_LOCK = string("lock"); +const string ASResource::AS_UNSAFE = string("unsafe"); +const string ASResource::AS_FIXED = string("fixed"); +const string ASResource::AS_GET = string("get"); +const string ASResource::AS_SET = string("set"); +const string ASResource::AS_ADD = string("add"); +const string ASResource::AS_REMOVE = string("remove"); + +const string ASResource::AS_CONST_CAST = string("const_cast"); +const string ASResource::AS_DYNAMIC_CAST = string("dynamic_cast"); +const string ASResource::AS_REINTERPRET_CAST = string("reinterpret_cast"); +const string ASResource::AS_STATIC_CAST = string("static_cast"); + + +/** + * Build the vector of assignment operators. + * Used by BOTH ASFormatter.cpp and ASBeautifier.cpp + * + * @param assignmentOperators a reference to the vector to be built. + */ +void ASResource::buildAssignmentOperators(vector<const string*> &assignmentOperators) +{ + assignmentOperators.push_back(&AS_ASSIGN); + assignmentOperators.push_back(&AS_PLUS_ASSIGN); + assignmentOperators.push_back(&AS_MINUS_ASSIGN); + assignmentOperators.push_back(&AS_MULT_ASSIGN); + assignmentOperators.push_back(&AS_DIV_ASSIGN); + assignmentOperators.push_back(&AS_MOD_ASSIGN); + assignmentOperators.push_back(&AS_OR_ASSIGN); + assignmentOperators.push_back(&AS_AND_ASSIGN); + assignmentOperators.push_back(&AS_XOR_ASSIGN); + + // Java + assignmentOperators.push_back(&AS_GR_GR_GR_ASSIGN); + assignmentOperators.push_back(&AS_GR_GR_ASSIGN); + assignmentOperators.push_back(&AS_LS_LS_ASSIGN); + + // Unknown + assignmentOperators.push_back(&AS_LS_LS_LS_ASSIGN); + + assignmentOperators.push_back(&AS_RETURN); +} + +/** + * Build the vector of C++ cast operators. + * Used by ONLY ASFormatter.cpp + * + * @param castOperators a reference to the vector to be built. + */ +void ASResource::buildCastOperators(vector<const string*> &castOperators) +{ + castOperators.push_back(&AS_CONST_CAST); + castOperators.push_back(&AS_DYNAMIC_CAST); + castOperators.push_back(&AS_REINTERPRET_CAST); + castOperators.push_back(&AS_STATIC_CAST); +} + +/** + * Build the vector of header words. + * Used by BOTH ASFormatter.cpp and ASBeautifier.cpp + * + * @param headers a reference to the vector to be built. + */ +void ASResource::buildHeaders(vector<const string*> &headers, int fileType, bool beautifier) +{ + headers.push_back(&AS_IF); + headers.push_back(&AS_ELSE); + headers.push_back(&AS_FOR); + headers.push_back(&AS_WHILE); + headers.push_back(&AS_DO); + headers.push_back(&AS_SWITCH); + headers.push_back(&AS_TRY); + headers.push_back(&AS_CATCH); + + if (beautifier) + { + headers.push_back(&AS_CASE); + headers.push_back(&AS_DEFAULT); + headers.push_back(&AS_CONST); + headers.push_back(&AS_STATIC); + headers.push_back(&AS_EXTERN); + headers.push_back(&AS_TEMPLATE); + } + + if (fileType == JAVA_TYPE) + { + headers.push_back(&AS_FINALLY); + headers.push_back(&AS_SYNCHRONIZED); + } + + if (fileType == SHARP_TYPE) + { + headers.push_back(&AS_FINALLY); + headers.push_back(&AS_FOREACH); + headers.push_back(&AS_LOCK); + headers.push_back(&AS_UNSAFE); + headers.push_back(&AS_FIXED); + headers.push_back(&AS_GET); + headers.push_back(&AS_SET); + headers.push_back(&AS_ADD); + headers.push_back(&AS_REMOVE); + } +} + +/** + * Build the vector of non-assignment operators. + * Used by ONLY ASBeautifier.cpp + * + * @param nonParenHeaders a reference to the vector to be built. + */ +void ASResource::buildNonAssignmentOperators(vector<const string*> &nonAssignmentOperators) +{ + nonAssignmentOperators.push_back(&AS_EQUAL); + nonAssignmentOperators.push_back(&AS_PLUS_PLUS); + nonAssignmentOperators.push_back(&AS_MINUS_MINUS); + nonAssignmentOperators.push_back(&AS_NOT_EQUAL); + nonAssignmentOperators.push_back(&AS_GR_EQUAL); + nonAssignmentOperators.push_back(&AS_GR_GR_GR); + nonAssignmentOperators.push_back(&AS_GR_GR); + nonAssignmentOperators.push_back(&AS_LS_EQUAL); + nonAssignmentOperators.push_back(&AS_LS_LS_LS); + nonAssignmentOperators.push_back(&AS_LS_LS); + nonAssignmentOperators.push_back(&AS_ARROW); + nonAssignmentOperators.push_back(&AS_AND); + nonAssignmentOperators.push_back(&AS_OR); +} + +/** + * Build the vector of header non-paren headers. + * Used by BOTH ASFormatter.cpp and ASBeautifier.cpp + * + * @param nonParenHeaders a reference to the vector to be built. + */ +void ASResource::buildNonParenHeaders(vector<const string*> &nonParenHeaders, int fileType, bool beautifier) +{ + nonParenHeaders.push_back(&AS_ELSE); + nonParenHeaders.push_back(&AS_DO); + nonParenHeaders.push_back(&AS_TRY); + + if (beautifier) + { + nonParenHeaders.push_back(&AS_CASE); + nonParenHeaders.push_back(&AS_DEFAULT); + nonParenHeaders.push_back(&AS_CONST); + nonParenHeaders.push_back(&AS_STATIC); + nonParenHeaders.push_back(&AS_EXTERN); + nonParenHeaders.push_back(&AS_TEMPLATE); + } + + if (fileType == JAVA_TYPE) + { + nonParenHeaders.push_back(&AS_FINALLY); + } + + if (fileType == SHARP_TYPE) + { + nonParenHeaders.push_back(&AS_FINALLY); + nonParenHeaders.push_back(&AS_UNSAFE); + nonParenHeaders.push_back(&AS_GET); + nonParenHeaders.push_back(&AS_SET); + nonParenHeaders.push_back(&AS_ADD); + nonParenHeaders.push_back(&AS_REMOVE); + } +} + +/** + * Build the vector of operators. + * Used by ONLY ASFormatter.cpp + * + * @param operators a reference to the vector to be built. + */ +void ASResource::buildOperators(vector<const string*> &operators) +{ + operators.push_back(&AS_PLUS_ASSIGN); + operators.push_back(&AS_MINUS_ASSIGN); + operators.push_back(&AS_MULT_ASSIGN); + operators.push_back(&AS_DIV_ASSIGN); + operators.push_back(&AS_MOD_ASSIGN); + operators.push_back(&AS_OR_ASSIGN); + operators.push_back(&AS_AND_ASSIGN); + operators.push_back(&AS_XOR_ASSIGN); + operators.push_back(&AS_EQUAL); + operators.push_back(&AS_PLUS_PLUS); + operators.push_back(&AS_MINUS_MINUS); + operators.push_back(&AS_NOT_EQUAL); + operators.push_back(&AS_GR_EQUAL); + operators.push_back(&AS_GR_GR_GR_ASSIGN); + operators.push_back(&AS_GR_GR_ASSIGN); + operators.push_back(&AS_GR_GR_GR); + operators.push_back(&AS_GR_GR); + operators.push_back(&AS_LS_EQUAL); + operators.push_back(&AS_LS_LS_LS_ASSIGN); + operators.push_back(&AS_LS_LS_ASSIGN); + operators.push_back(&AS_LS_LS_LS); + operators.push_back(&AS_LS_LS); + operators.push_back(&AS_ARROW); + operators.push_back(&AS_AND); + operators.push_back(&AS_OR); + operators.push_back(&AS_COLON_COLON); + operators.push_back(&AS_PLUS); + operators.push_back(&AS_MINUS); + operators.push_back(&AS_MULT); + operators.push_back(&AS_DIV); + operators.push_back(&AS_MOD); + operators.push_back(&AS_QUESTION); + operators.push_back(&AS_COLON); + operators.push_back(&AS_ASSIGN); + operators.push_back(&AS_LS); + operators.push_back(&AS_GR); + operators.push_back(&AS_NOT); + operators.push_back(&AS_BIT_OR); + operators.push_back(&AS_BIT_AND); + operators.push_back(&AS_BIT_NOT); + operators.push_back(&AS_BIT_XOR); + operators.push_back(&AS_OPERATOR); + operators.push_back(&AS_COMMA); + operators.push_back(&AS_RETURN); +} + +/** + * Build the vector of pre-block statements. + * Used by ONLY ASBeautifier.cpp + * + * @param preBlockStatements a reference to the vector to be built. + */ +void ASResource::buildPreBlockStatements(vector<const string*> &preBlockStatements) +{ + preBlockStatements.push_back(&AS_CLASS); + preBlockStatements.push_back(&AS_STRUCT); + preBlockStatements.push_back(&AS_UNION); + preBlockStatements.push_back(&AS_INTERFACE); + preBlockStatements.push_back(&AS_NAMESPACE); + preBlockStatements.push_back(&AS_THROWS); + preBlockStatements.push_back(&AS_EXTERN); +} + +/** + * Build the vector of pre-command headers. + * Used by ONLY ASFormatter.cpp + * + * @param preCommandHeaders a reference to the vector to be built. + */ +void ASResource::buildPreCommandHeaders(vector<const string*> &preCommandHeaders) +{ + preCommandHeaders.push_back(&AS_EXTERN); + preCommandHeaders.push_back(&AS_THROWS); + preCommandHeaders.push_back(&AS_CONST); +} + +/** + * Build the vector of pre-definition headers. + * Used by ONLY ASFormatter.cpp + * + * @param preDefinitionHeaders a reference to the vector to be built. + */ +void ASResource::buildPreDefinitionHeaders(vector<const string*> &preDefinitionHeaders) +{ + preDefinitionHeaders.push_back(&AS_CLASS); + preDefinitionHeaders.push_back(&AS_INTERFACE); + preDefinitionHeaders.push_back(&AS_NAMESPACE); + preDefinitionHeaders.push_back(&AS_STRUCT); +} + + +} // end namespace astyle diff --git a/lib/astyle/Makefile.am b/lib/astyle/Makefile.am new file mode 100644 index 00000000..dbafd18e --- /dev/null +++ b/lib/astyle/Makefile.am @@ -0,0 +1,4 @@ +INCLUDES = $(all_includes) +noinst_LTLIBRARIES = libastyle.la +libastyle_la_LDFLAGS = $(all_libraries) +libastyle_la_SOURCES = ASBeautifier.cpp ASEnhancer.cpp ASFormatter.cpp ASResource.cpp diff --git a/lib/astyle/astyle.h b/lib/astyle/astyle.h new file mode 100644 index 00000000..445aacf6 --- /dev/null +++ b/lib/astyle/astyle.h @@ -0,0 +1,497 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * astyle.h + * + * This file is a part of "Artistic Style" - an indentation and + * reformatting tool for C, C++, C# and Java source files. + * http://astyle.sourceforge.net + * + * The "Artistic Style" project, including all files needed to + * compile it, is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later + * version. + * + * This 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this project; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + +#ifndef ASTYLE_H +#define ASTYLE_H + +#ifdef __VMS +#define __USE_STD_IOSTREAM 1 +#include <sstream> +#endif + +#include <string> +#include <vector> +#include <cctype> + +#include <string.h> + +using namespace std; + + +// 4996 - secure version deprecation warnings for .NET 2005 +// 4267 - 64 bit signed/unsigned loss of data +#ifdef _MSC_VER +#pragma warning(disable: 4996) +#pragma warning(disable: 4267) +#endif + +namespace astyle +{ + +enum FileType { C_TYPE=0, JAVA_TYPE=1, SHARP_TYPE=2 }; + +/* The enums below are not recognized by 'vectors' in Microsoft Visual C++ + V5 when they are part of a namespace!!! Use Visual C++ V6 or higher. +*/ +enum BracketMode { NONE_MODE, ATTACH_MODE, BREAK_MODE, BDAC_MODE }; + +enum BracketType { NULL_TYPE = 0, + NAMESPACE_TYPE = 1, // also a DEFINITION_TYPE + CLASS_TYPE = 2, // also a DEFINITION_TYPE + DEFINITION_TYPE = 4, + COMMAND_TYPE = 8, + ARRAY_TYPE = 16, // arrays and enums + SINGLE_LINE_TYPE = 32 + }; + +class ASSourceIterator +{ + public: + int eolWindows; + int eolLinux; + int eolMacOld; + char outputEOL[4]; // output end of line char + ASSourceIterator() { eolWindows = eolLinux = eolMacOld = 0; } + virtual ~ASSourceIterator() {} + virtual bool hasMoreLines() const = 0; + virtual string nextLine() = 0; +}; + +class ASResource +{ + public: + void buildAssignmentOperators(vector<const string*> &assignmentOperators); + void buildCastOperators(vector<const string*> &castOperators); + void buildHeaders(vector<const string*> &headers, int fileType, bool beautifier=false); + void buildNonAssignmentOperators(vector<const string*> &nonAssignmentOperators); + void buildNonParenHeaders(vector<const string*> &nonParenHeaders, int fileType, bool beautifier=false); + void buildOperators(vector<const string*> &operators); + void buildPreBlockStatements(vector<const string*> &preBlockStatements); + void buildPreCommandHeaders(vector<const string*> &preCommandHeaders); + void buildPreDefinitionHeaders(vector<const string*> &preDefinitionHeaders); + + public: + static const string AS_IF, AS_ELSE; + static const string AS_DO, AS_WHILE; + static const string AS_FOR; + static const string AS_SWITCH, AS_CASE, AS_DEFAULT; + static const string AS_TRY, AS_CATCH, AS_THROWS, AS_FINALLY; + static const string AS_PUBLIC, AS_PROTECTED, AS_PRIVATE; + static const string AS_CLASS, AS_STRUCT, AS_UNION, AS_INTERFACE, AS_NAMESPACE, AS_EXTERN; + static const string AS_STATIC; + static const string AS_CONST; + static const string AS_SYNCHRONIZED; + static const string AS_OPERATOR, AS_TEMPLATE; + static const string AS_OPEN_BRACKET, AS_CLOSE_BRACKET; + static const string AS_OPEN_LINE_COMMENT, AS_OPEN_COMMENT, AS_CLOSE_COMMENT; + static const string AS_BAR_DEFINE, AS_BAR_INCLUDE, AS_BAR_IF, AS_BAR_EL, AS_BAR_ENDIF; + static const string AS_RETURN; + static const string AS_ASSIGN, AS_PLUS_ASSIGN, AS_MINUS_ASSIGN, AS_MULT_ASSIGN; + static const string AS_DIV_ASSIGN, AS_MOD_ASSIGN, AS_XOR_ASSIGN, AS_OR_ASSIGN, AS_AND_ASSIGN; + static const string AS_GR_GR_ASSIGN, AS_LS_LS_ASSIGN, AS_GR_GR_GR_ASSIGN, AS_LS_LS_LS_ASSIGN; + static const string AS_EQUAL, AS_PLUS_PLUS, AS_MINUS_MINUS, AS_NOT_EQUAL, AS_GR_EQUAL, AS_GR_GR_GR, AS_GR_GR; + static const string AS_LS_EQUAL, AS_LS_LS_LS, AS_LS_LS, AS_ARROW, AS_AND, AS_OR; + static const string AS_COLON_COLON, AS_PAREN_PAREN, AS_BLPAREN_BLPAREN; + static const string AS_PLUS, AS_MINUS, AS_MULT, AS_DIV, AS_MOD, AS_GR, AS_LS; + static const string AS_NOT, AS_BIT_XOR, AS_BIT_OR, AS_BIT_AND, AS_BIT_NOT; + static const string AS_QUESTION, AS_COLON, AS_SEMICOLON, AS_COMMA; + static const string AS_ASM; + static const string AS_FOREACH, AS_LOCK, AS_UNSAFE, AS_FIXED; + static const string AS_GET, AS_SET, AS_ADD, AS_REMOVE; + static const string AS_CONST_CAST, AS_DYNAMIC_CAST, AS_REINTERPRET_CAST, AS_STATIC_CAST; +}; + +class ASBeautifier : protected ASResource +{ + public: + ASBeautifier(); + virtual ~ASBeautifier(); + virtual void init(ASSourceIterator* iter); // pointer to dynamically created iterator. + void init(); + virtual bool hasMoreLines() const; + virtual string nextLine(); + virtual string beautify(const string &line); + void setTabIndentation(int length = 4, bool forceTabs = false); + void setSpaceIndentation(int length = 4); + void setMaxInStatementIndentLength(int max); + void setMinConditionalIndentLength(int min); + void setClassIndent(bool state); + void setSwitchIndent(bool state); + void setCaseIndent(bool state); + void setBracketIndent(bool state); + void setBlockIndent(bool state); + void setNamespaceIndent(bool state); + void setLabelIndent(bool state); + void setCStyle(); + void setJavaStyle(); + void setSharpStyle(); + void setEmptyLineFill(bool state); + void setPreprocessorIndent(bool state); + int getIndentLength(void); + string getIndentString(void); + bool getCaseIndent(void); + bool getCStyle(void); + bool getJavaStyle(void); + bool getSharpStyle(void); + bool getEmptyLineFill(void); + + protected: + int getNextProgramCharDistance(const string &line, int i); +// bool isLegalNameChar(char ch) const; + const string *findHeader(const string &line, int i, + const vector<const string*> &possibleHeaders, + bool checkBoundry = true); + string trim(const string &str); + int indexOf(vector<const string*> &container, const string *element); + int fileType; + bool isCStyle; + bool isJavaStyle; + bool isSharpStyle; + + // variables set by ASFormatter - must be updated in preprocessor + int inLineNumber; // for debugging + int outLineNumber; // for debugging + bool lineCommentNoBeautify; + bool isNonInStatementArray; + + private: + ASBeautifier(const ASBeautifier ©); + void operator=(ASBeautifier&); // not to be implemented + + void initStatic(); + void registerInStatementIndent(const string &line, int i, int spaceTabCount, + int minIndent, bool updateParenStack); + string preLineWS(int spaceTabCount, int tabCount); + + static vector<const string*> headers; + static vector<const string*> nonParenHeaders; + static vector<const string*> preBlockStatements; + static vector<const string*> assignmentOperators; + static vector<const string*> nonAssignmentOperators; + + ASSourceIterator *sourceIterator; + vector<ASBeautifier*> *waitingBeautifierStack; + vector<ASBeautifier*> *activeBeautifierStack; + vector<int> *waitingBeautifierStackLengthStack; + vector<int> *activeBeautifierStackLengthStack; + vector<const string*> *headerStack; + vector< vector<const string*>* > *tempStacks; + vector<int> *blockParenDepthStack; + vector<bool> *blockStatementStack; + vector<bool> *parenStatementStack; + vector<int> *inStatementIndentStack; + vector<int> *inStatementIndentStackSizeStack; + vector<int> *parenIndentStack; + vector<bool> *bracketBlockStateStack; + string indentString; + const string *currentHeader; + const string *previousLastLineHeader; + const string *immediatelyPreviousAssignmentOp; + const string *probationHeader; + bool isInQuote; + bool isInComment; + bool isInCase; + bool isInQuestion; + bool isInStatement; + bool isInHeader; + bool isInOperator; + bool isInTemplate; + bool isInDefine; + bool isInDefineDefinition; + bool classIndent; + bool isInClassHeader; + bool isInClassHeaderTab; + bool switchIndent; + bool caseIndent; + bool namespaceIndent; + bool bracketIndent; + bool blockIndent; + bool labelIndent; + bool preprocessorIndent; + bool isInConditional; + bool isMinimalConditinalIndentSet; + bool shouldForceTabIndentation; + bool emptyLineFill; + bool backslashEndsPrevLine; + bool blockCommentNoIndent; + bool blockCommentNoBeautify; + bool previousLineProbationTab; + int minConditionalIndent; + int parenDepth; + int indentLength; + int blockTabCount; + int leadingWhiteSpaces; + int maxInStatementIndent; + int templateDepth; + int prevFinalLineSpaceTabCount; + int prevFinalLineTabCount; + int defineTabCount; + char quoteChar; + char prevNonSpaceCh; + char currentNonSpaceCh; + char currentNonLegalCh; + char prevNonLegalCh; + char peekNextChar(string &line, int i); + + protected: // inline functions + // check if a specific character can be used in a legal variable/method/class name + inline bool isLegalNameChar(char ch) const { + return (isalnum(ch) || ch == '.' || ch == '_' || (isJavaStyle && ch == '$') || (isCStyle && ch == '~')); + } + + // check if a specific character is a whitespace character + inline bool isWhiteSpace(char ch) const { + return (ch == ' ' || ch == '\t'); + } +}; + + +class ASEnhancer +{ + public: + // functions + ASEnhancer(); + ~ASEnhancer(); + void init(int, string, bool, bool, bool, bool, bool); + void enhance(string &line); + + private: + // set by init function + int indentLength; + bool useTabs; + bool isCStyle; + bool isJavaStyle; + bool isSharpStyle; + bool caseIndent; + bool emptyLineFill; + + // parsing variables + int lineNumber; + bool isInQuote; + bool isInComment; + char quoteChar; + + // unindent variables + int bracketCount; + int switchDepth; + bool lookingForCaseBracket; + bool unindentNextLine; + + // stringstream for trace + stringstream *traceOut; + + private: // private functions + bool findKeyword(const string &line, int i, const char *header) const; + int indentLine(string &line, const int indent) const; + int unindentLine(string &line, const int unindent) const; + + private: + // struct used by ParseFormattedLine function + // contains variables used to unindent the case blocks + struct switchVariables { + int switchBracketCount; + int unindentDepth; + bool unindentCase; + + switchVariables() { // constructor + switchBracketCount = 0; + unindentDepth = 0; + unindentCase = false; + } + }; + + private: // inline functions + // check if a specific character can be used in a legal variable/method/class name + inline bool isLegalNameCharX(char ch) const { + return (isalnum(ch) || ch == '.' || ch == '_' || (isJavaStyle && ch == '$') || (isCStyle && ch == '~')); + } + + // check if a specific character is a whitespace character + inline bool isWhiteSpaceX(char ch) const { + return (ch == ' ' || ch == '\t'); + } +}; + + +class ASFormatter : public ASBeautifier, private ASEnhancer +{ + public: + ASFormatter(); + virtual ~ASFormatter(); + virtual void init(ASSourceIterator* iter); + virtual bool hasMoreLines() const; + virtual string nextLine(); + void setBracketFormatMode(BracketMode mode); + void setBreakClosingHeaderBracketsMode(bool state); + void setOperatorPaddingMode(bool mode); + void setParensOutsidePaddingMode(bool mode); + void setParensInsidePaddingMode(bool mode); + void setParensUnPaddingMode(bool state); + void setBreakOneLineBlocksMode(bool state); + void setSingleStatementsMode(bool state); + void setTabSpaceConversionMode(bool state); + void setBreakBlocksMode(bool state); + void setBreakClosingHeaderBlocksMode(bool state); + void setBreakElseIfsMode(bool state); + string fileName; + + private: + void ASformatter(ASFormatter ©); // not to be imlpemented + void operator=(ASFormatter&); // not to be implemented + void staticInit(); + void goForward(int i); + void trimNewLine(); + char peekNextChar() const; + BracketType getBracketType() const; + bool getNextChar(); + bool isBeforeComment() const; + bool isBeforeLineEndComment(int startPos) const; + bool isPointerOrReference() const; + bool isUnaryMinus() const; + bool isInExponent() const; + bool isOneLineBlockReached() const; +// bool isNextCharWhiteSpace() const; + bool lineBeginsWith(char charToCheck) const; + void appendChar(char ch, bool canBreakLine = true); + void appendCharInsideComments(); + void appendSequence(const string &sequence, bool canBreakLine = true); + void appendSpacePad(); + void appendSpaceAfter(); + void breakLine(); + void padOperators(const string *newOperator); + void padParens(); + void formatBrackets(BracketType bracketType); + void formatArrayBrackets(BracketType bracketType, bool isOpeningArrayBracket); + void adjustComments(); + const string *findHeader(const vector<const string*> &headers, bool checkBoundry = true); + + static vector<const string*> headers; + static vector<const string*> nonParenHeaders; + static vector<const string*> preDefinitionHeaders; + static vector<const string*> preCommandHeaders; + static vector<const string*> operators; + static vector<const string*> assignmentOperators; + static vector<const string*> castOperators; + + ASSourceIterator *sourceIterator; + vector<const string*> *preBracketHeaderStack; + vector<BracketType> *bracketTypeStack; + vector<int> *parenStack; + string readyFormattedLine; + string currentLine; + string formattedLine; + const string *currentHeader; + const string *previousOperator; // used ONLY by pad=oper + char currentChar; + char previousChar; + char previousNonWSChar; + char previousCommandChar; + char quoteChar; + int charNum; + int spacePadNum; + int templateDepth; + int traceFileNumber; + size_t formattedLineCommentNum; // comment location on formattedLine + size_t previousReadyFormattedLineLength; + BracketMode bracketFormatMode; + BracketType previousBracketType; + bool isVirgin; + bool shouldPadOperators; + bool shouldPadParensOutside; + bool shouldPadParensInside; + bool shouldUnPadParens; + bool shouldConvertTabs; + bool isInLineComment; + bool isInComment; + bool isInPreprocessor; + bool isInTemplate; // true both in template definitions (e.g. template<class A>) and template usage (e.g. F<int>). + bool doesLineStartComment; + bool isInQuote; + bool isInBlParen; + bool isSpecialChar; + bool isNonParenHeader; + bool foundQuestionMark; + bool foundPreDefinitionHeader; + bool foundNamespaceHeader; + bool foundClassHeader; + bool foundPreCommandHeader; + bool foundCastOperator; + bool isInLineBreak; +// bool isInClosingBracketLineBreak; + bool endOfCodeReached; + bool lineCommentNoIndent; + bool isLineReady; + bool isPreviousBracketBlockRelated; + bool isInPotentialCalculation; + bool isCharImmediatelyPostComment; + bool isPreviousCharPostComment; + bool isCharImmediatelyPostLineComment; + bool isCharImmediatelyPostOpenBlock; + bool isCharImmediatelyPostCloseBlock; + bool isCharImmediatelyPostTemplate; + bool shouldBreakOneLineBlocks; + bool shouldReparseCurrentChar; + bool shouldBreakOneLineStatements; + bool shouldBreakLineAfterComments; + bool shouldBreakClosingHeaderBrackets; + bool shouldBreakElseIfs; + bool passedSemicolon; + bool passedColon; + bool isImmediatelyPostComment; + bool isImmediatelyPostLineComment; + bool isImmediatelyPostEmptyBlock; + bool isImmediatelyPostPreprocessor; + + bool shouldBreakBlocks; + bool shouldBreakClosingHeaderBlocks; + bool isPrependPostBlockEmptyLineRequested; + bool isAppendPostBlockEmptyLineRequested; + + bool prependEmptyLine; + bool appendOpeningBracket; + bool foundClosingHeader; + + bool isInHeader; + bool isImmediatelyPostHeader; + + private: // inline functions + // append the CURRENT character (curentChar)to the current formatted line. + inline void appendCurrentChar(bool canBreakLine = true) { + appendChar(currentChar, canBreakLine); + } + + // check if a specific sequence exists in the current placement of the current line + inline bool isSequenceReached(const char *sequence) const { + return currentLine.compare(charNum, strlen(sequence), sequence) == 0; + } +}; + +} // end of namespace astyle + +#endif // closes ASTYLE_H + diff --git a/lib/astyle/compiler_defines.h b/lib/astyle/compiler_defines.h new file mode 100644 index 00000000..347ac40d --- /dev/null +++ b/lib/astyle/compiler_defines.h @@ -0,0 +1,49 @@ + +/* + * Copyright (c) 1998, 1999 Tal Davidson. All rights reserved. + * + * compiler_defines.h (1 January 1999) + * by Tal Davidson (davidsont@bigfoot.com) + * This file is a part of "Artistic Style" - an indentater and reformatter + * of C++, C, and Java source files. + * + * The "Artistic Style" project, including all files needed to compile it, + * is free software; you can redistribute it and/or use it and/or modify it + * under the terms of EITHER the "Artistic License" OR + * the 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. + * + * You should have received a copy of EITHER the "Artistic License" or + * the GNU General Public License along with this program. + */ + + + + + +/* + * comment out the line below if your compiler does NOT understand NAMESPACES + */ +#define USES_NAMESPACE + + +#if defined(__GNUC__) && __GNUC__ < 3 +// for G++ implementation of string.compare: +#define COMPARE(place, length, str) compare((str), (place), (length)) +#else +// for standard implementation of string.compare: +#define COMPARE(place, length, str) compare((place), (length), (str)) +#endif + + +// Fix by John A. McNamara +// Get rid of annoying MSVC warnings on debug builds about lengths of +// identifiers in template instantiations. +#ifdef _MSC_VER +#pragma warning( disable:4786 ) +#endif + diff --git a/lib/catalog/Mainpage.dox b/lib/catalog/Mainpage.dox new file mode 100644 index 00000000..f18b8ced --- /dev/null +++ b/lib/catalog/Mainpage.dox @@ -0,0 +1,10 @@ +/** +@mainpage The KDevelop Catalog Library + +This is the persistant symbol store library working with BerkeleyDb backend. + +<b>Link with</b>: -lkdevcatalog \$(KDEDIR)/kdevbdb/libdb.a + +<b>Include path</b>: -I\$(kde_includes)/kdevelop/catalog -I\$(KDEDIR)/kdevbdb/include +*/ + diff --git a/lib/catalog/Makefile.am b/lib/catalog/Makefile.am new file mode 100644 index 00000000..ff01a6f6 --- /dev/null +++ b/lib/catalog/Makefile.am @@ -0,0 +1,13 @@ +INCLUDES = $(all_includes) $(DB3INCLUDES) +lib_LTLIBRARIES = libkdevcatalog.la +libkdevcatalog_la_SOURCES = tag.cpp catalog.cpp +libkdevcatalog_la_LDFLAGS = -no-undefined $(all_libraries) $(DB3LDFLAGS) +libkdevcatalog_la_LIBADD = -l$(DB3LIB) $(LIB_KDECORE) $(LIB_QT) + +kdevcatalogincludedir = $(includedir)/kdevelop/catalog +kdevcataloginclude_HEADERS = catalog.h tag.h + +DOXYGEN_REFERENCES = dcop interfaces kdecore kdefx kdeui khtml kmdi kio kjs kparts kutils +DOXYGEN_PROJECTNAME = KDevelop Catalog Library +DOXYGEN_DOCDIRPREFIX = kdev +include ../../Doxyfile.am diff --git a/lib/catalog/catalog.cpp b/lib/catalog/catalog.cpp new file mode 100644 index 00000000..b3438f1e --- /dev/null +++ b/lib/catalog/catalog.cpp @@ -0,0 +1,462 @@ +/* This file is part of KDevelop + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "catalog.h" +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qdatastream.h> + +#include <krandomsequence.h> +#include <kdebug.h> + + +#include <cstring> +#include <cstdlib> +#include <db.h> + +#include <config.h> + +struct _Catalog_Private +{ + QString dbName; + + DB* dbp; + QMap<QCString, DB*> indexList; + KRandomSequence rnd; + bool enabled; + + _Catalog_Private() + : dbp( 0 ), enabled( true ) + { + } + + bool hasIndex( const QCString& name ) const + { + return indexList.contains( name ); + } + + DB* index( const QCString& name ) + { + return indexList[ name ]; + } + + bool addItem( DB* dbp, const QCString& id, const Tag& tag ) + { + Q_ASSERT( dbp != 0 ); + + DBT key, data; + int ret; + + std::memset( &key, 0, sizeof(key) ); + std::memset( &data, 0, sizeof(data) ); + + QByteArray a1; + { + QDataStream stream( a1, IO_WriteOnly ); + stream << id; + key.data = a1.data(); + key.size = a1.size(); + } + + QByteArray a2; + { + QDataStream stream( a2, IO_WriteOnly ); + tag.store( stream ); + data.data = a2.data(); + data.size = a2.size(); + } + + ret = dbp->put( dbp, 0, &key, &data, 0 ); + + return ret == 0; + } + + bool addItem( DB* dbp, const QVariant& id, const QCString& v ) + { + Q_ASSERT( dbp != 0 ); + + DBT key, data; + int ret; + + std::memset( &key, 0, sizeof(key) ); + std::memset( &data, 0, sizeof(data) ); + + QByteArray a1; + { + QDataStream stream( a1, IO_WriteOnly ); + stream << id; + key.data = a1.data(); + key.size = a1.size(); + } + + QByteArray a2; + { + QDataStream stream( a2, IO_WriteOnly ); + stream << v; + data.data = a2.data(); + data.size = a2.size(); + } + + ret = dbp->put( dbp, 0, &key, &data, 0 ); + + return ret == 0; + } + +}; + + +/*! + \fn Catalog::Catalog + */ + Catalog::Catalog() + : d( new _Catalog_Private() ) +{ +} + +/*! + \fn Catalog::~Catalog + */ + Catalog::~Catalog() +{ + close(); + delete( d ); + d = 0; +} + +/*! + \fn Catalog::indexList() const + */ + QValueList<QCString> Catalog::indexList() const +{ + QValueList<QCString> l; + QMap<QCString, DB*>::Iterator it = d->indexList.begin(); + while( it != d->indexList.end() ){ + l << it.key(); + ++it; + } + + return l; +} + +bool Catalog::enabled() const +{ + return d->enabled; +} + +void Catalog::setEnabled( bool isEnabled ) +{ + d->enabled = isEnabled; +} + +/*! + \fn Catalog::addIndex( const QString& name ) + @todo document these functions + */ + void Catalog::addIndex( const QCString& name ) +{ + Q_ASSERT( d->dbp != 0 ); + + QMap<QCString, DB*>::Iterator it = d->indexList.find( name ); + if( it == d->indexList.end() ){ + DB* dbp = 0; + + int ret; + + if ((ret = db_create(&dbp, 0, 0)) != 0) { + kdDebug() << "db_create: " << db_strerror(ret) << endl; + return /*false*/; + } + + if ((ret = dbp->set_flags(dbp, DB_DUP | DB_DUPSORT)) != 0) { + dbp->err(dbp, ret, "set_flags: DB_DUP | DB_DUPSORT"); + dbp->close( dbp, 0 ); + return; + } + + QFileInfo fileInfo( d->dbName ); + QString indexName = fileInfo.dirPath(true) + "/" + fileInfo.baseName(true) + "." + QString(name) + ".idx"; + + if( (ret = dbp->set_cachesize( dbp, 0, 2 * 1024 * 1024, 0 )) != 0 ){ + kdDebug() << "set_cachesize: " << db_strerror(ret) << endl; + } + + if ((ret = dbp->open( + dbp, NULL, QFile::encodeName( indexName ).data(), 0, DB_BTREE, DB_CREATE, 0664)) != 0) { + kdDebug() << "db_open: " << db_strerror(ret) << endl; + dbp->close( dbp, 0 ); + return; + } + + d->indexList[ name ] = dbp; + } +} + +/*! + \fn Catalog::close() + */ + + void Catalog::close() +{ + d->dbName = QString::null; + + QMap<QCString, DB*>::Iterator it = d->indexList.begin(); + while( it != d->indexList.end() ){ + if( it.data() ){ + it.data()->close( it.data(), 0 ); + } + ++it; + } + d->indexList.clear(); + + if( d->dbp != 0 ){ + d->dbp->close( d->dbp, 0 ); + d->dbp = 0; + } +} + +/*! + \fn Catalog::open( const QString& dbName ) + */ + + void Catalog::open( const QString& dbName ) +{ + Q_ASSERT( d->dbp == 0 ); + + d->dbName = dbName; + + int ret; + + if ((ret = db_create(&d->dbp, 0, 0)) != 0) { + kdDebug() << "db_create: " << db_strerror(ret) << endl; + return /*false*/; + } + + if ((ret = d->dbp->set_flags(d->dbp, DB_RECNUM)) != 0) { + d->dbp->err(d->dbp, ret, "set_flags: DB_RECNUM"); + close(); + return; + } + + if( (ret = d->dbp->set_cachesize( d->dbp, 0, 2 * 1024 * 1024, 0 )) != 0 ){ + kdDebug() << "set_cachesize: " << db_strerror(ret) << endl; + } + + if ((ret = d->dbp->open( + d->dbp, NULL, d->dbName.local8Bit(), 0, DB_BTREE, DB_CREATE, 0664)) != 0) { + kdDebug() << "db_open: " << db_strerror(ret) << endl; + close(); + return; + } +} + +/*! + \fn Catalog::dbName() const + */ + + QString Catalog::dbName() const +{ + return d->dbName; +} + +/*! + \fn Catalog::isValid() const + */ + + bool Catalog::isValid() const +{ + return d->dbp != 0; +} + +/*! + \fn Catalog::addItem( Tag& tag ) + */ + + void Catalog::addItem( Tag& tag ) +{ + if( tag.name().isEmpty() ) + return; + + QCString id = generateId(); + + tag.setId( id ); + if( d->addItem(d->dbp, id, tag) ){ + QMap<QCString, DB*>::Iterator it = d->indexList.begin(); + while( it != d->indexList.end() ){ + if( tag.hasAttribute(it.key()) ) + d->addItem( it.data(), tag.attribute(it.key()), id ); + ++it; + } + } +} + +/*! + \fn Catalog::getItemById( const QString& id ) + */ + + Tag Catalog::getItemById( const QCString& id ) +{ + Q_ASSERT( d->dbp != 0 ); + + DBT key, data; + std::memset( &key, 0, sizeof(key) ); + std::memset( &data, 0, sizeof(data) ); + + QByteArray a1; + { + QDataStream stream( a1, IO_WriteOnly ); + stream << id; + key.data = a1.data(); + key.size = a1.size(); + } + + int ret = d->dbp->get( d->dbp, 0, &key, &data, 0 ); + Q_ASSERT( ret == 0 ); + + Tag tag; + + if( ret == 0 ){ + QByteArray a; + a.setRawData( (const char*) data.data, data.size ); + QDataStream stream( a, IO_ReadOnly ); + tag.load( stream ); + a.resetRawData( (const char*) data.data, data.size ); + } + + return tag; +} + +/*! + \fn Catalog::sync() +*/ + + void Catalog::sync() +{ + Q_ASSERT( d->dbp != 0 ); + d->dbp->sync( d->dbp, 0 ); + + QMap<QCString, DB*>::Iterator it = d->indexList.begin(); + while( it != d->indexList.end() ){ + it.data()->sync( it.data(), 0 ); + ++it; + } +} + +/*! + \fn Catalog::query( const QValueList<QueryArgument>& args ) +*/ + + QValueList<Tag> Catalog::query( const QValueList<QueryArgument>& args ) +{ + QValueList<Tag> tags; + + DBT key, data; + + DBC** cursors = new DBC* [ args.size() + 1 ]; + + QValueList< QPair<QCString,QVariant> >::ConstIterator it = args.begin(); + int current = 0; + while( it != args.end() ){ + QCString indexName = (*it).first; + QVariant value = (*it).second; + + if( d->hasIndex(indexName) ) { + DB* dbp = d->index( indexName ); + Q_ASSERT( dbp != 0 ); + + std::memset( &key, 0, sizeof(key) ); + std::memset( &data, 0, sizeof(data) ); + + QByteArray a1; + { + QDataStream stream( a1, IO_WriteOnly ); + stream << value; + key.data = a1.data(); + key.size = a1.size(); + } + + DBC* cursor = 0; + int rtn = dbp->cursor( dbp, 0, &cursor, 0 ); + + if ( rtn == 0 ) { + + rtn = cursor->c_get( cursor, &key, &data, DB_SET ); + + if ( rtn == 0 ) { + cursors[ current++ ] = cursor; + } + else if ( rtn != DB_NOTFOUND) { + kdDebug() << "fetching cursor failed: " << db_strerror(rtn) << endl; + cursor->c_close(cursor); + } + } + else { + kdDebug() << "creating cursor failed: " << db_strerror(rtn) << endl; + } + } + ++it; + } + + cursors[ current ] = 0; + + if( current == args.size() ) { + + DBC* join_curs = 0; + int rtn = d->dbp->join( d->dbp, cursors, &join_curs, 0 ); + + if ( rtn == 0 ) { + + std::memset( &key, 0, sizeof(key) ); + std::memset( &data, 0, sizeof(data) ); + + while( join_curs->c_get(join_curs, &key, &data, 0) == 0 ) { + + QByteArray a2; + a2.setRawData( (const char*) data.data, data.size ); + QDataStream s( a2, IO_ReadOnly ); + Tag tag; + tag.load( s ); + a2.resetRawData( (const char*) data.data, data.size ); + tags << tag; + } + + join_curs->c_close( join_curs ); + } + else { + kdDebug() << "joining results failed: " << db_strerror(rtn) << endl; + } + } + + DBC** c = cursors; + while( *c != 0 ){ + (*c)->c_close( *c ); + ++c; + } + delete[] cursors; + + return tags; +} + + QCString Catalog::generateId() +{ + static int n = 1; + QCString asStr; + asStr.sprintf( "%05d", n++ ); + return asStr; +} + diff --git a/lib/catalog/catalog.h b/lib/catalog/catalog.h new file mode 100644 index 00000000..8183acaf --- /dev/null +++ b/lib/catalog/catalog.h @@ -0,0 +1,80 @@ +/* This file is part of KDevelop + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef CATALOG_H +#define CATALOG_H + +#include <qvaluelist.h> +#include <qpair.h> +#include <qvariant.h> + + +#include "tag.h" +/** +@file catalog.h +Catalog database - the persistent symbol store database. +*/ + +/** +Catalog objects represent separate symbol databases. +Catalogs can be created/loaded/unloaded dynamically. +To find a symbol in the repository each catalog should be queried. + +Persistent symbol store is useful to keep information about code that +never or rarely changes. System libraries are perfect examples of such code. +*/ +class Catalog +{ +public: + typedef QPair<QCString, QVariant> QueryArgument; + +public: + Catalog(); + virtual ~Catalog(); + + bool isValid() const; + QString dbName() const; + + bool enabled() const; + void setEnabled( bool en ); + + virtual void open( const QString& dbName ); + virtual void close(); + virtual void sync(); + + QValueList<QCString> indexList() const; + void addIndex( const QCString& name ); + + void addItem( Tag& tag ); + + Tag getItemById( const QCString& id ); + QValueList<Tag> query( const QValueList<QueryArgument>& args ); + + QCString generateId(); + +private: + class _Catalog_Private* d; + +private: + Catalog( const Catalog& source ); + void operator = ( const Catalog& source ); +}; + + +#endif diff --git a/lib/catalog/tag.cpp b/lib/catalog/tag.cpp new file mode 100644 index 00000000..f4dbda67 --- /dev/null +++ b/lib/catalog/tag.cpp @@ -0,0 +1,127 @@ +/* This file is part of KDevelop + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "tag.h" +#include <qdatastream.h> + +Tag::Tag() +{ + data = new TagData(); + data->kind = 0; + data->flags = 0; + data->startLine = 0; + data->startColumn = 0; + data->endLine = 0; + data->endColumn = 0; +} + +Tag::Tag( const Tag& source ) +{ + data = source.data; + data->ref(); +} + +Tag::~Tag() +{ + if( data->deref() ){ + delete( data ); + data = 0; + } +} + +void Tag::detach() +{ + if( data->count != 1 ) + *this = copy(); +} + +Tag Tag::copy() +{ + Tag t; + + t.data->id = data->id; + t.data->kind = data->kind; + t.data->flags = data->flags; + t.data->name = data->name; + t.data->scope = data->scope; + t.data->fileName = data->fileName; + t.data->startLine = data->startLine; + t.data->startColumn = data->startColumn; + t.data->endLine = data->endLine; + t.data->endColumn = data->endColumn; + t.data->attributes = data->attributes; + + return t; +} + +Tag& Tag::operator = ( const Tag& source ) +{ + source.data->ref(); + if ( data->deref() ){ + delete data; + } + data = source.data; + + return( *this ); +} + +void Tag::load( QDataStream& stream ) +{ + stream + >> data->id + >> data->kind + >> data->flags + >> data->name + >> data->scope + >> data->fileName + >> data->startLine + >> data->startColumn + >> data->endLine + >> data->endColumn + >> data->attributes; +} + +void Tag::store( QDataStream& stream ) const +{ + stream + << data->id + << data->kind + << data->flags + << data->name + << data->scope + << data->fileName + << data->startLine + << data->startColumn + << data->endLine + << data->endColumn + << data->attributes; +} + +QDataStream& operator << ( QDataStream& s, const Tag& t) +{ + t.store( s ); + return s; +} + +QDataStream& operator >> ( QDataStream& s, Tag& t ) +{ + t.load( s ); + return s; +} + diff --git a/lib/catalog/tag.h b/lib/catalog/tag.h new file mode 100644 index 00000000..af5879a6 --- /dev/null +++ b/lib/catalog/tag.h @@ -0,0 +1,309 @@ +/* This file is part of KDevelop + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef TAG_H +#define TAG_H + +#include <qmap.h> +#include <qvariant.h> +#include <qshared.h> + +class QDataStream; + +union TagFlags +{ + unsigned long flags; + struct + { + unsigned long access: + 3; + unsigned long isVirtual: + 1; + } + data; +} ; + +class Tag +{ +public: + enum Kind + { + Kind_Unknown, + + Kind_Typedef = 1000, + Kind_Namespace, + Kind_UsingDirective, + Kind_Base_class, + Kind_Enum, + Kind_Enumerator, + Kind_Class, + Kind_Struct, + Kind_Union, + Kind_VariableDeclaration, + Kind_Variable, + Kind_FunctionDeclaration, + Kind_Function, + Kind_NamespaceAlias, + Kind_TranslationUnit, + + // ... + + Kind_Custom = 2000 + }; + +public: + Tag(); + Tag( const Tag& source ); + ~Tag(); + + operator bool() const { + return kind() != Kind_Unknown && kind() != 0; + } + + Tag& operator = ( const Tag& source ); + + QCString id() const + { + return data->id; + } + + void setId( const QCString& id ) + { + detach(); + data->id = id; + } + + int kind() const + { + return data->kind; + } + + void setKind( int kind ) + { + detach(); + data->kind = kind; + } + + unsigned long flags() const + { + return data->flags; + } + + void setFlags( unsigned long flags ) + { + detach(); + data->flags = flags; + } + + QString fileName() const + { + return data->fileName; + } + + void setFileName( const QString& fileName ) + { + detach(); + data->fileName = fileName; + } + + QString path( const QString& sep = QString::fromLatin1("::") ) const + { + QString s = scope().join( sep ); + if( s.isNull() ) + return name(); + return s + sep + name(); + } + + QString name() const + { + return data->name; + } + + QString comment() const { + if( hasAttribute( "cmt" ) ) { + return attribute( "cmt" ).asString(); + } else { + return ""; + } + } + + void setComment( const QString& comment ) { + setAttribute( "cmt", comment ); + } + + void setName( const QString& name ) + { + detach(); + data->name = name; + } + + QStringList scope() const + { + return data->scope; + } + + void setScope( const QStringList& scope ) + { + detach(); + data->scope = scope; + } + + void getStartPosition( int* line, int* column ) const + { + if( line ) *line = data->startLine; + if( column ) *column = data->startColumn; + } + + void setStartPosition( int line, int column ) + { + detach(); + data->startLine = line; + data->startColumn = column; + } + + void getEndPosition( int* line, int* column ) const + { + if( line ) *line = data->endLine; + if( column ) *column = data->endColumn; + } + + void setEndPosition( int line, int column ) + { + detach(); + data->endLine = line; + data->endColumn = column; + } + + QString getSpecializationDeclaration() const { + if( hasAttribute( "spc" ) ) + return data->attributes["spc"].asString(); + else + return QString::null; + } + + bool hasSpecializationDeclaration() const { + return data->attributes.contains( "spc" ); + } + + void setSpecializationDeclaration( const QString& str ) { + data->attributes["spc"] = str; + } + + bool hasAttribute( const QCString& name ) const + { + if( name == "kind" || + name == "name" || + name == "scope" || + name == "fileName" || + name == "startLine" || + name == "startColumn" || + name == "endLine" || + name == "endColumn" ) + return true; + return data->attributes.contains( name ); + } + + QVariant attribute( const QCString& name ) const + { + if( name == "id" ) + return data->id; + else if( name == "kind" ) + return data->kind; + else if( name == "name" ) + return data->name; + else if( name == "scope" ) + return data->scope; + else if( name == "fileName" ) + return data->fileName; + else if( name == "startLine" ) + return data->startLine; + else if( name == "startColumn" ) + return data->startColumn; + else if( name == "endLine" ) + return data->endLine; + else if( name == "endColumn" ) + return data->endColumn; + else if( name == "prefix" ) + return data->name.left( 2 ); + return data->attributes[ name ]; + } + + void setAttribute( const QCString& name, const QVariant& value ) + { + detach(); + if( name == "id" ) + data->id = value.toCString(); + else if( name == "kind" ) + data->kind = value.toInt(); + else if( name == "name" ) + data->name = value.toString(); + else if( name == "scope" ) + data->scope = value.toStringList(); + else if( name == "fileName" ) + data->fileName = value.toString(); + else if( name == "startLine" ) + data->startLine = value.toInt(); + else if( name == "startColumn" ) + data->startColumn = value.toInt(); + else if( name == "endLine" ) + data->endLine = value.toInt(); + else if( name == "endColumn" ) + data->endColumn = value.toInt(); + else + data->attributes[ name ] = value; + } + + void addTemplateParam( const QString& param , const QString& def = "" ) { + QMap<QCString, QVariant>::iterator it = data->attributes.find( "tpl" ); + if( it != data->attributes.end() && (*it).type() == QVariant::StringList ) { + }else{ + it = data->attributes.insert( "tpl", QVariant( QStringList() ) ); + } + + QStringList& l( (*it).asStringList() ); + l << param; + l << def; + } + + void load( QDataStream& stream ); + void store( QDataStream& stream ) const; + +private: + Tag copy(); + void detach(); + +private: + struct TagData: public QShared + { + QCString id; + int kind; + unsigned long flags; + QString name; + QStringList scope; + QString fileName; + int startLine, startColumn; + int endLine, endColumn; + QMap<QCString, QVariant> attributes; + } *data; +}; + +QDataStream& operator << ( QDataStream&, const Tag& ); +QDataStream& operator >> ( QDataStream&, Tag& ); + +#endif diff --git a/lib/cppparser/Makefile.am b/lib/cppparser/Makefile.am new file mode 100644 index 00000000..1ee03ce7 --- /dev/null +++ b/lib/cppparser/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES = -I$(top_srcdir)/lib/interfaces $(all_includes) +AM_CXXFLAGS = -DKDEVELOP_BGPARSER +lib_LTLIBRARIES = libkdevcppparser.la +libkdevcppparser_la_LDFLAGS = $(all_libraries) +libkdevcppparser_la_LIBADD = $(LIB_QT) $(LIB_KDECORE) $(top_builddir)/lib/interfaces/libkdevinterfaces.la -lDCOP $(LIB_KDEUI) $(LIB_KPARTS) -lktexteditor $(LIB_KIO) -lkscript +libkdevcppparser_la_SOURCES = ast.cpp cachemanager.cpp driver.cpp errors.cpp \ + lexer.cpp lexercache.cpp lookup.cpp parser.cpp tree_parser.cpp + +kdevcppparserincludedir = $(includedir)/kdevelop/cppparser +kdevcppparserinclude_HEADERS = ast.h driver.h lexer.h parser.h tree_parser.h errors.h lookup.h cachemanager.h lexercache.h macro.h + diff --git a/lib/cppparser/ast.cpp b/lib/cppparser/ast.cpp new file mode 100644 index 00000000..b6f452c0 --- /dev/null +++ b/lib/cppparser/ast.cpp @@ -0,0 +1,1262 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "ast.h" +#include <qstringlist.h> +#include <kdebug.h> + +QString nodeTypeToString( int type ) +{ + switch( type ) + { + case NodeType_Generic: + return "Generic"; + case NodeType_TemplateArgumentList: + return "TemplateArgumentList"; + case NodeType_ClassOrNamespaceName: + return "ClassOrNamespaceName"; + case NodeType_Name: + return "Name"; + case NodeType_Declaration: + return "Declaration"; + case NodeType_TypeSpecifier: + return "TypeSpecifier"; + case NodeType_BaseSpecifier: + return "BaseSpecifier"; + case NodeType_BaseClause: + return "BaseClause"; + case NodeType_ClassSpecifier: + return "ClassSpecifier"; + case NodeType_Enumerator: + return "Enumerator"; + case NodeType_EnumSpecifier: + return "EnumSpecifier"; + case NodeType_ElaboratedTypeSpecifier: + return "ElaboratedTypeSpecifier"; + case NodeType_LinkageBody: + return "LinkageBody"; + case NodeType_LinkageSpecification: + return "LinkageSpecification"; + case NodeType_Namespace: + return "Namespace"; + case NodeType_NamespaceAlias: + return "NamespaceAlias"; + case NodeType_Using: + return "Using"; + case NodeType_UsingDirective: + return "UsingDirective"; + case NodeType_InitDeclaratorList: + return "InitDeclaratorList"; + case NodeType_Typedef: + return "Typedef"; + case NodeType_Declarator: + return "Declarator"; + case NodeType_InitDeclarator: + return "InitDeclarator"; + case NodeType_TemplateDeclaration: + return "TemplateDeclaration"; + case NodeType_SimpleDeclaration: + return "SimpleDeclaration"; + case NodeType_Statement: + return "Statement"; + case NodeType_IfStatement: + return "IfStatement"; + case NodeType_WhileStatement: + return "WhileStatement"; + case NodeType_DoStatement: + return "DoStatement"; + case NodeType_ForStatement: + return "ForStatement"; + case NodeType_ForEachStatement: // qt4 [erbsland] + return "ForEachStatement"; + case NodeType_SwitchStatement: + return "SwitchStatement"; + case NodeType_CatchStatement: + return "CatchStatement"; + case NodeType_CatchStatementList: + return "CatchStatementList"; + case NodeType_TryBlockStatement: + return "TryBlockStatement"; + case NodeType_DeclarationStatement: + return "DeclarationStatement"; + case NodeType_StatementList: + return "StatementList"; + case NodeType_TranslationUnit: + return "TranslationUnit"; + case NodeType_FunctionDefinition: + return "FunctionDefinition"; + case NodeType_ExpressionStatement: + return "ExpressionStatement"; + case NodeType_ParameterDeclaration: + return "ParameterDeclaration"; + case NodeType_ParameterDeclarationList: + return "ParameterDeclarationList"; + case NodeType_ParameterDeclarationClause: + return "ParameterDeclarationClause"; + case NodeType_Group: + return "Group"; + case NodeType_AccessDeclaration: + return "AccessDeclaration"; + case NodeType_TypeParameter: + return "TypeParameter"; + case NodeType_TemplateParameter: + return "TemplateParameter"; + case NodeType_TemplateParameterList: + return "TemplateParameterList"; + case NodeType_Condition: + return "Condition"; + case NodeType_Custom: + return "Custom"; + } + + return QString::null; +} + + +// ------------------------------------------------------------------------ +AST::AST() + : m_nodeType( NodeType_Generic ), m_parent( 0 ), + m_startLine( 0 ), m_startColumn( 0 ), + m_endLine( 0 ), m_endColumn( 0 ) +{ +#ifndef CPPPARSER_NO_CHILDREN + m_children.setAutoDelete( false ); +#endif +} + +AST::~AST() +{ +#ifndef CPPPARSER_NO_CHILDREN + if( m_parent ) + m_parent->removeChild( this ); +#endif +} + +void AST::setStartPosition( int line, int col ) +{ + m_startLine = line; + m_startColumn = col; +} + +void AST::getStartPosition( int* line, int* col ) const +{ + if( line ) + *line = m_startLine; + + if( col ) + * col = m_startColumn; +} + +void AST::setEndPosition( int line, int col ) +{ + m_endLine = line; + m_endColumn = col; +} + +void AST::getEndPosition( int* line, int* col ) const +{ + if( line ) + *line = m_endLine; + + if( col ) + * col = m_endColumn; +} + +void AST::setParent( AST* parent ) +{ +#ifndef CPPPARSER_NO_CHILDREN + if( m_parent ) + m_parent->removeChild( this ); +#endif + + m_parent = parent; + +#ifndef CPPPARSER_NO_CHILDREN + if( m_parent ) + m_parent->appendChild( this ); +#endif +} + +#ifndef CPPPARSER_NO_CHILDREN +void AST::appendChild( AST* child ) +{ + m_children.append( child ); +} + +void AST::removeChild( AST* child ) +{ + m_children.remove( child ); +} +#endif + +// ------------------------------------------------------------------------ +NameAST::NameAST() + : m_global( false ) +{ + m_classOrNamespaceNameList.setAutoDelete( true ); +} + +void NameAST::setGlobal( bool b ) +{ + m_global = b; +} + +void NameAST::setUnqualifiedName( ClassOrNamespaceNameAST::Node& unqualifiedName ) +{ + m_unqualifiedName = unqualifiedName; + if( m_unqualifiedName.get() ) m_unqualifiedName->setParent( this ); +} + +void NameAST::addClassOrNamespaceName( ClassOrNamespaceNameAST::Node& classOrNamespaceName ) +{ + if( !classOrNamespaceName.get() ) + return; + + classOrNamespaceName->setParent( this ); + m_classOrNamespaceNameList.append( classOrNamespaceName.release() ); +} + +QString NameAST::text() const +{ + if( !m_unqualifiedName.get() ) + return QString::null; + + QString str; + + if( m_global ) + str += "::"; + + QStringList l; + QPtrListIterator<ClassOrNamespaceNameAST> it( m_classOrNamespaceNameList ); + while( it.current() ){ + str += it.current()->text() + "::"; + ++it; + } + + if( m_unqualifiedName.get() ) + str += m_unqualifiedName->text(); + + return str; +} + +// ------------------------------------------------------------------------ +DeclarationAST::DeclarationAST() +{ +} + +// ------------------------------------------------------------------------ +LinkageBodyAST::LinkageBodyAST() +{ + m_declarationList.setAutoDelete( true ); +} + +void LinkageBodyAST::addDeclaration( DeclarationAST::Node& ast ) +{ + if( !ast.get() ) + return; + + ast->setParent( this ); + m_declarationList.append( ast.release() ); +} + +// ------------------------------------------------------------------------ +LinkageSpecificationAST::LinkageSpecificationAST() +{ +} + +void LinkageSpecificationAST::setExternType( AST::Node& externType ) +{ + m_externType = externType; + if( m_externType.get() ) m_externType->setParent( this ); +} + +void LinkageSpecificationAST::setLinkageBody( LinkageBodyAST::Node& linkageBody ) +{ + m_linkageBody = linkageBody; + if( m_linkageBody.get() ) m_linkageBody->setParent( this ); +} + +void LinkageSpecificationAST::setDeclaration( DeclarationAST::Node& decl ) +{ + m_declaration = decl; + if( m_declaration.get() ) m_declaration->setParent( this ); +} + +// ------------------------------------------------------------------------ +TranslationUnitAST::TranslationUnitAST() +{ + ////kdDebug(9007) << "++ TranslationUnitAST::TranslationUnitAST()" << endl; + m_declarationList.setAutoDelete( true ); +} + +void TranslationUnitAST::addDeclaration( DeclarationAST::Node& ast ) +{ + if( !ast.get() ) + return; + + ast->setParent( this ); + m_declarationList.append( ast.release() ); +} + +// ------------------------------------------------------------------------ +NamespaceAST::NamespaceAST() +{ +} + +void NamespaceAST::setNamespaceName( AST::Node& namespaceName ) +{ + m_namespaceName = namespaceName; + if( m_namespaceName.get() ) m_namespaceName->setParent( this ); +} + +void NamespaceAST::setLinkageBody( LinkageBodyAST::Node& linkageBody ) +{ + m_linkageBody = linkageBody; + if( m_linkageBody.get() ) m_linkageBody->setParent( this ); +} + + +// ------------------------------------------------------------------------ +NamespaceAliasAST::NamespaceAliasAST() +{ +} + +void NamespaceAliasAST::setNamespaceName( AST::Node& namespaceName ) +{ + m_namespaceName = namespaceName; + if( m_namespaceName.get() ) m_namespaceName->setParent( this ); +} + +void NamespaceAliasAST::setAliasName( NameAST::Node& name ) +{ + m_aliasName = name; + if( m_aliasName.get() ) m_aliasName->setParent( this ); +} + +// ------------------------------------------------------------------------ +UsingAST::UsingAST() +{ +} + +void UsingAST::setTypeName( AST::Node& typeName ) +{ + m_typeName = typeName; + if( m_typeName.get() ) m_typeName->setParent( this ); +} + +void UsingAST::setName( NameAST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +// ------------------------------------------------------------------------ +UsingDirectiveAST::UsingDirectiveAST() +{ +} + +void UsingDirectiveAST::setName( NameAST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +TypedefAST::TypedefAST() +{ +} + +void TypeSpecifierAST::setName( NameAST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +void TypedefAST::setTypeSpec( TypeSpecifierAST::Node& typeSpec ) +{ + m_typeSpec = typeSpec; + if( m_typeSpec.get() ) m_typeSpec->setParent( this ); +} + +void TypedefAST::setInitDeclaratorList( InitDeclaratorListAST::Node& initDeclaratorList ) +{ + m_initDeclaratorList = initDeclaratorList; + if( m_initDeclaratorList.get() ) m_initDeclaratorList->setParent( this ); +} + +// ------------------------------------------------------------------------ +TemplateArgumentListAST::TemplateArgumentListAST() +{ + m_argumentList.setAutoDelete( true ); +} + +void TemplateArgumentListAST::addArgument( AST::Node& arg ) +{ + if( !arg.get() ) + return; + + arg->setParent( this ); + m_argumentList.append( arg.release() ); +} + +QString TemplateArgumentListAST::text() const +{ + QStringList l; + + QPtrListIterator<AST> it( m_argumentList ); + while( it.current() ){ + l.append( it.current()->text() ); + ++it; + } + + return l.join( ", " ); +} + +// ------------------------------------------------------------------------ +TemplateDeclarationAST::TemplateDeclarationAST() +{ +} + +void TemplateDeclarationAST::setExported( AST::Node& exported ) +{ + m_exported = exported; + if( m_exported.get() ) m_exported->setParent( this ); +} + +void TemplateDeclarationAST::setTemplateParameterList( TemplateParameterListAST::Node& templateParameterList ) +{ + m_templateParameterList = templateParameterList; + if( m_templateParameterList.get() ) m_templateParameterList->setParent( this ); +} + +void TemplateDeclarationAST::setDeclaration( DeclarationAST::Node& declaration ) +{ + m_declaration = declaration; + if( m_declaration.get() ) m_declaration->setParent( this ); +} + +// ------------------------------------------------------------------------ +ClassOrNamespaceNameAST::ClassOrNamespaceNameAST() +{ +} + +void ClassOrNamespaceNameAST::setName( AST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +void ClassOrNamespaceNameAST::setTemplateArgumentList( TemplateArgumentListAST::Node& templateArgumentList ) +{ + m_templateArgumentList = templateArgumentList; + if( m_templateArgumentList.get() ) m_templateArgumentList->setParent( this ); +} + +QString ClassOrNamespaceNameAST::text() const +{ + if( !m_name.get() ) + return QString::null; + + QString str = m_name->text(); + if( m_templateArgumentList.get() ) + str += QString::fromLatin1("< ") + m_templateArgumentList->text() + QString::fromLatin1(" >"); + + return str; +} + +// ------------------------------------------------------------------------ +TypeSpecifierAST::TypeSpecifierAST() +{ +} + +void TypeSpecifierAST::setCvQualify( GroupAST::Node& cvQualify ) +{ + m_cvQualify = cvQualify; + if( m_cvQualify.get() ) m_cvQualify->setParent( this ); +} + +void TypeSpecifierAST::setCv2Qualify( GroupAST::Node& cv2Qualify ) +{ + m_cv2Qualify = cv2Qualify; + if( m_cv2Qualify.get() ) m_cv2Qualify->setParent( this ); +} + +QString TypeSpecifierAST::text() const +{ + QString str; + + if( m_cvQualify.get() ) + str += m_cvQualify->text() + " "; + + if( m_name.get() ) + str += m_name->text(); + + if( m_cv2Qualify.get() ) + str += QString(" ") + m_cv2Qualify->text(); + + return str; +} + +// ------------------------------------------------------------------------ +ClassSpecifierAST::ClassSpecifierAST() +{ + m_declarationList.setAutoDelete( true ); +} + +void ClassSpecifierAST::setClassKey( AST::Node& classKey ) +{ + m_classKey = classKey; + if( m_classKey.get() ) m_classKey->setParent( this ); +} + +void ClassSpecifierAST::addDeclaration( DeclarationAST::Node& declaration ) +{ + if( !declaration.get() ) + return; + + declaration->setParent( this ); + m_declarationList.append( declaration.release() ); +} + +void ClassSpecifierAST::setBaseClause( BaseClauseAST::Node& baseClause ) +{ + m_baseClause = baseClause; + if( m_baseClause.get() ) m_baseClause->setParent( this ); +} + +// ------------------------------------------------------------------------ +EnumSpecifierAST::EnumSpecifierAST() +{ + m_enumeratorList.setAutoDelete( true ); +} + +void EnumSpecifierAST::addEnumerator( EnumeratorAST::Node& enumerator ) +{ + if( !enumerator.get() ) + return; + + enumerator->setParent( this ); + m_enumeratorList.append( enumerator.release() ); +} + + +// ------------------------------------------------------------------------ +ElaboratedTypeSpecifierAST::ElaboratedTypeSpecifierAST() +{ +} + +void ElaboratedTypeSpecifierAST::setKind( AST::Node& kind ) +{ + m_kind = kind; + if( m_kind.get() ) m_kind->setParent( this ); +} + +QString ElaboratedTypeSpecifierAST::text() const +{ + if( m_kind.get() ) + return m_kind->text() + " " + TypeSpecifierAST::text(); + + return TypeSpecifierAST::text(); +} + +// ------------------------------------------------------------------------ +StatementAST::StatementAST() +{ +} + +// ------------------------------------------------------------------------ +EnumeratorAST::EnumeratorAST() +{ +} + +void EnumeratorAST::setId( AST::Node& id ) +{ + m_id = id; + if( m_id.get() ) m_id->setParent( this ); +} + +void EnumeratorAST::setExpr( AST::Node& expr ) +{ + m_expr = expr; + if( m_expr.get() ) m_expr->setParent( this ); +} + +// ------------------------------------------------------------------------ +BaseClauseAST::BaseClauseAST() +{ + m_baseSpecifierList.setAutoDelete( true ); +} + +void BaseClauseAST::addBaseSpecifier( BaseSpecifierAST::Node& baseSpecifier ) +{ + if( !baseSpecifier.get() ) + return; + + baseSpecifier->setParent( this ); + m_baseSpecifierList.append( baseSpecifier.release() ); +} + +// ------------------------------------------------------------------------ +BaseSpecifierAST::BaseSpecifierAST() +{ +} + +void BaseSpecifierAST::setIsVirtual( AST::Node& isVirtual ) +{ + m_isVirtual = isVirtual; + if( m_isVirtual.get() ) m_isVirtual->setParent( this ); +} + +void BaseSpecifierAST::setAccess( AST::Node& access ) +{ + m_access = access; + if( m_access.get() ) m_access->setParent( this ); +} + +void BaseSpecifierAST::setName( NameAST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +// ------------------------------------------------------------------------ +SimpleDeclarationAST::SimpleDeclarationAST() +{ +} + +void SimpleDeclarationAST::setFunctionSpecifier( GroupAST::Node& functionSpecifier ) +{ + m_functionSpecifier = functionSpecifier; + if( m_functionSpecifier.get() ) m_functionSpecifier->setParent( this ); +} + +void SimpleDeclarationAST::setStorageSpecifier( GroupAST::Node& storageSpecifier ) +{ + m_storageSpecifier = storageSpecifier; + if( m_storageSpecifier.get() ) m_storageSpecifier->setParent( this ); +} + +void SimpleDeclarationAST::setTypeSpec( TypeSpecifierAST::Node& typeSpec ) +{ + m_typeSpec = typeSpec; + if( m_typeSpec.get() ) m_typeSpec->setParent( this ); +} + +void SimpleDeclarationAST::setInitDeclaratorList( InitDeclaratorListAST::Node& initDeclaratorList ) +{ + m_initDeclaratorList = initDeclaratorList; + if( m_initDeclaratorList.get() ) m_initDeclaratorList->setParent( this ); +} + +void SimpleDeclarationAST::setWinDeclSpec( GroupAST::Node& winDeclSpec ) +{ + m_winDeclSpec = winDeclSpec; + if( m_winDeclSpec.get() ) m_winDeclSpec->setParent( this ); +} + + +// ------------------------------------------------------------------------ +InitDeclaratorListAST::InitDeclaratorListAST() +{ + m_initDeclaratorList.setAutoDelete( true ); +} + +void InitDeclaratorListAST::addInitDeclarator( InitDeclaratorAST::Node& decl ) +{ + if( !decl.get() ) + return; + + decl->setParent( this ); + m_initDeclaratorList.append( decl.release() ); +} + +// ------------------------------------------------------------------------ +DeclaratorAST::DeclaratorAST() +{ + m_ptrOpList.setAutoDelete( true ); + m_arrayDimensionList.setAutoDelete( true ); +} + +void DeclaratorAST::setSubDeclarator( DeclaratorAST::Node& subDeclarator ) +{ + m_subDeclarator = subDeclarator; + if( m_subDeclarator.get() ) m_subDeclarator->setParent( this ); +} + +void DeclaratorAST::setDeclaratorId( NameAST::Node& declaratorId ) +{ + m_declaratorId = declaratorId; + if( m_declaratorId.get() ) m_declaratorId->setParent( this ); +} + +void DeclaratorAST::setBitfieldInitialization( AST::Node& bitfieldInitialization ) +{ + m_bitfieldInitialization = bitfieldInitialization; + if( m_bitfieldInitialization.get() ) m_bitfieldInitialization->setParent( this ); +} + +void DeclaratorAST::addArrayDimension( AST::Node& arrayDimension ) +{ + if( !arrayDimension.get() ) + return; + + arrayDimension->setParent( this ); + m_arrayDimensionList.append( arrayDimension.release() ); +} + +void DeclaratorAST::setParameterDeclarationClause( ParameterDeclarationClauseAST::Node& parameterDeclarationClause ) +{ + m_parameterDeclarationClause = parameterDeclarationClause; + if( m_parameterDeclarationClause.get() ) m_parameterDeclarationClause->setParent( this ); +} + +void DeclaratorAST::setConstant( AST::Node& constant ) +{ + m_constant = constant; + if( m_constant.get() ) m_constant->setParent( this ); +} + +void DeclaratorAST::setExceptionSpecification( GroupAST::Node& exceptionSpecification ) +{ + m_exceptionSpecification = exceptionSpecification; + if( m_exceptionSpecification.get() ) m_exceptionSpecification->setParent( this ); +} + +void DeclaratorAST::addPtrOp( AST::Node& ptrOp ) +{ + if( !ptrOp.get() ) + return; + + ptrOp->setParent( this ); + m_ptrOpList.append( ptrOp.release() ); +} + +// -------------------------------------------------------------------------- +InitDeclaratorAST::InitDeclaratorAST() +{ +} + +void InitDeclaratorAST::setDeclarator( DeclaratorAST::Node& declarator ) +{ + m_declarator = declarator; + if( m_declarator.get() ) m_declarator->setParent( this ); +} + +void InitDeclaratorAST::setInitializer( AST::Node& initializer ) +{ + m_initializer = initializer; + if( m_initializer.get() ) m_initializer->setParent( this ); +} + +// -------------------------------------------------------------------------- +FunctionDefinitionAST::FunctionDefinitionAST() +{ +} + +void FunctionDefinitionAST::setFunctionSpecifier( GroupAST::Node& functionSpecifier ) +{ + m_functionSpecifier = functionSpecifier; + if( m_functionSpecifier.get() ) m_functionSpecifier->setParent( this ); +} + +void FunctionDefinitionAST::setStorageSpecifier( GroupAST::Node& storageSpecifier ) +{ + m_storageSpecifier = storageSpecifier; + if( m_storageSpecifier.get() ) m_storageSpecifier->setParent( this ); +} + +void FunctionDefinitionAST::setTypeSpec( TypeSpecifierAST::Node& typeSpec ) +{ + m_typeSpec = typeSpec; + if( m_typeSpec.get() ) m_typeSpec->setParent( this ); +} + +void FunctionDefinitionAST::setInitDeclarator( InitDeclaratorAST::Node& initDeclarator ) +{ + m_initDeclarator = initDeclarator; + if( m_initDeclarator.get() ) m_initDeclarator->setParent( this ); +} + +void FunctionDefinitionAST::setFunctionBody( StatementListAST::Node& functionBody ) +{ + m_functionBody = functionBody; + if( m_functionBody.get() ) m_functionBody->setParent( this ); +} + +void FunctionDefinitionAST::setWinDeclSpec( GroupAST::Node& winDeclSpec ) +{ + m_winDeclSpec = winDeclSpec; + if( m_winDeclSpec.get() ) m_winDeclSpec->setParent( this ); +} + +// -------------------------------------------------------------------------- +StatementListAST::StatementListAST() +{ + m_statementList.setAutoDelete( true ); +} + +void StatementListAST::addStatement( StatementAST::Node& statement ) +{ + if( !statement.get() ) + return; + + statement->setParent( this ); + m_statementList.append( statement.release() ); +} + +// -------------------------------------------------------------------------- +IfStatementAST::IfStatementAST() +{ +} + +void IfStatementAST::setCondition( ConditionAST::Node& condition ) +{ + m_condition = condition; + if( m_condition.get() ) m_condition->setParent( this ); +} + +void IfStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +void IfStatementAST::setElseStatement( StatementAST::Node& elseStatement ) +{ + m_elseStatement = elseStatement; + if( m_elseStatement.get() ) m_elseStatement->setParent( this ); +} + +// -------------------------------------------------------------------------- +WhileStatementAST::WhileStatementAST() +{ +} + +void WhileStatementAST::setCondition( ConditionAST::Node& condition ) +{ + m_condition = condition; + if( m_condition.get() ) m_condition->setParent( this ); +} + +void WhileStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +// -------------------------------------------------------------------------- +DoStatementAST::DoStatementAST() +{ +} + +void DoStatementAST::setCondition( ConditionAST::Node& condition ) +{ + m_condition = condition; + if( m_condition.get() ) m_condition->setParent( this ); +} + +void DoStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +// -------------------------------------------------------------------------- +ForStatementAST::ForStatementAST() +{ +} + +void ForStatementAST::setCondition( ConditionAST::Node& condition ) +{ + m_condition = condition; + if( m_condition.get() ) m_condition->setParent( this ); +} + +void ForStatementAST::setExpression( AST::Node& expression ) +{ + m_expression = expression; + if( m_expression.get() ) m_expression->setParent( this ); +} + +void ForStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +void ForStatementAST::setInitStatement( StatementAST::Node& initStatement ) +{ + m_initStatement = initStatement; + if( m_initStatement.get() ) m_initStatement->setParent( this ); +} + +// -------------------------------------------------------------------------- +ForEachStatementAST::ForEachStatementAST() +{ +} + +void ForEachStatementAST::setExpression( AST::Node& expression ) +{ + m_expression = expression; + if( m_expression.get() ) m_expression->setParent( this ); +} + +void ForEachStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +void ForEachStatementAST::setInitStatement( StatementAST::Node& initStatement ) +{ + m_initStatement = initStatement; + if( m_initStatement.get() ) m_initStatement->setParent( this ); +} + +// -------------------------------------------------------------------------- +SwitchStatementAST::SwitchStatementAST() +{ +} + +void SwitchStatementAST::setCondition( ConditionAST::Node& condition ) +{ + m_condition = condition; + if( m_condition.get() ) m_condition->setParent( this ); +} + +void SwitchStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +// -------------------------------------------------------------------------- +CatchStatementListAST::CatchStatementListAST() +{ + m_statementList.setAutoDelete( true ); +} + +void CatchStatementListAST::addStatement( CatchStatementAST::Node& statement ) +{ + if( !statement.get() ) + return; + + statement->setParent( this ); + m_statementList.append( statement.release() ); +} + +// -------------------------------------------------------------------------- +CatchStatementAST::CatchStatementAST() +{ +} + +void CatchStatementAST::setCondition( ConditionAST::Node& condition ) +{ + m_condition = condition; + if( m_condition.get() ) m_condition->setParent( this ); +} + +void CatchStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +// -------------------------------------------------------------------------- +TryBlockStatementAST::TryBlockStatementAST() +{ +} + +void TryBlockStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +void TryBlockStatementAST::setCatchStatementList( CatchStatementListAST::Node& statementList ) +{ + m_catchStatementList = statementList; + if( m_catchStatementList.get() ) m_catchStatementList->setParent( this ); +} + +// -------------------------------------------------------------------------- +DeclarationStatementAST::DeclarationStatementAST() +{ +} + +void DeclarationStatementAST::setDeclaration( DeclarationAST::Node& declaration ) +{ + m_declaration = declaration; + if( m_declaration.get() ) m_declaration->setParent( this ); +} + +// -------------------------------------------------------------------------- +ExpressionStatementAST::ExpressionStatementAST() +{ +} + +void ExpressionStatementAST::setExpression( AST::Node& expression ) +{ + m_expression = expression; + if( m_expression.get() ) m_expression->setParent( this ); +} + + +// -------------------------------------------------------------------------- +ParameterDeclarationAST::ParameterDeclarationAST() +{ +} + +void ParameterDeclarationAST::setTypeSpec( TypeSpecifierAST::Node& typeSpec ) +{ + m_typeSpec = typeSpec; + if( m_typeSpec.get() ) m_typeSpec->setParent( this ); +} + +void ParameterDeclarationAST::setDeclarator( DeclaratorAST::Node& declarator ) +{ + m_declarator = declarator; + if( m_declarator.get() ) m_declarator->setParent( this ); +} + +void ParameterDeclarationAST::setExpression( AST::Node& expression ) +{ + m_expression = expression; + if( m_expression.get() ) m_expression->setParent( this ); +} + +QString ParameterDeclarationAST::text() const +{ + QString str; + if( m_typeSpec.get() ) + str += m_typeSpec->text() + " "; + + if( m_declarator.get() ) + str += m_declarator->text(); + + if( m_expression.get() ) + str += QString( " = " ) + m_expression->text(); + + return str; +} + +// -------------------------------------------------------------------------- +ParameterDeclarationListAST::ParameterDeclarationListAST() +{ + m_parameterList.setAutoDelete( true ); +} + +void ParameterDeclarationListAST::addParameter( ParameterDeclarationAST::Node& parameter ) +{ + if( !parameter.get() ) + return; + + parameter->setParent( this ); + m_parameterList.append( parameter.release() ); +} + +QString ParameterDeclarationListAST::text() const +{ + QStringList l; + + QPtrListIterator<ParameterDeclarationAST> it( m_parameterList ); + while( it.current() ){ + l.append( it.current()->text() ); + ++it; + } + + return l.join( ", " ); +} + + +// -------------------------------------------------------------------------- +ParameterDeclarationClauseAST::ParameterDeclarationClauseAST() +{ +} + +void ParameterDeclarationClauseAST::setParameterDeclarationList( ParameterDeclarationListAST::Node& parameterDeclarationList ) +{ + m_parameterDeclarationList = parameterDeclarationList; + if( m_parameterDeclarationList.get() ) m_parameterDeclarationList->setParent( this ); +} + +void ParameterDeclarationClauseAST::setEllipsis( AST::Node& ellipsis ) +{ + m_ellipsis = ellipsis; + if( m_ellipsis.get() ) m_ellipsis->setParent( this ); +} + +QString ParameterDeclarationClauseAST::text() const +{ + QString str; + + if( m_parameterDeclarationList.get() ) + str += m_parameterDeclarationList->text(); + + if( m_ellipsis.get() ) + str += " ..."; + + return str; +} + + +// -------------------------------------------------------------------------- +GroupAST::GroupAST() +{ + m_nodeList.setAutoDelete( true ); +} + +void GroupAST::addNode( AST::Node& node ) +{ + if( !node.get() ) + return; + + node->setParent( this ); + m_nodeList.append( node.release() ); +} + +QString GroupAST::text() const +{ + QStringList l; + + QPtrListIterator<AST> it( m_nodeList ); + while( it.current() ){ + l.append( it.current()->text() ); + ++it; + } + + return l.join( " " ); +} + +// -------------------------------------------------------------------------- +AccessDeclarationAST::AccessDeclarationAST() +{ + m_accessList.setAutoDelete( true ); +} + +void AccessDeclarationAST::addAccess( AST::Node& access ) +{ + if( !access.get() ) + return; + + access->setParent( this ); + m_accessList.append( access.release() ); +} + +QString AccessDeclarationAST::text() const +{ + QStringList l; + + QPtrListIterator<AST> it( m_accessList ); + while( it.current() ){ + l.append( it.current()->text() ); + ++it; + } + + return l.join( " " ); +} + +// -------------------------------------------------------------------------- +TypeParameterAST::TypeParameterAST() +{ +} + +void TypeParameterAST::setKind( AST::Node& kind ) +{ + m_kind = kind; + if( m_kind.get() ) m_kind->setParent( this ); +} + +void TypeParameterAST::setTemplateParameterList( TemplateParameterListAST::Node& templateParameterList ) +{ + m_templateParameterList = templateParameterList; + if( m_templateParameterList.get() ) m_templateParameterList->setParent( this ); +} + +void TypeParameterAST::setName( NameAST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +void TypeParameterAST::setTypeId( AST::Node& typeId ) +{ + m_typeId = typeId; + if( m_typeId.get() ) m_typeId->setParent( this ); +} + +// -------------------------------------------------------------------------- +TemplateParameterAST::TemplateParameterAST() +{ +} + +void TemplateParameterAST::setTypeParameter( TypeParameterAST::Node& typeParameter ) +{ + m_typeParameter = typeParameter; + if( m_typeParameter.get() ) m_typeParameter->setParent( this ); +} + +void TemplateParameterAST::setTypeValueParameter( ParameterDeclarationAST::Node& typeValueParameter ) +{ + m_typeValueParameter = typeValueParameter; + if( m_typeValueParameter.get() ) m_typeValueParameter->setParent( this ); +} + +// -------------------------------------------------------------------------- +TemplateParameterListAST::TemplateParameterListAST() +{ + m_templateParameterList.setAutoDelete( true ); +} + +void TemplateParameterListAST::addTemplateParameter( TemplateParameterAST::Node& templateParameter ) +{ + if( !templateParameter.get() ) + return; + + templateParameter->setParent( this ); + m_templateParameterList.append( templateParameter.release() ); +} + +// -------------------------------------------------------------------------- +ConditionAST::ConditionAST() +{ +} + +void ConditionAST::setTypeSpec( TypeSpecifierAST::Node& typeSpec ) +{ + m_typeSpec = typeSpec; + if( m_typeSpec.get() ) m_typeSpec->setParent( this ); +} + +void ConditionAST::setDeclarator( DeclaratorAST::Node& declarator ) +{ + m_declarator = declarator; + if( m_declarator.get() ) m_declarator->setParent( this ); +} + +void ConditionAST::setExpression( AST::Node& expression ) +{ + m_expression = expression; + if( m_expression.get() ) m_expression->setParent( this ); +} + +void ClassSpecifierAST::setWinDeclSpec( GroupAST::Node & winDeclSpec ) +{ + m_winDeclSpec = winDeclSpec; + if( m_winDeclSpec.get() ) m_winDeclSpec->setParent( this ); +} diff --git a/lib/cppparser/ast.h b/lib/cppparser/ast.h new file mode 100644 index 00000000..4e877e0e --- /dev/null +++ b/lib/cppparser/ast.h @@ -0,0 +1,1578 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef __ast_h +#define __ast_h + +#include <memory> +#include <qstring.h> +#include <qptrlist.h> +#include <qstringlist.h> +#include <ksharedptr.h> + +#if defined( Q_OS_WIN32 ) || defined( Q_CC_SUN ) + +#ifndef _THROW0 +# define _THROW0() +#endif + +template <class _Tp> class AUTO_PTR { +private: + _Tp* _M_ptr; + +public: + typedef _Tp element_type; + + explicit AUTO_PTR(_Tp* __p = 0) _THROW0() : _M_ptr(__p) {} + + template <class _Tp1> AUTO_PTR(AUTO_PTR<_Tp1>& __a) _THROW0() + : _M_ptr(__a.release()) {} + + AUTO_PTR(AUTO_PTR& __a) _THROW0() : _M_ptr(__a.release()) {} + + + + template <class _Tp1> + AUTO_PTR& operator=(AUTO_PTR<_Tp1>& __a) _THROW0() { + if (__a.get() != this->get()) { + delete _M_ptr; + _M_ptr = __a.release(); + } + return *this; + } + + AUTO_PTR& operator=(AUTO_PTR& __a) _THROW0() { + if (&__a != this) { + delete _M_ptr; + _M_ptr = __a.release(); + } + return *this; + } + + ~AUTO_PTR() _THROW0() { delete _M_ptr; } + + _Tp& operator*() const _THROW0() { + return *_M_ptr; + } + _Tp* operator->() const _THROW0() { + return _M_ptr; + } + _Tp* get() const _THROW0() { + return _M_ptr; + } + _Tp* release() _THROW0() { + _Tp* __tmp = _M_ptr; + _M_ptr = 0; + return __tmp; + } + void reset(_Tp* __p = 0) _THROW0() { + delete _M_ptr; + _M_ptr = __p; + } + + // According to the C++ standard, these conversions are required. Most + // present-day compilers, however, do not enforce that requirement---and, + // in fact, most present-day compilers do not support the language + // features that these conversions rely on. + + +private: + template<class _Tp1> struct AUTO_PTR_ref { + _Tp1* _M_ptr; + AUTO_PTR_ref(_Tp1* __p) : _M_ptr(__p) {} + }; + +public: + AUTO_PTR(AUTO_PTR_ref<_Tp> __ref) _THROW0() + : _M_ptr(__ref._M_ptr) {} + template <class _Tp1> operator AUTO_PTR_ref<_Tp1>() _THROW0() + { return AUTO_PTR_ref<_Tp>(this->release()); } + template <class _Tp1> operator AUTO_PTR<_Tp1>() _THROW0() + { return AUTO_PTR<_Tp1>(this->release()) } + +}; + +#else +#define AUTO_PTR std::auto_ptr +#endif + +template <class T> typename T::Node CreateNode() +{ + typename T::Node node( new T ); + node->setNodeType( T::Type ); + return node; +} + +template <class T> typename T::Node NullNode() +{ + typename T::Node node; + return node; +} + +enum NodeType +{ + NodeType_Generic = 0, + + NodeType_TemplateArgumentList = 1000, + NodeType_ClassOrNamespaceName, + NodeType_Name, + NodeType_Declaration, + NodeType_TypeSpecifier, + NodeType_BaseSpecifier, + NodeType_BaseClause, + NodeType_ClassSpecifier, + NodeType_Enumerator, + NodeType_EnumSpecifier, + NodeType_ElaboratedTypeSpecifier, + NodeType_LinkageBody, + NodeType_LinkageSpecification, + NodeType_Namespace, + NodeType_NamespaceAlias, + NodeType_Using, + NodeType_UsingDirective, + NodeType_InitDeclaratorList, + NodeType_Typedef, + NodeType_Declarator, + NodeType_InitDeclarator, + NodeType_TemplateDeclaration, + NodeType_SimpleDeclaration, + NodeType_Statement, + NodeType_StatementList, + NodeType_IfStatement, + NodeType_WhileStatement, + NodeType_DoStatement, + NodeType_ForStatement, + NodeType_ForEachStatement, // qt4 [erbsland] + NodeType_SwitchStatement, + NodeType_CatchStatement, + NodeType_CatchStatementList, + NodeType_TryBlockStatement, + NodeType_DeclarationStatement, + NodeType_TranslationUnit, + NodeType_FunctionDefinition, + NodeType_ExpressionStatement, + NodeType_ParameterDeclaration, + NodeType_ParameterDeclarationList, + NodeType_ParameterDeclarationClause, + NodeType_Group, + NodeType_AccessDeclaration, + NodeType_TypeParameter, + NodeType_TemplateParameter, + NodeType_TemplateParameterList, + NodeType_Condition, + + NodeType_Custom = 2000 +}; + +QString nodeTypeToString( int type ); + + +#if defined(CPPPARSER_QUICK_ALLOCATOR) + +#include <quick_allocator.h> + +#define DECLARE_ALLOC(tp) \ + void * operator new(std::size_t) \ + { \ + return quick_allocator< tp >::alloc(); \ + } \ + \ + void operator delete(void * p) \ + { \ + quick_allocator< tp >::dealloc(p); \ + } +#else + +#define DECLARE_ALLOC(tp) + +#endif + +struct Slice +{ + QString source; + int position; + int length; + + inline Slice() + : position(0), length(0) {} +}; + + +class CommentAST { + QString m_comment; + public: + void setComment( const QString& comment ) { + m_comment = comment; + } + + void addComment( const QString& comment ) { + if( !m_comment.isEmpty() ) { + m_comment += "\n(" + comment + ")"; + } else { + m_comment = comment; + } + } + + QString comment() const { + return m_comment; + } + + bool haveComment() const { + return !m_comment.isEmpty(); + } +}; + +class AST : public CommentAST +{ +public: + typedef AUTO_PTR<AST> Node; + enum { Type=NodeType_Generic }; + + DECLARE_ALLOC( AST ) + +public: + AST(); + virtual ~AST(); + + int nodeType() const { return m_nodeType; } + void setNodeType( int nodeType ) { m_nodeType = nodeType; } + + AST* parent() { return m_parent; } + void setParent( AST* parent ); + + void setStartPosition( int line, int col ); + void getStartPosition( int* line, int* col ) const; + + void setEndPosition( int line, int col ); + void getEndPosition( int* line, int* col ) const; + +#ifndef CPPPARSER_NO_CHILDREN + QPtrList<AST> children() { return m_children; } + void appendChild( AST* child ); + void removeChild( AST* child ); +#endif + + virtual inline QString text() const + { return m_slice.source.mid(m_slice.position, m_slice.length); } + + inline void setSlice( const Slice& slice ) + { m_slice = slice; } + + inline void setSlice( const QString &text, int position, int length ) + { + CommentAST a; + m_slice.source = text; + m_slice.position = position; + m_slice.length = length; + } + + inline void setText(const QString &text) + { setSlice(text, 0, text.length()); } + +private: + int m_nodeType; + AST* m_parent; + int m_startLine, m_startColumn; + int m_endLine, m_endColumn; + Slice m_slice; +#ifndef CPPPARSER_NO_CHILDREN + QPtrList<AST> m_children; +#endif + +private: + AST( const AST& source ); + void operator = ( const AST& source ); +}; + +class GroupAST: public AST +{ +public: + typedef AUTO_PTR<GroupAST> Node; + enum { Type = NodeType_Group }; + + DECLARE_ALLOC( GroupAST ) + +public: + GroupAST(); + + QPtrList<AST> nodeList() { return m_nodeList; } + void addNode( AST::Node& node ); + + virtual QString text() const; + +private: + QPtrList<AST> m_nodeList; + +private: + GroupAST( const GroupAST& source ); + void operator = ( const GroupAST& source ); +}; + + +class TemplateArgumentListAST: public AST +{ +public: + typedef AUTO_PTR<TemplateArgumentListAST> Node; + enum { Type = NodeType_TemplateArgumentList }; + + DECLARE_ALLOC( TemplateArgumentListAST ) + +public: + TemplateArgumentListAST(); + + void addArgument( AST::Node& arg ); + QPtrList<AST> argumentList() { return m_argumentList; } + + virtual QString text() const; + +private: + QPtrList<AST> m_argumentList; + +private: + TemplateArgumentListAST( const TemplateArgumentListAST& source ); + void operator = ( const TemplateArgumentListAST& source ); +}; + +class ClassOrNamespaceNameAST: public AST +{ +public: + typedef AUTO_PTR<ClassOrNamespaceNameAST> Node; + enum { Type = NodeType_ClassOrNamespaceName }; + + DECLARE_ALLOC( ClassOrNamespaceNameAST ) + +public: + ClassOrNamespaceNameAST(); + + AST* name() { return m_name.get(); } + void setName( AST::Node& name ); + + TemplateArgumentListAST* templateArgumentList() { return m_templateArgumentList.get(); } + void setTemplateArgumentList( TemplateArgumentListAST::Node& templateArgumentList ); + + virtual QString text() const; + +private: + AST::Node m_name; + TemplateArgumentListAST::Node m_templateArgumentList; + +private: + ClassOrNamespaceNameAST( const ClassOrNamespaceNameAST& source ); + void operator = ( const ClassOrNamespaceNameAST& source ); +}; + +class NameAST: public AST +{ +public: + typedef AUTO_PTR<NameAST> Node; + enum { Type = NodeType_Name }; + + DECLARE_ALLOC( NameAST ) + +public: + NameAST(); + + bool isGlobal() const { return m_global; } + void setGlobal( bool b ); + + void addClassOrNamespaceName( ClassOrNamespaceNameAST::Node& classOrNamespaceName ); + QPtrList<ClassOrNamespaceNameAST> classOrNamespaceNameList() { return m_classOrNamespaceNameList; } + + ClassOrNamespaceNameAST* unqualifiedName() { return m_unqualifiedName.get(); } + void setUnqualifiedName( ClassOrNamespaceNameAST::Node& unqualifiedName ); + + virtual QString text() const; + +private: + bool m_global; + ClassOrNamespaceNameAST::Node m_unqualifiedName; + QPtrList<ClassOrNamespaceNameAST> m_classOrNamespaceNameList; + +private: + NameAST( const NameAST& source ); + void operator = ( const NameAST& source ); +}; + +class TypeParameterAST: public AST +{ +public: + typedef AUTO_PTR<TypeParameterAST> Node; + enum { Type = NodeType_TypeParameter }; + + DECLARE_ALLOC( TypeParameterAST ) + +public: + TypeParameterAST(); + + AST* kind() { return m_kind.get(); } + void setKind( AST::Node& kind ); + + class TemplateParameterListAST* templateParameterList() { return m_templateParameterList.get(); } + void setTemplateParameterList( AUTO_PTR<class TemplateParameterListAST>& templateParameterList ); + + NameAST* name() { return m_name.get(); } + void setName( NameAST::Node& name ); + + AST* typeId() { return m_typeId.get(); } + void setTypeId( AST::Node& typeId ); + +private: + AST::Node m_kind; + AUTO_PTR<class TemplateParameterListAST> m_templateParameterList; + NameAST::Node m_name; + AST::Node m_typeId; + +private: + TypeParameterAST( const TypeParameterAST& source ); + void operator = ( const TypeParameterAST& source ); +}; + +class DeclarationAST: public AST +{ +public: + typedef AUTO_PTR<DeclarationAST> Node; + enum { Type = NodeType_Declaration }; + + DECLARE_ALLOC( DeclarationAST ) + +public: + DeclarationAST(); + +private: + DeclarationAST( const DeclarationAST& source ); + void operator = ( const DeclarationAST& source ); +}; + +class AccessDeclarationAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<AccessDeclarationAST> Node; + enum { Type = NodeType_AccessDeclaration }; + + DECLARE_ALLOC( AccessDeclarationAST ) + +public: + AccessDeclarationAST(); + + QPtrList<AST> accessList() { return m_accessList; } + void addAccess( AST::Node& access ); + + virtual QString text() const; + +private: + QPtrList<AST> m_accessList; + +private: + AccessDeclarationAST( const AccessDeclarationAST& source ); + void operator = ( const AccessDeclarationAST& source ); +}; + +class TypeSpecifierAST: public AST +{ +public: + typedef AUTO_PTR<TypeSpecifierAST> Node; + enum { Type = NodeType_TypeSpecifier }; + + DECLARE_ALLOC( TypeSpecifierAST ) + +public: + TypeSpecifierAST(); + + virtual NameAST* name() { return m_name.get(); } + virtual void setName( NameAST::Node& name ); + + GroupAST* cvQualify() { return m_cvQualify.get(); } + void setCvQualify( GroupAST::Node& cvQualify ); + + GroupAST* cv2Qualify() { return m_cv2Qualify.get(); } + void setCv2Qualify( GroupAST::Node& cv2Qualify ); + + virtual QString text() const; + +private: + NameAST::Node m_name; + GroupAST::Node m_cvQualify; + GroupAST::Node m_cv2Qualify; + +private: + TypeSpecifierAST( const TypeSpecifierAST& source ); + void operator = ( const TypeSpecifierAST& source ); +}; + +class BaseSpecifierAST: public AST +{ +public: + typedef AUTO_PTR<BaseSpecifierAST> Node; + enum { Type = NodeType_BaseSpecifier }; + + DECLARE_ALLOC( BaseSpecifierAST ) + +public: + BaseSpecifierAST(); + + AST* isVirtual() { return m_isVirtual.get(); } + void setIsVirtual( AST::Node& isVirtual ); + + AST* access() { return m_access.get(); } + void setAccess( AST::Node& access ); + + NameAST* name() { return m_name.get(); } + void setName( NameAST::Node& name ); + +private: + AST::Node m_isVirtual; + AST::Node m_access; + NameAST::Node m_name; + +private: + BaseSpecifierAST( const BaseSpecifierAST& source ); + void operator = ( const BaseSpecifierAST& source ); +}; + +class BaseClauseAST: public AST +{ +public: + typedef AUTO_PTR<BaseClauseAST> Node; + enum { Type = NodeType_BaseClause }; + + DECLARE_ALLOC( BaseClauseAST ) + +public: + BaseClauseAST(); + + void addBaseSpecifier( BaseSpecifierAST::Node& baseSpecifier ); + QPtrList<BaseSpecifierAST> baseSpecifierList() { return m_baseSpecifierList; } + +private: + QPtrList<BaseSpecifierAST> m_baseSpecifierList; + +private: + BaseClauseAST( const BaseClauseAST& source ); + void operator = ( const BaseClauseAST& source ); +}; + +class ClassSpecifierAST: public TypeSpecifierAST +{ +public: + typedef AUTO_PTR<ClassSpecifierAST> Node; + enum { Type = NodeType_ClassSpecifier }; + + DECLARE_ALLOC( ClassSpecifierAST ) + +public: + ClassSpecifierAST(); + + GroupAST* winDeclSpec() { return m_winDeclSpec.get(); } + void setWinDeclSpec( GroupAST::Node& winDeclSpec ); + + AST* classKey() { return m_classKey.get(); } + void setClassKey( AST::Node& classKey ); + + BaseClauseAST* baseClause() { return m_baseClause.get(); } + void setBaseClause( BaseClauseAST::Node& baseClause ); + + QPtrList<DeclarationAST> declarationList() { return m_declarationList; } + void addDeclaration( DeclarationAST::Node& declaration ); + +private: + GroupAST::Node m_winDeclSpec; + AST::Node m_classKey; + BaseClauseAST::Node m_baseClause; + QPtrList<DeclarationAST> m_declarationList; +private: + ClassSpecifierAST( const ClassSpecifierAST& source ); + void operator = ( const ClassSpecifierAST& source ); +}; + +class EnumeratorAST: public AST +{ +public: + typedef AUTO_PTR<EnumeratorAST> Node; + enum { Type = NodeType_Enumerator }; + + DECLARE_ALLOC( EnumeratorAST ) + +public: + EnumeratorAST(); + + AST* id() { return m_id.get(); } + void setId( AST::Node& id ); + + AST* expr() { return m_expr.get(); } + void setExpr( AST::Node& expr ); + +private: + AST::Node m_id; + AST::Node m_expr; + +private: + EnumeratorAST( const EnumeratorAST& source ); + void operator = ( const EnumeratorAST& source ); +}; + +class EnumSpecifierAST: public TypeSpecifierAST +{ +public: + typedef AUTO_PTR<EnumSpecifierAST> Node; + enum { Type = NodeType_EnumSpecifier }; + + DECLARE_ALLOC( EnumSpecifierAST ) + +public: + EnumSpecifierAST(); + + void addEnumerator( EnumeratorAST::Node& enumerator ); + QPtrList<EnumeratorAST> enumeratorList() { return m_enumeratorList; } + +private: + QPtrList<EnumeratorAST> m_enumeratorList; + +private: + EnumSpecifierAST( const EnumSpecifierAST& source ); + void operator = ( const EnumSpecifierAST& source ); +}; + +class ElaboratedTypeSpecifierAST: public TypeSpecifierAST +{ +public: + typedef AUTO_PTR<ElaboratedTypeSpecifierAST> Node; + enum { Type = NodeType_ElaboratedTypeSpecifier }; + + DECLARE_ALLOC( ElaboratedTypeSpecifierAST ) + +public: + ElaboratedTypeSpecifierAST(); + + AST* kind() { return m_kind.get(); } + void setKind( AST::Node& kind ); + + virtual QString text() const; + +private: + AST::Node m_kind; + +private: + ElaboratedTypeSpecifierAST( const ElaboratedTypeSpecifierAST& source ); + void operator = ( const ElaboratedTypeSpecifierAST& source ); +}; + + +class LinkageBodyAST: public AST +{ +public: + typedef AUTO_PTR<LinkageBodyAST> Node; + enum { Type = NodeType_LinkageBody }; + + DECLARE_ALLOC( LinkageBodyAST ) + +public: + LinkageBodyAST(); + + void addDeclaration( DeclarationAST::Node& ast ); + QPtrList<DeclarationAST> declarationList() { return m_declarationList; } + +private: + QPtrList<DeclarationAST> m_declarationList; + +private: + LinkageBodyAST( const LinkageBodyAST& source ); + void operator = ( const LinkageBodyAST& source ); +}; + +class LinkageSpecificationAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<LinkageSpecificationAST> Node; + enum { Type = NodeType_LinkageSpecification }; + + DECLARE_ALLOC( LinkageSpecificationAST ) + +public: + LinkageSpecificationAST(); + + AST* externType() { return m_externType.get(); } + void setExternType( AST::Node& externType ); + + LinkageBodyAST* linkageBody() { return m_linkageBody.get(); } + void setLinkageBody( LinkageBodyAST::Node& linkageBody ); + + DeclarationAST* declaration() { return m_declaration.get(); } + void setDeclaration( DeclarationAST::Node& decl ); + +private: + AST::Node m_externType; + LinkageBodyAST::Node m_linkageBody; + DeclarationAST::Node m_declaration; + +private: + LinkageSpecificationAST( const LinkageSpecificationAST& source ); + void operator = ( const LinkageSpecificationAST& source ); +}; + +class NamespaceAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<NamespaceAST> Node; + enum { Type = NodeType_Namespace }; + + DECLARE_ALLOC( NamespaceAST ) + +public: + NamespaceAST(); + + AST* namespaceName() { return m_namespaceName.get(); } + void setNamespaceName( AST::Node& namespaceName ); + + LinkageBodyAST* linkageBody() { return m_linkageBody.get(); } + void setLinkageBody( LinkageBodyAST::Node& linkageBody ); + +private: + AST::Node m_namespaceName; + LinkageBodyAST::Node m_linkageBody; + +private: + NamespaceAST( const NamespaceAST& source ); + void operator = ( const NamespaceAST& source ); +}; + +class NamespaceAliasAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<NamespaceAliasAST> Node; + enum { Type = NodeType_NamespaceAlias }; + + DECLARE_ALLOC( NamespaceAliasAST ) + +public: + NamespaceAliasAST(); + + AST* namespaceName() { return m_namespaceName.get(); } + void setNamespaceName( AST::Node& name ); + + NameAST* aliasName() { return m_aliasName.get(); } + void setAliasName( NameAST::Node& name ); + +private: + AST::Node m_namespaceName; + NameAST::Node m_aliasName; + +private: + NamespaceAliasAST( const NamespaceAliasAST& source ); + void operator = ( const NamespaceAliasAST& source ); +}; + +class UsingAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<UsingAST> Node; + enum { Type = NodeType_Using }; + + DECLARE_ALLOC( UsingAST ) + +public: + UsingAST(); + + AST* typeName() { return m_typeName.get(); } + void setTypeName( AST::Node& typeName ); + + NameAST* name() { return m_name.get(); } + void setName( NameAST::Node& name ); + +private: + AST::Node m_typeName; + NameAST::Node m_name; + +private: + UsingAST( const UsingAST& source ); + void operator = ( const UsingAST& source ); +}; + +class UsingDirectiveAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<UsingDirectiveAST> Node; + enum { Type = NodeType_UsingDirective }; + + DECLARE_ALLOC( UsingDirectiveAST ) + +public: + UsingDirectiveAST(); + + NameAST* name() { return m_name.get(); } + void setName( NameAST::Node& name ); + +private: + NameAST::Node m_name; + +private: + UsingDirectiveAST( const UsingDirectiveAST& source ); + void operator = ( const UsingDirectiveAST& source ); +}; + +class DeclaratorAST: public AST +{ +public: + typedef AUTO_PTR<DeclaratorAST> Node; + enum { Type = NodeType_Declarator }; + + DECLARE_ALLOC( DeclaratorAST ) + +public: + DeclaratorAST(); + + QPtrList<AST> ptrOpList() { return m_ptrOpList; } + void addPtrOp( AST::Node& ptrOp ); + + DeclaratorAST* subDeclarator() { return m_subDeclarator.get(); } + void setSubDeclarator( AUTO_PTR<DeclaratorAST>& subDeclarator ); + + NameAST* declaratorId() { return m_declaratorId.get(); } + void setDeclaratorId( NameAST::Node& declaratorId ); + + AST* bitfieldInitialization() { return m_bitfieldInitialization.get(); } + void setBitfieldInitialization( AST::Node& bitfieldInitialization ); + + QPtrList<AST> arrayDimensionList() { return m_arrayDimensionList; } + void addArrayDimension( AST::Node& arrayDimension ); + + class ParameterDeclarationClauseAST* parameterDeclarationClause() { return m_parameterDeclarationClause.get(); } + void setParameterDeclarationClause( AUTO_PTR<class ParameterDeclarationClauseAST>& parameterDeclarationClause ); + + // ### replace 'constant' with cvQualify + AST* constant() { return m_constant.get(); } + void setConstant( AST::Node& constant ); + + GroupAST* exceptionSpecification() { return m_exceptionSpecification.get(); } + void setExceptionSpecification( GroupAST::Node& exceptionSpecification ); + +private: + QPtrList<AST> m_ptrOpList; + AUTO_PTR<DeclaratorAST> m_subDeclarator; + NameAST::Node m_declaratorId; + AST::Node m_bitfieldInitialization; + QPtrList<AST> m_arrayDimensionList; + AUTO_PTR<class ParameterDeclarationClauseAST> m_parameterDeclarationClause; + AST::Node m_constant; + GroupAST::Node m_exceptionSpecification; + +private: + DeclaratorAST( const DeclaratorAST& source ); + void operator = ( const DeclaratorAST& source ); +}; + +class ParameterDeclarationAST: public AST +{ +public: + typedef AUTO_PTR<ParameterDeclarationAST> Node; + enum { Type = NodeType_ParameterDeclaration }; + + DECLARE_ALLOC( ParameterDeclarationAST ) + +public: + ParameterDeclarationAST(); + + TypeSpecifierAST* typeSpec() { return m_typeSpec.get(); } + void setTypeSpec( TypeSpecifierAST::Node& typeSpec ); + + DeclaratorAST* declarator() { return m_declarator.get(); } + void setDeclarator( DeclaratorAST::Node& declarator ); + + AST* expression() { return m_expression.get(); } + void setExpression( AST::Node& expression ); + + virtual QString text() const; + +private: + TypeSpecifierAST::Node m_typeSpec; + DeclaratorAST::Node m_declarator; + AST::Node m_expression; + +private: + ParameterDeclarationAST( const ParameterDeclarationAST& source ); + void operator = ( const ParameterDeclarationAST& source ); +}; + +class ParameterDeclarationListAST: public AST +{ +public: + typedef AUTO_PTR<ParameterDeclarationListAST> Node; + enum { Type = NodeType_ParameterDeclarationList }; + + DECLARE_ALLOC( ParameterDeclarationListAST ) + +public: + ParameterDeclarationListAST(); + + QPtrList<ParameterDeclarationAST> parameterList() { return m_parameterList; } + void addParameter( ParameterDeclarationAST::Node& parameter ); + + virtual QString text() const; + +private: + QPtrList<ParameterDeclarationAST> m_parameterList; + +private: + ParameterDeclarationListAST( const ParameterDeclarationListAST& source ); + void operator = ( const ParameterDeclarationListAST& source ); +}; + +class ParameterDeclarationClauseAST: public AST +{ +public: + typedef AUTO_PTR<ParameterDeclarationClauseAST> Node; + enum { Type = NodeType_ParameterDeclarationClause }; + + DECLARE_ALLOC( ParameterDeclarationClauseAST ) + +public: + ParameterDeclarationClauseAST(); + + ParameterDeclarationListAST* parameterDeclarationList() { return m_parameterDeclarationList.get(); } + void setParameterDeclarationList( ParameterDeclarationListAST::Node& parameterDeclarationList ); + + AST* ellipsis() { return m_ellipsis.get(); } + void setEllipsis( AST::Node& ellipsis ); + + virtual QString text() const; + +private: + ParameterDeclarationListAST::Node m_parameterDeclarationList; + AST::Node m_ellipsis; + +private: + ParameterDeclarationClauseAST( const ParameterDeclarationClauseAST& source ); + void operator = ( const ParameterDeclarationClauseAST& source ); +}; + + +class InitDeclaratorAST: public AST +{ +public: + typedef AUTO_PTR<InitDeclaratorAST> Node; + enum { Type = NodeType_InitDeclarator }; + + DECLARE_ALLOC( InitDeclaratorAST ) + +public: + InitDeclaratorAST(); + + DeclaratorAST* declarator() { return m_declarator.get(); } + void setDeclarator( DeclaratorAST::Node& declarator ); + + AST* initializer() { return m_initializer.get(); } + void setInitializer( AST::Node& initializer ); + +private: + DeclaratorAST::Node m_declarator; + AST::Node m_initializer; + +private: + InitDeclaratorAST( const InitDeclaratorAST& source ); + void operator = ( const InitDeclaratorAST& source ); +}; + +class InitDeclaratorListAST: public AST +{ +public: + typedef AUTO_PTR<InitDeclaratorListAST> Node; + enum { Type = NodeType_InitDeclaratorList }; + + DECLARE_ALLOC( InitDeclaratorListAST ) + +public: + InitDeclaratorListAST(); + + QPtrList<InitDeclaratorAST> initDeclaratorList() { return m_initDeclaratorList; } + void addInitDeclarator( InitDeclaratorAST::Node& decl ); + +private: + QPtrList<InitDeclaratorAST> m_initDeclaratorList; + +private: + InitDeclaratorListAST( const InitDeclaratorListAST& source ); + void operator = ( const InitDeclaratorListAST& source ); +}; + +class TypedefAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<TypedefAST> Node; + enum { Type = NodeType_Typedef }; + + DECLARE_ALLOC( TypedefAST ) + +public: + TypedefAST(); + + TypeSpecifierAST* typeSpec() { return m_typeSpec.get(); } + void setTypeSpec( TypeSpecifierAST::Node& typeSpec ); + + InitDeclaratorListAST* initDeclaratorList() { return m_initDeclaratorList.get(); } + void setInitDeclaratorList( InitDeclaratorListAST::Node& initDeclaratorList ); + +private: + TypeSpecifierAST::Node m_typeSpec; + InitDeclaratorListAST::Node m_initDeclaratorList; + +private: + TypedefAST( const TypedefAST& source ); + void operator = ( const TypedefAST& source ); +}; + +class TemplateParameterAST: public AST +{ +public: + typedef AUTO_PTR<TemplateParameterAST> Node; + enum { Type = NodeType_TemplateParameter }; + + DECLARE_ALLOC( TemplateParameterAST ) + +public: + TemplateParameterAST(); + + TypeParameterAST* typeParameter() { return m_typeParameter.get(); } + void setTypeParameter( TypeParameterAST::Node& typeParameter ); + + ParameterDeclarationAST* typeValueParameter() { return m_typeValueParameter.get(); } + void setTypeValueParameter( ParameterDeclarationAST::Node& typeValueParameter ); + +private: + TypeParameterAST::Node m_typeParameter; + ParameterDeclarationAST::Node m_typeValueParameter; + +private: + TemplateParameterAST( const TemplateParameterAST& source ); + void operator = ( const TemplateParameterAST& source ); +}; + +class TemplateParameterListAST: public AST +{ +public: + typedef AUTO_PTR<TemplateParameterListAST> Node; + enum { Type = NodeType_TemplateParameterList }; + + DECLARE_ALLOC( TemplateParameterListAST ) + +public: + TemplateParameterListAST(); + + QPtrList<TemplateParameterAST> templateParameterList() { return m_templateParameterList; } + void addTemplateParameter( TemplateParameterAST::Node& templateParameter ); + +private: + QPtrList<TemplateParameterAST> m_templateParameterList; + +private: + TemplateParameterListAST( const TemplateParameterListAST& source ); + void operator = ( const TemplateParameterListAST& source ); +}; + +class TemplateDeclarationAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<TemplateDeclarationAST> Node; + enum { Type = NodeType_TemplateDeclaration }; + + DECLARE_ALLOC( TemplateDeclarationAST ) + +public: + TemplateDeclarationAST(); + + AST* exported() { return m_exported.get(); } + void setExported( AST::Node& exported ); + + TemplateParameterListAST* templateParameterList() { return m_templateParameterList.get(); } + void setTemplateParameterList( TemplateParameterListAST::Node& templateParameterList ); + + DeclarationAST* declaration() { return m_declaration.get(); } + void setDeclaration( DeclarationAST::Node& declaration ); + +private: + AST::Node m_exported; + TemplateParameterListAST::Node m_templateParameterList; + DeclarationAST::Node m_declaration; + +private: + TemplateDeclarationAST( const TemplateDeclarationAST& source ); + void operator = ( const TemplateDeclarationAST& source ); +}; + +class SimpleDeclarationAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<SimpleDeclarationAST> Node; + enum { Type = NodeType_SimpleDeclaration }; + + DECLARE_ALLOC( SimpleDeclarationAST ) + +public: + SimpleDeclarationAST(); + + GroupAST* functionSpecifier() { return m_functionSpecifier.get(); } + void setFunctionSpecifier( GroupAST::Node& functionSpecifier ); + + GroupAST* storageSpecifier() { return m_storageSpecifier.get(); } + void setStorageSpecifier( GroupAST::Node& storageSpecifier ); + + TypeSpecifierAST* typeSpec() { return m_typeSpec.get(); } + void setTypeSpec( TypeSpecifierAST::Node& typeSpec ); + + InitDeclaratorListAST* initDeclaratorList() { return m_initDeclaratorList.get(); } + void setInitDeclaratorList( InitDeclaratorListAST::Node& initDeclaratorList ); + + GroupAST* winDeclSpec() { return m_winDeclSpec.get(); } + void setWinDeclSpec( GroupAST::Node& winDeclSpec ); + +private: + GroupAST::Node m_functionSpecifier; + GroupAST::Node m_storageSpecifier; + TypeSpecifierAST::Node m_typeSpec; + InitDeclaratorListAST::Node m_initDeclaratorList; + GroupAST::Node m_winDeclSpec; + +private: + SimpleDeclarationAST( const SimpleDeclarationAST& source ); + void operator = ( const SimpleDeclarationAST& source ); +}; + +class StatementAST: public AST +{ +public: + typedef AUTO_PTR<StatementAST> Node; + enum { Type = NodeType_Statement }; + + DECLARE_ALLOC( StatementAST ) + +public: + StatementAST(); + +private: + StatementAST( const StatementAST& source ); + void operator = ( const StatementAST& source ); +}; + +class ExpressionStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<ExpressionStatementAST> Node; + enum { Type = NodeType_ExpressionStatement }; + + DECLARE_ALLOC( ExpressionStatementAST ) + +public: + ExpressionStatementAST(); + + AST* expression() { return m_expression.get(); } + void setExpression( AST::Node& expression ); + +private: + AST::Node m_expression; + +private: + ExpressionStatementAST( const ExpressionStatementAST& source ); + void operator = ( const ExpressionStatementAST& source ); +}; + +class ConditionAST: public AST +{ +public: + typedef AUTO_PTR<ConditionAST> Node; + enum { Type = NodeType_Condition }; + + DECLARE_ALLOC( ConditionAST ) + +public: + ConditionAST(); + + TypeSpecifierAST* typeSpec() { return m_typeSpec.get(); } + void setTypeSpec( TypeSpecifierAST::Node& typeSpec ); + + DeclaratorAST* declarator() { return m_declarator.get(); } + void setDeclarator( DeclaratorAST::Node& declarator ); + + AST* expression() { return m_expression.get(); } + void setExpression( AST::Node& expression ); + +private: + TypeSpecifierAST::Node m_typeSpec; + DeclaratorAST::Node m_declarator; + AST::Node m_expression; + +private: + ConditionAST( const ConditionAST& source ); + void operator = ( const ConditionAST& source ); +}; + +class IfStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<IfStatementAST> Node; + enum { Type = NodeType_IfStatement }; + + DECLARE_ALLOC( IfStatementAST ) + +public: + IfStatementAST(); + + ConditionAST* condition() const { return m_condition.get(); } + void setCondition( ConditionAST::Node& condition ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + + StatementAST* elseStatement() { return m_elseStatement.get(); } + void setElseStatement( StatementAST::Node& statement ); + +private: + ConditionAST::Node m_condition; + StatementAST::Node m_statement; + StatementAST::Node m_elseStatement; + +private: + IfStatementAST( const IfStatementAST& source ); + void operator = ( const IfStatementAST& source ); +}; + +class WhileStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<WhileStatementAST> Node; + enum { Type = NodeType_WhileStatement }; + + DECLARE_ALLOC( WhileStatementAST ) + +public: + WhileStatementAST(); + + ConditionAST* condition() const { return m_condition.get(); } + void setCondition( ConditionAST::Node& condition ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + +private: + ConditionAST::Node m_condition; + StatementAST::Node m_statement; + +private: + WhileStatementAST( const WhileStatementAST& source ); + void operator = ( const WhileStatementAST& source ); +}; + +class DoStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<DoStatementAST> Node; + enum { Type = NodeType_DoStatement }; + + DECLARE_ALLOC( DoStatementAST ) + +public: + DoStatementAST(); + + ConditionAST* condition() const { return m_condition.get(); } + void setCondition( ConditionAST::Node& condition ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + +private: + ConditionAST::Node m_condition; + StatementAST::Node m_statement; + +private: + DoStatementAST( const DoStatementAST& source ); + void operator = ( const DoStatementAST& source ); +}; + +class ForStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<ForStatementAST> Node; + enum { Type = NodeType_ForStatement }; + + DECLARE_ALLOC( ForStatementAST ) + +public: + ForStatementAST(); + + StatementAST* initStatement() { return m_initStatement.get(); } + void setInitStatement( StatementAST::Node& statement ); + + ConditionAST* condition() const { return m_condition.get(); } + void setCondition( ConditionAST::Node& condition ); + + AST* expression() const { return m_expression.get(); } + void setExpression( AST::Node& expression ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + +private: + ConditionAST::Node m_condition; + StatementAST::Node m_initStatement; + StatementAST::Node m_statement; + AST::Node m_expression; + +private: + ForStatementAST( const ForStatementAST& source ); + void operator = ( const ForStatementAST& source ); +}; + +// qt4 [erbsland] +class ForEachStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<ForEachStatementAST> Node; + enum { Type = NodeType_ForEachStatement }; + + DECLARE_ALLOC( ForEachStatementAST ) + +public: + ForEachStatementAST(); + + StatementAST* initStatement() { return m_initStatement.get(); } + void setInitStatement( StatementAST::Node& statement ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + + AST* expression() const { return m_expression.get(); } + void setExpression( AST::Node& expression ); + +private: + StatementAST::Node m_initStatement; + StatementAST::Node m_statement; + AST::Node m_expression; + +private: + ForEachStatementAST( const ForEachStatementAST& source ); + void operator = ( const ForEachStatementAST& source ); +}; + +class SwitchStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<SwitchStatementAST> Node; + enum { Type = NodeType_SwitchStatement }; + + DECLARE_ALLOC( SwitchStatementAST ) + +public: + SwitchStatementAST(); + + ConditionAST* condition() const { return m_condition.get(); } + void setCondition( ConditionAST::Node& condition ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + +private: + ConditionAST::Node m_condition; + StatementAST::Node m_statement; + +private: + SwitchStatementAST( const SwitchStatementAST& source ); + void operator = ( const SwitchStatementAST& source ); +}; + +class StatementListAST: public StatementAST +{ +public: + typedef AUTO_PTR<StatementListAST> Node; + enum { Type = NodeType_StatementList }; + + DECLARE_ALLOC( StatementListAST ) + +public: + StatementListAST(); + + QPtrList<StatementAST> statementList() { return m_statementList; } + void addStatement( StatementAST::Node& statement ); + +private: + QPtrList<StatementAST> m_statementList; + +private: + StatementListAST( const StatementListAST& source ); + void operator = ( const StatementListAST& source ); +}; + +class CatchStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<CatchStatementAST> Node; + enum { Type = NodeType_CatchStatement }; + + DECLARE_ALLOC( CatchStatementAST ) + +public: + CatchStatementAST(); + + ConditionAST* condition() const { return m_condition.get(); } + void setCondition( ConditionAST::Node& condition ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + +private: + ConditionAST::Node m_condition; + StatementAST::Node m_statement; + +private: + CatchStatementAST( const CatchStatementAST& source ); + void operator = ( const CatchStatementAST& source ); +}; + +class CatchStatementListAST: public StatementAST +{ +public: + typedef AUTO_PTR<CatchStatementListAST> Node; + enum { Type = NodeType_CatchStatementList }; + + DECLARE_ALLOC( CatchStatementListAST ) + +public: + CatchStatementListAST(); + + QPtrList<CatchStatementAST> statementList() { return m_statementList; } + void addStatement( CatchStatementAST::Node& statement ); + +private: + QPtrList<CatchStatementAST> m_statementList; + +private: + CatchStatementListAST( const CatchStatementListAST& source ); + void operator = ( const CatchStatementListAST& source ); +}; + +class TryBlockStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<TryBlockStatementAST> Node; + enum { Type = NodeType_TryBlockStatement }; + + DECLARE_ALLOC( TryBlockStatementAST ) + +public: + TryBlockStatementAST(); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + + CatchStatementListAST* catchStatementList() { return m_catchStatementList.get(); } + void setCatchStatementList( CatchStatementListAST::Node& statementList ); + +private: + StatementAST::Node m_statement; + CatchStatementListAST::Node m_catchStatementList; + +private: + TryBlockStatementAST( const TryBlockStatementAST& source ); + void operator = ( const TryBlockStatementAST& source ); +}; + +class DeclarationStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<DeclarationStatementAST> Node; + enum { Type = NodeType_DeclarationStatement }; + + DECLARE_ALLOC( DeclarationStatementAST ) + +public: + DeclarationStatementAST(); + + DeclarationAST* declaration() { return m_declaration.get(); } + void setDeclaration( DeclarationAST::Node& declaration ); + +private: + DeclarationAST::Node m_declaration; + +private: + DeclarationStatementAST( const DeclarationStatementAST& source ); + void operator = ( const DeclarationStatementAST& source ); +}; + +class FunctionDefinitionAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<FunctionDefinitionAST> Node; + enum { Type = NodeType_FunctionDefinition }; + + DECLARE_ALLOC( FunctionDefinitionAST ) + +public: + FunctionDefinitionAST(); + + GroupAST* functionSpecifier() { return m_functionSpecifier.get(); } + void setFunctionSpecifier( GroupAST::Node& functionSpecifier ); + + GroupAST* storageSpecifier() { return m_storageSpecifier.get(); } + void setStorageSpecifier( GroupAST::Node& storageSpecifier ); + + TypeSpecifierAST* typeSpec() { return m_typeSpec.get(); } + void setTypeSpec( TypeSpecifierAST::Node& typeSpec ); + + InitDeclaratorAST* initDeclarator() { return m_initDeclarator.get(); } + void setInitDeclarator( InitDeclaratorAST::Node& initDeclarator ); + + StatementListAST* functionBody() { return m_functionBody.get(); } + void setFunctionBody( StatementListAST::Node& functionBody ); + + GroupAST* winDeclSpec() { return m_winDeclSpec.get(); } + void setWinDeclSpec( GroupAST::Node& winDeclSpec ); + +private: + GroupAST::Node m_functionSpecifier; + GroupAST::Node m_storageSpecifier; + TypeSpecifierAST::Node m_typeSpec; + InitDeclaratorAST::Node m_initDeclarator; + StatementListAST::Node m_functionBody; + GroupAST::Node m_winDeclSpec; + +private: + FunctionDefinitionAST( const FunctionDefinitionAST& source ); + void operator = ( const FunctionDefinitionAST& source ); +}; + + +class TranslationUnitAST: public AST, public KShared +{ +public: + typedef KSharedPtr<TranslationUnitAST> Node; + enum { Type = NodeType_TranslationUnit }; + + DECLARE_ALLOC( TranslationUnitAST ) + +public: + TranslationUnitAST(); + + void addDeclaration( DeclarationAST::Node& ast ); + QPtrList<DeclarationAST> declarationList() { return m_declarationList; } + +private: + QPtrList<DeclarationAST> m_declarationList; + +private: + TranslationUnitAST( const TranslationUnitAST& source ); + void operator = ( const TranslationUnitAST& source ); +}; + +#endif diff --git a/lib/cppparser/cachemanager.cpp b/lib/cppparser/cachemanager.cpp new file mode 100644 index 00000000..966f4440 --- /dev/null +++ b/lib/cppparser/cachemanager.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + copyright : (C) 2006 by David Nolden + email : david.nolden.kdevelop@art-master.de +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "cachemanager.h" +#include <kdebug.h> + +void CacheNode::access() const { + m_manager->access( this ); +} + +void CacheManager::remove( const CacheNode* node ) { + m_set.erase( node ); +} + +void CacheManager::add( const CacheNode* node ) { + m_set.insert( node ); +} + +CacheNode::CacheNode( Manager* manager ) : m_manager( manager ), m_value(manager->currentMax()) { //initialize m_value with the current maximum, so the new node has a chance even in a cache full of high-rated nodes + m_manager->add( this ); +} + +CacheNode::~CacheNode() { + m_manager->remove( this ); +}; + +void CacheManager::restart( uint normalizeby ) { + m_currentFrame = 1; + m_currentMax = 1; + SetType oldSet = m_set; + m_set = SetType(); + for( SetType::iterator it = oldSet.begin(); it != oldSet.end(); ++it ) { + int newValue = (*it)->value() / ( normalizeby / 1000 ); + if( newValue > m_currentMax ) m_currentMax = newValue; + (*it)->setValue( newValue ); ///This way not all information is discarded + m_set.insert( *it ); + } +} + +void CacheManager::access( const CacheNode* node ) { + static const int limit = (std::numeric_limits<uint>::max() / 3)*2; + m_set.erase( node ); + node->setValue( m_currentMax+1 ); + m_set.insert( node ); + if( node->value() > m_currentMax ) + m_currentMax = node->value(); + if( node->value() > limit ) + restart( node->value() ); +} + +void CacheManager::setMaxNodes ( int maxNodes ) { + m_maxNodes = maxNodes; + increaseFrame(); +} + +void CacheManager::increaseFrame() { + m_currentFrame ++; + if( m_set.size() > m_maxNodes ) { + //kdDebug( 9007 ) << "CacheManager: Have " << m_set.size() << " nodes, maximum is " << m_maxNodes << ", erasing." << endl; + int mustErase = m_set.size() - m_maxNodes; + while( !m_set.empty() && mustErase != 0 ) { + --mustErase; + SetType::iterator it = m_set.begin(); + erase( *it ); + } + //kdDebug( 9007 ) << "CacheManager: Have " << m_set.size() << " nodes after erasing." << endl; + } +} + +void CacheManager::removeLowerHalf() { + int maxNodes = m_maxNodes; + setMaxNodes( m_set.size() / 2 ); + setMaxNodes( maxNodes ); +} + +void CacheManager::saveMemory() { + removeLowerHalf(); +} diff --git a/lib/cppparser/cachemanager.h b/lib/cppparser/cachemanager.h new file mode 100644 index 00000000..3804356f --- /dev/null +++ b/lib/cppparser/cachemanager.h @@ -0,0 +1,102 @@ + +/*************************************************************************** + copyright : (C) 2006 by David Nolden + email : david.nolden.kdevelop@art-master.de +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H +#include <cstdlib> +#include <set> +#include <limits> + +class CacheManager; + + +class CacheNode { + typedef CacheManager Manager; + public: + CacheNode( Manager* manager ); + + virtual ~CacheNode(); + + void access() const; + + inline uint value() const { + return m_value; + } + + inline void setValue( const uint v ) const { + m_value = v; + } + + inline void addValue( const uint diff ) const { + m_value += diff; + } + + private: + Manager* m_manager; + mutable uint m_value; //This value stands for the priority of the node(higher is better) +}; + +class CacheNodeCompare { + public: + bool operator() ( const CacheNode* lhs, const CacheNode* rhs ) const { + if( lhs->value() != rhs->value() ) + return lhs->value() < rhs->value(); + else + return lhs < rhs; //To be able to identify nodes precisely + } +}; + + +class CacheManager { + typedef std::set< const CacheNode*, CacheNodeCompare > SetType; + public: + CacheManager( int maxNodes = 1000 ) : m_currentFrame(1), m_maxNodes( maxNodes ), m_currentMax(1) { + }; + + inline int currentFrame() const { + return m_currentFrame; + } + + void access( const CacheNode* node ); + + ///Can be used from outside to set the maximum count of nodes to keep. + void setMaxNodes ( int maxNodes ); + + void increaseFrame(); + + ///Can be used on a regular basis(time-triggered) to save memory: Removes half of all triggered + void removeLowerHalf(); + + virtual void saveMemory(); + + int currentMax() const { + return m_currentMax; + } + + ///This triggered function should erase the given node. + virtual void erase( const CacheNode* node ) = 0; + private: + void restart( uint normalizeby ); + friend class CacheNode; + void remove( const CacheNode* node ); + void add( const CacheNode* node ); + int m_currentFrame; + int m_maxNodes; + int m_currentMax; + SetType m_set; +}; + + +#endif diff --git a/lib/cppparser/driver.cpp b/lib/cppparser/driver.cpp new file mode 100644 index 00000000..c5614d3e --- /dev/null +++ b/lib/cppparser/driver.cpp @@ -0,0 +1,965 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2006 David Nolden <david.nolden.kdevelop@art-master.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#define CACHELEXER + +#include "driver.h" +#include "lexer.h" +#include "parser.h" +#include <kdebug.h> +#include <klocale.h> +#include <stdlib.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qdir.h> +#include <qdatastream.h> +#include <qbuffer.h> +#include <assert.h> + +#include <iostream> + + +// void Macro::read( QDataStream& stream ) { +// stream >> m_idHashValid; +// stream >> m_valueHashValid; +// stream >> m_idHash; +// stream >> m_valueHash; +// stream >> m_name; +// stream >> m_body; +// stream >> m_fileName; +// stream >> m_hasArguments; +// stream >> m_argumentList; +// } +// +// void Macro::write( QDataStream& stream ) const { +// stream << m_idHashValid; +// stream << m_valueHashValid; +// stream << m_idHash; +// stream << m_valueHash; +// stream << m_name; +// stream << m_body; +// stream << m_fileName; +// stream << m_hasArguments; +// stream << m_argumentList; +// } + + +class IntIncreaser { + public: + IntIncreaser( int& i ) : m_i( i ) { + ++m_i; + } + ~IntIncreaser() { + --m_i; + } + private: + int& m_i; +}; + +class DefaultSourceProvider: public SourceProvider { + public: + DefaultSourceProvider() {} + + virtual QString contents( const QString& fileName ) { + QString source; + + QFile f( fileName ); + if ( f.open( IO_ReadOnly ) ) { + QTextStream s( &f ); + source = s.read(); + f.close(); + } + return source; + } + + virtual bool isModified( const QString& fileName ) { + Q_UNUSED( fileName ); + return true; + } + + private: + DefaultSourceProvider( const DefaultSourceProvider& source ); + void operator = ( const DefaultSourceProvider& source ); +}; + + +Driver::Driver() + : depresolv( FALSE ), lexer( 0 ), m_lexerCache( this ), m_dependenceDepth( 0 ), m_maxDependenceDepth( 20 ) { + m_sourceProvider = new DefaultSourceProvider(); +} + +Driver::~Driver() { + reset(); + delete m_sourceProvider; +} + +void Driver::setMaxDependenceDepth( int depth ) { + m_maxDependenceDepth = depth; +} + +SourceProvider* Driver::sourceProvider() { + return m_sourceProvider; +} + +void Driver::setSourceProvider( SourceProvider* sourceProvider ) { + delete m_sourceProvider; + m_sourceProvider = sourceProvider; +} + +void Driver::reset( ) { + m_lexerCache.clear(); + m_dependences.clear(); + m_macros.clear(); + m_problems.clear(); + m_includePaths.clear(); + + while ( m_parsedUnits.size() ) { + //TranslationUnitAST* unit = **m_parsedUnits.begin(); + m_parsedUnits.remove( m_parsedUnits.begin() ); + //delete( unit ); + } +} + +QStringList Driver::getCustomIncludePath( const QString& ) { + return includePaths(); +} + +void Driver::remove + ( const QString & fileName ) { + m_dependences.remove( fileName ); + m_problems.remove( fileName ); + if( !isResolveDependencesEnabled() ) + removeAllMacrosInFile( fileName ); + + QMap<QString, ParsedFilePointer>::Iterator it = m_parsedUnits.find( fileName ); + if ( it != m_parsedUnits.end() ) { + //TranslationUnitAST * unit = **it; + m_parsedUnits.remove( it ); + //delete( unit ); + } +} + +void Driver::removeAllMacrosInFile( const QString& fileName ) { + MacroMap::iterator it = m_macros.begin(); + while ( it != m_macros.end() ) { + Macro m = ( *it ).second; + if ( m.fileName() == fileName ) { + m_macros.erase( it++ ); + } else { + ++it; + } + } +} + +void Driver::usingString( const HashedString& str ) { + #ifdef CACHELEXER + if( m_currentLexerCache ) { + m_currentLexerCache->addString( m_lexerCache.unifyString( str ) ); + } + #endif +} + +bool Driver::hasMacro( const HashedString& name ) { + std::pair< MacroMap::const_iterator, MacroMap::const_iterator > range = m_macros.equal_range( name ); + if ( range.first == range.second ) { + return false; + } else { + const Macro& m( ( *( --range.second ) ).second ); + if ( m.isUndef() ) + return false; + else + return true; + } + return false; +} + +QString deepCopy( const QString& str ) { + return str; + //return str.ascii(); +} + +const Macro& Driver::macro( const HashedString& name ) const { + std::pair< MacroMap::const_iterator, MacroMap::const_iterator > range = m_macros.equal_range( name ); + if ( range.first == range.second ) { + return ( *const_cast<MacroMap&>( m_macros ).insert( std::make_pair( deepCopy( name.str() ), Macro() ) ) ).second; ///Since we need to return a reference, there's no other way. + } else { + return ( *( --range.second ) ).second; + } +} +Macro& Driver::macro( const HashedString& name ) { + std::pair< MacroMap::iterator, MacroMap::iterator > range = m_macros.equal_range( name ); + if ( range.first == range.second ) { + return ( *m_macros.insert( std::make_pair( deepCopy( name.str() ), Macro() ) ) ).second; + } else { + return ( *( --range.second ) ).second; + } +} + +void Driver::addMacro( const Macro & macro ) { + std::pair< MacroMap::iterator, MacroMap::iterator > range = m_macros.equal_range( macro.name() ); + + if ( range.first == range.second ) { + m_macros.insert( std::make_pair( deepCopy( macro.name() ), macro ) ); + } else { + ///Insert behind the other macros + m_macros.insert( range.second, std::make_pair( deepCopy( macro.name() ), macro ) ); + Macro cp = this->macro( macro.name() ); + assert( macro == cp ); + } + +#ifdef CACHELEXER + if( m_currentLexerCache ) + m_currentLexerCache->addDefinedMacro( macro ); +#endif +} + +void Driver::removeMacro( const HashedString& macroName ) { + std::pair< MacroMap::iterator, MacroMap::iterator > range = m_macros.equal_range( macroName ); + if ( range.first != range.second ) { + m_macros.erase( --range.second ); + } +} + +ParsedFilePointer Driver::takeTranslationUnit( const QString& fileName ) { + QMap<QString, ParsedFilePointer>::Iterator it = m_parsedUnits.find( fileName ); + ParsedFilePointer unit( *it ); + //m_parsedUnits.remove( it ); + m_parsedUnits[ fileName ] = 0; + return unit; +} + +void Driver::takeTranslationUnit( const ParsedFile& file ) { + QMap<QString, ParsedFilePointer>::Iterator it = m_parsedUnits.find( file.fileName() ); + m_parsedUnits[ file.fileName() ] = 0; +} + +ParsedFilePointer Driver::translationUnit( const QString& fileName ) const { + QMap<QString, ParsedFilePointer>::ConstIterator it = m_parsedUnits.find( fileName ); + return it != m_parsedUnits.end() ? *it : 0; +} + +class Driver::ParseHelper { + public: + ParseHelper( const QString& fileName, bool force, Driver* driver, bool reportMessages = true, QString includedFrom = QString() ) : m_wasReset( false ), m_fileName( fileName ), m_previousFileName( driver->m_currentFileName ), m_previousLexer( driver->lexer ), m_previousParsedFile( driver->m_currentParsedFile ), m_previousCachedLexedFile( driver->m_currentLexerCache ), m_force( force ), m_driver( driver ), m_lex( m_driver ) { + QFileInfo fileInfo( fileName ); + m_driver->m_currentParsedFile = new ParsedFile( fileName, fileInfo.lastModified() ); + if( !includedFrom.isEmpty() ) + m_driver->m_currentParsedFile->setIncludedFrom( includedFrom ); +#ifdef CACHELEXER + m_driver->m_currentLexerCache = new CachedLexedFile( fileName, &m_driver->m_lexerCache ); +#endif + m_absFilePath = fileInfo.absFilePath(); + + QMap<QString, ParsedFilePointer>::Iterator it = m_driver->m_parsedUnits.find( m_absFilePath ); + + if ( force && it != m_driver->m_parsedUnits.end() ) { + m_driver->takeTranslationUnit( m_absFilePath ); + } else if ( it != m_driver->m_parsedUnits.end() && *it != 0 ) { + // file already processed + return ; + } + + CachedLexedFilePointer lexedFileP = m_driver->m_lexerCache.lexedFile( HashedString( fileName ) ); + + m_driver->m_dependences.remove( fileName ); + m_driver->m_problems.remove( fileName ); + + driver->m_currentFileName = fileName; + + m_driver->lexer = &m_lex; + m_driver->setupLexer( &m_lex ); + + m_lex.setReportMessages( reportMessages ); + + //kdDebug( 9007 ) << "lexing file " << fileName << endl; + m_lex.setSource( m_driver->sourceProvider() ->contents( fileName ) ); + if(m_previousCachedLexedFile) + m_previousCachedLexedFile->merge( *m_driver->m_currentLexerCache ); + else + m_driver->findOrInsertProblemList( m_driver->m_currentMasterFileName ) += m_driver->m_currentLexerCache->problems(); + + if( !lexedFileP && m_previousParsedFile ) //only add the new cache-instance if a fitting isn't already stored, and if this file was included by another one. + m_driver->m_lexerCache.addLexedFile( m_driver->m_currentLexerCache ); + + //Copy the recursive include-files into the ParsedFile + m_driver->m_currentParsedFile->addIncludeFiles( m_driver->m_currentLexerCache->includeFiles() ); + m_driver->m_currentParsedFile->setSkippedLines( m_lex.skippedLines() ); + } + + void parse() { + QString oldMasterFileName = m_driver->m_currentMasterFileName; //Change the master-file so problems will be reported correctly + m_driver->m_currentMasterFileName = m_absFilePath; + + CachedLexedFilePointer lf = m_driver->m_currentLexerCache; //Set the lexer-cache to zero, so the problems registered through addProblem go directly into the file + m_driver->m_currentLexerCache = 0; + + Parser parser( m_driver, m_driver->lexer ); + m_driver->setupParser( &parser ); + + TranslationUnitAST::Node unit; + parser.parseTranslationUnit( unit ); + m_driver->m_currentParsedFile->setTranslationUnit( unit ); + m_driver->m_parsedUnits.insert( m_fileName, m_driver->m_currentParsedFile ); + m_driver->fileParsed( *m_driver->m_currentParsedFile ); + + m_driver->m_currentLexerCache = lf; + + m_driver->m_currentMasterFileName = oldMasterFileName; + } + + ParsedFilePointer parsedFile() const { + return m_driver->m_currentParsedFile; + } + + void reset() { + if ( !m_wasReset ) { + m_driver->m_currentFileName = m_previousFileName; + m_driver->lexer = m_previousLexer; + m_driver->m_currentParsedFile = m_previousParsedFile; + m_driver->m_currentLexerCache = m_previousCachedLexedFile; + if( m_driver->m_currentLexerCache == 0 ) { + + } + + m_wasReset = true; + } + } + + ~ParseHelper() { + reset(); + } + + + private: + bool m_wasReset; + QString m_fileName; + QString m_absFilePath; + QString m_previousFileName; + Lexer* m_previousLexer; + ParsedFilePointer m_previousParsedFile; + CachedLexedFilePointer m_previousCachedLexedFile; + bool m_force; + Driver* m_driver; + Lexer m_lex; +}; + + +void Driver::addDependence( const QString & fileName, const Dependence & dep ) { + + // this can happen if the parser was invoked on a snippet of text and not a file + if ( fileName.isEmpty() || !m_currentParsedFile ) + return; + + //@todo prevent cyclic dependency-loops + QFileInfo fileInfo( dep.first ); + QString fn = fileInfo.absFilePath(); + + if ( !depresolv ) { + findOrInsertDependenceList( fileName ).insert( fn, dep ); + m_currentParsedFile->addIncludeFile( dep.first, 0, dep.second == Dep_Local ); + return ; + } + + QString file = findIncludeFile( dep ); + + findOrInsertDependenceList( fileName ).insert( file, dep ); + m_currentParsedFile->addIncludeFile( file, 0, dep.second == Dep_Local ); + + if ( !QFile::exists( file ) ) { + Problem p( i18n( "Could not find include file %1" ).arg( dep.first ), + lexer ? lexer->currentLine() : -1, + lexer ? lexer->currentColumn() : -1, Problem::Level_Warning ); + addProblem( fileName, p ); + return ; + } + + if( m_currentLexerCache ) + m_currentLexerCache->addIncludeFile( file, QDateTime() ); ///The time will be overwritten in CachedLexedFile::merge(...) + + /**What should be done: + * 1. Lex the file to get all the macros etc. + * 2. TODO: Check what previously set macros the file was affected by, and compare those macros to any previously parsed instances of this file. + * 2.1 If there is a parse-instance of the file where all macros that played a role had the same values, we do not need to reparse this file. + * 2.2 If there is no such fitting instance, the file needs to be parsed and put to the code-model. + * + * It'll be necessary to have multiple versions of one file in the code-model. + */ + + IntIncreaser i( m_dependenceDepth ); + if( m_dependenceDepth > m_maxDependenceDepth ) { + //kdDebug( 9007 ) << "maximum dependence-depth of " << m_maxDependenceDepth << " was reached, " << fileName << " will not be processed" << endl; + return; + } + + CachedLexedFilePointer lexedFileP = m_lexerCache.lexedFile( HashedString( file ) ); + if( lexedFileP ) { + CachedLexedFile& lexedFile( *lexedFileP ); + m_currentLexerCache->merge( lexedFile ); //The ParseHelper will will copy the include-files into the result later + for( MacroSet::Macros::const_iterator it = lexedFile.definedMacros().macros().begin(); it != lexedFile.definedMacros().macros().end(); ++it ) { + addMacro( (*it) ); + } + ///@todo fill usingMacro(...) + return; + } + + ParseHelper h( file, true, this, false, m_currentMasterFileName ); + + /*if ( m_parsedUnits.find(file) != m_parsedUnits.end() ) + return;*/ + + if( shouldParseIncludedFile( m_currentParsedFile ) ) ///Until the ParseHelper is destroyed, m_currentParsedFile will stay the included file + h.parse(); +} + +void Driver::addProblem( const QString & fileName, const Problem & problem ) { + Problem p( problem ); + p.setFileName( fileName ); + + if( m_currentLexerCache ) + m_currentLexerCache->addProblem( p ); + else + findOrInsertProblemList( m_currentMasterFileName ).append( problem ); +} + +QMap< QString, Dependence >& Driver::findOrInsertDependenceList( const QString & fileName ) { + QMap<QString, QMap<QString, Dependence> >::Iterator it = m_dependences.find( fileName ); + if ( it != m_dependences.end() ) + return it.data(); + + QMap<QString, Dependence> l; + m_dependences.insert( deepCopy( fileName ), l ); + return m_dependences[ fileName ]; +} + +QValueList < Problem >& Driver::findOrInsertProblemList( const QString & fileName ) { + QMap<QString, QValueList<Problem> >::Iterator it = m_problems.find( fileName ); + if ( it != m_problems.end() ) + return it.data(); + + QValueList<Problem> l; + m_problems.insert( fileName, l ); + return m_problems[ fileName ]; +} + +QMap< QString, Dependence > Driver::dependences( const QString & fileName ) const { + QMap<QString, QMap<QString, Dependence> >::ConstIterator it = m_dependences.find( fileName ); + if ( it != m_dependences.end() ) + return it.data(); + return QMap<QString, Dependence>(); +} + +const Driver::MacroMap& Driver::macros() const { + return m_macros; +} + +void Driver::insertMacros( const MacroSet& macros ) { + for( MacroSet::Macros::const_iterator it = macros.m_usedMacros.begin(); it != macros.m_usedMacros.end(); ++it ) { + addMacro( *it ); + } +} + +QValueList < Problem > Driver::problems( const QString & fileName ) const { + QMap<QString, QValueList<Problem> >::ConstIterator it = m_problems.find( fileName ); + if ( it != m_problems.end() ) + return it.data(); + return QValueList<Problem>(); +} + +void Driver::clearMacros() { + m_macros.clear(); +} + +void Driver::clearParsedMacros() { + //Keep global macros + for( MacroMap::iterator it = m_macros.begin(); it != m_macros.end(); ) { + if( !(*it).second.fileName().isEmpty() ) { + m_macros.erase( it++ ); + } else { + ++it; + } + } +} + +void Driver::parseFile( const QString& fileName, bool onlyPreProcess, bool force , bool macrosGlobal ) +{ + QString oldMasterFileName = m_currentMasterFileName; + m_currentMasterFileName = fileName; + + //if( isResolveDependencesEnabled() ) + clearParsedMacros(); ///Since everything will be re-lexed, we do not need any old macros + + m_lexerCache.increaseFrame(); + + //Remove the problems now instead of in ParseHelper, because this way the problems reported by getCustomIncludePath(...) will not be discarded + m_problems.remove( fileName ); + + QStringList oldIncludePaths = m_includePaths; + m_includePaths = getCustomIncludePath( fileName ); + + ParseHelper p( fileName, force, this ); + if( !onlyPreProcess ){ + p.parse(); + } + if( macrosGlobal ) { + for( MacroMap::iterator it = m_macros.begin(); it != m_macros.end(); ++it) { + if( (*it).second.fileName() == fileName ) { + (*it).second.setFileName( QString::null ); + } + } + } + + m_includePaths = oldIncludePaths; + m_currentMasterFileName = oldMasterFileName; +} + +void Driver::setupLexer( Lexer * lexer ) { + // stl + lexer->addSkipWord( "__STL_BEGIN_NAMESPACE" ); + lexer->addSkipWord( "__STL_END_NAMESPACE" ); + lexer->addSkipWord( "__STL_BEGIN_RELOPS_NAMESPACE" ); + lexer->addSkipWord( "__STL_END_RELOPS_NAMESPACE" ); + lexer->addSkipWord( "__STL_TEMPLATE_NULL" ); + lexer->addSkipWord( "__STL_TRY" ); + lexer->addSkipWord( "__STL_UNWIND" ); + lexer->addSkipWord( "__STL_NOTHROW" ); + lexer->addSkipWord( "__STL_NULL_TMPL_ARGS" ); + lexer->addSkipWord( "__STL_UNWIND", SkipWordAndArguments ); + lexer->addSkipWord( "__GC_CONST" ); + lexer->addSkipWord( "__HASH_ALLOC_INIT", SkipWordAndArguments ); + lexer->addSkipWord( "__STL_DEFAULT_ALLOCATOR", SkipWordAndArguments, "T" ); + lexer->addSkipWord( "__STL_MUTEX_INITIALIZER" ); + lexer->addSkipWord( "__STL_NULL_TMPL_ARGS" ); + + // antlr + lexer->addSkipWord( "ANTLR_BEGIN_NAMESPACE", SkipWordAndArguments ); + lexer->addSkipWord( "ANTLR_USE_NAMESPACE", SkipWordAndArguments ); + lexer->addSkipWord( "ANTLR_USING_NAMESPACE", SkipWordAndArguments ); + lexer->addSkipWord( "ANTLR_END_NAMESPACE" ); + lexer->addSkipWord( "ANTLR_C_USING", SkipWordAndArguments ); + lexer->addSkipWord( "ANTLR_API" ); + + // gnu + lexer->addSkipWord( "__extension__", SkipWordAndArguments ); + lexer->addSkipWord( "__attribute__", SkipWordAndArguments ); + lexer->addSkipWord( "__BEGIN_DECLS" ); + lexer->addSkipWord( "__END_DECLS" ); + lexer->addSkipWord( "__THROW" ); + lexer->addSkipWord( "__restrict" ); + lexer->addSkipWord( "__restrict__" ); + lexer->addSkipWord( "__attribute_pure__" ); + lexer->addSkipWord( "__attribute_malloc__" ); + lexer->addSkipWord( "__attribute_format_strfmon__" ); + lexer->addSkipWord( "__asm__", SkipWordAndArguments ); + lexer->addSkipWord( "__devinit" ); + lexer->addSkipWord( "__devinit__" ); + lexer->addSkipWord( "__init" ); + lexer->addSkipWord( "__init__" ); + lexer->addSkipWord( "__signed" ); + lexer->addSkipWord( "__signed__" ); + lexer->addSkipWord( "__unsigned" ); + lexer->addSkipWord( "__unsigned__" ); + lexer->addSkipWord( "asmlinkage" ); + lexer->addSkipWord( "____cacheline_aligned" ); + lexer->addSkipWord( "__glibcpp_class_requires", SkipWordAndArguments ); + lexer->addSkipWord( "__glibcpp_class2_requires", SkipWordAndArguments ); + lexer->addSkipWord( "__glibcpp_class4_requires", SkipWordAndArguments ); + lexer->addSkipWord( "__glibcpp_function_requires", SkipWordAndArguments ); + lexer->addSkipWord( "restrict" ); + + lexer->addSkipWord( "__BEGIN_NAMESPACE_STD" ); + lexer->addSkipWord( "__END_NAMESPACE_STD" ); + lexer->addSkipWord( "__BEGIN_NAMESPACE_C99" ); + lexer->addSkipWord( "__END_NAMESPACE_C99" ); + lexer->addSkipWord( "__USING_NAMESPACE_STD", SkipWordAndArguments ); + + // kde + lexer->addSkipWord( "K_SYCOCATYPE", SkipWordAndArguments ); + lexer->addSkipWord( "EXPORT_DOCKCLASS" ); + lexer->addSkipWord( "K_EXPORT_COMPONENT_FACTORY", SkipWordAndArguments ); + lexer->addSkipWord( "K_SYCOCAFACTORY", SkipWordAndArguments ); + lexer->addSkipWord( "KDE_DEPRECATED" ); + + // qt + lexer->addSkipWord( "Q_OBJECT" ); + lexer->addSkipWord( "Q_OVERRIDE", SkipWordAndArguments ); + lexer->addSkipWord( "Q_ENUMS", SkipWordAndArguments ); + lexer->addSkipWord( "Q_PROPERTY", SkipWordAndArguments ); + lexer->addSkipWord( "Q_CLASSINFO", SkipWordAndArguments ); + lexer->addSkipWord( "Q_SETS", SkipWordAndArguments ); + lexer->addSkipWord( "Q_UNUSED", SkipWordAndArguments ); + lexer->addSkipWord( "Q_CREATE_INSTANCE", SkipWordAndArguments ); + lexer->addSkipWord( "Q_DUMMY_COMPARISON_OPERATOR", SkipWordAndArguments ); + lexer->addSkipWord( "ACTIVATE_SIGNAL_WITH_PARAM", SkipWordAndArguments ); + lexer->addSkipWord( "Q_INLINE_TEMPLATES" ); + lexer->addSkipWord( "Q_TEMPLATE_EXTERN" ); + lexer->addSkipWord( "Q_TYPENAME" ); + lexer->addSkipWord( "Q_REFCOUNT" ); + lexer->addSkipWord( "Q_EXPLICIT" ); + lexer->addSkipWord( "QMAC_PASCAL" ); + lexer->addSkipWord( "QT_STATIC_CONST" ); + lexer->addSkipWord( "QT_STATIC_CONST_IMPL" ); + lexer->addSkipWord( "QT_WIN_PAINTER_MEMBERS" ); + lexer->addSkipWord( "QT_NC_MSGBOX" ); + lexer->addSkipWord( "Q_VARIANT_AS", SkipWordAndArguments ); + lexer->addSkipWord( "CALLBACK_CALL_TYPE" ); + + // qt4 [erbsland] + lexer->addSkipWord( "Q_DECLARE_FLAGS", SkipWordAndArguments ); + lexer->addSkipWord( "Q_DECLARE_OPERATORS_FOR_FLAGS", SkipWordAndArguments ); + + // flex + lexer->addSkipWord( "yyconst" ); + lexer->addSkipWord( "YY_RULE_SETUP" ); + lexer->addSkipWord( "YY_BREAK" ); + lexer->addSkipWord( "YY_RESTORE_YY_MORE_OFFSET" ); + + // gtk + lexer->addSkipWord( "G_BEGIN_DECLS" ); + lexer->addSkipWord( "G_END_DECLS" ); + lexer->addSkipWord( "G_GNUC_CONST" ); + lexer->addSkipWord( "G_CONST_RETURN" ); + lexer->addSkipWord( "GTKMAIN_C_VAR" ); + lexer->addSkipWord( "GTKVAR" ); + lexer->addSkipWord( "GDKVAR" ); + lexer->addSkipWord( "G_GNUC_PRINTF", SkipWordAndArguments ); + + // windows + lexer->addSkipWord( "WINAPI" ); + lexer->addSkipWord( "__stdcall" ); + lexer->addSkipWord( "__cdecl" ); + lexer->addSkipWord( "_cdecl" ); + lexer->addSkipWord( "CALLBACK" ); + + // gcc extensions + if( !hasMacro( "__asm__" ) ) addMacro( Macro( "__asm__", "asm" ) ); + if( !hasMacro( "__inline" ) ) addMacro( Macro( "__inline", "inline" ) ); + if( !hasMacro( "__inline__" ) ) addMacro( Macro( "__inline__", "inline" ) ); + if( !hasMacro( "__const" ) ) addMacro( Macro( "__const", "const" ) ); + if( !hasMacro( "__const__" ) ) addMacro( Macro( "__const__", "const" ) ); + if( !hasMacro( "__volatile__" ) ) addMacro( Macro( "__volatile__", "volatile" ) ); + if( !hasMacro( "__complex__" ) ) addMacro( Macro( "__complex__", "" ) ); +} + +void Driver::setupParser( Parser * parser ) { + Q_UNUSED( parser ); +} + +void Driver::clearIncludePaths() { + m_includePaths.clear(); +} + +void Driver::addIncludePath( const QString &path ) { + if ( !path.stripWhiteSpace().isEmpty() ) + m_includePaths << path; +} + +QString Driver::findIncludeFile( const Dependence& dep, const QString& fromFile ) { + QString fileName = dep.first; + + if ( dep.second == Dep_Local ) { + QString path = QFileInfo( fromFile ).dirPath( true ); + QFileInfo fileInfo( QFileInfo( path, fileName ) ); + if ( fileInfo.exists() && fileInfo.isFile() ) + return fileInfo.absFilePath(); + } + + QStringList includePaths = getCustomIncludePath( fromFile ); + + for ( QStringList::ConstIterator it = includePaths.begin(); it != includePaths.end(); ++it ) { + QFileInfo fileInfo( *it, fileName ); + if ( fileInfo.exists() && fileInfo.isFile() ) + return fileInfo.absFilePath(); + } + + return QString::null; +} + +QString Driver::findIncludeFile( const Dependence& dep ) const { + QString fileName = dep.first; + + if ( dep.second == Dep_Local ) { + QString path = QFileInfo( currentFileName() ).dirPath( true ); + QFileInfo fileInfo( QFileInfo( path, fileName ) ); + if ( fileInfo.exists() && fileInfo.isFile() ) + return fileInfo.absFilePath(); + } + + for ( QStringList::ConstIterator it = m_includePaths.begin(); it != m_includePaths.end(); ++it ) { + QFileInfo fileInfo( *it, fileName ); + if ( fileInfo.exists() && fileInfo.isFile() ) + return fileInfo.absFilePath(); + } + + return QString::null; +} + +void Driver::setResolveDependencesEnabled( bool enabled ) { + depresolv = enabled; + if ( depresolv ) + setupPreProcessor(); +} + +bool Driver::shouldParseIncludedFile( const ParsedFilePointer& /*file*/) { + return false; +} + +void Driver::setupPreProcessor() {} + +void Driver::fileParsed( ParsedFile & fileName ) { + Q_UNUSED( fileName ); +} + +void Driver::usingMacro( const Macro& macro ) { + if( m_currentParsedFile ) + m_currentParsedFile->usedMacros().addMacro( macro ); +#ifdef CACHELEXER + if( m_currentLexerCache ) + m_currentLexerCache->addUsedMacro( macro ); +#endif +} + +// void Macro::computeHash() const { +// m_idHash = 7 * ( HashedString::hashString( m_name ) + m_argumentList.count() * 13 ); +// int a = 1; +// m_idHash += 31 * m_argumentList.count(); +// +// m_valueHash = 27 * ( HashedString::hashString( m_body ) + (m_isUndefMacro ? 1 : 0 ) ); +// +// for( QValueList<Argument>::const_iterator it = m_argumentList.begin(); it != m_argumentList.end(); ++it ) { +// a *= 19; +// m_valueHash += a * HashedString::hashString( *it ); +// } +// m_valueHashValid = true; +// m_idHashValid = true; +// } + +// MacroSet::MacroSet() : m_idHashValid( false ), m_valueHashValid( false ) { +// } + +void MacroSet::addMacro( const Macro& macro ) { + std::pair<Macros::const_iterator, bool> r = m_usedMacros.insert( macro ); + if( !r.second ) { + //Make sure the macro added later will be used + m_usedMacros.erase( r.first ); + m_usedMacros.insert( macro ); + } + + m_idHashValid = m_valueHashValid = false; +} + +void MacroSet::merge( const MacroSet& macros ) { + Macros m = macros.m_usedMacros; //Swap is needed so the merged macros take precedence + m.insert( m_usedMacros.begin(), m_usedMacros.end() ); + m_usedMacros = m; + m_idHashValid = m_valueHashValid = false; +} + + +size_t MacroSet::idHash() const { + if( !m_idHashValid ) computeHash(); + return m_idHash; +} + +size_t MacroSet::valueHash() const { + if( !m_valueHashValid ) computeHash(); + return m_valueHash; +} + +void MacroSet::computeHash() const { + m_idHash = 0; + m_valueHash = 0; + int mult = 1; + for( Macros::const_iterator it = m_usedMacros.begin(); it != m_usedMacros.end(); ++it ) { + mult *= 31; + m_idHash += (*it).idHash(); + m_valueHash += (*it).valueHash(); + } +} + +// void MacroSet::read( QDataStream& stream ) { +// stream >> m_idHashValid >> m_idHash >> m_valueHashValid >> m_valueHash; +// int cnt; +// stream >> cnt; +// m_usedMacros.clear(); +// Macro m; +// for( int a = 0; a < cnt; a++ ) { +// m.read( stream ); +// m_usedMacros.insert( m ); +// } +// } +// +// void MacroSet::write( QDataStream& stream ) const { +// stream << m_idHashValid << m_idHash << m_valueHashValid << m_valueHash; +// stream << m_usedMacros.size(); +// for( Macros::const_iterator it = m_usedMacros.begin(); it != m_usedMacros.end(); ++it ) { +// (*it).write( stream ); +// } +// } + +/** + * @return All Macros that were used while processing this translation-unit + * */ +MacroSet& ParsedFile::usedMacros() { + return m_usedMacros; +} + +const MacroSet& ParsedFile::usedMacros() const { + return m_usedMacros; +} + +ParsedFile::ParsedFile( const QString& fileName, const QDateTime& timeStamp ) : m_translationUnit( 0 ), m_fileName( fileName ), m_timeStamp( timeStamp ) { + m_includeFiles.insert( fileName ); +} + +ParsedFile::ParsedFile( const QByteArray& array ) { + QBuffer b( array ); + QDataStream d( &b ); + read( d ); +} + +QString ParsedFile::includedFrom() const { + return m_includedFrom; +} + +void ParsedFile::setIncludedFrom( const QString& str ) { + m_includedFrom = str; +} + +QByteArray ParsedFile::serialize() const { + QByteArray array; + QBuffer b( array ); + QDataStream d( &b ); + write( d ); + return array; +} + +// void ParsedFile::read( QDataStream& stream ) { +// int directIncludeFilesCount; +// stream >> directIncludeFilesCount; +// m_directIncludeFiles.clear(); +// for( int a = 0; a < directIncludeFilesCount; a++ ) { +// IncludeDesc i; +// stream >> i.local; +// stream >> i.includePath; +// //"parsed" will not be reconstructed +// m_directIncludeFiles.push_back( i ); +// } +// stream >> m_fileName; +// stream >> m_timeStamp; +// m_usedMacros.read( stream ); +// m_translationUnit = 0; +// m_includeFiles.read( stream ); +// } +// +// void ParsedFile::write( QDataStream& stream ) const { +// for( QValueList<IncludeDesc>::const_iterator it = m_directIncludeFiles.begin(); it != m_directIncludeFiles.end(); ++it ) { +// stream << (*it).local; +// stream << (*it).includePath; +// } +// stream << m_fileName; +// stream << m_timeStamp; +// m_usedMacros.write( stream ); +// m_includeFiles.write( stream ); +// } + +ParsedFile::operator TranslationUnitAST* () const { + if( !this ) return 0; + return m_translationUnit; +} + +void ParsedFile::setTranslationUnit( const TranslationUnitAST::Node& trans ) { + m_translationUnit = trans; +} + +// HashedStringSet& ParsedFile::includeFiles() { +// return m_includeFiles; +// } + +int ParsedFile::skippedLines() const { + return m_skippedLines; +} + +void ParsedFile::setSkippedLines( int lines ) { + m_skippedLines = lines; +} + +const HashedStringSet& ParsedFile::includeFiles() const { + return m_includeFiles; +} + +QString ParsedFile::fileName() const { + return m_fileName; +} + +QDateTime ParsedFile::timeStamp() const { + return m_timeStamp; +} + +void ParsedFile::addIncludeFiles( const HashedStringSet& includeFiles ) { + m_includeFiles += includeFiles; +} + +void ParsedFile::addIncludeFile( const QString& includePath, const ParsedFilePointer& parsed, bool localInclude ) { + m_includeFiles.insert( includePath ); + if( parsed ) + m_includeFiles += parsed->includeFiles(); + IncludeDesc d; + d.local = localInclude; + d.includePath = includePath; + d.parsed = parsed; + m_directIncludeFiles << d; +} + +const QValueList<ParsedFile::IncludeDesc>& ParsedFile::directIncludeFiles() const { + return m_directIncludeFiles; +} + +bool MacroSet::hasMacro( const QString& name ) const { + Macros::const_iterator it = m_usedMacros.find( Macro( name, "" ) ); + if( it != m_usedMacros.end() ) { + return true; + } else { + return false; + } +} + +bool MacroSet::hasMacro( const HashedString& name ) const { + Macros::const_iterator it = m_usedMacros.find( Macro( name.str(), "" ) ); + if( it != m_usedMacros.end() ) { + return true; + } else { + return false; + } +} + +Macro MacroSet::macro( const QString& name ) const { + Macros::const_iterator it = m_usedMacros.find( Macro( name, "" ) ); + + if( it != m_usedMacros.end() ) { + return *it; + } else { + return Macro(); + } +} + +LexerCache* Driver::lexerCache() { + return &m_lexerCache; +} + diff --git a/lib/cppparser/driver.h b/lib/cppparser/driver.h new file mode 100644 index 00000000..31f62443 --- /dev/null +++ b/lib/cppparser/driver.h @@ -0,0 +1,460 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef DRIVER_H +#define DRIVER_H + +#include "ast.h" + +#include "macro.h" +#include <qpair.h> +#include <qvaluestack.h> +#include <qstringlist.h> +#include <qcstring.h> +#include <qdatastream.h> +#include <qmap.h> +#include <qdatetime.h> +#include <qvaluelist.h> +#include <map> +#include <set> +#include <hashedstring.h> +#include <ksharedptr.h> +#include <codemodel.h> +#include <ext/hash_map> +#include "lexercache.h" + + +class Lexer; +class Parser; + +enum +{ + Dep_Global, + Dep_Local +}; + +typedef QPair<QString, int> Dependence; + +class ParsedFile; +typedef KSharedPtr< ParsedFile > ParsedFilePointer; + +class ParsedFile : public AbstractParseResult { + public: + struct IncludeDesc { + bool local; //Whether it is a local include(#include "local.h", not #include <global.h>) + QString includePath; + ParsedFilePointer parsed; //May be zero! + }; +// ParsedFile() { +// } + ParsedFile( QDataStream& s ) { + read( s ); + } + + ParsedFile( const QString& fileName, const QDateTime& timeStamp ); + + ///Deserializes the ParsedFile from a previous call to serialize(). AST will always be zero after a call to this. + ParsedFile( const QByteArray& array ); + + /** + * @return All Macros that were used while processing this translation-unit. May be modified. + */ + MacroSet& usedMacros(); + const MacroSet& usedMacros() const; + + /** + * @return the count of lines that were skipped while preprocessing the file + * */ + int skippedLines() const; + + void setSkippedLines( int lines ); + /** + * @return Absolutely all files included by this one(no matter through how many other files they were included) + */ +// HashedStringSet& includeFiles(); + + const HashedStringSet& includeFiles() const; + + void addIncludeFiles( const HashedStringSet& includeFiles ); + + void addIncludeFile( const QString& includePath, const ParsedFilePointer& parsed, bool localInclude ); + + ///If this file was parsed while resolving the dependencies of another file, this returns the file this one was included from. Else returns an empty string. + QString includedFrom() const; + + void setIncludedFrom( const QString& str ); + /** + * @return Reference to the internal list of all directly included files(without those included indirectly) + */ + const QValueList<IncludeDesc>& directIncludeFiles() const; + + operator TranslationUnitAST* () const; //May be zero! + + TranslationUnitAST* operator -> () const { + if( !this ) return 0; + return m_translationUnit; + } + + void setTranslationUnit( const TranslationUnitAST::Node& trans ); + + QString fileName() const; + + QDateTime timeStamp() const; + + ///Serializes the content of this class into a byte-array. Note that this does not serialize the AST. + QByteArray serialize() const; + + /*void read( QDataStream& stream ); + void write( QDataStream& stream ) const;*/ + + virtual void read( QDataStream& stream ) { + int directIncludeFilesCount; + stream >> directIncludeFilesCount; + m_directIncludeFiles.clear(); + for( int a = 0; a < directIncludeFilesCount; a++ ) { + IncludeDesc i; + Q_INT8 in; + stream >> in; + i.local = in; + stream >> i.includePath; + //"parsed" will not be reconstructed + m_directIncludeFiles.push_back( i ); + } + stream >> m_skippedLines; + stream >> m_fileName; + stream >> m_timeStamp; + stream >> m_includedFrom; + m_usedMacros.read( stream ); + m_translationUnit = 0; + m_includeFiles.read( stream ); + } + + virtual void write( QDataStream& stream ) const { + int i = m_directIncludeFiles.size(); + stream << i; + for( QValueList<IncludeDesc>::const_iterator it = m_directIncludeFiles.begin(); it != m_directIncludeFiles.end(); ++it ) { + Q_INT8 i = (*it).local; + stream << i; + stream << (*it).includePath; + } + stream << m_skippedLines; + stream << m_fileName; + stream << m_timeStamp; + stream << m_includedFrom; + m_usedMacros.write( stream ); + m_includeFiles.write( stream ); + } + + virtual ParsedFileType type() const { + return CppParsedFile; + } + + private: + QValueList<IncludeDesc> m_directIncludeFiles; + MacroSet m_usedMacros; + TranslationUnitAST::Node m_translationUnit; + HashedStringSet m_includeFiles; + int m_skippedLines; + QString m_fileName; + QDateTime m_timeStamp; + QString m_includedFrom; +}; + +/** + * An interface that provides source code to the Driver + */ +class SourceProvider { + public: + SourceProvider() {} + virtual ~SourceProvider() {} + + /** + * Get the contents of a file + * \param fileName The name of the file to get the contents for. An absolute + * path should be used. + * \return A QString that contains the contents of the file + */ + virtual QString contents( const QString& fileName ) = 0; + + /** + * Check to see if a file has been modified + * \param fileName The name of hte file to get the modification state of. An + * absolute path should be used. + * \return true if the file has been modified + * \return false if the file has not been modified + */ + virtual bool isModified( const QString& fileName ) = 0; + + private: + SourceProvider( const SourceProvider& source ); + void operator = ( const SourceProvider& source ); +}; + +class Driver { + public: + Driver(); + virtual ~Driver(); + + typedef std::multimap< HashedString, Macro > MacroMap; + + /** + * Get the source provider for this driver. This would be useful for + * getting the text the driver is working with. + */ + SourceProvider* sourceProvider(); + /** + * Sets the source provider the driver will use + * @param sourceProvider the SourceProvider the driver will use + */ + void setSourceProvider( SourceProvider* sourceProvider ); + + /** + * @brief Resets the driver + * + * Clears the driver of all problems, dependencies, macros, and include paths and + * removes any translation units that have been parsed + */ + virtual void reset(); + + /** + * Tells the driver to start parsing a file + * @param fileName The name of the file to parse + * @param onlyPreProcesss Tells the driver to only run the file through the preprocessor. Defaults to false + * @param force Force the parsing of the file. Defaults to false + * @param macrosGlobal Should the macros be global? (Global macros are not deleted once a new translation-unit is parsed) + */ + virtual void parseFile( const QString& fileName, bool onlyPreProcesss = false, bool force = false, bool macrosGlobal = false ); + + /** + * Indicates that the file has been parsed + * @param fileName The name of the file parsed. It is legal to create a ParsedFilePointer on the given item. + */ + virtual void fileParsed( ParsedFile& fileName ); + + /** + * Removes the file specified by @p fileName from the driver + * @param fileName The name of the file to remove + */ + virtual void remove + ( const QString& fileName ); + + /** + * Add a dependency on another header file for @p fileName + * @param fileName The file name to add the dependency for + * @param dep The dependency to add + */ + virtual void addDependence( const QString& fileName, const Dependence& dep ); + + /** + * Add a macro to the driver + * @param macro The macro to add to the driver + */ + virtual void addMacro( const Macro& macro ); + + /** + * Add a problem to the driver + * @param fileName The file name to add the problem for + * @param problem The problem to add + */ + virtual void addProblem( const QString& fileName, const Problem& problem ); + + /** + * The current file name the driver is working with + */ + QString currentFileName() const { + return m_currentFileName; + } + ParsedFilePointer takeTranslationUnit( const QString& fileName ); + + void takeTranslationUnit( const ParsedFile& file ); + /** + * Get the translation unit contained in the driver for @p fileName. + * @param fileName The name of the file to get the translation unit for + * @return The TranslationUnitAST pointer that represents the translation unit + * @return 0 if no translation unit exists for the file + */ + ParsedFilePointer translationUnit( const QString& fileName ) const; + /** + * Get the dependencies for a file + * @param fileName The file name to get dependencies for + * @return The dependencies for the file + */ + QMap<QString, Dependence> dependences( const QString& fileName ) const; + /** + * Get all the macros the driver contains + * @return The macros + */ + const MacroMap& macros() const; + + /** + * Take all macros from the given map(forgetting own macros) */ + void insertMacros( const MacroSet& macros ); + /** + * Get the list of problem areas the driver contains + * @param fileName The filename to get problems for + * @return The list of problems for @p fileName + */ + QValueList<Problem> problems( const QString& fileName ) const; + + void usingString( const HashedString& str ); + /** + * Check if we have a macro in the driver + * If the last stacked macro of that name is an undef-macro, false is returned. + * @param name The name of the macro to check for + * @return true if we have the macro in the driver + * @return false if we don't have the macro in the driver + */ + bool hasMacro( const HashedString& name ) ; + /** + * Get the macro identified by @p name + * @param name The name of the macro to get + * @return A const reference of the macro object represented by @p name + */ + const Macro& macro( const HashedString& name ) const; + /** + * Get the last inserted macro identified by @p name + * @override + * @param name The name of the macro to get + * @return A non-const reference of the macro object represented by @p name + * + */ + Macro& macro( const HashedString& name ); + + /** + * Remove the last inserted Macro of that name + * @param macroName The name of the macro to remove + */ + virtual void removeMacro( const HashedString& macroName ); + + /** + * Remove all macros from the driver for a certain file + * @param fileName The file name + */ + virtual void removeAllMacrosInFile( const QString& fileName ); ///Check when this is called. It may be wrong. + + QStringList includePaths() const { + return m_includePaths; + } + + virtual QStringList getCustomIncludePath( const QString& ); + + + virtual void addIncludePath( const QString &path ); + + virtual void clearIncludePaths(); + + /// @todo remove + const QMap<QString, ParsedFilePointer> &parsedUnits() const { + return m_parsedUnits; + } + + /** + * Set whether or not to enable dependency resolving for files added to the driver + */ + virtual void setResolveDependencesEnabled( bool enabled ); + /** + * Check if dependency resolving is enabled + * \return true if dependency resolving is enabled + * \return false if dependency resolving is disabled + */ + bool isResolveDependencesEnabled() const { + return depresolv; + } + + void setMaxDependenceDepth( int depth ); + + /** + * Used by the Lexer to indicate that a Macro was used + * @param macro The used macro + * */ + void usingMacro( const Macro& macro ); + + /** + * Returns the local instance of the lexer-cache, can be used from outside to control the cache-behavior. + * */ + LexerCache* lexerCache(); + + ///This uses getCustomIncludePath(..) to resolve the include-path internally + QString findIncludeFile( const Dependence& dep, const QString& fromFile ); + + protected: + ///This uses the state of the parser to find the include-file + QString findIncludeFile( const Dependence& dep ) const; + + /** + * Set up the lexer. + */ + virtual void setupLexer( Lexer* lexer ); + /** + * Setup the parser + */ + virtual void setupParser( Parser* parser ); + /** + * Set up the preprocessor + */ + virtual void setupPreProcessor(); + + /** + * Is code-information for this file already available? If false is returned, the file will be parsed. + * Code-model and static repository should be checked to find out whether the file is already available. + * This function is only used when dependency-resolving is activated. + * @arg file absolute path to the file + */ + virtual bool shouldParseIncludedFile( const ParsedFilePointer& /*file*/ ); + + void clearMacros(); + + void clearParsedMacros(); + + private: + QMap<QString, Dependence>& findOrInsertDependenceList( const QString& fileName ); + QValueList<Problem>& findOrInsertProblemList( const QString& fileName ); + + + private: + QString m_currentFileName; + QString m_currentMasterFileName; + typedef QMap<QString, Dependence> DependenceMap; + typedef QMap< QString, DependenceMap> DependencesMap; + DependencesMap m_dependences; + MacroMap m_macros; + QMap< QString, QValueList<Problem> > m_problems; + QMap<QString, ParsedFilePointer> m_parsedUnits; + QStringList m_includePaths; + uint depresolv : + 1; + Lexer *lexer; + SourceProvider* m_sourceProvider; + + ParsedFilePointer m_currentParsedFile; + CachedLexedFilePointer m_currentLexerCache; + LexerCache m_lexerCache; + + int m_dependenceDepth; + int m_maxDependenceDepth; + + class ParseHelper; + friend class ParseHelper; + + private: + Driver( const Driver& source ); + void operator = ( const Driver& source ); +}; + +#endif diff --git a/lib/cppparser/errors.cpp b/lib/cppparser/errors.cpp new file mode 100644 index 00000000..154301c6 --- /dev/null +++ b/lib/cppparser/errors.cpp @@ -0,0 +1,25 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "errors.h" +#include <klocale.h> + +QT_STATIC_CONST_IMPL Error& Errors::InternalError = Error( 1, -1, i18n("Internal Error") ); +QT_STATIC_CONST_IMPL Error& Errors::SyntaxError = Error( 2, -1, i18n("Syntax Error before '%1'") ); +QT_STATIC_CONST_IMPL Error& Errors::ParseError = Error( 3, -1, i18n("Parse Error before '%1'") ); diff --git a/lib/cppparser/errors.h b/lib/cppparser/errors.h new file mode 100644 index 00000000..f846533d --- /dev/null +++ b/lib/cppparser/errors.h @@ -0,0 +1,45 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef ERRORS_H +#define ERRORS_H + +#include <qstring.h> + + +struct Error{ + int code; + int level; + QString text; + + Error( int c, int l, const QString& s ) + : code( c ), level( l ), text( s ) + {} +}; + +class Errors{ +public: + QT_STATIC_CONST Error& InternalError; + QT_STATIC_CONST Error& SyntaxError; + QT_STATIC_CONST Error& ParseError; +}; + + + +#endif diff --git a/lib/cppparser/keywords.h b/lib/cppparser/keywords.h new file mode 100644 index 00000000..e649a5a9 --- /dev/null +++ b/lib/cppparser/keywords.h @@ -0,0 +1,95 @@ +// +// Keywords file is included in lookup.cpp +// [erbsland] replacement for old hash table +// +#define INSERT( x, y ) keywords.insert( std::pair<HashedString, Type>( x, y ) ) +// KDE Keywords +INSERT( "K_DCOP", Token_K_DCOP ); +INSERT( "k_dcop", Token_k_dcop ); +INSERT( "k_dcop_signals", Token_k_dcop_signals ); + +// Qt Keywords +INSERT( "Q_OBJECT", Token_Q_OBJECT ); +INSERT( "signals", Token_signals ); +INSERT( "slots", Token_slots ); +INSERT( "emit", Token_emit ); +INSERT( "foreach", Token_foreach ); + +// C++ Keywords +INSERT( "__int64", Token_int ); +INSERT( "__asm__", Token_asm ); +INSERT( "and", Token_and ); +INSERT( "and_eq", Token_and_eq ); +INSERT( "asm", Token_asm ); +INSERT( "auto", Token_auto ); +INSERT( "bitand", Token_bitand ); +INSERT( "bitor", Token_bitor ); +INSERT( "bool", Token_bool ); +INSERT( "break", Token_break ); +INSERT( "case", Token_case ); +INSERT( "catch", Token_catch ); +INSERT( "char", Token_char ); +INSERT( "class", Token_class ); +INSERT( "compl", Token_compl ); +INSERT( "const", Token_const ); +INSERT( "const_cast", Token_const_cast ); +INSERT( "continue", Token_continue ); +INSERT( "default", Token_default ); +INSERT( "delete", Token_delete ); +INSERT( "do", Token_do ); +INSERT( "double", Token_double ); +INSERT( "dynamic_cast", Token_dynamic_cast ); +INSERT( "else", Token_else ); +INSERT( "enum", Token_enum ); +INSERT( "explicit", Token_explicit ); +INSERT( "export", Token_export ); +INSERT( "extern", Token_extern ); +INSERT( "float", Token_float ); +INSERT( "for", Token_for ); +INSERT( "friend", Token_friend ); +INSERT( "goto", Token_goto ); +INSERT( "if", Token_if ); +INSERT( "inline", Token_inline ); +INSERT( "int", Token_int ); +INSERT( "long", Token_long ); +INSERT( "mutable", Token_mutable ); +INSERT( "namespace", Token_namespace ); +INSERT( "new", Token_new ); +INSERT( "not", Token_not ); +INSERT( "not_eq", Token_not_eq ); +INSERT( "operator", Token_operator ); +INSERT( "or", Token_or ); +INSERT( "or_eq", Token_or_eq ); +INSERT( "private", Token_private ); +INSERT( "protected", Token_protected ); +INSERT( "public", Token_public ); +INSERT( "register", Token_register ); +INSERT( "reinterpret_cast", Token_reinterpret_cast ); +INSERT( "return", Token_return ); +INSERT( "short", Token_short ); +INSERT( "signed", Token_signed ); +INSERT( "sizeof", Token_sizeof ); +INSERT( "static", Token_static ); +INSERT( "static_cast", Token_static_cast ); +INSERT( "struct", Token_struct ); +INSERT( "switch", Token_switch ); +INSERT( "template", Token_template ); +INSERT( "this", Token_this ); +INSERT( "throw", Token_throw ); +INSERT( "try", Token_try ); +INSERT( "typedef", Token_typedef ); +INSERT( "typeid", Token_typeid ); +INSERT( "typename", Token_typename ); +INSERT( "union", Token_union ); +INSERT( "unsigned", Token_unsigned ); +INSERT( "using", Token_using ); +INSERT( "virtual", Token_virtual ); +INSERT( "void", Token_void ); +INSERT( "volatile", Token_volatile ); +INSERT( "while", Token_while ); +INSERT( "xor", Token_xor ); +INSERT( "xor_eq", Token_xor_eq ); + +// +// End of file +// diff --git a/lib/cppparser/lexer.cpp b/lib/cppparser/lexer.cpp new file mode 100644 index 00000000..972b0bad --- /dev/null +++ b/lib/cppparser/lexer.cpp @@ -0,0 +1,1032 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "lexer.h" +#include "lookup.h" + +#include <kdebug.h> +#include <klocale.h> + +#include <qregexp.h> +#include <qmap.h> +#include <qvaluelist.h> + +#if defined( KDEVELOP_BGPARSER ) +#include <qthread.h> + +class KDevTread: public QThread +{ +public: + static void yield() + { + msleep( 0 ); + } +}; + +inline void qthread_yield() +{ + KDevTread::yield(); +} + +#endif + +#define CREATE_TOKEN(type, start, len) Token( (type), (start), (len), m_source ) +#define ADD_TOKEN(tk) m_tokens.insert( m_size++, new Token(tk) ); + +using namespace std; + +struct LexerData +{ + typedef QMap<QString, QString> Scope; + typedef QValueList<Scope> StaticChain; + + StaticChain staticChain; + + void beginScope() + { + Scope scope; + staticChain.push_front( scope ); + } + + void endScope() + { + staticChain.pop_front(); + } + + void bind( const QString& name, const QString& value ) + { + Q_ASSERT( staticChain.size() > 0 ); + staticChain.front().insert( name, value ); + } + + bool hasBind( const QString& name ) const + { + StaticChain::ConstIterator it = staticChain.begin(); + while( it != staticChain.end() ){ + const Scope& scope = *it; + ++it; + + if( scope.contains(name) ) + return true; + } + + return false; + } + + QString apply( const QString& name ) const + { + StaticChain::ConstIterator it = staticChain.begin(); + while( it != staticChain.end() ){ + const Scope& scope = *it; + ++it; + + if( scope.contains(name) ) + return scope[ name ]; + } + + return QString::null; + } + +}; + +Lexer::Lexer( Driver* driver ) + : d( new LexerData), + m_driver( driver ), + m_recordComments( true ), + m_recordWhiteSpaces( false ), + m_skipWordsEnabled( true ), + m_preprocessorEnabled( true ), + m_reportWarnings( false ), + m_reportMessages( false ) +{ + m_tokens.setAutoDelete( true ); + reset(); + d->beginScope(); +} + +Lexer::~Lexer() +{ + d->endScope(); + delete( d ); +} + +void Lexer::setSource( const QString& source ) +{ + reset(); + m_source = source; + m_ptr = offset( 0 ); + m_endPtr = offset( m_source.length() ); + m_inPreproc = false; + if( !source.isEmpty() ) { + m_currentChar = m_source[0]; + } else { + m_currentChar = QChar::null; + } + + tokenize(); +} + +int Lexer::skippedLines() const { + return m_skippedLines; +} + +void Lexer::reset() +{ + m_skippedLines = 0; + m_index = 0; + m_size = 0; + m_tokens.clear(); + m_source = QString::null; + m_ptr = 0; + m_endPtr = 0; + m_startLine = false; + m_ifLevel = 0; + m_skipping.resize( 200 ); + m_skipping.fill( 0 ); + m_trueTest.resize( 200 ); + m_trueTest.fill( 0 ); + + m_currentLine = 0; + m_currentColumn = 0; +} + +// ### should all be done with a "long" type IMO +int Lexer::toInt( const Token& token ) +{ + QString s = token.text(); + if( token.type() == Token_number_literal ){ + // hex literal ? + if( s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) + return s.mid( 2 ).toInt( 0, 16 ); + QString n; + int i = 0; + while( i < int(s.length()) && s[i].isDigit() ) + n += s[i++]; + // ### respect more prefixes and suffixes ? + return n.toInt(); + } else if( token.type() == Token_char_literal ){ + int i = s[0] == 'L' ? 2 : 1; // wide char ? + if( s[i] == '\\' ){ + // escaped char + int c = s[i+1].unicode(); + switch( c ) { + case '0': + return 0; + case 'n': + return '\n'; + // ### more + default: + return c; + } + } else { + return s[i].unicode(); + } + } else { + return 0; + } +} + +void Lexer::getTokenPosition( const Token& token, int* line, int* col ) +{ + token.getStartPosition( line, col ); +} + +void Lexer::nextToken( Token& tk, bool stopOnNewline ) +{ + int op = 0; + + if( m_size == (int)m_tokens.size() ){ + m_tokens.resize( m_tokens.size() + 5000 + 1 ); + } + + readWhiteSpaces( !stopOnNewline ); + + int startLine = m_currentLine; + int startColumn = m_currentColumn; + + QChar ch = currentChar(); + QChar ch1 = peekChar(); + + if( ch.isNull() || ch.isSpace() ){ + /* skip */ + } else if( m_startLine && ch == '#' ){ + + nextChar(); // skip # + readWhiteSpaces( false ); // skip white spaces + m_startLine = false; + + int start = currentPosition(); + readIdentifier(); // read the directive + QString directive = m_source.mid( start, currentPosition() - start ); + + handleDirective( directive ); + } else if( m_startLine && m_skipping[ m_ifLevel ] ){ + // skip line and continue + m_startLine = false; + int ppe = preprocessorEnabled(); + setPreprocessorEnabled( false ); + while( !currentChar().isNull() && currentChar() != '\n' ){ + Token tok(m_source); + nextToken( tok, true ); + } + ++m_skippedLines; + m_startLine = true; + setPreprocessorEnabled( ppe ); + return; + } else if( ch == '/' && ch1 == '/' ){ + int start = currentPosition(); + readLineComment(); + if( recordComments() ){ + tk = CREATE_TOKEN( Token_comment, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } + } else if( ch == '/' && ch1 == '*' ){ + int start = currentPosition(); + nextChar( 2 ); + readMultiLineComment(); + + if( recordComments() ){ + tk = CREATE_TOKEN( Token_comment, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } + } else if( ch == '\'' || (ch == 'L' && ch1 == '\'') ){ + int start = currentPosition(); + readCharLiteral(); + tk = CREATE_TOKEN( Token_char_literal, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } else if( ch == '"' ){ + int start = currentPosition(); + readStringLiteral(); + tk = CREATE_TOKEN( Token_string_literal, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } else if( ch.isLetter() || ch == '_' ){ + int start = currentPosition(); + readIdentifier(); + HashedString ide = m_source.mid( start, currentPosition() - start ); + int k = Lookup::find( ide ); + if( k == -1 && m_preprocessorEnabled ) m_driver->usingString( ide ); + + if( m_preprocessorEnabled && m_driver->hasMacro(ide) && + (k == -1 || !m_driver->macro(ide).body().isEmpty()) ){ + + + bool preproc = m_preprocessorEnabled; + m_preprocessorEnabled = false; + + d->beginScope(); + + int svLine = currentLine(); + int svColumn = currentColumn(); + + Macro m = m_driver->macro( ide ); + m_driver->usingMacro( m ); + + QString ellipsisArg; + + if( m.hasArguments() ){ + int endIde = currentPosition(); + + readWhiteSpaces(); + if( currentChar() == '(' ){ + nextChar(); + int argIdx = 0; + int argCount = m.argumentList().size(); + while( currentChar() && argIdx<argCount ){ + readWhiteSpaces(); + + QString argName = m.argumentList()[ argIdx ]; + + bool ellipsis = argName == "..."; + + QString arg = readArgument(); + + if( !ellipsis ) + d->bind( argName, arg ); + else + ellipsisArg += arg; + + if( currentChar() == ',' ){ + nextChar(); + if( !ellipsis ){ + ++argIdx; + } else { + ellipsisArg += ", "; + } + } else if( currentChar() == ')' ){ + break; + } + } + if( currentChar() == ')' ){ + // valid macro + nextChar(); + } + } else { + tk = CREATE_TOKEN( Token_identifier, start, endIde - start ); + tk.setStartPosition( svLine, svColumn ); + tk.setEndPosition( svLine, svColumn + (endIde - start) ); + + m_startLine = false; + + d->endScope(); // OPS!! + m_preprocessorEnabled = preproc; + return; + } + } + + int argsEndAtLine = currentLine(); + int argsEndAtColumn = currentColumn(); + +#if defined( KDEVELOP_BGPARSER ) + qthread_yield(); +#endif + insertCurrent( m.body() ); + + // tokenize the macro body + + QString textToInsert; + + setEndPtr( offset( currentPosition() + m.body().length() ) ); + + while( currentChar() ){ + + readWhiteSpaces(); + + Token tok(m_source); + nextToken( tok ); + + bool stringify = !m_inPreproc && tok == '#'; + bool merge = !m_inPreproc && tok == Token_concat; + + if( stringify || merge ) + nextToken( tok ); + + if( tok == Token_eof ) + break; + + QString tokText = tok.text(); + HashedString str = (tok == Token_identifier && d->hasBind(tokText)) ? d->apply( tokText ) : tokText; + if( str == ide ){ + //Problem p( i18n("unsafe use of macro '%1', macro is ignored").arg(ide.str()), m_currentLine, m_currentColumn, Problem::Level_Warning ); + //m_driver->addProblem( m_driver->currentFileName(), p ); + m_driver->removeMacro( ide ); + // str = QString::null; + } + + if( stringify ) { + textToInsert.append( QString::fromLatin1("\"") + str.str() + QString::fromLatin1("\" ") ); + } else if( merge ){ + textToInsert.truncate( textToInsert.length() - 1 ); + textToInsert.append( str.str() + QString::fromLatin1(" ") ); + } else if( tok == Token_ellipsis && d->hasBind("...") ){ + textToInsert.append( ellipsisArg ); + } else { + textToInsert.append( str.str() + QString::fromLatin1(" ") ); + } + } + +#if defined( KDEVELOP_BGPARSER ) + qthread_yield(); +#endif + insertCurrent( textToInsert ); //also corrects the end-pointer + + d->endScope(); + m_preprocessorEnabled = preproc; + //m_driver->addMacro( m ); + m_currentLine = argsEndAtLine; + m_currentColumn = argsEndAtColumn; + } else if( k != -1 ){ + tk = CREATE_TOKEN( k, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } else if( m_skipWordsEnabled ){ + __gnu_cxx::hash_map< HashedString, QPair<SkipType, QString> >::iterator pos = m_words.find( ide ); + if( pos != m_words.end() ){ + if( (*pos).second.first == SkipWordAndArguments ){ + readWhiteSpaces(); + if( currentChar() == '(' ) + skip( '(', ')' ); + } + if( !(*pos).second.second.isEmpty() ){ +#if defined( KDEVELOP_BGPARSER ) + qthread_yield(); +#endif + insertCurrent( QString(" ") + (*pos).second.second + QString(" ") ); + } + } else if( /*qt_rx.exactMatch(ide) ||*/ + ide.str().endsWith("EXPORT") || + (ide.str().startsWith("Q_EXPORT") && ide.str() != "Q_EXPORT_INTERFACE") || + ide.str().startsWith("QM_EXPORT") || + ide.str().startsWith("QM_TEMPLATE")){ + + readWhiteSpaces(); + if( currentChar() == '(' ) + skip( '(', ')' ); + } else if( ide.str().startsWith("K_TYPELIST_") || ide.str().startsWith("TYPELIST_") ){ + tk = CREATE_TOKEN( Token_identifier, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + readWhiteSpaces(); + if( currentChar() == '(' ) + skip( '(', ')' ); + } else{ + tk = CREATE_TOKEN( Token_identifier, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } + } else { + tk = CREATE_TOKEN( Token_identifier, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } + } else if( ch.isNumber() ){ + int start = currentPosition(); + readNumberLiteral(); + tk = CREATE_TOKEN( Token_number_literal, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } else if( -1 != (op = findOperator3()) ){ + tk = CREATE_TOKEN( op, currentPosition(), 3 ); + nextChar( 3 ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } else if( -1 != (op = findOperator2()) ){ + tk = CREATE_TOKEN( op, currentPosition(), 2 ); + nextChar( 2 ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } else { + tk = CREATE_TOKEN( ch, currentPosition(), 1 ); + nextChar(); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } + + m_startLine = false; +} + + +void Lexer::tokenize() +{ + m_startLine = true; + m_size = 0; + + for( ;; ) { + Token tk(m_source); + nextToken( tk ); + + if( tk.type() != -1 ) + ADD_TOKEN( tk ); + + if( currentChar().isNull() ) + break; + } + + Token tk = CREATE_TOKEN( Token_eof, currentPosition(), 0 ); + tk.setStartPosition( m_currentLine, m_currentColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + ADD_TOKEN( tk ); +} + +void Lexer::resetSkipWords() +{ + m_words.clear(); +} + +void Lexer::addSkipWord( const QString& word, SkipType skipType, const QString& str ) +{ + m_words[ word ] = qMakePair( skipType, str ); +} + +void Lexer::skip( int l, int r ) +{ + int svCurrentLine = m_currentLine; + int svCurrentColumn = m_currentColumn; + + int count = 0; + + while( !eof() ){ + Token tk(m_source); + nextToken( tk ); + + if( (int)tk == l ) + ++count; + else if( (int)tk == r ) + --count; + + if( count == 0 ) + break; + } + + m_currentLine = svCurrentLine; + m_currentColumn = svCurrentColumn; +} + +QString Lexer::readArgument() +{ + int count = 0; + + QString arg; + + readWhiteSpaces(); + while( currentChar() ){ + + readWhiteSpaces(); + QChar ch = currentChar(); + + if( ch.isNull() || (!count && (ch == ',' || ch == ')')) ) + break; + + Token tk(m_source); + nextToken( tk ); + + if( tk == '(' ){ + ++count; + } else if( tk == ')' ){ + --count; + } + + if( tk != -1 ) + arg += tk.text() + " "; + } + + return arg.stripWhiteSpace(); +} + +void Lexer::handleDirective( const QString& directive ) +{ + m_inPreproc = true; + + bool skip = skipWordsEnabled(); + bool preproc = preprocessorEnabled(); + + setSkipWordsEnabled( false ); + setPreprocessorEnabled( false ); + + if( directive == "define" ){ + if( !m_skipping[ m_ifLevel ] ){ + Macro m; + processDefine( m ); + } + } else if( directive == "else" ){ + processElse(); + } else if( directive == "elif" ){ + processElif(); + } else if( directive == "endif" ){ + processEndif(); + } else if( directive == "if" ){ + processIf(); + } else if( directive == "ifdef" ){ + processIfdef(); + } else if( directive == "ifndef" ){ + processIfndef(); + } else if( directive == "include" ){ + if( !m_skipping[ m_ifLevel ] ){ + processInclude(); + } + } else if( directive == "undef" ){ + if( !m_skipping[ m_ifLevel ] ){ + processUndef(); + } + } + + // skip line + while( currentChar() && currentChar() != '\n' ){ + Token tk(m_source); + nextToken( tk, true ); + } + + setSkipWordsEnabled( skip ); + setPreprocessorEnabled( preproc ); + + m_inPreproc = false; +} + +int Lexer::testIfLevel() +{ + int rtn = !m_skipping[ m_ifLevel++ ]; + m_skipping[ m_ifLevel ] = m_skipping[ m_ifLevel - 1 ]; + return rtn; +} + +int Lexer::macroDefined() +{ + readWhiteSpaces( false ); + int startWord = currentPosition(); + readIdentifier(); + HashedString word = m_source.mid( startWord, currentPosition() - startWord ); + m_driver->usingString( word ); + bool r = m_driver->hasMacro( word ); + + if( r ) m_driver->usingMacro( m_driver->macro( word ) ); + + return r; +} + +void Lexer::processDefine( Macro& m ) +{ + m.setFileName( m_driver->currentFileName() ); + m.setLine( m_currentLine ); + m.setColumn( m_currentColumn ); + readWhiteSpaces( false ); + + int startMacroName = currentPosition(); + readIdentifier(); + QString macroName = m_source.mid( startMacroName, int(currentPosition()-startMacroName) ); + m.setName( macroName ); + + if( currentChar() == '(' ){ + m.setHasArguments( true ); + nextChar(); + + readWhiteSpaces( false ); + + while( currentChar() && currentChar() != ')' ){ + readWhiteSpaces( false ); + + int startArg = currentPosition(); + + if( currentChar() == '.' && peekChar() == '.' && peekChar(2) == '.' ) + nextChar( 3 ); + else + readIdentifier(); + + QString arg = m_source.mid( startArg, int(currentPosition()-startArg) ); + + m.addArgument( Macro::Argument(arg) ); + + readWhiteSpaces( false ); + if( currentChar() != ',' ) + break; + + nextChar(); // skip ',' + } + + if( currentChar() == ')' ) + nextChar(); // skip ')' + } + + setPreprocessorEnabled( true ); + + QString body; + while( currentChar() && currentChar() != '\n' ){ + + if( currentChar().isSpace() ){ + readWhiteSpaces( false ); + body += " "; + } else { + + Token tk(m_source); + nextToken( tk, true ); + + //Do not ignore c-style comments, those may be useful in the body, and ignoring them using this check causes problems + if( tk.type() != -1 && (tk.type() != Token_comment || ( tk.text().length() >= 2 && tk.text()[1] == '*') ) ){ + QString s = tk.text(); + body += s; + } + } + } + + m.setBody( body ); + m_driver->addMacro( m ); +} + +void Lexer::processElse() +{ + if( m_ifLevel == 0 ) + /// @todo report error + return; + + if( m_ifLevel > 0 && m_skipping[m_ifLevel-1] ) + m_skipping[ m_ifLevel ] = m_skipping[ m_ifLevel - 1 ]; + else + m_skipping[ m_ifLevel ] = m_trueTest[ m_ifLevel ]; +} + +void Lexer::processElif() +{ + if( m_ifLevel == 0 ) + /// @todo report error + return; + + if( !m_trueTest[m_ifLevel] ){ + /// @todo implement the correct semantic for elif!! + bool inSkip = m_ifLevel > 0 && m_skipping[ m_ifLevel-1 ]; + m_trueTest[ m_ifLevel ] = macroExpression() != 0; + m_skipping[ m_ifLevel ] = inSkip ? inSkip : !m_trueTest[ m_ifLevel ]; + } + else + m_skipping[ m_ifLevel ] = true; +} + +void Lexer::processEndif() +{ + if( m_ifLevel == 0 ) + /// @todo report error + return; + + m_skipping[ m_ifLevel ] = 0; + m_trueTest[ m_ifLevel-- ] = 0; +} + +void Lexer::processIf() +{ + bool inSkip = m_skipping[ m_ifLevel ]; + + if( testIfLevel() ) { +#if 0 + int n; + if( (n = testDefined()) != 0 ) { + int isdef = macroDefined(); + m_trueTest[ m_ifLevel ] = (n == 1 && isdef) || (n == -1 && !isdef); + } else +#endif + m_trueTest[ m_ifLevel ] = macroExpression() != 0; + m_skipping[ m_ifLevel ] = inSkip ? inSkip : !m_trueTest[ m_ifLevel ]; + } +} + +void Lexer::processIfdef() +{ + bool inSkip = m_skipping[ m_ifLevel ]; + + if( testIfLevel() ){ + m_trueTest[ m_ifLevel ] = macroDefined(); + m_skipping[ m_ifLevel ] = inSkip ? inSkip : !m_trueTest[ m_ifLevel ]; + } +} + +void Lexer::processIfndef() +{ + bool inSkip = m_skipping[ m_ifLevel ]; + + if( testIfLevel() ){ + m_trueTest[ m_ifLevel ] = !macroDefined(); + m_skipping[ m_ifLevel ] = inSkip ? inSkip : !m_trueTest[ m_ifLevel ]; + } +} + +void Lexer::processInclude() +{ + if( m_skipping[m_ifLevel] ) + return; + + readWhiteSpaces( false ); + if( currentChar() ){ + QChar ch = currentChar(); + if( ch == '"' || ch == '<' ){ + nextChar(); + QChar ch2 = ch == QChar('"') ? QChar('"') : QChar('>'); + + int startWord = currentPosition(); + while( currentChar() && currentChar() != ch2 ) + nextChar(); + if( currentChar() ){ + QString word = m_source.mid( startWord, int(currentPosition()-startWord) ); + m_driver->addDependence( m_driver->currentFileName(), + Dependence(word, ch == '"' ? Dep_Local : Dep_Global) ); + nextChar(); + } + } + } +} + +void Lexer::processUndef() +{ + readWhiteSpaces(); + int startWord = currentPosition(); + readIdentifier(); + QString word = m_source.mid( startWord, currentPosition() - startWord ); + + Macro m( word, "" ); + m.setFileName( m_driver->currentFileName() ); + m.setUndef(); + + ///Adds an undef-macro that shadows the previous macro + m_driver->addMacro( m ); +} + +int Lexer::macroPrimary() +{ + readWhiteSpaces( false ); + int result = 0; + switch( currentChar() ) { + case '(': + nextChar(); + result = macroExpression(); + if( currentChar() != ')' ){ + /// @todo report error + return 0; + } + nextChar(); + return result; + + case '+': + case '-': + case '!': + case '~': + { + QChar tk = currentChar(); + nextChar(); + int result = macroPrimary(); + if( tk == '-' ) return -result; + else if( tk == '!' ) return !result; + else if( tk == '~' ) return ~result; + } + break; + + default: + { + Token tk(m_source); + nextToken( tk, false ); + switch( tk.type() ){ + case Token_identifier: + if( tk.text() == "defined" ){ + return macroPrimary(); + } + /// @todo implement + { + HashedString h( tk.text() ); + m_driver->usingString( h ); + if( m_driver->hasMacro( h ) ) { + m_driver->usingMacro( m_driver->macro( h ) ); + return true; + } else { + return false; + } + } + case Token_number_literal: + case Token_char_literal: + return toInt( tk ); + default: + break; + } // end switch + + } // end default + + } // end switch + + return 0; +} + +int Lexer::macroMultiplyDivide() +{ + int result = macroPrimary(); + int iresult, op; + for (;;) { + readWhiteSpaces( false ); + if( currentChar() == '*' ) + op = 0; + else if( currentChar() == '/' && !(peekChar() == '*' || peekChar() == '/') ) + op = 1; + else if( currentChar() == '%' ) + op = 2; + else + break; + nextChar(); + iresult = macroPrimary(); + result = op == 0 ? (result * iresult) : + op == 1 ? (iresult == 0 ? 0 : (result / iresult)) : + (iresult == 0 ? 0 : (result % iresult)) ; + } + return result; +} + +int Lexer::macroAddSubtract() +{ + int result = macroMultiplyDivide(); + int iresult, ad; + readWhiteSpaces( false ); + while( currentChar() == '+' || currentChar() == '-') { + ad = currentChar() == '+'; + nextChar(); + iresult = macroMultiplyDivide(); + result = ad ? (result+iresult) : (result-iresult); + } + return result; +} + +int Lexer::macroRelational() +{ + int result = macroAddSubtract(); + int iresult; + readWhiteSpaces( false ); + while( currentChar() == '<' || currentChar() == '>') { + int lt = currentChar() == '<'; + nextChar(); + if( currentChar() == '=') { + nextChar(); + + iresult = macroAddSubtract(); + result = lt ? (result <= iresult) : (result >= iresult); + } + else { + iresult = macroAddSubtract(); + result = lt ? (result < iresult) : (result > iresult); + } + } + + return result; +} + +int Lexer::macroEquality() +{ + int result = macroRelational(); + int iresult, eq; + readWhiteSpaces( false ); + while ((currentChar() == '=' || currentChar() == '!') && peekChar() == '=') { + eq = currentChar() == '='; + nextChar( 2 ); + iresult = macroRelational(); + result = eq ? (result==iresult) : (result!=iresult); + } + return result; +} + +int Lexer::macroBoolAnd() +{ + int result = macroEquality(); + readWhiteSpaces( false ); + while( currentChar() == '&' && peekChar() != '&') { + nextChar(); + result &= macroEquality(); + } + return result; +} + +int Lexer::macroBoolXor() +{ + int result = macroBoolAnd(); + readWhiteSpaces( false ); + while( currentChar() == '^') { + nextChar(); + result ^= macroBoolAnd(); + } + return result; +} + +int Lexer::macroBoolOr() +{ + int result = macroBoolXor(); + readWhiteSpaces( false ); + while( currentChar() == '|' && peekChar() != '|') { + nextChar(); + result |= macroBoolXor(); + } + return result; +} + +int Lexer::macroLogicalAnd() +{ + int result = macroBoolOr(); + readWhiteSpaces( false ); + while( currentChar() == '&' && peekChar() == '&') { + nextChar( 2 ); + int start = currentPosition(); + result = macroBoolOr() && result; + QString s = m_source.mid( start, currentPosition() - start ); + } + return result; +} + +int Lexer::macroLogicalOr() +{ + int result = macroLogicalAnd(); + readWhiteSpaces( false ); + while( currentChar() == '|' && peekChar() == '|') { + nextChar( 2 ); + result = macroLogicalAnd() || result; + } + return result; +} + +int Lexer::macroExpression() +{ + readWhiteSpaces( false ); + return macroLogicalOr(); +} + +// *IMPORTANT* +// please, don't include lexer.moc here, because Lexer isn't a QObject class!! +// if you have problem while recompiling try to remove cppsupport/.deps, +// cppsupport/Makefile.in and rerun automake/autoconf + diff --git a/lib/cppparser/lexer.h b/lib/cppparser/lexer.h new file mode 100644 index 00000000..8b3f57ed --- /dev/null +++ b/lib/cppparser/lexer.h @@ -0,0 +1,867 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef LEXER_H +#define LEXER_H + +#include "driver.h" + +#include <qstring.h> +#include <qmap.h> +#include <qvaluestack.h> +#include <qpair.h> +#include <qptrvector.h> +#include <hashedstring.h> +#include <ext/hash_map> + +#define CHARTYPE QChar + +enum Type { + Token_eof = 0, + Token_identifier = 1000, + Token_number_literal, + Token_char_literal, + Token_string_literal, + Token_whitespaces, + Token_comment, + Token_preproc, + + Token_assign = 2000, + Token_ptrmem, + Token_ellipsis, + Token_scope, + Token_shift, + Token_eq, + Token_leq, + Token_geq, + Token_incr, + Token_decr, + Token_arrow, + + Token_concat, + + Token_K_DCOP, + Token_k_dcop, + Token_k_dcop_signals, + + Token_Q_OBJECT, + Token_signals, + Token_slots, + Token_emit, + Token_foreach, // qt4 [erbsland] + + Token_and, + Token_and_eq, + Token_asm, + Token_auto, + Token_bitand, + Token_bitor, + Token_bool, + Token_break, + Token_case, + Token_catch, + Token_char, + Token_class, + Token_compl, + Token_const, + Token_const_cast, + Token_continue, + Token_default, + Token_delete, + Token_do, + Token_double, + Token_dynamic_cast, + Token_else, + Token_enum, + Token_explicit, + Token_export, + Token_extern, + Token_false, + Token_float, + Token_for, + Token_friend, + Token_goto, + Token_if, + Token_inline, + Token_int, + Token_long, + Token_mutable, + Token_namespace, + Token_new, + Token_not, + Token_not_eq, + Token_operator, + Token_or, + Token_or_eq, + Token_private, + Token_protected, + Token_public, + Token_register, + Token_reinterpret_cast, + Token_return, + Token_short, + Token_signed, + Token_sizeof, + Token_static, + Token_static_cast, + Token_struct, + Token_switch, + Token_template, + Token_this, + Token_throw, + Token_true, + Token_try, + Token_typedef, + Token_typeid, + Token_typename, + Token_union, + Token_unsigned, + Token_using, + Token_virtual, + Token_void, + Token_volatile, + Token_wchar_t, + Token_while, + Token_xor, + Token_xor_eq +}; + +enum SkipType { + SkipWord, + SkipWordAndArguments +}; + +struct LexerData; + +class Token +{ + Token(const QString &); + Token( int type, int position, int length, const QString& text ); + Token( const Token& source ); + + Token& operator = ( const Token& source ); + bool operator == ( const Token& token ) const; + operator int () const; + +public: + bool isNull() const; + + int type() const; + void setType( int type ); + + void getStartPosition( int* line, int* column ) const; + void setStartPosition( int line, int column ); + void getEndPosition( int* line, int* column ) const; + void setEndPosition( int line, int column ); + + unsigned int length() const; + void setLength( unsigned int length ); + + int position() const; + void setPosition( int position ); + + QString text() const; + +private: + int m_type; + int m_position; + int m_length; + int m_startLine; + int m_startColumn; + int m_endLine; + int m_endColumn; + const QString & m_text; + + friend class Lexer; + friend class Parser; +}; // class Token + +class Lexer +{ +public: + Lexer( Driver* driver ); + ~Lexer(); + + bool recordComments() const; + void setRecordComments( bool record ); + + bool recordWhiteSpaces() const; + void setRecordWhiteSpaces( bool record ); + + bool reportWarnings() const; + void setReportWarnings( bool enable ); + + bool reportMessages() const; + void setReportMessages( bool enable ); + + bool skipWordsEnabled() const; + void setSkipWordsEnabled( bool enabled ); + + bool preprocessorEnabled() const; + void setPreprocessorEnabled( bool enabled ); + + void resetSkipWords(); + void addSkipWord( const QString& word, SkipType skipType=SkipWord, const QString& str = QString::null ); + + QString source() const; + void setSource( const QString& source ); + + int index() const; + void setIndex( int index ); + + //returns the count of lines that wer skipped due to #ifdef's + int skippedLines() const; + + void reset(); + + const Token& tokenAt( int position ) const; + const Token& nextToken(); + const Token& lookAhead( int n ) const; + + static int toInt( const Token& token ); + + int tokenPosition( const Token& token ) const; + void getTokenPosition( const Token& token, int* line, int* col ); + + int currentLine() const { return m_currentLine; } + int currentColumn() const { return m_currentColumn; } + + inline const CHARTYPE* offset( int offset ) const { + return m_source.unicode() + offset; + } + + inline int getOffset( const QChar* p ) const { + return int(p - (m_source.unicode())); + } + +private: + void setEndPtr( const QChar* c ) { + m_endPtr = c; + if( m_ptr < m_endPtr ) + m_currentChar = *m_ptr; + else + m_currentChar = QChar::null; + } + const QChar currentChar() const; + QChar peekChar( int n=1 ) const; + int currentPosition() const; + + void insertCurrent( const QString& str ); + + void tokenize(); + void nextToken( Token& token, bool stopOnNewline=false ); + void nextChar(); + void nextChar( int n ); + void skip( int l, int r ); + void readIdentifier(); + void readWhiteSpaces( bool skipNewLine=true, bool skipOnlyOnce=false ); + void readLineComment(); + void readMultiLineComment(); + void readCharLiteral(); + void readStringLiteral(); + void readNumberLiteral(); + + int findOperator3() const; + int findOperator2() const; + bool eof() const; + + // preprocessor (based on an article of Al Stevens on Dr.Dobb's journal) + int testIfLevel(); + int macroDefined(); + QString readArgument(); + + int macroPrimary(); + int macroMultiplyDivide(); + int macroAddSubtract(); + int macroRelational(); + int macroEquality(); + int macroBoolAnd(); + int macroBoolXor(); + int macroBoolOr(); + int macroLogicalAnd(); + int macroLogicalOr(); + int macroExpression(); + + void handleDirective( const QString& directive ); + void processDefine( Macro& macro ); + void processElse(); + void processElif(); + void processEndif(); + void processIf(); + void processIfdef(); + void processIfndef(); + void processInclude(); + void processUndef(); + +private: + LexerData* d; + Driver* m_driver; + QPtrVector< Token > m_tokens; + int m_size; + int m_index; + QString m_source; + const QChar* m_ptr; + const QChar* m_endPtr; + QChar m_currentChar; + bool m_recordComments; + bool m_recordWhiteSpaces; + bool m_startLine; + __gnu_cxx::hash_map< HashedString, QPair<SkipType, QString> > m_words; + + + int m_skippedLines; + int m_currentLine; + int m_currentColumn; + bool m_skipWordsEnabled; + + // preprocessor + QMemArray<bool> m_skipping; + QMemArray<bool> m_trueTest; + int m_ifLevel; + bool m_preprocessorEnabled; + bool m_inPreproc; + + bool m_reportWarnings; + bool m_reportMessages; + +private: + Lexer( const Lexer& source ); + void operator = ( const Lexer& source ); +}; + + +inline Token::Token(const QString & text = "") + : m_type( -1 ), + m_position( 0 ), + m_length( 0 ), + m_text( text ) +{ +} + +inline Token::Token( int type, int position, int length, const QString& text ) + : m_type( type ), + m_position( position ), + m_length( length ), + m_text( text ) +{ +} + +inline Token::Token( const Token& source ) + : m_type( source.m_type ), + m_position( source.m_position ), + m_length( source.m_length ), + m_startLine( source.m_startLine ), + m_startColumn( source.m_startColumn ), + m_endLine( source.m_endLine ), + m_endColumn( source.m_endColumn ), + m_text( source.m_text ) +{ +} + +inline Token& Token::operator = ( const Token& source ) +{ + m_type = source.m_type; + m_position = source.m_position; + m_length = source.m_length; + m_startLine = source.m_startLine; + m_startColumn = source.m_startColumn; + m_endLine = source.m_endLine; + m_endColumn = source.m_endColumn; +// m_text = source.m_text; + return( *this ); +} + +inline Token::operator int () const +{ + return m_type; +} + +inline bool Token::operator == ( const Token& token ) const +{ + return m_type == token.m_type && + m_position == token.m_position && + m_length == token.m_length && + m_startLine == token.m_startLine && + m_startColumn == token.m_startColumn && + m_endLine == token.m_endLine && + m_endColumn == token.m_endColumn && + m_text == token.m_text; +} + +inline bool Token::isNull() const +{ + return m_type == Token_eof || m_length == 0; +} + +inline int Token::type() const +{ + return m_type; +} + +inline void Token::setType( int type ) +{ + m_type = type; +} + +inline int Token::position() const +{ + return m_position; +} + +inline QString Token::text() const +{ + return m_text.mid(m_position, m_length); +} + +inline void Token::setStartPosition( int line, int column ) +{ + m_startLine = line; + m_startColumn = column; +} + +inline void Token::setEndPosition( int line, int column ) +{ + m_endLine = line; + m_endColumn = column; +} + +inline void Token::getStartPosition( int* line, int* column ) const +{ + if( line ) *line = m_startLine; + if( column ) *column = m_startColumn; +} + +inline void Token::getEndPosition( int* line, int* column ) const +{ + if( line ) *line = m_endLine; + if( column ) *column = m_endColumn; +} + +inline void Token::setPosition( int position ) +{ + m_position = position; +} + +inline unsigned int Token::length() const +{ + return m_length; +} + +inline void Token::setLength( unsigned int length ) +{ + m_length = length; +} + +inline bool Lexer::recordComments() const +{ + return m_recordComments; +} + +inline void Lexer::setRecordComments( bool record ) +{ + m_recordComments = record; +} + +inline bool Lexer::recordWhiteSpaces() const +{ + return m_recordWhiteSpaces; +} + +inline void Lexer::setRecordWhiteSpaces( bool record ) +{ + m_recordWhiteSpaces = record; +} + +inline QString Lexer::source() const +{ + return m_source; +} + +inline int Lexer::index() const +{ + return m_index; +} + +inline void Lexer::setIndex( int index ) +{ + m_index = index; +} + +inline const Token& Lexer::nextToken() +{ + if( m_index < m_size ) + return *m_tokens[ m_index++ ]; + + return *m_tokens[ m_index ]; +} + +inline const Token& Lexer::tokenAt( int n ) const +{ + return *m_tokens[ QMIN(n, m_size-1) ]; +} + +inline const Token& Lexer::lookAhead( int n ) const +{ + return *m_tokens[ QMIN(m_index + n, m_size-1) ]; +} + +inline int Lexer::tokenPosition( const Token& token ) const +{ + return token.position(); +} + +inline void Lexer::nextChar() +{ + if(*m_ptr == '\n') { + ++m_currentLine; + m_currentColumn = 0; + m_startLine = true; + } else { + ++m_currentColumn; + } + ++m_ptr; + + if( m_ptr < m_endPtr ) + m_currentChar = *m_ptr; + else + m_currentChar = QChar::null; +} + +inline void Lexer::nextChar( int n ) +{ + m_currentColumn += n; + m_ptr += n; + + if( m_ptr < m_endPtr ) + m_currentChar = *m_ptr; + else + m_currentChar = QChar::null; +} + +inline void Lexer::readIdentifier() +{ + while( currentChar().isLetterOrNumber() || currentChar() == '_' ) + nextChar(); +} + +inline void Lexer::readWhiteSpaces( bool skipNewLine, bool skipOnlyOnce ) +{ + while( !currentChar().isNull() ){ + QChar ch = currentChar(); + + if( ch == '\n' && !skipNewLine ){ + break; + } else if( ch.isSpace() ){ + nextChar(); + } else if( m_inPreproc && currentChar() == '\\' ){ + nextChar(); + readWhiteSpaces( true, true ); + } else { + break; + } + if( skipOnlyOnce && ch == '\n' ) { + skipNewLine = false; + } + } +} + +//little hack for better performance +inline bool isTodo( const QString& txt, int position ) { + if( txt.length() < position + 4 ) return false; + return (txt[ position ] == 't' || txt[ position ] == 'T') + && (txt[ position+1 ] == 'o' || txt[ position+1 ] == 'O') + && (txt[ position+2 ] == 'd' || txt[ position+2 ] == 'D') + && (txt[ position+3 ] == 'o' || txt[ position+3 ] == 'O'); +} + +inline bool isFixme( const QString& txt, int position ) { + if( txt.length() < position + 5 ) return false; + return (txt[ position ] == 'f' || txt[ position ] == 'F') + && (txt[ position+1 ] == 'i' || txt[ position+1 ] == 'I') + && (txt[ position+2 ] == 'x' || txt[ position+2 ] == 'X') + && (txt[ position+3 ] == 'm' || txt[ position+3 ] == 'M') + && (txt[ position+4 ] == 'e' || txt[ position+4 ] == 'E'); +} + +inline void Lexer::readLineComment() +{ + while( !currentChar().isNull() && currentChar() != '\n' ){ + if( m_reportMessages && isTodo( m_source, currentPosition() ) ){ + nextChar( 4 ); + QString msg; + int line = m_currentLine; + int col = m_currentColumn; + + while( currentChar() ){ + if( currentChar() == '*' && peekChar() == '/' ) + break; + else if( currentChar() == '\n' ) + break; + + msg += currentChar(); + nextChar(); + } + m_driver->addProblem( m_driver->currentFileName(), Problem(msg, line, col, Problem::Level_Todo) ); + } else + if( m_reportMessages && isFixme( m_source, currentPosition() ) ){ + nextChar( 5 ); + QString msg; + int line = m_currentLine; + int col = m_currentColumn; + + while( currentChar() ){ + if( currentChar() == '*' && peekChar() == '/' ) + break; + else if( currentChar() == '\n' ) + break; + + msg += currentChar(); + nextChar(); + } + m_driver->addProblem( m_driver->currentFileName(), Problem(msg, line, col, Problem::Level_Fixme) ); + } else + nextChar(); + } +} + +inline void Lexer::readMultiLineComment() +{ + while( !currentChar().isNull() ){ + if( currentChar() == '*' && peekChar() == '/' ){ + nextChar( 2 ); + return; + } else if( m_reportMessages && isTodo( m_source, currentPosition() ) ){ + nextChar( 4 ); + QString msg; + int line = m_currentLine; + int col = m_currentColumn; + + while( currentChar() ){ + if( currentChar() == '*' && peekChar() == '/' ) + break; + else if( currentChar() == '\n' ) + break; + msg += currentChar(); + nextChar(); + } + m_driver->addProblem( m_driver->currentFileName(), Problem(msg, line, col, Problem::Level_Todo) ); + } else + if( m_reportMessages && isFixme( m_source, currentPosition() ) ) { + nextChar( 5 ); + QString msg; + int line = m_currentLine; + int col = m_currentColumn; + + while( currentChar() ){ + if( currentChar() == '*' && peekChar() == '/' ) + break; + else if( currentChar() == '\n' ) + break; + + msg += currentChar(); + nextChar(); + } + m_driver->addProblem( m_driver->currentFileName(), Problem(msg, line, col, Problem::Level_Fixme) ); + } else + nextChar(); + } +} + +inline void Lexer::readCharLiteral() +{ + if( currentChar() == '\'' ) + nextChar(); // skip ' + else if( currentChar() == 'L' && peekChar() == '\'' ) + nextChar( 2 ); // slip L' + else + return; + + while( !currentChar().isNull() ){ + int len = getOffset( m_endPtr ) - currentPosition(); + + if( len>=2 && (currentChar() == '\\' && peekChar() == '\'') ){ + nextChar( 2 ); + } else if( len>=2 && (currentChar() == '\\' && peekChar() == '\\') ){ + nextChar( 2 ); + } else if( currentChar() == '\'' ){ + nextChar(); + break; + } else { + nextChar(); + } + } +} + +inline void Lexer::readStringLiteral() +{ + if( currentChar() != '"' ) + return; + + nextChar(); // skip " + + while( !currentChar().isNull() ){ + int len = getOffset( m_endPtr ) - currentPosition(); + + if( len>=2 && currentChar() == '\\' && peekChar() == '"' ){ + nextChar( 2 ); + } else if( len>=2 && currentChar() == '\\' && peekChar() == '\\' ){ + nextChar( 2 ); + } else if( currentChar() == '"' ){ + nextChar(); + break; + } else { + nextChar(); + } + } +} + +inline void Lexer::readNumberLiteral() +{ + while( currentChar().isLetterOrNumber() || currentChar() == '.' ) + nextChar(); +} + +inline int Lexer::findOperator3() const +{ + int n = getOffset( m_endPtr ) - currentPosition(); + + if( n >= 3){ + QChar ch = currentChar(), ch1=peekChar(), ch2=peekChar(2); + + if( ch == '<' && ch1 == '<' && ch2 == '=' ) return Token_assign; + else if( ch == '>' && ch1 == '>' && ch2 == '=' ) return Token_assign; + else if( ch == '-' && ch1 == '>' && ch2 == '*' ) return Token_ptrmem; + else if( ch == '.' && ch1 == '.' && ch2 == '.' ) return Token_ellipsis; + } + + return -1; +} + +inline int Lexer::findOperator2() const +{ + int n = getOffset( m_endPtr ) - currentPosition(); + + if( n>=2 ){ + QChar ch = currentChar(), ch1=peekChar(); + + if( ch == ':' && ch1 == ':' ) return Token_scope; + else if( ch == '.' && ch1 == '*' ) return Token_ptrmem; + else if( ch == '+' && ch1 == '=' ) return Token_assign; + else if( ch == '-' && ch1 == '=' ) return Token_assign; + else if( ch == '*' && ch1 == '=' ) return Token_assign; + else if( ch == '/' && ch1 == '=' ) return Token_assign; + else if( ch == '%' && ch1 == '=' ) return Token_assign; + else if( ch == '^' && ch1 == '=' ) return Token_assign; + else if( ch == '&' && ch1 == '=' ) return Token_assign; + else if( ch == '|' && ch1 == '=' ) return Token_assign; + else if( ch == '<' && ch1 == '<' ) return Token_shift; + else if( ch == '>' && ch1 == '>' ) return Token_shift; + else if( ch == '=' && ch1 == '=' ) return Token_eq; + else if( ch == '!' && ch1 == '=' ) return Token_eq; + else if( ch == '<' && ch1 == '=' ) return Token_leq; + else if( ch == '>' && ch1 == '=' ) return Token_geq; + else if( ch == '&' && ch1 == '&' ) return Token_and; + else if( ch == '|' && ch1 == '|' ) return Token_or; + else if( ch == '+' && ch1 == '+' ) return Token_incr; + else if( ch == '-' && ch1 == '-' ) return Token_decr; + else if( ch == '-' && ch1 == '>' ) return Token_arrow; + else if( ch == '#' && ch1 == '#' ) return Token_concat; + } + + return -1; +} + +inline bool Lexer::skipWordsEnabled() const +{ + return m_skipWordsEnabled; +} + +inline void Lexer::setSkipWordsEnabled( bool enabled ) +{ + m_skipWordsEnabled = enabled; +} + +inline bool Lexer::preprocessorEnabled() const +{ + return m_preprocessorEnabled; +} + +inline void Lexer::setPreprocessorEnabled( bool enabled ) +{ + m_preprocessorEnabled = enabled; +} + +inline int Lexer::currentPosition() const +{ + return getOffset( m_ptr ); +} + +inline const QChar Lexer::currentChar() const +{ + return m_currentChar; +} + +inline QChar Lexer::peekChar( int n ) const +{ + const QChar* p = m_ptr + n; + + if( p < m_endPtr ) + return *p; + else + return QChar::null; +} + +inline bool Lexer::eof() const +{ + return m_ptr >= m_endPtr; +} + +inline bool Lexer::reportWarnings() const +{ + return m_reportWarnings; +} + +inline void Lexer::setReportWarnings( bool enable ) +{ + m_reportWarnings = enable; +} + +inline bool Lexer::reportMessages() const +{ + return m_reportMessages; +} + +inline void Lexer::setReportMessages( bool enable ) +{ + m_reportMessages = enable; +} + +inline void Lexer::insertCurrent( const QString& str ) { + int posi = currentPosition(); + m_source.insert( posi, str ); + + m_ptr = offset( posi ); + m_endPtr = offset( m_source.length() ); + if( m_ptr < m_endPtr ) + m_currentChar = *m_ptr; + else + m_currentChar = QChar::null; +} + +#endif diff --git a/lib/cppparser/lexercache.cpp b/lib/cppparser/lexercache.cpp new file mode 100644 index 00000000..a7e40dd6 --- /dev/null +++ b/lib/cppparser/lexercache.cpp @@ -0,0 +1,249 @@ +/*************************************************************************** + copyright : (C) 2006 by David Nolden + email : david.nolden.kdevelop@art-master.de +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "lexercache.h" +#include "driver.h" +#include <kdebug.h> + +LexerCache::LexerCache( Driver* d ) : m_driver( d ) {} + +void LexerCache::addLexedFile( const CachedLexedFilePointer& file ) { + //kdDebug( 9007 ) << "LexerCache: adding an instance of " << file->fileName().str() << endl; + + std::pair< CachedLexedFileMap::iterator, CachedLexedFileMap::iterator> files = m_files.equal_range( file->fileName() ); + + if ( files.first == files.second ) { + m_files.insert( std::make_pair( file->fileName(), file ) ); + } else { + //Make sure newer files appear first + m_files.insert( files.first, std::make_pair( file->fileName(), file ) ); + } + + int cnt = 0; + while ( files.first != files.second ) { + if ( sourceChanged( *( *( files.first ) ).second ) ) { + m_files.erase( files.first++ ); + } else { + cnt++; + files.first++; + } + } + //kdDebug( 9007 ) << "LexerCache: new count of cached instances for the file: " << cnt << endl; +} + +CachedLexedFilePointer LexerCache::lexedFile( const HashedString& fileName ) { + initFileModificationCache(); + std::pair< CachedLexedFileMap::iterator, CachedLexedFileMap::iterator> files = m_files.equal_range( fileName ); + + ///@todo optimize with standard-algorithms(by first computing the intersection) + + /* if( files.first != files.second ) + //kdDebug( 9007 ) << "LexerCache: cache for file " << fileName.str() << " is not empty" << endl; + else + //kdDebug( 9007 ) << "LexerCache: cache for file " << fileName.str() << " is empty" << endl;*/ + + while ( files.first != files.second ) { + const CachedLexedFile& file( *( *( files.first ) ).second ); + if ( sourceChanged( file ) ) { + //kdDebug( 9007 ) << "LexerCache: cache for file " << fileName.str() << " is being discarded because the file was modified" << endl; + m_files.erase( files.first++ ); + continue; + } + bool success = true; + //Make sure that none of the macros stored in the driver affect the file in a different way than the one before + Driver::MacroMap::const_iterator end = m_driver->macros().end(); + for ( Driver::MacroMap::const_iterator rit = m_driver->macros().begin(); rit != end; ) { + Driver::MacroMap::const_iterator it = rit; + ++rit; + if ( rit != end && ( *it ).first == ( *rit ).first ) continue; //Always only use the last macro of the same name for comparison, it is on top of the macro-stack + if (( *it ).second.isUndef() ) continue; //Undef-macros theoretically don't exist + + if ( file.hasString(( *it ).first ) ) { + if ( file.m_usedMacros.hasMacro(( *it ).first ) ) { + Macro m( file.m_usedMacros.macro(( *it ).first.str() ) ); + if ( !( m == ( *it ).second ) ) { + //kdDebug( 9007 ) << "LexerCache: The cached file " << fileName.str() << " depends on the string \"" << ( *it ).first.str() << "\" and used a macro for it with the body \"" << m.body() << "\"(from " << m.fileName() << "), but the driver contains the same macro with body \"" << ( *it ).second.body() << "\"(from " << ( *it ).second.fileName() << "), cache is not used" << endl; + + //Macro with the same name was used, but it is different + success = false; + break; + } + + } else { + //There is a macro that affects the file, but was not used while the previous parse + //kdDebug( 9007 ) << "LexerCache: The cached file " << fileName.str() << " depends on the string \"" << ( *it ).first.str() << "\" and the driver contains a macro of that name with body \"" << ( *it ).second.body() << "\"(from " << ( *it ).second.fileName() << "), the cached file is not used" << endl; + success = false; + break; + } + } + } + //Make sure that all external macros used by the file now exist too + MacroSet::Macros::const_iterator end2 = file.usedMacros().macros().end(); + for ( MacroSet::Macros::const_iterator it = file.usedMacros().macros().begin(); it != end2; ++it ) { + if ( !m_driver->hasMacro( HashedString(( *it ).name() ) ) ) { + //kdDebug( 9007 ) << "LexerCache: The cached file " << fileName.str() << " used a macro called \"" << it->name() << "\"(from " << it->fileName() << "), but the driver does not contain that macro, the cached file is not used" << endl; + success = false; + break; + } + } + + if ( success ) { + //kdDebug( 9007 ) << "LexerCache: Using cached file " << fileName.str() << endl; + (*files.first).second->access(); + return ( *files.first ).second; + } + ++files.first; + } + return CachedLexedFilePointer(); +} + +QDateTime LexerCache::fileModificationTimeCached( const HashedString& fileName ) { + FileModificationMap::const_iterator it = m_fileModificationCache.find( fileName ); + if( it != m_fileModificationCache.end() ) { + ///Use the cache for 10 seconds + if( (*it).second.m_readTime.secsTo( m_currentDateTime ) < 10 ) { + return (*it).second.m_modificationTime; + } + } + + QFileInfo fileInfo( fileName.str() ); + m_fileModificationCache[fileName].m_readTime = QDateTime::currentDateTime(); + m_fileModificationCache[fileName].m_modificationTime = fileInfo.lastModified(); + return fileInfo.lastModified(); + +} + +//Should be cached too! +bool LexerCache::sourceChanged( const CachedLexedFile& file ) { + //@todo Check if any of the dependencies changed + + QDateTime modTime = fileModificationTimeCached( file.fileName() ); + + if ( modTime != file.modificationTime() ) + return true; + + for( QMap<HashedString, QDateTime>::const_iterator it = file.allModificationTimes().begin(); it != file.allModificationTimes().end(); ++it ) { + QDateTime modTime = fileModificationTimeCached( it.key() ); + if( modTime != *it ) + return true; + } + + return false; +} + + +void LexerCache::clear() { + m_files.clear(); + m_totalStringSet.clear(); + m_fileModificationCache.clear(); +} + +void LexerCache::erase( const CacheNode* node ) { + std::pair< CachedLexedFileMap::iterator, CachedLexedFileMap::iterator> files = m_files.equal_range( ((const CachedLexedFile*)(node))->fileName() ); + while ( files.first != files.second ) { + if( (*files.first).second.data() == ((const CachedLexedFile*)(node)) ) { + m_files.erase( files.first ); + return; + } + files.first++; + } + //kdDebug( 9007 ) << "Error: could not find a node in the list for file " << ((const CachedLexedFile*)(node))->fileName().str() << endl; +} + +CachedLexedFile::CachedLexedFile( const HashedString& fileName, LexerCache* manager ) : CacheNode( manager ), m_fileName( fileName ) { + QFileInfo fileInfo( fileName.str() ); + m_modificationTime = fileInfo.lastModified(); + m_allModificationTimes[ fileName ] = m_modificationTime; +} + +void CachedLexedFile::addDefinedMacro( const Macro& macro ) { +#ifdef LEXERCACHE_DEBUG + //kdDebug( 9007 ) << "defined macro " << macro.name() << endl; +#endif + m_definedMacros.addMacro( macro ); + m_definedMacroNames.insert( HashedString( macro.name() ) ); +} + +void CachedLexedFile::addUsedMacro( const Macro& macro ) { + if ( !m_definedMacros.hasMacro( macro.name() ) ) { +#ifdef LEXERCACHE_DEBUG + //kdDebug( 9007 ) << "used macro " << macro.name() << endl; +#endif + m_usedMacros.addMacro( macro ); + } +} + +void CachedLexedFile::addIncludeFile( const HashedString& file, const QDateTime& modificationTime ) { + m_includeFiles.insert( file ); + m_allModificationTimes[file] = modificationTime; +} + + +QDateTime CachedLexedFile::modificationTime() const { + return m_modificationTime; +} + +void CachedLexedFile::addProblem( const Problem& p ) { + m_problems << p; +} + +QValueList<Problem> CachedLexedFile::problems() const { + return m_problems; +} + +//The parameter should be a CachedLexedFile that was lexed AFTER the content of this file +void CachedLexedFile::merge( const CachedLexedFile& file ) { +#ifdef LEXERCACHE_DEBUG + //kdDebug( 9007 ) << fileName().str() << ": merging " << file.fileName().str() << endl << "defined in this: " << m_definedMacroNames.print().c_str() << endl << "defined macros in other: " << file.m_definedMacroNames.print().c_str() << endl;; +#endif + HashedStringSet tempStrings = file.m_strings; + tempStrings -= m_definedMacroNames; + m_strings += tempStrings; + m_includeFiles += file.m_includeFiles; + //Only add macros to the usedMacros-list that were not defined locally + for ( MacroSet::Macros::const_iterator it = file.m_usedMacros.macros().begin(); it != file.m_usedMacros.macros().end(); ++it ) { + if ( !m_definedMacros.hasMacro(( *it ).name() ) ) {///If the macro was not defined locally, add it to the macros-list. +#ifdef LEXERCACHE_DEBUG + //kdDebug( 9007 ) << "inserting used macro " << ( *it ).name() << endl; +#endif + m_usedMacros.addMacro( *it ); + } + } + + m_definedMacros.merge( file.m_definedMacros ); + m_definedMacroNames += file.m_definedMacroNames; + + for( QMap<HashedString, QDateTime>::const_iterator it = file.m_allModificationTimes.begin(); it != file.m_allModificationTimes.end(); ++it ) + m_allModificationTimes[it.key()] = *it; + + +#ifdef LEXERCACHE_DEBUG + //kdDebug( 9007 ) << fileName().str() << ": defined in this after merge: " << m_definedMacroNames.print().c_str() << endl; +#endif + m_problems += file.m_problems; +} + +size_t CachedLexedFile::hash() const { + return m_usedMacros.valueHash() + m_usedMacros.idHash() + m_definedMacros.idHash() + m_definedMacros.valueHash() + m_strings.hash(); +} + +void LexerCache::initFileModificationCache() { + m_currentDateTime = QDateTime::currentDateTime(); +} + +void LexerCache::saveMemory() { + m_fileModificationCache.clear(); + + m_totalStringSet.clear(); ///it's unclear how often this should be emptied. It may happen that completely unused strings end up in this set, then deleting it will save us memory. +} diff --git a/lib/cppparser/lexercache.h b/lib/cppparser/lexercache.h new file mode 100644 index 00000000..c32d5406 --- /dev/null +++ b/lib/cppparser/lexercache.h @@ -0,0 +1,162 @@ +/*************************************************************************** + copyright : (C) 2006 by David Nolden + email : david.nolden.kdevelop@art-master.de +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef LEXERCACHE_H +#define LEXERCACHE_H +#include <hashedstring.h> +#include <ext/hash_map> +#include <ksharedptr.h> +#include "macro.h" +#include <kdebug.h> +#include <qdatetime.h> +#include <qfileinfo.h> +#include <ext/hash_set> +#include "cachemanager.h" + +//#define LEXERCACHE_DEBUG + +class LexerCache; + +class CachedLexedFile : public KShared, public CacheNode { + public: + ///@todo add and manage the set of included files + CachedLexedFile( const HashedString& fileName, LexerCache* manager ); + + inline void addString( const HashedString& string ) { + if( !m_definedMacroNames[ string ] ) { + m_strings.insert( string ); + } + } + + void addDefinedMacro( const Macro& macro ); + + void addUsedMacro( const Macro& macro ); + + void addIncludeFile( const HashedString& file, const QDateTime& modificationTime ); + + inline bool hasString( const HashedString& string ) const { + return m_strings[string]; + } + + QDateTime modificationTime() const; + + void addProblem( const Problem& p ); + + QValueList<Problem> problems() const; + + //The parameter should be a CachedLexedFile that was lexed AFTER the content of this file + void merge( const CachedLexedFile& file ); + + bool operator < ( const CachedLexedFile& rhs ) const { + return m_fileName < rhs.m_fileName; + } + + size_t hash() const; + + HashedString fileName() const { + return m_fileName; + } + + const HashedStringSet& includeFiles() const { + return m_includeFiles; + } + + const MacroSet& definedMacros() const { + return m_definedMacros; + } + + const MacroSet& usedMacros() const { + return m_usedMacros; + } + + ///Should contain a modification-time for each include-file + const QMap<HashedString, QDateTime>& allModificationTimes() const { + return m_allModificationTimes; + } + + private: + friend class LexerCache; + HashedString m_fileName; + QDateTime m_modificationTime; + HashedStringSet m_strings; //Set of all strings that can be affected by macros from outside + HashedStringSet m_includeFiles; //Set of all files + MacroSet m_usedMacros; //Set of all macros that were used, and were defined outside of this file + MacroSet m_definedMacros; //Set of all macros that were defined while lexing this file + HashedStringSet m_definedMacroNames; + QValueList<Problem> m_problems; + QMap<HashedString, QDateTime> m_allModificationTimes; + /* + Needed data: + 1. Set of all strings that appear in this file(For memory-reasons they should be taken from a global string-repository, because many will be the same) + 2. Set of all macros that were defined outside of, but affected the file + + Algorithm: + Iterate over all available macros, and check whether they affect the file. If it does, make sure that the macro is in the macro-set and has the same body. + If the check fails: We need to reparse. + */ +}; + +typedef KSharedPtr<CachedLexedFile> CachedLexedFilePointer; + +struct CachedLexedFilePointerCompare { + bool operator() ( const CachedLexedFilePointer& lhs, const CachedLexedFilePointer& rhs ) const { + return (*lhs) < (*rhs ); + } +}; + +class Driver; + +class LexerCache : public CacheManager { + public: + LexerCache( Driver* d ); + + void addLexedFile( const CachedLexedFilePointer& file ); + + ///Returns zero if no fitting file is available for the current context + CachedLexedFilePointer lexedFile( const HashedString& fileName ); + + void clear(); + + const HashedString& unifyString( const HashedString& str ) { + __gnu_cxx::hash_set<HashedString>::const_iterator it = m_totalStringSet.find( str ); + if( it != m_totalStringSet.end() ) { + return *it; + } else { + m_totalStringSet.insert( str ); + return str; + } + } + virtual void saveMemory(); + private: + ///before this can be called, initFileModificationCache should be called once + QDateTime fileModificationTimeCached( const HashedString& fileName ); + void initFileModificationCache(); + virtual void erase( const CacheNode* node ); + bool sourceChanged( const CachedLexedFile& file );///Returns true if the file itself, or any of its dependencies was modified. + //typedef __gnu_cxx::hash_multimap<HashedString, CachedLexedFilePointer> CachedLexedFileMap; + typedef std::multimap<HashedString, CachedLexedFilePointer> CachedLexedFileMap; + CachedLexedFileMap m_files; + __gnu_cxx::hash_set<HashedString> m_totalStringSet; ///This is used to reduce memory-usage: Most strings appear again and again. Because QString is reference-counted, this set contains a unique copy of each string to used for each appearance of the string + struct FileModificationCache { + QDateTime m_readTime; + QDateTime m_modificationTime; + }; + typedef __gnu_cxx::hash_map<HashedString, FileModificationCache> FileModificationMap; + FileModificationMap m_fileModificationCache; + Driver* m_driver; + QDateTime m_currentDateTime; +}; + + +#endif diff --git a/lib/cppparser/lookup.cpp b/lib/cppparser/lookup.cpp new file mode 100644 index 00000000..192b4d4c --- /dev/null +++ b/lib/cppparser/lookup.cpp @@ -0,0 +1,42 @@ +/* This file is part of KDevelop + Copyright (C) 2005 Tobias Erbsland <te@profzone.ch> + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "lookup.h" + +int Lookup::find( const HashedString& s ) +{ + KeywordMap::const_iterator it = keywords().find( s ); + if( it == keywords().end() ) + return -1; + return static_cast<int>((*it).second); +} + +const Lookup::KeywordMap& Lookup::keywords() +{ + static KeywordMap keywords; + + if( keywords.empty() ) + { +#include "keywords.h" + } + + return keywords; +} + diff --git a/lib/cppparser/lookup.h b/lib/cppparser/lookup.h new file mode 100644 index 00000000..6e41481e --- /dev/null +++ b/lib/cppparser/lookup.h @@ -0,0 +1,45 @@ +/* This file is part of KDevelop + Copyright (C) 2005 Tobias Erbsland <te@profzone.ch> + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _LOOKUP_H_ +#define _LOOKUP_H_ + +#include <qstring.h> +#include <qmap.h> + +#include "lexer.h" +#include <ext/hash_map> +#include <hashedstring.h> + +/** +* @short Fast keyword lookup. +*/ +class Lookup { +public: + typedef __gnu_cxx::hash_map<HashedString,Type> KeywordMap; + /** + * Find an entry in the table, and return its value + */ + static int find( const HashedString& s ); + + static const KeywordMap& keywords(); +}; + +#endif diff --git a/lib/cppparser/macro.h b/lib/cppparser/macro.h new file mode 100644 index 00000000..03e76984 --- /dev/null +++ b/lib/cppparser/macro.h @@ -0,0 +1,452 @@ +/*************************************************************************** + copyright : (C) 2006 by David Nolden + email : david.nolden.kdevelop@art-master.de +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef MACRO_H +#define MACRO_H + +#include <qpair.h> +#include <qvaluestack.h> +#include <qstringlist.h> +#include <qcstring.h> +#include <qdatastream.h> +#include <qmap.h> +#include <qdatetime.h> +#include <qvaluelist.h> +#include <map> +#include <set> +#include <hashedstring.h> +#include <ksharedptr.h> +#include <codemodel.h> +#include <ext/hash_map> + +//This files should be renamed to something like "helpers.h" + +/** + * Encapsulates a problem in a piece of source code. + */ +class Problem { + public: + enum + { + Level_Error = 0, ///< Indicates an error that will prevent the code from compiling + Level_Warning, ///< Indicates a warning + Level_Todo, ///< Indicates there is still something left to do + Level_Fixme ///< Indicates that something needs to be fixed + }; + + public: + Problem() {} + Problem( const Problem& source ) + : m_text( source.m_text ), m_line( source.m_line ), + m_column( source.m_column ), m_level( source.m_level ), m_file( source.m_file ) {} + Problem( const QString& text, int line, int column, int level = Level_Error ) + : m_text( text ), m_line( line ), m_column( column ), m_level( level ) {} + + Problem( const Problem& source, bool /*threadSafeClone*/ ) + : m_text( QString::fromUtf8( source.m_text.utf8().data() ) ), m_line( source.m_line ), + m_column( source.m_column ), m_level( source.m_level ), m_file( QString::fromUtf8( source.m_file.utf8().data() ) ) {} + + Problem& operator = ( const Problem& source ) { + m_text = source.m_text; + m_line = source.m_line; + m_column = source.m_column; + m_level = source.m_level; + m_file = source.m_file; + return ( *this ); + } + + void setFileName( const QString& fileName ) { + m_file = fileName; + } + + bool operator == ( const Problem& p ) const { + return m_text == p.m_text && m_line == p.m_line && m_column == p.m_column && m_level == p.m_level && m_file == p.m_file; + } + + /** Get the filename in which the problem was encountered */ + QString fileName() const { + return m_file; + } + + /** Get the text for the problem */ + QString text() const { + return m_text; + } + /** Get the line number of the problem */ + int line() const { + return m_line; + } + /** Get the column of the problem */ + int column() const { + return m_column; + } + /** + * Get the seriousness of the problem. There are four possibilities: + * \li Error + * \li Warning + * \li Todo + * \li Fixme + */ + int level() const { + return m_level; + } + + private: + QString m_text; + int m_line; + int m_column; + int m_level; + QString m_file; +}; + + +/** + * A datatype that represents a preprocessor macro. + * Most of the functions in this class need to be inline, so we do not have to import cppparser to many modules. The other solution would be moving macro into interfaces. + */ +class Macro { + public: + typedef QString Argument; + + public: + Macro( bool hasArguments = false ) : m_idHashValid( false ), m_valueHashValid( false ), m_line( 0 ), m_column( 0 ), m_hasArguments( hasArguments ), m_isUndefMacro( false ) {} + Macro( const QString &n, const QString &b ) : m_idHashValid( false ), m_valueHashValid( false ), m_name( n ), m_line( 0 ), m_column( 0 ), m_body( b ), m_hasArguments( false ), m_isUndefMacro( false ) {} + + //Sorts the macros by their hash-value, then by their name. + struct NameArgCompare { + bool operator () ( const Macro& lhs, const Macro& rhs ) const { + size_t lhash = lhs.idHash(); + size_t rhash = rhs.idHash(); + if( lhash < rhash ) return true; + else if( lhash > rhash ) return false; + + int df = lhs.m_name.compare( rhs.m_name ); + if ( df < 0 ) + return true; + if ( df == 0 ) { + if ( !lhs.m_hasArguments && rhs.m_hasArguments ) { + return true; + } else if ( lhs.m_hasArguments == rhs.m_hasArguments ) { + return lhs.m_argumentList.count() < rhs.m_argumentList.count(); + + } else { + return false; + } + } + return false; + } + }; + struct NameCompare { + bool operator () ( const Macro& lhs, const Macro& rhs ) const { + size_t lhash = lhs.idHash(); + size_t rhash = rhs.idHash(); + if( lhash < rhash ) return true; + else if( lhash > rhash ) return false; + + int df = lhs.m_name.compare( rhs.m_name ); + return df < 0; + } + }; + + struct NameArgHash { + size_t operator () ( const Macro& macro ) const { + return macro.idHash(); + } + }; + + struct NameArgEqual { + bool operator () ( const Macro& lhs, const Macro& rhs ) const { + int df = lhs.m_name.compare( rhs.m_name ); + if ( df == 0 ) { + if ( lhs.m_hasArguments != rhs.m_hasArguments ) { + return false; + } else { + if( lhs.m_argumentList.count() != rhs.m_argumentList.count() ) return false; + /*QStringList::const_iterator it2 = rhs.m_argumentList.begin(); + for( QStringList::const_iterator it = lhs.m_argumentList.begin(); it != lhs.m_argumentList.end(); ) { + if( *it != *it2 ) return false; + + ++it; + ++it2; + }*/ + return true; + + } + } + return false; + } + }; + + Macro( const Macro& source ) + : m_idHashValid( source.m_idHashValid ), m_valueHashValid( source.m_valueHashValid ), m_idHash( source.m_idHash ), m_valueHash( source.m_valueHash ), m_name( source.m_name ), + m_fileName( source.m_fileName ), + m_line( source.m_line ), + m_column( source.m_column ), + m_body( source.m_body ), + m_hasArguments( source.m_hasArguments ), + m_argumentList( source.m_argumentList ), m_isUndefMacro( source.m_isUndefMacro ) {} + + Macro& operator = ( const Macro& source ) { + m_idHashValid = source.m_idHashValid; + m_valueHashValid = source.m_valueHashValid; + m_idHash = source.m_idHash; + m_valueHash = source.m_valueHash; + m_name = source.m_name; + m_fileName = source.m_fileName; + m_line = source.m_line; + m_column = source.m_column; + m_body = source.m_body; + m_hasArguments = source.m_hasArguments; + m_argumentList = source.m_argumentList; + m_isUndefMacro = source.m_isUndefMacro; + return *this; + } + + bool operator == ( const Macro& source ) const { + if( !m_idHashValid || !m_valueHashValid ) computeHash(); + if( !source.m_idHashValid || !source.m_valueHashValid ) source.computeHash(); + + if( m_idHash != source.m_idHash ) return false; + if( m_valueHash != source.m_valueHash ) return false; + + return m_name == source.m_name && + m_fileName == source.m_fileName && + m_body == source.m_body && + m_hasArguments == source.m_hasArguments && + m_argumentList == source.m_argumentList && m_isUndefMacro == source.m_isUndefMacro; + } + + void read( QDataStream& stream ) { + Q_INT8 i; + stream >> i; m_idHashValid = i; + stream >> i; m_valueHashValid = i; + stream >> i; m_hasArguments = i; + + stream >> m_idHash; + stream >> m_valueHash; + stream >> m_name; + stream >> m_line; + stream >> m_column; + stream >> m_body; + stream >> m_fileName; + stream >> m_argumentList; + } + + void write( QDataStream& stream ) const { + Q_INT8 i; + i = m_idHashValid; stream << i; + i = m_valueHashValid; stream << i; + i = m_hasArguments; stream << i; + + stream << m_idHash; + stream << m_valueHash; + stream << m_name; + stream << m_line; + stream << m_column; + stream << m_body; + stream << m_fileName; + stream << m_argumentList; + } + + /** Get the name for this macro */ + QString name() const { + return m_name; + } + /** Set the name for this macro */ + void setName( const QString& name ) { + m_name = name; + invalidateHash(); + } + + /** Get the file name that contains this macro */ + QString fileName() const { + return m_fileName; + } + /** Set the file name that contains this macro */ + void setFileName( const QString& fileName ) { + m_fileName = fileName; + invalidateHash(); + } + + /** Get the line the macro is defined on */ + int line() const { + return m_line; + } + /** Set the line the macro is defined on */ + void setLine( int line ) { + m_line = line; + } + + /** Get the column the macro starts at */ + int column() const { + return m_column; + } + /** Set the column the macro starts at */ + void setColumn( int column ) { + m_column = column; + } + + /** Get the body of the macro */ + QString body() const { + return m_body; + } + /** Set the body of the macro */ + void setBody( const QString& body ) { + m_body = body; + invalidateHash(); + } + + /** This is used so the lexer does not have to remove macros that should really stay(they are just temporarily shadowed by an isUndef-macro */ + bool isUndef() const { + return m_isUndefMacro; + }; + + void setUndef() { + m_isUndefMacro = true; + invalidateHash(); + }; + + /** Check whether the macro has arguments that are passed to it */ + bool hasArguments() const { + return m_hasArguments; + } + void setHasArguments( bool hasArguments ) { + m_hasArguments = hasArguments; + invalidateHash(); + } + /** Get a list of arguments passed to this macro */ + QValueList<Argument> argumentList() const { + return m_argumentList; + } + + /** Clear the list of arguments this macro has */ + void clearArgumentList() { + m_argumentList.clear(); + m_hasArguments = false; + invalidateHash(); + } + /** Add an argument to this macro */ + void addArgument( const Argument& argument ) { + m_argumentList << argument; + } + /** Add a list of arguments to this macro */ + void addArgumentList( const QValueList<Argument>& arguments ) { + m_argumentList += arguments; + invalidateHash(); + } + + ///This hash respects macro-name and argument-count + size_t idHash() const { + if( !m_idHashValid ) computeHash(); + return m_idHash; + } + + ///This hash respects body and if it is an undef-macro + size_t valueHash() const { + if( !m_valueHashValid ) computeHash(); + return m_valueHash; + } + + private: + inline void invalidateHash() const { + m_idHashValid = m_valueHashValid = false; + } + + void computeHash() const { + m_idHash = 7 * ( HashedString::hashString( m_name ) ); + int a = 1; + //m_idHash += 31 * m_argumentList.count(); + + m_valueHash = 27 * ( HashedString::hashString( m_body ) + (m_isUndefMacro ? 1 : 0 ) ); + + for( QValueList<Argument>::const_iterator it = m_argumentList.begin(); it != m_argumentList.end(); ++it ) { + a *= 19; + m_valueHash += a * HashedString::hashString( *it ); + } + m_valueHashValid = true; + m_idHashValid = true; + } + + mutable bool m_idHashValid; + mutable bool m_valueHashValid; + mutable size_t m_idHash; //Hash that represents the ids of all macros + mutable size_t m_valueHash; //Hash that represents the values of all macros + + QString m_name; + QString m_fileName; + int m_line; + int m_column; + QString m_body; + bool m_hasArguments; + QStringList m_argumentList; //While identification, only the count plays a role, not the values. + bool m_isUndefMacro; + friend class NameCompare; + friend class NameArgEqual; +}; + +class MacroSet { + public: + //typedef __gnu_cxx::hash_set< Macro, Macro::NameArgHash, Macro::NameArgEqual > Macros; + typedef std::set< Macro, Macro::NameCompare > Macros; + MacroSet() : m_idHashValid( false ), m_valueHashValid( false ) { + } + + void addMacro( const Macro& macro ); + + void read( QDataStream& stream ) { + //stream >> m_idHashValid >> m_idHash >> m_valueHashValid >> m_valueHash; + m_idHashValid = false; + m_valueHashValid = false; + int cnt; + stream >> cnt; + m_usedMacros.clear(); + Macro m; + for( int a = 0; a < cnt; a++ ) { + m.read( stream ); + m_usedMacros.insert( m ); + } + } + + void write( QDataStream& stream ) const { + //stream << m_idHashValid << m_idHash << m_valueHashValid << m_valueHash; + stream << int( m_usedMacros.size() ); + for( Macros::const_iterator it = m_usedMacros.begin(); it != m_usedMacros.end(); ++it ) { + (*it).write( stream ); + } + } + + bool hasMacro( const QString& name ) const; + bool hasMacro( const HashedString& name ) const; + Macro macro( const QString& name ) const; + + size_t idHash() const; + size_t valueHash() const; + + const Macros& macros() const { + return m_usedMacros; + } + + void merge( const MacroSet& macros ); + private: + void computeHash() const; + Macros m_usedMacros; + mutable bool m_idHashValid; + mutable bool m_valueHashValid; + mutable size_t m_idHash; //Hash that represents the ids of all macros + mutable size_t m_valueHash; //Hash that represents the values of all macros + + friend class Driver; +}; + +#endif diff --git a/lib/cppparser/parser.cpp b/lib/cppparser/parser.cpp new file mode 100644 index 00000000..b790f5a1 --- /dev/null +++ b/lib/cppparser/parser.cpp @@ -0,0 +1,4330 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +// c++ support +#include "driver.h" +#include "lexer.h" +#include "parser.h" +#include "errors.h" + +// qt +#include <qstring.h> +#include <qstringlist.h> +#include <qasciidict.h> + +#include <kdebug.h> +#include <klocale.h> + +using namespace std; + +#define ADVANCE(tk, descr) \ +{ \ + const Token& token = lex->lookAhead( 0 ); \ + if( token != tk ){ \ + reportError( i18n("'%1' expected found '%2'").arg(descr).arg(token.text()) ); \ + return false; \ + } \ + nextToken(); \ +} + +#define ADVANCE_NR(tk, descr) \ +{ \ + const Token& token = lex->lookAhead( 0 ); \ + if( token != tk ){ \ + reportError( i18n("'%1' expected found '%2'").arg(descr).arg(token.text()) ); \ + } \ + else \ + nextToken(); \ +} + +#define CHECK(tk, descr) \ +{ \ + const Token& token = lex->lookAhead( 0 ); \ + if( token != tk ){ \ + return false; \ + } \ + nextToken(); \ +} + +#define MATCH(tk, descr) \ +{ \ + const Token& token = lex->lookAhead( 0 ); \ + if( token != tk ){ \ + reportError( Errors::SyntaxError ); \ + return false; \ + } \ +} + + + +#define UPDATE_POS(node, start, end) \ +{ \ + int line, col; \ + const Token &a = lex->tokenAt(start); \ + const Token &b = lex->tokenAt( end!=start ? end-1 : end ); \ + a.getStartPosition( &line, &col ); \ + (node)->setStartPosition( line, col ); \ + b.getEndPosition( &line, &col ); \ + (node)->setEndPosition( line, col ); \ + if( (node)->nodeType() == NodeType_Generic ) { \ + if ((start) == (end) || (end) == (start)+1) \ + (node)->setSlice(lex->source(), a.position(), a.length()); \ + else \ + (node)->setText( toString((start),(end)) ); \ + } \ +} + +#define AST_FROM_TOKEN(node, tk) \ + AST::Node node = CreateNode<AST>(); \ + UPDATE_POS( node, (tk), (tk)+1 ); + + +//@todo remove me +enum +{ + OBJC_CLASS, + OBJC_PROTOCOL, + OBJC_ALIAS +}; + +struct ParserPrivateData +{ + ParserPrivateData() + {} +}; + +Parser::Parser( Driver* driver, Lexer* lexer ) + : m_driver( driver ), + lex( lexer ), m_problems(0) +{ + d = new ParserPrivateData(); + + m_maxProblems = 5; + objcp = false; +} + +Parser::~Parser() +{ + delete d; + d = 0; +} + +bool Parser::reportError( const Error& err ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::reportError()" << endl; + if( m_problems < m_maxProblems ){ + ++m_problems; + int line=0, col=0; + const Token& token = lex->lookAhead( 0 ); + lex->getTokenPosition( token, &line, &col ); + + QString s = lex->lookAhead(0).text(); + s = s.left( 30 ).stripWhiteSpace(); + if( s.isEmpty() ) + s = i18n( "<eof>" ); + + m_driver->addProblem( m_driver->currentFileName(), Problem(err.text.arg(s), line, col) ); + } + + return true; +} + +bool Parser::reportError( const QString& msg ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::reportError()" << endl; + if( m_problems < m_maxProblems ){ + ++m_problems; + int line=0, col=0; + const Token& token = lex->lookAhead( 0 ); + lex->getTokenPosition( token, &line, &col ); + + m_driver->addProblem( m_driver->currentFileName(), Problem(msg, line, col) ); + } + + return true; +} + +void Parser::syntaxError() +{ + (void) reportError( Errors::SyntaxError ); +} + +bool Parser::skipUntil( int token ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipUntil()" << endl; + while( !lex->lookAhead(0).isNull() ){ + if( lex->lookAhead(0) == token ) + return true; + + nextToken(); + } + + return false; +} + +bool Parser::skipUntilDeclaration() +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipUntilDeclaration()" << endl; + clearComment(); + + while( !lex->lookAhead(0).isNull() ){ + + switch( lex->lookAhead(0) ){ + case ';': + case '~': + case Token_scope: + case Token_identifier: + case Token_operator: + case Token_char: + case Token_wchar_t: + case Token_bool: + case Token_short: + case Token_int: + case Token_long: + case Token_signed: + case Token_unsigned: + case Token_float: + case Token_double: + case Token_void: + case Token_extern: + case Token_namespace: + case Token_using: + case Token_typedef: + case Token_asm: + case Token_template: + case Token_export: + + case Token_const: // cv + case Token_volatile: // cv + + case Token_public: + case Token_protected: + case Token_private: + case Token_signals: // Qt + case Token_slots: // Qt + return true; + + default: + nextToken(); + } + } + + return false; +} + +bool Parser::skipUntilStatement() +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipUntilStatement() -- token = " << lex->lookAhead(0).text() << endl; + + while( !lex->lookAhead(0).isNull() ){ + switch( lex->lookAhead(0) ){ + case ';': + case '{': + case '}': + case Token_const: + case Token_volatile: + case Token_identifier: + case Token_case: + case Token_default: + case Token_if: + case Token_switch: + case Token_while: + case Token_do: + case Token_for: + case Token_break: + case Token_continue: + case Token_return: + case Token_goto: + case Token_try: + case Token_catch: + case Token_throw: + case Token_char: + case Token_wchar_t: + case Token_bool: + case Token_short: + case Token_int: + case Token_long: + case Token_signed: + case Token_unsigned: + case Token_float: + case Token_double: + case Token_void: + case Token_class: + case Token_struct: + case Token_union: + case Token_enum: + case Token_scope: + case Token_template: + case Token_using: + return true; + + default: + nextToken(); + } + } + + return false; +} + +bool Parser::skip( int l, int r ) +{ + int count = 0; + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + + if( tk == l ) + ++count; + else if( tk == r ) + --count; + else if( l != '{' && (tk == '{' || tk == '}' || tk == ';') ) + return false; + + if( count == 0 ) + return true; + + nextToken(); + } + + return false; +} + +bool Parser::skipCommaExpression( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipCommaExpression()" << endl; + + int start = lex->index(); + + AST::Node expr; + if( !skipExpression(expr) ) + return false; + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( !skipExpression(expr) ){ + reportError( i18n("expression expected") ); + return false; + } + } + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::skipExpression( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipExpression()" << endl; + + int start = lex->index(); + + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + + switch( tk ){ + case '(': + skip( '(', ')' ); + nextToken(); + break; + + case '[': + skip( '[', ']' ); + nextToken(); + break; + +#if 0 + case Token_identifier: + nextToken(); + if( lex->lookAhead( 0 ) == Token_identifier ) + return true; + break; +#endif + + case ';': + case ',': + case ']': + case ')': + case '{': + case '}': + case Token_case: + case Token_default: + case Token_if: + case Token_while: + case Token_do: + case Token_for: + case Token_break: + case Token_continue: + case Token_return: + case Token_goto: + { + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + } + return true; + + default: + nextToken(); + } + } + + return false; +} + +bool Parser::parseName( NameAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseName()" << endl; + + GroupAST::Node winDeclSpec; + parseWinDeclSpec( winDeclSpec ); + + int start = lex->index(); + + NameAST::Node ast = CreateNode<NameAST>(); + + if( lex->lookAhead(0) == Token_scope ){ + ast->setGlobal( true ); + nextToken(); + } + + int idx = lex->index(); + + while( true ){ + ClassOrNamespaceNameAST::Node n; + if( !parseUnqualifiedName(n) ) { + return false; + } + + if( lex->lookAhead(0) == Token_scope ){ + nextToken(); + ast->addClassOrNamespaceName( n ); + if( lex->lookAhead(0) == Token_template ) + nextToken(); /// skip optional template #### @todo CHECK + } else { + ast->setUnqualifiedName( n ); + break; + } + } + + if( idx == lex->index() ) + return false; + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseTranslationUnit( TranslationUnitAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTranslationUnit()" << endl; + + int start = lex->index(); + + m_problems = 0; + TranslationUnitAST::Node tun = CreateNode<TranslationUnitAST>(); + node = tun; + if( lex->lookAhead(0) == Token_comment ) { + node->setComment( lex->lookAhead(0).text() ); + nextToken(); + } + + while( !lex->lookAhead(0).isNull() ){ + DeclarationAST::Node def; + int startDecl = lex->index(); + if( !parseDeclaration(def) ){ + // error recovery + if( startDecl == lex->index() ) + nextToken(); // skip at least one token + skipUntilDeclaration(); + } + node->addDeclaration( def ); + } + + UPDATE_POS( node, start, lex->index() ); + + // force (0,0) as start position + node->setStartPosition( 0, 0 ); + + return m_problems == 0; +} + +bool Parser::parseDeclaration( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclaration()" << endl; + + int start = lex->index(); + + switch( lex->lookAhead(0) ){ + + case ';': + nextToken(); + return true; + + case Token_extern: + return parseLinkageSpecification( node ); + + case Token_namespace: + return parseNamespace( node ); + + case Token_using: + return parseUsing( node ); + + case Token_typedef: + return parseTypedef( node ); + + case Token_asm: + return parseAsmDefinition( node ); + + case Token_template: + case Token_export: + return parseTemplateDeclaration( node ); + + default: + { + // lex->setIndex( start ); + + if( objcp && parseObjcDef(node) ) + return true; + + lex->setIndex( start ); + + GroupAST::Node storageSpec; + parseStorageClassSpecifier( storageSpec ); + + GroupAST::Node cv; + parseCvQualify( cv ); + + TypeSpecifierAST::Node spec; + AST::Node declarator; + if( parseEnumSpecifier(spec) || parseClassSpecifier(spec) ) { + int line, c; + spec->getEndPosition( &line, &c ); + spec->setCvQualify( cv ); + + GroupAST::Node cv2; + parseCvQualify( cv2 ); + spec->setCv2Qualify( cv2 ); + + InitDeclaratorListAST::Node declarators; + parseInitDeclaratorList(declarators); + + SimpleDeclarationAST::Node ast = CreateNode<SimpleDeclarationAST>(); + + ADVANCE( ';', ";" ); + + preparseLineComments( line ); + + ast->setComment( m_commentStore.getCommentInRange( line ) ); + + ast->setStorageSpecifier( storageSpec ); + ast->setTypeSpec( spec ); + ast->setInitDeclaratorList( declarators ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; + } + + lex->setIndex( start ); + return parseDeclarationInternal( node ); + } + + } // end switch +} + +bool Parser::parseLinkageSpecification( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseLinkageSpecification()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_extern ){ + return false; + } + nextToken(); + + LinkageSpecificationAST::Node ast = CreateNode<LinkageSpecificationAST>(); + + int startExternType = lex->index(); + if( lex->lookAhead(0) == Token_string_literal ){ + nextToken(); + AST::Node externType = CreateNode<AST>(); + UPDATE_POS( externType, startExternType, lex->index() ); + + ast->setExternType( externType ); + } + + if( lex->lookAhead(0) == '{' ){ + LinkageBodyAST::Node linkageBody; + parseLinkageBody( linkageBody ); + ast->setLinkageBody( linkageBody ); + } else { + DeclarationAST::Node decl; + if( !parseDeclaration(decl) ){ + reportError( i18n("Declaration syntax error") ); + } + ast->setDeclaration( decl ); + } + + UPDATE_POS( ast, start, lex->index() ); + + node = ast; + + return true; +} + +bool Parser::parseLinkageBody( LinkageBodyAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseLinkageBody()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != '{' ){ + return false; + } + nextToken(); + + LinkageBodyAST::Node lba = CreateNode<LinkageBodyAST>(); + node = lba; + + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + + if( tk == '}' ) + break; + + DeclarationAST::Node def; + int startDecl = lex->index(); + if( parseDeclaration(def) ){ + node->addDeclaration( def ); + } else { + // error recovery + if( startDecl == lex->index() ) + nextToken(); // skip at least one token + skipUntilDeclaration(); + } + } + + clearComment(); + + if( lex->lookAhead(0) != '}' ){ + reportError( i18n("} expected") ); + } else + nextToken(); + + UPDATE_POS( node, start, lex->index() ); + return true; +} + +bool Parser::parseNamespace( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseNamespace()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_namespace ){ + return false; + } + nextToken(); + + int startNamespaceName = lex->index(); + if( lex->lookAhead(0) == Token_identifier ){ + nextToken(); + } + AST::Node namespaceName = CreateNode<AST>(); + UPDATE_POS( namespaceName, startNamespaceName, lex->index() ); + + if ( lex->lookAhead(0) == '=' ) { + // namespace alias + nextToken(); + + NameAST::Node name; + if( parseName(name) ){ + ADVANCE( ';', ";" ); + + NamespaceAliasAST::Node ast = CreateNode<NamespaceAliasAST>(); + ast->setNamespaceName( namespaceName ); + ast->setAliasName( name ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; + } else { + reportError( i18n("namespace expected") ); + return false; + } + } else if( lex->lookAhead(0) != '{' ){ + reportError( i18n("{ expected") ); + return false; + } + + NamespaceAST::Node ast = CreateNode<NamespaceAST>(); + ast->setNamespaceName( namespaceName ); + + LinkageBodyAST::Node linkageBody; + parseLinkageBody( linkageBody ); + + ast->setLinkageBody( linkageBody ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseUsing( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseUsing()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_using ){ + return false; + } + nextToken(); + + if( lex->lookAhead(0) == Token_namespace ){ + if( !parseUsingDirective(node) ){ + return false; + } + UPDATE_POS( node, start, lex->index() ); + return true; + } + + UsingAST::Node ast = CreateNode<UsingAST>(); + + int startTypeName = lex->index(); + if( lex->lookAhead(0) == Token_typename ){ + nextToken(); + AST::Node tn = CreateNode<AST>(); + UPDATE_POS( tn, startTypeName, lex->index() ); + ast->setTypeName( tn ); + } + + NameAST::Node name; + if( !parseName(name) ) + return false; + + ast->setName( name ); + + ADVANCE( ';', ";" ); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseUsingDirective( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseUsingDirective()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_namespace ){ + return false; + } + nextToken(); + + NameAST::Node name; + if( !parseName(name) ){ + reportError( i18n("Namespace name expected") ); + return false; + } + + ADVANCE( ';', ";" ); + + UsingDirectiveAST::Node ast = CreateNode<UsingDirectiveAST>(); + ast->setName( name ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + +bool Parser::parseOperatorFunctionId( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseOperatorFunctionId()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_operator ){ + return false; + } + nextToken(); + + AST::Node op; + if( parseOperator(op) ){ + AST::Node asn = CreateNode<AST>(); + node = asn; + UPDATE_POS( node, start, lex->index() ); + return true; + } else { + // parse cast operator + GroupAST::Node cv; + parseCvQualify(cv); + + TypeSpecifierAST::Node spec; + if( !parseSimpleTypeSpecifier(spec) ){ + syntaxError(); + return false; + } + spec->setCvQualify( cv ); + + GroupAST::Node cv2; + parseCvQualify(cv2); + spec->setCv2Qualify( cv2 ); + + AST::Node ptrOp; + while( parsePtrOperator(ptrOp) ) + ; + + AST::Node asn = CreateNode<AST>(); + node = asn; + UPDATE_POS( node, start, lex->index() ); + return true; + } +} + +bool Parser::parseTemplateArgumentList( TemplateArgumentListAST::Node& node, bool reportError ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTemplateArgumentList()" << endl; + + int start = lex->index(); + + TemplateArgumentListAST::Node ast = CreateNode<TemplateArgumentListAST>(); + + AST::Node templArg; + if( !parseTemplateArgument(templArg) ) + return false; + ast->addArgument( templArg ); + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( !parseTemplateArgument(templArg) ){ + if( reportError ){ + syntaxError(); + break; + } else + return false; + } + ast->addArgument( templArg ); + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseTypedef( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTypedef()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_typedef ){ + return false; + } + nextToken(); + + TypeSpecifierAST::Node spec; + if( !parseTypeSpecifierOrClassSpec(spec) ){ + reportError( i18n("Need a type specifier to declare") ); + return false; + } + + InitDeclaratorListAST::Node declarators; + if( !parseInitDeclaratorList(declarators) ){ + //reportError( i18n("Need an identifier to declare") ); + //return false; + } + + TypedefAST::Node ast = CreateNode<TypedefAST>(); + + + if( comment() ) { + ast->setComment( comment() ); + clearComment(); + + preparseLineComments( currentLine() ); + + if( comment() ) { + ast->addComment( comment() ); + clearComment(); + } + } + + ADVANCE( ';', ";" ); + + ast->setTypeSpec( spec ); + ast->setInitDeclaratorList( declarators ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseAsmDefinition( DeclarationAST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseAsmDefinition()" << endl; + + ADVANCE( Token_asm, "asm" ); + + GroupAST::Node cv; + parseCvQualify( cv ); + + skip( '(', ')' ); + ADVANCE( ')', ")" ); + ADVANCE( ';', ';' ); + + return true; +} + +bool Parser::parseTemplateDeclaration( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTemplateDeclaration()" << endl; + + int start = lex->index(); + + AST::Node exp; + + int startExport = lex->index(); + if( lex->lookAhead(0) == Token_export ){ + nextToken(); + AST::Node n = CreateNode<AST>(); + UPDATE_POS( n, startExport, lex->index() ); + exp = n; + } + + if( lex->lookAhead(0) != Token_template ){ + return false; + } + nextToken(); + + TemplateParameterListAST::Node params; + if( lex->lookAhead(0) == '<' ){ + nextToken(); + parseTemplateParameterList( params ); + + ADVANCE( '>', ">" ); + } + + DeclarationAST::Node def; + if( !parseDeclaration(def) ){ + reportError( i18n("expected a declaration") ); + } + + TemplateDeclarationAST::Node ast = CreateNode<TemplateDeclarationAST>(); + ast->setExported( exp ); + ast->setTemplateParameterList( params ); + ast->setDeclaration( def ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseOperator( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseOperator()" << endl; + QString text = lex->lookAhead(0).text(); + + switch( lex->lookAhead(0) ){ + case Token_new: + case Token_delete: + nextToken(); + if( lex->lookAhead(0) == '[' && lex->lookAhead(1) == ']' ){ + nextToken(); + nextToken(); + text += "[]"; + } + return true; + + case '+': + case '-': + case '*': + case '/': + case '%': + case '^': + case '&': + case '|': + case '~': + case '!': + case '=': + case '<': + case '>': + case ',': + case Token_assign: + case Token_shift: + case Token_eq: + case Token_not_eq: + case Token_leq: + case Token_geq: + case Token_and: + case Token_or: + case Token_incr: + case Token_decr: + case Token_ptrmem: + case Token_arrow: + nextToken(); + return true; + + default: + if( lex->lookAhead(0) == '(' && lex->lookAhead(1) == ')' ){ + nextToken(); + nextToken(); + return true; + } else if( lex->lookAhead(0) == '[' && lex->lookAhead(1) == ']' ){ + nextToken(); + nextToken(); + return true; + } + } + + return false; +} + +bool Parser::parseCvQualify( GroupAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCvQualify()" << endl; + + int start = lex->index(); + + GroupAST::Node ast = CreateNode<GroupAST>(); + + int n = 0; + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + if( tk == Token_const || tk == Token_volatile ){ + ++n; + int startWord = lex->index(); + nextToken(); + AST::Node word = CreateNode<AST>(); + UPDATE_POS( word, startWord, lex->index() ); + ast->addNode( word ); + } else + break; + } + + if( n == 0 ) + return false; + + + ////kdDebug(9007)<< "-----------------> token = " << lex->lookAhead(0).text() << endl; + UPDATE_POS( ast, start, lex->index() ); + + node = ast; + return true; +} + +bool Parser::parseSimpleTypeSpecifier( TypeSpecifierAST::Node& node ) +{ + int start = lex->index(); + bool isIntegral = false; + bool done = false; + + while( !done ){ + + switch( lex->lookAhead(0) ){ + case Token_char: + case Token_wchar_t: + case Token_bool: + case Token_short: + case Token_int: + case Token_long: + case Token_signed: + case Token_unsigned: + case Token_float: + case Token_double: + case Token_void: + isIntegral = true; + nextToken(); + break; + + default: + done = true; + } + } + + TypeSpecifierAST::Node ast = CreateNode<TypeSpecifierAST>(); + if( isIntegral ){ + ClassOrNamespaceNameAST::Node cl = CreateNode<ClassOrNamespaceNameAST>(); + + AST::Node n = CreateNode<AST>(); + UPDATE_POS( n, start, lex->index() ); + cl->setName( n ); + UPDATE_POS( cl, start, lex->index() ); + + NameAST::Node name = CreateNode<NameAST>(); + name->setUnqualifiedName( cl ); + UPDATE_POS( name, start, lex->index() ); + ast->setName( name ); + + } else { + NameAST::Node name; + if( !parseName(name) ){ + lex->setIndex( start ); + return false; + } + ast->setName( name ); + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parsePtrOperator( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parsePtrOperator()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) == '&' ){ + nextToken(); + } else if( lex->lookAhead(0) == '*' ){ + nextToken(); + } else { + int index = lex->index(); + AST::Node memPtr; + if( !parsePtrToMember(memPtr) ){ + lex->setIndex( index ); + return false; + } + } + + GroupAST::Node cv; + parseCvQualify( cv ); + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + +bool Parser::parseTemplateArgument( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTemplateArgument()" << endl; + + int start = lex->index(); + if( parseTypeId(node) ){ + if( lex->lookAhead(0) == ',' || lex->lookAhead(0) == '>' ) + return true; + } + + lex->setIndex( start ); + if( !parseLogicalOrExpression(node, true) ){ + return false; + } + + return true; +} + +bool Parser::parseTypeSpecifier( TypeSpecifierAST::Node& spec ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTypeSpecifier()" << endl; + + GroupAST::Node cv; + parseCvQualify( cv ); + + if( parseElaboratedTypeSpecifier(spec) || parseSimpleTypeSpecifier(spec) ){ + spec->setCvQualify( cv ); + + GroupAST::Node cv2; + parseCvQualify( cv2 ); + spec->setCv2Qualify( cv2 ); + + return true; + } + + return false; +} + +bool Parser::parseDeclarator( DeclaratorAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclarator()" << endl; + + int start = lex->index(); + + DeclaratorAST::Node ast = CreateNode<DeclaratorAST>(); + + DeclaratorAST::Node decl; + NameAST::Node declId; + + AST::Node ptrOp; + while( parsePtrOperator(ptrOp) ){ + ast->addPtrOp( ptrOp ); + } + + if( lex->lookAhead(0) == '(' ){ + nextToken(); + + if( !parseDeclarator(decl) ){ + return false; + } + ast->setSubDeclarator( decl ); + + if( lex->lookAhead(0) != ')'){ + return false; + } + nextToken(); + } else { + + if( lex->lookAhead(0) == ':' ){ + // unnamed bitfield + } else if( parseDeclaratorId(declId) ){ + ast->setDeclaratorId( declId ); + } else { + lex->setIndex( start ); + return false; + } + + if( lex->lookAhead(0) == ':' ){ + nextToken(); + AST::Node expr; + if( !parseConstantExpression(expr) ){ + reportError( i18n("Constant expression expected") ); + } + goto update_pos; + } + } + + { + bool isVector = true; + + while( lex->lookAhead(0) == '[' ){ + int startArray = lex->index(); + nextToken(); + AST::Node expr; + parseCommaExpression( expr ); + + ADVANCE( ']', "]" ); + AST::Node array = CreateNode<AST>(); + UPDATE_POS( array, startArray, lex->index() ); + ast->addArrayDimension( array ); + isVector = true; + } + + bool skipParen = false; + if( lex->lookAhead(0) == Token_identifier && lex->lookAhead(1) == '(' && lex->lookAhead(2) == '(' ){ + nextToken(); + nextToken(); + skipParen = true; + } + + if( ast->subDeclarator() && (!isVector || lex->lookAhead(0) != '(') ){ + lex->setIndex( start ); + return false; + } + + int index = lex->index(); + if( lex->lookAhead(0) == '(' ){ + nextToken(); + + ParameterDeclarationClauseAST::Node params; + if( !parseParameterDeclarationClause(params) ){ + ////kdDebug(9007)<< "----------------------> not a parameter declaration, maybe an initializer!?" << endl; + lex->setIndex( index ); + goto update_pos; + } + ast->setParameterDeclarationClause( params ); + + if( lex->lookAhead(0) != ')' ){ + lex->setIndex( index ); + goto update_pos; + } + + nextToken(); // skip ')' + + int startConstant = lex->index(); + if( lex->lookAhead(0) == Token_const ){ + nextToken(); + AST::Node constant = CreateNode<AST>(); + UPDATE_POS( constant, startConstant, lex->index() ); + ast->setConstant( constant ); + } + + GroupAST::Node except; + if( parseExceptionSpecification(except) ){ + ast->setExceptionSpecification( except ); + } + } + + if( skipParen ){ + if( lex->lookAhead(0) != ')' ){ + reportError( i18n("')' expected") ); + } else + nextToken(); + } + + } + +update_pos: + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseAbstractDeclarator( DeclaratorAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclarator()" << endl; + int start = lex->index(); + + DeclaratorAST::Node ast = CreateNode<DeclaratorAST>(); + + DeclaratorAST::Node decl; + NameAST::Node declId; + + AST::Node ptrOp; + while( parsePtrOperator(ptrOp) ){ + ast->addPtrOp( ptrOp ); + } + + if( lex->lookAhead(0) == '(' ){ + nextToken(); + + if( !parseAbstractDeclarator(decl) ){ + return false; + } + ast->setSubDeclarator( decl ); + + if( lex->lookAhead(0) != ')'){ + return false; + } + nextToken(); + } + + { + + while( lex->lookAhead(0) == '[' ){ + int startArray = lex->index(); + nextToken(); + AST::Node expr; + skipCommaExpression( expr ); + + ADVANCE( ']', "]" ); + AST::Node array = CreateNode<AST>(); + UPDATE_POS( array, startArray, lex->index() ); + ast->addArrayDimension( array ); + } + + bool skipParen = false; + if( lex->lookAhead(0) == Token_identifier && lex->lookAhead(1) == '(' && lex->lookAhead(2) == '(' ){ + nextToken(); + nextToken(); + skipParen = true; + } + + int index = lex->index(); + if( lex->lookAhead(0) == '(' ){ + nextToken(); + + ParameterDeclarationClauseAST::Node params; + if( !parseParameterDeclarationClause(params) ){ + lex->setIndex( index ); + goto UPDATE_POS; + } + ast->setParameterDeclarationClause( params ); + + if( lex->lookAhead(0) != ')' ){ + lex->setIndex( index ); + goto UPDATE_POS; + } else + nextToken(); + + int startConstant = lex->index(); + if( lex->lookAhead(0) == Token_const ){ + nextToken(); + AST::Node constant = CreateNode<AST>(); + UPDATE_POS( constant, startConstant, lex->index() ); + ast->setConstant( constant ); + } + + GroupAST::Node except; + if( parseExceptionSpecification(except) ){ + ast->setExceptionSpecification( except ); + } + } + + if( skipParen ){ + if( lex->lookAhead(0) != ')' ){ + reportError( i18n("')' expected") ); + } else + nextToken(); + } + + } + +UPDATE_POS: + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + +bool Parser::parseEnumSpecifier( TypeSpecifierAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseEnumSpecifier()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_enum ){ + return false; + } + + nextToken(); + + Comment c = comment(); + clearComment(); + NameAST::Node name; + parseName( name ); + + if( lex->lookAhead(0) != '{' ){ + lex->setIndex( start ); + return false; + } + nextToken(); + + EnumSpecifierAST::Node ast = CreateNode<EnumSpecifierAST>(); + ast->setName( name ); + + ast->setComment( c ); + + EnumeratorAST::Node enumerator; + if( parseEnumerator(enumerator) ){ + ast->addEnumerator( enumerator ); + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( !parseEnumerator(enumerator) ){ + //reportError( i18n("Enumerator expected") ); + break; + } + + ast->addEnumerator( enumerator ); + } + } + + clearComment( ); + + if( lex->lookAhead(0) != '}' ) + reportError( i18n("} missing") ); + else + nextToken(); + + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseTemplateParameterList( TemplateParameterListAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTemplateParameterList()" << endl; + + int start = lex->index(); + + TemplateParameterListAST::Node ast = CreateNode<TemplateParameterListAST>(); + + TemplateParameterAST::Node param; + if( !parseTemplateParameter(param) ){ + return false; + } + ast->addTemplateParameter( param ); + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( !parseTemplateParameter(param) ){ + syntaxError(); + break; + } else { + ast->addTemplateParameter( param ); + } + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseTemplateParameter( TemplateParameterAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTemplateParameter()" << endl; + + int start = lex->index(); + TemplateParameterAST::Node ast = CreateNode<TemplateParameterAST>(); + + TypeParameterAST::Node typeParameter; + ParameterDeclarationAST::Node param; + + int tk = lex->lookAhead( 0 ); + + if( (tk == Token_class || tk == Token_typename || tk == Token_template) && parseTypeParameter(typeParameter) ){ + ast->setTypeParameter( typeParameter ); + goto ok; + } + + if( !parseParameterDeclaration(param) ) + return false; + ast->setTypeValueParameter( param ); + +ok: + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseTypeParameter( TypeParameterAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTypeParameter()" << endl; + + int start = lex->index(); + TypeParameterAST::Node ast = CreateNode<TypeParameterAST>(); + + AST_FROM_TOKEN( kind, lex->index() ); + ast->setKind( kind ); + + switch( lex->lookAhead(0) ){ + + case Token_class: + case Token_typename: + { + nextToken(); // skip class + + // parse optional name + NameAST::Node name; + if( parseName(name) ){ + ast->setName( name ); + if( lex->lookAhead(0) == '=' ){ + nextToken(); + + AST::Node typeId; + if( !parseTypeId(typeId) ){ + syntaxError(); + return false; + } + ast->setTypeId( typeId ); + } + } + } + break; + + case Token_template: + { + nextToken(); // skip template + ADVANCE( '<', '<' ); + + TemplateParameterListAST::Node params; + if( !parseTemplateParameterList(params) ){ + return false; + } + ast->setTemplateParameterList( params ); + + ADVANCE( '>', ">" ); + + if( lex->lookAhead(0) == Token_class ) + nextToken(); + + // parse optional name + NameAST::Node name; + if( parseName(name) ){ + ast->setName( name ); + if( lex->lookAhead(0) == '=' ){ + nextToken(); + + AST::Node typeId; + if( !parseTypeId(typeId) ){ + syntaxError(); + return false; + } + ast->setTypeId( typeId ); + } + } + + if( lex->lookAhead(0) == '=' ){ + nextToken(); + + NameAST::Node templ_name; + parseName( templ_name ); + } + } + break; + + default: + return false; + + } // end switch + + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseStorageClassSpecifier( GroupAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseStorageClassSpecifier()" << endl; + + int start = lex->index(); + GroupAST::Node ast = CreateNode<GroupAST>(); + + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + if( tk == Token_friend || tk == Token_auto || tk == Token_register || tk == Token_static || + tk == Token_extern || tk == Token_mutable ){ + int startNode = lex->index(); + nextToken(); + + AST::Node n = CreateNode<AST>(); + UPDATE_POS( n, startNode, lex->index() ); + ast->addNode( n ); + } else + break; + } + + if( ast->nodeList().count() == 0 ) + return false; + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseFunctionSpecifier( GroupAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseFunctionSpecifier()" << endl; + + int start = lex->index(); + GroupAST::Node ast = CreateNode<GroupAST>(); + + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + if( tk == Token_inline || tk == Token_virtual || tk == Token_explicit ){ + int startNode = lex->index(); + nextToken(); + + AST::Node n = CreateNode<AST>(); + UPDATE_POS( n, startNode, lex->index() ); + ast->addNode( n ); + } else { + break; + } + } + + if( ast->nodeList().count() == 0 ) + return false; + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseTypeId( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTypeId()" << endl; + + /// @todo implement the AST for typeId + int start = lex->index(); + AST::Node ast = CreateNode<AST>(); + + TypeSpecifierAST::Node spec; + if( !parseTypeSpecifier(spec) ){ + return false; + } + + DeclaratorAST::Node decl; + parseAbstractDeclarator( decl ); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseInitDeclaratorList( InitDeclaratorListAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInitDeclaratorList()" << endl; + + int start = lex->index(); + + InitDeclaratorListAST::Node ast = CreateNode<InitDeclaratorListAST>(); + InitDeclaratorAST::Node decl; + + if( !parseInitDeclarator(decl) ){ + return false; + } + ast->addInitDeclarator( decl ); + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( !parseInitDeclarator(decl) ){ + syntaxError(); + break; + } + ast->addInitDeclarator( decl ); + } + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInitDeclaratorList() -- end" << endl; + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseParameterDeclarationClause( ParameterDeclarationClauseAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseParameterDeclarationClause()" << endl; + + int start = lex->index(); + + ParameterDeclarationClauseAST::Node ast = CreateNode<ParameterDeclarationClauseAST>(); + + ParameterDeclarationListAST::Node params; + if( !parseParameterDeclarationList(params) ){ + + if ( lex->lookAhead(0) == ')' ) + goto good; + + if( lex->lookAhead(0) == Token_ellipsis && lex->lookAhead(1) == ')' ){ + AST_FROM_TOKEN( ellipsis, lex->index() ); + ast->setEllipsis( ellipsis ); + nextToken(); + goto good; + } + return false; + } + + if( lex->lookAhead(0) == Token_ellipsis ){ + AST_FROM_TOKEN( ellipsis, lex->index() ); + ast->setEllipsis( ellipsis ); + nextToken(); + } + +good: + ast->setParameterDeclarationList( params ); + + /// @todo add ellipsis + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + +void Parser::nextToken( bool skipComm ) { + lex->nextToken(); + if( skipComm ) { + if( lex->lookAhead(0) == Token_comment ) { + processComment(); + nextToken(); + } + } +} + + +Comment Parser::comment() { + return m_commentStore.latestComment(); +} + + +void Parser::preparseLineComments( int l ) { + for( int a = 0; a < 40; a++ ) { + if( lex->lookAhead(a).isNull() ) break; + int line, col; + lex->lookAhead( a ).getStartPosition( &line, &col ); + if( line < l ) { + continue; + } else if( line == l ) { + if( lex->lookAhead( a ) == Token_comment ) { + processComment( a ); + } + } else { + break; + } + } +} + + +void Parser::processComment( int offset ) { + int line, col; + lex->lookAhead( offset ).getStartPosition( &line, &col ); + m_commentStore.addComment( Comment( lex->lookAhead(offset).text(), line ) ); +} + +template<class Type> +void Parser::eventuallyTakeComment( int startLn, int endLn, Type& ast ) { + if( comment().line() >= startLn && comment().line() <= endLn ) { + if( &(*ast) ) { + if( comment() ) { + ast->setComment( comment() ); + } + } + + clearComment(); + } +} + +template<class Type> +void Parser::eventuallyTakeComment( Type& ast ) { + if( &(*ast) && comment() ) { + ast->setComment( comment() ); + } + + clearComment(); +} + +void Parser::clearComment( ) { + + m_commentStore.clear(); + +} + +int Parser::currentLine() { + int ln, col; + lex->lookAhead( 0 ).getStartPosition( &ln, &col ); + return ln; +} + +bool Parser::parseParameterDeclarationList( ParameterDeclarationListAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseParameterDeclarationList()" << endl; + + int start = lex->index(); + + ParameterDeclarationListAST::Node ast = CreateNode<ParameterDeclarationListAST>(); + + ParameterDeclarationAST::Node param; + if( !parseParameterDeclaration(param) ){ + lex->setIndex( start ); + return false; + } + ast->addParameter( param ); + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( lex->lookAhead(0) == Token_ellipsis ) + break; + + if( !parseParameterDeclaration(param) ){ + lex->setIndex( start ); + return false; + } + ast->addParameter( param ); + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseParameterDeclaration( ParameterDeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseParameterDeclaration()" << endl; + + int start = lex->index(); + + // parse decl spec + TypeSpecifierAST::Node spec; + if( !parseTypeSpecifier(spec) ){ + lex->setIndex( start ); + return false; + } + + int index = lex->index(); + + DeclaratorAST::Node decl; + if( !parseDeclarator(decl) ){ + lex->setIndex( index ); + + // try with abstract declarator + if( !parseAbstractDeclarator(decl) ) + return false; + } + + AST::Node expr; + if( lex->lookAhead(0) == '=' ){ + nextToken(); + if( !parseLogicalOrExpression(expr,true) ){ + //reportError( i18n("Expression expected") ); + } + } + + ParameterDeclarationAST::Node ast = CreateNode<ParameterDeclarationAST>(); + ast->setTypeSpec( spec ); + ast->setDeclarator( decl ); + ast->setExpression( expr ); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + +bool Parser::parseClassSpecifier( TypeSpecifierAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseClassSpecifier()" << endl; + + int start = lex->index(); + + AST::Node classKey; + int classKeyStart = lex->index(); + + int kind = lex->lookAhead( 0 ); + if( kind == Token_class || kind == Token_struct || kind == Token_union ){ + AST::Node asn = CreateNode<AST>(); + classKey = asn; + nextToken(); + UPDATE_POS( classKey, classKeyStart, lex->index() ); + } else { + return false; + } + + GroupAST::Node winDeclSpec; + parseWinDeclSpec( winDeclSpec ); + + while( lex->lookAhead(0) == Token_identifier && lex->lookAhead(1) == Token_identifier ) + nextToken(); + + NameAST::Node name; + parseName( name ); + + BaseClauseAST::Node bases; + if( lex->lookAhead(0) == ':' ){ + if( !parseBaseClause(bases) ){ + skipUntil( '{' ); + } + } + + if( lex->lookAhead(0) != '{' ){ + lex->setIndex( start ); + return false; + } + + ClassSpecifierAST::Node ast = CreateNode<ClassSpecifierAST>(); + + eventuallyTakeComment( ast ); + + ADVANCE( '{', '{' ); + + + ast->setWinDeclSpec( winDeclSpec ); + ast->setClassKey( classKey ); + ast->setName( name ); + ast->setBaseClause( bases ); + + + while( !lex->lookAhead(0).isNull() ){ + if( lex->lookAhead(0) == '}' ) + break; + + DeclarationAST::Node memSpec; + int startDecl = lex->index(); + if( !parseMemberSpecification(memSpec) ){ + if( startDecl == lex->index() ) + nextToken(); // skip at least one token + skipUntilDeclaration(); + } else + ast->addDeclaration( memSpec ); + } + + clearComment(); + + if( lex->lookAhead(0) != '}' ){ + reportError( i18n("} missing") ); + } else + nextToken(); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseAccessSpecifier( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseAccessSpecifier()" << endl; + + int start = lex->index(); + + switch( lex->lookAhead(0) ){ + case Token_public: + case Token_protected: + case Token_private: { + AST::Node asn = CreateNode<AST>(); + node = asn; + nextToken(); + UPDATE_POS( node, start, lex->index() ); + return true; + } + } + + return false; +} + +bool Parser::parseMemberSpecification( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseMemberSpecification()" << endl; + + int start = lex->index(); + + AST::Node access; + + if( lex->lookAhead(0) == ';' ){ + nextToken(); + return true; + } else if( lex->lookAhead(0) == Token_Q_OBJECT || lex->lookAhead(0) == Token_K_DCOP ){ + nextToken(); + return true; + } else if( lex->lookAhead(0) == Token_signals || lex->lookAhead(0) == Token_k_dcop || lex->lookAhead(0) == Token_k_dcop_signals ){ + AccessDeclarationAST::Node ast = CreateNode<AccessDeclarationAST>(); + nextToken(); + AST::Node n = CreateNode<AST>(); + UPDATE_POS( n, start, lex->index() ); + ast->addAccess( n ); + ADVANCE( ':', ":" ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; + } else if( parseTypedef(node) ){ + return true; + } else if( parseUsing(node) ){ + return true; + } else if( parseTemplateDeclaration(node) ){ + return true; + } else if( parseAccessSpecifier(access) ){ + AccessDeclarationAST::Node ast = CreateNode<AccessDeclarationAST>(); + ast->addAccess( access ); + + int startSlot = lex->index(); + if( lex->lookAhead(0) == Token_slots ){ + nextToken(); + AST::Node sl = CreateNode<AST>(); + UPDATE_POS( sl, startSlot, lex->index() ); + ast->addAccess( sl ); + } + ADVANCE( ':', ":" ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; + } + + lex->setIndex( start ); + + GroupAST::Node storageSpec; + parseStorageClassSpecifier( storageSpec ); + + GroupAST::Node cv; + parseCvQualify( cv ); + + TypeSpecifierAST::Node spec; + if( parseEnumSpecifier(spec) || parseClassSpecifier(spec) ){ + spec->setCvQualify( cv ); + + GroupAST::Node cv2; + parseCvQualify( cv2 ); + spec->setCv2Qualify( cv2 ); + + InitDeclaratorListAST::Node declarators; + parseInitDeclaratorList( declarators ); + ADVANCE( ';', ";" ); + + SimpleDeclarationAST::Node ast = CreateNode<SimpleDeclarationAST>(); + ast->setTypeSpec( spec ); + ast->setInitDeclaratorList( declarators ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; + } + + lex->setIndex( start ); + return parseDeclarationInternal( node ); +} + +bool Parser::parseCtorInitializer( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCtorInitializer()" << endl; + + if( lex->lookAhead(0) != ':' ){ + return false; + } + nextToken(); + + AST::Node inits; + if( !parseMemInitializerList(inits) ){ + reportError( i18n("Member initializers expected") ); + } + + return true; +} + +bool Parser::parseElaboratedTypeSpecifier( TypeSpecifierAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseElaboratedTypeSpecifier()" << endl; + + int start = lex->index(); + + int tk = lex->lookAhead( 0 ); + if( tk == Token_class || + tk == Token_struct || + tk == Token_union || + tk == Token_enum || + tk == Token_typename ) + { + AST::Node kind = CreateNode<AST>(); + nextToken(); + UPDATE_POS( kind, start, lex->index() ); + + NameAST::Node name; + + if( parseName(name) ){ + ElaboratedTypeSpecifierAST::Node ast = CreateNode<ElaboratedTypeSpecifierAST>(); + ast->setKind( kind ); + ast->setName( name ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; + } + } + + lex->setIndex( start ); + return false; +} + +bool Parser::parseDeclaratorId( NameAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclaratorId()" << endl; + return parseName( node ); +} + +bool Parser::parseExceptionSpecification( GroupAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseExceptionSpecification()" << endl; + + if( lex->lookAhead(0) != Token_throw ){ + return false; + } + nextToken(); + + ADVANCE( '(', "(" ); + if( lex->lookAhead(0) == Token_ellipsis ){ + // extension found in MSVC++ 7.x headers + int start = lex->index(); + GroupAST::Node ast = CreateNode<GroupAST>(); + AST_FROM_TOKEN( ellipsis, lex->index() ); + ast->addNode( ellipsis ); + nextToken(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + } else if( lex->lookAhead(0) == ')' ) { + node = CreateNode<GroupAST>(); + } else { + parseTypeIdList( node ); + } + ADVANCE( ')', ")" ); + + return true; +} + +bool Parser::parseEnumerator( EnumeratorAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseEnumerator()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_identifier ){ + return false; + } + + nextToken(); + + + EnumeratorAST::Node ena = CreateNode<EnumeratorAST>(); + node = ena; + + AST::Node id = CreateNode<AST>(); + UPDATE_POS( id, start, lex->index() ); + node->setId( id ); + int line = currentLine(); + + if( lex->lookAhead(0) == '=' ){ + nextToken(); + + AST::Node expr; + line = currentLine(); + if( !parseConstantExpression(expr) ){ + reportError( i18n("Constant expression expected") ); + } + node->setExpr( expr ); + } + + UPDATE_POS( node, start, lex->index() ); + + preparseLineComments( line ); + + node->setComment( m_commentStore.getCommentInRange( line ) ); + + return true; +} + +bool Parser::parseInitDeclarator( InitDeclaratorAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInitDeclarator()" << endl; + + int start = lex->index(); + + DeclaratorAST::Node decl; + AST::Node init; + if( !parseDeclarator(decl) ){ + return false; + } + + parseInitializer( init ); + + InitDeclaratorAST::Node ast = CreateNode<InitDeclaratorAST>(); + ast->setDeclarator( decl ); + ast->setInitializer( init ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + + +bool Parser::parseBaseClause( BaseClauseAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseBaseClause()" << endl; + + int start = lex->index(); + if( lex->lookAhead(0) != ':' ){ + return false; + } + nextToken(); + + BaseClauseAST::Node bca = CreateNode<BaseClauseAST>(); + + BaseSpecifierAST::Node baseSpec; + if( parseBaseSpecifier(baseSpec) ){ + bca->addBaseSpecifier( baseSpec ); + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( !parseBaseSpecifier(baseSpec) ){ + reportError( i18n("Base class specifier expected") ); + return false; + } + bca->addBaseSpecifier( baseSpec ); + } + } else + return false; + + UPDATE_POS( bca, start, lex->index() ); + node = bca; + + return true; +} + +bool Parser::parseInitializer( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInitializer()" << endl; + + if( lex->lookAhead(0) == '=' ){ + nextToken(); + + AST::Node init; + if( !parseInitializerClause(node) ){ + reportError( i18n("Initializer clause expected") ); + return false; + } + } else if( lex->lookAhead(0) == '(' ){ + nextToken(); + AST::Node expr; + skipCommaExpression( expr ); + + ADVANCE( ')', ")" ); + } + + return false; +} + +bool Parser::parseMemInitializerList( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseMemInitializerList()" << endl; + + AST::Node init; + if( !parseMemInitializer(init) ){ + return false; + } + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( parseMemInitializer(init) ){ + } else { + break; + } + } + + return true; +} + +bool Parser::parseMemInitializer( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseMemInitializer()" << endl; + + NameAST::Node initId; + if( !parseMemInitializerId(initId) ){ + reportError( i18n("Identifier expected") ); + return false; + } + ADVANCE( '(', '(' ); + AST::Node expr; + skipCommaExpression( expr ); + ADVANCE( ')', ')' ); + + return true; +} + +bool Parser::parseTypeIdList( GroupAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTypeIdList()" << endl; + + int start = lex->index(); + + AST::Node typeId; + if( !parseTypeId(typeId) ){ + return false; + } + + GroupAST::Node ast = CreateNode<GroupAST>(); + ast->addNode( typeId ); + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + if( parseTypeId(typeId) ){ + ast->addNode( typeId ); + } else { + reportError( i18n("Type id expected") ); + break; + } + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseBaseSpecifier( BaseSpecifierAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseBaseSpecifier()" << endl; + + int start = lex->index(); + BaseSpecifierAST::Node ast = CreateNode<BaseSpecifierAST>(); + + AST::Node access; + if( lex->lookAhead(0) == Token_virtual ){ + AST_FROM_TOKEN( virt, lex->index() ); + ast->setIsVirtual( virt ); + + nextToken(); + + parseAccessSpecifier( access ); + } else { + parseAccessSpecifier( access ); + + if( lex->lookAhead(0) == Token_virtual ){ + AST_FROM_TOKEN( virt, lex->index() ); + ast->setIsVirtual( virt ); + nextToken(); + } + } + + NameAST::Node name; + if( !parseName(name) ){ + reportError( i18n("Class name expected") ); + } + + ast->setAccess( access ); + ast->setName( name ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + +bool Parser::parseInitializerClause( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInitializerClause()" << endl; + + if( lex->lookAhead(0) == '{' ){ + if( !skip('{','}') ){ + reportError( i18n("} missing") ); + } else { + clearComment(); + nextToken(); + } + } else { + if( !parseAssignmentExpression(node) ){ + //reportError( i18n("Expression expected") ); + } + } + + return true; +} + +bool Parser::parseMemInitializerId( NameAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseMemInitializerId()" << endl; + + return parseName( node ); +} + +bool Parser::parsePtrToMember( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parsePtrToMember()" << endl; + + if( lex->lookAhead(0) == Token_scope ){ + nextToken(); + } + + while( lex->lookAhead(0) == Token_identifier ){ + nextToken(); + + if( lex->lookAhead(0) == Token_scope && lex->lookAhead(1) == '*' ){ + nextToken(); // skip :: + nextToken(); // skip * + return true; + } else + break; + } + + return false; +} + +bool Parser::parseUnqualifiedName( ClassOrNamespaceNameAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseUnqualifiedName()" << endl; + + int start = lex->index(); + bool isDestructor = false; + + ClassOrNamespaceNameAST::Node ast = CreateNode<ClassOrNamespaceNameAST>(); + + if( lex->lookAhead(0) == Token_identifier ){ + int startName = lex->index(); + AST::Node n = CreateNode<AST>(); + nextToken(); + UPDATE_POS( n, startName, lex->index() ); + ast->setName( n ); + } else if( lex->lookAhead(0) == '~' && lex->lookAhead(1) == Token_identifier ){ + int startName = lex->index(); + AST::Node n = CreateNode<AST>(); + nextToken(); // skip ~ + nextToken(); // skip classname + UPDATE_POS( n, startName, lex->index() ); + ast->setName( n ); + isDestructor = true; + } else if( lex->lookAhead(0) == Token_operator ){ + AST::Node n; + if( !parseOperatorFunctionId(n) ) + return false; + ast->setName( n ); + } else { + return false; + } + + if( !isDestructor ){ + + int index = lex->index(); + + if( lex->lookAhead(0) == '<' ){ + nextToken(); + + // optional template arguments + TemplateArgumentListAST::Node args; + parseTemplateArgumentList( args ); + + if( lex->lookAhead(0) != '>' ){ + lex->setIndex( index ); + } else { + nextToken(); + ast->setTemplateArgumentList( args ); + } + } + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseStringLiteral( AST::Node& /*node*/ ) +{ + while( !lex->lookAhead(0).isNull() ) { + if( lex->lookAhead(0) == Token_identifier && + lex->lookAhead(0).text() == "L" && lex->lookAhead(1) == Token_string_literal ) { + + nextToken(); + nextToken(); + } else if( lex->lookAhead(0) == Token_string_literal ) { + nextToken(); + } else + return false; + } + return true; +} + +bool Parser::skipExpressionStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipExpressionStatement()" << endl; + + int start = lex->index(); + + AST::Node expr; + skipCommaExpression( expr ); + + ADVANCE( ';', ";" ); + + ExpressionStatementAST::Node ast = CreateNode<ExpressionStatementAST>(); + ast->setExpression( expr ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseStatement( StatementAST::Node& node ) // thanks to fiore@8080.it ;) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseStatement()" << endl; + switch( lex->lookAhead(0) ){ + + case Token_while: + return parseWhileStatement( node ); + + case Token_do: + return parseDoStatement( node ); + + case Token_for: + return parseForStatement( node ); + + case Token_foreach: + return parseForEachStatement( node ); + + case Token_if: + return parseIfStatement( node ); + + case Token_switch: + return parseSwitchStatement( node ); + + case Token_try: + return parseTryBlockStatement( node ); + + case Token_case: + case Token_default: + return parseLabeledStatement( node ); + + case Token_break: + case Token_continue: + nextToken(); + ADVANCE( ';', ";" ); + return true; + + case Token_goto: + nextToken(); + ADVANCE( Token_identifier, "identifier" ); + ADVANCE( ';', ";" ); + return true; + + case Token_return: + { + nextToken(); + AST::Node expr; + skipCommaExpression( expr ); + ADVANCE( ';', ";" ); + } + return true; + + case '{': + return parseCompoundStatement( node ); + + case Token_identifier: + if( parseLabeledStatement(node) ) + return true; + break; + } + + ////kdDebug(9007)<< "------------> try with declaration statement" << endl; + if ( parseDeclarationStatement(node) ) + return true; + + return skipExpressionStatement( node ); +} + +bool Parser::parseCondition( ConditionAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCondition()" << endl; + + int start = lex->index(); + + ConditionAST::Node ast = CreateNode<ConditionAST>(); + + TypeSpecifierAST::Node spec; + if( parseTypeSpecifier(spec) ){ + DeclaratorAST::Node decl; + if( parseDeclarator(decl) ) { + if( lex->lookAhead(0) == '=' ) { + nextToken(); + + AST::Node expr; + if( skipExpression(expr) ){ + ast->setTypeSpec( spec ); + ast->setDeclarator( decl ); + ast->setExpression( expr ); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; + } + } else { + ast->setTypeSpec( spec ); + ast->setDeclarator( decl ); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; + } + } + } + + lex->setIndex( start ); + + AST::Node expr; + if( !skipCommaExpression(expr) ) + return false; + + ast->setExpression( expr ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + + +bool Parser::parseWhileStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseWhileStatement()" << endl; + int start = lex->index(); + + ADVANCE( Token_while, "while" ); + ADVANCE( '(' , "(" ); + + ConditionAST::Node cond; + if( !parseCondition(cond) ){ + reportError( i18n("condition expected") ); + return false; + } + ADVANCE( ')', ")" ); + + StatementAST::Node body; + if( !parseStatement(body) ){ + reportError( i18n("statement expected") ); + } + + WhileStatementAST::Node ast = CreateNode<WhileStatementAST>(); + ast->setCondition( cond ); + ast->setStatement( body ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseDoStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDoStatement()" << endl; + int start = lex->index(); + + ADVANCE( Token_do, "do" ); + + StatementAST::Node body; + if( !parseStatement(body) ){ + reportError( i18n("statement expected") ); + //return false; + } + + ADVANCE_NR( Token_while, "while" ); + ADVANCE_NR( '(' , "(" ); + + AST::Node expr; + if( !skipCommaExpression(expr) ){ + reportError( i18n("expression expected") ); + //return false; + } + + ADVANCE_NR( ')', ")" ); + ADVANCE_NR( ';', ";" ); + + DoStatementAST::Node ast = CreateNode<DoStatementAST>(); + ast->setStatement( body ); + //ast->setCondition( condition ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseForStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseForStatement()" << endl; + int start = lex->index(); + + ADVANCE( Token_for, "for" ); + ADVANCE( '(', "(" ); + + StatementAST::Node init; + if( !parseForInitStatement(init) ){ + reportError( i18n("for initialization expected") ); + return false; + } + + ConditionAST::Node cond; + parseCondition( cond ); + ADVANCE( ';', ";" ); + + AST::Node expr; + skipCommaExpression( expr ); + ADVANCE( ')', ")" ); + + StatementAST::Node body; + parseStatement(body); + + ForStatementAST::Node ast = CreateNode<ForStatementAST>(); + ast->setInitStatement( init ); + ast->setCondition( cond ); + // ast->setExpression( expression ); + ast->setStatement( body ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +// qt4 [erbsland] +///@todo add the right parsing for the foreach statement +bool Parser::parseForEachStatement( StatementAST::Node& node ) +{ + int start = lex->index(); + + ADVANCE( Token_foreach, "foreach" ); + ADVANCE( '(', "(" ); + + AST::Node expr; + // replace with the right parsing + skipCommaExpression( expr ); + ADVANCE( ')', ")" ); + + StatementAST::Node body; + parseStatement(body); + + ForEachStatementAST::Node ast = CreateNode<ForEachStatementAST>(); + // add here the parser results + ast->setStatement( body ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseForInitStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseForInitStatement()" << endl; + + if ( parseDeclarationStatement(node) ) + return true; + + return skipExpressionStatement( node ); +} + +bool Parser::parseCompoundStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCompoundStatement()" << endl; + int start = lex->index(); + + if( lex->lookAhead(0) != '{' ){ + return false; + } + nextToken(); + + StatementListAST::Node ast = CreateNode<StatementListAST>(); + + while( !lex->lookAhead(0).isNull() ){ + if( lex->lookAhead(0) == '}' ) + break; + + StatementAST::Node stmt; + int startStmt = lex->index(); + if( !parseStatement(stmt) ){ + if( startStmt == lex->index() ) + nextToken(); + skipUntilStatement(); + } else { + ast->addStatement( stmt ); + } + } + + clearComment(); + + if( lex->lookAhead(0) != '}' ){ + reportError( i18n("} expected") ); + } else { + nextToken(); + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseIfStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseIfStatement()" << endl; + + int start = lex->index(); + + ADVANCE( Token_if, "if" ); + + ADVANCE( '(' , "(" ); + + IfStatementAST::Node ast = CreateNode<IfStatementAST>(); + + ConditionAST::Node cond; + if( !parseCondition(cond) ){ + reportError( i18n("condition expected") ); + return false; + } + ADVANCE( ')', ")" ); + + StatementAST::Node stmt; + if( !parseStatement(stmt) ){ + reportError( i18n("statement expected") ); + } + + ast->setCondition( cond ); + ast->setStatement( stmt ); + + if( lex->lookAhead(0) == Token_else ){ + nextToken(); + StatementAST::Node elseStmt; + if( !parseStatement(elseStmt) ) { + reportError( i18n("statement expected") ); + } + ast->setElseStatement( elseStmt ); + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseSwitchStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseSwitchStatement()" << endl; + int start = lex->index(); + ADVANCE( Token_switch, "switch" ); + + ADVANCE( '(' , "(" ); + + ConditionAST::Node cond; + if( !parseCondition(cond) ){ + reportError( i18n("condition expected") ); + return false; + } + ADVANCE( ')', ")" ); + + StatementAST::Node stmt; + if( !parseCompoundStatement(stmt) ){ + syntaxError(); + return false; + } + + SwitchStatementAST::Node ast = CreateNode<SwitchStatementAST>(); + ast->setCondition( cond ); + ast->setStatement( stmt ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseLabeledStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseLabeledStatement()" << endl; + switch( lex->lookAhead(0) ){ + case Token_identifier: + case Token_default: + if( lex->lookAhead(1) == ':' ){ + nextToken(); + nextToken(); + + StatementAST::Node stmt; + if( parseStatement(stmt) ){ + node = stmt; + return true; + } + } + break; + + case Token_case: + { + nextToken(); + AST::Node expr; + if( !parseConstantExpression(expr) ){ + reportError( i18n("expression expected") ); + } else if( lex->lookAhead(0) == Token_ellipsis ){ + nextToken(); + + AST::Node expr2; + if( !parseConstantExpression(expr2) ){ + reportError( i18n("expression expected") ); + } + } + ADVANCE( ':', ":" ); + + StatementAST::Node stmt; + if( parseStatement(stmt) ){ + node = stmt; + return true; + } + } + break; + + } + + return false; +} + +bool Parser::parseBlockDeclaration( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseBlockDeclaration()" << endl; + switch( lex->lookAhead(0) ) { + case Token_typedef: + return parseTypedef( node ); + case Token_using: + return parseUsing( node ); + case Token_asm: + return parseAsmDefinition( node ); + case Token_namespace: + return parseNamespaceAliasDefinition( node ); + } + + int start = lex->index(); + + GroupAST::Node storageSpec; + parseStorageClassSpecifier( storageSpec ); + + GroupAST::Node cv; + parseCvQualify( cv ); + + TypeSpecifierAST::Node spec; + if ( !parseTypeSpecifierOrClassSpec(spec) ) { // replace with simpleTypeSpecifier?!?! + lex->setIndex( start ); + return false; + } + spec->setCvQualify( cv ); + + GroupAST::Node cv2; + parseCvQualify( cv2 ); + spec->setCv2Qualify( cv2 ); + + InitDeclaratorListAST::Node declarators; + parseInitDeclaratorList( declarators ); + + if( lex->lookAhead(0) != ';' ){ + lex->setIndex( start ); + return false; + } + nextToken(); + + SimpleDeclarationAST::Node ast = CreateNode<SimpleDeclarationAST>(); + ast->setTypeSpec( spec ); + ast->setInitDeclaratorList( declarators ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseNamespaceAliasDefinition( DeclarationAST::Node& /*node*/ ) +{ + if ( lex->lookAhead(0) != Token_namespace ) { + return false; + } + nextToken(); + + ADVANCE( Token_identifier, "identifier" ); + ADVANCE( '=', "=" ); + + NameAST::Node name; + if( !parseName(name) ){ + reportError( i18n("Namespace name expected") ); + } + + ADVANCE( ';', ";" ); + + return true; + +} + +bool Parser::parseDeclarationStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclarationStatement()" << endl; + + int start = lex->index(); + + DeclarationAST::Node decl; + if ( !parseBlockDeclaration(decl) ){ + return false; + } + + DeclarationStatementAST::Node ast = CreateNode<DeclarationStatementAST>(); + ast->setDeclaration( decl ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + ////kdDebug(9007)<< "---------------------> found a block declaration" << endl; + return true; +} + +bool Parser::parseDeclarationInternal( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclarationInternal()" << endl; + + int start = lex->index(); + + // that is for the case '__declspec(dllexport) int ...' or + // '__declspec(dllexport) inline int ...', etc. + GroupAST::Node winDeclSpec; + parseWinDeclSpec( winDeclSpec ); + + GroupAST::Node funSpec; + bool hasFunSpec = parseFunctionSpecifier( funSpec ); + + GroupAST::Node storageSpec; + bool hasStorageSpec = parseStorageClassSpecifier( storageSpec ); + + if( hasStorageSpec && !hasFunSpec ) + hasFunSpec = parseFunctionSpecifier( funSpec ); + + // that is for the case 'friend __declspec(dllexport) ....' + GroupAST::Node winDeclSpec2; + parseWinDeclSpec( winDeclSpec2 ); + + GroupAST::Node cv; + parseCvQualify( cv ); + + int index = lex->index(); + NameAST::Node name; + if( parseName(name) && lex->lookAhead(0) == '(' ){ + // no type specifier, maybe a constructor or a cast operator?? + + lex->setIndex( index ); + + InitDeclaratorAST::Node declarator; + if( parseInitDeclarator(declarator) ){ + int endSignature = lex->index(); + + switch( lex->lookAhead(0) ){ + case ';': + { + nextToken(); + + InitDeclaratorListAST::Node declarators = CreateNode<InitDeclaratorListAST>(); + + // update declarators position + int startLine, startColumn, endLine, endColumn; + if( declarator.get() ){ + declarator->getStartPosition( &startLine, &startColumn ); + declarator->getEndPosition( &endLine, &endColumn ); + declarators->setStartPosition( startLine, startColumn ); + declarators->setEndPosition( endLine, endColumn ); + } + declarators->addInitDeclarator( declarator ); + + SimpleDeclarationAST::Node ast = CreateNode<SimpleDeclarationAST>(); + ast->setInitDeclaratorList( declarators ); + ast->setText( toString(start, endSignature) ); + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; + + } + break; + + case ':': + { + AST::Node ctorInit; + StatementListAST::Node funBody; + if( parseCtorInitializer(ctorInit) && parseFunctionBody(funBody) ){ + FunctionDefinitionAST::Node ast = CreateNode<FunctionDefinitionAST>(); + ast->setStorageSpecifier( storageSpec ); + ast->setFunctionSpecifier( funSpec ); + ast->setInitDeclarator( declarator ); + ast->setFunctionBody( funBody ); + ast->setText( toString(start, endSignature) ); + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; + } + } + break; + + case '{': + { + StatementListAST::Node funBody; + if( parseFunctionBody(funBody) ){ + FunctionDefinitionAST::Node ast = CreateNode<FunctionDefinitionAST>(); + ast->setStorageSpecifier( storageSpec ); + ast->setFunctionSpecifier( funSpec ); + ast->setInitDeclarator( declarator ); + ast->setText( toString(start, endSignature) ); + ast->setFunctionBody( funBody ); + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; + } + } + break; + + case '(': + case '[': + // ops!! it seems a declarator + goto start_decl; + break; + } + + } + + syntaxError(); + return false; + } + +start_decl: + lex->setIndex( index ); + + if( lex->lookAhead(0) == Token_const && lex->lookAhead(1) == Token_identifier && lex->lookAhead(2) == '=' ){ + // constant definition + nextToken(); + InitDeclaratorListAST::Node declarators; + if( parseInitDeclaratorList(declarators) ){ + ADVANCE( ';', ";" ); + DeclarationAST::Node ast = CreateNode<DeclarationAST>(); + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; + } + syntaxError(); + return false; + } + + Comment mcomment = comment(); + clearComment(); + + TypeSpecifierAST::Node spec; + if( parseTypeSpecifier(spec) ){ + if ( !hasFunSpec ) + parseFunctionSpecifier( funSpec ); // e.g. "void inline" + spec->setCvQualify( cv ); + + InitDeclaratorListAST::Node declarators; + + InitDeclaratorAST::Node decl; + int startDeclarator = lex->index(); + bool maybeFunctionDefinition = false; + + if( lex->lookAhead(0) != ';' ){ + if( parseInitDeclarator(decl) && lex->lookAhead(0) == '{' ){ + // function definition + maybeFunctionDefinition = true; + } else { + lex->setIndex( startDeclarator ); + if( !parseInitDeclaratorList(declarators) ){ + syntaxError(); + return false; + } + } + } + + int endSignature = lex->index(); + switch( lex->lookAhead(0) ){ + case ';': + { + nextToken(); + SimpleDeclarationAST::Node ast = CreateNode<SimpleDeclarationAST>(); + int line, col; + ast->setComment( mcomment ); + if( &(*decl) ) { + decl->getEndPosition( &line, &col ); + + preparseLineComments( line ); + Comment c = m_commentStore.getCommentInRange( line ); + if( c ) { + ast->addComment( c ); + } + } + + ast->setStorageSpecifier( storageSpec ); + ast->setFunctionSpecifier( funSpec ); + ast->setText( toString(start, endSignature) ); + ast->setTypeSpec( spec ); + ast->setWinDeclSpec( winDeclSpec ); + ast->setInitDeclaratorList( declarators ); + + node = ast; + UPDATE_POS( node, start, lex->index() ); + } + return true; + + case '{': + { + if( !maybeFunctionDefinition ){ + syntaxError(); + return false; + } + StatementListAST::Node funBody; + if ( parseFunctionBody(funBody) ) { + FunctionDefinitionAST::Node ast = CreateNode<FunctionDefinitionAST>(); + + ast->setComment( mcomment ); + + ast->setWinDeclSpec( winDeclSpec ); + ast->setStorageSpecifier( storageSpec ); + ast->setFunctionSpecifier( funSpec ); + ast->setText( toString(start, endSignature) ); + ast->setTypeSpec( spec ); + ast->setFunctionBody( funBody ); + ast->setInitDeclarator( decl ); + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; + } + } + break; + + } + } + + syntaxError(); + return false; +} + +bool Parser::parseFunctionBody( StatementListAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseFunctionBody()" << endl; + + int start = lex->index(); + if( lex->lookAhead(0) != '{' ){ + return false; + } + nextToken(); + + StatementListAST::Node ast = CreateNode<StatementListAST>(); + + while( !lex->lookAhead(0).isNull() ){ + if( lex->lookAhead(0) == '}' ) + break; + + StatementAST::Node stmt; + int startStmt = lex->index(); + if( !parseStatement(stmt) ){ + if( startStmt == lex->index() ) + nextToken(); + skipUntilStatement(); + } else + ast->addStatement( stmt ); + } + + clearComment(); + + if( lex->lookAhead(0) != '}' ){ + reportError( i18n("} expected") ); + } else + nextToken(); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +QString Parser::toString( int start, int end, const QString& sep ) const +{ + QStringList l; + + for( int i=start; i<end; ++i ){ + const Token& t = lex->tokenAt(i); + if( t != Token_comment ) + l << t.text(); + } + + return l.join( sep ).stripWhiteSpace(); +} + +bool Parser::parseTypeSpecifierOrClassSpec( TypeSpecifierAST::Node& node ) +{ + if( parseClassSpecifier(node) ) + return true; + else if( parseEnumSpecifier(node) ) + return true; + else if( parseTypeSpecifier(node) ) + return true; + + return false; +} + +bool Parser::parseTryBlockStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTryBlockStatement()" << endl; + + int start = lex->index(); + if( lex->lookAhead(0) != Token_try ){ + return false; + } + nextToken(); + + StatementAST::Node stmt; + if( !parseCompoundStatement(stmt) ){ + syntaxError(); + } + + if( lex->lookAhead(0) != Token_catch ){ + reportError( i18n("catch expected") ); + } + + CatchStatementListAST::Node list = CreateNode<CatchStatementListAST>(); + + while( lex->lookAhead(0) == Token_catch ){ + + nextToken(); + ADVANCE( '(', "(" ); + ConditionAST::Node cond; + if( !parseCondition(cond) ){ + reportError( i18n("condition expected") ); + return false; + } + ADVANCE( ')', ")" ); + + StatementAST::Node body; + if( !parseCompoundStatement(body) ){ + syntaxError(); + } + + CatchStatementAST::Node cstmt = CreateNode<CatchStatementAST>(); + cstmt->setCondition( cond ); + cstmt->setStatement( body ); + int l=0, c=0; + if( cond.get() ) + cond->getStartPosition( &l, &c ); + else if( body.get() ) + body->getStartPosition( &l, &c ); + + cstmt->setStartPosition( l, c ); + if( body.get() ) + body->getEndPosition( &l, &c ); + + cstmt->setEndPosition( l, c ); + list->addStatement( cstmt ); + } + + TryBlockStatementAST::Node ast = CreateNode<TryBlockStatementAST>(); + ast->setStatement( stmt ); + ast->setCatchStatementList( list ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parsePrimaryExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parsePrimarExpression()" << endl; + + + switch( lex->lookAhead(0) ){ + case Token_string_literal: + { + AST::Node lit; + parseStringLiteral( lit ); + } + return true; + + case Token_number_literal: + case Token_char_literal: + case Token_true: + case Token_false: + nextToken(); + return true; + + case Token_this: + nextToken(); + return true; + + case Token_dynamic_cast: + case Token_static_cast: + case Token_reinterpret_cast: + case Token_const_cast: + { + nextToken(); + + CHECK( '<', "<" ); + AST::Node typeId; + parseTypeId( typeId ); + CHECK( '>', ">" ); + + CHECK( '(', "(" ); + AST::Node expr; + parseCommaExpression( expr ); + CHECK( ')', ")" ); + } + return true; + + case Token_typeid: + { + nextToken(); + CHECK( '(', "(" ); + AST::Node expr; + parseCommaExpression( expr ); + CHECK( ')', ")" ); + } + return true; + + case '(': + { + nextToken(); + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "token = " << lex->lookAhead(0).text() << endl; + AST::Node expr; + if( !parseExpression(expr) ){ + return false; + } + CHECK( ')', ")" ); + } + return true; + + default: + { + int start = lex->index(); + TypeSpecifierAST::Node typeSpec; + if( parseSimpleTypeSpecifier(typeSpec) && lex->lookAhead(0) == '(' ){ + nextToken(); + AST::Node expr; + parseCommaExpression( expr ); + CHECK( ')', ")" ); + return true; + } + + lex->setIndex( start ); + NameAST::Node name; + if( parseName(name) ) + return true; + } + } + + return false; +} + +bool Parser::parsePostfixExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parsePostfixExpression()" << endl; + + AST::Node expr; + if( !parsePrimaryExpression(expr) ) + return false; + + while( true ){ + switch(lex->lookAhead(0)) + { + case '[': + { + nextToken(); + AST::Node e; + parseCommaExpression( e ); + CHECK( ']', "]" ); + } + break; + + case '(': + { + nextToken(); + AST::Node funArgs; + parseCommaExpression( funArgs ); + CHECK( ')', ")" ); + } + break; + + case Token_incr: + case Token_decr: + nextToken(); + break; + + case '.': + case Token_arrow: + { + nextToken(); + if( lex->lookAhead(0) == Token_template ) + nextToken(); + + NameAST::Node name; + if( !parseName(name) ){ + return false; + } + } + break; + + case Token_typename: + { + nextToken(); + + NameAST::Node name; + if( !parseName(name) ){ + return false; + } + + CHECK( '(', "(" ); + AST::Node expr; + parseCommaExpression(expr); + CHECK( ')', ")" ); + } + return true; + + default: + return true; + + } // end switch + + } // end while + + return true; +} + +bool Parser::parseUnaryExpression( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseUnaryExpression()" << endl; + + switch( lex->lookAhead(0) ){ + case Token_incr: + case Token_decr: + case '*': + case '&': + case '+': + case '-': + case '!': + case '~': + { + nextToken(); + AST::Node expr; + return parseCastExpression( expr ); + } + + case Token_sizeof: + { + nextToken(); + int index = lex->index(); + if( lex->lookAhead(0) == '(' ){ + nextToken(); + AST::Node typeId; + if( parseTypeId(typeId) && lex->lookAhead(0) == ')' ){ + nextToken(); + return true; + } + lex->setIndex( index ); + } + AST::Node expr; + return parseUnaryExpression( expr ); + } + + case Token_new: + return parseNewExpression( node ); + + case Token_delete: + return parseDeleteExpression( node ); + } + + return parsePostfixExpression( node ); +} + +bool Parser::parseNewExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseNewExpression()" << endl; + if( lex->lookAhead(0) == Token_scope && lex->lookAhead(1) == Token_new ) + nextToken(); + + CHECK( Token_new, "new"); + + if( lex->lookAhead(0) == '(' ){ + nextToken(); + AST::Node expr; + parseCommaExpression( expr ); + CHECK( ')', ")" ); + } + + if( lex->lookAhead(0) == '(' ){ + nextToken(); + AST::Node typeId; + parseTypeId( typeId ); + CHECK( ')', ")" ); + } else { + AST::Node typeId; + parseNewTypeId( typeId ); + } + + AST::Node init; + parseNewInitializer( init ); + return true; +} + +bool Parser::parseNewTypeId( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseNewTypeId()" << endl; + TypeSpecifierAST::Node typeSpec; + if( parseTypeSpecifier(typeSpec) ){ + AST::Node declarator; + parseNewDeclarator( declarator ); + return true; + } + + return false; +} + +bool Parser::parseNewDeclarator( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseNewDeclarator()" << endl; + AST::Node ptrOp; + if( parsePtrOperator(ptrOp) ){ + AST::Node declarator; + parseNewDeclarator( declarator ); + return true; + } + + if( lex->lookAhead(0) == '[' ){ + while( lex->lookAhead(0) == '[' ){ + nextToken(); + AST::Node expr; + parseExpression( expr ); + ADVANCE( ']', "]" ); + } + return true; + } + + return false; +} + +bool Parser::parseNewInitializer( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseNewInitializer()" << endl; + if( lex->lookAhead(0) != '(' ) + return false; + + nextToken(); + AST::Node expr; + parseCommaExpression( expr ); + CHECK( ')', ")" ); + + return true; +} + +bool Parser::parseDeleteExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeleteExpression()" << endl; + if( lex->lookAhead(0) == Token_scope && lex->lookAhead(1) == Token_delete ) + nextToken(); + + CHECK( Token_delete, "delete" ); + + if( lex->lookAhead(0) == '[' ){ + nextToken(); + CHECK( ']', "]" ); + } + + AST::Node expr; + return parseCastExpression( expr ); +} + +bool Parser::parseCastExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCastExpression()" << endl; + + int index = lex->index(); + + if( lex->lookAhead(0) == '(' ){ + nextToken(); + AST::Node typeId; + if ( parseTypeId(typeId) ) { + if ( lex->lookAhead(0) == ')' ) { + nextToken(); + AST::Node expr; + if( parseCastExpression(expr) ) + return true; + } + } + } + + lex->setIndex( index ); + + AST::Node expr; + return parseUnaryExpression( expr ); +} + +bool Parser::parsePmExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser:parsePmExpression()" << endl; + AST::Node expr; + if( !parseCastExpression(expr) ) + return false; + + while( lex->lookAhead(0) == Token_ptrmem ){ + nextToken(); + + if( !parseCastExpression(expr) ) + return false; + } + + return true; +} + +bool Parser::parseMultiplicativeExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseMultiplicativeExpression()" << endl; + AST::Node expr; + if( !parsePmExpression(expr) ) + return false; + + while( lex->lookAhead(0) == '*' || lex->lookAhead(0) == '/' || lex->lookAhead(0) == '%' ){ + nextToken(); + + if( !parsePmExpression(expr) ) + return false; + } + + return true; +} + + +bool Parser::parseAdditiveExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseAdditiveExpression()" << endl; + AST::Node expr; + if( !parseMultiplicativeExpression(expr) ) + return false; + + while( lex->lookAhead(0) == '+' || lex->lookAhead(0) == '-' ){ + nextToken(); + + if( !parseMultiplicativeExpression(expr) ) + return false; + } + + return true; +} + +bool Parser::parseShiftExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseShiftExpression()" << endl; + AST::Node expr; + if( !parseAdditiveExpression(expr) ) + return false; + + while( lex->lookAhead(0) == Token_shift ){ + nextToken(); + + if( !parseAdditiveExpression(expr) ) + return false; + } + + return true; +} + +bool Parser::parseRelationalExpression( AST::Node& /*node*/, bool templArgs ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseRelationalExpression()" << endl; + AST::Node expr; + if( !parseShiftExpression(expr) ) + return false; + + while( lex->lookAhead(0) == '<' || (lex->lookAhead(0) == '>' && !templArgs) || + lex->lookAhead(0) == Token_leq || lex->lookAhead(0) == Token_geq ){ + nextToken(); + + if( !parseShiftExpression(expr) ) + return false; + } + + return true; +} + +bool Parser::parseEqualityExpression( AST::Node& /*node*/, bool templArgs ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseEqualityExpression()" << endl; + AST::Node expr; + if( !parseRelationalExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == Token_eq || lex->lookAhead(0) == Token_not_eq ){ + nextToken(); + + if( !parseRelationalExpression(expr, templArgs) ) + return false; + } + + return true; +} + +bool Parser::parseAndExpression( AST::Node& /*node*/, bool templArgs ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseAndExpression()" << endl; + AST::Node expr; + if( !parseEqualityExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == '&' ){ + nextToken(); + + if( !parseEqualityExpression(expr, templArgs) ) + return false; + } + + return true; +} + +bool Parser::parseExclusiveOrExpression( AST::Node& /*node*/, bool templArgs ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseExclusiveOrExpression()" << endl; + AST::Node expr; + if( !parseAndExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == '^' ){ + nextToken(); + + if( !parseAndExpression(expr, templArgs) ) + return false; + } + + return true; +} + +bool Parser::parseInclusiveOrExpression( AST::Node& /*node*/, bool templArgs ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInclusiveOrExpression()" << endl; + AST::Node expr; + if( !parseExclusiveOrExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == '|' ){ + nextToken(); + + if( !parseExclusiveOrExpression(expr, templArgs) ) + return false; + } + + return true; +} + +bool Parser::parseLogicalAndExpression( AST::Node& /*node*/, bool templArgs ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseLogicalAndExpression()" << endl; + + AST::Node expr; + if( !parseInclusiveOrExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == Token_and ){ + nextToken(); + + if( !parseInclusiveOrExpression(expr, templArgs) ) + return false; + } + + return true; +} + +bool Parser::parseLogicalOrExpression( AST::Node& node, bool templArgs ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseLogicalOrExpression()" << endl; + + int start = lex->index(); + + AST::Node expr; + if( !parseLogicalAndExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == Token_or ){ + nextToken(); + + if( !parseLogicalAndExpression(expr, templArgs) ) + return false; + } + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseConditionalExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseConditionalExpression()" << endl; + AST::Node expr; + if( !parseLogicalOrExpression(expr) ) + return false; + + if( lex->lookAhead(0) == '?' ){ + nextToken(); + + if( !parseExpression(expr) ) + return false; + + CHECK( ':', ":" ); + + if( !parseAssignmentExpression(expr) ) + return false; + } + + return true; +} + +bool Parser::parseAssignmentExpression( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseAssignmentExpression()" << endl; + int start = lex->index(); + AST::Node expr; + if( lex->lookAhead(0) == Token_throw && !parseThrowExpression(expr) ) + return false; + else if( !parseConditionalExpression(expr) ) + return false; + + while( lex->lookAhead(0) == Token_assign || lex->lookAhead(0) == '=' ){ + nextToken(); + + if( !parseConditionalExpression(expr) ) + return false; + } + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseConstantExpression( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseConstantExpression()" << endl; + int start = lex->index(); + if( parseConditionalExpression(node) ){ + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; + } + return false; +} + +bool Parser::parseExpression( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseExpression()" << endl; + + int start = lex->index(); + + if( !parseCommaExpression(node) ) + return false; + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseCommaExpression( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCommaExpression()" << endl; + int start = lex->index(); + + AST::Node expr; + if( !parseAssignmentExpression(expr) ) + return false; + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( !parseAssignmentExpression(expr) ) + return false; + } + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseThrowExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseThrowExpression()" << endl; + if( lex->lookAhead(0) != Token_throw ) + return false; + + CHECK( Token_throw, "throw" ); + AST::Node expr; + if( !parseAssignmentExpression(expr) ) + return false; + + return true; +} + +bool Parser::parseIvarDeclList( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseIvarDecls( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseIvarDecl( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseIvars( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseIvarDeclarator( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseMethodDecl( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseUnarySelector( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordSelector( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseSelector( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordDecl( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseReceiver( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcMessageExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseMessageArgs( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordArgList( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordArg( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseReservedWord( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseMyParms( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseMyParm( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseOptParmList( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcSelectorExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseSelectorArg( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordNameList( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordName( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcEncodeExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcString( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseProtocolRefs( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseIdentifierList( GroupAST::Node & node ) +{ + int start = lex->index(); + + if( lex->lookAhead(0) != Token_identifier ) + return false; + + GroupAST::Node ast = CreateNode<GroupAST>(); + + AST_FROM_TOKEN( tk, lex->index() ); + ast->addNode( tk ); + nextToken(); + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + if( lex->lookAhead(0) == Token_identifier ){ + AST_FROM_TOKEN( tk, lex->index() ); + ast->addNode( tk ); +// nextToken(); + } + ADVANCE( Token_identifier, "identifier" ); + } + + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; +} + +bool Parser::parseIdentifierColon( AST::Node & node ) +{ + Q_UNUSED( node ); + + if( lex->lookAhead(0) == Token_identifier && lex->lookAhead(1) == ':' ){ + nextToken(); + nextToken(); + return true; + } // ### else if PTYPENAME -> return true ; + + return false; +} + +bool Parser::parseObjcProtocolExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcOpenBracketExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcCloseBracket( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcDef( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcClassDef( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcClassDecl( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + + ADVANCE( OBJC_CLASS, "@class" ); + + GroupAST::Node idList; + if ( !parseIdentifierList( idList ) ) + return false; + + ADVANCE( ';', ";" ); + + return true; +} + +bool Parser::parseObjcProtocolDecl( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + + ADVANCE( OBJC_PROTOCOL, "@protocol" ); + + GroupAST::Node idList; + if ( !parseIdentifierList( idList ) ) + return false; + + ADVANCE( ';', ";" ); + + return true; +} + +bool Parser::parseObjcAliasDecl( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + + ADVANCE( OBJC_ALIAS, "@alias" ); + + GroupAST::Node idList; + if ( !parseIdentifierList( idList ) ) + return false; + + ADVANCE( ';', ";" ); + + return true; +} + +bool Parser::parseObjcProtocolDef( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcMethodDef( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseWinDeclSpec( GroupAST::Node & node ) +{ + if( lex->lookAhead(0) == Token_identifier && lex->lookAhead(0).text() == "__declspec" && lex->lookAhead(1) == '(' && lex->lookAhead(2) != ')'){ + int start = lex->index(); + nextToken(); + nextToken(); // skip '(' + + if ( !parseIdentifierList( node ) ) + return false; + + ADVANCE( ')', ")" ); + + UPDATE_POS( node, start, lex->index() ); + return true; + } + + return false; +} + diff --git a/lib/cppparser/parser.h b/lib/cppparser/parser.h new file mode 100644 index 00000000..ff3891ac --- /dev/null +++ b/lib/cppparser/parser.h @@ -0,0 +1,459 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef PARSER_H +#define PARSER_H + +#include "ast.h" + +#include <qstring.h> +#include <qstringlist.h> +#include <qvaluelist.h> +#include <qvaluestack.h> +#include <set> +#include <iostream> + +struct ParserPrivateData; + +class Driver; +class Lexer; +class Token; +struct Error; + + +class CommentFormatter { + static inline bool isWhite( QChar c ) { + return c.isSpace(); + } + + static void rStrip( QString str, QString& from ) { + if( str.isEmpty() ) return; + + int i = 0; + int ip = from.length(); + int s = from.length(); + + for( int a = s-1; a >= 0; a-- ) { + if( isWhite( from[a] ) ) { + continue; + } else { + if( from[a] == str[i] ) { + i++; + ip = a; + if( i == (int)str.length() ) break; + } else { + break; + } + } + } + + if( ip != (int)from.length() ) from = from.left( ip ); + } + + static void strip( QString str, QString& from ) { + if( str.isEmpty() ) return; + + int i = 0; + int ip = 0; + int s = from.length(); + + for( int a = 0; a < s; a++ ) { + if( isWhite( from[a] ) ) { + continue; + } else { + if( from[a] == str[i] ) { + i++; + ip = a+1; + if( i == (int)str.length() ) break; + } else { + break; + } + } + } + + if( ip ) from = from.mid( ip ); + } + + public: + + static QString formatComment( QString comment ) { + QString ret; + int i = 0; + int s = comment.length(); + while( i < s && comment[i] == '/' ) { + i++; + } + + if( i > 1 ) { + ret = comment.mid( i ); + } else { + ///remove the star in each line + QStringList lines = QStringList::split( "\n", comment ); + + if( lines.isEmpty() ) return ret; + + strip( "/**", lines.front() ); + rStrip( "/**", lines.back() ); + + QStringList::iterator it = lines.begin(); + ++it; + QStringList::iterator eit = lines.end(); + + if( it != lines.end() ) { + --eit; + + for( ; it != eit; ++it ) { + strip( "*", *it ); + } + + if( lines.front().stripWhiteSpace().isEmpty() ) + lines.pop_front(); + + if( lines.back().stripWhiteSpace().isEmpty() ) + lines.pop_back(); + } + + ret = lines.join( "\n" ); + } + + return ret; + } +}; + +class Comment { + QString m_text; + int m_line; + bool m_formatted; + + + void format() { + if( m_formatted ) return; + m_formatted = true; + m_text = CommentFormatter::formatComment( m_text ); + } + + public: + Comment( QString text = "", int line = -1 ) : m_text( text ), m_line( line ), m_formatted(false) { + } + + Comment( int line ) : m_line( line ) { + } + + void operator += ( Comment rhs ) { + format(); + rhs.format(); + m_text += " " + rhs.m_text; + } + + operator bool() const { + return !m_text.isEmpty(); + } + + operator QString() { + format(); + return m_text; + } + + inline int line() const { + return m_line; + } + + bool operator < ( Comment& rhs ) const { + return m_line < rhs.m_line; + } + + bool isSame ( const Comment& rhs ) { + if( rhs.m_formatted ) format(); + return m_text == rhs.m_text; + } + + struct cmp { + bool operator () ( const Comment& c1, const Comment& c2 ) const { + return c1.line() < c2.line(); + } + }; +}; + + +class CommentStore { + private: + typedef std::set< Comment, Comment::cmp > CommentSet; + CommentSet m_comments; + + public: + + ///Returns the comment nearest to "end"(inclusive), and returns & removes it + Comment getCommentInRange( int end, int start = 0 ) { + CommentSet::iterator it = m_comments.lower_bound( end ); + + + while( it != m_comments.begin() && (*it).line() > end ) { + --it; + } + + if( it != m_comments.end() && (*it).line() >= start && (*it).line() <= end ) { + Comment ret = *it; + m_comments.erase( it ); + return ret; + } else { + return Comment(); + } + } + + ///Returns and removes the comment in the line + Comment getComment( int line ) { + CommentSet::iterator it = m_comments.find( line ); + if( it != m_comments.end() ) { + Comment ret = *it; + m_comments.erase( it ); + return ret; + } else { + return Comment(); + } + } + + void addComment( Comment comment ) { + + CommentSet::iterator it = m_comments.find( comment ); + if( it != m_comments.end() ) { + if( comment.isSame( *it ) ) return; + Comment c = *it; + c += comment; + comment = c; + m_comments.erase( it ); + } + + m_comments.insert( comment ); + } + + ///Does not delete the comment + Comment latestComment() { + CommentSet::iterator it = m_comments.end(); + if( it == m_comments.begin() ) return Comment(); + --it; + return *it; + } + + void clear() { + m_comments.clear(); + } +}; + + +class Parser +{ +public: + Parser( Driver* driver, Lexer* lexer ); + virtual ~Parser(); + +private: + virtual bool reportError( const Error& err ); + /** @todo remove*/ virtual bool reportError( const QString& msg ); + /** @todo remove*/ virtual void syntaxError(); + +public /*rules*/ : + + bool parseTranslationUnit( TranslationUnitAST::Node& node ); + + bool parseDeclaration( DeclarationAST::Node& node ); + bool parseBlockDeclaration( DeclarationAST::Node& node ); + bool parseLinkageSpecification( DeclarationAST::Node& node ); + bool parseLinkageBody( LinkageBodyAST::Node& node ); + bool parseNamespace( DeclarationAST::Node& node ); + bool parseNamespaceAliasDefinition( DeclarationAST::Node& node ); + bool parseUsing( DeclarationAST::Node& node ); + bool parseUsingDirective( DeclarationAST::Node& node ); + bool parseTypedef( DeclarationAST::Node& node ); + bool parseAsmDefinition( DeclarationAST::Node& node ); + bool parseTemplateDeclaration( DeclarationAST::Node& node ); + bool parseDeclarationInternal( DeclarationAST::Node& node ); + + bool parseUnqualifiedName( ClassOrNamespaceNameAST::Node& node ); + bool parseStringLiteral( AST::Node& node ); + bool parseName( NameAST::Node& node ); + bool parseOperatorFunctionId( AST::Node& node ); + bool parseTemplateArgumentList( TemplateArgumentListAST::Node& node, bool reportError=true ); + bool parseOperator( AST::Node& node ); + bool parseCvQualify( GroupAST::Node& node ); + bool parseSimpleTypeSpecifier( TypeSpecifierAST::Node& node ); + bool parsePtrOperator( AST::Node& node ); + bool parseTemplateArgument( AST::Node& node ); + bool parseTypeSpecifier( TypeSpecifierAST::Node& node ); + bool parseTypeSpecifierOrClassSpec( TypeSpecifierAST::Node& node ); + bool parseDeclarator( DeclaratorAST::Node& node ); + bool parseTemplateParameterList( TemplateParameterListAST::Node& node ); + bool parseTemplateParameter( TemplateParameterAST::Node& node ); + bool parseStorageClassSpecifier( GroupAST::Node& node ); + bool parseFunctionSpecifier( GroupAST::Node& node ); + bool parseInitDeclaratorList( InitDeclaratorListAST::Node& node ); + bool parseInitDeclarator( InitDeclaratorAST::Node& node ); + bool parseParameterDeclarationClause( ParameterDeclarationClauseAST::Node& node ); + bool parseCtorInitializer( AST::Node& node ); + bool parsePtrToMember( AST::Node& node ); + bool parseEnumSpecifier( TypeSpecifierAST::Node& node ); + bool parseClassSpecifier( TypeSpecifierAST::Node& node ); + bool parseWinDeclSpec( GroupAST::Node& node ); + bool parseElaboratedTypeSpecifier( TypeSpecifierAST::Node& node ); + bool parseDeclaratorId( NameAST::Node& node ); + bool parseExceptionSpecification( GroupAST::Node& node ); + bool parseEnumerator( EnumeratorAST::Node& node ); + bool parseTypeParameter( TypeParameterAST::Node& node ); + bool parseParameterDeclaration( ParameterDeclarationAST::Node& node ); + bool parseTypeId( AST::Node& node ); + bool parseAbstractDeclarator( DeclaratorAST::Node& node ); + bool parseParameterDeclarationList( ParameterDeclarationListAST::Node& node ); + bool parseMemberSpecification( DeclarationAST::Node& node ); + bool parseAccessSpecifier( AST::Node& node ); + bool parseTypeIdList( GroupAST::Node& node ); + bool parseMemInitializerList( AST::Node& node ); + bool parseMemInitializer( AST::Node& node ); + bool parseInitializer( AST::Node& node ); + bool parseBaseClause( BaseClauseAST::Node& node ); + bool parseBaseSpecifier( BaseSpecifierAST::Node& node ); + bool parseInitializerClause( AST::Node& node ); + bool parseMemInitializerId( NameAST::Node& node ); + bool parseFunctionBody( StatementListAST::Node& node ); + + // expression + bool skipExpression( AST::Node& node ); + bool skipCommaExpression( AST::Node& node ); + bool skipExpressionStatement( StatementAST::Node& node ); + + bool parseExpression( AST::Node& node ); + bool parsePrimaryExpression( AST::Node& node ); + bool parsePostfixExpression( AST::Node& node ); + bool parseUnaryExpression( AST::Node& node ); + bool parseNewExpression( AST::Node& node ); + bool parseNewTypeId( AST::Node& node ); + bool parseNewDeclarator( AST::Node& node ); + bool parseNewInitializer( AST::Node& node ); + bool parseDeleteExpression( AST::Node& node ); + bool parseCastExpression( AST::Node& node ); + bool parsePmExpression( AST::Node& node ); + bool parseMultiplicativeExpression( AST::Node& node ); + bool parseAdditiveExpression( AST::Node& node ); + bool parseShiftExpression( AST::Node& node ); + bool parseRelationalExpression( AST::Node& node, bool templArgs=false ); + bool parseEqualityExpression( AST::Node& node, bool templArgs=false ); + bool parseAndExpression( AST::Node& node, bool templArgs=false ); + bool parseExclusiveOrExpression( AST::Node& node, bool templArgs=false ); + bool parseInclusiveOrExpression( AST::Node& node, bool templArgs=false ); + bool parseLogicalAndExpression( AST::Node& node, bool templArgs=false ); + bool parseLogicalOrExpression( AST::Node& node, bool templArgs=false ); + bool parseConditionalExpression( AST::Node& node ); + bool parseAssignmentExpression( AST::Node& node ); + bool parseConstantExpression( AST::Node& node ); + bool parseCommaExpression( AST::Node& node ); + bool parseThrowExpression( AST::Node& node ); + + // statement + bool parseCondition( ConditionAST::Node& node ); + bool parseStatement( StatementAST::Node& node ); + bool parseWhileStatement( StatementAST::Node& node ); + bool parseDoStatement( StatementAST::Node& node ); + bool parseForStatement( StatementAST::Node& node ); + bool parseForEachStatement( StatementAST::Node& node ); // qt4 [erbsland] + bool parseCompoundStatement( StatementAST::Node& node ); + bool parseForInitStatement( StatementAST::Node& node ); + bool parseIfStatement( StatementAST::Node& node ); + bool parseSwitchStatement( StatementAST::Node& node ); + bool parseLabeledStatement( StatementAST::Node& node ); + bool parseDeclarationStatement( StatementAST::Node& node ); + bool parseTryBlockStatement( StatementAST::Node& node ); + + // objective c + bool parseObjcDef( DeclarationAST::Node& node ); + bool parseObjcClassDef( DeclarationAST::Node& node ); + bool parseObjcClassDecl( DeclarationAST::Node& node ); + bool parseObjcProtocolDecl( DeclarationAST::Node& node ); + bool parseObjcAliasDecl( DeclarationAST::Node& node ); + bool parseObjcProtocolDef( DeclarationAST::Node& node ); + bool parseObjcMethodDef( DeclarationAST::Node& node ); + + bool parseIvarDeclList( AST::Node& node ); + bool parseIvarDecls( AST::Node& node ); + bool parseIvarDecl( AST::Node& node ); + bool parseIvars( AST::Node& node ); + bool parseIvarDeclarator( AST::Node& node ); + bool parseMethodDecl( AST::Node& node ); + bool parseUnarySelector( AST::Node& node ); + bool parseKeywordSelector( AST::Node& node ); + bool parseSelector( AST::Node& node ); + bool parseKeywordDecl( AST::Node& node ); + bool parseReceiver( AST::Node& node ); + bool parseObjcMessageExpr( AST::Node& node ); + bool parseMessageArgs( AST::Node& node ); + bool parseKeywordExpr( AST::Node& node ); + bool parseKeywordArgList( AST::Node& node ); + bool parseKeywordArg( AST::Node& node ); + bool parseReservedWord( AST::Node& node ); + bool parseMyParms( AST::Node& node ); + bool parseMyParm( AST::Node& node ); + bool parseOptParmList( AST::Node& node ); + bool parseObjcSelectorExpr( AST::Node& node ); + bool parseSelectorArg( AST::Node& node ); + bool parseKeywordNameList( AST::Node& node ); + bool parseKeywordName( AST::Node& node ); + bool parseObjcEncodeExpr( AST::Node& node ); + bool parseObjcString( AST::Node& node ); + bool parseProtocolRefs( AST::Node& node ); + bool parseIdentifierList( GroupAST::Node& node ); + bool parseIdentifierColon( AST::Node& node ); + bool parseObjcProtocolExpr( AST::Node& node ); + bool parseObjcOpenBracketExpr( AST::Node& node ); + bool parseObjcCloseBracket( AST::Node& node ); + + void nextToken( bool skipComments = true ); + + ///parses all comments until the end of the line + Comment comment(); + void preparseLineComments( int line ); + void processComment( int offset = 0 ); + void clearComment( ); + + bool skipUntil( int token ); + bool skipUntilDeclaration(); + bool skipUntilStatement(); + bool skip( int l, int r ); + QString toString( int start, int end, const QString& sep=" " ) const; + +private: + int currentLine(); + CommentStore m_commentStore; + + template<class Type> + void eventuallyTakeComment( int startLn, int line, Type& ast ); + template<class Type> + void eventuallyTakeComment( Type& ast ); + + ParserPrivateData* d; + Driver* m_driver; + Lexer* lex; + Comment m_currentComment; + int m_problems; + int m_maxProblems; + bool objcp; + +private: + Parser( const Parser& source ); + void operator = ( const Parser& source ); +}; + + +#endif diff --git a/lib/cppparser/tree_parser.cpp b/lib/cppparser/tree_parser.cpp new file mode 100644 index 00000000..780d86a2 --- /dev/null +++ b/lib/cppparser/tree_parser.cpp @@ -0,0 +1,212 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "tree_parser.h" +#include "driver.h" +#include <kdebug.h> + +TreeParser::TreeParser() +{ +} + +TreeParser::~TreeParser() +{ +} + +void TreeParser::parseTranslationUnit( const ParsedFile& translationUnit ) +{ + ////kdDebug(9007) << "TreeParser::parseTranslationUnit()" << endl; + + QPtrList<DeclarationAST> declarations = translationUnit->declarationList(); + QPtrListIterator<DeclarationAST> it( declarations ); + while( it.current() ){ + if( (it.current() == 0 ) ) { + //kdDebug( 9007 ) << "declaration is zero" << endl; + continue; + } + parseDeclaration( it.current() ); + ++it; + } +} + +void TreeParser::parseDeclaration( DeclarationAST* declaration ) +{ + ////kdDebug(9007) << "TreeParser::parseDeclaration()" << endl; + + if( !declaration ) + return; + + switch( declaration->nodeType() ) + { + case NodeType_LinkageSpecification: + parseLinkageSpecification( static_cast<LinkageSpecificationAST*>(declaration) ); + break; + + case NodeType_Namespace: + parseNamespace( static_cast<NamespaceAST*>(declaration) ); + break; + + case NodeType_NamespaceAlias: + parseNamespaceAlias( static_cast<NamespaceAliasAST*>(declaration) ); + break; + + case NodeType_Using: + parseUsing( static_cast<UsingAST*>(declaration) ); + break; + + case NodeType_UsingDirective: + parseUsingDirective( static_cast<UsingDirectiveAST*>(declaration) ); + break; + + case NodeType_Typedef: + parseTypedef( static_cast<TypedefAST*>(declaration) ); + break; + + case NodeType_TemplateDeclaration: + parseTemplateDeclaration( static_cast<TemplateDeclarationAST*>(declaration) ); + break; + + case NodeType_SimpleDeclaration: + parseSimpleDeclaration( static_cast<SimpleDeclarationAST*>(declaration) ); + break; + + case NodeType_FunctionDefinition: + parseFunctionDefinition( static_cast<FunctionDefinitionAST*>(declaration) ); + break; + + case NodeType_AccessDeclaration: + parseAccessDeclaration( static_cast<AccessDeclarationAST*>(declaration) ); + break; + } +} + +void TreeParser::parseLinkageSpecification( LinkageSpecificationAST* ast ) +{ + ////kdDebug(9007) << "TreeParser::parseLinkageSpecification()" << endl; + if( ast->linkageBody() ) + parseLinkageBody( ast->linkageBody() ); + else if( ast->declaration() ) + parseDeclaration( ast->declaration() ); +} + +void TreeParser::parseNamespace( NamespaceAST* decl ) +{ + ////kdDebug(9007) << "TreeParser::parseNamespace()" << endl; + if( decl->linkageBody() ) + parseLinkageBody( decl->linkageBody() ); +} + +void TreeParser::parseNamespaceAlias( NamespaceAliasAST* decl ) +{ + ////kdDebug(9007) << "TreeParser::parseNamespaceAlias()" << endl; + Q_UNUSED( decl ); +} + +void TreeParser::parseUsing( UsingAST* decl ) +{ + ////kdDebug(9007) << "TreeParser::parseUsing()" << endl; + Q_UNUSED( decl ); +} + +void TreeParser::parseUsingDirective( UsingDirectiveAST* decl ) +{ + ////kdDebug(9007) << "TreeParser::parseUsingDirective()" << endl; + Q_UNUSED( decl ); +} + +void TreeParser::parseTypedef( TypedefAST* decl ) +{ + ////kdDebug(9007) << "TreeParser::parseTypedef()" << endl; + if( decl->typeSpec() ) + parseTypeSpecifier( decl->typeSpec() ); +} + +void TreeParser::parseTemplateDeclaration( TemplateDeclarationAST* decl ) +{ + ////kdDebug(9007) << "TreeParser::parseTemplateDeclaration()" << endl; + Q_UNUSED( decl ); +} + +void TreeParser::parseSimpleDeclaration( SimpleDeclarationAST* decl ) +{ + ////kdDebug(9007) << "TreeParser::parseSimpleDeclaration()" << endl; + Q_UNUSED( decl ); +} + +void TreeParser::parseFunctionDefinition( FunctionDefinitionAST* def ) +{ + ////kdDebug(9007) << "TreeParser::parseFunctionDefinition()" << endl; + Q_UNUSED( def ); +} + +void TreeParser::parseLinkageBody( LinkageBodyAST* linkageBody ) +{ + ////kdDebug(9007) << "TreeParser::parseLinkageBody()" << endl; + QPtrList<DeclarationAST> declarations = linkageBody->declarationList(); + for( QPtrListIterator<DeclarationAST> it(declarations); it.current(); ++it ){ + parseDeclaration( it.current() ); + } +} + +void TreeParser::parseTypeSpecifier( TypeSpecifierAST* typeSpec ) +{ + ////kdDebug(9007) << "TreeParser::parseTypeSpecifier()" << endl; + switch( typeSpec->nodeType() ) + { + case NodeType_ClassSpecifier: + parseClassSpecifier( static_cast<ClassSpecifierAST*>(typeSpec) ); + break; + + case NodeType_EnumSpecifier: + parseEnumSpecifier( static_cast<EnumSpecifierAST*>(typeSpec) ); + break; + + case NodeType_ElaboratedTypeSpecifier: + parseElaboratedTypeSpecifier( static_cast<ElaboratedTypeSpecifierAST*>(typeSpec) ); + break; + } +} + +void TreeParser::parseClassSpecifier( ClassSpecifierAST* classSpec ) +{ + ////kdDebug(9007) << "TreeParser::parseClassSpecifier()" << endl; + QPtrList<DeclarationAST> declarations = classSpec->declarationList(); + for( QPtrListIterator<DeclarationAST> it(declarations); it.current(); ++it ){ + parseDeclaration( it.current() ); + } +} + +void TreeParser::parseEnumSpecifier( EnumSpecifierAST* enumSpec ) +{ + ////kdDebug(9007) << "TreeParser::parseEnumSpecifier()" << endl; + Q_UNUSED( enumSpec ); +} + +void TreeParser::parseElaboratedTypeSpecifier( ElaboratedTypeSpecifierAST* typeSpec ) +{ + ////kdDebug(9007) << "TreeParser::parseElaboratedTypeSpecifier()" << endl; + Q_UNUSED( typeSpec ); +} + +void TreeParser::parseAccessDeclaration ( AccessDeclarationAST * access ) +{ + ////kdDebug(9007) << "TreeParser::parseAccessDeclaration()" << endl; + Q_UNUSED( access ); +} + diff --git a/lib/cppparser/tree_parser.h b/lib/cppparser/tree_parser.h new file mode 100644 index 00000000..a7477a16 --- /dev/null +++ b/lib/cppparser/tree_parser.h @@ -0,0 +1,60 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef __tree_parser_h +#define __tree_parser_h + +#include "ast.h" +class ParsedFile; + +class TreeParser +{ +public: + TreeParser(); + virtual ~TreeParser(); + + // translation-unit + virtual void parseTranslationUnit( const ParsedFile& ); + + // declarations + virtual void parseDeclaration( DeclarationAST* ); + virtual void parseLinkageSpecification( LinkageSpecificationAST* ); + virtual void parseNamespace( NamespaceAST* ); + virtual void parseNamespaceAlias( NamespaceAliasAST* ); + virtual void parseUsing( UsingAST* ); + virtual void parseUsingDirective( UsingDirectiveAST* ); + virtual void parseTypedef( TypedefAST* ); + virtual void parseTemplateDeclaration( TemplateDeclarationAST* ); + virtual void parseSimpleDeclaration( SimpleDeclarationAST* ); + virtual void parseFunctionDefinition( FunctionDefinitionAST* ); + virtual void parseLinkageBody( LinkageBodyAST* ); + virtual void parseAccessDeclaration( AccessDeclarationAST* ); + + // type-specifier + virtual void parseTypeSpecifier( TypeSpecifierAST* ); + virtual void parseClassSpecifier( ClassSpecifierAST* ); + virtual void parseEnumSpecifier( EnumSpecifierAST* ); + virtual void parseElaboratedTypeSpecifier( ElaboratedTypeSpecifierAST* ); + +private: + TreeParser( const TreeParser& source ); + void operator = ( const TreeParser& source ); +}; + +#endif // __tree_parser_h diff --git a/lib/dummy.cpp b/lib/dummy.cpp new file mode 100644 index 00000000..8d1c8b69 --- /dev/null +++ b/lib/dummy.cpp @@ -0,0 +1 @@ + diff --git a/lib/interfaces/KDevCoreIface.cpp b/lib/interfaces/KDevCoreIface.cpp new file mode 100644 index 00000000..e2e53346 --- /dev/null +++ b/lib/interfaces/KDevCoreIface.cpp @@ -0,0 +1,59 @@ + + +/* This file is part of the KDE project + Copyright (C) 2001 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2001-2002 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2002 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include <kdebug.h> +#include <dcopclient.h> +#include "KDevCoreIface.h" +#include "kdevcore.h" + + +KDevCoreIface::KDevCoreIface(KDevCore *core) + : QObject(core), DCOPObject("KDevCore"), m_core(core) +{ + connect( m_core, SIGNAL(projectOpened()), this, SLOT(forwardProjectOpened()) ); + connect( m_core, SIGNAL(projectClosed()), this, SLOT(forwardProjectClosed()) ); +} + + +KDevCoreIface::~KDevCoreIface() +{} + + +void KDevCoreIface::forwardProjectOpened() +{ + kdDebug(9000) << "dcop emitting project opened" << endl; + emitDCOPSignal("projectOpened()", QByteArray()); +} + + +void KDevCoreIface::forwardProjectClosed() +{ + kdDebug(9000) << "dcop emitting project closed" << endl; + emitDCOPSignal("projectClosed()", QByteArray()); +} + +void KDevCoreIface::openProject( const QString & projectFileName ) +{ + m_core->openProject(projectFileName); +} + +#include "KDevCoreIface.moc" diff --git a/lib/interfaces/KDevCoreIface.h b/lib/interfaces/KDevCoreIface.h new file mode 100644 index 00000000..5bf5b0ab --- /dev/null +++ b/lib/interfaces/KDevCoreIface.h @@ -0,0 +1,51 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2001-2002 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2002 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef _KDEVCOREIFACE_H_ +#define _KDEVCOREIFACE_H_ + +#include <dcopobject.h> +#include <dcopref.h> + +class KDevCore; + + +class KDevCoreIface : public QObject, public DCOPObject +{ + Q_OBJECT + K_DCOP + +public: + + KDevCoreIface( KDevCore *core ); + ~KDevCoreIface(); + +k_dcop: + void openProject(const QString& projectFileName); + +private slots: + void forwardProjectOpened(); + void forwardProjectClosed(); + +private: + KDevCore *m_core; +}; + +#endif diff --git a/lib/interfaces/KDevPartControllerIface.cpp b/lib/interfaces/KDevPartControllerIface.cpp new file mode 100644 index 00000000..be6564d4 --- /dev/null +++ b/lib/interfaces/KDevPartControllerIface.cpp @@ -0,0 +1,95 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include <kdebug.h> +#include <dcopclient.h> +#include <kurl.h> + + +#include "KDevPartControllerIface.h" +#include "kdevpartcontroller.h" + + +KDevPartControllerIface::KDevPartControllerIface(KDevPartController *pc) + : QObject(pc), DCOPObject("KDevPartController"), m_controller(pc) +{ + connect(pc, SIGNAL(loadedFile(const KURL &)), this, SLOT(forwardLoadedFile(const KURL &))); + connect(pc, SIGNAL(savedFile(const KURL &)), this, SLOT(forwardSavedFile(const KURL &))); + connect(pc, SIGNAL(closedFile(const KURL &)), this, SLOT(forwardClosedFile(const KURL &))); +} + + +KDevPartControllerIface::~KDevPartControllerIface() +{ +} + + +void KDevPartControllerIface::editDocument(const QString &url, int lineNum) +{ + m_controller->editDocument(KURL(url), lineNum); +} + + +void KDevPartControllerIface::showDocument(const QString &url, bool newWin) +{ + m_controller->showDocument(KURL(url), newWin); +} + + +void KDevPartControllerIface::saveAllFiles() +{ + m_controller->saveAllFiles(); +} + + +void KDevPartControllerIface::revertAllFiles() +{ + m_controller->revertAllFiles(); +} + + +void KDevPartControllerIface::forwardLoadedFile(const KURL &fileName) +{ + kdDebug(9000) << "dcop emitting loadedFile " << fileName << endl; + emitDCOPSignal("projectOpened()", QByteArray()); +} + + +void KDevPartControllerIface::forwardSavedFile(const KURL &fileName) +{ + kdDebug(9000) << "dcop emitting savedFile " << fileName << endl; + emitDCOPSignal("projectClosed()", QByteArray()); +} + +void KDevPartControllerIface::forwardClosedFile(const KURL &fileName) +{ + kdDebug(9000) << "dcop emitting closedFile " << fileName << endl; + emitDCOPSignal("projectClosed()", QByteArray()); +} + +bool KDevPartControllerIface::closeAllFiles( ) +{ + return m_controller->closeAllFiles(); +} + +uint KDevPartControllerIface::documentState( const KURL & url ) +{ + return (uint) m_controller->documentState(url); +} + +#include "KDevPartControllerIface.moc" diff --git a/lib/interfaces/KDevPartControllerIface.h b/lib/interfaces/KDevPartControllerIface.h new file mode 100644 index 00000000..90de786a --- /dev/null +++ b/lib/interfaces/KDevPartControllerIface.h @@ -0,0 +1,63 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef _KDEVPARTCONTROLLERIFACE_H_ +#define _KDEVPARTCONTROLLERIFACE_H_ + +#include <dcopobject.h> +#include <dcopref.h> +#include <kurl.h> + +class KDevPartController; + +class KDevPartControllerIface : public QObject, public DCOPObject +{ + Q_OBJECT + K_DCOP + +public: + + KDevPartControllerIface(KDevPartController *pc); + ~KDevPartControllerIface(); + + +k_dcop: + + void editDocument(const QString &url, int lineNum); + void showDocument(const QString &url, bool newWin); + + void saveAllFiles(); + void revertAllFiles(); + + bool closeAllFiles(); + uint documentState( const KURL & url ); + +private slots: + + void forwardLoadedFile(const KURL &fileName); + void forwardSavedFile(const KURL &fileName); + void forwardClosedFile(const KURL &fileName); + +private: + + KDevPartController *m_controller; + +}; + + +#endif diff --git a/lib/interfaces/Mainpage.dox b/lib/interfaces/Mainpage.dox new file mode 100644 index 00000000..2b4d818a --- /dev/null +++ b/lib/interfaces/Mainpage.dox @@ -0,0 +1,10 @@ +/** +@mainpage The KDevelop Interfaces Library + +This library contains all interfaces and classes that form KDevelop plugin architecture. + +<b>Link with</b>: -lkdevelop + +<b>Include path</b>: -I\$(kde_includes)/kdevelop/interfaces +*/ + diff --git a/lib/interfaces/Makefile.am b/lib/interfaces/Makefile.am new file mode 100644 index 00000000..6b171b10 --- /dev/null +++ b/lib/interfaces/Makefile.am @@ -0,0 +1,40 @@ +INCLUDES = -I$(top_srcdir)/lib/interfaces/external -I$(top_srcdir)/lib/util -I$(top_srcdir)/lib/cppparser \ + $(all_includes) + +noinst_LTLIBRARIES = libkdevinterfaces.la + +libkdevinterfaces_la_SOURCES = kdevcore.cpp kdevproject.cpp \ + kdevlanguagesupport.cpp kdevpartcontroller.cpp kdevapi.cpp KDevCoreIface.skel \ + kdevmainwindow.cpp KDevPartControllerIface.skel kdevplugin.cpp kdevcoderepository.cpp \ + codemodel.cpp codemodel_treeparser.cpp codemodel_utils.cpp \ + kdevdesignerintegration.cpp kdevplugincontroller.cpp kdevplugininfo.cpp KDevCoreIface.cpp \ + KDevPartControllerIface.cpp katedocumentmanagerinterface.cpp katedocumentmanagerinterface.skel \ + kdevprojectiface.cpp kdevprojectiface.skel kdevdesignerintegrationiface.cpp \ + kdevdesignerintegrationiface.skel hashedstring.cpp +libkdevinterfaces_la_LDFLAGS = -no-undefined $(all_libraries) +libkdevinterfaces_la_LIBADD = $(top_builddir)/lib/interfaces/external/libkinterfacedesigner.la \ + $(top_builddir)/lib/util/libkdevutil.la \ + $(LIB_KDEUI) $(LIB_KHTML) $(LIB_KPARTS) -lktexteditor -lkscript -lDCOP + +kdevelopincludedir = $(includedir)/kdevelop/interfaces +kdevelopinclude_HEADERS = kdevlanguagesupport.h kdevmainwindow.h \ + kdevpartcontroller.h kdevplugin.h kdevproject.h kdevcore.h kdevcoderepository.h codemodel.h \ + codemodel_utils.h codemodel_treeparser.h kdevgenericfactory.h kdevapi.h \ + kdevdesignerintegration.h kdevplugincontroller.h kdevplugininfo.h KDevCoreIface.h \ + KDevPartControllerIface.h katedocumentmanagerinterface.h \ + hashedstring.h + + +servicetypedir = $(kde_servicetypesdir) +servicetype_DATA = kdevelopproject.desktop kdeveloplanguagesupport.desktop \ + kdevelopplugin.desktop + +METASOURCES = AUTO + +SUBDIRS = extensions external extras + +DOXYGEN_REFERENCES = dcop interfaces kdecore kdefx kdeui khtml kmdi kio kjs kparts kutils kdevutil designer_integration kdevcatalog kdevprofileslib kdevextensions +DOXYGEN_PROJECTNAME = KDevelop Interfaces Library +DOXYGEN_DOCDIRPREFIX = kdev +include ../../Doxyfile.am +noinst_HEADERS = kdevprojectiface.h kdevdesignerintegrationiface.h diff --git a/lib/interfaces/codemodel.cpp b/lib/interfaces/codemodel.cpp new file mode 100644 index 00000000..567f785a --- /dev/null +++ b/lib/interfaces/codemodel.cpp @@ -0,0 +1,1823 @@ +/* This file is part of KDevelop + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "codemodel.h" +#include "driver.h" +#include <kdebug.h> +#include <kdatastream.h> + +///Little helper-functions to save a lot of typing and possible errors +template<class MapContainer> +bool eachCanUpdate( const MapContainer& old, const MapContainer& newMap ) { + if( old.size() != newMap.size() ) return false; + + typename MapContainer::const_iterator oldIt = old.begin(); + typename MapContainer::const_iterator newIt = newMap.begin(); + while( oldIt != old.end() ) { + typedef typename MapContainer::mapped_type ListType; + if( (*oldIt).size() != (*newIt).size() ) return false; + typename ListType::const_iterator it1 = (*oldIt).begin(); + typename ListType::const_iterator it2 = (*newIt).begin(); + + while( it1 != (*oldIt).end() ) { + if( !(*it1)->canUpdate( *it2 ) ) return false; + ++it1; + ++it2; + } + ++oldIt; + ++newIt; + } + return true; +} + +template<class MapContainer> +void eachUpdate( MapContainer& old, const MapContainer& newMap ) { + if( old.size() != newMap.size() ) kdError( 9007 ) << "error in eachUpdate(...) 1" << endl; + typename MapContainer::iterator oldIt = old.begin(); + typename MapContainer::const_iterator newIt = newMap.begin(); + while( oldIt != old.end() ) { + if( (*oldIt).size() != (*newIt).size() ) kdError( 9007 ) << "error in eachUpdate(...) 2" << endl; + typedef typename MapContainer::mapped_type ListType; + typename ListType::iterator it1 = (*oldIt).begin(); + typename ListType::const_iterator it2 = (*newIt).begin(); + while( it1 != (*oldIt).end() ) { + (*it1)->update( *it2 ); + ++it1; + ++it2; + } + ++oldIt; + ++newIt; + } +} + +///Versions for contains that do not contain maps again +template<class MapContainer> +bool eachCanUpdateSingle( const MapContainer& old, const MapContainer& newMap ) { + if( old.size() != newMap.size() ) return false; + + typename MapContainer::const_iterator oldIt = old.begin(); + typename MapContainer::const_iterator newIt = newMap.begin(); + while( oldIt != old.end() ) { + if( !(*oldIt)->canUpdate( *newIt ) ) return false; + ++oldIt; + ++newIt; + } + return true; +} + +template<class MapContainer> +void eachUpdateSingle( MapContainer& old, const MapContainer& newMap ) { + if( old.size() != newMap.size() ) kdError( 9007 ) << "error in eachUpdate(...) 1" << endl; + typename MapContainer::iterator oldIt = old.begin(); + typename MapContainer::const_iterator newIt = newMap.begin(); + while( oldIt != old.end() ) { + (*oldIt)->update( *newIt ); + ++oldIt; + ++newIt; + } +} + +CodeModel::CodeModel() +{ + wipeout(); + m_currentGroupId = 1; ///0 stands for invalid group +} + +CodeModel::~ CodeModel( ) +{ +} + +int CodeModel::newGroupId() { + return (m_currentGroupId++) * 2; +} + +inline bool isSingleGroup( const int group ) { + return (group % 2) == 0; +} + +QStringList CodeModel::getGroupStrings(int gid) const { + QStringList ret; + for(QMap<QString, FileDom>::ConstIterator it = m_files.begin(); it != m_files.end(); ++it) { + if((*it)->groupId() == gid) ret.append( (*it)-> name() ); + } + return ret; +} + + +FileList CodeModel::getGroup(int gid) const { + FileList ret; + for(QMap<QString, FileDom>::ConstIterator it = m_files.begin(); it != m_files.end(); ++it) { + if((*it)->groupId() == gid) ret.append(*it); + } + return ret; +} + +FileList CodeModel::getGroup( const FileDom& dom) const { + return getGroup( dom->groupId() ); +} + +int CodeModel::mergeGroups( int g1, int g2) { + if( !g1 || !g2 ) return 0; + if( g1 == g2 ) return g1; + int ng = isSingleGroup( g1 ) ? g2 : g1; + if( isSingleGroup( ng ) ) + ng = newGroupId() + 1; + + for( QMap<QString, FileDom>::iterator it = m_files.begin(); it != m_files.end(); ++it ) { + if( (*it)->groupId() == g2 || (*it)->groupId() == g1 ) (*it)->setGroupId( ng ); + } + return ng; +} + +template<class Type> static void dumpMap( std::ostream& file, QMap<QString, Type>& map ) { + typename QMap<QString, Type>::Iterator it = map.begin(); + for( ; it != map.end(); ++it) { + typename Type::Iterator it2 = (*it).begin(); + for( ; it2 != (*it).end(); ++it2) { + (*it2) -> dump( file, true ); + } + } +} + +template<class Type> static void dumpMapDirect( std::ostream& file, QMap<QString, Type>& map ) { + typename QMap<QString, Type>::Iterator it = map.begin(); + for( ; it != map.end(); ++it) { + (*it) -> dump( file, true ); + } +} + +void CodeModelItem::dump( std::ostream& file, bool recurse, QString Info ) +{ + ostringstream str( ostringstream::out ); + + str << "name: " << name().ascii() << "\n"; + str << "kind: " << m_kind << " "; + + if( isFile() ) str << "isFile "; + if( isNamespace() ) str << "isNamespace "; + if( isClass() ) str << "isClass "; + if( isFunction() ) str << "isFunction "; + if( isFunctionDefinition() ) str << "isFunctionDefinition "; + if( isVariable() ) str << "isVariable "; + if( isArgument() ) str << "isArgument "; + if( isEnum() ) str << "isEnum "; + if( isEnumerator() ) str << "isEnumerator "; + if( isTypeAlias() ) str << "isTypeAlias "; + if( isCustom() ) str << "isCustom "; + str << "\n"; + str << "File: " << fileName().ascii() << " "; + int line, col; + getStartPosition( &line, &col ); + str << "s:(" << line << ", " << col << ") "; + getEndPosition( &line, &col ); + str << "e:(" << line << ", " << col << ")\n"; + + + Info.prepend( str.str().c_str() ); + + file << Info.ascii() << "\n"; + if(recurse) {} ///just to get rid of the warning +} + +void ClassModel::dump( std::ostream& file, bool recurse, QString Info ) +{ + ostringstream str( ostringstream::out ); + + + str << "scope: " << m_scope.join("::").ascii() << "\n"; + str << "bases: " << m_baseClassList.join(" ").ascii() << "\n"; + + Info.prepend( str.str().c_str() ); + + CodeModelItem::dump( file, false, Info ); + + if( recurse ) { + dumpMap( file, m_classes ); + } +} + +void NamespaceAliasModel::read( QDataStream& stream ) { + QString tempFileName; + stream >> m_name >> m_aliasName >> tempFileName; + m_fileName = HashedString( tempFileName ); +} + +void NamespaceAliasModel::write( QDataStream& stream ) const { + stream << m_name << m_aliasName << m_fileName.str(); +} + +void NamespaceImportModel::read( QDataStream& stream ) { + QString tempFileName; + stream >> m_name >> tempFileName; + m_fileName = HashedString( tempFileName ); +} + +void NamespaceImportModel::write( QDataStream& stream ) const { + stream << m_name << m_fileName.str(); +} + +void NamespaceModel::dump( std::ostream& file, bool recurse, QString Info ) +{ + ostringstream str( ostringstream::out ); + + Info.prepend( str.str().c_str() ); + + ClassModel::dump( file, false, Info ); + + if( recurse ) { + dumpMapDirect( file, m_namespaces ); + } +} + +void ArgumentModel::dump( std::ostream& file, bool recurse, QString Info ) +{ + ostringstream str( ostringstream::out ); + + str << "type: " << m_type.ascii() << " default: " << m_defaultValue.ascii() << "\n"; + + Info.prepend( str.str().c_str() ); + + CodeModelItem::dump( file, false, Info ); + + if(recurse) {} ///just to get rid of the warning +} + +void FunctionModel::dump( std::ostream& file, bool recurse, QString Info ) +{ + ostringstream str( ostringstream::out ); + + str << "access: " << m_access; + + str << " scope: " << m_scope.join("::").ascii() << "\n"; + + if(isAbstract()) str << "isAbstract "; + if(isConstant()) str << "isConstant "; + if(isFunction()) str << "isFunction "; + if(isInline()) str << "isInline "; + if(isSignal()) str << "isSignal "; + if(isSlot()) str << "isSlot "; + if(isStatic()) str << "isStatic "; + if(isVirtual()) str << "isVirtual "; + + str << "\n"; + str << "result-type: " << resultType().ascii() << "\n"; + + Info.prepend( str.str().c_str() ); + + CodeModelItem::dump( file, false, Info ); + + if(recurse) { + for( ArgumentList::iterator it = m_arguments.begin(); it != m_arguments.end(); ++it) { + (*it) -> dump( file, true ); + } + } +} + +void VariableModel::dump( std::ostream& file, bool recurse, QString Info ) +{ + ostringstream str( ostringstream::out ); + + str << "access: " << m_access << "type: " << m_type.ascii() << "\n"; + + if(isStatic()) str << "isStatic "; + + str << "\n"; + + Info.prepend( str.str().c_str() ); + + CodeModelItem::dump( file, false, Info ); + + if(recurse) {} ///just to get rid of the warning +} + +void CodeModel::dump( std::ostream& file, QString Info ) { + ostringstream str(ostringstream::out); + + Info.prepend( str.str().c_str() ); + + file << Info.ascii() << "\n"; + + QMap<QString, FileDom>::iterator it = m_files.begin(); + for(; it != m_files.end(); ++it) { + (*it) -> dump( file, true ); + } +} + +void EnumModel::dump( std::ostream& file, bool recurse, QString Info ) +{ + ostringstream str( ostringstream::out ); + + str << "access: " << m_access << "\n"; + + Info.prepend( str.str().c_str() ); + + CodeModelItem::dump( file, false, Info ); + + if( recurse ) { + dumpMapDirect( file, m_enumerators ); + } +} + +void EnumeratorModel::dump( std::ostream& file, bool recurse, QString Info ) +{ + ostringstream str( ostringstream::out ); + + str << "value: " << m_value.ascii() << "\n"; + + Info.prepend( str.str().c_str() ); + + CodeModelItem::dump( file, false, Info ); + + if(recurse) {} ///just to get rid of the warning +} + +void TypeAliasModel::dump( std::ostream& file, bool recurse, QString Info ) { + ostringstream str( ostringstream::out ); + + str << "type: " << m_type.ascii() << "\n"; + + Info.prepend( str.str().c_str() ); + + CodeModelItem::dump( file, false, Info ); + + if(recurse) {} ///just to get rid of the warning +} + +void CodeModel::wipeout() +{ + m_files.clear(); + NamespaceDom ns = create<NamespaceModel>(); + ns->setName( "::" ); + + m_globalNamespace = ns; +} + +FileList CodeModel::fileList( ) +{ + return m_files.values(); +} + +const FileList CodeModel::fileList( ) const +{ + return m_files.values(); +} + +bool CodeModel::hasFile( const QString & name ) const +{ + return m_files.contains( name ); +} + +FileDom CodeModel::fileByName( const QString & name ) +{ + QMap<QString, FileDom>::const_iterator it = m_files.find( name ); + if( it != m_files.end() ) { + return *it; + } else { + return FileDom(); + } +} + +const FileDom CodeModel::fileByName( const QString & name ) const +{ + QMap<QString, FileDom>::const_iterator it = m_files.find( name ); + if( it != m_files.end() ) { + return *it; + } else { + return FileDom(); + } +} + +void CodeModel::addNamespace( NamespaceDom target, NamespaceDom source ) +{ + if( source->name().isEmpty() ){ + return; + } else if( !target->hasNamespace(source->name()) ){ + NamespaceDom ns = this->create<NamespaceModel>(); + ns->setName( source->name() ); + ns->setFileName( source->fileName() ); /// \FIXME ROBE + ns->setScope( source->scope() ); + target->addNamespace( ns ); + } + + NamespaceDom ns = target->namespaceByName( source->name() ); + + NamespaceList namespaceList = source->namespaceList(); + ClassList classList = source->classList(); + FunctionList functionList = source->functionList(); + FunctionDefinitionList functionDefinitionList = source->functionDefinitionList(); + VariableList variableList = source->variableList(); + EnumList enumList = source->enumList(); + TypeAliasList typeAliasList = source->typeAliasList(); + const NamespaceModel::NamespaceAliasModelList& namespaceAliases = source->namespaceAliases(); + const NamespaceModel::NamespaceImportModelList& namespaceImports = source->namespaceImports(); + + for( NamespaceList::Iterator it=namespaceList.begin(); it!=namespaceList.end(); ++it ) + addNamespace( ns, *it ); + for( ClassList::Iterator it=classList.begin(); it!=classList.end(); ++it ) + ns->addClass( *it ); + for( FunctionList::Iterator it=functionList.begin(); it!=functionList.end(); ++it ) + ns->addFunction( *it ); + for( FunctionDefinitionList::Iterator it=functionDefinitionList.begin(); it!=functionDefinitionList.end(); ++it ) + ns->addFunctionDefinition( *it ); + for( VariableList::Iterator it=variableList.begin(); it!=variableList.end(); ++it ) + ns->addVariable( *it ); + for( EnumList::Iterator it=enumList.begin(); it!=enumList.end(); ++it ) + ns->addEnum( *it ); + for( TypeAliasList::Iterator it=typeAliasList.begin(); it!=typeAliasList.end(); ++it ) + ns->addTypeAlias( *it ); + for( NamespaceModel::NamespaceAliasModelList::const_iterator it=namespaceAliases.begin(); it != namespaceAliases.end(); ++it ) + ns->addNamespaceAlias( *it ); + for( NamespaceModel::NamespaceImportModelList::const_iterator it=namespaceImports.begin(); it != namespaceImports.end(); ++it ) + ns->addNamespaceImport( *it ); +} + +void CodeModel::removeNamespace( NamespaceDom target, NamespaceDom source ) +{ + if( source->name().isEmpty() || !target->hasNamespace(source->name()) ) + return; + + NamespaceDom ns = target->namespaceByName( source->name() ); + + NamespaceList namespaceList = source->namespaceList(); + ClassList classList = source->classList(); + FunctionList functionList = source->functionList(); + FunctionDefinitionList functionDefinitionList = source->functionDefinitionList(); + VariableList variableList = source->variableList(); + EnumList enumList = source->enumList(); + TypeAliasList typeAliasList = source->typeAliasList(); + const NamespaceModel::NamespaceAliasModelList& namespaceAliases = source->namespaceAliases(); + const NamespaceModel::NamespaceImportModelList& namespaceImports = source->namespaceImports(); + + for( NamespaceList::Iterator it=namespaceList.begin(); it!=namespaceList.end(); ++it ) + removeNamespace( ns, *it ); + for( ClassList::Iterator it=classList.begin(); it!=classList.end(); ++it ) + ns->removeClass( *it ); + for( FunctionList::Iterator it=functionList.begin(); it!=functionList.end(); ++it ) + ns->removeFunction( *it ); + for( FunctionDefinitionList::Iterator it=functionDefinitionList.begin(); it!=functionDefinitionList.end(); ++it ) + ns->removeFunctionDefinition( *it ); + for( VariableList::Iterator it=variableList.begin(); it!=variableList.end(); ++it ) + ns->removeVariable( *it ); + for( EnumList::Iterator it=enumList.begin(); it!=enumList.end(); ++it ) + ns->removeEnum( *it ); + for( TypeAliasList::Iterator it=typeAliasList.begin(); it!=typeAliasList.end(); ++it ) + ns->removeTypeAlias( *it ); + for( NamespaceModel::NamespaceAliasModelList::const_iterator it=namespaceAliases.begin(); it != namespaceAliases.end(); ++it ) + ns->removeNamespaceAlias( *it ); + for( NamespaceModel::NamespaceImportModelList::const_iterator it=namespaceImports.begin(); it != namespaceImports.end(); ++it ) + ns->removeNamespaceImport( *it ); + + if( ns->namespaceList().isEmpty() && + ns->classList().isEmpty() && + ns->functionList().isEmpty() && + ns->functionDefinitionList().isEmpty() && + ns->variableList().isEmpty() && + ns->enumList().isEmpty() && + ns->typeAliasList().isEmpty() && + ns->namespaceImports().empty() && + ns->namespaceAliases().empty() ) + { + target->removeNamespace( ns ); + } +} + +bool CodeModel::addFile( FileDom file ) +{ + if( file->name().isEmpty() ) + return false; + + if( m_files.find( file->name() ) != m_files.end() ) { + ///the error-channel is set to 9007 because this problem appears with the cpp-support, so it is needed while debugging it + kdDebug(9007) << "file " << file->name() << " was added to code-model without removing it before! \n" << kdBacktrace() << endl; + removeFile( fileByName( file->name() ) ); + } + + // update global namespace + NamespaceList namespaceList = file->namespaceList(); + ClassList classList = file->classList(); + FunctionList functionList = file->functionList(); + FunctionDefinitionList functionDefinitionList = file->functionDefinitionList(); + VariableList variableList = file->variableList(); + EnumList enumList = file->enumList(); + TypeAliasList typeAliasList = file->typeAliasList(); + const NamespaceModel::NamespaceAliasModelList& namespaceAliases = file->namespaceAliases(); + const NamespaceModel::NamespaceImportModelList& namespaceImports = file->namespaceImports(); + + for( NamespaceList::Iterator it=namespaceList.begin(); it!=namespaceList.end(); ++it ) + addNamespace( m_globalNamespace, *it ); + for( ClassList::Iterator it=classList.begin(); it!=classList.end(); ++it ) + m_globalNamespace->addClass( *it ); + for( FunctionList::Iterator it=functionList.begin(); it!=functionList.end(); ++it ) + m_globalNamespace->addFunction( *it ); + for( FunctionDefinitionList::Iterator it=functionDefinitionList.begin(); it!=functionDefinitionList.end(); ++it ) + m_globalNamespace->addFunctionDefinition( *it ); + for( VariableList::Iterator it=variableList.begin(); it!=variableList.end(); ++it ) + m_globalNamespace->addVariable( *it ); + for( EnumList::Iterator it=enumList.begin(); it!=enumList.end(); ++it ) + m_globalNamespace->addEnum( *it ); + for( TypeAliasList::Iterator it=typeAliasList.begin(); it!=typeAliasList.end(); ++it ) + m_globalNamespace->addTypeAlias( *it ); + for( NamespaceModel::NamespaceAliasModelList::const_iterator it=namespaceAliases.begin(); it != namespaceAliases.end(); ++it ) + m_globalNamespace->addNamespaceAlias( *it ); + for( NamespaceModel::NamespaceImportModelList::const_iterator it=namespaceImports.begin(); it != namespaceImports.end(); ++it ) + m_globalNamespace->addNamespaceImport( *it ); + + m_files.insert( file->name(), file ); + return true; +} + +void CodeModel::removeFile( FileDom file ) +{ + // update global namespace + NamespaceList namespaceList = file->namespaceList(); + ClassList classList = file->classList(); + FunctionList functionList = file->functionList(); + FunctionDefinitionList functionDefinitionList = file->functionDefinitionList(); + VariableList variableList = file->variableList(); + EnumList enumList = file->enumList(); + TypeAliasList typeAliasList = file->typeAliasList(); + const NamespaceModel::NamespaceAliasModelList& namespaceAliases = file->namespaceAliases(); + const NamespaceModel::NamespaceImportModelList& namespaceImports = file->namespaceImports(); + + for( NamespaceList::Iterator it=namespaceList.begin(); it!=namespaceList.end(); ++it ) + removeNamespace( m_globalNamespace, *it ); + for( ClassList::Iterator it=classList.begin(); it!=classList.end(); ++it ) + m_globalNamespace->removeClass( *it ); + for( FunctionList::Iterator it=functionList.begin(); it!=functionList.end(); ++it ) + m_globalNamespace->removeFunction( *it ); + for( FunctionDefinitionList::Iterator it=functionDefinitionList.begin(); it!=functionDefinitionList.end(); ++it ) + m_globalNamespace->removeFunctionDefinition( *it ); + for( VariableList::Iterator it=variableList.begin(); it!=variableList.end(); ++it ) + m_globalNamespace->removeVariable( *it ); + for( EnumList::Iterator it=enumList.begin(); it!=enumList.end(); ++it ) + m_globalNamespace->removeEnum( *it ); + for( TypeAliasList::Iterator it=typeAliasList.begin(); it!=typeAliasList.end(); ++it ) + m_globalNamespace->removeTypeAlias( *it ); + for( NamespaceModel::NamespaceAliasModelList::const_iterator it=namespaceAliases.begin(); it != namespaceAliases.end(); ++it ) + m_globalNamespace->removeNamespaceAlias( *it ); + for( NamespaceModel::NamespaceImportModelList::const_iterator it=namespaceImports.begin(); it != namespaceImports.end(); ++it ) + m_globalNamespace->removeNamespaceImport( *it ); + + m_files.remove( file->name() ); +} + +// ------------------------------------------------------------------------ +CodeModelItem::CodeModelItem( int kind, CodeModel* model ) + : m_kind( kind ), m_model( model ) +{ + //kdDebug() << "CodeModelItem::CodeModelItem()" << endl; + m_startLine = 0; + m_startColumn = 0; + m_endLine = 0; + m_endColumn = 0; +} + +CodeModelItem::~ CodeModelItem( ) +{ +} + +QString CodeModelItem::name( ) const +{ + return m_name; +} + +void CodeModelItem::setName( const QString & name ) +{ + m_name = name; +} + +const FileDom CodeModelItem::file( ) const +{ + return m_model->fileByName( m_fileName ); +} + +FileDom CodeModelItem::file( ) +{ + return m_model->fileByName( m_fileName ); +} + +QString CodeModelItem::fileName() const +{ + return m_fileName; +} + +void CodeModelItem::setFileName( const QString& fileName ) +{ + m_fileName = fileName; +} + +void CodeModelItem::getStartPosition( int * line, int * column ) const +{ + if( line ) *line = m_startLine; + if( column ) *column = m_startColumn; +} + +void CodeModelItem::setStartPosition( int line, int column ) +{ + m_startLine = line; + m_startColumn = column; +} + +void CodeModelItem::getEndPosition( int * line, int * column ) const +{ + if( line ) *line = m_endLine; + if( column ) *column = m_endColumn; +} + +void CodeModelItem::setEndPosition( int line, int column ) +{ + m_endLine = line; + m_endColumn = column; +} + +void CodeModelItem::update( const CodeModelItem* i ) { + m_startLine = i->m_startLine; + m_startColumn = i->m_startColumn; + m_endLine = i->m_endLine; + m_endColumn = i->m_endColumn; +} + +bool CodeModelItem::canUpdate( const CodeModelItem* i ) const { + if( i->m_kind != m_kind || i->m_name != m_name ) + return false; + return true; +} + + +// ------------------------------------------------------------------------ +NamespaceModel::NamespaceModel( CodeModel* model ) + : ClassModel( model ) +{ + setKind( Namespace ); +} + +NamespaceList NamespaceModel::namespaceList( ) +{ + return m_namespaces.values(); +} + +const NamespaceList NamespaceModel::namespaceList( ) const +{ + return m_namespaces.values(); +} + +NamespaceDom NamespaceModel::namespaceByName( const QString & name ) +{ + return m_namespaces.contains( name ) ? m_namespaces[ name ] : NamespaceDom(); +} + +const NamespaceDom NamespaceModel::namespaceByName( const QString & name ) const +{ + return m_namespaces.contains( name ) ? m_namespaces[ name ] : NamespaceDom(); +} + +bool NamespaceModel::hasNamespace( const QString & name ) const +{ + return m_namespaces.contains( name ); +} + +bool NamespaceModel::addNamespace( NamespaceDom ns ) +{ + if( ns->name().isEmpty() ) + return false; + + m_namespaces[ ns->name() ] = ns; + return true; +} + +void NamespaceModel::removeNamespace( NamespaceDom ns ) +{ + m_namespaces.remove( ns->name() ); +} + +// ------------------------------------------------------------------------ +FileModel::FileModel( CodeModel* model ) + : NamespaceModel( model ), m_groupId( model->newGroupId() ), m_parseResult( 0 ) +{ +} + +// ------------------------------------------------------------------------ +ClassModel::ClassModel( CodeModel* model ) + : CodeModelItem( Class, model) +{ +} + +QStringList ClassModel::baseClassList( ) const +{ + return m_baseClassList; +} + +bool ClassModel::addBaseClass( const QString & baseClass ) +{ + m_baseClassList.push_back( baseClass ); + return true; +} + +void ClassModel::removeBaseClass( const QString & baseClass ) +{ + m_baseClassList.remove( baseClass ); +} + +ClassList ClassModel::classList( ) +{ + ClassList l; + QMap<QString, ClassList>::Iterator it = m_classes.begin(); + while( it != m_classes.end() ){ + l += *it; + ++it; + } + + return l; +} + +const ClassList ClassModel::classList( ) const +{ + ClassList l; + QMap<QString, ClassList>::ConstIterator it = m_classes.begin(); + while( it != m_classes.end() ){ + l += *it; + ++it; + } + + return l; +} + +bool ClassModel::hasClass( const QString & name ) const +{ + return m_classes.contains( name ); +} + +ClassList ClassModel::classByName( const QString & name ) +{ + return m_classes.contains( name ) ? m_classes[ name ] : ClassList(); +} + +const ClassList ClassModel::classByName( const QString & name ) const +{ + return m_classes.contains( name ) ? m_classes[ name ] : ClassList(); +} + +bool ClassModel::addClass( ClassDom klass ) +{ + if( klass->name().isEmpty() ) + return false; + + m_classes[ klass->name() ].push_back( klass ); + return true; +} + +void ClassModel::removeClass( ClassDom klass ) +{ + m_classes[ klass->name() ].remove( klass ); + + if( m_classes[klass->name()].isEmpty() ) + m_classes.remove( klass->name() ); +} + +FunctionList ClassModel::functionList( ) +{ + FunctionList l; + QMap<QString, FunctionList>::Iterator it = m_functions.begin(); + while( it != m_functions.end() ){ + l += *it; + ++it; + } + + return l; +} + +const FunctionList ClassModel::functionList( ) const +{ + FunctionList l; + QMap<QString, FunctionList>::ConstIterator it = m_functions.begin(); + while( it != m_functions.end() ){ + l += *it; + ++it; + } + + return l; +} + +bool ClassModel::hasFunction( const QString & name ) const +{ + return m_functions.contains( name ); +} + +FunctionList ClassModel::functionByName( const QString & name ) +{ + return m_functions.contains( name ) ? m_functions[ name ] : FunctionList(); +} + +const FunctionList ClassModel::functionByName( const QString & name ) const +{ + return m_functions.contains( name ) ? m_functions[ name ] : FunctionList(); +} + +bool ClassModel::addFunction( FunctionDom fun ) +{ + if( fun->name().isEmpty() ) + return false; + + m_functions[ fun->name() ].push_back( fun ); + return true; +} + +void ClassModel::removeFunction( FunctionDom fun ) +{ + m_functions[ fun->name() ].remove( fun ); + + if( m_functions[fun->name()].isEmpty() ) + m_functions.remove( fun->name() ); +} + +FunctionDefinitionList ClassModel::functionDefinitionList( ) +{ + FunctionDefinitionList l; + QMap<QString, FunctionDefinitionList>::Iterator it = m_functionDefinitions.begin(); + while( it != m_functionDefinitions.end() ){ + l += *it; + ++it; + } + + return l; +} + +const FunctionDefinitionList ClassModel::functionDefinitionList( ) const +{ + FunctionDefinitionList l; + QMap<QString, FunctionDefinitionList>::ConstIterator it = m_functionDefinitions.begin(); + while( it != m_functionDefinitions.end() ){ + l += *it; + ++it; + } + + return l; +} + +bool ClassModel::hasFunctionDefinition( const QString & name ) const +{ + return m_functionDefinitions.contains( name ); +} + +FunctionDefinitionList ClassModel::functionDefinitionByName( const QString & name ) +{ + return m_functionDefinitions.contains( name ) ? m_functionDefinitions[ name ] : FunctionDefinitionList(); +} + +const FunctionDefinitionList ClassModel::functionDefinitionByName( const QString & name ) const +{ + return m_functionDefinitions.contains( name ) ? m_functionDefinitions[ name ] : FunctionDefinitionList(); +} + +bool ClassModel::addFunctionDefinition( FunctionDefinitionDom fun ) +{ + if( fun->name().isEmpty() ) + return false; + + m_functionDefinitions[ fun->name() ].push_back( fun ); + return true; +} + +void ClassModel::removeFunctionDefinition( FunctionDefinitionDom fun ) +{ + m_functionDefinitions[ fun->name() ].remove( fun ); + + if( m_functionDefinitions[fun->name()].isEmpty() ) + m_functionDefinitions.remove( fun->name() ); +} + +VariableList ClassModel::variableList( ) +{ + return m_variables.values(); +} + +const VariableList ClassModel::variableList( ) const +{ + return m_variables.values(); +} + +VariableDom ClassModel::variableByName( const QString & name ) +{ + return m_variables.contains( name ) ? m_variables[ name ] : VariableDom(); +} + +const VariableDom ClassModel::variableByName( const QString & name ) const +{ + return m_variables.contains( name ) ? m_variables[ name ] : VariableDom(); +} + +bool ClassModel::hasVariable( const QString & name ) const +{ + return m_variables.contains( name ); +} + +bool ClassModel::addVariable( VariableDom var ) +{ + if( var->name().isEmpty() ) + return false; + + m_variables.insert( var->name(), var ); + return true; +} + +void ClassModel::removeVariable( VariableDom var ) +{ + m_variables.remove( var->name() ); +} + +EnumList ClassModel::enumList( ) +{ + return m_enumerators.values(); +} + +const EnumList ClassModel::enumList( ) const +{ + return m_enumerators.values(); +} + +EnumDom ClassModel::enumByName( const QString & name ) +{ + return m_enumerators.contains( name ) ? m_enumerators[ name ] : EnumDom(); +} + +const EnumDom ClassModel::enumByName( const QString & name ) const +{ + return m_enumerators.contains( name ) ? m_enumerators[ name ] : EnumDom(); +} + +bool ClassModel::hasEnum( const QString & name ) const +{ + return m_enumerators.contains( name ); +} + +bool ClassModel::addEnum( EnumDom e ) +{ + if( e->name().isEmpty() ) + return false; + + m_enumerators.insert( e->name(), e ); + return true; +} + +void ClassModel::update( const ClassModel* klass ) { + CodeModelItem::update( klass ); + eachUpdate( m_classes, klass->m_classes ) ; + eachUpdate( m_functions, klass->m_functions ) ; + eachUpdate( m_functionDefinitions, klass->m_functionDefinitions ) ; + eachUpdateSingle( m_variables, klass->m_variables ) ; + eachUpdateSingle( m_enumerators, klass->m_enumerators ) ; + eachUpdate( m_typeAliases, klass->m_typeAliases ); +} + +bool ClassModel::canUpdate( const ClassModel* klass ) const { + if( !CodeModelItem::canUpdate( klass ) ) + return false; + + return eachCanUpdate( m_classes, klass->m_classes ) && + eachCanUpdate( m_functions, klass->m_functions ) && + eachCanUpdate( m_functionDefinitions, klass->m_functionDefinitions ) && + eachCanUpdateSingle( m_variables, klass->m_variables ) && + eachCanUpdateSingle( m_enumerators, klass->m_enumerators ) && + eachCanUpdate( m_typeAliases, klass->m_typeAliases ); +} + +void ClassModel::removeEnum( EnumDom e ) +{ + m_enumerators.remove( e->name() ); +} + +TypeAliasList ClassModel::typeAliasList( ) +{ + TypeAliasList l; + QMap<QString, TypeAliasList>::Iterator it = m_typeAliases.begin(); + while( it != m_typeAliases.end() ){ + l += *it; + ++it; + } + + return l; +} + +const TypeAliasList ClassModel::typeAliasList( ) const +{ + TypeAliasList l; + QMap<QString, TypeAliasList>::ConstIterator it = m_typeAliases.begin(); + while( it != m_typeAliases.end() ){ + l += *it; + ++it; + } + + return l; +} + +bool ClassModel::hasTypeAlias( const QString & name ) const +{ + return m_typeAliases.contains( name ); +} + +TypeAliasList ClassModel::typeAliasByName( const QString & name ) +{ + return m_typeAliases.contains( name ) ? m_typeAliases[ name ] : TypeAliasList(); +} + +const TypeAliasList ClassModel::typeAliasByName( const QString & name ) const +{ + return m_typeAliases.contains( name ) ? m_typeAliases[ name ] : TypeAliasList(); +} + +bool ClassModel::addTypeAlias( TypeAliasDom typeAlias ) +{ + if( typeAlias->name().isEmpty() ) + return false; + + m_typeAliases[ typeAlias->name() ].push_back( typeAlias ); + return true; +} + +void ClassModel::removeTypeAlias( TypeAliasDom typeAlias ) +{ + m_typeAliases[ typeAlias->name() ].remove( typeAlias ); + + if( m_typeAliases[typeAlias->name()].isEmpty() ) + m_typeAliases.remove( typeAlias->name() ); +} + + + +// ------------------------------------------------------------------------ +ArgumentModel::ArgumentModel( CodeModel* model ) + : CodeModelItem( Argument, model) +{ +} + +QString ArgumentModel::type( ) const +{ + return m_type; +} + +void ArgumentModel::setType( const QString& type ) +{ + m_type = type; +} + +QString ArgumentModel::defaultValue( ) const +{ + return m_defaultValue; +} + +void ArgumentModel::setDefaultValue( const QString & defaultValue ) +{ + m_defaultValue = defaultValue; +} + +// ------------------------------------------------------------------------ +FunctionModel::FunctionModel( CodeModel* model ) + : CodeModelItem( Function, model) +{ + m_access = Public; + d.v.m_signal = false; + d.v.m_slot = false; + d.v.m_virtual = false; + d.v.m_static = false; + d.v.m_inline = false; + d.v.m_constant = false; + d.v.m_abstract = false; +} + +bool FunctionModel::isVirtual( ) const +{ + return d.v.m_virtual; +} + +void FunctionModel::setVirtual( bool isVirtual ) +{ + d.v.m_virtual = isVirtual; +} + +bool FunctionModel::isStatic( ) const +{ + return d.v.m_static; +} + +void FunctionModel::setStatic( bool isStatic ) +{ + d.v.m_static = isStatic; +} + +bool FunctionModel::isInline( ) const +{ + return d.v.m_inline; +} + +void FunctionModel::setInline( bool isInline ) +{ + d.v.m_inline = isInline; +} + +bool FunctionModel::isConstant( ) const +{ + return d.v.m_constant; +} + +void FunctionModel::setConstant( bool isConstant ) +{ + d.v.m_constant = isConstant; +} + +bool FunctionModel::isAbstract( ) const +{ + return d.v.m_abstract; +} + +void FunctionModel::setAbstract( bool isAbstract ) +{ + d.v.m_abstract = isAbstract; +} + +QString FunctionModel::resultType( ) const +{ + return m_resultType; +} + +void FunctionModel::setResultType( const QString& type ) +{ + m_resultType = type; +} + +ArgumentList FunctionModel::argumentList( ) +{ + return m_arguments; +} + +const ArgumentList FunctionModel::argumentList( ) const +{ + return m_arguments; +} + +bool FunctionModel::addArgument( ArgumentDom arg ) +{ + m_arguments.push_back( arg ); + return true; +} + +void FunctionModel::removeArgument( ArgumentDom arg ) +{ + m_arguments.remove( arg ); +} + +void FunctionModel::update( const FunctionModel* i ) { + m_access = i->m_access; + CodeModelItem::update( i ); +} + +bool FunctionModel::canUpdate( const FunctionModel* i ) const { + if( !CodeModelItem::canUpdate( i ) ) + return false; + if( m_resultType != i->m_resultType || m_arguments.count() != i->m_arguments.count() || m_scope != i->m_scope ) + return false; + return true; +} + + +// ------------------------------------------------------------------------ +VariableModel::VariableModel( CodeModel* model ) + : CodeModelItem( Variable, model) +{ + m_access = Public; + m_static = false; + m_isEnumeratorVariable = false; +} + +bool VariableModel::isStatic( ) const +{ + return m_static; +} + +void VariableModel::setStatic( bool isStatic ) +{ + m_static = isStatic; +} + +QString VariableModel::type( ) const +{ + return m_type; +} + +void VariableModel::setType( const QString& type ) +{ + m_type = type; +} + +bool VariableModel::isEnumeratorVariable() const { + return m_isEnumeratorVariable; +} + +void VariableModel::setEnumeratorVariable( bool b) { + m_isEnumeratorVariable = b; +} + +int FunctionModel::access( ) const +{ + return m_access; +} + +void FunctionModel::setAccess( int access ) +{ + m_access = access; +} + +bool FunctionModel::isSignal( ) const +{ + return d.v.m_signal; +} + +void FunctionModel::setSignal( bool isSignal ) +{ + d.v.m_signal = isSignal; +} + +bool FunctionModel::isSlot( ) const +{ + return d.v.m_slot; +} + +void FunctionModel::setSlot( bool isSlot ) +{ + d.v.m_slot = isSlot; +} + +FunctionDefinitionModel::FunctionDefinitionModel( CodeModel* model ) + : FunctionModel( model ) +{ +} + +int VariableModel::access( ) const +{ + return m_access; +} + +void VariableModel::setAccess( int access ) +{ + m_access = access; +} + +const NamespaceDom CodeModel::globalNamespace( ) const +{ + return m_globalNamespace; +} + +void CodeModelItem::read( QDataStream & stream ) +{ + stream + >> m_kind + >> m_name + >> m_fileName + >> m_startLine + >> m_startColumn + >> m_endLine + >> m_endColumn + >> m_comment; + + if( isTemplateable() ) { + TemplateModelItem* t = (TemplateModelItem*)( this ); + + t->read( stream ); + } +} + +void CodeModelItem::write( QDataStream & stream ) const +{ + stream + << m_kind + << m_name + << m_fileName + << m_startLine + << m_startColumn + << m_endLine + << m_endColumn + << m_comment; + + if( isTemplateable() ) { + TemplateModelItem* t = (TemplateModelItem*)( this ); + t-> write( stream ); + } +} + +void ClassModel::read( QDataStream & stream ) +{ + CodeModelItem::read( stream ); + + TemplateModelItem::read( stream ); + + stream >> m_scope >> m_baseClassList; + + int n; + + m_classes.clear(); + stream >> n; + for( int i=0; i<n; ++i ){ + ClassDom klass = codeModel()->create<ClassModel>(); + klass->read( stream ); + addClass( klass ); + } + + m_functions.clear(); + stream >> n; + for( int i=0; i<n; ++i ){ + FunctionDom fun = codeModel()->create<FunctionModel>(); + fun->read( stream ); + addFunction( fun ); + } + + m_functionDefinitions.clear(); + stream >> n; + for( int i=0; i<n; ++i ){ + FunctionDefinitionDom fun = codeModel()->create<FunctionDefinitionModel>(); + fun->read( stream ); + addFunctionDefinition( fun ); + } + + m_variables.clear(); + stream >> n; + for( int i=0; i<n; ++i ){ + VariableDom var = codeModel()->create<VariableModel>(); + var->read( stream ); + addVariable( var ); + } + + m_enumerators.clear(); + stream >> n; + for( int i=0; i<n; ++i ){ + EnumDom e = codeModel()->create<EnumModel>(); + e->read( stream ); + addEnum( e ); + } + + m_typeAliases.clear(); + stream >> n; + for( int i=0; i<n; ++i ){ + TypeAliasDom typeAlias = codeModel()->create<TypeAliasModel>(); + typeAlias->read( stream ); + addTypeAlias( typeAlias ); + } +} + +void ClassModel::write( QDataStream & stream ) const +{ + CodeModelItem::write( stream ); + + TemplateModelItem::write( stream ); + + stream << m_scope << m_baseClassList; + + const ClassList class_list = classList(); + stream << int( class_list.size() ); + for( ClassList::ConstIterator it = class_list.begin(); it!=class_list.end(); ++it ) + (*it)->write( stream ); + + const FunctionList function_list = functionList(); + stream << int( function_list.size() ); + for( FunctionList::ConstIterator it = function_list.begin(); it!=function_list.end(); ++it ) + (*it)->write( stream ); + + const FunctionDefinitionList function_definition_list = functionDefinitionList(); + stream << int( function_definition_list.size() ); + for( FunctionDefinitionList::ConstIterator it = function_definition_list.begin(); it!=function_definition_list.end(); ++it ) + (*it)->write( stream ); + + const VariableList variable_list = variableList(); + stream << int( variable_list.size() ); + for( VariableList::ConstIterator it = variable_list.begin(); it!=variable_list.end(); ++it ) + (*it)->write( stream ); + + const EnumList enum_list = enumList(); + stream << int( enum_list.size() ); + for( EnumList::ConstIterator it = enum_list.begin(); it!=enum_list.end(); ++it ) + (*it)->write( stream ); + + const TypeAliasList type_alias_list = typeAliasList(); + stream << int( type_alias_list.size() ); + for( TypeAliasList::ConstIterator it = type_alias_list.begin(); it!=type_alias_list.end(); ++it ) + (*it)->write( stream ); + +} + +void NamespaceModel::read( QDataStream & stream ) +{ + ClassModel::read( stream ); + + int n; + + m_namespaces.clear(); m_namespaceAliases.clear(); m_namespaceImports.clear(); + stream >> n; + for( int i=0; i<n; ++i ){ + NamespaceDom ns = codeModel()->create<NamespaceModel>(); + ns->read( stream ); + addNamespace( ns ); + } + + stream >> n; + for( int a = 0; a < n; a++ ) { + NamespaceAliasModel m; + m.read( stream ); + m_namespaceAliases.insert( m ); + } + stream >> n; + for( int a = 0; a < n; a++ ) { + NamespaceImportModel m; + m.read( stream ); + m_namespaceImports.insert( m ); + } +} + +void NamespaceModel::addNamespaceImport( const NamespaceImportModel& import ) { + m_namespaceImports.insert( import ); +} + +void NamespaceModel::addNamespaceAlias( const NamespaceAliasModel& alias ) { + m_namespaceAliases.insert( alias ); +} + +void NamespaceModel::removeNamespaceImport( const NamespaceImportModel& import ) { + m_namespaceImports.erase( import ); +} + +void NamespaceModel::removeNamespaceAlias( const NamespaceAliasModel& alias ) { + m_namespaceAliases.erase( alias ); +} + +void NamespaceModel::write( QDataStream & stream ) const +{ + ClassModel::write( stream ); + + const NamespaceList namespace_list = namespaceList(); + stream << int( namespace_list.size() ); + for( NamespaceList::ConstIterator it = namespace_list.begin(); it!=namespace_list.end(); ++it ) + (*it)->write( stream ); + + stream << int( m_namespaceAliases.size() ); + for( NamespaceAliasModelList::const_iterator it = m_namespaceAliases.begin(); it != m_namespaceAliases.end(); ++it ) + (*it).write( stream ); + stream << int( m_namespaceImports.size() ); + for( NamespaceImportModelList::const_iterator it = m_namespaceImports.begin(); it != m_namespaceImports.end(); ++it ) + (*it).write( stream ); +} + +bool NamespaceModel::canUpdate( const NamespaceModel* ns ) const { + if( !ClassModel::canUpdate( ns ) ) + return false; + + const NamespaceAliasModelList& aliases = namespaceAliases(); + const NamespaceImportModelList& imports = namespaceImports(); + const NamespaceAliasModelList& aliases2 = ns->namespaceAliases(); + const NamespaceImportModelList& imports2 = ns->namespaceImports(); + + if( aliases.size() != aliases2.size() ) return false; + if( imports.size() != imports2.size() ) return false; + + ///Test if all aliases are same, if not return false + NamespaceModel::NamespaceAliasModelList::const_iterator it_al1 = aliases.begin(); + NamespaceModel::NamespaceAliasModelList::const_iterator it_al2 = aliases2.begin(); + while( it_al1 != aliases.end() ) { + if( !(*it_al1 == *it_al2) ) + return false; + + ++it_al1; + ++it_al2; + } + + ///Test if all imports are same, if not return false + NamespaceModel::NamespaceImportModelList::const_iterator it_ip1 = imports.begin(); + NamespaceModel::NamespaceImportModelList::const_iterator it_ip2 = imports2.begin(); + while( it_ip1 != imports.end() ) { + if( !(*it_ip1 == *it_ip2) ) + return false; + + ++it_ip1; + ++it_ip2; + } + + return eachCanUpdateSingle( m_namespaces, ns->m_namespaces ); +} + +void NamespaceModel::update( const NamespaceModel* ns ) +{ + ClassModel::update( ns ); + + eachUpdateSingle( m_namespaces, ns->m_namespaces ); +} + +void FileModel::read( QDataStream & stream ) +{ + stream >> m_groupId; + bool b; + stream >> b; + if( b ) { + int i; + stream >> i; + ParsedFileType t( (ParsedFileType) i ); + switch( t ) { + case CppParsedFile: + m_parseResult = (AbstractParseResult*)(new ParsedFile( stream )); + break; + } + } + + NamespaceModel::read( stream ); +} + +void FileModel::write( QDataStream & stream ) const +{ + stream << m_groupId; + bool b = m_parseResult; + stream << b; + if( b ) { + int i = m_parseResult->type(); + stream << i; + m_parseResult->write( stream ); + } + + NamespaceModel::write( stream ); +} + +void ArgumentModel::read( QDataStream & stream ) +{ + CodeModelItem::read( stream ); + + + stream >> m_type >> m_defaultValue; +} + +void ArgumentModel::write( QDataStream & stream ) const +{ + CodeModelItem::write( stream ); + + stream << m_type << m_defaultValue; +} + +void FunctionModel::read( QDataStream & stream ) +{ + CodeModelItem::read( stream ); + TemplateModelItem::read( stream ); + + stream >> m_scope; + stream >> d.flags; + + int n; + + m_arguments.clear(); + stream >> n; + for( int i=0; i<n; ++i ){ + ArgumentDom arg = codeModel()->create<ArgumentModel>(); + arg->read( stream ); + addArgument( arg ); + } + + stream + >> m_resultType; +} + +void FunctionModel::write( QDataStream & stream ) const +{ + CodeModelItem::write( stream ); + TemplateModelItem::write( stream ); + + stream << m_scope; + stream << d.flags; + + const ArgumentList argument_list = argumentList(); + stream << int( argument_list.size() ); + for( ArgumentList::ConstIterator it = argument_list.begin(); it!=argument_list.end(); ++it ) + (*it)->write( stream ); + + stream + << m_resultType; +} + +void CodeModel::read( QDataStream & stream ) +{ + int n; + + m_files.clear(); + + stream >> n; + for( int i=0; i<n; ++i ){ + FileDom file = this->create<FileModel>(); + file->read( stream ); + addFile( file ); + } +} + +void CodeModel::write( QDataStream & stream ) const +{ + const FileList file_list = fileList(); + stream << int( file_list.size() ); + for( FileList::ConstIterator it = file_list.begin(); it!=file_list.end(); ++it ) + (*it)->write( stream ); +} + +void VariableModel::read( QDataStream & stream ) +{ + CodeModelItem::read( stream ); + stream >> m_access >> m_static >> m_type >> m_isEnumeratorVariable; +} + +void VariableModel::write( QDataStream & stream ) const +{ + CodeModelItem::write( stream ); + stream << m_access << m_static << m_type << m_isEnumeratorVariable; +} + +void VariableModel::update( const VariableModel* i ) { + m_access = i->m_access; + CodeModelItem::update( i ); +} + +bool VariableModel::canUpdate( const VariableModel* i ) const { + if( !CodeModelItem::canUpdate( i ) ) + return false; + if( m_access != i->m_access || m_static != i->m_static || m_type != i->m_type || m_isEnumeratorVariable != i->m_isEnumeratorVariable ) + return false; + return true; +} + +// ------------------------------------------------------- +EnumModel::EnumModel( CodeModel * model ) + : CodeModelItem( Enum, model) +{ +} + +int EnumModel::access( ) const +{ + return m_access; +} + +void EnumModel::setAccess( int access ) +{ + m_access = access; +} + +EnumeratorList EnumModel::enumeratorList( ) +{ + return m_enumerators.values(); +} + +const EnumeratorList EnumModel::enumeratorList( ) const +{ + return m_enumerators.values(); +} + +void EnumModel::addEnumerator( EnumeratorDom enumerator ) +{ + m_enumerators.insert( enumerator->name(), enumerator ); +} + +void EnumModel::read( QDataStream & stream ) +{ + CodeModelItem::read( stream ); + stream >> m_access; + + int n; + stream >> n; + for( int i=0; i<n; ++i ){ + EnumeratorDom e = codeModel()->create<EnumeratorModel>(); + e->read( stream ); + addEnumerator( e ); + } +} + +void EnumModel::write( QDataStream & stream ) const +{ + CodeModelItem::write( stream ); + + stream << m_access; + const EnumeratorList enumerator_list = enumeratorList(); + stream << int( enumerator_list.size() ); + for( EnumeratorList::ConstIterator it = enumerator_list.begin(); it!=enumerator_list.end(); ++it ) + (*it)->write( stream ); +} + +EnumeratorModel::EnumeratorModel( CodeModel * model ) + : CodeModelItem( Enumerator, model ) +{ +} + +QString EnumeratorModel::value( ) const +{ + return m_value; +} + +void EnumeratorModel::setValue( const QString & value ) +{ + m_value = value; +} + +void EnumeratorModel::read( QDataStream & stream ) +{ + CodeModelItem::read( stream ); + stream >> m_value; +} + +void EnumeratorModel::write( QDataStream & stream ) const +{ + CodeModelItem::write( stream ); + stream << m_value; +} + +void EnumModel::removeEnumerator( EnumeratorDom e ) +{ + m_enumerators.remove( e->name() ); +} + +void EnumModel::update( const EnumModel* i ) { + m_access = i->m_access; + CodeModelItem::update( i ); +} + +bool EnumModel::canUpdate( const EnumModel* i ) const { + if( !CodeModelItem::canUpdate( i ) ) + return false; + ///@todo check not complete + if( m_access != i->m_access || m_enumerators.count() != i->m_enumerators.count() ) + return false; + return true; +} + +// --------------------------------------------------------------- +TypeAliasModel::TypeAliasModel( CodeModel * model ) + : CodeModelItem( TypeAlias, model ) +{ +} + +void TypeAliasModel::read( QDataStream & stream ) +{ + CodeModelItem::read( stream ); + + stream >> m_type; +} + +void TypeAliasModel::write( QDataStream & stream ) const +{ + CodeModelItem::write( stream ); + + stream << m_type; +} + +QString TypeAliasModel::type( ) const +{ + return m_type; +} + +void TypeAliasModel::setType( const QString & type ) +{ + m_type = type; +} + +void TypeAliasModel::update( const TypeAliasModel* i ) { + CodeModelItem::update( i ); +} + +bool TypeAliasModel::canUpdate( const TypeAliasModel* i ) const { + if( !CodeModelItem::canUpdate( i ) ) + return false; + return m_type == i->m_type; +} + +void FileModel::update( const FileModel* file ) { + m_parseResult = file->m_parseResult; + NamespaceModel::update( file ); +} + +FileList FileModel::wholeGroup() { + if( isSingleGroup( m_groupId ) ) return ( FileList() << FileDom(this) ); + return codeModel()->getGroup( m_groupId ); +} + +QStringList FileModel::wholeGroupStrings() const { + if( isSingleGroup( m_groupId ) ) return (QStringList() << name() ); + return codeModel()->getGroupStrings( m_groupId ); +} + +ParseResultPointer FileModel::parseResult() const { + return m_parseResult; +} + +void FileModel::setParseResult( const ParseResultPointer& result ) { + m_parseResult = result; +} diff --git a/lib/interfaces/codemodel.h b/lib/interfaces/codemodel.h new file mode 100644 index 00000000..b8c0c894 --- /dev/null +++ b/lib/interfaces/codemodel.h @@ -0,0 +1,1612 @@ +/* This file is part of KDevelop + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2004 Matt Rogers <mattr@kde.org> + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef CODEMODEL_H +#define CODEMODEL_H + +/** +@file codemodel.h +Code Model - a memory symbol store. +*/ + + +#include <qmap.h> +#include <qstringlist.h> +#include <ksharedptr.h> +#include <qvaluevector.h> +#include "hashedstring.h" + +#include <iostream> +#include <ostream> +#include <string> +#include <sstream> +#include <set> + +enum ParsedFileType { + CppParsedFile +}; + +class AbstractParseResult : public KShared { +public: + virtual void read( QDataStream& stream ) = 0; + + virtual void write( QDataStream& stream ) const = 0; + + virtual ParsedFileType type() const = 0; +}; + +typedef KSharedPtr<AbstractParseResult> ParseResultPointer; + +using namespace std; + +class CodeModel; +class CodeModelItem; +class FileModel; +class NamespaceModel; +class ClassModel; +class FunctionModel; +class FunctionDefinitionModel; +class VariableModel; +class ArgumentModel; +class EnumModel; +class EnumeratorModel; +class TypeAliasModel; + +/** +@class ItemDom +Safe pointer to the @ref CodeModelItem. +This is a type definition: @code typedef KSharedPtr<CodeModelItem> ItemDom; @endcode +@sa KSharedPtr +*/ +typedef KSharedPtr<CodeModelItem> ItemDom; + +/** +@class FileDom +Safe pointer to the @ref FileModel. +This is a type definition: @code typedef KSharedPtr<FileModel> FileDom; @endcode +@sa KSharedPtr +*/ +typedef KSharedPtr<FileModel> FileDom; + +/** +@class NamespaceDom +Safe pointer to the @ref NamespaceModel. +This is a type definition: @code typedef KSharedPtr<NamespaceModel> NamespaceDom; @endcode +@sa KSharedPtr +*/ +typedef KSharedPtr<NamespaceModel> NamespaceDom; + +/** +@class ClassDom +Safe pointer to the @ref ClassModel. +This is a type definition: @code typedef KSharedPtr<ClassModel> ClassDom; @endcode +@sa KSharedPtr +*/ +typedef KSharedPtr<ClassModel> ClassDom; + +/** +@class FunctionDom +Safe pointer to the @ref FunctionModel. +This is a type definition: @code typedef KSharedPtr<FunctionModel> FunctionDom; @endcode +@sa KSharedPtr +*/ +typedef KSharedPtr<FunctionModel> FunctionDom; + +/** +@class FunctionDefinitionDom +Safe pointer to the @ref FunctionDefinitionModel. +This is a type definition: @code typedef KSharedPtr<FunctionDefinitionModel> FunctionDefinitionDom; @endcode +@sa KSharedPtr +*/ +typedef KSharedPtr<FunctionDefinitionModel> FunctionDefinitionDom; + +/** +@class VariableDom +Safe pointer to the @ref VariableModel. +This is a type definition: @code typedef KSharedPtr<VariableModel> VariableDom; @endcode +@sa KSharedPtr +*/ +typedef KSharedPtr<VariableModel> VariableDom; + +/** +@class ArgumentDom +Safe pointer to the @ref ArgumentModel. +This is a type definition: @code typedef KSharedPtr<ArgumentModel> ArgumentDom; @endcode +@sa KSharedPtr +*/ +typedef KSharedPtr<ArgumentModel> ArgumentDom; + +/** +@class EnumDom +Safe pointer to the @ref EnumModel. +This is a type definition: @code typedef KSharedPtr<EnumModel> EnumDom; @endcode +@sa KSharedPtr +*/ +typedef KSharedPtr<EnumModel> EnumDom; + +/** +@class TypeAliasDom +Safe pointer to the @ref TypeAliasModel. +This is a type definition: @code typedef KSharedPtr<TypeAliasModel> TypeAliasDom; @endcode +@sa KSharedPtr +*/ +typedef KSharedPtr<TypeAliasModel> TypeAliasDom; + +/** +@class EnumeratorDom +Safe pointer to the @ref EnumeratorModel. +This is a type definition: @code typedef KSharedPtr<EnumeratorModel> EnumeratorDom; @endcode +@sa KSharedPtr +*/ +typedef KSharedPtr<EnumeratorModel> EnumeratorDom; + +/** +@class ItemList +The list of code model items. +This is a type definition: @code typedef QValueList<ItemDom> ItemList; @endcode +@sa QValueList +*/ +typedef QValueList<ItemDom> ItemList; + +/** +@class FileList +The list of code model files. +This is a type definition: @code typedef QValueList<FileDom> FileList; @endcode +@sa QValueList +*/ +typedef QValueList<FileDom> FileList; + +/** +@class NamespaceList +The list of code model namespaces. +This is a type definition: @code typedef QValueList<NamespaceDom> NamespaceList; @endcode +@sa QValueList +*/ +typedef QValueList<NamespaceDom> NamespaceList; + +/** +@class ClassList +The list of code model classes. +This is a type definition: @code typedef QValueList<ClassDom> ClassList; @endcode +@sa QValueList +*/ +typedef QValueList<ClassDom> ClassList; + +/** +@class FunctionList +The list of code model functions. +This is a type definition: @code typedef QValueList<FunctionDom> FunctionList; @endcode +@sa QValueList +*/ +typedef QValueList<FunctionDom> FunctionList; + +/** +@class FunctionDefinitionList +The list of code model function definitions. +This is a type definition: @code typedef QValueList<FunctionDefinitionDom> FunctionDefinitionList; @endcode +@sa QValueList +*/ + +typedef QValueList<FunctionDefinitionDom> FunctionDefinitionList; +/** +@class VariableList +The list of code model variables. +This is a type definition: @code typedef QValueList<VariableDom> VariableList; @endcode +@sa QValueList +*/ +typedef QValueList<VariableDom> VariableList; + +/** +@class ArgumentList +The list of code model arguments. +This is a type definition: @code typedef QValueList<ArgumentDom> ArgumentList; @endcode +@sa QValueList +*/ +typedef QValueList<ArgumentDom> ArgumentList; + +/** +@class EnumList +The list of code model enums. +This is a type definition: @code typedef QValueList<EnumDom> EnumList; @endcode +@sa QValueList +*/ +typedef QValueList<EnumDom> EnumList; + +/** +@class TypeAliasList +The list of code model type aliases. +This is a type definition: @code typedef QValueList<TypeAliasDom> TypeAliasList; @endcode +@sa QValueList +*/ +typedef QValueList<TypeAliasDom> TypeAliasList; + +/** +@class EnumeratorList +The list of code model enumerators. +This is a type definition: @code typedef QValueList<EnumeratorDom> EnumeratorList; @endcode +@sa QValueList +*/ +typedef QValueList<EnumeratorDom> EnumeratorList; + +/** +Iterates through @p lst and creates sorted list of code model item names. +Can be used, for example, to get the list of classes in the store: +@code +QStringList classList = sortedNameList(codeModel()->globalNamespace()->classList()); +@endcode +@param lst The list to iterate. +@return Sorted list of code model item names. +*/ +template <class ItemList> +QStringList sortedNameList( const ItemList& lst ) +{ + QStringList nameList; + + typename ItemList::ConstIterator it = lst.begin(); + while( it != lst.end() ){ + if( !(*it)->name().isEmpty() ) + nameList << (*it)->name(); + ++it; + } + + nameList.sort(); + return nameList; +} + +/** +Casts safe code model pointers (@p KSharedPtr<T> objects like +FileDom, NamespaceDom, etc.) to the @p Result type. + +Example: +@code +//ns is of type NamespaceDom +ClassDom cl = model_cast<ClassDom>(ns); +@endcode +@param x Object to cast. +*/ +template <class Result, class T> +Result model_cast( KSharedPtr<T> x ) +{ + Result r( static_cast<T*>(x) ); + return r; +} + +/** +Casts code model pointers (objects like +FileModel, NamespaceModel, etc.) to the @p Result type. + +Example: +@code +//ns is of type NamespaceModel* +ClassDom cl = model_cast<ClassDom>(ns); +@endcode +@param x Object to cast. +*/ +template <class Result, class T> +Result model_cast( T* x ) +{ + Result r( static_cast<T*>(x) ); + return r; +} + + +/** +Code Model - a memory symbol store. +Symbol store (aka class store) is a database of symbols +found in code with the important information about those symbols. + +For example, programming language support plugins use symbol store +to remember information about classes, functions, etc. For each type +of symbol a certain information can be stored - symbol name, the +location in source file, etc. + +@sa codemodel.h documentation for a list of typedefs and other convenience functions. + +@sa codemodel_utils.h documentation for an additional code model utility functions and classes reference. +*/ +class CodeModel +{ +public: + /**Constructor.*/ + CodeModel(); + /**Destructor.*/ + virtual ~CodeModel(); + + /**Creates a code model item. This should be used to create + code model items. + + For example, to create a class model somewhere in your plugin, use: + @code + klass = codeModel()->create<ClassModel>(); + klass->setName("ClassName"); + klass->setFileName("FileName"); + klass->setStartPosition(line, column); + @endcode + @return Created code model item.*/ + template <class T> typename T::Ptr create() + { + typename T::Ptr ptr( new T(this) ); + return ptr; + } + + /**Resets the CodeModel.*/ + void wipeout(); + + /**Gets the list of files in the store. + @return The FileList object that contains the list of files.*/ + FileList fileList(); + + /**Gets the list of files in the store. + This is a const version for convenience. + @return The FileList object that contains the list of files.*/ + const FileList fileList() const; + + /**Checks to see if a file is in the store. + @return true if @p name is in the file list.*/ + bool hasFile( const QString& name ) const; + + /**Gets the FileDom object for a file. + @param name The name of the file to get the FileDom object for.*/ + FileDom fileByName( const QString& name ); + + /**Gets the FileDom object for a file. + This is a const version provided for convenience. + @param name the name of the file to get the FileDom object for.*/ + const FileDom fileByName( const QString& name ) const; + + /**Adds a file to the store. + @param file The FileDom object to add to the store. + @return true if the file was added successfully.*/ + bool addFile( FileDom file ); + + /**Removes a file from the store. + @param file the FileDom object to remove from the store.*/ + void removeFile( FileDom file ); + + /**Gets the global namespace + @return The NamespaceDom object that represents the global namespace.*/ + const NamespaceDom globalNamespace() const; + + /**Reads the model from a stream. + Use this to save the memory symbol store to a file. + + Language support plugins usually save symbols from projects before the project is + closed to avoid reparsing when the project is opened next time. + @param stream Stream to read from. + @return whether the read succeeded(may fail when the store-format is deprecated).*/ + virtual void read( QDataStream& stream ); + /**Writes the model to a stream. + Use this to restore the memory symbol store to a file. + + Language support plugins usually save symbols from projects before the project is + closed to avoid reparsing when the project is opened next time. + @param stream Stream to write to.*/ + virtual void write( QDataStream& stream ) const; + + /** this will dump the whole tree into dot-file-format so it can be inspected, not ready yet*/ + virtual void dump( std::ostream& file, QString Info="" ); + + /** Merges two groups, by changing the group-ids of the files. + Returns the id of the new group, or 0 on fail. + @param g1 first group + @param g2 second group */ + int mergeGroups( int g1, int g2 ); + + /** Returns all files within the given group + it should be preferred calling FileModel::wholeGroup and + FileModel::wholeGroupStrings because those return in constant + time if they are the only member of the group */ + FileList getGroup( int gid ) const; + + FileList getGroup( const FileDom& file) const; + + /** Same as above, but returns the names instead of the objects */ + virtual QStringList getGroupStrings( int gid ) const; + +private: + /**Adds a namespace to the store. + @param target The NamespaceDom object that the namespace will be added to. + @param source The NamespaceDom object that contains the namespace to remove.*/ + void addNamespace( NamespaceDom target, NamespaceDom source ); + + /**Removes a namespace from the store. + @param target The NamespaceDom object that the namespace will be removed from. + @param source The NamespaceDom object that contains the namespace to remove.*/ + void removeNamespace( NamespaceDom target, NamespaceDom source ); + +private: + QMap<QString, FileDom> m_files; + NamespaceDom m_globalNamespace; + + virtual int newGroupId(); + ///the groups were introduced to represent dependencies between different files. + ///Files can have slaves that are owned by other files within the same group. + ///While parsing, whole groups should always be parsed/reparsed together. + int m_currentGroupId; ///normally, each file has its own group. + +private: + CodeModel( const CodeModel& source ); + void operator = ( const CodeModel& source ); + friend class CodeModelItem; + friend class FileModel; +}; + + +/** +Item in code model (symbol store). +Item is a symbol in a store. Code model provides several predefined classes +for predefined item types (files, namespaces, classes, functions and function definitions, +variables, arguments, enums and enumerators, type aliases. + +Instances of this class should be created using @ref CodeModel::create method but usually +it is better to create instances of derived classes like ClassModel, NamespaceModel, FileModel, etc. +*/ +class CodeModelItem: public KShared +{ +public: + /**A definition of safe pointer to the code model item.*/ + typedef ItemDom Ptr; + + /**A type of a code model item.*/ + enum Kind + { + File, /**<File.*/ + Namespace, /**<Namespace.*/ + Class, /**<Class.*/ + Function, /**<Function or class method.*/ + Variable, /**<Variable.*/ + Argument, /**<Function or method parameter.*/ + FunctionDefinition, /**<Function definition.*/ + Enum, /**<Enum.*/ + Enumerator, /**<Enumerator - a member of an Enum (example: @code enum Type { A, B, C} @endcode + Type will be an Enum; A, B and C - Enumerators.*/ + TypeAlias, /**<Type alias (aka typedef in c++).*/ + + Custom = 1000 /**<Custom model items should have type greater than 1000*/ + }; + + /**An access to the code model item.*/ + enum Access + { + Public, /**<Public.*/ + Protected, /**<Protected.*/ + Private /**<Private.*/ + }; + void update( const CodeModelItem* i ); + bool canUpdate( const CodeModelItem* i ) const; + +protected: + /**Constructor. + @param kind The type, see also @ref CodeModelItem::Kind. + @param model Code model which stores this item.*/ + CodeModelItem( int kind, CodeModel* model ); + +public: + /**Destructor.*/ + virtual ~CodeModelItem(); + + /**@return The type (kind) of item.*/ + int kind() const { return m_kind; } + + /**Sets the type (kind) of item. + @param kind The type, see also @ref CodeModelItem::Kind.*/ + void setKind( int kind ) { m_kind = kind; } + + /**@return The name of the item.*/ + QString name() const; + + QString comment() const { + return m_comment; + } + + void setComment( QString comment ) { + m_comment = comment; + } + + /**Sets the name of the item. + @param name The name.*/ + void setName( const QString& name ); + + /**Gets the file of the item. + @return The FileDom object for the item.*/ + FileDom file(); + + /**Gets the file of the item + This is a const version provided for convenience. + @return The FileDom object for the item.*/ + const FileDom file() const; + + /**@return The filename of the item.*/ + QString fileName() const; + + /**Sets the filename of the item. + @param fileName The file name.*/ + void setFileName( const QString& fileName ); + + /**Gets the start position of the item. + @param line Will be set to the line number of the items start position. Pass 0 if line number is not necessary. + @param col Will be set to the column number of the items start position. Pass 0 if column number is not necessary.*/ + void getStartPosition( int* line, int* col ) const; + + /**Sets the start position of the item. + @param line Line number. + @param col Column number.*/ + void setStartPosition( int line, int col ); + + /**Get the end position of the item. + @param line Will be set to the line number of the items end position. Pass 0 if line number is not necessary. + @param col Will be set to the column number of the items end position. Pass 0 if column number is not necessary.*/ + void getEndPosition( int* line, int* col ) const; + + /**Set the end position of the item. + @param line Line number. + @param col Column number.*/ + void setEndPosition( int line, int col ); + + /**@return true if an item is a FileModel.*/ + virtual bool isFile() const { return false; } + /**@return true if an item is a NamespaceModel.*/ + virtual bool isNamespace() const { return false; } + /**@return true if an item is a ClassModel.*/ + virtual bool isClass() const { return false; } + /**@return true if an item is a FunctionModel.*/ + virtual bool isFunction() const { return false; } + /**@return true if an item is a FileDefinitionModel.*/ + virtual bool isFunctionDefinition() const { return false; } + /**@return true if an item is a VariableModel.*/ + virtual bool isVariable() const { return false; } + /**@return true if an item is an ArgumentModel.*/ + virtual bool isArgument() const { return false; } + /**@return true if an item is a EnumModel.*/ + virtual bool isEnum() const { return false; } + /**@return true if an item is a EnumeratorModel.*/ + virtual bool isEnumerator() const { return false; } + /**@return true if an item is a TypeAliasModel.*/ + virtual bool isTypeAlias() const { return false; } + /**@return true if an item is a custom item.*/ + virtual bool isCustom() const { return false; } + + virtual bool isTemplateable() const { return false; } + + /**Reads an item from the stream. + @param stream The stream to read from.*/ + virtual void read( QDataStream& stream ); + /**Writes an item to the stream. + @param stream The stream to write to.*/ + virtual void write( QDataStream& stream ) const; + + virtual void dump( std::ostream& file, bool recurse=false, QString Info="" ); + + /**@return The code model for this item.*/ + CodeModel* codeModel() { return m_model; } + + /**@note This is a const version provided for convenience. + @return The code model for this item*/ + const CodeModel* codeModel() const { return m_model; } + +private: + int m_kind; + CodeModel* m_model; + QString m_name; + QString m_fileName; + QString m_comment; ///not stored yet + int m_startLine, m_startColumn; + int m_endLine, m_endColumn; + +private: + CodeModelItem( const CodeModelItem& source ); + void operator = ( const CodeModelItem& source ); +}; + + + +class TemplateModelItem { + public: + typedef QPair< QString, QString > ParamPair; + typedef QValueVector< ParamPair > ParamMap; ///The first is the name, and the second the default-parameter, or "" if there is none. + + virtual const ParamMap& getTemplateParams() { + return m_params; + } + + virtual void addTemplateParam( QString name, QString def = "" ) { + m_params.push_back( ParamPair( name, def ) ); + } + + virtual void clearTemplateParams() { + m_params.clear(); + } + + bool hasSpecializationDeclaration() const { + return !m_specialization.isEmpty(); + } + + virtual QString getSpecializationDeclaration() const { + return m_specialization; + } + + void setSpecializationDeclaration( const QString& str ) { + m_specialization = str; + } + + ///returns -1 if the parameter does not exist + virtual int findTemplateParam( const QString& name ) const { + for( unsigned int a = 0; a< m_params.size(); a++) + if( m_params[a].first == name ) return a; + return -1; + } + + const ParamPair getParam( int index ) const { + return m_params[index]; + } + + virtual bool isTemplateable() const { return true; } + + void write( QDataStream & stream ) const { + stream << m_specialization; + stream << (int)m_params.size(); + for( ParamMap::const_iterator it = m_params.begin(); it != m_params.end(); ++it ) { + stream << (*it).first; + stream << (*it).second; + } + } + + void read( QDataStream & stream ) { + int count; + stream >> m_specialization; + stream >> count; + for( int a = 0; a < count; a++ ) { + ParamPair tmp; + stream >> tmp.first; + stream >> tmp.second; + m_params.push_back( tmp ); + } + } + + protected: + ParamMap m_params; + QString m_specialization; +}; + + + +/** +Class model. +Represents a class in the code model. + +Instances of this class should be created using @ref CodeModel::create method. +*/ +class ClassModel: public CodeModelItem, public TemplateModelItem +{ +protected: + /**Constructor. + @param model Code model which stores this item.*/ + ClassModel( CodeModel* model ); + +public: + /**A definition of safe pointer to the class model.*/ + typedef ClassDom Ptr; + + virtual bool isClass() const { return true; } + + /**@return The scope of the class. Scope is a string list composed from names of parent classes and namespaces.*/ + QStringList scope() const { return m_scope; } + /**Sets the scope of this class. + @param scope The scope - a list of parent classes and namespaces.*/ + void setScope( const QStringList& scope ) { m_scope = scope; } + + /**@return The list of base class names.*/ + QStringList baseClassList() const; + + /**Adds a base class to the list of base classes. + @param baseClass The base class name.*/ + bool addBaseClass( const QString& baseClass ); + + /**Removes a base class from the list of base classes. + @param baseClass The base class name.*/ + void removeBaseClass( const QString& baseClass ); + + /**@return The list of (sub)classes in this model.*/ + ClassList classList(); + + /**@note This is a const version provided for convenience. + @return The list of (sub)classes in this model.*/ + const ClassList classList() const; + + /**Checks if the class specified by @p name is in this model. + @param name The name of a class to look for. + @return true if the model has a class.*/ + bool hasClass( const QString& name ) const; + + /**@param name The name of a class. + @return A list of classes that match the name given by @p name.*/ + ClassList classByName( const QString& name ); + + /**@param name The name of a class. + @return A list of classes that match the name given by @p name. + @note This is a const version provided for convenience.*/ + const ClassList classByName( const QString& name ) const; + + /**Adds a class to the model. + @param klass The class model to add. + @return true if addition was successful.*/ + bool addClass( ClassDom klass ); + + /**Removes a class from the model. + @param klass The class model to remove.*/ + void removeClass( ClassDom klass ); + + /**@return A list of functions in the model.*/ + FunctionList functionList(); + + /**@return A list of functions in the model. + @note This is a const version provided for convenience.*/ + const FunctionList functionList() const; + + /**Check if the function specified by @p name is in the model. + @param name The name of a function to look for. + @return true if the model has a class.*/ + bool hasFunction( const QString& name ) const; + + /**@param name The name of a function to look for. + @return A list of functions that match the name given by @p name.*/ + FunctionList functionByName( const QString& name ); + + /**@param name The name of a function to look for. + @return A list of functions that match the name given by @p name. + @note This is a const version provided for convenience.*/ + const FunctionList functionByName( const QString& name ) const; + + /**Adds a function to the class model. + @param fun The function model to add. + @return true if addition was successful.*/ + bool addFunction( FunctionDom fun ); + + /**Removes a function from the class model. + @param fun The FunctionDom object to remove from the model.*/ + void removeFunction( FunctionDom fun ); + + /**@return The list of function definitions in the model.*/ + FunctionDefinitionList functionDefinitionList(); + + /**@return The list of function definitions + @note This is a const version provided for convenience.*/ + const FunctionDefinitionList functionDefinitionList() const; + + /**Checks if the function definition specified by \p name is in the model. + @param name The name of a function definition to look for. + @return true if the function definition was found.*/ + bool hasFunctionDefinition( const QString& name ) const; + + /**Gets the list of functions that match the name given by \p name. + If there are no matches, then the list returned is empty. + @param name The name of a function definition to look for. + @return The FunctionDefinitionList object containing the definitions that match.*/ + FunctionDefinitionList functionDefinitionByName( const QString& name ); + + /**Gets the list of functions that match the name given by \p name. + If there are no matches, then the list returned is empty. + @param name The name of a function definition to look for. + @return The FunctionDefinitionList object containing the definitions that match. + @note This is a const version provided for convenience.*/ + const FunctionDefinitionList functionDefinitionByName( const QString& name ) const; + + /**Adds a function definition to the model. + @param fun The function fefinition model to add to the model. + @return true if the addition was successful.*/ + bool addFunctionDefinition( FunctionDefinitionDom fun ); + + /**Removes a function definition from the model. + @param fun The function fefinition model to remove from the model.*/ + void removeFunctionDefinition( FunctionDefinitionDom fun ); + + /**@return The list of variables in the model.*/ + VariableList variableList(); + + /**@return The list of variables in the model. + @note This is a const version provided for convenience.*/ + const VariableList variableList() const; + + /**Checks if the variable specified by @p name is in the model. + @param name The name of a variable. + @return true if the variable was found.*/ + bool hasVariable( const QString& name ) const; + + /**Gets the variable specified by @p name. + If there are no matches, then the VariableDom object returned is empty. + @param name The name of a variable. + @return A VariableDom object that matches the name specified.*/ + VariableDom variableByName( const QString& name ); + + /**Gets the variable specified by @p name. + If there are no matches, then the VariableDom object returned is empty. + @param name The name of a variable. + @return A VariableDom object that matches the name specified. + @note This is a const version provided for convenience.*/ + const VariableDom variableByName( const QString& name ) const; + + /**Adds a variable to the model. + @param var The variable model to add to the model. + @return true if the addition was successful.*/ + bool addVariable( VariableDom var ); + + /**Removes a variable from the model. + @param var The variable model to remove from the model.*/ + void removeVariable( VariableDom var ); + + /**@return The type alias list for this model.*/ + TypeAliasList typeAliasList(); + + /**@return The type alias list for this model. + @note This is a const version provided for convenience.*/ + const TypeAliasList typeAliasList() const; + + /**Checks if the type alias specified by @p name is in the model. + @param name The name of a type alias. + @return true if the type alias was found.*/ + bool hasTypeAlias( const QString& name ) const; + + /**Gets the list of type aliases that match @p name. + If there are no matches, the TypeAliasList object is empty. + @param name The name of a type alias. + @return A TypeAliasList object that contains the matches.*/ + TypeAliasList typeAliasByName( const QString& name ); + + /**Gets the list of type aliases that match @p name. + If there are no matches, the TypeAliasList object is empty. + @param name The name of a type alias. + @return A TypeAliasList object that contains the matches. + @note This is a const version provided for convenience.*/ + const TypeAliasList typeAliasByName( const QString& name ) const; + + /**Adds a type alias to the model. + @param typeAlias The type alias model to add to the model. + @return true if the addition was successful.*/ + bool addTypeAlias( TypeAliasDom typeAlias ); + + /**Removes a type alias from the model. + @param typeAlias The TypeAliasDom object to remove from the model.*/ + void removeTypeAlias( TypeAliasDom typeAlias ); + + /**@return The list of enums in the model.*/ + EnumList enumList(); + + /**@return The list of enums in the model. + @note This is a const version provided for convenience.*/ + const EnumList enumList() const; + + /**Checks if the enum specified by @p name is in the model. + @param name The name of an enum. + @return true if the enum was found.*/ + bool hasEnum( const QString& name ) const; + + /**Gets the enum specified by @p name. + The EnumDom object returned will be empty if no match is found. + @param name The name of an enum. + @return The EnumDom object that contains the match.*/ + EnumDom enumByName( const QString& name ); + + /**Gets the enum specified by @p name. + The EnumDom object returned will be empty if no match is found. + @param name The name of an enum. + @return The EnumDom object that contains the match.*/ + const EnumDom enumByName( const QString& name ) const; + + /**Adds an enum to the model. + @param e The enum model to add to the model. + @return true if the addition was successful.*/ + bool addEnum( EnumDom e ); + + /**Removes an enum from the model. + @param e The enum model to remove from the model.*/ + void removeEnum( EnumDom e ); + + void update( const ClassModel* i ); + bool canUpdate( const ClassModel* i ) const; + + virtual void read( QDataStream& stream ); + virtual void write( QDataStream& stream ) const; + + virtual void dump( std::ostream& file, bool recurse=false, QString Info="" ); + +private: + QStringList m_scope; + QStringList m_baseClassList; + QMap<QString, ClassList> m_classes; + QMap<QString, FunctionList> m_functions; + QMap<QString, FunctionDefinitionList> m_functionDefinitions; + QMap<QString, VariableDom> m_variables; + QMap<QString, TypeAliasList> m_typeAliases; + QMap<QString, EnumDom> m_enumerators; + +private: + ClassModel( const ClassModel& source ); + void operator = ( const ClassModel& source ); + friend class CodeModel; +}; + +class NamespaceAliasModel { +public: + virtual void read( QDataStream& stream ); + virtual void write( QDataStream& stream ) const; + + QString name() const { + return m_name; + } + + void setName( const QString& name ) { + m_name = name; + } + + void setAliasName( const QString& theValue ) { + m_aliasName = theValue; + } + + QString aliasName() const { + return m_aliasName; + } + + void setFileName( const HashedString& theValue ) { + m_fileName = theValue; + } + + HashedString fileName() const { + return m_fileName; + } + + bool operator < ( const NamespaceAliasModel& rhs ) const { + if( m_name < rhs.m_name ) return true; + if( m_name == rhs.m_name ) { + if( m_aliasName < rhs.m_aliasName ) return true; + if( m_aliasName == rhs.m_aliasName && m_fileName < rhs.m_fileName ) return true; + } + return false; + } + + bool operator == ( const NamespaceAliasModel& rhs ) const { + return m_name == rhs.m_name && m_aliasName == rhs.m_aliasName && m_fileName == rhs.m_fileName; + } + +private: + QString m_name; + QString m_aliasName; + HashedString m_fileName; +}; + +class NamespaceImportModel { +public: + virtual void read( QDataStream& stream ); + virtual void write( QDataStream& stream ) const; + + QString name() const { + return m_name; + } + + HashedString fileName() const { + return m_fileName; + } + + void setName( const QString& name ) { + m_name = name; + } + + void setFileName( const HashedString& file ) { + m_fileName = file; + } + + bool operator < ( const NamespaceImportModel& rhs ) const { + if( m_name < rhs.m_name ) return true; + if( m_name == rhs.m_name ) + if( m_fileName < rhs.m_fileName ) return true; + + return false; + } + + bool operator == ( const NamespaceImportModel& rhs ) const { + return m_name == rhs.m_name && m_fileName == rhs.m_fileName; + } + +private: + QString m_name; + HashedString m_fileName; +}; + +/** +Namespace model. +Represents a namespace in the code model. +Namespace model can represent either usual c++ namespaces +and packages or modules from other languages. + +Instances of this class should be created using @ref CodeModel::create method. +*/ +class NamespaceModel: public ClassModel +{ +protected: + /**Constructor. + @param model Code model which stores this item.*/ + NamespaceModel( CodeModel* model ); + +public: + typedef std::set<NamespaceAliasModel> NamespaceAliasModelList; ///I'm using std-sets here, because Qt-3 has no appropriate replacement + typedef std::set<NamespaceImportModel> NamespaceImportModelList; + + /**A definition of safe pointer to the namespace model.*/ + typedef NamespaceDom Ptr; + + virtual bool isClass() const { return false; } + virtual bool isNamespace() const { return true; } + + /**@return The list of namespaces in this model.*/ + NamespaceList namespaceList(); + + /**@return The list of namespaces in this model. + @note This is a const version provided for convenience.*/ + const NamespaceList namespaceList() const; + + /**Checks if the namespace referenced by @p name is in the model. + @param name The name of a namespace. + @return true if the namespace was found.*/ + bool hasNamespace( const QString& name ) const; + + /**Gets the namespace specified by @p name. + If there are no matches, then the NamespaceDom object returned is empty. + @param name The name of a namespace. + @return The NamespaceDom object that contains the match.*/ + NamespaceDom namespaceByName( const QString& name ); + + /**Gets the namespace specified by @p name. + If there are no matches, then the NamespaceDom object returned is empty. + @param name The name of a namespace. + @return The NamespaceDom object that contains the match. + @note This is a const version provided for convenience.*/ + const NamespaceDom namespaceByName( const QString& name ) const; + + /**Adds a namespace to the model. + @param ns The namespace model to add to the model. + @return true if addition was successful.*/ + bool addNamespace( NamespaceDom ns ); + + /**Removes the namespace from the model. + @param ns The namespace model to remove from the model.*/ + void removeNamespace( NamespaceDom ns ); + + /**Updates this model so it has the same content as the other one. Only the line/column is updated. canUpdate(..) must be tested before. + * @param ns the namespace to match + */ + void update( const NamespaceModel* ns ); + bool canUpdate( const NamespaceModel* ns ) const; + + virtual void read( QDataStream& stream ); + virtual void write( QDataStream& stream ) const; + + virtual void dump( std::ostream& file, bool recurse=false, QString Info="" ); + + void addNamespaceImport( const NamespaceImportModel& import ); + void addNamespaceAlias( const NamespaceAliasModel& alias ); + void removeNamespaceImport( const NamespaceImportModel& import ); + void removeNamespaceAlias( const NamespaceAliasModel& alias ); + + ///Must not be called on temporary objects because a reference is returned(for performance-reasons) + const NamespaceAliasModelList& namespaceAliases() const { + return m_namespaceAliases; + } + + ///Must not be called on temporary objects because a reference is returned(for performance-reasons) + const NamespaceImportModelList& namespaceImports() const { + return m_namespaceImports; + } +private: + QMap<QString, NamespaceDom> m_namespaces; + NamespaceAliasModelList m_namespaceAliases; + NamespaceImportModelList m_namespaceImports; + +private: + + NamespaceModel( const NamespaceModel& source ); + void operator = ( const NamespaceModel& source ); + friend class CodeModel; +}; + + + + +/** +File model. +Represents a file in the code model. +Files in general contain classes, namespaces, functions, +types, etc. Therefore FileModel is derived from NamespaceModel. + +Instances of this class should be created using @ref CodeModel::create method. +*/ +class FileModel: public NamespaceModel +{ +protected: + /**Constructor. + @param model Code model which stores this item.*/ + FileModel( CodeModel* model ); + +public: + /**A definition of safe pointer to the file model.*/ + typedef FileDom Ptr; + + virtual bool isFile() const { return true; } + + virtual int groupId() const { + return m_groupId; + } + + virtual void setGroupId(int newId) { + m_groupId = newId; + } + + /** This function additionally does version-checking and + should be used instead of read when read should be called + from outside. + @return whether the read was successful */ + + virtual void write( QDataStream& stream ) const; + + FileList wholeGroup() ; + + QStringList wholeGroupStrings() const; + + virtual void read( QDataStream& stream ); + + ParseResultPointer parseResult() const; + void setParseResult( const ParseResultPointer& result ); + + void update( const FileModel* i ); +private: + int m_groupId; + ParseResultPointer m_parseResult; + FileModel( const FileModel& ); + void operator = ( const FileModel& ); + friend class CodeModel; +}; + + +/** +Function (procedure) argument model. +Represents an argument in the function. + +Instances of this class should be created using @ref CodeModel::create method. +*/ +class ArgumentModel: public CodeModelItem +{ +protected: + ArgumentModel( CodeModel* model ); + +public: + /**A definition of safe pointer to the argument model.*/ + typedef ArgumentDom Ptr; + + virtual bool isArgument() const { return true; } + + /**@return The type of this argument.*/ + QString type() const; + + /**Sets the type of this argument. + @param type The type to set.*/ + void setType( const QString& type ); + + /**@return The default value of this argument.*/ + QString defaultValue() const; + + /**Sets the default value of this argument. + @param defaultValue The default value to set.*/ + void setDefaultValue( const QString& defaultValue ); + + virtual void read( QDataStream& stream ); + virtual void write( QDataStream& stream ) const; + + virtual void dump( std::ostream& file, bool recurse=false, QString Info="" ); + +private: + QString m_type; + QString m_defaultValue; + +private: + ArgumentModel( const ArgumentModel& source ); + void operator = ( const ArgumentModel& source ); + friend class CodeModel; +}; + + +/** +Function model. +Represents: +- functions; +- procedures; +- class methods; +. +In languages that have separate function declarations and definitions (c++) +this represents only function declarations. @see FunctionDefinitionModel +for a model of function definitions. + +Instances of this class should be created using @ref CodeModel::create method. +*/ +class FunctionModel: public CodeModelItem, public TemplateModelItem +{ +protected: + /**Constructor. + @param model Code model which stores this item.*/ + FunctionModel( CodeModel* model ); + +public: + /**A definition of safe pointer to the function model.*/ + typedef FunctionDom Ptr; + + virtual bool isFunction() const { return true; } + + /**@return The scope of the function. Scope is a string list composed + from names of parent functions, classes and namespaces.*/ + QStringList scope() const { return m_scope; } + + /**Sets the scope of the function. + @param scope The scope to set.*/ + void setScope( const QStringList& scope ) { m_scope = scope; } + + /**@return The access level of the function. Can return either values of type @ref CodeModelItem::Access or + other integers if the function has other access level (for example pascal methods can have "published" + access level).*/ + int access() const; + + /**Sets the access level of the function. + @param access The access level.*/ + void setAccess( int access ); + + /**@return true if the function is a signal.*/ + bool isSignal() const; + /**Sets the function to be a signal. + @param isSignal The signal flag.*/ + void setSignal( bool isSignal ); + + /**@return true if the function is a slot.*/ + bool isSlot() const; + /**Sets the function to be a slot. + @param isSlot The slot flag.*/ + void setSlot( bool isSlot ); + + /**@return true if the function is a virtual function.*/ + bool isVirtual() const; + /**Sets the function to be a virtual function. + @param isVirtual The virtual flag.*/ + void setVirtual( bool isVirtual ); + + /**@return true if the function is a static function.*/ + bool isStatic() const; + /**Sets the function to be a static function. + @param isStatic The static flag.*/ + void setStatic( bool isStatic ); + + /**@return true if the function is an inline function.*/ + bool isInline() const; + /**Sets the function to be an inline function. + @param isInline The inline flag.*/ + void setInline( bool isInline ); + + /**@return true if the function is a constant function.*/ + bool isConstant() const; + /**Sets the function to be a constant function. + @param isConstant The constant flag.*/ + void setConstant( bool isConstant ); + + /**@return true if the function is an abstract function.*/ + bool isAbstract() const; + /**Sets the function to be an inline function. + @param isAbstract The abstract flag.*/ + void setAbstract( bool isAbstract ); + + /**@return The result type of a function.*/ + QString resultType() const; + /**Sets the result type of a function. + @param type The type of a function result.*/ + void setResultType( const QString& type ); + + /**Gets the list of arguments being passed to the function. + If there are no arguments, then the list is empty. + @return The ArgumentList object that contains the arguments for this function.*/ + ArgumentList argumentList(); + + /**Gets the list of arguments being passed to the function. + If there are no arguments, then the list is empty. + @return The ArgumentList object that contains the arguments for this function. + @note This is a const version provided for convenience.*/ + const ArgumentList argumentList() const; + + /**Adds an argument to the function. + @param arg The argument model to add as an argument to the function. + @return true if the addition was successful.*/ + bool addArgument( ArgumentDom arg ); + + /**Removes an argument from the function. + @param arg The argument model to remove from the function.*/ + void removeArgument( ArgumentDom arg ); + + virtual void read( QDataStream& stream ); + virtual void write( QDataStream& stream ) const; + + virtual void dump( std::ostream& file, bool recurse=false, QString Info="" ); + + void update( const FunctionModel* i ); + bool canUpdate( const FunctionModel* i ) const; + +private: + QStringList m_scope; + int m_access; + + union { + struct { + int m_signal : 1; + int m_slot : 1; + int m_virtual : 1; + int m_static : 1; + int m_inline : 1; + int m_constant : 1; + int m_abstract : 1; + } v; + int flags; + } d; + + QString m_resultType; + ArgumentList m_arguments; + +private: + FunctionModel( const FunctionModel& source ); + void operator = ( const FunctionModel& source ); + friend class CodeModel; +}; + +/** +Function model. +Represents function definition for languages that have such. + +Instances of this class should be created using @ref CodeModel::create method. +*/ +class FunctionDefinitionModel: public FunctionModel +{ +protected: + /**Constructor. + @param model Code model which stores this item.*/ + FunctionDefinitionModel( CodeModel* model ); + +public: + /**A definition of safe pointer to the function definition model.*/ + typedef FunctionDefinitionDom Ptr; + + virtual bool isFunctionDefinition() const { return true; } + +private: + FunctionDefinitionModel( const FunctionDefinitionModel& source ); + void operator = ( const FunctionDefinitionModel& source ); + friend class CodeModel; +}; + + +/** +Variable model. +Represents variables and class attributes. + +Instances of this class should be created using @ref CodeModel::create method. +*/ +class VariableModel: public CodeModelItem +{ +protected: + /**Constructor. + @param model Code model which stores this item.*/ + VariableModel( CodeModel* model ); + +public: + /**A definition of safe pointer to the variable model.*/ + typedef VariableDom Ptr; + + virtual bool isVariable() const { return true; } + + /**@return The access level of the variable. Can return either values of type @ref CodeModelItem::Access or + other integers if the variable has other access level (for example pascal attributes can have "published" + access level).*/ + int access() const; + /**Sets the access level of the variable. + @param access The access level.*/ + void setAccess( int access ); + + /**@return true if the variable is a static variable.*/ + bool isStatic() const; + /**Sets the variable to be a static variable. + @param isStatic The static flag.*/ + void setStatic( bool isStatic ); + + /**@return A type of the variable.*/ + QString type() const; + /**Sets the type of the variable. + @param type The type name.*/ + void setType( const QString& type ); + + /**@return If this is an enumerator, the enum it is part of, else an empty string. This is just a hack, necessary because EnumeratorModel is not used at all by the cpp-code-model. */ + bool isEnumeratorVariable() const; + + void setEnumeratorVariable( bool b ); + + virtual void read( QDataStream& stream ); + virtual void write( QDataStream& stream ) const; + + virtual void dump( std::ostream& file, bool recurse=false, QString Info="" ); + + void update( const VariableModel* i ); + bool canUpdate( const VariableModel* i ) const; + +private: + int m_access; + int m_static; + QString m_type; + int m_isEnumeratorVariable; + +private: + VariableModel( const VariableModel& source ); + void operator = ( const VariableModel& source ); + friend class CodeModel; +}; + + +/** +Enum model. +Represents enums. + +Instances of this class should be created using @ref CodeModel::create method. +*/ +class EnumModel: public CodeModelItem +{ +protected: + /**Constructor. + @param model Code model which stores this item.*/ + EnumModel( CodeModel* model ); + +public: + /**A definition of safe pointer to the enum model.*/ + typedef EnumDom Ptr; + + virtual bool isEnum() const { return true; } + + /**@return The access level of the enum. Can return either values + of type @ref CodeModelItem::Access or other integers if the enum has other access level.*/ + int access() const; + /**Sets the access level of the enum. + @param access The access level.*/ + void setAccess( int access ); + + /**@return The list of enumerators in this enum.*/ + EnumeratorList enumeratorList(); + /**@return The list of enumerators in this enum. + @note This is a const version provided for convenience.*/ + const EnumeratorList enumeratorList() const; + /**Adds an enumerator to the model. + @param e The enumerator model to add.*/ + void addEnumerator( EnumeratorDom e ); + /**Removes an enumerator from the model. + @param e The enumerator model to remove.*/ + void removeEnumerator( EnumeratorDom e ); + + virtual void read( QDataStream& stream ); + virtual void write( QDataStream& stream ) const; + + ///The dump-function is not ready yet + virtual void dump( std::ostream& file, bool recurse=false, QString Info="" ); + + void update( const EnumModel* i ); + bool canUpdate( const EnumModel* i ) const; + +private: + int m_access; + QMap<QString, EnumeratorDom> m_enumerators; + +private: + EnumModel( const EnumModel& source ); + void operator = ( const EnumModel& source ); + friend class CodeModel; +}; + + +/** +Enumerator model. +Represents enumerators. Enums consist of enumerators, for example in code: +@code +enum Type { A, B, C}; +@endcode +Type is represented as EnumModel;\n +A, B, C are represented with EnumeratorModel. + +Instances of this class should be created using @ref CodeModel::create method. +*/ +class EnumeratorModel: public CodeModelItem +{ +protected: + /**Constructor. + @param model Code model which stores this item.*/ + EnumeratorModel( CodeModel* model ); + +public: + /**A definition of safe pointer to the enumerator model.*/ + typedef EnumeratorDom Ptr; + + virtual bool isEnumerator() const { return true; } + + /**@return The value of an enumerator.*/ + QString value() const; + /**Sets the value of an enumerator. + @param value The value.*/ + void setValue( const QString& value ); + + virtual void read( QDataStream& stream ); + virtual void write( QDataStream& stream ) const; + + virtual void dump( std::ostream& file, bool recurse=false, QString Info="" ); + +private: + QString m_value; + +private: + EnumeratorModel( const EnumeratorModel& source ); + void operator = ( const EnumeratorModel& source ); + friend class CodeModel; +}; + + +/** +Type alias model. +Represents type aliases (like subtypes/derived types in Ada and typedefs in c++). +*/ +class TypeAliasModel: public CodeModelItem +{ +protected: + /**Constructor. + @param model Code model which stores this item.*/ + TypeAliasModel( CodeModel* model ); + +public: + /**A definition of safe pointer to the type alias model.*/ + typedef TypeAliasDom Ptr; + + virtual bool isTypeAlias() const { return true; } + + /**@return The actual type of an alias.*/ + QString type() const; + /**Sets the type of an alias. + @param type The type name.*/ + void setType( const QString& type ); + + virtual void read( QDataStream& stream ); + virtual void write( QDataStream& stream ) const; + + + virtual void dump( std::ostream& file, bool recurse=false, QString Info="" ); + + void update( const TypeAliasModel* i ); + bool canUpdate( const TypeAliasModel* i ) const; + +private: + QString m_type; + +private: + TypeAliasModel( const TypeAliasModel& source ); + void operator = ( const TypeAliasModel& source ); + friend class CodeModel; +}; + +#endif diff --git a/lib/interfaces/codemodel_treeparser.cpp b/lib/interfaces/codemodel_treeparser.cpp new file mode 100644 index 00000000..b514a90e --- /dev/null +++ b/lib/interfaces/codemodel_treeparser.cpp @@ -0,0 +1,108 @@ +/* This file is part of KDevelop + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "codemodel.h" +#include "codemodel_treeparser.h" + +CodeModelTreeParser::CodeModelTreeParser( ) +{ +} + +CodeModelTreeParser::~CodeModelTreeParser( ) +{ +} + +void CodeModelTreeParser::parseCode( const CodeModel * model ) +{ + const FileList fileList = model->fileList(); + for( FileList::ConstIterator it=fileList.begin(); it!=fileList.end(); ++it ) + parseFile( *it ); +} + +void CodeModelTreeParser::parseFile( const FileModel * file ) +{ + const NamespaceList namespaceList = file->namespaceList(); + const ClassList classList = file->classList(); + const FunctionList functionList = file->functionList(); + const FunctionDefinitionList functionDefinitionList = file->functionDefinitionList(); + const VariableList variableList = file->variableList(); + + for( NamespaceList::ConstIterator it=namespaceList.begin(); it!=namespaceList.end(); ++it ) + parseNamespace( *it ); + for( ClassList::ConstIterator it=classList.begin(); it!=classList.end(); ++it ) + parseClass( *it ); + for( FunctionList::ConstIterator it=functionList.begin(); it!=functionList.end(); ++it ) + parseFunction( *it ); + for( FunctionDefinitionList::ConstIterator it=functionDefinitionList.begin(); it!=functionDefinitionList.end(); ++it ) + parseFunctionDefinition( *it ); + for( VariableList::ConstIterator it=variableList.begin(); it!=variableList.end(); ++it ) + parseVariable( *it ); +} + +void CodeModelTreeParser::parseNamespace( const NamespaceModel * ns ) +{ + const NamespaceList namespaceList = ns->namespaceList(); + const ClassList classList = ns->classList(); + const FunctionList functionList = ns->functionList(); + const FunctionDefinitionList functionDefinitionList = ns->functionDefinitionList(); + const VariableList variableList = ns->variableList(); + + for( NamespaceList::ConstIterator it=namespaceList.begin(); it!=namespaceList.end(); ++it ) + parseNamespace( *it ); + for( ClassList::ConstIterator it=classList.begin(); it!=classList.end(); ++it ) + parseClass( *it ); + for( FunctionList::ConstIterator it=functionList.begin(); it!=functionList.end(); ++it ) + parseFunction( *it ); + for( FunctionDefinitionList::ConstIterator it=functionDefinitionList.begin(); it!=functionDefinitionList.end(); ++it ) + parseFunctionDefinition( *it ); + for( VariableList::ConstIterator it=variableList.begin(); it!=variableList.end(); ++it ) + parseVariable( *it ); +} + +void CodeModelTreeParser::parseClass( const ClassModel * klass ) +{ + const ClassList classList = klass->classList(); + const FunctionList functionList = klass->functionList(); + const FunctionDefinitionList functionDefinitionList = klass->functionDefinitionList(); + const VariableList variableList = klass->variableList(); + + for( ClassList::ConstIterator it=classList.begin(); it!=classList.end(); ++it ) + parseClass( *it ); + for( FunctionList::ConstIterator it=functionList.begin(); it!=functionList.end(); ++it ) + parseFunction( *it ); + for( FunctionDefinitionList::ConstIterator it=functionDefinitionList.begin(); it!=functionDefinitionList.end(); ++it ) + parseFunctionDefinition( *it ); + for( VariableList::ConstIterator it=variableList.begin(); it!=variableList.end(); ++it ) + parseVariable( *it ); +} + +void CodeModelTreeParser::parseFunction( const FunctionModel * /*fun*/ ) +{ +} + +void CodeModelTreeParser::parseFunctionDefinition( const FunctionDefinitionModel * /*fun*/ ) +{ +} + +void CodeModelTreeParser::parseVariable( const VariableModel * /*var*/ ) +{ +} + + + diff --git a/lib/interfaces/codemodel_treeparser.h b/lib/interfaces/codemodel_treeparser.h new file mode 100644 index 00000000..9271ae64 --- /dev/null +++ b/lib/interfaces/codemodel_treeparser.h @@ -0,0 +1,85 @@ +/* This file is part of KDevelop + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef CODEMODEL_TREEPARSER_H +#define CODEMODEL_TREEPARSER_H + +/** +@file codemodel_treeparser.h +Tree parser for a code model. +*/ + +class FileModel; +class NamespaceModel; +class ClassModel; +class FunctionModel; +class FunctionDefinitionModel; +class VariableModel; + +/** +Tree parser for a code model. +This is a base class which can be subclassed to create a code model "tree parser". +Such tree parsers can be used to recursively walk through the whole code model +or its parts and do some actions. + +Default implementation only walks through the model and does nothing. You need +to reimplement necessary virtual methods of this class to add desired functionality. +*/ +class CodeModelTreeParser +{ +public: + /**Constructor.*/ + CodeModelTreeParser(); + /**Destructor.*/ + virtual ~CodeModelTreeParser(); + + /**Parses recursively all code in the @p model. + @param model CodeModel to parse.*/ + virtual void parseCode(const CodeModel* model); + + /**Parses recursively all code in the @p file. + @param file FileModel to parse.*/ + virtual void parseFile(const FileModel* file); + + /**Parses recursively all code in the namespace @p ns. + @param ns NamespaceModel to parse.*/ + virtual void parseNamespace(const NamespaceModel* ns); + + /**Parses recursively all code in the class @p klass. + @param klass ClassModel to parse.*/ + virtual void parseClass(const ClassModel* klass); + + /**Parses function (function declaration) @p fun. + @param fun FunctionModel to parse.*/ + virtual void parseFunction(const FunctionModel* fun); + + /**Parses function definition @p fun. + @param fun FunctionDefinitionModel to parse.*/ + virtual void parseFunctionDefinition(const FunctionDefinitionModel* fun); + + /**Parses variable @p var. + @param var VariableModel to parse.*/ + virtual void parseVariable(const VariableModel* var); + +private: + CodeModelTreeParser( const CodeModelTreeParser& source ); + void operator = ( const CodeModelTreeParser& source ); +}; + +#endif diff --git a/lib/interfaces/codemodel_utils.cpp b/lib/interfaces/codemodel_utils.cpp new file mode 100644 index 00000000..68211f34 --- /dev/null +++ b/lib/interfaces/codemodel_utils.cpp @@ -0,0 +1,747 @@ +/* This file is part of KDevelop + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2003 Alexander Dymo <adymo@kdevelop.org> + Copyright (C) 2004 Jonas Jacobi <j.jacobi@gmx.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "codemodel_utils.h" + +namespace CodeModelUtils +{ + +namespace Functions +{ + +void processClasses(FunctionList &list, const ClassDom dom) +{ + const ClassList cllist = dom->classList(); + for (ClassList::ConstIterator it = cllist.begin(); it != cllist.end(); ++it) + { + processClasses(list, *it); + } + + const FunctionList fnlist = dom->functionList(); + for (FunctionList::ConstIterator it = fnlist.begin(); it != fnlist.end(); ++it) + { + list.append(*it); + } +} + +void processNamespaces(FunctionList &list, const NamespaceDom dom) +{ + const NamespaceList nslist = dom->namespaceList(); + for (NamespaceList::ConstIterator it = nslist.begin(); it != nslist.end(); ++it) + { + processNamespaces(list, *it); + } + const ClassList cllist = dom->classList(); + for (ClassList::ConstIterator it = cllist.begin(); it != cllist.end(); ++it) + { + processClasses(list, *it); + } + + const FunctionList fnlist = dom->functionList(); + for (FunctionList::ConstIterator it = fnlist.begin(); it != fnlist.end(); ++it) + { + list.append(*it); + } +} + +void processNamespaces( FunctionList & list, const NamespaceDom dom, QMap< FunctionDom, Scope > & relations ) +{ + const NamespaceList nslist = dom->namespaceList(); + for (NamespaceList::ConstIterator it = nslist.begin(); it != nslist.end(); ++it) + { + processNamespaces(list, *it, relations); + } + const ClassList cllist = dom->classList(); + for (ClassList::ConstIterator it = cllist.begin(); it != cllist.end(); ++it) + { + processClasses(list, *it, relations, dom); + } + + const FunctionList fnlist = dom->functionList(); + for (FunctionList::ConstIterator it = fnlist.begin(); it != fnlist.end(); ++it) + { + list.append(*it); + relations[*it].ns = dom; + } +} + +void processClasses( FunctionList & list, const ClassDom dom, QMap< FunctionDom, Scope > & relations ) +{ + const ClassList cllist = dom->classList(); + for (ClassList::ConstIterator it = cllist.begin(); it != cllist.end(); ++it) + { + processClasses(list, *it, relations); + } + + const FunctionList fnlist = dom->functionList(); + for (FunctionList::ConstIterator it = fnlist.begin(); it != fnlist.end(); ++it) + { + list.append(*it); + relations[*it].klass = dom; + } +} + +void processClasses( FunctionList & list, const ClassDom dom, QMap< FunctionDom, Scope > & relations, const NamespaceDom & nsdom ) +{ + const ClassList cllist = dom->classList(); + for (ClassList::ConstIterator it = cllist.begin(); it != cllist.end(); ++it) + { + processClasses(list, *it, relations, nsdom); + } + + const FunctionList fnlist = dom->functionList(); + for (FunctionList::ConstIterator it = fnlist.begin(); it != fnlist.end(); ++it) + { + list.append(*it); + relations[*it].klass = dom; + relations[*it].ns = nsdom; + } +} + +} // end of Functions namespace + + + +namespace FunctionDefinitions +{ + +void processClasses(FunctionDefinitionList &list, const ClassDom dom) +{ + const ClassList cllist = dom->classList(); + for (ClassList::ConstIterator it = cllist.begin(); it != cllist.end(); ++it) + { + processClasses(list, *it); + } + + const FunctionDefinitionList fnlist = dom->functionDefinitionList(); + for (FunctionDefinitionList::ConstIterator it = fnlist.begin(); it != fnlist.end(); ++it) + { + list.append(*it); + } +} + +void processNamespaces(FunctionDefinitionList &list, const NamespaceDom dom) +{ + const NamespaceList nslist = dom->namespaceList(); + for (NamespaceList::ConstIterator it = nslist.begin(); it != nslist.end(); ++it) + { + processNamespaces(list, *it); + } + const ClassList cllist = dom->classList(); + for (ClassList::ConstIterator it = cllist.begin(); it != cllist.end(); ++it) + { + processClasses(list, *it); + } + + const FunctionDefinitionList fnlist = dom->functionDefinitionList(); + for (FunctionDefinitionList::ConstIterator it = fnlist.begin(); it != fnlist.end(); ++it) + { + list.append(*it); + } +} + +void processNamespaces( FunctionDefinitionList & list, const NamespaceDom dom, QMap< FunctionDefinitionDom, Scope > & relations ) +{ + const NamespaceList nslist = dom->namespaceList(); + for (NamespaceList::ConstIterator it = nslist.begin(); it != nslist.end(); ++it) + { + processNamespaces(list, *it, relations); + } + const ClassList cllist = dom->classList(); + for (ClassList::ConstIterator it = cllist.begin(); it != cllist.end(); ++it) + { + processClasses(list, *it, relations, dom); + } + + const FunctionDefinitionList fnlist = dom->functionDefinitionList(); + for (FunctionDefinitionList::ConstIterator it = fnlist.begin(); it != fnlist.end(); ++it) + { + list.append(*it); + relations[*it].ns = dom; + } +} + +void processClasses( FunctionDefinitionList & list, const ClassDom dom, QMap< FunctionDefinitionDom, Scope > & relations ) +{ + const ClassList cllist = dom->classList(); + for (ClassList::ConstIterator it = cllist.begin(); it != cllist.end(); ++it) + { + processClasses(list, *it, relations); + } + + const FunctionDefinitionList fnlist = dom->functionDefinitionList(); + for (FunctionDefinitionList::ConstIterator it = fnlist.begin(); it != fnlist.end(); ++it) + { + list.append(*it); + relations[*it].klass = dom; + } +} + +void processClasses( FunctionDefinitionList & list, const ClassDom dom, QMap< FunctionDefinitionDom, Scope > & relations, const NamespaceDom & nsdom ) +{ + const ClassList cllist = dom->classList(); + for (ClassList::ConstIterator it = cllist.begin(); it != cllist.end(); ++it) + { + processClasses(list, *it, relations, nsdom); + } + + const FunctionDefinitionList fnlist = dom->functionDefinitionList(); + for (FunctionDefinitionList::ConstIterator it = fnlist.begin(); it != fnlist.end(); ++it) + { + list.append(*it); + relations[*it].klass = dom; + relations[*it].ns = nsdom; + } +} + +} // end of FunctionDefinitions namespace + + + +FunctionList allFunctions(const FileDom &dom) +{ + using namespace Functions; + FunctionList list; + + const NamespaceList nslist = dom->namespaceList(); + for (NamespaceList::ConstIterator it = nslist.begin(); it != nslist.end(); ++it) + { + processNamespaces(list, *it); + } + + const ClassList cllist = dom->classList(); + for (ClassList::ConstIterator it = cllist.begin(); it != cllist.end(); ++it) + { + processClasses(list, *it); + } + + const FunctionList fnlist = dom->functionList(); + for (FunctionList::ConstIterator it = fnlist.begin(); it != fnlist.end(); ++it) + { + list.append(*it); + } + + return list; +} + +AllFunctions allFunctionsDetailed( const FileDom & dom ) +{ + using namespace Functions; + AllFunctions list; + + const NamespaceList nslist = dom->namespaceList(); + for (NamespaceList::ConstIterator it = nslist.begin(); it != nslist.end(); ++it) + { + processNamespaces(list.functionList, *it, list.relations); + } + + const ClassList cllist = dom->classList(); + for (ClassList::ConstIterator it = cllist.begin(); it != cllist.end(); ++it) + { + processClasses(list.functionList, *it, list.relations); + } + + const FunctionList fnlist = dom->functionList(); + for (FunctionList::ConstIterator it = fnlist.begin(); it != fnlist.end(); ++it) + { + list.functionList.append(*it); + } + + return list; +} + +AllFunctionDefinitions allFunctionDefinitionsDetailed( const FileDom & dom ) +{ + using namespace FunctionDefinitions; + AllFunctionDefinitions list; + + const NamespaceList nslist = dom->namespaceList(); + for (NamespaceList::ConstIterator it = nslist.begin(); it != nslist.end(); ++it) + { + processNamespaces(list.functionList, *it, list.relations); + } + + const ClassList cllist = dom->classList(); + for (ClassList::ConstIterator it = cllist.begin(); it != cllist.end(); ++it) + { + processClasses(list.functionList, *it, list.relations); + } + + const FunctionDefinitionList fnlist = dom->functionDefinitionList(); + for (FunctionDefinitionList::ConstIterator it = fnlist.begin(); it != fnlist.end(); ++it) + { + list.functionList.append(*it); + } + + return list; +} + + +bool resultTypesFit( const FunctionDom & dec, const FunctionDefinitionDom & def ) { + if( !def->resultType().contains("::") ) return dec->resultType() == def->resultType(); + QStringList l1 = dec->scope() + QStringList::split("::", dec->resultType() ); + QStringList l2 = QStringList::split("::", def->resultType() ); + + if( l1.isEmpty() || l2.isEmpty() || l1.back() != l2.back() ) return false; + + while( !l1.isEmpty() && !l2.isEmpty() ) { + if( l1.back() == l2.back() ) { + l1.pop_back(); + l2.pop_back(); + } else { + l1.pop_back(); + } + } + + if( l2.isEmpty() ) return true; + + return false; +} + + +bool compareDeclarationToDefinition( const FunctionDom & dec, const FunctionDefinitionDom & def ) +{ + if (dec->scope() == def->scope() && dec->name() == def->name() && resultTypesFit( dec, def ) && dec->isConstant() == def->isConstant()) + { + const ArgumentList defList = def->argumentList(), decList = dec->argumentList(); + if (defList.size() != decList.size()) + return false; + + size_t n = defList.size(); + for(size_t i = 0; i < n; ++i) + if (defList[i]->type() != decList[i]->type()) + return false; + + return true; + } + return false; +} + + +bool compareDeclarationToDefinition( const FunctionDom & dec, const FunctionDefinitionDom & def, const std::set<NamespaceImportModel> & nsImports ) +{ + if (dec->name() == def->name() && resultTypesFit( dec, def ) && dec->isConstant() == def->isConstant()) + { + const ArgumentList defList = def->argumentList(), decList = dec->argumentList(); + if (defList.size() != decList.size()) + return false; + + size_t n = defList.size(); + for(size_t i = 0; i < n; ++i) + if (defList[i]->type() != decList[i]->type()) + return false; + + const QStringList &defScope = def->scope(), &decScope = dec->scope(); + if (defScope != decScope) + { + if (defScope.size() >= decScope.size()) + return false; + + n = decScope.size(); + for(size_t i1 = 0, i2 = 0; i1 < n; ++i1) + { + if (i2 >= defScope.size() || decScope[i1] != defScope[i2]) + { + NamespaceImportModel model; + model.setName(decScope[i1]); + model.setFileName(def->fileName()); + if (nsImports.find(model) == nsImports.end()) + return false; + } + else + { + ++i2; + } + } + } + + return true; + } + return false; +} + + + +FunctionList allFunctionsExhaustive(FileDom &dom) { + PredAmOwner<FunctionDom> ow( dom ); + FunctionList ret; + + findFunctionDeclarations( ow, dom->wholeGroup(), ret ); + + return ret; +} + + +FunctionDefinitionList allFunctionDefinitionsExhaustive(FileDom &dom) { + PredAmOwner<FunctionDefinitionDom> ow( dom ); + FunctionDefinitionList ret; + + findFunctionDefinitions( ow, dom->wholeGroup(), ret ); + + return ret; +} + + + +ClassDom findClassByPosition( NamespaceModel* nameSpace, int line, int col ) +{ + if (nameSpace == 0) + return 0; + + NamespaceList nsList = nameSpace->namespaceList(); + for (NamespaceList::iterator i = nsList.begin(); i != nsList.end(); ++i) + { + ClassDom result = findClassByPosition(*i, line, col); + if (result != 0) + return result; + } + + ClassList classes = nameSpace->classList(); + for(ClassList::iterator i = classes.begin(); i != classes.end(); ++i) + { + ClassDom result = findClassByPosition( *i, line, col ); + if (result != 0) + return result; + } + + return 0; +} + +ClassDom findClassByPosition( ClassModel* aClass, int line, int col ) +{ + if (aClass == 0) + return 0; + + ClassList classes = aClass->classList(); + for(ClassList::iterator i = classes.begin(); i != classes.end(); ++i) + { + ClassDom result = findClassByPosition( *i, line, col ); + if (result != 0) + return result; + } + + int startLine, startCol; + aClass->getStartPosition(&startLine, &startCol); + + if (startLine <= line) + { + int endLine, endCol; + aClass->getEndPosition(&endLine, &endCol); + if (endLine >= line) + return (aClass); + } + + return 0; +} + +int findLastMethodLine( ClassDom aClass, CodeModelItem::Access access ) +{ + int point = -1; + + const FunctionList functionList = aClass->functionList(); + for( FunctionList::ConstIterator it=functionList.begin(); it!=functionList.end(); ++it ) + { + int funEndLine, funEndColumn; + (*it)->getEndPosition( &funEndLine, &funEndColumn ); + + if ((*it)->access() == access && point < funEndLine) + point = funEndLine; + } + + return point; +} + +int findLastVariableLine( ClassDom aClass, CodeModelItem::Access access ) +{ + int point = -1; + + const VariableList varList = aClass->variableList(); + for( VariableList::ConstIterator it= varList.begin(); it!= varList.end(); ++it ) + { + int varEndLine, varEndColumn; + (*it)->getEndPosition( &varEndLine, &varEndColumn ); + + if ((*it)->access() == access && point < varEndLine) + point = varEndLine; + } + + return point; +} + +QString accessSpecifierToString( CodeModelItem::Access access ) +{ + switch(access) + { + case CodeModelItem::Public: return "public"; + case CodeModelItem::Protected: return "protected"; + case CodeModelItem::Private: return "private"; + default: return "unknown"; + } +} + +FunctionDefinitionDom CodeModelHelper::functionDefinitionAt(NamespaceDom ns, int line, int column) +{ + NamespaceList namespaceList = ns->namespaceList(); + NamespaceList::iterator nslEnd = namespaceList.end(); + + for (NamespaceList::iterator it=namespaceList.begin(); it!=nslEnd; ++it) + { + if (FunctionDefinitionDom def = functionDefinitionAt(*it, line, column)) + return def; + } + + ClassList classList = ns->classList(); + ClassList::iterator clEnd = classList.end(); + + for (ClassList::iterator it=classList.begin(); it!=clEnd; ++it) + { + if (FunctionDefinitionDom def = functionDefinitionAt(*it, line, column)) + return def; + } + + FunctionDefinitionList functionDefinitionList = ns->functionDefinitionList(); + FunctionDefinitionList::iterator fdlEnd = functionDefinitionList.end(); + + for (FunctionDefinitionList::iterator it=functionDefinitionList.begin(); + it!=fdlEnd; ++it ) + { + if (FunctionDefinitionDom def = functionDefinitionAt(*it, line, column)) + return def; + } + + return FunctionDefinitionDom(); +} + +FunctionDefinitionDom CodeModelHelper::functionDefinitionAt(ClassDom klass, int line, int column) +{ + ClassList classList = klass->classList(); + ClassList::iterator clEnd = classList.end(); + + for (ClassList::iterator it=classList.begin(); it!=clEnd; ++it) + { + if (FunctionDefinitionDom def = functionDefinitionAt(*it, line, column)) + return def; + } + + FunctionDefinitionList functionDefinitionList = klass->functionDefinitionList(); + FunctionDefinitionList::iterator fdlEnd = functionDefinitionList.end(); + for (FunctionDefinitionList::Iterator it=functionDefinitionList.begin(); + it!=fdlEnd; ++it) + { + if (FunctionDefinitionDom def = functionDefinitionAt(*it, line, column)) + return def; + } + + return FunctionDefinitionDom(); +} + +FunctionDefinitionDom CodeModelHelper::functionDefinitionAt(FunctionDefinitionDom fun, int line, int // column + ) +{ + int startLine, startColumn; + int endLine, endColumn; + + fun->getStartPosition(&startLine, &startColumn); + fun->getEndPosition(&endLine, &endColumn); + + if (!(line >= startLine && line <= endLine) || fun->fileName() != m_fileName ) + return FunctionDefinitionDom(); + + /* + if (line == startLine && column < startColumn) + return FunctionDefinitionDom(); + + if (line == endLine && column > endColumn) + return FunctionDefinitionDom();*/ + + return fun; +} + + + +FunctionDom CodeModelHelper::functionDeclarationAt(NamespaceDom ns, int line, int column) +{ + NamespaceList namespaceList = ns->namespaceList(); + NamespaceList::iterator nsEnd = namespaceList.end(); + for (NamespaceList::iterator it=namespaceList.begin(); it!=nsEnd; ++it) + { + if (FunctionDom def = functionDeclarationAt(*it, line, column)) + return def; + } + + ClassList classList = ns->classList(); + ClassList::iterator clEnd = classList.end(); + for (ClassList::iterator it=classList.begin(); it!=clEnd; ++it) + { + if (FunctionDom def = functionDeclarationAt(*it, line, column)) + return def; + } + + FunctionList functionList = ns->functionList(); + FunctionList::iterator flEnd = functionList.end(); + for (FunctionList::iterator it=functionList.begin(); + it!=flEnd; ++it ) + { + if (FunctionDom def = functionDeclarationAt(*it, line, column)) + return def; + } + + return FunctionDom(); +} + +FunctionDom CodeModelHelper::functionDeclarationAt(ClassDom klass, int line, int column) +{ + ClassList classList = klass->classList(); + ClassList::iterator clEnd = classList.end(); + for (ClassList::iterator it=classList.begin(); it!=clEnd; ++it) + { + if (FunctionDom def = functionDeclarationAt(*it, line, column)) + return def; + } + + FunctionList functionList = klass->functionList(); + FunctionList::iterator flEnd = functionList.end(); + for (FunctionList::Iterator it=functionList.begin(); + it!=flEnd; ++it) + { + if (FunctionDom def = functionDeclarationAt(*it, line, column)) + return def; + } + + return FunctionDom(); +} + +FunctionDom CodeModelHelper::functionDeclarationAt(FunctionDom fun, int line, int // column + ) +{ + int startLine, startColumn; + int endLine, endColumn; + + fun->getStartPosition(&startLine, &startColumn); + fun->getEndPosition(&endLine, &endColumn); + + if (!(line >= startLine && line <= endLine) || fun->fileName() != m_fileName ) + return FunctionDom(); + + + /* if (line == startLine && column < startColumn) + return FunctionDom(); + + if (line == endLine && column > endColumn) + return FunctionDom();*/ + + return fun; +} + + + + +ClassDom CodeModelHelper::classAt(NamespaceDom ns, int line, int column) +{ + NamespaceList namespaceList = ns->namespaceList(); + NamespaceList::iterator nsEnd = namespaceList.end(); + + for (NamespaceList::iterator it=namespaceList.begin(); it!=nsEnd; ++it) + { + if (ClassDom def = classAt(*it, line, column)) + return def; + } + + ClassList classList = ns->classList(); + ClassList::iterator clEnd = classList.end(); + for (ClassList::iterator it=classList.begin(); it!=clEnd; ++it) + { + if (ClassDom def = classAt(*it, line, column)) + return def; + } + + return ClassDom(); +} + +ClassDom CodeModelHelper::classAt(ClassDom klass, int line, int column) +{ + ClassList classList = klass->classList(); + ClassList::iterator clEnd = classList.end(); + for (ClassList::iterator it=classList.begin(); it!=clEnd; ++it) + { + if (ClassDom def = classAt(*it, line, column)) + return def; + } + + int startLine, startColumn; + int endLine, endColumn; + + klass->getStartPosition(&startLine, &startColumn); + klass->getEndPosition(&endLine, &endColumn); + + if (!(line >= startLine && line <= endLine) || klass->fileName() != m_fileName ) + return ClassDom(); + + return klass; +} + + +CodeModelHelper::CodeModelHelper( CodeModel* model, FileDom file ) : m_model( model ) { + if( !file ) return; + m_files = file->wholeGroup(); + m_fileName = file->name(); +} + + +FunctionDom CodeModelHelper::functionAt( int line, int column, FunctionTypes types ) { + if( m_files.isEmpty() ) return FunctionDom(); + + + FunctionDom ret; + FileList::iterator it = m_files.begin(); + while( it != m_files.end() ) { + if( types & Declaration ) { + ret = functionDeclarationAt(model_cast<NamespaceDom>(*it), line, column); + if(ret) return ret; + } + if( types & Definition ) { + FunctionDefinitionDom r = functionDefinitionAt(model_cast<NamespaceDom>(*it), line, column); + if(r) { + ret = model_cast<FunctionDom>( r ); + return ret; + } + } + ++it; + } + + return ret; +} + +ClassDom CodeModelHelper::classAt( int line, int column ) { + if( m_files.isEmpty() ) return ClassDom(); + + ClassDom ret; + FileList::iterator it = m_files.begin(); + while( it != m_files.end() ) { + ret = classAt( model_cast<NamespaceDom>(*it), line, column ); + if(ret) return ret; + ++it; + } + + return ret; +} + +}//end of namespace CodeModeUtils diff --git a/lib/interfaces/codemodel_utils.h b/lib/interfaces/codemodel_utils.h new file mode 100644 index 00000000..dc970366 --- /dev/null +++ b/lib/interfaces/codemodel_utils.h @@ -0,0 +1,570 @@ +/* This file is part of KDevelop + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2003-2004 Alexander Dymo <adymo@kdevelop.org> + Copyright (C) 2004 Jonas Jacobi<j.jacobi@gmx.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef CODEMODEL_UTILS_H +#define CODEMODEL_UTILS_H + +#include "codemodel.h" + +/** +@file codemodel_utils.h +Utility functions and classes for the CodeModel. +*/ + +/** +@class Pred +The predicate. +Pred is not a real class, it is only a template parameter used in @ref CodeModelUtils functions. + +<b>How to create the predicate:</b>@n +Predicate is simply a class that have +@code bool operator() (predicateArgument) @endcode. +The return value of that operator is the result of a predicate. + +For example we want to find all function definitions with a particular name. +We can use @ref CodeModelUtils::findFunctionDefinitions functions that require +you to write a predicate for function definition DOM's. +This can be done with following code: +@code +class MyPred{ +public: + MyPred(const QString &name): m_name(name) {} + + bool operator() (const FunctionDefinitionDom &def) const + { + return def->name() == m_name; + } + +private: + QString m_name; +}; +@endcode +*/ + + +/**Namespace which contains utility functions and classes for the CodeModel.*/ +namespace CodeModelUtils +{ + +/**Finds function definitions which match given predicate in files. + +Predicate can be considered as a condition. If it is true then the function definition is +added to the result list otherwise it is skipped. +@see Pred class documentation for a detailed description on how to create and use predicates. + +@param pred Predicate which is applied to a function definition before it is returned. +@param fileList The list of files to find function definitions in. +@param lst The reference to a list of function definitions. Will be filled by this function.*/ +template <class Pred> void findFunctionDefinitions( Pred pred, const FileList& fileList, FunctionDefinitionList & lst ); + +/**Finds function definitions which match given predicate in the namespace. + +Predicate can be considered as a condition. If it is true then the function definition is +added to the result list otherwise it is skipped. +@see Pred class documentation for a detailed description on how to create and use predicates. + +@param pred Predicate which is applied to a function definition before it is returned. +@param ns The namespace to find function definitions in. +@param lst The reference to a list of function definitions. Will be filled by this function.*/ +template <class Pred> void findFunctionDefinitions( Pred pred, const NamespaceDom& ns, FunctionDefinitionList & lst ); + +/**Finds function definitions which match given predicate in namespaces. + +Predicate can be considered as a condition. If it is true then the function definition is +added to the result list otherwise it is skipped. +@see Pred class documentation for a detailed description on how to create and use predicates. + +@param pred Predicate which is applied to a function definition before it is returned. +@param namespaceList The list of namespaces to find function definitions in. +@param lst The reference to a list of function definitions. Will be filled by this function.*/ +template <class Pred> void findFunctionDefinitions( Pred pred, const NamespaceList& namespaceList, FunctionDefinitionList & lst ); + +/**Finds function definitions which match given predicate in classes. + +Predicate can be considered as a condition. If it is true then the function definition is +added to the result list otherwise it is skipped. +@see Pred class documentation for a detailed description on how to create and use predicates. + +@param pred Predicate which is applied to a function definition before it is returned. +@param classList The list of classes to find function definitions in. +@param lst The reference to a list of function definitions. Will be filled by this function.*/ +template <class Pred> void findFunctionDefinitions( Pred pred, const ClassList& classList, FunctionDefinitionList & lst ); + +/**Finds function definitions which match given predicate in the list of function definitions. + +Predicate can be considered as a condition. If it is true then the function definition is +added to the result list otherwise it is skipped. +@see Pred class documentation for a detailed description on how to create and use predicates. + +@param pred Predicate which is applied to a function definition before it is returned. +@param functionDefinitionList The list of function definitions to find function definitions in. +@param lst The reference to a list of function definitions. Will be filled by this function.*/ +template <class Pred> void findFunctionDefinitions( Pred pred, const FunctionDefinitionList& functionDefinitionList, FunctionDefinitionList & lst ); + +/**Finds function definitions which match given predicate in the class. + +Predicate can be considered as a condition. If it is true then the function definition is +added to the result list otherwise it is skipped. +@see Pred class documentation for a detailed description on how to create and use predicates. + +@param pred Predicate which is applied to a function definition before it is returned. +@param klass The class to find function definitions in. +@param lst The reference to a list of function definitions. Will be filled by this function.*/ +template <class Pred> void findFunctionDefinitions( Pred pred, const ClassDom& klass, FunctionDefinitionList & lst ); + +/**Applies a predicate to a function definition. + +Predicate can be considered as a condition. If it is true then the function definition is +added to the result list otherwise it is skipped. +@see Pred class documentation for a detailed description on how to create and use predicates. + +@param pred Predicate which is applied to a function definition before it is returned. +@param fun The function definition. +@param lst The reference to a list of function definitions. Will be filled by this function.*/ +template <class Pred> void findFunctionDefinitions( Pred pred, const FunctionDefinitionDom& fun, FunctionDefinitionList & lst ); + +/**Finds function declarations which match given predicate in files. + +Predicate can be considered as a condition. If it is true then the function declaration is +added to the result list otherwise it is skipped. +@see Pred class documentation for a detailed description on how to create and use predicates. + +@param pred Predicate which is applied to a function declaration before it is returned. +@param fileList The list of files to find function declarations in. +@param lst The reference to a list of function declarations. Will be filled by this function.*/ +template <class Pred> void findFunctionDeclarations( Pred pred, const FileList& fileList, FunctionList & lst ); + +/**Finds function declarations which match given predicate in the namespace. + +Predicate can be considered as a condition. If it is true then the function declaration is +added to the result list otherwise it is skipped. +@see Pred class documentation for a detailed description on how to create and use predicates. + +@param pred Predicate which is applied to a function declaration before it is returned. +@param ns The namespace to find function declarations in. +@param lst The reference to a list of function declarations. Will be filled by this function.*/ +template <class Pred> void findFunctionDeclarations( Pred pred, const NamespaceDom& ns, FunctionList & lst ); + +/**Finds function declarations which match given predicate in namespaces. + +Predicate can be considered as a condition. If it is true then the function declaration is +added to the result list otherwise it is skipped. +@see Pred class documentation for a detailed description on how to create and use predicates. + +@param pred Predicate which is applied to a function declaration before it is returned. +@param namespaceList The list of namespaces to find function declarations in. +@param lst The reference to a list of function declarations. Will be filled by this function.*/ +template <class Pred> void findFunctionDeclarations( Pred pred, const NamespaceList& namespaceList, FunctionList & lst ); + +/**Finds function declarations which match given predicate in classes. + +Predicate can be considered as a condition. If it is true then the function declaration is +added to the result list otherwise it is skipped. +@see Pred class documentation for a detailed description on how to create and use predicates. + +@param pred Predicate which is applied to a function declaration before it is returned. +@param classList The list of classes to find function declarations in. +@param lst The reference to a list of function declarations. Will be filled by this function.*/ +template <class Pred> void findFunctionDeclarations( Pred pred, const ClassList& classList, FunctionList & lst ); + +/**Finds function declarations which match given predicate in the list of function declarations. + +Predicate can be considered as a condition. If it is true then the function declaration is +added to the result list otherwise it is skipped. +@see Pred class documentation for a detailed description on how to create and use predicates. + +@param pred Predicate which is applied to a function declaration before it is returned. +@param functionList The list of function declarations to find function declarations in. +@param lst The reference to a list of function declarations. Will be filled by this function.*/ +template <class Pred> void findFunctionDeclarations( Pred pred, const FunctionList& functionList, FunctionList & lst ); + +/**Finds function declarations which match given predicate in the class. + +Predicate can be considered as a condition. If it is true then the function declaration is +added to the result list otherwise it is skipped. +@see Pred class documentation for a detailed description on how to create and use predicates. + +@param pred Predicate which is applied to a function declaration before it is returned. +@param klass The class to find function declarations in. +@param lst The reference to a list of function declarations. Will be filled by this function.*/ +template <class Pred> void findFunctionDeclarations( Pred pred, const ClassDom& klass, FunctionList & lst ); + +/**Applies a predicate to a function declaration. + +Predicate can be considered as a condition. If it is true then the function declaration is +added to the result list otherwise it is skipped. +@see Pred class documentation for a detailed description on how to create and use predicates. + +@param pred Predicate which is applied to a function declaration before it is returned. +@param fun The function declaration. +@param lst The reference to a list of function declarations. Will be filled by this function.*/ +template <class Pred> void findFunctionDeclarations( Pred pred, const FunctionDom& fun, FunctionList & lst ); + + +//implementations of function templates defined above: + +template <class Pred> +void findFunctionDefinitions( Pred pred, const FileList& fileList, FunctionDefinitionList & lst ) +{ + for( FileList::ConstIterator it=fileList.begin(); it!=fileList.end(); ++it ) + findFunctionDefinitions( pred, model_cast<NamespaceDom>(*it), lst ); +} + +template <class Pred> +void findFunctionDefinitions( Pred pred, const NamespaceDom& ns, FunctionDefinitionList & lst ) +{ + findFunctionDefinitions( pred, ns->namespaceList(), lst ); + findFunctionDefinitions( pred, ns->classList(), lst ); + findFunctionDefinitions( pred, ns->functionDefinitionList(), lst ); +} + +template <class Pred> +void findFunctionDefinitions( Pred pred, const NamespaceList& namespaceList, FunctionDefinitionList & lst ) +{ + for( NamespaceList::ConstIterator it=namespaceList.begin(); it!=namespaceList.end(); ++it ) + findFunctionDefinitions( pred, *it, lst ); +} + +template <class Pred> +void findFunctionDefinitions( Pred pred, const ClassList& classList, FunctionDefinitionList & lst ) +{ + for( ClassList::ConstIterator it=classList.begin(); it!=classList.end(); ++it ) + findFunctionDefinitions( pred, *it, lst ); +} + +template <class Pred> +void findFunctionDefinitions( Pred pred, const FunctionDefinitionList& functionDefinitionList, FunctionDefinitionList & lst ) +{ + for( FunctionDefinitionList::ConstIterator it=functionDefinitionList.begin(); it!=functionDefinitionList.end(); ++it ) + findFunctionDefinitions( pred, *it, lst ); +} + +template <class Pred> +void findFunctionDefinitions( Pred pred, const ClassDom& klass, FunctionDefinitionList & lst ) +{ + findFunctionDefinitions( pred, klass->classList(), lst ); + findFunctionDefinitions( pred, klass->functionDefinitionList(), lst ); +} + +template <class Pred> +void findFunctionDefinitions( Pred pred, const FunctionDefinitionDom& fun, FunctionDefinitionList & lst ) +{ + if( pred(fun) ) + lst << fun; +} + + + +template <class Pred> +void findFunctionDeclarations( Pred pred, const FileList& fileList, FunctionList & lst ) +{ + for( FileList::ConstIterator it=fileList.begin(); it!=fileList.end(); ++it ) + findFunctionDeclarations( pred, model_cast<NamespaceDom>(*it), lst ); +} + +template <class Pred> +void findFunctionDeclarations( Pred pred, const NamespaceDom& ns, FunctionList & lst ) +{ + findFunctionDeclarations( pred, ns->namespaceList(), lst ); + findFunctionDeclarations( pred, ns->classList(), lst ); + findFunctionDeclarations( pred, ns->functionList(), lst ); +} + +template <class Pred> +void findFunctionDeclarations( Pred pred, const NamespaceList& namespaceList, FunctionList & lst ) +{ + for( NamespaceList::ConstIterator it=namespaceList.begin(); it!=namespaceList.end(); ++it ) + findFunctionDeclarations( pred, *it, lst ); +} + +template <class Pred> +void findFunctionDeclarations( Pred pred, const ClassList& classList, FunctionList & lst ) +{ + for( ClassList::ConstIterator it=classList.begin(); it!=classList.end(); ++it ) + findFunctionDeclarations( pred, *it, lst ); +} + +template <class Pred> +void findFunctionDeclarations( Pred pred, const FunctionList& functionList, FunctionList & lst ) +{ + for( FunctionList::ConstIterator it=functionList.begin(); it!=functionList.end(); ++it ) + findFunctionDeclarations( pred, *it, lst ); +} + +template <class Pred> +void findFunctionDeclarations( Pred pred, const ClassDom& klass, FunctionList & lst ) +{ + findFunctionDeclarations( pred, klass->classList(), lst ); + findFunctionDeclarations( pred, klass->functionList(), lst ); +} + +template <class Pred> +void findFunctionDeclarations( Pred pred, const FunctionDom& fun, FunctionList & lst ) +{ + if( pred(fun) ) + lst << fun; +} + +/**A scope.*/ +struct Scope{ + /**Class.*/ + ClassDom klass; + /**Namespace.*/ + NamespaceDom ns; +}; + +/**Information about functions.*/ +struct AllFunctions{ + /**Scope of functions.*/ + QMap<FunctionDom, Scope> relations; + /**List of functions.*/ + FunctionList functionList; +}; +/**Information about function definitions.*/ +struct AllFunctionDefinitions{ + /**Scope of function definitions.*/ + QMap<FunctionDefinitionDom, Scope> relations; + /**List of function definitions.*/ + FunctionDefinitionList functionList; +}; + +/**Namespace with utilities to find functions in the @ref CodeModel.*/ +namespace Functions{ +/**Looks for functions in the class. +@param list The list of functions found by this routine. +@param dom The class to look for functions.*/ +void processClasses(FunctionList &list, const ClassDom dom); + +/**Looks for functions in the namespace. +@param list The list of functions found by this routine. +@param dom The namespace to look for functions.*/ +void processNamespaces(FunctionList &list, const NamespaceDom dom); + +/**Looks for functions in the class and also saves their scope. +@param list The list of functions found by this routine. +@param dom The class to look for functions. +@param relations The scope information.*/ +void processClasses(FunctionList &list, const ClassDom dom, QMap<FunctionDom, Scope> &relations); + +/**Looks for functions in the class and also saves their scope. +Used for classes withing a namespace. +@param list The list of functions found by this routine. +@param dom The class to look for functions. +@param relations The scope information. +@param nsdom The namespace which contains a class.*/ +void processClasses(FunctionList &list, const ClassDom dom, QMap<FunctionDom, Scope> &relations, const NamespaceDom &nsdom); + +/**Looks for functions in the namespace and also saves their scope. +@param list The list of functions found by this routine. +@param dom The namespace to look for functions. +@param relations The scope information.*/ +void processNamespaces(FunctionList &list, const NamespaceDom dom, QMap<FunctionDom, Scope> &relations); +} + +/**Namespace with utilities to find function definitions in the @ref CodeModel.*/ +namespace FunctionDefinitions{ +/**Looks for function definitions in the class. +@param list The list of function definitions found by this routine. +@param dom The class to look for function definitions.*/ +void processClasses(FunctionDefinitionList &list, const ClassDom dom); + +/**Looks for function definitions in the namespace. +@param list The list of function definitions found by this routine. +@param dom The namespace to look for function definitions.*/ +void processNamespaces(FunctionDefinitionList &list, const NamespaceDom dom); + +/**Looks for function definitions in the class and also saves their scope. +@param list The list of function definitions found by this routine. +@param dom The class to look for function definitions. +@param relations The scope information.*/ +void processClasses(FunctionDefinitionList &list, const ClassDom dom, QMap<FunctionDefinitionDom, Scope> &relations); + +/**Looks for function definitions in the class and also saves their scope. +Used for classes withing a namespace. +@param list The list of function definitions found by this routine. +@param dom The class to look for function definitions . +@param relations The scope information. +@param nsdom The namespace which contains a class.*/ +void processClasses(FunctionDefinitionList &list, const ClassDom dom, QMap<FunctionDefinitionDom, Scope> &relations, const NamespaceDom &nsdom); + +/**Looks for function definitions in the namespace and also saves their scope. +@param list The list of function definitions found by this routine. +@param dom The namespace to look for function definitions. +@param relations The scope information.*/ +void processNamespaces(FunctionDefinitionList &list, const NamespaceDom dom, QMap<FunctionDefinitionDom, Scope> &relations); +} + +/** + * Compares a declaration and a defintion of a function. + * @param dec declaration + * @param def definition + * @return true, if dec is the declaration of the function definition def, false otherwise + * @author Jonas Jacobi <j.jacobi@gmx.de> + */ +bool compareDeclarationToDefinition(const FunctionDom& dec, const FunctionDefinitionDom& def); + +/** + * Compares a declaration and a defintion of a function. + * @param dec declaration + * @param def definition + * @param nsImports namespace imports for the namespace the definition appears in + * @return true, if dec is the declaration of the function definition def, false otherwise + */ +bool compareDeclarationToDefinition(const FunctionDom& dec, const FunctionDefinitionDom& def, const std::set<NamespaceImportModel>& nsImports); + +/** + * Predicate for use with findFunctionDefintions. Searches for a defintion matching a declaration. + * @sa Pred documentation to learn more about predicates used with code model. + * @author Jonas Jacobi + */ +class PredDefinitionMatchesDeclaration{ +public: + PredDefinitionMatchesDeclaration(const FunctionDom& func) : m_declaration(func){}; + bool operator() (const FunctionDefinitionDom& def) const + { + return compareDeclarationToDefinition(m_declaration, def); + } + +private: + const FunctionDom m_declaration; +}; + +template <class InputDomType> +class PredAmOwner{ + public: + PredAmOwner(const FileDom& file) : m_file(file){}; + bool operator() (const InputDomType& def) const + { + return def->file() == m_file; + } + + private: + const FileDom m_file; +}; + +/**@return A list of all functions in the file. +@param dom File Dom to look for functions in.*/ +FunctionList allFunctions(const FileDom &dom); +/**@return A detailed list of all functions in the file (detailed list contains +the information about a scope of each FunctionDom found). +@param dom File Dom to look for functions in.*/ +AllFunctions allFunctionsDetailed(const FileDom &dom); +/**@return A detailed list of all function definitions in the file (detailed list contains +the information about a scope of each FunctionDefinitionDom found). +@param dom File Dom to look for functions in.*/ +AllFunctionDefinitions allFunctionDefinitionsDetailed(const FileDom &dom); + +/**@return A list of all functions in the file. +This version searches the file's whole group for +functions that may have been inserted into the other file's +structure. +Unlike the methods above, this guarantees that all returned +functions physically belong to that file. +@param dom File Dom to look for functions in. */ +FunctionList allFunctionsExhaustive(FileDom &dom); + +/**@return A list of all function-definitions in the file. +This version searches the file's whole group for +functions that may have been inserted into the other file's +structure. +Unlike the methods above, this guarantees that all returned +functions physically belong to that file. +@param dom File Dom to look for functions in. */ +FunctionDefinitionList allFunctionDefinitionsExhaustive(FileDom &dom); + +/** + * Finds a class by its position in a file(position inside the part of the file, where the class is declared). + * In the case of nested classes the innermost class which is declared at/around the provided position. + * @param nameSpace A namespace to search for the class. + * @param line A linenumber inside the class declaration. + * @param col The colum of line. + * @return The innermost class, which is declared at/around position defined with line / col, or 0 if no class is found. + * @author Jonas Jacobi <j.jacobi@gmx.de> + */ +ClassDom findClassByPosition( NamespaceModel* nameSpace, int line, int col ); + +/** + * Same as above, just searches inside a class instead of a namespace. + */ +ClassDom findClassByPosition( ClassModel* aClass, int line, int col ); + +/** + * Finds the last occurrence (line of file wise) of a method inside a class declaration with specific access specificer. + * This can be used e.g. to find a position to new methods to the class. + * @param aClass class to search for method. + * @param access the access specifier with which methods are searched for. + * @return The last line a Method with access specifier access is found, + * or -1 if no method with that access specifier was found. + * @author Jonas Jacobi <j.jacobi@gmx.de> + */ +int findLastMethodLine( ClassDom aClass, CodeModelItem::Access access ); + +/** + * Same as above, but finds a membervariable instead of a method. + */ +int findLastVariableLine( ClassDom aClass, CodeModelItem::Access access ); + +/** + * Get the string representation of an accesss pecifier + * @param access An access specifier to get a string representation of. + * @return string The representation of an access (e.g. "public"). + * @author Jonas Jacobi <j.jacobi@gmx.de> + */ +QString accessSpecifierToString( CodeModelItem::Access access ); + + +class CodeModelHelper { + private: + CodeModel* m_model; + FileList m_files; + QString m_fileName; + + FunctionDefinitionDom functionDefinitionAt(NamespaceDom ns, int line, int column); + + FunctionDefinitionDom functionDefinitionAt(ClassDom klass, int line, int column); + + FunctionDefinitionDom functionDefinitionAt(FunctionDefinitionDom fun, int line, int ); + + FunctionDom functionDeclarationAt(NamespaceDom ns, int line, int column); + + FunctionDom functionDeclarationAt(ClassDom klass, int line, int column); + + FunctionDom functionDeclarationAt(FunctionDom fun, int line, int column ); + + + ClassDom classAt(NamespaceDom ns, int line, int column); + + ClassDom classAt(ClassDom klass, int line, int column); + + public: + CodeModelHelper( CodeModel* model, FileDom file ); + + enum FunctionTypes { + Declaration = 1, + Definition = 2 + }; + + FunctionDom functionAt( int line, int column, FunctionTypes types = (FunctionTypes)3 ); + ClassDom classAt( int line, int column ); +}; + +} + +#endif diff --git a/lib/interfaces/extensions/Mainpage.dox b/lib/interfaces/extensions/Mainpage.dox new file mode 100644 index 00000000..d306a1e8 --- /dev/null +++ b/lib/interfaces/extensions/Mainpage.dox @@ -0,0 +1,61 @@ +/** +@mainpage The KDevelop Extension Interfaces Library + +This library contains extension interfaces used by KDevelop plugin architecture. + +<b>Link with</b>: -lkdevelop + +<b>Include path</b>: -I\$(kde_includes)/kdevelop/interfaces/extensions + +\section whatisextension What is the KDevelop extension + +Extension is a KDevelop plugin which implements one of extension interfaces. +Extension is usually not important enough to be returned by @ref KDevApi and @ref KDevPlugin +methods. Therefore extension instance can be obtained by @ref KDevPlugin::extension method. + +Note: extension plugins can be either core, global and project plugins. They are loaded +in the same way other plugins are. But extensions differ from usual plugins. + +Note: many plugins implementing extension interface can be created but only one of +those should be loaded at a time. This can be accomplished by: +- using a shell plugin profile (as done in current generic shell implementation) - define + different X-KDevelop-Properties for different extension implementations; +- writing project manager which looks into the project file and loads the neccesary extension. + +\section creatingextension Creating and using an extension +- Define a service, use following .desktop file: + @code + [Desktop Entry] + Encoding=UTF-8 + Type=ServiceType + X-KDE-ServiceType=KDevelop/MyExtension + X-KDE-Derived=KDevelop/Plugin + Name=My Extension Interface + [PropertyDef::X-KDevelop-Version] + Type=int + @endcode +- Define an abstract base class for an extension like: + @code + class KDevMyExtension: public KDevPlugin { + public: + KDevMyExtension(const KDevPluginInfo *info, QObject* parent, const char* name) + :KDevPlugin(info, parent, name) {} + + virtual void doSomething() = 0; + }; + @endcode +- Create an usual plugin, but instead of setting service type to "KDevelop/Plugin", set: + @code + ServiceTypes=KDevelop/MyExtension + @endcode +- Use your extension: + @code + KDevMyExtension *myext = extension<KDevMyExtension>("KDevelop/MyExtension"); + if (sf) { + // do something + } else { + // fail + } + @endcode +*/ + diff --git a/lib/interfaces/extensions/Makefile.am b/lib/interfaces/extensions/Makefile.am new file mode 100644 index 00000000..4bc069cb --- /dev/null +++ b/lib/interfaces/extensions/Makefile.am @@ -0,0 +1,23 @@ +kdevelopincludeextdir = $(includedir)/kdevelop/interfaces/extensions +kdevelopincludeext_HEADERS = codebrowserfrontend.h kdevappfrontend.h \ + kdevcreatefile.h kdevdifffrontend.h kdevmakefrontend.h kdevquickopen.h \ + kdevsourceformatter.h kdevversioncontrol.h + +servicetypedir = $(kde_servicetypesdir) +servicetype_DATA = kdevelopappfrontend.desktop \ + kdevelopcodebrowserfrontend.desktop kdevelopcreatefile.desktop kdevelopdifffrontend.desktop \ + kdevelopmakefrontend.desktop kdevelopquickopen.desktop kdevelopsourceformatter.desktop \ + kdevelopversioncontrol.desktop + +SUBDIRS = dcop +libkdevextensions_la_LDFLAGS = $(all_libraries) +noinst_LTLIBRARIES = libkdevextensions.la +libkdevextensions_la_SOURCES = kdevappfrontend.cpp kdevmakefrontend.cpp +INCLUDES = -I$(top_srcdir)/lib/interfaces $(all_includes) +METASOURCES = AUTO + +DOXYGEN_REFERENCES = dcop interfaces kdecore kdefx kdeui khtml kmdi kio kjs kparts kutils kdevinterfaces +DOXYGEN_PROJECTNAME = KDevelop Extension Interfaces Library +DOXYGEN_DOCDIRPREFIX = kdev +include ../../../Doxyfile.am +noinst_HEADERS = codebrowserfrontend.h diff --git a/lib/interfaces/extensions/codebrowserfrontend.h b/lib/interfaces/extensions/codebrowserfrontend.h new file mode 100644 index 00000000..57852ac8 --- /dev/null +++ b/lib/interfaces/extensions/codebrowserfrontend.h @@ -0,0 +1,40 @@ +/* This file is part of the KDE project + Copyright (C) 2006 David Nolden <david.nolden.kdevelop@art-master.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef CODEBROWSERRONTEND_H +#define CODEBROWSERRONTEND_H + + +#include <kdevplugin.h> +#include <codemodel.h> + +namespace Extensions { + +class KDevCodeBrowserFrontend : public KDevPlugin { + Q_OBJECT + + public: + KDevCodeBrowserFrontend(const KDevPluginInfo *info, QObject *parent=0, const char *name=0 ) + :KDevPlugin(info, parent, name ? name : "CodeBrowserFrontend") {} + + ///Used by the quickopen-plugin to notify extensions that it jumped to a searched item + virtual bool jumpedToItem( ItemDom item ) = 0; +}; +} + +#endif diff --git a/lib/interfaces/extensions/dcop/KDevAppFrontendIface.cpp b/lib/interfaces/extensions/dcop/KDevAppFrontendIface.cpp new file mode 100644 index 00000000..8bf5b7ce --- /dev/null +++ b/lib/interfaces/extensions/dcop/KDevAppFrontendIface.cpp @@ -0,0 +1,76 @@ + + +/* This file is part of the KDE project + Copyright (C) 2001 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2002 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2002 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2003 Amilcar do Carmo Lucas <amilcar@ida.ing.tu-bs.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "KDevAppFrontendIface.h" +#include "kdevappfrontend.h" + + +KDevAppFrontendIface::KDevAppFrontendIface(KDevAppFrontend *appFrontend) + : DCOPObject("KDevAppFrontend"), m_appFrontend(appFrontend) +{ +} + + +KDevAppFrontendIface::~KDevAppFrontendIface() +{} + + +void KDevAppFrontendIface::startAppCommand(const QString &directory, const QString &command, bool inTerminal) +{ + m_appFrontend->startAppCommand(directory, command, inTerminal); +} + +void KDevAppFrontendIface::stopApplication( ) +{ + m_appFrontend->stopApplication(); +} + +bool KDevAppFrontendIface::isRunning( ) +{ + return m_appFrontend->isRunning(); +} + +void KDevAppFrontendIface::clearView( ) +{ + m_appFrontend->clearView(); +} + +void KDevAppFrontendIface::insertStderrLine( const QCString & line ) +{ + m_appFrontend->insertStderrLine(line); +} + +void KDevAppFrontendIface::insertStdoutLine( const QCString & line ) +{ + m_appFrontend->insertStdoutLine(line); +} + +void KDevAppFrontendIface::addPartialStderrLine( const QCString& line ) +{ + m_appFrontend->addPartialStderrLine(line); +} + +void KDevAppFrontendIface::addPartialStdoutLine( const QCString& line ) +{ + m_appFrontend->addPartialStdoutLine(line); +} diff --git a/lib/interfaces/extensions/dcop/KDevAppFrontendIface.h b/lib/interfaces/extensions/dcop/KDevAppFrontendIface.h new file mode 100644 index 00000000..e54e25b0 --- /dev/null +++ b/lib/interfaces/extensions/dcop/KDevAppFrontendIface.h @@ -0,0 +1,52 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2002 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2002 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2003 Amilcar do Carmo Lucas <amilcar@ida.ing.tu-bs.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef _KDEVAPPFRONTENDIFACE_H_ +#define _KDEVAPPFRONTENDIFACE_H_ + +#include <dcopobject.h> + +class KDevAppFrontend; + +class KDevAppFrontendIface : public DCOPObject +{ + K_DCOP + +public: + + KDevAppFrontendIface( KDevAppFrontend *appFrontend ); + ~KDevAppFrontendIface(); + +k_dcop: + void startAppCommand(const QString &directory, const QString &command, bool inTerminal); + void stopApplication(); + bool isRunning(); + void clearView(); + void insertStderrLine(const QCString &line); + void insertStdoutLine(const QCString &line); + void addPartialStderrLine(const QCString &line); + void addPartialStdoutLine(const QCString &line); + +private: + KDevAppFrontend *m_appFrontend; +}; + +#endif diff --git a/lib/interfaces/extensions/dcop/KDevMakeFrontendIface.cpp b/lib/interfaces/extensions/dcop/KDevMakeFrontendIface.cpp new file mode 100644 index 00000000..2050a970 --- /dev/null +++ b/lib/interfaces/extensions/dcop/KDevMakeFrontendIface.cpp @@ -0,0 +1,46 @@ + + +/* This file is part of the KDE project + Copyright (C) 2001 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2001 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2002 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "KDevMakeFrontendIface.h" +#include "kdevmakefrontend.h" + + +KDevMakeFrontendIface::KDevMakeFrontendIface(KDevMakeFrontend *makeFrontend) + : DCOPObject("KDevMakeFrontend") +{ + m_makeFrontend = makeFrontend; +} + + +KDevMakeFrontendIface::~KDevMakeFrontendIface() +{} + + +void KDevMakeFrontendIface::queueCommand(const QString &dir, const QString &command) +{ + m_makeFrontend->queueCommand(dir, command); +} + +bool KDevMakeFrontendIface::isRunning( ) +{ + return m_makeFrontend->isRunning(); +} diff --git a/lib/interfaces/extensions/dcop/KDevMakeFrontendIface.h b/lib/interfaces/extensions/dcop/KDevMakeFrontendIface.h new file mode 100644 index 00000000..89ecdf5e --- /dev/null +++ b/lib/interfaces/extensions/dcop/KDevMakeFrontendIface.h @@ -0,0 +1,45 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2001 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2002 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef _KDEVMAKEFRONTENDIFACE_H_ +#define _KDEVMAKEFRONTENDIFACE_H_ + +#include <dcopobject.h> + +class KDevMakeFrontend; + +class KDevMakeFrontendIface : public DCOPObject +{ + K_DCOP + +public: + + KDevMakeFrontendIface( KDevMakeFrontend *makeFrontend ); + ~KDevMakeFrontendIface(); + +k_dcop: + void queueCommand(const QString &dir, const QString &command); + bool isRunning(); + +private: + KDevMakeFrontend *m_makeFrontend; +}; + +#endif diff --git a/lib/interfaces/extensions/dcop/Makefile.am b/lib/interfaces/extensions/dcop/Makefile.am new file mode 100644 index 00000000..0b904eeb --- /dev/null +++ b/lib/interfaces/extensions/dcop/Makefile.am @@ -0,0 +1,10 @@ +INCLUDES = -I$(top_srcdir)/lib/interfaces -I$(top_srcdir)/lib/interfaces/extensions $(all_includes) +METASOURCES = AUTO + +noinst_LTLIBRARIES = libkdevdcopextensions.la +libkdevdcopextensions_la_LDFLAGS = $(all_libraries) +libkdevdcopextensions_la_SOURCES = KDevAppFrontendIface.cpp \ + KDevMakeFrontendIface.cpp KDevAppFrontendIface.skel KDevMakeFrontendIface.skel + +dcopincludeextdir = $(includedir)/kdevelop/interfaces/extensions/dcop +dcopincludeext_HEADERS = KDevAppFrontendIface.h KDevMakeFrontendIface.h diff --git a/lib/interfaces/extensions/kdevappfrontend.cpp b/lib/interfaces/extensions/kdevappfrontend.cpp new file mode 100644 index 00000000..3d918fe2 --- /dev/null +++ b/lib/interfaces/extensions/kdevappfrontend.cpp @@ -0,0 +1,2 @@ +#include "kdevappfrontend.h" +#include "kdevappfrontend.moc" diff --git a/lib/interfaces/extensions/kdevappfrontend.h b/lib/interfaces/extensions/kdevappfrontend.h new file mode 100644 index 00000000..65424ed9 --- /dev/null +++ b/lib/interfaces/extensions/kdevappfrontend.h @@ -0,0 +1,115 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2001-2002 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2002-2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2002 Simon Hausmann <hausmann@kde.org> + Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org> + Copyright (C) 2003 Amilcar do Carmo Lucas <amilcar@ida.ing.tu-bs.de> + Copyright (C) 2003 Hamish Rodda <rodda@kde.org> + Copyright (C) 2003 Jens Dagerbo <jens.dagerbo@swipnet.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVAPPFRONTEND_H +#define KDEVAPPFRONTEND_H + +#include <qstringlist.h> +#include <kdevplugin.h> + +/** +@file kdevappfrontend.h +Application frontend interface. +*/ + +/** +Application frontend interface. +This interface is responsible for handling the running of an application in KDevelop. +Currently, this interface defines ways to do the following: +- check if the application is running; +- execute the application; +- stop the currently running application; +- control the output view as seen in the 'Application' tool dock. + +Instances that implement this interface are available through extension architecture: +@code +KDevAppFrontend *apf = extension<KDevAppFrontend>("KDevelop/AppFrontend"); +if (apf) { + // do something +} else { + // fail +} +@endcode +@sa KDevPlugin::extension method documentation. +*/ +class KDevAppFrontend : public KDevPlugin +{ + Q_OBJECT + +public: + /**Constructor. + @param info Important information about the plugin - plugin internal and generic + (GUI) name, description, a list of authors, etc. That information is used to show + plugin information in various places like "about application" dialog, plugin selector + dialog, etc. Plugin does not take ownership on info object, also its lifetime should + be equal to the lifetime of the plugin. + @param parent The parent object for the plugin. Parent object must implement @ref KDevApi + interface. Otherwise the plugin will not be constructed. + @param name The internal name which identifies the plugin.*/ + KDevAppFrontend(const KDevPluginInfo *info, QObject *parent=0, const char *name=0 ) + :KDevPlugin(info, parent, name ? name : "KDevAppFrontend") {} + + /**@return Whether the application is currently running.*/ + virtual bool isRunning() = 0; + +public slots: + /** + * The component shall start to execute an app-like command. + * Running the application is always asynchronous. + * @param directory The working directory to start the app in, + * if empty then the user's home directory is used. + * @param program A program to start. + * @param inTerminal If true then the program is started in an external konsole. + */ + virtual void startAppCommand(const QString &directory, const QString &program, bool inTerminal) = 0; + + /** + * Stops the currently running application. + */ + virtual void stopApplication() = 0; + + /** + * Inserts a string into the application output view. + * @param line A string to insert. + */ + virtual void insertStdoutLine(const QCString &line) = 0; + + /** + * Inserts a string into the application output view marked as stderr output + * (usually colored). + * @param line An error string to insert. + */ + virtual void insertStderrLine(const QCString &line) = 0; + + virtual void addPartialStderrLine( const QCString& line ) = 0; + virtual void addPartialStdoutLine( const QCString& line ) = 0; + + /** + * Clears the output view. + */ + virtual void clearView() = 0; +}; + +#endif diff --git a/lib/interfaces/extensions/kdevcreatefile.h b/lib/interfaces/extensions/kdevcreatefile.h new file mode 100644 index 00000000..b142cd4d --- /dev/null +++ b/lib/interfaces/extensions/kdevcreatefile.h @@ -0,0 +1,146 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Julian Rockey <linux@jrockey.com> + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVCREATEFILE_H +#define KDEVCREATEFILE_H + +#include <qstring.h> + +#include <kdevplugin.h> + +/** +@file kdevcreatefile.h +File creation facility interface. +*/ + +/** +File creation facility interface. + +An abstract class for all extensions that are responsible for file creation. + +Instances that implement this interface are available through extension architecture: +@code +KDevCreateFile *cf = extension<KDevCreateFile>("KDevelop/CreateFile"); +if (cf) { + // do something +} else { + // fail +} +@endcode +@sa KDevPlugin::extension method documentation. +*/ +class KDevCreateFile : public KDevPlugin +{ + +public: + /**File created with @ref KDevCreateFile implementation.*/ + class CreatedFile { + + public: + /**The status of a file.*/ + enum Status { + STATUS_OK /**<File was successfuly created.*/, + STATUS_CANCELED /**<File was not created due to user intervention.*/, + STATUS_NOTCREATED /**<File was not created due to error.*/, + STATUS_NOTWITHINPROJECT /**<File was successfuly created but not added to a project.*/ + }; + + /**Constructor. + Sets status to STATUS_NOTCREATED.*/ + CreatedFile() + : status( STATUS_NOTCREATED ) {} + + CreatedFile( const CreatedFile& source ) + : dir( source.dir ), filename( source.filename ), + ext( source.ext ), subtype( source.subtype ), + status( source.status ), addToProject(false) {} + + CreatedFile& operator = ( const CreatedFile& source ) + { + dir = source.dir; + filename = source.filename; + ext = source.ext; + subtype = source.subtype; + status = source.status; + addToProject = source.addToProject; + return( *this ); + } + + bool operator == ( const CreatedFile& source ) const + { + return + dir == source.dir && + filename == source.filename && + ext == source.ext && + subtype == source.subtype && + status == source.status && + addToProject == source.addToProject; + } + + // this should be private + /**The directory.*/ + QString dir; + /**The name (without directory path).*/ + QString filename; + /**The extension of a file. Extension defines a "type" of the file template + to use during file creation.*/ + QString ext; + /**The subtype of a file. "Subtype" defines a file template to use when + there are several file templates for each extension.*/ + QString subtype; + /**Current status.*/ + Status status; + /**true if the file should be added to a project.*/ + bool addToProject; + }; + + +public: + + /**Constructor. + @param info Important information about the plugin - plugin internal and generic + (GUI) name, description, a list of authors, etc. That information is used to show + plugin information in various places like "about application" dialog, plugin selector + dialog, etc. Plugin does not take ownership on info object, also its lifetime should + be equal to the lifetime of the plugin. + @param parent The parent object for the plugin. Parent object must implement @ref KDevApi + interface. Otherwise the plugin will not be constructed. + @param name The internal name which identifies the plugin.*/ + KDevCreateFile(const KDevPluginInfo *info, QObject * parent = 0, const char * name = 0) + :KDevPlugin(info, parent, name) {} + + /**Creates a new file, within or without the project. + Supply as much information as you know. Leave what you don't know as QString::null. + The user will be prompted as necessary for the missing information, and the + file created, and added to the project as necessary. + @param ext File extension (type). + @param dir The absolute path to a directory. + @param name The name of a file. + @param subtype The subtype, pass this only if an extension is not enough to find the + file template. + @return @ref CreatedFile instance with information about file and file creation process.*/ + virtual CreatedFile createNewFile(QString ext = QString::null, + QString dir = QString::null, + QString name = QString::null, + QString subtype = QString::null) = 0; + + +}; + +#endif diff --git a/lib/interfaces/extensions/kdevdifffrontend.cpp b/lib/interfaces/extensions/kdevdifffrontend.cpp new file mode 100644 index 00000000..a2e2c49b --- /dev/null +++ b/lib/interfaces/extensions/kdevdifffrontend.cpp @@ -0,0 +1,33 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Harald Fernengel <harry@kdevelop.org> + Copyright (C) 2002 F@lk Brettschneider <falkbr@kdevelop.org> + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "kdevdifffrontend.h" + + +KDevDiffFrontend::KDevDiffFrontend(const KDevPluginInfo *info, QObject *parent, const char *name) + : KDevPlugin(info, parent, name ? name : "KDevDiffFrontend") +{ +} + +KDevDiffFrontend::~KDevDiffFrontend() +{ +} + +#include "kdevdifffrontend.moc" diff --git a/lib/interfaces/extensions/kdevdifffrontend.h b/lib/interfaces/extensions/kdevdifffrontend.h new file mode 100644 index 00000000..7c5874a2 --- /dev/null +++ b/lib/interfaces/extensions/kdevdifffrontend.h @@ -0,0 +1,79 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Harald Fernengel <harry@kdevelop.org> + Copyright (C) 2002 F@lk Brettschneider <falkbr@kdevelop.org> + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVDIFFFRONTEND_H +#define KDEVDIFFFRONTEND_H + +#include <kurl.h> +#include <kdevplugin.h> + +/** +@file kdevdifffrontend.h +Diff frontend interface. +*/ + +/** +KDevelop diff frontend interface. +This is the abstract base class for plugins that want to display differencies between +files. + +Instances that implement this interface are available through extension architecture: +@code +KDevDiffFrontend *df = extension<KDevDiffFrontend>("KDevelop/DiffFrontend"); +if (df) { + // do something +} else { + // fail +} +@endcode +@sa KDevPlugin::extension method documentation. +*/ +class KDevDiffFrontend : public KDevPlugin +{ + +public: + /**Constructor. + @param info Important information about the plugin - plugin internal and generic + (GUI) name, description, a list of authors, etc. That information is used to show + plugin information in various places like "about application" dialog, plugin selector + dialog, etc. Plugin does not take ownership on info object, also its lifetime should + be equal to the lifetime of the plugin. + @param parent The parent object for the plugin. Parent object must implement @ref KDevApi + interface. Otherwise the plugin will not be constructed. + @param name The internal name which identifies the plugin.*/ + KDevDiffFrontend( const KDevPluginInfo *info, QObject *parent=0, const char *name=0 ) + :KDevPlugin(info, parent, name ? name : "KDevDiffFrontend") {} + + /**Displays the patch. + @param diff A string which contains a patch in unified format.*/ + virtual void showDiff( const QString& diff ) = 0; + + /**Displays a patch file. + @param url An url of the patch file.*/ + virtual void openURL( const KURL &url ) = 0; + + /**Displays the difference between the two files. + @param url1 First file to compare. + @param url2 Second file to compare.*/ + virtual void showDiff( const KURL &url1, const KURL &url2 ) = 0; + +}; + +#endif diff --git a/lib/interfaces/extensions/kdevelopappfrontend.desktop b/lib/interfaces/extensions/kdevelopappfrontend.desktop new file mode 100644 index 00000000..60cd678b --- /dev/null +++ b/lib/interfaces/extensions/kdevelopappfrontend.desktop @@ -0,0 +1,38 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KDevelop/AppFrontend +X-KDE-Derived=KDevelop/Plugin +Name=App Frontend Interface +Name[ca]=Interfície de l'entorn de les aplicacions +Name[da]=Programbrugerflade-grænseflade +Name[de]=Oberflächen-Schnittstelle (KDevelop) +Name[el]=Διασύνδεση προγράμματος App +Name[es]=Interfaz de entorno de aplicación +Name[et]=Rakenduse kasutajaliides +Name[eu]=Aplikazioaren inguruneko interfazea +Name[fa]=واسط پایانۀ کاربرد +Name[fr]=Interface graphique pour une application +Name[gl]=Interface do frontal da aplicación +Name[hu]=Alkalmazás kezelőfelülete +Name[it]=Applicazione per le interfacce +Name[ja]=App フロントエンド インターフェース +Name[nds]=Programmböversiet-Koppelsteed +Name[ne]=एप फ्रेन्टइन्ड इन्टरफेस +Name[pl]=Interfejs do programów +Name[pt]=Interface da Aplicação +Name[pt_BR]=Interface Frontend para Aplicativos +Name[ru]=Интерфейс менеджера приложений +Name[sk]=Aplikačné rozhranie +Name[sr]=Кориснички интерфејс програма +Name[sr@Latn]=Korisnički interfejs programa +Name[sv]=Programgränssnitt +Name[ta]=ஆப் முன்னிருந்த இடைமுகம் +Name[tg]=Интерфейси мудири гузориш +Name[tr]=Uygulama Önucu Arayüzü +Name[zh_CN]=应用程序前端接口 +Name[zh_TW]=App 前端介面 + +# versioning - prevent DLL hell +[PropertyDef::X-KDevelop-Version] +Type=int + diff --git a/lib/interfaces/extensions/kdevelopcodebrowserfrontend.desktop b/lib/interfaces/extensions/kdevelopcodebrowserfrontend.desktop new file mode 100644 index 00000000..9dd7f8d2 --- /dev/null +++ b/lib/interfaces/extensions/kdevelopcodebrowserfrontend.desktop @@ -0,0 +1,28 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KDevelop/CodeBrowserFrontend +X-KDE-Derived=KDevelop/Plugin +Name=Code Browser Frontend +Name[ca]=Interfície per al navegador de codi +Name[da]=Kodebrowser-grænseflade +Name[de]=Quelltextbrowser-Oberfläche +Name[el]=Πρόγραμμα περιήγησης κώδικα +Name[es]=Interfaz para el navegador de código +Name[et]=Koodibrauseri kasutajaliides +Name[fr]=Interface de navigation dans le code +Name[hu]=Kódböngésző +Name[it]=Interfaccia di navigazione del codice +Name[ja]=コードブラウザのフロントエンド +Name[nds]=Kodekieker-Böversiet +Name[pl]=Interfejs do przeglądarki kodu +Name[pt]=Interface de Navegação do Código +Name[pt_BR]=Interface de Navegação do Código +Name[ru]=Навигатор по коду +Name[sk]=Rozhranie pre prehliadač kódu +Name[sr]=Интерфејс прегледача кода +Name[sr@Latn]=Interfejs pregledača koda +Name[sv]=Kodbläddringsgränssnitt +Name[zh_TW]=源碼瀏覽器前端介面 + +[PropertyDef::X-KDevelop-Version] +Type=int diff --git a/lib/interfaces/extensions/kdevelopcreatefile.desktop b/lib/interfaces/extensions/kdevelopcreatefile.desktop new file mode 100644 index 00000000..d100b330 --- /dev/null +++ b/lib/interfaces/extensions/kdevelopcreatefile.desktop @@ -0,0 +1,38 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KDevelop/CreateFile +X-KDE-Derived=KDevelop/Plugin +Name=KDevelop Create File Interface +Name[ca]=Interfície de creació de fitxers per a KDevelop +Name[da]=KDevelop opret filgrænseflade +Name[de]=KDevelop-Schnittstelle zum Anlegen von Dateien +Name[el]=Διασύνδεση δημιουργίας αρχείου KDevelop +Name[es]=Interfaz de creación de archivos de KDevelop +Name[et]=KDevelopi faili loomise liides +Name[eu]=KDevelop-en "Sortu fitxategia" interfazea +Name[fa]=واسط پروندۀ ایجاد KDevelop +Name[fr]=Interface de création de fichiers pour KDevelop +Name[gl]=Interface de crear ficheiro de KDevelop +Name[hu]=KDevelop fájllétrehozási felület +Name[it]=Interfaccia KDevelop per creare file +Name[ja]=KDevelop Create File インターフェース +Name[nds]=KDevelop-Koppelsteed för't Dateiopstellen +Name[ne]=केडीई विकास फाइल इन्टरफेस सिर्जना +Name[nl]=KDevelop Bestand aanmaken-interface +Name[pl]=Interfejs KDevelopa do tworzenia pliku +Name[pt]=Interface de Criação de Ficheiro do KDevelop +Name[pt_BR]=Interface para Criar Arquivo do KDevelop +Name[ru]=Интерфейс средства создания файлов для KDevelop +Name[sk]=KDevelop rozhranie na vytvorenie súboru +Name[sl]=Vmesnik za ustvarjanje datotek v KDevelopu +Name[sr]=KDevelop-ов интерфејс за прављење фајла +Name[sr@Latn]=KDevelop-ov interfejs za pravljenje fajla +Name[sv]=KDevelop gränssnitt för att skapa filer +Name[tr]=KDevelop Dosya Yaratma Arayüzü +Name[zh_CN]=KDevelop 文件创建接口 +Name[zh_TW]=KDevelop 建立檔案介面 + +# versioning - prevent DLL hell +[PropertyDef::X-KDevelop-Version] +Type=int + diff --git a/lib/interfaces/extensions/kdevelopdifffrontend.desktop b/lib/interfaces/extensions/kdevelopdifffrontend.desktop new file mode 100644 index 00000000..31dec5af --- /dev/null +++ b/lib/interfaces/extensions/kdevelopdifffrontend.desktop @@ -0,0 +1,41 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KDevelop/DiffFrontend +X-KDE-Derived=KDevelop/Plugin +Name=Diff Frontend Interface +Name[ca]=Interfície de l'entorn per a diff +Name[da]=Diff-brugerflade grænseflade +Name[de]=Schnittstelle für Diff-Oberflächen (KDevelop) +Name[el]=Διασύνδεση προγράμματος Diff +Name[es]=Interfaz del entorno de diff +Name[et]=Diff kasutajaliides +Name[eu]=Desberdintasun ingurunearen interfazea +Name[fa]=واسط پایانۀ تفاوت +Name[fr]=Interface du programme « Diff » +Name[gl]=Interface do frontal de Diff +Name[hi]=डिफ़ फ्रन्टएण्ड इंटरफ़ेस +Name[hu]=Diff-kezelési felület +Name[it]=Interfaccia a Diff +Name[ja]=Diff フロントエンド インターフェース +Name[nds]=Koppelsteed för "Diff"-Böversiet +Name[ne]=डिफ फ्रेन्टइन्ड इन्टरफेस +Name[nl]=Diff Frontend-interface +Name[pl]=Interfejs do programu diff +Name[pt]=Interface para o 'Diff' +Name[pt_BR]=Interface de Frontend do Diff +Name[ru]=Интерфейс системы нахождения различий +Name[sk]=Rozhranie rozdielu +Name[sl]=Vmesnik za diff +Name[sr]=Кориснички интерфејс за diff +Name[sr@Latn]=Korisnički interfejs za diff +Name[sv]=Gränssnitt för jämförelse +Name[ta]=டிப் முன்நிறுத்த இடைமுகம் +Name[tg]=Барномаи интерфейс барои ёфтани тағирпазирӣ +Name[tr]=Diff Önuç Arayüzü +Name[zh_CN]=Diff 前端接口 +Name[zh_TW]=Diff 前端介面 + +# versioning - prevent DLL hell +[PropertyDef::X-KDevelop-Version] +Type=int + diff --git a/lib/interfaces/extensions/kdevelopmakefrontend.desktop b/lib/interfaces/extensions/kdevelopmakefrontend.desktop new file mode 100644 index 00000000..f38d2d14 --- /dev/null +++ b/lib/interfaces/extensions/kdevelopmakefrontend.desktop @@ -0,0 +1,41 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KDevelop/MakeFrontend +X-KDE-Derived=KDevelop/Plugin +Name=Make Frontend Interface +Name[ca]=Interfície de l'entorn per a make +Name[da]=Make brugerflade-grænseflade +Name[de]=Schnittstelle für Make-Oberflächen (KDevelop) +Name[el]=Διασύνδεση προγράμματος Make +Name[es]=Interfaz del entorno de make +Name[et]=Make kasutajaliides +Name[eu]=Make ingurunearen interfazea +Name[fa]=واسط پایانۀ Make +Name[fr]=Interface du programme « make » +Name[gl]=Interface do frontal de Make +Name[hi]=मेक फ्रन्टएण्ड इंटरफ़ेस +Name[hu]=Make kezelőfelület +Name[it]=Interfaccia per Make +Name[ja]=Make フロントエンド インターフェース +Name[nds]=Koppelsteed för "Make"-Böversiet +Name[ne]=मेक फ्रेन्टइन्ड इन्टरफेस +Name[nl]=Make Frontend-interface +Name[pl]=Interfejs do Make +Name[pt]=Interface para o Make +Name[pt_BR]=Interface de Frontend do Make +Name[ru]=Интерфейс Make +Name[sk]=Rozhranie pre make +Name[sl]=Vmesnik za make +Name[sr]=Кориснички интерфејс за make +Name[sr@Latn]=Korisnički interfejs za make +Name[sv]=Byggränssnitt +Name[ta]=முன்பகுதி இடை விளிம்பை அமை +Name[tg]=Интерфейс пӯсти Make +Name[tr]=Make Önuç Arayüzü +Name[zh_CN]=Make 前端接口 +Name[zh_TW]=Make 前端介面 + +# versioning - prevent DLL hell +[PropertyDef::X-KDevelop-Version] +Type=int + diff --git a/lib/interfaces/extensions/kdevelopquickopen.desktop b/lib/interfaces/extensions/kdevelopquickopen.desktop new file mode 100644 index 00000000..57ae2fd5 --- /dev/null +++ b/lib/interfaces/extensions/kdevelopquickopen.desktop @@ -0,0 +1,58 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KDevelop/QuickOpen +X-KDE-Derived=KDevelop/Plugin +Name=Quick Open Interface +Name[ca]=Interfície Open Quick +Name[da]=Quick Open-grænseflade +Name[de]=Schnittstelle für Schnellöffnen +Name[el]=Διασύνδεση γρήγορου ανοίγματος +Name[es]=Interfaz de apertura rápida +Name[et]=Kiiravamise kasutajaliides +Name[hu]=Gyors megnyitási kezelőfelület +Name[it]=Interfaccia Quick Open +Name[nds]=Fixopmaak-Koppelsteed +Name[nl]=Snelopen-interface +Name[pl]=Open Interface +Name[pt]=Interface de Abertura Rápida +Name[pt_BR]=Interface de Abertura Rápida +Name[ru]=Интерфейс быстрого открытия +Name[sk]=Rozhranie pre Quick Open +Name[sr]=Интерфејс брзог отварања +Name[sr@Latn]=Interfejs brzog otvaranja +Name[sv]=Snabböppningsgränssnitt +Name[zh_TW]=快速開啟介面 + +# versioning - prevent DLL hell +[PropertyDef::X-KDevelop-Version] +Type=int + +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KDevelop/QuickOpen +X-KDE-Derived=KDevelop/Plugin +Name=Quick Open Interface +Name[ca]=Interfície Open Quick +Name[da]=Quick Open-grænseflade +Name[de]=Schnittstelle für Schnellöffnen +Name[el]=Διασύνδεση γρήγορου ανοίγματος +Name[es]=Interfaz de apertura rápida +Name[et]=Kiiravamise kasutajaliides +Name[hu]=Gyors megnyitási kezelőfelület +Name[it]=Interfaccia Quick Open +Name[nds]=Fixopmaak-Koppelsteed +Name[nl]=Snelopen-interface +Name[pl]=Open Interface +Name[pt]=Interface de Abertura Rápida +Name[pt_BR]=Interface de Abertura Rápida +Name[ru]=Интерфейс быстрого открытия +Name[sk]=Rozhranie pre Quick Open +Name[sr]=Интерфејс брзог отварања +Name[sr@Latn]=Interfejs brzog otvaranja +Name[sv]=Snabböppningsgränssnitt +Name[zh_TW]=快速開啟介面 + +# versioning - prevent DLL hell +[PropertyDef::X-KDevelop-Version] +Type=int + diff --git a/lib/interfaces/extensions/kdevelopsourceformatter.desktop b/lib/interfaces/extensions/kdevelopsourceformatter.desktop new file mode 100644 index 00000000..2bdadc38 --- /dev/null +++ b/lib/interfaces/extensions/kdevelopsourceformatter.desktop @@ -0,0 +1,39 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KDevelop/SourceFormatter +X-KDE-Derived=KDevelop/Plugin +Name=Source Formatter Interface +Name[ca]=Interfície del formatador de codi font +Name[da]=Kildekodeformatering grænseflade +Name[de]=Quelltext-Formatierer-Schnittstelle (KDevelop) +Name[el]=Διασύνδεση μορφοποίησης κώδικα +Name[es]=Interfaz del formateador de código fuente +Name[et]=Koodi vormindamise liides +Name[eu]=Iturburu formateatzailearen interfazea +Name[fa]=واسط قالبدهندۀ منبع +Name[fr]=Interface pour le formatage de code source +Name[gl]=Interface do formateador de código +Name[hu]=Forrásformázási felület +Name[it]=Interfaccia per il formattatore del codice sorgente +Name[ja]=ソースフォーマッタ インターフェース +Name[nds]=Koppelsteed för Borntext-Formaterer +Name[ne]=स्रोत ढाँचाबद्धक इन्टरफेस +Name[nl]=Broncode formatteren-interface +Name[pl]=Interfejs do programu formatowania źródeł +Name[pt]=Interface de Formatação de Código +Name[pt_BR]=Interface de Formatação de Fonte +Name[ru]=Интерфейс форматирования исходного кода +Name[sk]=Rozhranie pre formátovanie +Name[sr]=Интерфејс форматера изворног кода +Name[sr@Latn]=Interfejs formatera izvornog koda +Name[sv]=Gränssnitt för källkodsformatering +Name[ta]=வடிவமைப்பவரின் இடை விளிம்பு மூலம் +Name[tg]=Интерфейс қолабгузори коди берунӣ +Name[tr]=Kaynak Biçimleyici Arayüzü +Name[zh_CN]=源代码格式化接口 +Name[zh_TW]=程式碼格式化介面 + +# versioning - prevent DLL hell +[PropertyDef::X-KDevelop-Version] +Type=int + diff --git a/lib/interfaces/extensions/kdevelopversioncontrol.desktop b/lib/interfaces/extensions/kdevelopversioncontrol.desktop new file mode 100644 index 00000000..c772b9a6 --- /dev/null +++ b/lib/interfaces/extensions/kdevelopversioncontrol.desktop @@ -0,0 +1,40 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KDevelop/VersionControl +X-KDE-Derived=KDevelop/Plugin +Name=KDevelop Version Control Interface +Name[ca]=Interfície del control de versions per a KDevelop +Name[da]=KDevelop versionskontrol grænseflade +Name[de]=Versionsverwaltung-Schnittstelle (KDevelop) +Name[el]=Διασύνδεση ελέγχου εκδόσεων KDevelop +Name[es]=Interfaz del control de versiones de KDevelop +Name[et]=KDevelopi versioonide kontrollimise liides +Name[eu]=KDevelop bertsio kontrol interfazea +Name[fa]=واسط کنترل نسخۀ KDevelop +Name[fr]=Interface pour le contrôle de versions de KDevelop +Name[gl]=Interface do control de versións de KDevelop +Name[hi]=के-डेवलप संस्करण नियंत्रण इंटरफ़ेस +Name[hu]=KDevelop verziókezelési felület +Name[ja]=KDevelop バージョンコントロール インターフェース +Name[nds]=Verschoonkuntrull-Koppelsteed vun KDevelop +Name[ne]=केडीई विकास संस्करण नियन्त्रण इन्टरफेस +Name[nl]=KDevelop Versiebeheer-interface +Name[pl]=Interfejs Kdevelopa do kontroli wersji +Name[pt]=Interface de Controlo de Versões do KDevelop +Name[pt_BR]=Interface de Controle de Versão do KDevelop +Name[ru]=Интерфейс системы управления версиями +Name[sk]=KDevelop rozhranie pre riadenie verzií +Name[sl]=Vmesnik nadzora različic za KDevelop +Name[sr]=KDevelop-ов интерфејс за контролу верзије +Name[sr@Latn]=KDevelop-ov interfejs za kontrolu verzije +Name[sv]=KDevelop gränssnitt för versionskontroll +Name[ta]=KDevelop பதிப்பு கட்டுப்பாட்டு இடைஇணைப்பு +Name[tg]=Интерфейс барномаи идоракунии тафсирҳо +Name[tr]=KDevelop Sürüm Kontrol Arayüzü +Name[zh_CN]=KDevelop 版本控制接口 +Name[zh_TW]=KDevelop 版本控制介面 + +# versioning - prevent DLL hell +[PropertyDef::X-KDevelop-Version] +Type=int + diff --git a/lib/interfaces/extensions/kdevmakefrontend.cpp b/lib/interfaces/extensions/kdevmakefrontend.cpp new file mode 100644 index 00000000..5e035270 --- /dev/null +++ b/lib/interfaces/extensions/kdevmakefrontend.cpp @@ -0,0 +1,2 @@ +#include "kdevmakefrontend.h" +#include "kdevmakefrontend.moc" diff --git a/lib/interfaces/extensions/kdevmakefrontend.h b/lib/interfaces/extensions/kdevmakefrontend.h new file mode 100644 index 00000000..6014ff34 --- /dev/null +++ b/lib/interfaces/extensions/kdevmakefrontend.h @@ -0,0 +1,106 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2001 Sandy Meier <smeier@kdevelop.org> + Copyright (C) 2001-2002 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2002-2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2002 Simon Hausmann <hausmann@kde.org> + Copyright (C) 2002 F@lk Brettschneider <falkbr@kdevelop.org> + Copyright (C) 2003 Amilcar do Carmo Lucas <amilcar@ida.ing.tu-bs.de> + Copyright (C) 2003 Hamish Rodda <rodda@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVMAKEFRONTEND_H +#define KDEVMAKEFRONTEND_H + +#include <qstringlist.h> +#include <kdevplugin.h> + +/** +@file kdevmakefrontend.h +Make frontend interface. +*/ + +/** +KDevelop make frontend interface. +This is the abstract base class for plugins that are able to run "make" +or similar commands to build a project, api documentation, etc. + +Instances that implement this interface are available through extension architecture: +@code +KDevMakeFrontend *mf = extension<KDevMakeFrontend>("KDevelop/MakeFrontend"); +if (mf) { + // do something +} else { + // fail +} +@endcode +@sa KDevPlugin::extension method documentation. +*/ +class KDevMakeFrontend : public KDevPlugin +{ + Q_OBJECT + +public: + + /**Constructor. + @param info Important information about the plugin - plugin internal and generic + (GUI) name, description, a list of authors, etc. That information is used to show + plugin information in various places like "about application" dialog, plugin selector + dialog, etc. Plugin does not take ownership on info object, also its lifetime should + be equal to the lifetime of the plugin. + @param parent The parent object for the plugin. Parent object must implement @ref KDevApi + interface. Otherwise the plugin will not be constructed. + @param name The internal name which identifies the plugin.*/ + KDevMakeFrontend(const KDevPluginInfo *info, QObject *parent=0, const char *name=0 ) + :KDevPlugin(info, parent, name ? name : "KDevMakeFrontend") {} + + /**@return The widget where the make output is shown.*/ + virtual QWidget* widget() { return 0L; } + + /**The component shall start to execute a make-like command. + * Commands are always asynchronous. You can submit several jobs + * without caring about another job already running. There are + * executed in the order in which they are submitted. If one of + * then fails, all following jobs are dropped. + * You should not make any assumptions about the directory in which + * the command is started. If the command depends on that, put and + * explicit 'cd' into the command. + * @param dir A starting directory to find files when parsing compiler error + * messages. + * @param command A shell command to execute. + */ + virtual void queueCommand(const QString &dir, const QString &command) = 0; + + /**@return Whether the application is currently running.*/ + virtual bool isRunning() = 0; + + /**Advices to synchronize the settings from KConfig because they were changed externally.*/ + virtual void updateSettingsFromConfig() = 0; + +signals: + /** + * Only emitted if the command was succesfully finished. + */ + void commandFinished(const QString &command); + + /** + * Emitted if a command failed. + */ + void commandFailed(const QString &command); +}; + +#endif diff --git a/lib/interfaces/extensions/kdevquickopen.h b/lib/interfaces/extensions/kdevquickopen.h new file mode 100644 index 00000000..3fd108c8 --- /dev/null +++ b/lib/interfaces/extensions/kdevquickopen.h @@ -0,0 +1,130 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVQUICKOPEN_H +#define KDEVQUICKOPEN_H + +#include <kdevplugin.h> +#include <kurl.h> + +/** +@file kdevquickopen.h +Source formatter interface. +*/ + +/** +Quick open plugin interface. + +Use it when you need to present a dialog to choose between files to open. +@code +KDevQuickOpen *qo = extension<KDevQuickOpen>("KDevelop/QuickOpen"); +if (qo) { + // do something +} else { + // fail +} +@endcode +@sa @ref KDevPlugin::extension method documentation. +@sa @ref whatisextension and @ref creatingextension sections of Platform API documentation. +*/ +class KDevQuickOpen : public KDevPlugin +{ +public: + /**Constructor. + @param info Important information about the plugin - plugin internal and generic + (GUI) name, description, a list of authors, etc. That information is used to show + plugin information in various places like "about application" dialog, plugin selector + dialog, etc. Plugin does not take ownership on info object, also its lifetime should + be equal to the lifetime of the plugin. + @param parent The parent object for the plugin. Parent object must implement @ref KDevApi + interface. Otherwise the plugin will not be constructed. + @param name The internal name which identifies the plugin.*/ + KDevQuickOpen(const KDevPluginInfo *info, QObject* parent, const char* name) + :KDevPlugin(info, parent, name) {} + + /**Shows the file selection dialog. + @param text A list of urls to open.*/ + virtual void quickOpenFile(const KURL::List urls) = 0; +}; + +#endif +/* This file is part of the KDE project + Copyright (C) 2007 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVQUICKOPEN_H +#define KDEVQUICKOPEN_H + +#include <kdevplugin.h> +#include <kurl.h> + +/** +@file kdevquickopen.h +Source formatter interface. +*/ + +/** +Quick open plugin interface. + +Use it when you need to present a dialog to choose between files to open. +@code +KDevQuickOpen *qo = extension<KDevQuickOpen>("KDevelop/QuickOpen"); +if (qo) { + // do something +} else { + // fail +} +@endcode +@sa @ref KDevPlugin::extension method documentation. +@sa @ref whatisextension and @ref creatingextension sections of Platform API documentation. +*/ +class KDevQuickOpen : public KDevPlugin +{ +public: + /**Constructor. + @param info Important information about the plugin - plugin internal and generic + (GUI) name, description, a list of authors, etc. That information is used to show + plugin information in various places like "about application" dialog, plugin selector + dialog, etc. Plugin does not take ownership on info object, also its lifetime should + be equal to the lifetime of the plugin. + @param parent The parent object for the plugin. Parent object must implement @ref KDevApi + interface. Otherwise the plugin will not be constructed. + @param name The internal name which identifies the plugin.*/ + KDevQuickOpen(const KDevPluginInfo *info, QObject* parent, const char* name) + :KDevPlugin(info, parent, name) {} + + /**Shows the file selection dialog. + @param text A list of urls to open.*/ + virtual void quickOpenFile(const KURL::List urls) = 0; +}; + +#endif diff --git a/lib/interfaces/extensions/kdevsourceformatter.h b/lib/interfaces/extensions/kdevsourceformatter.h new file mode 100644 index 00000000..82a9531f --- /dev/null +++ b/lib/interfaces/extensions/kdevsourceformatter.h @@ -0,0 +1,69 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVSOURCEFORMATTER_H +#define KDEVSOURCEFORMATTER_H + +#include <kdevplugin.h> + +/** +@file kdevsourceformatter.h +Source formatter interface. +*/ + +/** +Source formatter interface. +This interface is responsible for formatting source files and strings of code. + +Instances that implement this interface are available through extension architecture: +@code +KDevSourceFormatter *sf = extension<KDevSourceFormatter>("KDevelop/SourceFormatter"); +if (sf) { + // do something +} else { + // fail +} +@endcode +@sa @ref KDevPlugin::extension method documentation. +@sa @ref whatisextension and @ref creatingextension sections of Platform API documentation. +*/ +class KDevSourceFormatter : public KDevPlugin +{ +public: + /**Constructor. + @param info Important information about the plugin - plugin internal and generic + (GUI) name, description, a list of authors, etc. That information is used to show + plugin information in various places like "about application" dialog, plugin selector + dialog, etc. Plugin does not take ownership on info object, also its lifetime should + be equal to the lifetime of the plugin. + @param parent The parent object for the plugin. Parent object must implement @ref KDevApi + interface. Otherwise the plugin will not be constructed. + @param name The internal name which identifies the plugin.*/ + KDevSourceFormatter(const KDevPluginInfo *info, QObject* parent, const char* name) + :KDevPlugin(info, parent, name) {} + + /**Formats the source. + @param text A string with a code. + @return The formatted string.*/ + virtual QString formatSource(const QString text) = 0; + + /**@return The indentation string. For example, tab or four spaces can be returned.*/ + virtual QString indentString() const = 0; +}; + +#endif diff --git a/lib/interfaces/extensions/kdevversioncontrol.h b/lib/interfaces/extensions/kdevversioncontrol.h new file mode 100644 index 00000000..d6e69e08 --- /dev/null +++ b/lib/interfaces/extensions/kdevversioncontrol.h @@ -0,0 +1,237 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2002-2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2002 Simon Hausmann <hausmann@kde.org> + Copyright (C) 2003 Mario Scalas <mario.scalas@libero.it> + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVVERSIONCONTROL_H +#define KDEVVERSIONCONTROL_H + +#include <qobject.h> +#include <qstring.h> +#include <qwidget.h> +#include <qmap.h> +#include <qvaluelist.h> + +#include <kdevplugin.h> + +/** +@file kdevversioncontrol.h +Version control system interface and utility classes. +*/ + +/** +Info about file state in VCS. + +Used, for example in file views to display VCS related information about files. +*/ +struct VCSFileInfo +{ + /**State of the file.*/ + enum FileState { + Unknown /**<No VCS information about a file is known.*/, + Added /**<File was added to the repository but not commited.*/, + Uptodate /**<File was updated or it is already at up to date version.*/, + Modified /**<File was modified locally.*/, + Conflict /**<Local version conflicts with the one in a repository.*/, + Sticky /**<File is sticky.*/, + NeedsPatch /**<File needs a patch.*/, + NeedsCheckout /**<File needs to be checkout again.*/, + Directory /**<This is a directory.*/ , + Deleted /**<File or Directory is scheduled to be deleted. */ , + Replaced /**<File was scheduled for deletion, and then a new file with the same name was scheduled for addition in its place. */ + }; + + /**Constructor.*/ + VCSFileInfo() {} + /**Constructor. + @param fn The file name (without a path). + @param workRev The current working revision of a file. + @param repoRev The last revision of a file in the repository. + @param aState The state of a file.*/ + VCSFileInfo( QString fn, QString workRev, QString repoRev, FileState aState ) + : fileName(fn), workRevision(workRev), repoRevision(repoRev), state(aState) {} + + /**The file name.*/ + QString fileName; // Yeah, _just_ the file name ;-) + /**The working revision number.*/ + QString workRevision; + /**The repository revision number.*/ + QString repoRevision; + /**The state of a file.*/ + FileState state; + + /**@return A descriptive string with all VCS related info about the file.*/ + QString toString() const + { + return "(" + fileName + ", " + workRevision + ", " + repoRevision + ", " + state2String( state ) + ")"; + } + + /**@return A textual VCS state description.*/ + static QString state2String( FileState state ) + { + switch (state) + { + case Added: return "added"; + case Uptodate: return "up-to-date"; + case Modified: return "modified"; + case Conflict: return "conflict"; + case Sticky: return "sticky"; + case NeedsPatch: return "needs patch"; + case NeedsCheckout: return "needs check-out"; + case Directory: return "directory"; + case Deleted: return "deleted"; + case Replaced: return "replaced"; + case Unknown: + default: + return "unknown"; + } + } + +}; + +/**@class FileDom +Info for a bunch of files that got modified. +This is a type definition: @code QMap<QString,VCSFileInfo> VCSFileInfoMap; @endcode +*/ +typedef QMap<QString,VCSFileInfo> VCSFileInfoMap; + +class KDevVCSFileInfoProvider; + + +/** +KDevelop version control system interface. +This is the abstract base class which encapsulates everything +necessary for communicating with version control systems. +VCS support plugins should implement this interface. + +Instances that implement this interface are available through extension architecture: +@code +KDevVersionControl *vcs = extension<KDevVersionControl>("KDevelop/VersionControl"); +if (vcs) { + // do something +} else { + // fail +} +@endcode +@sa KDevPlugin::extension method documentation. +*/ +class KDevVersionControl: public KDevPlugin +{ + Q_OBJECT + +public: + /**Constructs a VCS plugin. + @param info Important information about the plugin - plugin internal and generic + (GUI) name, description, a list of authors, etc. That information is used to show + plugin information in various places like "about application" dialog, plugin selector + dialog, etc. Plugin does not take ownership on info object, also its lifetime should + be equal to the lifetime of the plugin. + @param parent The parent object for the plugin. Parent object must implement @ref KDevApi + interface. Otherwise the plugin will not be constructed. + @param name The internal name which identifies the plugin.*/ + KDevVersionControl(const KDevPluginInfo *info, QObject *parent, const char *name ) + :KDevPlugin(info, parent, name ) {} + + /**Creates a new project in the passed path @p dir. This should instantiate + VCS infrastructure and import a project into the VCS in that directory. + @param dir The absolute path to the directory where VCS infrastructure should be + created.*/ + virtual void createNewProject(const QString& dir) = 0; + + /**Fetches a module from remote repository, so it can be used for importing. + @return true in case of success.*/ + virtual bool fetchFromRepository() = 0; + + /**@return The file info provider for this version control (0 if none is available).*/ + virtual KDevVCSFileInfoProvider *fileInfoProvider() const = 0; + + /**Checks if the directory is valid for this version control (for example + CVS may check for the presence of "<dirPath>/CVS/" subdir and something else) + @param dirPath The absolute path of the directory. + @return true if the directory is valid for this version control + <b>warning</b>: this returns false by default.*/ + virtual bool isValidDirectory(const QString &dirPath) const = 0; + + +signals: + /**Emitted when the Version Control has finished importing a module from remote + repository + @param destinationDir The directory where the module has been fetched.*/ + void finishedFetching(QString destinationDir); + +}; + +/** +Basic interface for providing info on file registered in a version control repository repository. +*/ +class KDevVCSFileInfoProvider: public QObject +{ + Q_OBJECT +public: + /**Constructor. + @param parent The parent VCS plugin. + @param name The name of a provider object.*/ + KDevVCSFileInfoProvider(KDevVersionControl *parent, const char *name) + : QObject( parent, name ), m_owner(parent) {} + + /**Gets the status for local files in the specified directory: + the info are collected locally so they are necessarily in sync with the repository + + This is a <b>synchronous operation</b> (blocking). + @param dirPath The relative (to project dir) directory path to stat. + @return Status for all <b>registered</b> files.*/ + virtual const VCSFileInfoMap *status(const QString &dirPath) = 0; + + /**Starts a request for directory status to the remote repository. + Requests and answers are asynchronous. + + This is an <b>asynchronous operation for requesting data</b>, so + for obvious reasons: the caller must connect the statusReady() signal and + check for the return value of this method. + @param dirPath The (relative to project directory) directory which status you are asking for. + @param callerData The pointer to some data you want the provider will return + to you when it has done. + @param recursive If false, retrieve information only for dirPath's immediate children. + @param checkRepos If true, contact remote repository and augment repository's status. + If false, retrieve only for local modification information. + @return true if the request has been successfully started, false otherwise.*/ + virtual bool requestStatus( const QString &dirPath, void *callerData, bool recursive = true, bool checkRepos = true ) = 0; + +signals: + /**Emitted when the status request to remote repository has finished. + @param fileInfoMap The status for <b>registered in repository</b> files. + @param callerData The pointer to some data you want the provider will return + to you when it has done + @see requestStatus for to find out when this signal should be used.*/ + void statusReady(const VCSFileInfoMap &fileInfoMap, void *callerData); + +protected: + /**@return The version control which owns this provider.*/ + KDevVersionControl *owner() const { return m_owner; } + +private: + KDevVersionControl *m_owner; + +private: + KDevVCSFileInfoProvider( const KDevVCSFileInfoProvider & ); + KDevVCSFileInfoProvider &operator=( const KDevVCSFileInfoProvider & ); +}; + +#endif diff --git a/lib/interfaces/external/Mainpage.dox b/lib/interfaces/external/Mainpage.dox new file mode 100644 index 00000000..4618c3d5 --- /dev/null +++ b/lib/interfaces/external/Mainpage.dox @@ -0,0 +1,57 @@ +/** +@mainpage The %KInterfaceDesigner Library + +This library contains all %KInterfaceDesigner classes and interfaces which form the core of +GUI Designer integration framework. + +<b>Link with</b>: -lkinterfacedesigner + +<b>Include path</b>: -I\$(kde_includes)/kinterfacedesigner + +\section designerintegration Overview of GUI designer integration process +Each KPart that wants to act as a GUI Designer must implement @ref KInterfaceDesigner::Designer +interface. It defines necessary signals to communicate with an IDE and abstract virtual +functions to determine designer type. + +If a part which can "design" user interface files of a certain mimetype +implements this interface and sets itself as a default handler for that +mimetype then it becomes automatically integrated into KDevelop IDE. + +When a part is embedded into KDevelop shell, its signals (defined in @ref +KInterfaceDesigner::Designer interface): +@code + void addedFunction(DesignerType type, const QString &formName, Function function) + void removedFunction(DesignerType type, const QString &formName, Function function) + void editedFunction(DesignerType type, const QString &formName, Function oldFunction, Function function) + void editFunction(DesignerType type, const QString &formName, const QString &functionName) + void editSource(DesignerType type, const QString &formName); +@endcode +are connected to corresponding slots of KDevelop designer integration engine which can be implemented in KDevelop language support plugin. + +Each language support which wants to use integrated designer, must reimplement +@code +virtual KDevDesignerIntegration *KDevLanguageSupport::designer(KInterfaceDesigner::DesignerType type) +@endcode +method and return designer integration object. + +Convenience designer integration support library is available for programming language support +developers. With the convenience library writing %Qt designer integration for the language is a +trivial task. + + +\section kdevdesigner KDevelop Designer technical overview +KDevelop version >= 3.1 comes with a customized version (fork ;)) of %Qt Designer. It is called KDevelop Designer (KDevDesigner, kdevdesigner from the command line). KDevDesigner has some important differences: +- KDevDesigner provides a read/write KPart which can be embedded into any application which wants to edit .ui files. KDevelop IDE embeds KDevDesigner this way. +- KDevDesigner uses %KDE icons and dialogs and thus provides better integration with a system. +. + +It is safe to preview forms with some %KDE widgets from kdeui and kio libraries - KDevDesigner part is linked to those libraries so it will not crash under some circumstances. + +KDevDesigner will not create .ui.h files - this feature is completely disabled. Integrated KDevDesigner will use subclassing approach, standalone does not allow to enter code. + +\section other Other information + +@note It is technically possible to integrate not only %Qt Designer, but also, for example, Glade. Glade-3 can be compiled as a library and probably be embedded via XParts technology. + +*/ + diff --git a/lib/interfaces/external/Makefile.am b/lib/interfaces/external/Makefile.am new file mode 100644 index 00000000..bcad37b3 --- /dev/null +++ b/lib/interfaces/external/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES = $(all_includes) + +METASOURCES = AUTO + +kinterfacedesignerdir = $(includedir)/kinterfacedesigner +kinterfacedesigner_HEADERS = designer.h + +lib_LTLIBRARIES = libkinterfacedesigner.la +libkinterfacedesigner_la_LIBADD = $(LIB_QT) $(LIB_KPARTS) $(LIB_KDEUI) +libkinterfacedesigner_la_LDFLAGS = $(all_libraries) +libkinterfacedesigner_la_SOURCES = designer.cpp + +DOXYGEN_REFERENCES = dcop interfaces kdecore kdefx kdeui khtml kmdi kio kjs kparts kutils +DOXYGEN_PROJECTNAME = KInterfaceDesigner Library +include ../../../Doxyfile.am diff --git a/lib/interfaces/external/designer.cpp b/lib/interfaces/external/designer.cpp new file mode 100644 index 00000000..b7c5de7a --- /dev/null +++ b/lib/interfaces/external/designer.cpp @@ -0,0 +1,30 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Alexander Dymo <cloudtemple@mksat.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "designer.h" + +namespace KInterfaceDesigner{ + +Designer::Designer(QObject *parent, const char *name) + :KParts::ReadWritePart(parent, name) +{ +} + +} + +#include "designer.moc" diff --git a/lib/interfaces/external/designer.h b/lib/interfaces/external/designer.h new file mode 100644 index 00000000..5c7821fa --- /dev/null +++ b/lib/interfaces/external/designer.h @@ -0,0 +1,98 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Alexander Dymo <cloudtemple@mksat.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KINTERFACEDESIGNER_FORMEDITOR_H +#define KINTERFACEDESIGNER_FORMEDITOR_H + +#include <kparts/part.h> + +/**Contains KInterfaceDesigner classes.*/ +namespace KInterfaceDesigner{ + +/**The type of a GUI Designer.*/ +enum DesignerType { + QtDesigner /**<Qt Designer.*/, + Glade /**<Glade (version >= 3).*/ +}; + +/**Function type.*/ +enum FunctionType { + ftFunction /**<Simple function or a callback.*/, + ftQtSlot /**<Qt slot.*/ +}; + +/**Function.*/ +struct Function{ + /**Return type.*/ + QString returnType; + /**Function name.*/ + QString function; + /**Specifier, e.g. virtual, static, etc.*/ + QString specifier; + /**Access, e.g. private, protected, public, etc.*/ + QString access; + /**Function type.*/ + FunctionType type; +}; + +/** +GUI Designer Part Interface. +Each KPart that wants to act as a GUI Designer must implement this interface. +It defines necessary signals to communicate with an IDE and abstract virtual +functions to determine designer type. + +Parts that implement this interface must emit its signals when necessary. +See signals documentation for an explanation on when to emit those. + +If a part which can "design" user interface files of a certain mimetype +implements this interface and sets itself as a default handler for that +mimetype then it becomes automatically integrated into KDevelop IDE. +*/ +class Designer: public KParts::ReadWritePart{ + Q_OBJECT +public: + Designer(QObject *parent, const char *name); + + /**Reimplement this to be able to open projects.*/ + virtual void openProject(const QString &projectFile) = 0; + /**Reimplement this return the type of this designer.*/ + virtual DesignerType designerType() = 0; + +signals: + /**Emit this signal when a function was added by a designer. For example, when a slot + or a callback function was defined.*/ + void addedFunction(DesignerType type, const QString &formName, Function function); + /**Emit this signal when a function was removed by a designer.*/ + void removedFunction(DesignerType type, const QString &formName, Function function); + /**Emit this signal when a function signature was edited by a designer.*/ + void editedFunction(DesignerType type, const QString &formName, Function oldFunction, Function function); + + /**Emit this signal when a designer wants to open the editor with function definition.*/ + void editFunction(DesignerType type, const QString &formName, const QString &functionName); + /**Emit this signal when a designer wants to open the editor for a form sources.*/ + void editSource(DesignerType type, const QString &formName); + + /**Emitted when a form state is changed in the designer. + @param formName An absolute name of the form file. + @param status 0: form is clean, 1: form is modified.*/ + void newStatus(const QString &formName, int status); +}; + +} + +#endif diff --git a/lib/interfaces/extras/Mainpage.dox b/lib/interfaces/extras/Mainpage.dox new file mode 100644 index 00000000..f72dade5 --- /dev/null +++ b/lib/interfaces/extras/Mainpage.dox @@ -0,0 +1,11 @@ +/** +@mainpage The KDevelop Extra Interfaces Library + +This library contains extra interfaces that are not the part of KDevelop plugin architecture +but that can be implemented by extra plugins or "plugins for plugins". + +<b>Link with</b>: -lkdevextras + +<b>Include path</b>: -I\$(kde_includes)/kdevelop/interfaces/extras +*/ + diff --git a/lib/interfaces/extras/Makefile.am b/lib/interfaces/extras/Makefile.am new file mode 100644 index 00000000..b8b3e091 --- /dev/null +++ b/lib/interfaces/extras/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = -I$(top_srcdir)/lib/interfaces/external -I$(top_srcdir)/lib/util \ + $(all_includes) +METASOURCES = AUTO +libkdevextras_la_LIBADD = $(LIB_QT) +libkdevextras_la_LDFLAGS = $(all_libraries) +lib_LTLIBRARIES = libkdevextras.la +kdevelopincludedir = $(includedir)/kdevelop/interfaces/extras +servicetypedir = $(kde_servicetypesdir) + +kdevelopinclude_HEADERS = kdevcompileroptions.h kdevvcsintegrator.h +libkdevextras_la_SOURCES = kdevcompileroptions.cpp kdevvcsintegrator.cpp +servicetype_DATA = kdevelopcompileroptions.desktop \ + kdevelopvcsintegrator.desktop + +DOXYGEN_REFERENCES = dcop interfaces kdecore kdefx kdeui khtml kmdi kio kjs kparts kutils kdevinterfaces +DOXYGEN_PROJECTNAME = KDevelop Extra Interfaces Library +DOXYGEN_DOCDIRPREFIX = kdev +include ../../../Doxyfile.am diff --git a/lib/interfaces/extras/kdevcompileroptions.cpp b/lib/interfaces/extras/kdevcompileroptions.cpp new file mode 100644 index 00000000..5d87eec7 --- /dev/null +++ b/lib/interfaces/extras/kdevcompileroptions.cpp @@ -0,0 +1,6 @@ +#include "kdevcompileroptions.h" + +KDevCompilerOptions::KDevCompilerOptions( QObject * parent, const char * name ) + :QObject(parent, name) +{ +} diff --git a/lib/interfaces/extras/kdevcompileroptions.h b/lib/interfaces/extras/kdevcompileroptions.h new file mode 100644 index 00000000..268ce0af --- /dev/null +++ b/lib/interfaces/extras/kdevcompileroptions.h @@ -0,0 +1,90 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2002 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** +@file kdevcompileroptions.h +The interface to compiler options configuration. +*/ + +#ifndef _KDEVCOMPILEROPTIONS_H_ +#define _KDEVCOMPILEROPTIONS_H_ + +#include <qobject.h> + +/** +The interface to compiler options configuration. +Used by build systems to give users a compiler options configuration dialog. + +Common use case: +@code +static KDevCompilerOptions *createCompilerOptions( const QString &name, QObject *parent ) +{ + KService::Ptr service = KService::serviceByDesktopName( name ); + if ( !service ) + return 0; + + KLibFactory *factory = KLibLoader::self()->factory(QFile::encodeName(service->library())); + if (!factory) + return 0; + + QStringList args; + QVariant prop = service->property("X-KDevelop-Args"); + if (prop.isValid()) + args = QStringList::split(" ", prop.toString()); + + QObject *obj = factory->create(parent, service->name().latin1(), + "KDevCompilerOptions", args); + + if (!obj->inherits("KDevCompilerOptions")) + return 0; + + KDevCompilerOptions *dlg = (KDevCompilerOptions*) obj; + return dlg; +} + +... +KDevCompilerOptions *plugin = createCompilerOptions(compilerName, parent); +QString flags = ""; //old compiler flags +if ( plugin ) +{ + flags = plugin->exec( parent, flags ); //new compiler flags are returned + delete plugin; +} +@endcode +*/ +class KDevCompilerOptions : public QObject +{ + Q_OBJECT + +public: + KDevCompilerOptions( QObject *parent=0, const char *name=0 ); + + /** + * Opens a dialog which allows the user to configure the + * compiler options. The initial settings in the dialog + * will be set from the flags argument of this method. + * After the dialog is accepted, the new settings will + * be returned as a string. If the dialog was cancelled, + * QString::null is returned. + */ + virtual QString exec(QWidget *parent, const QString &flags) = 0; +}; + +#endif diff --git a/lib/interfaces/extras/kdevelopcompileroptions.desktop b/lib/interfaces/extras/kdevelopcompileroptions.desktop new file mode 100644 index 00000000..7ed1df67 --- /dev/null +++ b/lib/interfaces/extras/kdevelopcompileroptions.desktop @@ -0,0 +1,48 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KDevelop/CompilerOptions +X-KDE-Derived=KDevelop/Plugin +Name=KDevelop Compiler Options Dialog Interface +Name[ca]=Interfície del diàleg d'opcions del compilador per a KDevelop +Name[da]=KDevelop grænseflade til oversætterindstillinger +Name[de]=Schnittstelle für Compiler-Einstellungsdialoge (KDevelop) +Name[el]=Διασύνδεση διαλόγου επιλογών μεταγλωττιστή KDevelop +Name[en_GB]=KDevelop Compiler Options Dialogue Interface +Name[es]=Interfaz del diálogo de opciones del compilador de KDevelop +Name[et]=KDevelopi kompilaatori valikute dialoogiliides +Name[eu]=KDevelop-en konpiladore aukeren elkarrizketa-koadro interfazea +Name[fa]=واسط محاورۀ گزینههای مترجم KDevelop +Name[fr]=Interface de la boîte de dialogue des options du compilateur de KDevelop +Name[gl]=Interface do diálogo de opcións de compilación de KDevelop +Name[hi]=के-डेवलप कम्पायलर विकल्प संवाद इंटरफ़ेस +Name[hu]=A KDevelop fordítási opcióinak párbeszédablaka +Name[it]=Interfaccia KDevelop per le opzioni di compilazione +Name[ja]=KDevelop コンパイラオプションダイアログ インターフェース +Name[nds]=KDevelop-Dialoogkoppelsteed för Kompilerer-Optschonen +Name[ne]=केडीई विकास कम्पाइलर विकल्प संवाद इन्टरफेस +Name[nl]=KDevelop Compileroptiesdialoog-interface +Name[pl]=Interfejs KDevelopa do okna dialogowego opcji kompilatora +Name[pt]=Interface da Janela de Opções do Compilador do KDevelop +Name[pt_BR]=Interface de Diálogo de Opções do Compilador do KDevelop +Name[ru]=Интерфейс диалога опций компилятора +Name[sk]=KDevelop rozhranie pre možnosti kompilátora +Name[sl]=Vmesnik za možnosti prevajanja v KDevelopu +Name[sr]=KDevelop-ов интерфејс дијалога „Опције преводиоца“ +Name[sr@Latn]=KDevelop-ov interfejs dijaloga „Opcije prevodioca“ +Name[sv]=KDevelop dialoggränssnitt för kompilatoralternativ +Name[ta]=கெடெவலப் தொகுப்பித் தேர்வுகள் உரை இடைமுகம் +Name[tg]=Гуфтугуи интерфейси талфифгари интихоб +Name[tr]=KDevelop Derleyici Seçenekleri Pencere Arayüzü +Name[zh_CN]=KDevelop编译器选项对话框接口 +Name[zh_TW]=KDevelop 編譯器選項對話框介面 + +[PropertyDef::X-KDevelop-Language] +Type=QString + +[PropertyDef::X-KDevelop-Default] +Type=bool + +# versioning - prevent DLL hell +[PropertyDef::X-KDevelop-Version] +Type=int + diff --git a/lib/interfaces/extras/kdevelopvcsintegrator.desktop b/lib/interfaces/extras/kdevelopvcsintegrator.desktop new file mode 100644 index 00000000..141bf189 --- /dev/null +++ b/lib/interfaces/extras/kdevelopvcsintegrator.desktop @@ -0,0 +1,45 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KDevelop/VCSIntegrator +X-KDE-Derived=KDevelop/Plugin +Name=KDevelop VCS Integrator +Name[ca]=Integrador VCS per a KDevelop +Name[da]=KDevelop VCS-importør +Name[de]=VCS-Integration für KDevelop +Name[el]=Ενσωματωτής VCS του KDevelop +Name[es]=Integrador VCS de KDevelop +Name[et]=KDevelopi VCS põimija +Name[eu]=KDevelop-en VCS integratzailea +Name[fa]=مجتمعساز KDevelop VCS +Name[fr]=Intégrateur VCS de KDevelop +Name[gl]=Integrador de VCS de KDevelop +Name[hu]=KDevelop VCS-integráló +Name[it]=Integratore VCS di KDevelop +Name[ja]=KDevelop VCS インテグレータ +Name[nds]=VKS-Integreren för KDevelop +Name[ne]=KDevelop VCS इन्टिगेटर +Name[pl]=KDevelop: integracja z VCS +Name[pt]=Importador de VCS do KDevelop +Name[pt_BR]=Integrador VCS para o KDevelop +Name[ru]=Модуль работы с системами контроля версий для KDevelop +Name[sk]=Kdevelop VCS integrácia +Name[sr]=KDevelop-ов VCS интегратор +Name[sr@Latn]=KDevelop-ov VCS integrator +Name[sv]=KDevelop VCS-import +Name[tr]=KDevelop VCS Bütünleyicisi +Name[zh_CN]=KDevelop VCS 集成器 +Name[zh_TW]=KDevelop VCS 整合器 + +[PropertyDef::X-KDevelop-VCS] +Type=QString + +[PropertyDef::X-KDevelop-VCSPlugin] +Type=QString + +[PropertyDef::X-KDevelop-Default] +Type=bool + +# versioning - prevent DLL hell +[PropertyDef::X-KDevelop-Version] +Type=int + diff --git a/lib/interfaces/extras/kdevvcsintegrator.cpp b/lib/interfaces/extras/kdevvcsintegrator.cpp new file mode 100644 index 00000000..d231030a --- /dev/null +++ b/lib/interfaces/extras/kdevvcsintegrator.cpp @@ -0,0 +1,26 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "kdevvcsintegrator.h" + +KDevVCSIntegrator::KDevVCSIntegrator(QObject *parent, const char *name) + :QObject(parent, name) +{ +} + +#include "kdevvcsintegrator.moc" diff --git a/lib/interfaces/extras/kdevvcsintegrator.h b/lib/interfaces/extras/kdevvcsintegrator.h new file mode 100644 index 00000000..d180e55f --- /dev/null +++ b/lib/interfaces/extras/kdevvcsintegrator.h @@ -0,0 +1,76 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVVCSINTEGRATOR_H +#define KDEVVCSINTEGRATOR_H + +#include <qobject.h> + +/** +@file kdevvcsintegrator.h +The interface to VCS integrators. +*/ + +class QDomDocument; +class QWidget; + +/** +VCS Integration Dialog. + +Usually it is created as: +@code +class MyVCSDialog: public QWidget, public VCSDialog { + MyVCSDialog(QWidget *parent = 0, const char *name = 0); + virtual void accept() { ... } + virtual void init(const QString &projectName, const QString &projectLocation) { ... } + virtual QWidget *self() { + return const_cast<MyVCSDialog*>(this); + } +} +@endcode +*/ +class VCSDialog { +public: + VCSDialog() { } + /**Implement all integration actions here. Do not use QDialog::accept method + to perform integration actions.*/ + virtual void accept() = 0; + /**Init integration dialog with the project name and location.*/ + virtual void init(const QString &projectName, const QString &projectLocation) = 0; + /**Reimplement to return an actual integration widget. Use QWidgets for that, not + QDialogs because integrator dialogs are usually have parent containers.*/ + virtual QWidget *self() = 0; +}; + +/** +The interface to VCS integrators. +VCS integrator takes care about setting up VCS for new and existing projects. +It can, for example, perform checkout or import operations. +*/ +class KDevVCSIntegrator: public QObject { + Q_OBJECT +public: + KDevVCSIntegrator(QObject *parent = 0, const char *name = 0); + + /**Reimplement to return a dialog to fetch the project from VCS.*/ + virtual VCSDialog *fetcher(QWidget *parent) = 0; + /**Reimplement to return a dialog to integrate the project into VCS.*/ + virtual VCSDialog *integrator(QWidget *parent) = 0; +}; + +#endif diff --git a/lib/interfaces/hashedstring.cpp b/lib/interfaces/hashedstring.cpp new file mode 100644 index 00000000..38fdf4a9 --- /dev/null +++ b/lib/interfaces/hashedstring.cpp @@ -0,0 +1,362 @@ +/*************************************************************************** + copyright : (C) 2006 by David Nolden + email : david.nolden.kdevelop@art-master.de +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "hashedstring.h" +#include <kdatastream.h> +#include <sstream> +#include <algorithm> +#include <iterator> +#include<ext/hash_set> +#include<set> +#include<algorithm> + +//It needs to be measured whether this flag should be turned on or off. It seems just to move the complexity from one position to the other, without any variant being really better. +#define USE_HASHMAP + +size_t fastHashString( const QString& str ); + +size_t hashStringSafe( const QString& str ) { + size_t hash = 0; + int len = str.length(); + for( int a = 0; a < len; a++ ) { + hash = str[a].unicode() + (hash * 17); + } + return hash; +} + +size_t HashedString::hashString( const QString& str ) +{ + return fastHashString( str ); +} + +size_t fastHashString( const QString& str ) { + size_t hash = 0; + if( !str.isEmpty() ) { + const QChar* curr = str.unicode(); + const QChar* end = curr + str.length(); + QChar c; + for(; curr < end ;) { + c = *curr; + hash = c.unicode() + ( hash * 17 ); + ++curr; + } + } + return hash; +} + +void HashedString::initHash() { + m_hash = hashString( m_str ); +} + + +class HashedStringSetData : public KShared { + public: +#ifdef USE_HASHMAP + typedef __gnu_cxx::hash_set<HashedString> StringSet; +#else + typedef std::set<HashedString> StringSet; //must be a set, so the set-algorithms work +#endif + StringSet m_files; + mutable bool m_hashValid; + mutable size_t m_hash; + HashedStringSetData() : m_hashValid( false ) { + } + inline void invalidateHash() { + m_hashValid = false; + } + + void computeHash() const; +}; + +void HashedStringSetData::computeHash() const { + int num = 1; + m_hash = 0; + for( StringSet::const_iterator it = m_files.begin(); it != m_files.end(); ++it ) { + num *= 7; + m_hash += num * (*it).hash(); + } + m_hashValid = true; +} + +HashedStringSet::HashedStringSet() {} + +HashedStringSet::~HashedStringSet() {} + +HashedStringSet::HashedStringSet( const HashedString& file ) { + insert( file ); +} + +HashedStringSet::HashedStringSet( const HashedStringSet& rhs ) : m_data( rhs.m_data ) {} + +HashedStringSet operator + ( const HashedStringSet& lhs, const HashedStringSet& rhs ) { + HashedStringSet ret = lhs; + ret += rhs; + + return ret; +} + +int HashedStringSet::size() const { + if( !m_data ) return 0; + return m_data->m_files.size(); +} + +HashedStringSet& HashedStringSet::operator = ( const HashedStringSet& rhs ) { + m_data = rhs.m_data; + return *this; +} + +HashedStringSet& HashedStringSet::operator +=( const HashedStringSet& rhs ) { + if ( !rhs.m_data ) + return * this; + +#ifndef USE_HASHMAP + KSharedPtr<HashedStringSetData> oldData = m_data; + if( !oldData ) oldData = new HashedStringSetData(); + m_data = new HashedStringSetData(); + std::set_union( oldData->m_files.begin(), oldData->m_files.end(), rhs.m_data->m_files.begin(), rhs.m_data->m_files.end(), std::insert_iterator<HashedStringSetData::StringSet>( m_data->m_files, m_data->m_files.end() ) ); +#else + makeDataPrivate(); + m_data->m_files.insert( rhs.m_data->m_files.begin(), rhs.m_data->m_files.end() ); + /*HashedStringSetData::StringSet::const_iterator end = rhs.m_data->m_files.end(); + HashedStringSetData::StringSet& mySet( m_data->m_files ); + for( HashedStringSetData::StringSet::const_iterator it = rhs.m_data->m_files.begin(); it != end; ++it ) { + mySet.insert( *it ); + }*/ + +#endif + return *this; +} + +HashedStringSet& HashedStringSet::operator -=( const HashedStringSet& rhs ) { + if( !m_data ) return *this; + if( !rhs.m_data ) return *this; +#ifndef USE_HASHMAP + KSharedPtr<HashedStringSetData> oldData = m_data; + m_data = new HashedStringSetData(); + std::set_difference( oldData->m_files.begin(), oldData->m_files.end(), rhs.m_data->m_files.begin(), rhs.m_data->m_files.end(), std::insert_iterator<HashedStringSetData::StringSet>( m_data->m_files, m_data->m_files.end() ) ); +#else + makeDataPrivate(); + HashedStringSetData::StringSet::const_iterator end = rhs.m_data->m_files.end(); + HashedStringSetData::StringSet::const_iterator myEnd = m_data->m_files.end(); + HashedStringSetData::StringSet& mySet( m_data->m_files ); + for( HashedStringSetData::StringSet::const_iterator it = rhs.m_data->m_files.begin(); it != end; ++it ) { + mySet.erase( *it ); + } + +#endif + return *this; +} + + +void HashedStringSet::makeDataPrivate() { + if ( !m_data ) { + m_data = new HashedStringSetData(); + return ; + } + if ( m_data->_KShared_count() != 1 ) + m_data = new HashedStringSetData( *m_data ); +} + +bool HashedStringSet::operator[] ( const HashedString& rhs ) const { + //if ( rhs.str() == "*" ) + //return true; /// * stands for "any file" + if ( !m_data ) + return false; + return m_data->m_files.find( rhs ) != m_data->m_files.end(); +} + +void HashedStringSet::insert( const HashedString& str ) { + if( str.str().isEmpty() ) return; + makeDataPrivate(); + m_data->m_files.insert( str ); + m_data->invalidateHash(); +} + +bool HashedStringSet::operator <= ( const HashedStringSet& rhs ) const { + if ( !m_data ) + return true; + if ( m_data->m_files.empty() ) + return true; + if ( !rhs.m_data ) + return false; +#ifndef USE_HASHMAP + return std::includes( rhs.m_data->m_files.begin(), rhs.m_data->m_files.end(), m_data->m_files.begin(), m_data->m_files.end() ); +#else + const HashedStringSetData::StringSet& otherSet( rhs.m_data->m_files ); + HashedStringSetData::StringSet::const_iterator end = rhs.m_data->m_files.end(); + HashedStringSetData::StringSet::const_iterator myEnd = m_data->m_files.end(); + + for( HashedStringSetData::StringSet::const_iterator it = m_data->m_files.begin(); it != myEnd; ++it ) { + HashedStringSetData::StringSet::const_iterator i = otherSet.find( *it ); + if( i == end ) return false; + } + return true; +#endif +} + +bool HashedStringSet::operator == ( const HashedStringSet& rhs ) const { + if( hash() != rhs.hash() ) return false; + + bool empty1 = false; + if ( !m_data ) + empty1 = true; + else if ( m_data->m_files.empty() ) + empty1 = true; + bool empty2 = false; + if ( !rhs.m_data ) + empty2 = true; + else if ( rhs.m_data->m_files.empty() ) + empty2 = true; + + if ( empty1 && empty2 ) + return true; + if ( empty1 || empty2 ) + return false; + + return m_data->m_files == rhs.m_data->m_files; +} + +size_t HashedStringSet::hash() const { + if( !m_data ) return 0; + if( !m_data->m_hashValid ) m_data->computeHash(); + return m_data->m_hash; +} + +void HashedStringSet::read( QDataStream& stream ) { + bool b; + stream >> b; + if( b ) { + m_data = new HashedStringSetData(); + int cnt; + stream >> cnt; + HashedString s; + for( int a = 0; a < cnt; a++ ) { + stream >> s; + m_data->m_files.insert( s ); + } + } else { + m_data = 0; + } +} + +void HashedStringSet::write( QDataStream& stream ) const { + bool b = m_data; + stream << b; + if( b ) { + int cnt = m_data->m_files.size(); + stream << cnt; + for( HashedStringSetData::StringSet::const_iterator it = m_data->m_files.begin(); it != m_data->m_files.end(); ++it ) { + stream << *it; + } + } +} + +std::string HashedStringSet::print() const { + std::ostringstream s; + if( m_data ) { + for( HashedStringSetData::StringSet::const_iterator it = m_data->m_files.begin(); it != m_data->m_files.end(); ++it ) { + s << (*it).str().ascii() << "\n"; + } + } + return s.str(); +} + +QDataStream& operator << ( QDataStream& stream, const HashedString& str ) { + stream << str.m_str; + stream << str.m_hash; + return stream; +} + +QDataStream& operator >> ( QDataStream& stream, HashedString& str ) { + stream >> str.m_str; + stream >> str.m_hash; + return stream; +} + +void HashedStringSetGroup::addSet( size_t id, const HashedStringSet& set ) { + if( set.m_data && !set.m_data->m_files.empty() ) { + m_sizeMap[ id ] = set.size(); + for( HashedStringSetData::StringSet::const_iterator it = set.m_data->m_files.begin(); it != set.m_data->m_files.end(); ++it ) { + GroupMap::iterator itr = m_map.find( *it ); + if( itr == m_map.end() ) { + itr = m_map.insert( std::make_pair( *it, ItemSet() ) ).first; + } + itr->second.insert( id ); + } + } else { + m_global.insert( id ); + } +} + +void HashedStringSetGroup::disableSet( size_t id ) { + m_disabled.insert( id ); +} + +void HashedStringSetGroup::enableSet( size_t id ) { + m_disabled.erase( id ); +} + +bool HashedStringSetGroup::isDisabled( size_t id ) const { + return m_disabled.find( id ) != m_disabled.end(); +} + +void HashedStringSetGroup::removeSet( size_t id ) { + m_disabled.erase( id ); + m_global.erase( id ); + m_sizeMap.erase( id ); + for( GroupMap::iterator it = m_map.begin(); it != m_map.end(); ++it ) { + it->second.erase( id ); + } +} + +void HashedStringSetGroup::findGroups( HashedStringSet strings, ItemSet& target ) const { + target.clear(); + if( !strings.m_data ) { + std::set_difference( m_global.begin(), m_global.end(), m_disabled.begin(), m_disabled.end(), std::insert_iterator<ItemSet>( target, target.end() ) ); + return; + } + //This might yet be optimized by sorting the sets according to their size, and starting the intersectioning with the smallest ones. + __gnu_cxx::hash_map<size_t, int> hitCounts; + + for( HashedStringSetData::StringSet::const_iterator it = strings.m_data->m_files.begin(); it != strings.m_data->m_files.end(); ++it ) { + GroupMap::const_iterator itr = m_map.find( *it ); + if( itr == m_map.end() ) { + //There are no string-sets that include the currently searched for string + continue; + } + + for( ItemSet::const_iterator it2 = itr->second.begin(); it2 != itr->second.end(); ++it2 ) { + __gnu_cxx::hash_map<size_t, int>::iterator v = hitCounts.find( *it2 ); + if( v != hitCounts.end() ) { + ++(*v).second; + } else { + hitCounts[*it2] = 1; + } + } + } + + //Now count together all groups that are completely within the given string-set(their hitCount equals their size) + ItemSet found; + for( __gnu_cxx::hash_map<size_t, int>::const_iterator it = hitCounts.begin(); it != hitCounts.end(); ++it ) { + if( (*it).second == (*m_sizeMap.find( (*it).first )).second ) + found.insert( (*it).first ); + } + + + std::set_union( found.begin(), found.end(), m_global.begin(), m_global.end(), std::insert_iterator<ItemSet>( target, target.end() ) ); + + target.swap( found ); + target.clear(); + std::set_difference( found.begin(), found.end(), m_disabled.begin(), m_disabled.end(), std::insert_iterator<ItemSet>( target, target.end() ) ); +} diff --git a/lib/interfaces/hashedstring.h b/lib/interfaces/hashedstring.h new file mode 100644 index 00000000..e62ab2e3 --- /dev/null +++ b/lib/interfaces/hashedstring.h @@ -0,0 +1,155 @@ +/*************************************************************************** + copyright : (C) 2006 by David Nolden + email : david.nolden.kdevelop@art-master.de +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef HASHED_STRING_H +#define HASHED_STRING_H + +#include<qstring.h> +#include<qdatastream.h> +#include<ksharedptr.h> +#include<set> +#include <ext/hash_map> +#include <string> + +///A simple class that stores a string together with it's appropriate hash-key +class HashedString { + public: + HashedString() : m_hash( 0 ) {} + + HashedString( const QString& str ) : m_str( str ) { + initHash(); + } + + HashedString( const char* str ) : m_str( str ) { + initHash(); + } + + inline size_t hash() const { + return m_hash; + } + + QString str() const { + return m_str; + } + + bool operator == ( const HashedString& rhs ) const { + if ( m_hash != rhs.m_hash ) + return false; + return m_str == rhs.m_str; + } + + ///Does not compare alphabetically, uses the hash-key for ordering. + bool operator < ( const HashedString& rhs ) const { + if ( m_hash < rhs.m_hash ) + return true; + if ( m_hash == rhs.m_hash ) + return m_str < rhs.m_str; + return false; + } + + static size_t hashString( const QString& str ); + + private: + void initHash(); + + QString m_str; + size_t m_hash; + + friend QDataStream& operator << ( QDataStream& stream, const HashedString& str ); + friend QDataStream& operator >> ( QDataStream& stream, HashedString& str ); +}; + +QDataStream& operator << ( QDataStream& stream, const HashedString& str ); + +QDataStream& operator >> ( QDataStream& stream, HashedString& str ); + +class HashedStringSetData; +class HashedStringSetGroup; + +///This is a reference-counting string-set optimized for fast lookup of hashed strings +class HashedStringSet { + public: + HashedStringSet(); + + ~HashedStringSet(); + + ///Constructs a string-set from one single file + HashedStringSet( const HashedString& file ); + + HashedStringSet( const HashedStringSet& rhs ); + + int size() const; + + HashedStringSet& operator = ( const HashedStringSet& rhs ); + ///@return whether the given file-name was included + bool operator[] ( const HashedString& rhs ) const; + + void insert( const HashedString& str ); + + HashedStringSet& operator +=( const HashedStringSet& ); + + HashedStringSet& operator -=( const HashedStringSet& ); + + ///intersection-test + ///Returns true if all files that are part of this set are also part of the given set + bool operator <= ( const HashedStringSet& rhs ) const; + + bool operator == ( const HashedStringSet& rhs ) const; + + void read( QDataStream& stream ); + void write( QDataStream& stream ) const; + + std::string print() const; + + size_t hash() const; + private: + friend class HashedStringSetGroup; + void makeDataPrivate(); + KSharedPtr<HashedStringSetData> m_data; //this implies some additional cost because KShared's destructor is virtual. Maybe change that by copying KShared without the virtual destructor. + friend HashedStringSet operator + ( const HashedStringSet& lhs, const HashedStringSet& rhs ); +}; + +HashedStringSet operator + ( const HashedStringSet& lhs, const HashedStringSet& rhs ); + +namespace __gnu_cxx { +template<> +struct hash<HashedString> { + size_t operator () ( const HashedString& str ) const { + return str.hash(); + } +}; +} + +///Used to find all registered HashedStringSet's that contain all strings given to findGroups(..) +class HashedStringSetGroup { + public: + typedef std::set<size_t> ItemSet; + void addSet( size_t id, const HashedStringSet& set ); + void enableSet( size_t id ); + bool isDisabled( size_t id ) const; + void disableSet( size_t id ); + void removeSet( size_t id ); + + //Writes the ids of all registered and not disabled HashedStringSet's that are completely included in the given HashedStringSet efficiently) + void findGroups( HashedStringSet strings, ItemSet& target ) const; + + private: + typedef __gnu_cxx::hash_map<HashedString, ItemSet> GroupMap; + typedef __gnu_cxx::hash_map<size_t, size_t> SizeMap; + GroupMap m_map; + SizeMap m_sizeMap; + ItemSet m_disabled; + ItemSet m_global; +}; +#endif diff --git a/lib/interfaces/katedocumentmanagerinterface.cpp b/lib/interfaces/katedocumentmanagerinterface.cpp new file mode 100644 index 00000000..3ad76dd7 --- /dev/null +++ b/lib/interfaces/katedocumentmanagerinterface.cpp @@ -0,0 +1,104 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Ian Reinhart Geiser <geiser@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "katedocumentmanagerinterface.h" +#include "kdevpartcontroller.h" +#include <kurl.h> +#include <dcopclient.h> +#include <kapplication.h> +#include <dcopref.h> +#include <kate/document.h> +#include <kurl.h> +#include <kdebug.h> + +KateDocumentManagerInterface::KateDocumentManagerInterface( KDevPartController *pc) + : QObject(pc), DCOPObject("KateDocumentManager"), m_controller(pc) +{ + +} + + +KateDocumentManagerInterface::~KateDocumentManagerInterface() {} + + + +DCOPRef KateDocumentManagerInterface::activeDocument( ) +{ + return documentWithID(activeDocumentNumber()); +} + +DCOPRef KateDocumentManagerInterface::document( uint n ) +{ + return documentWithID(n); +} + +DCOPRef KateDocumentManagerInterface::documentWithID( uint id ) +{ + QString dcopobj = "KateDocument#" + QString::number(id); + return DCOPRef(kapp->dcopClient()->appId(), dcopobj.latin1() ); +} + +DCOPRef KateDocumentManagerInterface::openURL( const KURL &url, const QString &encoding ) +{ + m_controller->editDocument(url); + m_controller->setEncoding(encoding); + int idx = findDocument( url ); + return documentWithID(idx); +} + +bool KateDocumentManagerInterface::closeAllDocuments( ) +{ + return m_controller->closeAllFiles(); +} + +bool KateDocumentManagerInterface::closeDocument( uint n ) +{ + return false; +} + +bool KateDocumentManagerInterface::isOpen( const KURL &url ) +{ + return (m_controller->partForURL(url) != 0L); +} + +int KateDocumentManagerInterface::findDocument( const KURL &url ) +{ + KTextEditor::Document *doc = dynamic_cast<KTextEditor::Document*>(m_controller->partForURL(url)); + if( doc ) + return doc->documentNumber(); + else + return 0L; +} + +uint KateDocumentManagerInterface::activeDocumentNumber( ) +{ + KTextEditor::Document *doc = dynamic_cast<KTextEditor::Document*>(m_controller->activePart()); + if( doc ) + { + return doc->documentNumber(); + } + else + return 0; +} + +uint KateDocumentManagerInterface::documents( ) +{ + return m_controller->openURLs().count(); +} + +#include "katedocumentmanagerinterface.moc" diff --git a/lib/interfaces/katedocumentmanagerinterface.h b/lib/interfaces/katedocumentmanagerinterface.h new file mode 100644 index 00000000..f6adfd4f --- /dev/null +++ b/lib/interfaces/katedocumentmanagerinterface.h @@ -0,0 +1,58 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Ian Reinhart Geiser <geiser@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KATEDOCUMENTMANAGERINTERFACE_H +#define KATEDOCUMENTMANAGERINTERFACE_H + +#include <qobject.h> +#include <dcopobject.h> +#include <dcopref.h> +#include <kurl.h> +class KDevPartController; + +/** +This is an emulated interface to provide compatibility with Kate scripts. + +@author KDevelop Authors +*/ +class KateDocumentManagerInterface : public QObject, public DCOPObject { + + Q_OBJECT + K_DCOP +public: + KateDocumentManagerInterface( KDevPartController *pc ); + + ~KateDocumentManagerInterface(); + +k_dcop: + DCOPRef activeDocument(); + DCOPRef document( uint n ); + DCOPRef documentWithID( uint id ); + DCOPRef openURL( const KURL &url, const QString &encoding ); + bool closeAllDocuments(); + bool closeDocument( uint n ); + bool isOpen( const KURL &url ); + int findDocument( const KURL &url ); + uint activeDocumentNumber(); + uint documents(); +private: + + KDevPartController *m_controller; +}; + +#endif diff --git a/lib/interfaces/kdevapi.cpp b/lib/interfaces/kdevapi.cpp new file mode 100644 index 00000000..fb178683 --- /dev/null +++ b/lib/interfaces/kdevapi.cpp @@ -0,0 +1,93 @@ +/* This file is part of the KDE project + Copyright (C) 2000-2001 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "kdevapi.h" + +#include "kdevcoderepository.h" + +/////////////////////////////////////////////////////////////////////////////// +// class KDevApi::Private +/////////////////////////////////////////////////////////////////////////////// + +class KDevApi::Private +{ +public: + Private() + : m_projectDom(0), m_project(0), m_languageSupport(0), + m_codeRepository(0) + {} + + QDomDocument *m_projectDom; + KDevProject *m_project; + KDevLanguageSupport *m_languageSupport; + KDevCodeRepository* m_codeRepository; +}; + +/////////////////////////////////////////////////////////////////////////////// +// class KDevApi +/////////////////////////////////////////////////////////////////////////////// + +KDevApi::KDevApi() +{ + d = new KDevApi::Private; + d->m_codeRepository = new KDevCodeRepository(); +} + +KDevApi::~KDevApi() +{ + delete d->m_codeRepository; + delete d; +} + +KDevProject *KDevApi::project() const +{ + return d->m_project; +} + +void KDevApi::setProject(KDevProject *project) +{ + d->m_project = project; +} + +KDevLanguageSupport *KDevApi::languageSupport() const +{ + return d->m_languageSupport; +} + +void KDevApi::setLanguageSupport(KDevLanguageSupport *languageSupport) +{ + d->m_languageSupport = languageSupport; +} + +QDomDocument *KDevApi::projectDom() const +{ + return d->m_projectDom; +} + +void KDevApi::setProjectDom(QDomDocument *dom) +{ + d->m_projectDom = dom; +} + +KDevCodeRepository *KDevApi::codeRepository() const +{ + return d->m_codeRepository; +} + +#include "kdevapi.moc" diff --git a/lib/interfaces/kdevapi.h b/lib/interfaces/kdevapi.h new file mode 100644 index 00000000..1b6115b0 --- /dev/null +++ b/lib/interfaces/kdevapi.h @@ -0,0 +1,103 @@ +/* This file is part of the KDE project + Copyright (C) 2000-2001 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVAPI_H +#define KDEVAPI_H + +#include <qobject.h> + +class QStringList; +class QDomDocument; +class KDevCore; +class KDevProject; +class KDevLanguageSupport; +class CodeModel; +class KDevPartController; +class KDevMainWindow; +class KDevCodeRepository; +class KDevPlugin; +class KDevPluginController; + +/** +@file kdevapi.h +KDevelop API interface. +*/ + +/** +The interface to KDevelop's core components. +Needs to be implemented in a shell. Developers do not need to use this +class because @ref KDevPlugin already provides API convenience methods. +*/ +class KDevApi: public QObject +{ + Q_OBJECT +public: + /**Constructor.*/ + KDevApi(); + + /**Destructor.*/ + virtual ~KDevApi(); + + /**@return A reference to the toplevel widget.*/ + virtual KDevMainWindow *mainWindow() const = 0; + + /**@return A reference to the part controller which is used to manipulate loaded KParts.*/ + virtual KDevPartController *partController() const = 0; + + /**@return A reference to the plugin controller which is used to manipulate loaded plugin.*/ + virtual KDevPluginController *pluginController() const = 0; + + /**@return A reference to the application core - an object which provides + basic functionalities for inter-parts communications / cooperation.*/ + virtual KDevCore *core() const = 0; + + /**@return A reference to the memory symbol store.*/ + virtual CodeModel *codeModel() const = 0; + + /**@return A reference to the DOM tree that represents the project file or 0 if no project is loaded.*/ + QDomDocument *projectDom() const; + + /**Sets the Document Object Model for the current project. + @param dom The project DOM.*/ + void setProjectDom(QDomDocument *dom); + + /**@return A reference to the current project component or 0 if no project is loaded.*/ + KDevProject *project() const; + + /**Sets the current project. + @param project The project plugin which becames the current project.*/ + void setProject(KDevProject *project); + + /**@return A reference to the language support component or 0 if no support available.*/ + KDevLanguageSupport *languageSupport() const; + + /**Sets the object charged of providing handling for the source files written in particular + language (languages support component). + @param languageSupport The language support plugin.*/ + void setLanguageSupport(KDevLanguageSupport *languageSupport); + + /**@return A reference to the code repository (accessor to persistent symbol stores).*/ + KDevCodeRepository *codeRepository() const; + +private: + class Private; + Private *d; +}; + +#endif diff --git a/lib/interfaces/kdevcoderepository.cpp b/lib/interfaces/kdevcoderepository.cpp new file mode 100644 index 00000000..81480940 --- /dev/null +++ b/lib/interfaces/kdevcoderepository.cpp @@ -0,0 +1,71 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "kdevcoderepository.h" + +struct KDevCodeRepositoryData +{ + Catalog* mainCatalog; + QValueList<Catalog*> catalogs; + + KDevCodeRepositoryData(): mainCatalog( 0 ) {} +}; + +KDevCodeRepository::KDevCodeRepository( ) + : d( new KDevCodeRepositoryData ) +{ +} + +KDevCodeRepository::~KDevCodeRepository( ) +{ + delete( d ); +} + +Catalog* KDevCodeRepository::mainCatalog() +{ + return d->mainCatalog; +} + +void KDevCodeRepository::setMainCatalog( Catalog * mainCatalog ) +{ + d->mainCatalog = mainCatalog; +} + +void KDevCodeRepository::registerCatalog( Catalog * catalog ) +{ + d->catalogs.append( catalog ); + emit catalogRegistered( catalog ); +} + +void KDevCodeRepository::unregisterCatalog( Catalog * catalog ) +{ + d->catalogs.remove( catalog ); + emit catalogUnregistered( catalog ); +} + +void KDevCodeRepository::touchCatalog( Catalog * catalog ) +{ + emit catalogChanged( catalog ); +} + +QValueList< Catalog * > KDevCodeRepository::registeredCatalogs( ) +{ + return d->catalogs; +} + +#include "kdevcoderepository.moc" diff --git a/lib/interfaces/kdevcoderepository.h b/lib/interfaces/kdevcoderepository.h new file mode 100644 index 00000000..9012c428 --- /dev/null +++ b/lib/interfaces/kdevcoderepository.h @@ -0,0 +1,94 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVCODEREPOSITORY_H +#define KDEVCODEREPOSITORY_H + +#include <qobject.h> +#include <qvaluelist.h> + +/** +@file kdevcoderepository.h +Code repository - the persistent symbol store accessor. +*/ + +class KDevCodeRepositoryData; +class Catalog; + +/** +Code repository - the persistent symbol store accessor. +Symbols from parsed files can be saved to the persistent symbol store. +Persistence in this case means that symbol database is never loaded into memory +and works like a usual database which executes queries. + +Code repository consists from @ref Catalog objects that represent separate symbol +databases. Catalogs can be created/loaded/unloaded dynamically. +To find a symbol in the repository each catalog should be queried. + +Persistent symbol store is useful to keep information about code that +never or rarely changes. System libraries are perfect examples of such code. +Symbols from code contained in project files are better stored in memory +symbol store like @ref CodeModel. +*/ +class KDevCodeRepository : public QObject +{ + Q_OBJECT +public: + /**Constructor.*/ + KDevCodeRepository(); + /**Destructor.*/ + virtual ~KDevCodeRepository(); + + /**@return The main catalog. Each catalog can be marked is main + to provide easy access to it.*/ + Catalog* mainCatalog(); + /**Sets the main catalog. + @param mainCatalog The catalog to be marked as main.*/ + void setMainCatalog( Catalog* mainCatalog ); + + /**@return The list of registered catalogs.*/ + QValueList<Catalog*> registeredCatalogs(); + + /**Registers catalog in the repository. + @param catalog The catalog to register.*/ + void registerCatalog( Catalog* catalog ); + /**Unregisters catalog from the repository. + @param catalog The catalog to unregister.*/ + void unregisterCatalog( Catalog* catalog ); + /**Marks catalog as changed and emits @ref catalogChanged signal. + @param catalog The catalog to touch.*/ + void touchCatalog( Catalog* catalog ); + +signals: + /**Emitted when a new catalog is registered. + @param catalog The new catalog.*/ + void catalogRegistered( Catalog* catalog ); + + /**Emitted when a catalog in removed + @param catalog The catalog that was removed.*/ + void catalogUnregistered( Catalog* catalog ); + + /**Emitted when the contens of catalog is changed. + @param catalog Changed catalog.*/ + void catalogChanged( Catalog* catalog ); + +private: + KDevCodeRepositoryData* d; +}; + +#endif diff --git a/lib/interfaces/kdevcore.cpp b/lib/interfaces/kdevcore.cpp new file mode 100644 index 00000000..c10f9626 --- /dev/null +++ b/lib/interfaces/kdevcore.cpp @@ -0,0 +1,298 @@ +/* This file is part of the KDE project + Copyright (C) 2001-2002 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2002-2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2003 Mario Scalas <mario.scalas@libero.it> + Copyright (C) 2003 Amilcar do Carmo Lucas <amilcar@ida.ing.tu-bs.de> + Copyright (C) 2003 Jens Dagerbo <jens.dagerbo@swipnet.se> + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "KDevCoreIface.h" +#include "kdevcore.h" + +#include "urlutil.h" + +/////////////////////////////////////////////////////////////////////////////// +// class Context +/////////////////////////////////////////////////////////////////////////////// + +Context::Context() +{ +} + +Context::~Context() +{ +} + +bool Context::hasType( int aType ) const +{ + return aType == this->type(); +} + +/////////////////////////////////////////////////////////////////////////////// +// class EditorContext +/////////////////////////////////////////////////////////////////////////////// + +class EditorContext::Private +{ +public: + Private( const KURL &url, int line, int col, const QString &linestr, + const QString &wordstr ) + : m_url(url), m_line(line), m_col(col), + m_linestr(linestr), m_wordstr(wordstr) + { + } + + KURL m_url; + int m_line, m_col; + QString m_linestr, m_wordstr; +}; + +EditorContext::EditorContext( const KURL &url, int line, int col, + const QString &linestr, const QString &wordstr ) + : Context(), d( new Private(url, line, col, linestr, wordstr) ) +{ +} + +EditorContext::~EditorContext() +{ + delete d; + d = 0; +} + +int EditorContext::type() const +{ + return Context::EditorContext; +} + +const KURL &EditorContext::url() const +{ + return d->m_url; +} + +int EditorContext::line() const +{ + return d->m_line; +} + +int EditorContext::col() const +{ + return d->m_col; +} + +QString EditorContext::currentLine() const +{ + return d->m_linestr; +} + +QString EditorContext::currentWord() const +{ + return d->m_wordstr; +} + +/////////////////////////////////////////////////////////////////////////////// +// class FileContext +/////////////////////////////////////////////////////////////////////////////// + +class FileContext::Private +{ +public: + Private( const KURL::List &someURLs ) : m_urls(someURLs) + { + if (m_urls.count() == 0) + { + m_fileName = "INVALID-FILENAME"; + m_isDirectory = false; // well, "true" should be ok too ... + } + else + { + m_fileName = m_urls[0].path(); + m_isDirectory = URLUtil::isDirectory( m_urls[0] ); + } + } + Private( const QString &fileName, bool isDirectory ) + : m_fileName(fileName), m_isDirectory(isDirectory) + { + } + + KURL::List m_urls; + /// \FIXME the following data members should be removed, but first other + // parts should be modified to comply with this change. + QString m_fileName; + bool m_isDirectory; +}; + +FileContext::FileContext( const KURL::List &someURLs ) + : Context(), d( new Private(someURLs) ) +{ +} + +FileContext::~FileContext() +{ + delete d; + d = 0; +} + +int FileContext::type() const +{ + return Context::FileContext; +} + +const KURL::List &FileContext::urls() const +{ + return d->m_urls; +} + +/////////////////////////////////////////////////////////////////////////////// +// class DocumentationContext +/////////////////////////////////////////////////////////////////////////////// + +class DocumentationContext::Private +{ +public: + Private( const QString &url, const QString &selection ) + : m_url(url), m_selection(selection) + { + } + + QString m_url; + QString m_selection; +}; + +DocumentationContext::DocumentationContext( const QString &url, const QString &selection ) + : Context(), d( new Private(url, selection) ) +{ +} + +DocumentationContext::DocumentationContext( const DocumentationContext &aContext ) + : Context(), d( 0 ) +{ + *this = aContext; +} + +DocumentationContext &DocumentationContext::operator=( const DocumentationContext &aContext) +{ + if (d) { + delete d; d = 0; + } + d = new Private( *aContext.d ); + return *this; +} + +DocumentationContext::~DocumentationContext() +{ + delete d; + d = 0; +} + +int DocumentationContext::type() const +{ + return Context::DocumentationContext; +} + +QString DocumentationContext::url() const +{ + return d->m_url; +} + +QString DocumentationContext::selection() const +{ + return d->m_selection; +} + +/////////////////////////////////////////////////////////////////////////////// +// class CodeModelItemContext +/////////////////////////////////////////////////////////////////////////////// + +class CodeModelItemContext::Private +{ +public: + Private( const CodeModelItem* item ) : m_item( item ) {} + + const CodeModelItem* m_item; +}; + +CodeModelItemContext::CodeModelItemContext( const CodeModelItem* item ) + : Context(), d( new Private(item) ) +{ +} + +CodeModelItemContext::~CodeModelItemContext() +{ + delete d; + d = 0; +} + +int CodeModelItemContext::type() const +{ + return Context::CodeModelItemContext; +} + +const CodeModelItem* CodeModelItemContext::item() const +{ + return d->m_item; +} + +/////////////////////////////////////////////////////////////////////////////// +// class ProjectModelItemContext +/////////////////////////////////////////////////////////////////////////////// + +class ProjectModelItemContext::Private +{ +public: + Private( const ProjectModelItem* item ) : m_item( item ) {} + + const ProjectModelItem* m_item; +}; + +ProjectModelItemContext::ProjectModelItemContext( const ProjectModelItem* item ) + : Context(), d( new Private(item) ) +{ +} + +ProjectModelItemContext::~ProjectModelItemContext() +{ + delete d; + d = 0; +} + +int ProjectModelItemContext::type() const +{ + return Context::ProjectModelItemContext; +} + +const ProjectModelItem* ProjectModelItemContext::item() const +{ + return d->m_item; +} + + +/////////////////////////////////////////////////////////////////////////////// +// class KDevCore +/////////////////////////////////////////////////////////////////////////////// + +KDevCore::KDevCore( QObject *parent, const char *name ) + : QObject( parent, name ) +{ + new KDevCoreIface(this); +} + +KDevCore::~KDevCore() +{ +} + +#include "kdevcore.moc" diff --git a/lib/interfaces/kdevcore.h b/lib/interfaces/kdevcore.h new file mode 100644 index 00000000..cdc5ca87 --- /dev/null +++ b/lib/interfaces/kdevcore.h @@ -0,0 +1,387 @@ +/* This file is part of the KDE project + Copyright (C) 2001-2002 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2001-2002 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2001 Sandy Meier <smeier@kdevelop.org> + Copyright (C) 2002 Daniel Engelschalt <daniel.engelschalt@gmx.net> + Copyright (C) 2002 Simon Hausmann <hausmann@kde.org> + Copyright (C) 2002-2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2003 Mario Scalas <mario.scalas@libero.it> + Copyright (C) 2003 Harald Fernengel <harry@kdevelop.org> + Copyright (C) 2003 Hamish Rodda <rodda@kde.org> + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVCORE_H +#define KDEVCORE_H + +/** +@file kdevcore.h +The interface to the application core and context menu classes. +*/ + +#include <qstringlist.h> +#include <qdict.h> +#include <qobject.h> + +#include <kurl.h> + +class KDialogBase; +class KDevPlugin; +class CodeModelItem; +class ProjectModelItem; + +namespace KParts +{ + class Part; +} + +class QStatusBar; +class QPopupMenu; + +/** +Base class for every context. +Think of a Context-based class as "useful +info associated to a context menu". Several context menu can be defined, +each defining different information: because of these context menus being +used in many modules, they are defined here. + +When context menu with a certain "context" associated appears, KDevelop core +sends a notification signal and all plugins which receive this signal have +the ability to add own items into the menu. For example, VCS plugin could +add "commit" and "update" menu items to the context menu of a file. + +<b>How to use context from a plugin:</b> +-# Create a popup menu in context menu event handler: @code KPopupMenu menu(this); @endcode +-# Create a context: @code MyContext context(param). @endcode +-# Fill a context menu: @code core()->fillContextMenu(&menu, &context); @endcode +-# Show the popup menu: @code menu.exec(event->globalPos()); @endcode +. +In this example @em event is an object of QContextMenuEvent class which you have access +to if you reimplement QWidget::contextMenuEvent method. + +<b>How to fill context menu from a plugin:</b> +-# Create a @code contextMenu(QPopupMenu *, const Context *) @endcode slot in your plugin class. +-# Connect KDevCore::contextMenu(QPopupMenu *, const Context *) signal to that slot in +the constructor of your plugin:\n +@code +connect(core(), SIGNAL(contextMenu(QPopupMenu *, const Context *)), + this, SLOT(contextMenu(QPopupMenu *, const Context *))); +@endcode +-# Fill the menu in the slot you created, for example:\n +@code +if (context->hasType(Context::EditorContext)) +{ + const EditorContext *econtext = static_cast<const EditorContext*>(context); + int id = popup->insertItem(i18n("My Menu Item 1"), this, SLOT(myMenuAction1())); + popup->setWhatsThis(id, i18n("What's this for my menu item 1")); +} +else if context->hasType(MyContext)) +{ + int id = popup->insertItem(... + ... +} +... +@endcode +*/ +class Context +{ +public: + /**Pre-defined context types. More may be added so it is possible to add custom + contexts. <strong>We reserve enum values until 1000 (yeah, it is one thousand ) + for kdevelop official context types.</strong>*/ + enum Type + { + EditorContext, /**<Editor context menu.*/ + DocumentationContext, /**<Documentation browser context menu.*/ + FileContext, /**<File context menu.*/ + ProjectModelItemContext, /**<Project tree context menu.*/ + CodeModelItemContext /**<Class tree context menu.*/ + }; + + /**Implement this in the context so we can provide rtti.*/ + virtual int type() const = 0; + + /**@return The type of this Context, so clients can discriminate + between different file contexts.*/ + virtual bool hasType(int type) const; + +protected: + /**Constructor.*/ + Context(); + + /**Destructor.*/ + virtual ~Context(); +}; + +/**A context for the popup menu in the editor.*/ +class EditorContext: public Context +{ +public: + /**Builds a context for an editor part. + @param url The url of a file in the editor. + @param line The line number where the cursor is. + @param col The column number where the cursor is. + @param linestr The content of the line where the cursor is. + @param wordstr The current word under the cursor.*/ + EditorContext(const KURL &url, int line, int col, + const QString &linestr, const QString &wordstr); + + /**Destructor.*/ + virtual ~EditorContext(); + + virtual int type() const; + + /**@return The url for the file which this context was invoked for.*/ + const KURL &url() const; + + /**@return The line number for the cursor position.*/ + int line() const; + + /**@return The column number for the cursor position.*/ + int col() const; + + /**@return A QString with the content of the line which this context was + invoked for.*/ + QString currentLine() const; + + /**@return A QString containing the word near to the cursor when this + context object was created.*/ + QString currentWord() const; + +private: + class Private; + Private *d; + + EditorContext( const EditorContext &); + EditorContext &operator=( const EditorContext &); +}; + + +/** +A context for the popup menu in the documentation browser widget. +*/ +class DocumentationContext: public Context +{ +public: + + /**Builds a DocumentationContext. + @param url The URL that the context will be for. + @param selection Selected text.*/ + DocumentationContext(const QString &url, const QString &selection ); + + /**Copy constructor.*/ + DocumentationContext(const DocumentationContext &); + DocumentationContext &operator=(const DocumentationContext &); + + /**Destructor.*/ + virtual ~DocumentationContext(); + + virtual int type() const; + + /**@return The url of the document this context was invoked for.*/ + QString url() const; + + /**@return The selected text in the document.*/ + QString selection() const; + +private: + class Private; + Private *d; +}; + +/** +A context for the popup menu in file views and other parts that show files. +Context allows multiple selections of files. +*/ +class FileContext : public Context +{ +public: + /**Builds the file context using a @ref KURL::List + @param someURLs The list of selected files URLs.*/ + FileContext(const KURL::List &someURLs); + + /**Destructor.*/ + virtual ~FileContext(); + + virtual int type() const; + + /**@return A reference to the selected of URLs.*/ + const KURL::List &urls() const; + +private: + class Private; + Private *d; + + FileContext( const FileContext &); + FileContext &operator=( const FileContext &); +}; + +/** +A context for the popup menu in class views. +*/ +class CodeModelItemContext: public Context +{ +public: + /**Builds the context. + @param item Selected code model item representation. Usually a symbol from the code + like class, function, etc.*/ + CodeModelItemContext(const CodeModelItem* item); + + /**Destructor.*/ + virtual ~CodeModelItemContext(); + + virtual int type() const; + + /**@return The code model item for the selected item.*/ + const CodeModelItem* item() const; + +private: + class Private; + Private *d; + + CodeModelItemContext( const CodeModelItemContext &); + CodeModelItemContext &operator=( const CodeModelItemContext &); +}; + +/** +A context for the popup menu in project views. +*/ +class ProjectModelItemContext : public Context +{ +public: + /**Builds the context. + @param item The item to build the context from.*/ + ProjectModelItemContext(const ProjectModelItem* item); + + /**Destructor.*/ + virtual ~ProjectModelItemContext(); + + virtual int type() const; + + /**@return The code model item for the selected item.*/ + const ProjectModelItem* item() const; + +private: + class Private; + Private *d; + + ProjectModelItemContext( const ProjectModelItemContext &); + ProjectModelItemContext &operator=( const ProjectModelItemContext &); +}; + + + + +/** +A KDevCore class defines an object which takes care about the cooperation +between the various plug-in which compose KDevelop. +It defines: +- signals that can be captured for menu customization; +- notifications about opening / closing projects; +- methods to access functionality of KDevelop core; +- requests to fill project and global settings widgets; +- etc. +. +*/ +class KDevCore: public QObject +{ + Q_OBJECT +public: + /**Constructor + @param parent The QObject that's the parent of this class. + @param name The name of the class.*/ + KDevCore(QObject *parent=0, const char *name=0); + + /**Destructor.*/ + virtual ~KDevCore(); + + /**Fills the context menu. + This method should be called by a part that wants to show a + context menu. The parameter @p context should be filled with + information about the context in which this happens (see + EditorContext, DocumentationContext, ClassContext, ...). + Essentially, this method emits the signal contextMenu() + which other parts can use to hook in. + @sa Context for a detailed explanation of context menu initializations and usage. + @param popup The popup menu to fill. + @param context The pointer to a Context object of this popup menu.*/ + virtual void fillContextMenu(QPopupMenu *popup, const Context *context) = 0; + + /**Closes the current project and open the new one. You cannot use the @ref KDevPlugin::project() + * method right after opening a new project, as it will return a null pointer. + *You must wait for the eventloop to be reentered, so use a signleshot timer + *to do the job needed after the project is opened or connect a slot to the + *@ref projectOpened signal. + * @param projectFileName The file name of the project to open.*/ + virtual void openProject(const QString& projectFileName) = 0; + + /**Marks the component as running (or not running). As long as at least one + component is running, the stop button is enabled. When it is pressed, + component get a stopButtonClicked(). This is usable for plugins which + run certain commands and want KDevelop core to be notified of that. + If core is notified, it can allow the user to stop(interrupt) the command + manually by means of stop button. + @param which The plugin to mark. + @param runs true if plugin is running something, false if it is not.*/ + virtual void running(KDevPlugin *which, bool runs) = 0; + +signals: + /**Emitted after the core has done all initializations and + the main window has been shown.*/ + void coreInitialized(); + + /**A project has been opened.*/ + void projectOpened(); + + /**The project is about to be closed.*/ + void projectClosed(); + + /**The language support part has been changed.*/ + void languageChanged(); + + /**The user has clicked the stop button. + If all actions should be cancelled, pass 0 to @p which + @param which The KDevPlugin object to stop.*/ + void stopButtonClicked(KDevPlugin *which); + + /**A context menu has been requested somewhere. Components + may hook some entries into it. More information on the + context can be obtained by looking for the type of + @p context and casting it accordingly. + @sa Context for a detailed explanation of context menu initializations and usage. + @param popupMenu The popup menu to fill. + @param context The Context of this popup menu.*/ + void contextMenu(QPopupMenu *popupMenu, const Context *context); + + /**Expects that a configuration page for use in the + KDevelop settings dialog is created by the component. + The configuration page is not demand-loading, it will be created before + global settings dialog is shown. Use @ref ConfigWidgetProxy in your plugin + to create demand-loading configuration pages. + @param dlg The dialog which the configuration widget should be added to.*/ + void configWidget(KDialogBase *dlg); + + /**Expects that a configuration page for use in the + Project settings dialog is created by the component. + The configuration page is not demand-loading, it will be created before + project settings dialog is shown. Use @ref ConfigWidgetProxy in your plugin + to create demand-loading configuration pages. + @param dlg The dialog which the configuration widget should be added to.*/ + void projectConfigWidget(KDialogBase *dlg); +}; + +#endif diff --git a/lib/interfaces/kdevdesignerintegration.cpp b/lib/interfaces/kdevdesignerintegration.cpp new file mode 100644 index 00000000..60dbd61b --- /dev/null +++ b/lib/interfaces/kdevdesignerintegration.cpp @@ -0,0 +1,40 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "kdevdesignerintegration.h" +#include "kdevdesignerintegrationiface.h" + +class KDevDesignerIntegration::KDevDesignerIntegrationPrivate { +public: + KDevDesignerIntegrationIface *m_iface; +}; + +KDevDesignerIntegration::KDevDesignerIntegration(QObject *parent, const char *name) + : QObject(parent, name) +{ + dptr = new KDevDesignerIntegrationPrivate(); + + dptr->m_iface = new KDevDesignerIntegrationIface(this); +} + +KDevDesignerIntegration::~KDevDesignerIntegration() +{ + delete dptr; +} + +#include "kdevdesignerintegration.moc" diff --git a/lib/interfaces/kdevdesignerintegration.h b/lib/interfaces/kdevdesignerintegration.h new file mode 100644 index 00000000..7e2c7fc5 --- /dev/null +++ b/lib/interfaces/kdevdesignerintegration.h @@ -0,0 +1,97 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVDESIGNERINTEGRATION_H +#define KDEVDESIGNERINTEGRATION_H + +#include <designer.h> +#include <qobject.h> +#include <qdom.h> + +/** +@file kdevdesignerintegration.h +KDevelop designer integration interface. +*/ + +/** +KDevelop designer integration interface. +Designer integration object provides functionality which is used by integrated +into KDevelop GUI designers: +- adding/removing/editing functions (class methods); +- opening form implementation files; +- saving and loading designer integration settings. +. + +Designer integration is a layer between visual form (GUI) designer of any type +and the current language support. Usually designers create some kind of forms +and allow developers to define actions which are executed upon some GUI events +(for example, Qt Designer allows to define slots connected to GUI signals). +The actual code with action implementations needs to be written by hand in source +files. Designer integration captures such implementation requests and opens +necessary files at desired line numbers and adds (edits, removes) code for implementations. + +For example, Qt Designer integration should be able to create an subclass for a form +and reimplement slots defined in the form. +*/ +class KDevDesignerIntegration : public QObject +{ +Q_OBJECT +public: + /**Constructor. + @param parent Parent object. + @param name Internal name.*/ + KDevDesignerIntegration(QObject *parent = 0, const char *name = 0); + /**Destructor.*/ + ~KDevDesignerIntegration(); + + /**Saves designer integration settings.*/ + virtual void saveSettings(QDomDocument dom, QString path) = 0; + /**Loads designer integration settings.*/ + virtual void loadSettings(QDomDocument dom, QString path) = 0; + +public slots: + /**Adds the implementation for a function. + @param formName The name of a GUI form. + @param function The function which needs to be implemented in the source.*/ + virtual void addFunction(const QString &formName, KInterfaceDesigner::Function function) = 0; + + /**Removes the implementation of a function. + @param formName The name of a GUI form. + @param function The function which needs to be removed from the source.*/ + virtual void removeFunction(const QString &formName, KInterfaceDesigner::Function function) = 0; + + /**Edites the implementation of a function. + @param formName The name of a GUI form. + @param oldFunction The function which needs to be edited from the source. + @param function The new function declaration.*/ + virtual void editFunction(const QString &formName, KInterfaceDesigner::Function oldFunction, KInterfaceDesigner::Function function) = 0; + + /**Opens the function and jumps to the line number of its definition (declaration). + @param formName The name of a GUI form. + @param functionName The name of a function to find in the source.*/ + virtual void openFunction(const QString &formName, const QString &functionName) = 0; + + /**Opens the form source. + @param formName The name of a GUI form.*/ + virtual void openSource(const QString &formName) = 0; + private: + class KDevDesignerIntegrationPrivate; + KDevDesignerIntegrationPrivate *dptr; +}; + +#endif diff --git a/lib/interfaces/kdevdesignerintegrationiface.cpp b/lib/interfaces/kdevdesignerintegrationiface.cpp new file mode 100644 index 00000000..459e3ee9 --- /dev/null +++ b/lib/interfaces/kdevdesignerintegrationiface.cpp @@ -0,0 +1,63 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Ian Reinhart Geiser <geiseri@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "kdevdesignerintegrationiface.h" +#include <designer.h> +#include "kdevdesignerintegration.h" + +KDevDesignerIntegrationIface::KDevDesignerIntegrationIface(KDevDesignerIntegration *p) + : DCOPObject("GUIDesigner"), m_designer(p) +{ + //FIXME figure out what number our interface is... +} + + +KDevDesignerIntegrationIface::~KDevDesignerIntegrationIface() +{ +} + +void KDevDesignerIntegrationIface::addFunction( const QString & formName, QString returnType, QString function, QString specifier, QString access, uint type ) +{ + KInterfaceDesigner::Function f = {returnType, function, specifier, access, (KInterfaceDesigner::FunctionType)type }; + m_designer->addFunction(formName, f); +} + +void KDevDesignerIntegrationIface::removeFunction( const QString & formName, QString returnType, QString function, QString specifier, QString access, uint type ) +{ + KInterfaceDesigner::Function f = {returnType, function, specifier, access, (KInterfaceDesigner::FunctionType)type }; + m_designer->addFunction(formName, f); +} + +void KDevDesignerIntegrationIface::editFunction( const QString & formName, QString oldReturnType, QString oldFunction, QString oldSpecifier, QString oldAccess, uint oldType, QString returnType, QString function, QString specifier, QString access, uint type ) +{ + KInterfaceDesigner::Function fold = {oldReturnType, oldFunction, oldSpecifier, oldAccess, (KInterfaceDesigner::FunctionType)oldType }; + KInterfaceDesigner::Function fnew = {returnType, function, specifier, access, (KInterfaceDesigner::FunctionType)type }; + m_designer->editFunction(formName,fold,fnew); +} + +void KDevDesignerIntegrationIface::openFunction( const QString & formName, const QString & functionName ) +{ + m_designer->openFunction(formName,functionName); +} + +void KDevDesignerIntegrationIface::openSource( const QString & formName ) +{ + m_designer->openSource(formName); +} + + diff --git a/lib/interfaces/kdevdesignerintegrationiface.h b/lib/interfaces/kdevdesignerintegrationiface.h new file mode 100644 index 00000000..38993f8f --- /dev/null +++ b/lib/interfaces/kdevdesignerintegrationiface.h @@ -0,0 +1,49 @@ +/* I am drunk. + Copyright (C) 2005 Ian Reinhart Geiser <geiseri@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVDESIGNERINTEGRATIONIFACE_H +#define KDEVDESIGNERINTEGRATIONIFACE_H + +#include <dcopobject.h> +class KDevDesignerIntegration; +/** +The dcop interface for GUI designers in KDevelop. + +@author KDevelop Authors +*/ +class KDevDesignerIntegrationIface : public DCOPObject +{ + K_DCOP +public: + KDevDesignerIntegrationIface(KDevDesignerIntegration *p); + + ~KDevDesignerIntegrationIface(); +k_dcop: + void addFunction(const QString &formName, QString returnType, QString function,QString specifier,QString access,uint type); + void removeFunction(const QString &formName, QString returnType, QString function,QString specifier,QString access,uint type); + void editFunction(const QString &formName, QString oldReturnType, QString oldFunction, + QString oldSpecifier,QString oldAccess,uint oldType, + QString returnType, QString function,QString specifier,QString access,uint type); + void openFunction(const QString &formName, const QString &functionName); + void openSource(const QString &formName); + + private: + KDevDesignerIntegration *m_designer; +}; + +#endif diff --git a/lib/interfaces/kdeveloplanguagesupport.desktop b/lib/interfaces/kdeveloplanguagesupport.desktop new file mode 100644 index 00000000..217bfc44 --- /dev/null +++ b/lib/interfaces/kdeveloplanguagesupport.desktop @@ -0,0 +1,41 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KDevelop/LanguageSupport +X-KDE-Derived=KDevelop/Plugin +Name=KDevelop Language Support Interface +Name[ca]=Interfície del suport de llenguatges per a KDevelop +Name[da]=KDevelop grænseflade for sprogunderstøttelse +Name[de]=Schnittstelle für Sprachunterstützung (KDevelop) +Name[el]=Διασύνδεση υποστήριξης γλώσσας KDevelop +Name[es]=Interfaz del soporte de lenguajes de KDevelop +Name[et]=KDevelopi keeletoetuse liides +Name[eu]=KDevelop-en lengoaien euskarrien interfazea +Name[fa]=واسط پشتیبانی زبان KDevelop +Name[fr]=Interface de prise en charge de langage de KDevelop +Name[gl]=Interface de soporte de linguaxes de KDevelop +Name[hi]=के-डेवलप भाषा समर्थन इंटरफ़ेस +Name[hu]=KDevelop nyelvtámogatási felület +Name[it]=Interfaccia KDevelop per il supporto di linguaggio +Name[ja]=KDevelop 言語サポート インターフェース +Name[nds]=KDevelop-Koppelsteed för Spraakünnerstütten +Name[ne]=केडीई विकास भाषा समर्थन इन्टरफेस +Name[nl]=KDevelop Interface voor taalondersteuning +Name[pl]=Interfejs KDevelopa do obsługi języków +Name[pt]=Interface de Suporte a Linguagens do KDevelop +Name[pt_BR]=Interface de Suporte à Linguagem do KDevelop +Name[ru]=Интерфейс языковой поддержки +Name[sk]=KDevelop rozhranie pre podporu jazykov +Name[sl]=Vmesnik jezikovne podpore za KDevelop +Name[sr]=KDevelop-ов интерфејс за језичку подршку +Name[sr@Latn]=KDevelop-ov interfejs za jezičku podršku +Name[sv]=KDevelop gränssnitt för språkstöd +Name[ta]=KDevelop மொழி ஆதரவான இடைவிளிம்பு +Name[tg]=Интерфейс ёрӣ расони забонӣ +Name[tr]=KDevelop Dil Desteği Arayüzü +Name[zh_CN]=KDevelop 语言支持接口 +Name[zh_TW]=KDevelop 語言支援介面 + +# versioning - prevent DLL hell +[PropertyDef::X-KDevelop-Version] +Type=int + diff --git a/lib/interfaces/kdevelopplugin.desktop b/lib/interfaces/kdevelopplugin.desktop new file mode 100644 index 00000000..c1a9998c --- /dev/null +++ b/lib/interfaces/kdevelopplugin.desktop @@ -0,0 +1,75 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KDevelop/Plugin +Name=KDevelop Plugin +Name[br]=Lugent KDevelop +Name[ca]=Connector per a KDevelop +Name[da]=KDevelop plugin +Name[de]=KDevelop-Modul +Name[el]=Πρόσθετο KDevelop +Name[es]=Complemento para KDevelop +Name[et]=KDevelopi plugin +Name[eu]=KDevelop plugin-a +Name[fa]=وصلۀ KDevelop +Name[fr]=Module externe de KDevelop +Name[ga]=Breiseán KDevelop +Name[gl]=Extensión para KDevelop +Name[hi]=के-डेवलप प्लगइन +Name[hu]=KDevelop-bővítőmodul +Name[it]=Plugin di KDevelop +Name[ja]=KDevelop プラグイン +Name[nds]=KDevelop-Moduul +Name[ne]=केडीई विकास प्लगइन +Name[pl]=Wtyczka do KDevelopa +Name[pt]='Plugin' do KDevelop +Name[pt_BR]=Plug-in do KDevelop +Name[ru]=Модуль KDevelop +Name[sk]=KDevelop modul +Name[sl]=Vstavek za KDevelop +Name[sr]=Прикључак за KDevelop +Name[sr@Latn]=Priključak za KDevelop +Name[sv]=KDevelop insticksmodul +Name[ta]=KDevelop சொருகு +Name[tg]=Модули KDevelop +Name[tr]=KDevelop Eklentisi +Name[zh_CN]=KDevelop 插件 +Name[zh_TW]=KDevelop 外掛程式 + +# versioning - prevent DLL hell +[PropertyDef::X-KDevelop-Version] +Type=int + +[PropertyDef::X-KDevelop-Scope] +Type=QString + +[PropertyDef::X-KDevelop-Mode] +Type=QString + +[PropertyDef::X-KDevelop-Plugin-Version] +Type=QString + +[PropertyDef::X-KDevelop-Plugin-Homepage] +Type=QString + +[PropertyDef::X-KDevelop-Plugin-License] +Type=QString + +[PropertyDef::X-KDevelop-Plugin-BugsEmailAddress] +Type=QString + +[PropertyDef::X-KDevelop-Plugin-Copyright] +Type=QString + +# defines a list of properties which the plugin has +# a profile will query all plugins for properties and will load only +# those that have queried properties +[PropertyDef::X-KDevelop-Properties] +Type=QStringList + +# only important for project plugins, you can add a list of programming languages which are supported +# the plugin will be loaded by default, if the project use this language +[PropertyDef::X-KDevelop-ProgrammingLanguages] +Type=QStringList + +[PropertyDef::X-KDevelop-Args] +Type=QString diff --git a/lib/interfaces/kdevelopproject.desktop b/lib/interfaces/kdevelopproject.desktop new file mode 100644 index 00000000..e8cad380 --- /dev/null +++ b/lib/interfaces/kdevelopproject.desktop @@ -0,0 +1,40 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KDevelop/Project +X-KDE-Derived=KDevelop/Plugin +Name=KDevelop Project Interface +Name[ca]=Interfície de projecte per a KDevelop +Name[da]=KDevelop projektgrænseflade +Name[de]=Projekt-Schnittstelle (KDevelop) +Name[el]=Διασύνδεση έργου KDevelop +Name[es]=Interfaz de proyecto de KDevelop +Name[et]=KDevelopi projektiliides +Name[eu]=KDevelop-en proiektu interfazea +Name[fa]=واسط پروژۀ KDevelop +Name[fr]=Interface de projet de KDevelop +Name[gl]=Interface de proxecto KDevelop +Name[hi]=के-डेवलप परियोजना इंटरफ़ेस +Name[hu]=KDevelop projektkezelő felület +Name[it]=Interfaccia KDevelop di progetto +Name[ja]=KDevelop プロジェクト インターフェース +Name[nds]=KDevelop-Projektkoppelsteed +Name[ne]=केडीई विकास परियोजना इन्टरफेस +Name[nl]=KDevelop Projectinterface +Name[pl]=Interfejs KDevelopa do projektu +Name[pt]=Interface de Projecto do KDevelop +Name[pt_BR]=Interface de Projeto do KDevelop +Name[ru]=Интерфейс проекта +Name[sk]=KDevelop rozhranie pre projekt +Name[sl]=Projektni vmesnik v KDevelopu +Name[sr]=KDevelop-ов интерфејс пројекта +Name[sr@Latn]=KDevelop-ov interfejs projekta +Name[sv]=KDevelop projektgränssnitt +Name[ta]=KDevelop திட்ட இடைஇணைப்பு +Name[tg]=Лоиҳаи интерфейс +Name[tr]=KDevelop Proje Arayüzü +Name[zh_CN]=KDevelop 工程接口 +Name[zh_TW]=KDevelop 專案介面 + +# versioning - prevent DLL hell +[PropertyDef::X-KDevelop-Version] +Type=int diff --git a/lib/interfaces/kdevgenericfactory.h b/lib/interfaces/kdevgenericfactory.h new file mode 100644 index 00000000..581aa36a --- /dev/null +++ b/lib/interfaces/kdevgenericfactory.h @@ -0,0 +1,91 @@ +/* This file is part of the KDE project + * Copyright (C) 2003 Harald Fernengel <harry@kdevelop.org> + * Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include <kgenericfactory.h> +#include <kaboutdata.h> + +/** +@file kdevgenericfactory.h +KDevelop generic plugin factory. +*/ + +/** +This class provides a generic implementation of a KLibFactory for +use with KDevelop plugins. +Usually it is convenient to use K_EXPORT_COMPONENT_FACTORY macro +to create factories for KDevelop plugins. For example, for DummyPlugin +the factory can be created (in dummyplugin.cpp file) as: +@code +typedef KDevGenericFactory<DummyPlugin> DummyPluginFactory; +K_EXPORT_COMPONENT_FACTORY(libkdevdummyplugin, DummyPluginFactory( data ) ) +@endcode +Data should be a const static object. This way it complies with the requirements +for data objecs of KDevGenericFactory constructor. + +<b>Important:</b><br> +There is no need to create @ref KAboutData objects. It is more useful to create +a static const @ref KDevPluginInfo object which can be used also in the constructor +of a plugin. + +For example, dummyplugin.cpp file could contain: +@code +#include <kdevplugininfo.h> + +static const KDevPluginInfo data("KDevDummyPlugin"); +typedef KDevGenericFactory<DummyPlugin> DummyPluginFactory; +K_EXPORT_COMPONENT_FACTORY(libkdevdummyplugin, DummyPluginFactory( data ) ) + +DummyPlugin::DummyPlugin(QObject *parent, const char *name, const QStringList & ) + :KDevPlugin(&data, parent, name) +{ +} +@endcode + +In the example above the duplication of information is avoided as same @ref KDevPluginInfo +objects are used for plugin and for plugin factory. This is possible because @ref KDevPluginInfo +class has an operator to cast @ref KDevPluginInfo to @ref KAboutData. +*/ +template <class T, class ParentType = QObject> +class KDevGenericFactory: public KGenericFactory<T, ParentType> +{ +public: + /**Constructor. + @param data A reference to KAboutData with an information about the plugin. + Data should have: + - plugin name as an application name; + - untranslated plugin generic name as a product name; + - license type number. + . + data object should live as long as factory lives.*/ + KDevGenericFactory(KAboutData *data) + :KGenericFactory<T, ParentType>(data->appName()), aboutData(data) + { + } + + /**Creates an instance.*/ + KInstance *createInstance() + { + return new KInstance(aboutData); + } + +private: + KAboutData *aboutData; + +}; + diff --git a/lib/interfaces/kdevlanguagesupport.cpp b/lib/interfaces/kdevlanguagesupport.cpp new file mode 100644 index 00000000..3a0b7c9e --- /dev/null +++ b/lib/interfaces/kdevlanguagesupport.cpp @@ -0,0 +1,166 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2001-2002 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2002-2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2002 Simon Hausmann <hausmann@kde.org> + Copyright (C) 2002 F@lk Brettschneider <falkbr@kdevelop.org> + Copyright (C) 2003 Alexander Dymo <adymo@kdevelop.org> + Copyright (C) 2003 Amilcar do Carmo Lucas <amilcar@ida.ing.tu-bs.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "codemodel.h" + +#include <kdebug.h> + +#include "kdevdesignerintegration.h" +#include "kdevlanguagesupport.h" + +KDevLanguageSupport::KDevLanguageSupport(const KDevPluginInfo *info, QObject *parent, const char *name) + : KDevPlugin(info, parent, name ? name : "KDevLanguageSupport" ) +{ +} + +KDevLanguageSupport::~KDevLanguageSupport() +{ +} + +KDevLanguageSupport::Features KDevLanguageSupport::features() +{ + return Features(0); +} + +KMimeType::List KDevLanguageSupport::mimeTypes() +{ + return KMimeType::List(); +} + +QString KDevLanguageSupport::formatTag( const Tag& /*tag*/ ) +{ + return QString::null; +} + +QString KDevLanguageSupport::formatClassName(const QString &name) +{ + return name; +} + +QString KDevLanguageSupport::unformatClassName(const QString &name) +{ + return name; +} + +bool KDevLanguageSupport::shouldSplitDocument(const KURL &url) +{ + return false; +} + +Qt::Orientation KDevLanguageSupport::splitOrientation() const +{ + return Qt::Vertical; +} + +void KDevLanguageSupport::addClass() +{ +} + +void KDevLanguageSupport::addMethod( ClassDom /*klass*/ ) +{ +} + +void KDevLanguageSupport::implementVirtualMethods( ClassDom /*klass*/ ) +{ +} + +void KDevLanguageSupport::addAttribute( ClassDom /*klass*/ ) +{ +} + +QStringList KDevLanguageSupport::subclassWidget(const QString& /*formName*/) +{ + return QStringList(); +} + +QStringList KDevLanguageSupport::updateWidget(const QString& /*formName*/, const QString& /*fileName*/) +{ + return QStringList(); +} + +QString KDevLanguageSupport::formatModelItem( const CodeModelItem *item, bool /*shortDescription*/ ) +{ + return item->name(); +} + +void KDevLanguageSupport::addFunction( DesignerType type, const QString & formName, Function function ) +{ +// kdDebug() << "KDevLanguageSupport::addFunction: 1" << endl; + KDevDesignerIntegration *designerIntegration = designer(type); +// kdDebug() << "KDevLanguageSupport::addFunction: 2" << endl; + if (!designerIntegration) + { +// kdDebug() << "KDevLanguageSupport::addFunction: x" << endl; + return; + } +// kdDebug() << "KDevLanguageSupport::addFunction: 3" << endl; + designerIntegration->addFunction(formName, function); +// kdDebug() << "KDevLanguageSupport::addFunction: 4" << endl; +} + +void KDevLanguageSupport::editFunction( DesignerType type, const QString & formName, Function oldFunction, Function function ) +{ + KDevDesignerIntegration *designerIntegration = designer(type); + if (!designerIntegration) + return; + designerIntegration->editFunction(formName, oldFunction, function); +} + +void KDevLanguageSupport::removeFunction( DesignerType type, const QString & formName, Function function ) +{ + KDevDesignerIntegration *designerIntegration = designer(type); + if (!designerIntegration) + return; + designerIntegration->removeFunction(formName, function); +} + +KDevDesignerIntegration * KDevLanguageSupport::designer( DesignerType // type + ) +{ + return 0; +} + +void KDevLanguageSupport::openFunction( DesignerType type, const QString & formName, const QString & functionName ) +{ + KDevDesignerIntegration *designerIntegration = designer(type); + if (!designerIntegration) + return; + designerIntegration->openFunction(formName, functionName); +} + +void KDevLanguageSupport::createAccessMethods( ClassDom // theClass + , VariableDom // theVariable + ) +{ +} + +void KDevLanguageSupport::openSource( DesignerType type, const QString & formName) +{ + KDevDesignerIntegration *designerIntegration = designer(type); + if (!designerIntegration) + return; + designerIntegration->openSource(formName); +} + +#include "kdevlanguagesupport.moc" diff --git a/lib/interfaces/kdevlanguagesupport.h b/lib/interfaces/kdevlanguagesupport.h new file mode 100644 index 00000000..06be2f26 --- /dev/null +++ b/lib/interfaces/kdevlanguagesupport.h @@ -0,0 +1,260 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2001-2002 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2002-2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2002 Simon Hausmann <hausmann@kde.org> + Copyright (C) 2002 F@lk Brettschneider <falkbr@kdevelop.org> + Copyright (C) 2003 Amilcar do Carmo Lucas <amilcar@ida.ing.tu-bs.de> + Copyright (C) 2003-2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVLANGUAGESUPPORT_H +#define KDEVLANGUAGESUPPORT_H + +#include <qstring.h> +#include <qstringlist.h> +#include <kmimetype.h> +#include "kdevplugin.h" +#include "codemodel.h" +#include <designer.h> + +/** +@file kdevlanguagesupport.h +Interface to programming language specific features. +*/ + +class Tag; +class KDevDesignerIntegration; + +using namespace KInterfaceDesigner; + +/** +KDevelop language support interface - the base class for all programming language support plugins. +Language support is used to load facilities specific to certain programming language. +Language supports are usually loaded among with a project. In this case project file defines +which language support to load. + +Language support plugin is a good place for: +- a language parser which fills memory and persistent symbol store +(see @ref CodeModel and @ref KDevCodeRepository); +- code wizards specific to a programming language (like new class wizard); +- GUI designer integration (see @ref KDevLanguageSupport::designer method +documentation and @ref KDevDesignerIntegration class documentation; +- symbol (class, function, etc.) name formatting to a human-readable convention (pretty +formatted name). +. +*/ +class KDevLanguageSupport: public KDevPlugin +{ + Q_OBJECT + +public: + + /**Features supported by this language support.*/ + enum Features { + /*features of the language itself*/ + Classes=1 /**<Language has classes (or class-like packages).*/, + Structs=2 /**<Language has structures or records.*/, + Functions=4 /**<Language has functions.*/, + Variables=8 /**<Language has variables.*/, + + Namespaces=16 /**<Language has namespaces.*/, + Signals=32 /**<Language has signals (or qt library bindings are available).*/, + Slots=64 /**<Language has slots (or qt library bindings are available).*/, + Declarations=128 /**<Language has function declarations (like c, c++ and pascal).*/, + + /*features of the language support part*/ + NewClass=512 /**<Language support can create classes.*/, + AddMethod=1024 /**<Language support can create methods.*/, + AddAttribute=2048 /**<Language support can create class attributes.*/, + CreateAccessMethods=4096 /**<Language support can create get/set methods for attributes.*/ + }; + + /**Constructs a language support plugin. + @param info Important information about the plugin - plugin internal and generic + (GUI) name, description, a list of authors, etc. That information is used to show + plugin information in various places like "about application" dialog, plugin selector + dialog, etc. Plugin does not take ownership on info object, also its lifetime should + be equal to the lifetime of the plugin. + @param parent The parent object for the plugin. Parent object must implement @ref KDevApi + interface. Otherwise the plugin will not be constructed. + @param name The internal name which identifies the plugin.*/ + KDevLanguageSupport(const KDevPluginInfo *info, QObject *parent, const char *name); + /**Destructor.*/ + ~KDevLanguageSupport(); + + /**@return The feature set of the language. This is e.g. used + by the class view to decide which organizer items to display and which not.*/ + virtual Features features(); + + /**@return A typical mimetype list for the support language, this list + should be configurable in the languagesupport dialog.*/ + virtual KMimeType::List mimeTypes(); + + /**Formats a Tag as used by the persistent symbol store to the human-readable convention. + @param tag Tag to format.*/ + virtual QString formatTag(const Tag& tag); + + /**Formats a CodeModelItem as used by the CodeModel to the human-readable convention. + @param item Symbol to format. + @param shortDescription Show short description of a symbol. For example, when + formatting functions short description could be a function signature without + the return type and argument default values.*/ + virtual QString formatModelItem(const CodeModelItem *item, bool shortDescription=false); + + /**Formats a canonicalized class path as used by the symbol store to the + human-readable convention. For example, the C++ support part formats the + string "KParts.Part" into "KParts::Part". + @param name Class name.*/ + virtual QString formatClassName(const QString &name); + + /**The opposite of @ref formatClassName. Reverts formatting. + @param name Class name.*/ + virtual QString unformatClassName(const QString &name); + + /**Determines whether the document should be opened in a split view. The + language part can determine this internally and then set the active document + and if so, set the active document so that the newly opened one will be split + with it. + @param name URL of the document in question.*/ + virtual bool shouldSplitDocument( const KURL &url ); + + /**Returns the language parts Split View orientation. + By default Qt::Vertical.*/ + virtual Qt::Orientation splitOrientation() const; + + /**Opens a "New class" dialog and adds the configured class to the sources. + Define NewClass feature if you reimplement this method.*/ + virtual void addClass(); + + /**Opens an "Add method" dialog and adds the configured method to the sources. + Define AddMethod feature if you reimplement this method. + @param klass The class DOM to add a method to.*/ + virtual void addMethod(ClassDom klass); + + /**Opens an "Implement Virtual Methods" dialog and adds the configured methods + to the sources. Define AddMethod feature if you reimplement this method. + @param klass The class DOM to add a virtual method to.*/ + virtual void implementVirtualMethods(ClassDom klass); + + /**Opens an "Add attribute" dialog and adds the configured attribute to the sources. + Define AddAttribute feature if you reimplement this method. + @param klass The class DOM to add an attribute to.*/ + virtual void addAttribute(ClassDom klass); + + /** + * Opens an "create get/set methods" dialog and adds the configured methods to the sources. + * Define CreateAccessMethods feature if you reimplement this method. + * @param theClass The class the methods should be added to. + * @param theVariable The attribute the access methods should be generated for. + */ + virtual void createAccessMethods(ClassDom theClass, VariableDom theVariable); + + /**Opens an "Subclass Widget" dialog for given Qt .ui file (formName) + and propmts to implement it's slots. + @param formName The name of a form to subclass. + @return A list of newly created files.*/ + virtual QStringList subclassWidget(const QString& formName); + + /**Opens an "Update Widget" dialog for given Qt .ui file (formName) + and prompts to add missing slot implementations + in the subclass located in fileName. + @param formName The name of a form which is being subclassed. + @param fileName The name of a file with a subclass. + @return A list of updated files. Can be empty because usually no additional + actions are required on updated files.*/ + virtual QStringList updateWidget(const QString& formName, const QString& fileName); + + /**Reimplement this method if you want to use integrated GUI designer for the language. + Implementation could look like (in pseudo code): + @code + KDevDesignerIntegration *des = 0; + switch (type) + { + case KInterfaceDesigner::QtDesigner: + des = getDesignerFromCache(type); + if (des == 0) + { + MyLanguageImplementationWidget *impl = new MyLanguageImplementationWidget(this); + des = new QtDesignerMyLanguageIntegration(this, impl); + des->loadSettings(*project()->projectDom(), "kdevmylangsupport/designerintegration"); + saveDesignerToCache(type, des); + } + break; + } + return des; + @endcode + @ref ImplementationWidget and @ref QtDesignerIntegration classes are available + from designerintegration support library. + @param type The type of the designer to integrate. + @return The pointer to designer integration of given type or 0.*/ + virtual KDevDesignerIntegration *designer(KInterfaceDesigner::DesignerType type); + +public slots: + /**Adds a function requested by a GUI designer. No need to reimplement this slot + unless you want to use specific implementation of KDevDesignerIntegration interface. + @param type The type of integrated designer. + @param formName The name of a GUI form. + @param function The function to implement (add).*/ + void addFunction(DesignerType type, const QString &formName, Function function); + + /**Removes a function requested by a GUI designer. No need to reimplement this slot + unless you want to use specific implementation of KDevDesignerIntegration interface. + @param type The type of integrated designer. + @param formName The name of a GUI form. + @param function The function to remove.*/ + void removeFunction(DesignerType type, const QString &formName, Function function); + + /**Edits a function requested by a GUI designer. No need to reimplement this slot + unless you want to use specific implementation of KDevDesignerIntegration interface. + @param type The type of integrated designer. + @param formName The name of a GUI form. + @param oldFunction The old function signature before editing. + @param function The new function signature after editing.*/ + void editFunction(DesignerType type, const QString &formName, Function oldFunction, Function function); + + /**Opens a function requested by a GUI designer. No need to reimplement this slot + unless you want to use specific implementation of KDevDesignerIntegration interface. + @param type The type of integrated designer. + @param formName The name of a GUI form. + @param functionName The name of a function to seek in the code for.*/ + void openFunction(DesignerType type, const QString &formName, const QString &functionName); + + /**Opens a form source requested by a GUI designer. No need to reimplement this slot + unless you want to use specific implementation of KDevDesignerIntegration interface. + @param type The type of integrated designer. + @param formName The name of a GUI form.*/ + void openSource(DesignerType type, const QString &formName); + +signals: + /**Emitted when the content of the memory symbol store has been modified.*/ + void updatedSourceInfo(); + + /**Emitted before removing the file from the memory symbol store.*/ + void aboutToRemoveSourceInfo(const QString& fileName); + + /**Emitted when a file has been removed from the memory symbol store.*/ + void removedSourceInfo(const QString& fileName); + + /**Emitted when a file has been added to the memory symbol store.*/ + void addedSourceInfo( const QString& fileName ); + + /**Emitted when the language part changes the Split View orientation.*/ + void splitOrientationChanged( Qt::Orientation orientation ); +}; + +#endif diff --git a/lib/interfaces/kdevmainwindow.cpp b/lib/interfaces/kdevmainwindow.cpp new file mode 100644 index 00000000..e54b616e --- /dev/null +++ b/lib/interfaces/kdevmainwindow.cpp @@ -0,0 +1,28 @@ +/* This file is part of the KDE project + Copyright (C) 2002 F@lk Brettschneider <falkbr@kdevelop.org> + Copyright (C) 2003 John Firebaugh <jfirebaugh@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "kdevmainwindow.h" + +#include <kmainwindow.h> +#include <kstatusbar.h> + +KStatusBar *KDevMainWindow::statusBar() +{ + return main()->statusBar(); +} diff --git a/lib/interfaces/kdevmainwindow.h b/lib/interfaces/kdevmainwindow.h new file mode 100644 index 00000000..3e2553b3 --- /dev/null +++ b/lib/interfaces/kdevmainwindow.h @@ -0,0 +1,101 @@ +/* This file is part of the KDE project + Copyright (C) 2003 F@lk Brettschneider <falkbr@kdevelop.org> + Copyright (C) 2003 John Firebaugh <jfirebaugh@kde.org> + Copyright (C) 2003 Amilcar do Carmo Lucas <amilcar@ida.ing.tu-bs.de> + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEV_MAINWINDOW_H +#define KDEV_MAINWINDOW_H + +#include <qstring.h> + +class QWidget; +class KStatusBar; +class KMainWindow; + +/** +@file kdevmainwindow.h +KDevelop main window interface. +*/ + +/** +KDevelop main window interface. +Provides methods to control the main window of an application. +*/ +class KDevMainWindow +{ +public: + /**Destroys the main window*/ + virtual ~KDevMainWindow() {} // must be virtual! + + /**Embeds a view of a part into the main window. + @param view The view to embed. Must be a KPart. + @param title The title of a view. + @param toolTip The tooltip of a view.*/ + virtual void embedPartView(QWidget *view, const QString &title, const QString& toolTip = QString::null) = 0; + + /**Embeds a toolview at the left of the main window. + @param view The view to embed. Must be a KPart. + @param title The title of a view. + @param toolTip The tooltip of a view.*/ + virtual void embedSelectView(QWidget *view, const QString &title, const QString &toolTip) = 0; + + /**Embeds a toolview at the bottom of the main window. + @param view The view to embed. Must be a KPart. + @param title The title of a view. + @param toolTip The tooltip of a view.*/ + virtual void embedOutputView(QWidget *view, const QString &title, const QString &toolTip) = 0; + + /**Embeds a toolview at the right of the main window. + @param view The view to embed. Must be a KPart. + @param title The title of a view. + @param toolTip The tooltip of a view.*/ + virtual void embedSelectViewRight(QWidget* view, const QString& title, const QString &toolTip) = 0; + + /**Removes a view from the main window. + @param view The view to remove.*/ + virtual void removeView(QWidget *view) = 0; + + /**Shows or hides a view. + @param pView The view to show or hide. + @param bEnabled true if view should be shown, false it it is not.*/ + virtual void setViewAvailable(QWidget *pView, bool bEnabled) = 0; + + /**Raises a view (shows it if it was minimized). + @param view The view to be raised.*/ + virtual void raiseView(QWidget *view) = 0; + + /**Minimize a view. + @param view The view to be lowered.*/ + virtual void lowerView(QWidget *view) = 0; + + /**Loads main window settings.*/ + virtual void loadSettings() = 0; + + /**Sets the current document's caption, if applicable. + @param caption the caption to set.*/ + virtual void setCurrentDocumentCaption( const QString &caption ) = 0; + + /**@return KMainWindow object which actually represents the main window.*/ + virtual KMainWindow *main() = 0; + + /**@return KStatusBar object which actually represents the status bar in the main window.*/ + KStatusBar *statusBar(); +}; + +#endif diff --git a/lib/interfaces/kdevpartcontroller.cpp b/lib/interfaces/kdevpartcontroller.cpp new file mode 100644 index 00000000..66afa81f --- /dev/null +++ b/lib/interfaces/kdevpartcontroller.cpp @@ -0,0 +1,30 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "KDevPartControllerIface.h" +#include "kdevpartcontroller.h" +#include "katedocumentmanagerinterface.h" + +KDevPartController::KDevPartController(QWidget *parent) + : KParts::PartManager(parent) +{ + new KDevPartControllerIface(this); + new KateDocumentManagerInterface(this); +} + +#include "kdevpartcontroller.moc" diff --git a/lib/interfaces/kdevpartcontroller.h b/lib/interfaces/kdevpartcontroller.h new file mode 100644 index 00000000..dced10ac --- /dev/null +++ b/lib/interfaces/kdevpartcontroller.h @@ -0,0 +1,175 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2002 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2003 Hamish Rodda <rodda@kde.org> + Copyright (C) 2003 Harald Fernengel <harry@kdevelop.org> + Copyright (C) 2003 Jens Dagerbo <jens.dagerbo@swipnet.se> + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEV_PARTCONTROLLER_H +#define KDEV_PARTCONTROLLER_H + +#include <kurl.h> +#include <kparts/partmanager.h> +#include <ktrader.h> + +/** +@file kdevpartcontroller.h +KDevelop part controller interface. +*/ + +namespace KParts +{ + class ReadOnlyPart; +} + +/**Document state enum.*/ +enum DocumentState +{ + Clean, /**<Document is not touched.*/ + Modified, /**<Document is modified inside a shell.*/ + Dirty, /**<Document is modified by an external process.*/ + DirtyAndModified /**<Document is modified inside a shell and at the same time by an external process.*/ +}; + +/** +Interface to control loaded parts and other documents. +Part controller works with embedded into the shell parts. Such parts are usually editors, +GUI designers, etc. +*/ +class KDevPartController: public KParts::PartManager +{ + Q_OBJECT + +public: + /**Constructor. + @param parent The parent object.*/ + KDevPartController(QWidget *parent); + + /**Call this before a call to @ref editDocument to set the encoding of the + document to be opened. + @param encoding The encoding to open as.*/ + virtual void setEncoding(const QString &encoding) = 0; + + /**Opens a new or existing document. + @param url The URL of the document to open. + @param lineNum The line number to place the cursor at, if applicable. + @param col The column number to place the cursor at, if applicable.*/ + virtual void editDocument(const KURL &url, int lineNum=-1, int col=-1) = 0; + + /**Opens a new or existing document by splitting the view with the current, + if applicable. Offers split views of source code and header files for instance. + @param url The URL of the document to open. + @param lineNum The line number to place the cursor at, if applicable. + @param col The column number to place the cursor at, if applicable.*/ + virtual void splitCurrentDocument(const KURL &url, int lineNum=-1, int col=-1) = 0; + + /**Scrolls the viewport of the already opened document to the specified line + and column if applicable, but does not give focus to the document. + @param url The URL of the already opened document. + @param lineNum The line number to place the cursor at, if applicable. + @param col The column number to place the cursor at, if applicable.*/ + virtual void scrollToLineColumn(const KURL &url, int lineNum=-1, int col=-1, bool storeHistory = false ) = 0; + + /**Shows a HTML document in the documentation viewer. + @param url The URL of the document to view. + @param newWin If true, the new window will be created instead of using current.*/ + virtual void showDocument(const KURL &url, bool newWin = false) = 0; + + /**Embeds a part into the main area of the mainwindow. + @param part The part to embed. + @param name The name of the part. + @param shortDescription Currently not used.*/ + virtual void showPart(KParts::Part* part, const QString& name, const QString& shortDescription ) = 0; + + /**Finds the embedded part corresponding to a given URL. + @param url The URL of the document. + @return The corresponding part, 0 if not found.*/ + virtual KParts::ReadOnlyPart *partForURL(const KURL & url) = 0; + + /**Finds the embedded part corresponding to a given main widget + @param widget The parts main widget. + @return The corresponding part, 0 if not found.*/ + virtual KParts::Part *partForWidget(const QWidget *widget) = 0; + + /**@return The list of open documents*/ + virtual KURL::List openURLs() = 0; + + /**Saves all open files. + @return false if it was cancelled by the user, true otherwise */ + virtual bool saveAllFiles() = 0; + + /**Saves a list of files. + @param list The list of URLs to save. + @return false if it was cancelled by the user, true otherwise */ + virtual bool saveFiles(const KURL::List &list) = 0; + + /**Reloads all open files.*/ + virtual void revertAllFiles() = 0; + + /**Reloads a list of files. + * @param list The list of URLs to reload.*/ + virtual void revertFiles(const KURL::List &list) = 0; + + /**Closes all open files.*/ + virtual bool closeAllFiles() = 0; + + /**Closes a list of files. + @param list The list of URLs for the files to close.*/ + virtual bool closeFiles(const KURL::List &list) = 0; + + /**Closes this part (closes the window/tab for this part). + @param part The part to close. + @return true if the part was sucessfuly closed.*/ + virtual bool closePart(KParts::Part *part) = 0; + + /**Activate this part. + @param part The part to activate.*/ + virtual void activatePart( KParts::Part * part ) = 0; + + /**Checks the state of a document. + @param url The URL to check. + @return The DocumentState enum corresponding to the document state.*/ + virtual DocumentState documentState( KURL const & url ) = 0; + +signals: + + /**Emitted when a document has been saved.*/ + void savedFile(const KURL &); + + /**Emitted when a document has been loaded.*/ + void loadedFile(const KURL &); + + /**Emitted when a document has been closed.*/ + void closedFile(const KURL &); + + /**Emitted when a file has been modified outside of KDevelop.*/ + void fileDirty(const KURL &); + + /**This is typically emitted when an editorpart does "save as" + which will change the part's URL.*/ + void partURLChanged(KParts::ReadOnlyPart *); + + /**This is emitted when the document changes, + either internally or on disc.*/ + void documentChangedState(const KURL &, DocumentState); + +}; + +#endif diff --git a/lib/interfaces/kdevplugin.cpp b/lib/interfaces/kdevplugin.cpp new file mode 100644 index 00000000..fa289ab8 --- /dev/null +++ b/lib/interfaces/kdevplugin.cpp @@ -0,0 +1,139 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Simon Hausmann <hausmann@kde.org> + Copyright (C) 2002 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2002 Harald Fernengel <harry@kdevelop.org> + Copyright (C) 2002 F@lk Brettschneider <falkbr@kdevelop.org> + Copyright (C) 2003 Julian Rockey <linux@jrockey.com> + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2003 Jens Dagerbo <jens.dagerbo@swipnet.se> + Copyright (C) 2003 Mario Scalas <mario.scalas@libero.it> + Copyright (C) 2003-2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "kdevplugin.h" + +#include "kdevapi.h" +#include "kdevcore.h" +#include "kdevplugininfo.h" +#include "kdevpartcontroller.h" +#include "kdevplugincontroller.h" + +#include <kaction.h> +#include <kglobal.h> +#include <kiconloader.h> + +#include <qdom.h> +#include <qmap.h> + +#include <assert.h> + +/////////////////////////////////////////////////////////////////////////////// +// struct KDevPlugin::Private +/////////////////////////////////////////////////////////////////////////////// + +struct KDevPlugin::Private +{ + const KDevPluginInfo *info; +}; + +/////////////////////////////////////////////////////////////////////////////// +// class KDevPlugin +/////////////////////////////////////////////////////////////////////////////// + +KDevPlugin::KDevPlugin(const KDevPluginInfo *info, QObject *parent, const char *name) + :QObject(parent, name), d(new Private) +{ + assert(parent->inherits( "KDevApi" )); + m_api = static_cast<KDevApi *>( parent ); + + actionCollection()->setHighlightingEnabled( true ); + + d->info = info; + KGlobal::iconLoader()->addAppDir("kdevelop"); +} + +KDevPlugin::~KDevPlugin() +{ + delete( d ); +} + +KDevMainWindow *KDevPlugin::mainWindow() +{ + return m_api->mainWindow(); +} + +KDevCore *KDevPlugin::core() const +{ + return m_api->core(); +} + +KDevProject *KDevPlugin::project() const +{ + return m_api->project(); +} + +CodeModel *KDevPlugin::codeModel() const +{ + return m_api->codeModel(); +} + +QDomDocument *KDevPlugin::projectDom() const +{ + return m_api->projectDom(); +} + +KDevLanguageSupport *KDevPlugin::languageSupport() const +{ + return m_api->languageSupport(); +} + +KDevPartController *KDevPlugin::partController() const +{ + return m_api->partController(); +} + +KDevPluginController *KDevPlugin::pluginController() const +{ + return m_api->pluginController(); +} + +void KDevPlugin::restorePartialProjectSession(const QDomElement* /*el*/) +{ + // there's still nothing to do in the base class +} + +void KDevPlugin::savePartialProjectSession(QDomElement* /*el*/) +{ + // there's still nothing to do in the base class +} + +KDevCodeRepository * KDevPlugin::codeRepository() const +{ + return m_api->codeRepository(); +} + +KDevPlugin * KDevPlugin::extension_internal(const QString &serviceType, const QString &constraint) +{ + return m_api->pluginController()->extension(serviceType, constraint); +} + +const KDevPluginInfo *KDevPlugin::info() +{ + return d->info; +} + +#include "kdevplugin.moc" diff --git a/lib/interfaces/kdevplugin.h b/lib/interfaces/kdevplugin.h new file mode 100644 index 00000000..b65b411f --- /dev/null +++ b/lib/interfaces/kdevplugin.h @@ -0,0 +1,200 @@ +/* This file is part of the KDE project + Copyright (C) 1999-2001 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVPLUGIN_H +#define KDEVPLUGIN_H + +#include <qobject.h> +#include <kxmlguiclient.h> +#include "kdevapi.h" + +class KDevCore; +class KDevProject; +class KDevVersionControl; +class KDevLanguageSupport; +class KDevPartController; +class KDevMainWindow; +class KDevCodeRepository; +class CodeModel; +class KDevPluginInfo; +class QDomElement; + +/** +@file kdevplugin.h +KDevelop plugin interface. +*/ + +/**Current KDevelop plugin interface version. Interfaces declare plugin version to make sure +old source (or binary) incompatible plugins are not loaded. Increase this if +it is necessary that old plugins stop working.*/ +#define KDEVELOP_PLUGIN_VERSION 5 + +/** +The base class for all KDevelop plugins. +Plugin is a component which is loaded into KDevelop shell at startup or by request. +Each plugin should have corresponding .desktop file with a description. +.desktop file template looks like: +@code +[Desktop Entry] +Encoding=UTF-8 +Type=Service +Name= +GenericName= +Comment= +Icon= +X-KDevelop-Plugin-Version= +X-KDevelop-Plugin-Homepage= +X-KDevelop-Plugin-BugsEmailAddress= +X-KDevelop-Plugin-Copyright= +X-KDE-Library= +X-KDevelop-Version= +X-KDevelop-Scope= +X-KDevelop-Properties= +X-KDevelop-Args= +@endcode +<b>Description of parameters in .desktop file:</b> +- <i>Name</i> is a non-translatable name of a plugin, it is used in KTrader queries +to search for a plugin (required); +- <i>GenericName</i> is a translatable name of a plugin, it is used to show +plugin names in GUI (required); +- <i>Comment</i> is a short description about the plugin (optional); +- <i>Icon</i> is a plugin icon (preferred); +- <i>X-KDevelop-Plugin-Version</i> is a version of a plugin (optional); +- <i>X-KDevelop-Plugin-Homepage</i> is a home page of a plugin (optional); +- <i>X-KDevelop-Plugin-License</i> is a license (optional). can be: GPL, LGPL, BSD, Artistic, +QPL or Custom. If this property is not set, license is considered as unknown; +- <i>X-KDevelop-Plugin-BugsEmailAddress</i> is an email address for bug reports (optional); +- <i>X-KDevelop-Plugin-Copyright</i> is a copyright statement (optional); +- <i>X-KDE-Library</i> is a name of library which contains the plugin (required); +- <i>X-KDevelop-Version</i> is a version of KDevelop interfaces which is supported by the plugin (required); +- <i>X-KDevelop-Scope</i> is a scope of a plugin (see below for explanation) (required); +- <i>X-KDevelop-Args</i> is a list of additional arguments passed to plugins constructor (optional); +- <i>X-KDevelop-Properties</i> is a list of properties which this plugin supports, see @ref Profile class documentation for explanation (required to work with shells that support profiles). +. +Plugin scope can be either: +- Core +- Global +- Project +. +Global plugins are plugins which require only shell to be loaded and do not operate on @ref KDevProject interface and/or do not use project wide information.\n +Core plugins are global plugins which offer some important "core" functionality and thus +are not selectable by user in plugin configuration pages.\n +Project plugins require a project to be loaded and are usually loaded/unloaded among with the project. +If your plugin use @ref KDevProject interface and/or operate on project-related information then this is the project plugin. + +@sa KDevGenericFactory class documentation for an information about plugin instantiation +and writing factories for plugins. + +@sa KDevCore class documentation for an information about features which are available to plugins +from shell applications. +*/ +class KDevPlugin: public QObject, public KXMLGUIClient +{ + Q_OBJECT + +public: + /**Constructs a plugin. + @param info Important information about the plugin - plugin internal and generic + (GUI) name, description, a list of authors, etc. That information is used to show + plugin information in various places like "about application" dialog, plugin selector + dialog, etc. Plugin does not take ownership on info object, also its lifetime should + be equal to the lifetime of the plugin. + @param parent The parent object for the plugin. Parent object must implement @ref KDevApi + interface. Otherwise the plugin will not be constructed. + @param name The internal name which identifies the plugin.*/ + KDevPlugin(const KDevPluginInfo *info, QObject *parent, const char *name = 0); + + /**Destructs a plugin.*/ + virtual ~KDevPlugin(); + + /**Provides an information about the plugin. + @return KAboutData object which was initialized in the constructor.*/ + const KDevPluginInfo* info(); + + /**@return A reference to the toplevel widget.*/ + KDevMainWindow *mainWindow(); + + /**@return A reference to the application core - an object which provides + basic functionalities for inter-parts communications / cooperation.*/ + KDevCore *core() const; + + /**@return A reference to the current project component or 0 if no project is loaded.*/ + KDevProject *project() const; + + /**@return A reference to the language support component or 0 if no support available.*/ + KDevLanguageSupport *languageSupport() const; + + /**@return A reference to the memory symbol store.*/ + CodeModel *codeModel() const; + + /**@return A reference to the DOM tree that represents the project file or 0 if no project is loaded.*/ + QDomDocument *projectDom() const; + + /**@return A reference to the part controller which is used to manipulate loaded KParts.*/ + KDevPartController *partController() const; + + /**@return A reference to the plugin controller which is used to manipulate loaded plugin.*/ + virtual KDevPluginController *pluginController() const; + + /**@return A reference to the code repository (accessor to persistent symbol stores).*/ + KDevCodeRepository* codeRepository() const; + + /**Queries for the plugin which supports given service type (such plugins are called extensions in KDevelop). + All already loaded plugins will be queried and the <b>first loaded one</b> to support + the service type will be returned. Any plugin can be an extension, only "ServiceTypes=..." + entry is required in .desktop file for that plugin. + + Template argument is used as a type to cast the result to. This is done because extension + is usually derived from a certain base class and not directly from KDevPlugin. + @param serviceType The service type of an extension (like "KDevelop/SourceFormatter"). + @param constraint The constraint which is applied when quering for the service. This + constraint is a usual KTrader constraint statement (like "[X-KDevelop-Foo]=='MyFoo'"). + @return A KDevelop extension plugin for given service type or 0 if no plugin supports it*/ + template <class Extension> + Extension *extension(const QString &serviceType, const QString &constraint = "") + { + return static_cast<Extension*>(extension_internal(serviceType, constraint)); + } + + /**Override this base class method to restore any settings which differs from project to project. + Data can be read from a certain subtree of the project session file. + During project loading, respectively project session (.kdevses) loading, + this method will be called to give a chance to adapt the plugin to + the newly loaded project. For instance, the debugger plugin might restore the + list of breakpoints from the previous debug session for the certain project. + @note Take attention to the difference to common not-project-related session stuff. + They belong to the application rc file (kdeveloprc) + @note Project session file is useful for settings which cannot be shared between + developers. If a setting should be shared, modify projectDom instead. + @param el The parent DOM element for plugins session settings.*/ + virtual void restorePartialProjectSession(const QDomElement* el); + + /**Saves session settings. + @sa restorePartialProjectSession - this is the other way round, the same just for saving.*/ + virtual void savePartialProjectSession(QDomElement* el); + +private: + KDevPlugin *extension_internal(const QString &serviceType, const QString &constraint = ""); + + KDevApi *m_api; + class Private; + Private *d; +}; + +#endif diff --git a/lib/interfaces/kdevplugincontroller.cpp b/lib/interfaces/kdevplugincontroller.cpp new file mode 100644 index 00000000..ca2321de --- /dev/null +++ b/lib/interfaces/kdevplugincontroller.cpp @@ -0,0 +1,49 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "kdevplugincontroller.h" + +#include "kdevplugin.h" + +KDevPluginController::KDevPluginController() +{ +} + +KTrader::OfferList KDevPluginController::query(const QString &serviceType, + const QString &constraint) +{ + return KTrader::self()->query(serviceType, + QString("%1 and [X-KDevelop-Version] == %2").arg(constraint).arg(KDEVELOP_PLUGIN_VERSION)); +} + +KTrader::OfferList KDevPluginController::queryPlugins(const QString &constraint) +{ + return query("KDevelop/Plugin", constraint); +} + +KURL::List KDevPluginController::profileResources(const QString &/*nameFilter*/) +{ + return KURL::List(); +} + +KURL::List KDevPluginController::profileResourcesRecursive(const QString &/*nameFilter*/) +{ + return KURL::List(); +} + +#include "kdevplugincontroller.moc" diff --git a/lib/interfaces/kdevplugincontroller.h b/lib/interfaces/kdevplugincontroller.h new file mode 100644 index 00000000..8bb70bd0 --- /dev/null +++ b/lib/interfaces/kdevplugincontroller.h @@ -0,0 +1,118 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVPLUGINCONTROLLER_H +#define KDEVPLUGINCONTROLLER_H + +#include <qobject.h> + +#include <kurl.h> +#include <ktrader.h> + +/** +@file kdevplugincontroller.h +KDevelop plugin controller interface. +*/ + +class KDevPlugin; +class ProfileEngine; + +/** +The base class for KDevelop plugin controller. +Plugin controller is responsible for quering, loading and unloading available plugins. +*/ +class KDevPluginController: public QObject +{ + Q_OBJECT +public: + + /** + * Returns a uniquely specified plugin. If it isn't already loaded, it will be. + * Use with caution! See extension for parameter details. + */ + virtual KDevPlugin * loadPlugin( const QString & serviceType, const QString & constraint ) = 0; + + /** + * Unloads the plugin specified by @p plugin + * @param plugin The plugin desktopEntryName of the plugin to unload + */ + virtual void unloadPlugin( const QString & plugin ) = 0; + + /**Unloads plugins specified by @p list. + @param list The list of plugin names to unload. plugin name corresponds + to the "Name" property in plugin .desktop file.*/ + virtual void unloadPlugins(QStringList const &list) = 0; + + /**@return The list of currently loaded plugins.*/ + virtual const QValueList<KDevPlugin*> loadedPlugins() = 0; + + /**Queries for the plugin which supports given service type. + All already loaded plugins will be queried and the first one to support the service type + will be returned. Any plugin can be an extension, only "ServiceTypes=..." entry is + required in .desktop file for that plugin. + @param serviceType The service type of an extension (like "KDevelop/SourceFormatter") + @param constraint The constraint which is applied when quering for the service. This + constraint is a usual KTrader constraint statement (like "[X-KDevelop-Foo]=='MyFoo'"). + @return A KDevelop extension plugin for given service type or 0 if no plugin supports it*/ + virtual KDevPlugin *extension(const QString &serviceType, const QString &constraint = "") = 0; + + /**Queries KDevelop services. Version is checked automatically + by adding proper X-KDevelop-Version=N statement into the query. + @param serviceType The service type to query, for example "KDevelop/Plugin" or + "KDevelop/SourceFormatter." + @param constraint A constraint for the service. Do not include plugin version number - it + is done automatically. + @return The list of plugin offers.*/ + static KTrader::OfferList query(const QString &serviceType, const QString &constraint); + + /**Queries KDevelop plugins. Works like + KDevPluginController::query with serviceType set to "KDevelop/Plugin". + @param constraint A constraint for the service. Do not include plugin version number - it + is done automatically. + @return The list of plugin offers.*/ + static KTrader::OfferList queryPlugins(const QString &constraint); + + /**Reimplement this function only if your shell supports plugin profiles. + @return The list of URLs to the profile resources (files) with given @p extension. + @param nameFilter Name filter for files. @see QDir::setNameFilter documentation + for name filters syntax.*/ + virtual KURL::List profileResources(const QString &nameFilter); + + /**Reimplement this function only if your shell supports plugin profiles. + @return The list of URLs to the resources (files) with given @p extension. + This list is obtained by a recursive search that process given profile + and all it's subprofiles. + @param nameFilter Name filter for files. @see QDir::setNameFilter documentation + for name filters syntax.*/ + virtual KURL::List profileResourcesRecursive(const QString &nameFilter); + + /** @return The current Profile Engine */ + virtual ProfileEngine &engine() = 0; + +signals: + /**Emitted when a plugin profile was changed (reloaded, other profile opened, etc.). + Should work only on shells with plugin profiles support.*/ + void profileChanged(); + +protected: + /**Constructor.*/ + KDevPluginController(); + +}; + +#endif diff --git a/lib/interfaces/kdevplugininfo.cpp b/lib/interfaces/kdevplugininfo.cpp new file mode 100644 index 00000000..98f8d03d --- /dev/null +++ b/lib/interfaces/kdevplugininfo.cpp @@ -0,0 +1,181 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "kdevplugininfo.h" + +#include <qvariant.h> + +#include <kservice.h> +#include <kdebug.h> + +#include "kdevplugincontroller.h" + +struct KDevPluginInfo::Private { + QString m_pluginName; + QString m_rawGenericName; + + QString m_genericName; + QString m_description; + QString m_icon; + + QString m_version; + int m_licenseType; + QString m_copyrightStatement; + QString m_homePageAddress; + QString m_bugsEmailAddress; + + QValueList<KAboutPerson> m_authors; + QValueList<KAboutPerson> m_credits; + + KAboutData *m_data; +}; + + +KDevPluginInfo::KDevPluginInfo(const QString &pluginName) + :d(new Private()) +{ + d->m_pluginName = pluginName; + + KService::Ptr offer = KService::serviceByDesktopName(pluginName); + if (offer != 0) + { + d->m_genericName = offer->genericName(); + d->m_icon = offer->icon(); + d->m_description = offer->comment(); + + d->m_rawGenericName = offer->untranslatedGenericName(); + + d->m_version = offer->property("X-KDevelop-Plugin-Version").toString(); + d->m_homePageAddress = offer->property("X-KDevelop-Plugin-Homepage").toString(); + d->m_bugsEmailAddress = offer->property("X-KDevelop-Plugin-BugsEmailAddress").toString(); + d->m_copyrightStatement = offer->property("X-KDevelop-Plugin-Copyright").toString(); + + QString lic = offer->property("X-KDevelop-Plugin-License").toString(); + if (lic == "GPL") + d->m_licenseType = KAboutData::License_GPL; + else if (lic == "LGPL") + d->m_licenseType = KAboutData::License_LGPL; + else if (lic == "BSD") + d->m_licenseType = KAboutData::License_BSD; + else if (lic == "QPL") + d->m_licenseType = KAboutData::License_QPL; + else if (lic == "Artistic") + d->m_licenseType = KAboutData::License_Artistic; + else if (lic == "Custom") + d->m_licenseType = KAboutData::License_Custom; + else + d->m_licenseType = KAboutData::License_Unknown; + + d->m_data = new KAboutData(d->m_pluginName.ascii(), d->m_rawGenericName.ascii(), "1", 0, d->m_licenseType); + } + else + kdDebug() << "Unable to load information for plugin: " << pluginName + << ". Check if " << pluginName << ".desktop exists." << endl; +} + + +KDevPluginInfo::operator KAboutData *() const +{ + return d->m_data; +} + +QString KDevPluginInfo::pluginName() const +{ + return d->m_pluginName; +} + +QString KDevPluginInfo::genericName() const +{ + return d->m_genericName; +} + +QString KDevPluginInfo::icon() const +{ + return d->m_icon; +} + +QString KDevPluginInfo::description() const +{ + return d->m_description; +} + +QString KDevPluginInfo::version() const +{ + return d->m_version; +} + +int KDevPluginInfo::licenseType() const +{ + return d->m_licenseType; +} + +QString KDevPluginInfo::license() const +{ + KDevPluginInfo &info = *const_cast<KDevPluginInfo*>(this); +// return KAboutData(info).license(); + KAboutData *data = info; + return data->license(); +} + +QString KDevPluginInfo::copyrightStatement() const +{ + return d->m_copyrightStatement; +} + +QString KDevPluginInfo::homePageAddress() const +{ + return d->m_homePageAddress; +} + +QString KDevPluginInfo::bugsEmailAddress() const +{ + return d->m_bugsEmailAddress; +} + +QVariant KDevPluginInfo::property(const QString &name) const +{ + KTrader::OfferList offers = KDevPluginController::queryPlugins(QString("Name='%1'").arg(d->m_pluginName)); + if (offers.count() == 1) + return offers.first()->property(name); + return QVariant(); +} + +QVariant KDevPluginInfo::operator [](const QString &name) const +{ + return property(name); +} + +QStringList KDevPluginInfo::propertyNames( ) const +{ + KTrader::OfferList offers = KDevPluginController::queryPlugins(QString("Name='%1'").arg(d->m_pluginName)); + if (offers.count() == 1) + return offers.first()->propertyNames(); + return QStringList(); +} + +void KDevPluginInfo::addAuthor(const char *name, const char *task, + const char *emailAddress, const char *webAddress) +{ + d->m_authors.append(KAboutPerson(name, task, emailAddress, webAddress)); +} + +void KDevPluginInfo::addCredit(const char *name, const char *task, + const char *emailAddress, const char *webAddress) +{ + d->m_credits.append(KAboutPerson(name, task, emailAddress, webAddress)); +} diff --git a/lib/interfaces/kdevplugininfo.h b/lib/interfaces/kdevplugininfo.h new file mode 100644 index 00000000..b8a2b954 --- /dev/null +++ b/lib/interfaces/kdevplugininfo.h @@ -0,0 +1,111 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVPLUGININFO_H +#define KDEVPLUGININFO_H + +#include <qvariant.h> + +#include <kaboutdata.h> + +/** +@file kdevplugininfo.h +KDevelop plugin information container class. +*/ + +/** +Information about KDevelop plugin. +It uses plugin name to query plugin .desktop files and +retrieve most information. Names of authors and credits to other people +must be added manually using KDevPluginInfo::addAuthor and KDevPluginInfo::addCredit +methods. +@sa KDevPlugin class documentation for more information about .desktop files. +*/ +class KDevPluginInfo +{ +public: + /**Constructor. + @param pluginName A name of a plugin. Must be the same as the name of a .desktop file + and the same as the location of plugin resource files.*/ + KDevPluginInfo(const QString &pluginName); + + /**Casts KDevPluginInfo to KAboutData. Uses KDevPluginInfo::pluginName as KAboutData::appName, + KDevPluginInfo::rawGenericName as KAboutData::programName KDevPluginInfo::licenseType as KAboutData::licenseType. Other parameters are ignored.*/ + operator KAboutData*() const; + + /**@return A name of a plugin (always untranslated).*/ + QString pluginName() const; + /**@return A generic name of a plugin (translated). Use this in GUI. Information is taken from .desktop file.*/ + QString genericName() const; + /**@return An icon name of a plugin. Information is taken from .desktop file.*/ + QString icon() const; + /**@return A description of a plugin. Information is taken from .desktop file.*/ + QString description() const; + /**@return A version of a plugin. Information is taken from .desktop file.*/ + QString version() const; + /**@return A license type of a plugin. Information is taken from .desktop file.*/ + int licenseType() const; + /**@return A license text for known license types (GPL, LGPL, BSD, Artistic, QPL).*/ + QString license() const; + /**@return A copyright statement of a plugin. Information is taken from .desktop file.*/ + QString copyrightStatement() const; + /**@return A home page of a plugin. Information is taken from .desktop file.*/ + QString homePageAddress() const; + /**@return A email address for bugs of a plugin. Information is taken from .desktop file.*/ + QString bugsEmailAddress() const; + + /**@param name The name of a property. + @return Any property value which exists in .desktop file.*/ + QVariant property(const QString &name) const; + /**@param name The name of a property. + @return Any property value which exists in .desktop file.*/ + QVariant operator [] (const QString &name) const; + /**@return A list of property names set in .desktop file.*/ + QStringList propertyNames() const; + + /**@return A list of plugin authors.*/ + const QValueList<KAboutPerson> authors() const; + /**@return A list of other plugin contributors.*/ + const QValueList<KAboutPerson> credits() const; + + /**Adds an author to the list of authors. + You can call this function as many times you need. Each entry is appended to a list. + @param name The developer's name in UTF-8 encoding. + @param task What the person is responsible for. This text can contain newlines. It should be marked for translation like this: I18N_NOOP("Task description..."). Can be 0. + @param emailAddress An Email address where the person can be reached. Can be 0. + @param webAddress The person's homepage or a relevant link. Start the address with "http://". "http://some.domain" is correct, "some.domain" is not. Can be 0.*/ + void addAuthor(const char *name, const char *task, + const char *emailAddress = 0, const char *webAddress = 0); + /**Adds a contributor to the list of contributors. + You can call this function as many times you need. Each entry is appended to a list. + @param name The developer's name in UTF-8 encoding. + @param task What the person is responsible for. This text can contain newlines. It should be marked for translation like this: I18N_NOOP("Task description..."). Can be 0. + @param emailAddress An Email address where the person can be reached. Can be 0. + @param webAddress The person's homepage or a relevant link. Start the address with "http://". "http://some.domain" is correct, "some.domain" is not. Can be 0.*/ + void addCredit(const char *name, const char *task = 0, + const char *emailAddress = 0, const char *webAddress = 0); + +private: + /**Returns the untranslated generic name of a plugin as set in .desktop file.*/ + const char *rawGenericName() const; + + class Private; + Private *d; +}; + +#endif diff --git a/lib/interfaces/kdevproject.cpp b/lib/interfaces/kdevproject.cpp new file mode 100644 index 00000000..e4e3281d --- /dev/null +++ b/lib/interfaces/kdevproject.cpp @@ -0,0 +1,164 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2002-2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2002 Simon Hausmann <hausmann@kde.org> + Copyright (C) 2003 Jens Dagerbo <jens.dagerbo@swipnet.se> + Copyright (C) 2003 Mario Scalas <mario.scalas@libero.it> + Copyright (C) 2003-2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <kdebug.h> + +#include "kdevproject.h" +#include <urlutil.h> +#include <qfileinfo.h> +#include <qtimer.h> +#include "kdevprojectiface.h" + +struct KDevProject::Private { + QMap<QString, QString> m_absToRel; + QStringList m_symlinkList; + QTimer *m_timer; + KDevProjectIface *m_iface; +}; + +KDevProject::KDevProject(const KDevPluginInfo *info, QObject *parent, const char *name) + : KDevPlugin(info, parent, name), d(new KDevProject::Private()) +{ + connect( this, SIGNAL(addedFilesToProject(const QStringList& )), this, SLOT(buildFileMap()) ); + connect( this, SIGNAL(removedFilesFromProject(const QStringList& )), this, SLOT(buildFileMap()) ); + + connect( this, SIGNAL(addedFilesToProject(const QStringList& )), this, SLOT(slotAddFilesToFileMap(const QStringList& )) ); + connect( this, SIGNAL(removedFilesFromProject(const QStringList& )), this, SLOT(slotRemoveFilesFromFileMap(const QStringList& )) ); + + d->m_timer = new QTimer(this); + connect(d->m_timer, SIGNAL(timeout()), this, SLOT(slotBuildFileMap())); + d->m_iface = new KDevProjectIface(this); +} + +KDevProject::~KDevProject() +{ + d->m_timer->stop(); + delete d->m_iface; + delete d->m_timer; + delete d; +} + +void KDevProject::changedFile( const QString & fileName ) +{ + QStringList fileList; + fileList.append ( fileName ); + + emit changedFilesInProject( fileList ); + +} + +void KDevProject::changedFiles( const QStringList & fileList ) +{ + emit changedFilesInProject( fileList ); +} + +KDevProject::Options KDevProject::options() const +{ + return (KDevProject::Options)0; +} + +bool KDevProject::isProjectFile( const QString & absFileName ) +{ + return d->m_absToRel.contains( absFileName ); +} + +QString KDevProject::relativeProjectFile( const QString & absFileName ) +{ + if( isProjectFile(absFileName) ) + return d->m_absToRel[ absFileName ]; + return QString::null; +} + +void KDevProject::buildFileMap() +{ + d->m_timer->stop(); + d->m_timer->start(0, true); +} + +void KDevProject::slotBuildFileMap() +{ + kdDebug(9000) << k_funcinfo << endl; + + d->m_absToRel.clear(); + d->m_symlinkList.clear(); + const QStringList fileList = allFiles(); + for( QStringList::ConstIterator it=fileList.begin(); it!=fileList.end(); ++it ) + { + QFileInfo fileInfo( projectDirectory() + "/" + *it ); + d->m_absToRel[ URLUtil::canonicalPath(fileInfo.absFilePath()) ] = *it; + + if ( URLUtil::canonicalPath( fileInfo.absFilePath() ) != fileInfo.absFilePath() ) + { + d->m_symlinkList << *it; + } + } +} + +void KDevProject::openProject( const QString & /*dirName*/, const QString & /*projectName*/ ) +{ + buildFileMap(); +} + +QStringList KDevProject::symlinkProjectFiles( ) +{ + return d->m_symlinkList; +} + +QString KDevProject::defaultRunDirectory(const QString& projectPluginName) const +{ + return DomUtil::readEntry(*projectDom(), "/" + projectPluginName + "/run/globalcwd"); +} + +void KDevProject::slotAddFilesToFileMap( const QStringList & fileList ) +{ + QStringList::ConstIterator it = fileList.begin(); + while( it != fileList.end() ) + { + QFileInfo fileInfo( projectDirectory() + "/" + *it ); + d->m_absToRel[ URLUtil::canonicalPath(fileInfo.absFilePath()) ] = *it; + + if ( URLUtil::canonicalPath( fileInfo.absFilePath() ) != fileInfo.absFilePath() ) + { + d->m_symlinkList << *it; + } + + ++it; + } +} + +void KDevProject::slotRemoveFilesFromFileMap( const QStringList & fileList ) +{ + QStringList::ConstIterator it = fileList.begin(); + while( it != fileList.end() ) + { + QFileInfo fileInfo( projectDirectory() + "/" + *it ); + d->m_absToRel.remove( URLUtil::canonicalPath(fileInfo.absFilePath()) ); + + d->m_symlinkList.remove( *it ); + + ++it; + } +} + +#include "kdevproject.moc" diff --git a/lib/interfaces/kdevproject.h b/lib/interfaces/kdevproject.h new file mode 100644 index 00000000..64c8ed80 --- /dev/null +++ b/lib/interfaces/kdevproject.h @@ -0,0 +1,218 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Copyright (C) 2001-2002 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2002-2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2002 Simon Hausmann <hausmann@kde.org> + Copyright (C) 2003 Jens Dagerbo <jens.dagerbo@swipnet.se> + Copyright (C) 2003 Mario Scalas <mario.scalas@libero.it> + Copyright (C) 2003-2004 Alexander Dymo <adymo@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KDEVPROJECT_H +#define KDEVPROJECT_H + +#include "kdevplugin.h" +#include "domutil.h" + +#include <qstringlist.h> +#include <qmap.h> + +/** +@file kdevproject.h +KDevelop project interface. +*/ + +class QTimer; + +/** +KDevelop project interface. +Plugins implementing the KDevProject interfaces are used to manage projects. + +Project can be considered as a way of grouping files (in text editors) or +as a way of providing support for a build system (like it is done in KDevelop IDE buildtools). +*/ +class KDevProject: public KDevPlugin +{ + Q_OBJECT +public: + /**Constructs a project plugin. + @param info Important information about the plugin - plugin internal and generic + (GUI) name, description, a list of authors, etc. That information is used to show + plugin information in various places like "about application" dialog, plugin selector + dialog, etc. Plugin does not take ownership on info object, also its lifetime should + be equal to the lifetime of the plugin. + @param parent The parent object for the plugin. Parent object must implement @ref KDevApi + interface. Otherwise the plugin will not be constructed. + @param name The internal name which identifies the plugin.*/ + KDevProject(const KDevPluginInfo *info, QObject *parent=0, const char *name=0); + /**Destructor.*/ + virtual ~KDevProject(); + + /**Options of the project plugin.*/ + enum Options { + UsesOtherBuildSystem = 0 /**<Project uses unknown or unspecified build system or build system is not used at all.*/, + UsesAutotoolsBuildSystem = 1 /**<Project uses autotools for building.*/, + UsesQMakeBuildSystem =2 /**<Project uses qmake for building.*/ + }; + + /**This method is invoked when the project is opened + (i.e. actually just after this class has been + instantiated). + @param dirName The project directory, which should afterwards be returned by + the projectDirectory() method. + @param projectName The project name, which is equivalent + to the project file name without the suffix.*/ + virtual void openProject(const QString &dirName, const QString &projectName); + + /**This method is invoked when the project is about to be closed.*/ + virtual void closeProject() = 0; + + /**Reimplement this method to set project plugin options. Default implementation + returns KDevProject::UsesOtherBuildSystem.*/ + virtual Options options() const; + + /**@return The canonical absolute directory of the project. Canonical means that + a path does not contain symbolic links or redundant "." or ".." elements.*/ + virtual QString projectDirectory() const = 0; + + /**Returns the name of the project.*/ + virtual QString projectName() const = 0; + + /**@return The environment variables that sould be set before running mainProgram().*/ + virtual DomUtil::PairList runEnvironmentVars() const = 0; + + /**@return The path to main binary program of the project. + @param relative if true then the path returned is relative to the project directory.*/ + virtual QString mainProgram() const = 0; + + /**Absolute path (directory) from where the mainProgram() should be run.*/ + virtual QString runDirectory() const = 0; + + /**The command line arguments that the mainProgram() should be run with.*/ + virtual QString runArguments() const = 0; + + /**The command line arguments that the mainProgram() should be debugged with.*/ + virtual QString debugArguments() const = 0; + + /**Returns the path (relative to the project directory) + of the active directory. All newly automatically generated + classes and files are usually added here.*/ + virtual QString activeDirectory() const = 0; + + /**@return The canonical build directory of the project. + If the separate build directory is not supported, this should + return the same as projectDiretory(). Canonical means that + a path does not contain symbolic links or redundant "." or ".." elements.*/ + virtual QString buildDirectory() const = 0; + + /**@return The list of all files in the project. The names are relative to + the project directory.*/ + virtual QStringList allFiles() const = 0; + + /**@return The list of files that are part of the distribution but not under + project control. Used mainly to package and publish extra files among with the project.*/ + virtual QStringList distFiles() const = 0; + + /**Adds a list of files to the project. Provided for convenience when adding many files. + @param fileList The list of file names relative to the project directory.*/ + virtual void addFiles(const QStringList &fileList) = 0; + + /**Adds a file to the project. + @param fileName The file name relative to the project directory.*/ + virtual void addFile(const QString &fileName)= 0; + + /**Removes a list of files from the project. Provided for convenience when removing many files. + @param fileList The list of file names relative to the project directory.*/ + virtual void removeFiles(const QStringList& fileList)= 0; + + /**Removes a file from the project. + @param fileName The file name relative to the project directory.*/ + virtual void removeFile(const QString &fileName) = 0; + + /**Notifies the project about changes to the files. Provided for + convenience when changing many files. + @param fileList The list of file names relative to the project directory..*/ + virtual void changedFiles(const QStringList &fileList); + + /**Notifies the project of a change to one of the files. + @param fileName The file name relative to the project directory.*/ + virtual void changedFile(const QString &fileName); + + /**@return true if the file @p absFileName is a part of the project. + @param absFileName Absolute name of a file to check.*/ + virtual bool isProjectFile(const QString &absFileName); + + /**@return The path (relative to the project directory) of the file @p absFileName. + @param absFileName Absolute name of a file.*/ + virtual QString relativeProjectFile(const QString &absFileName); + + /**@return The list of files known to the project through symlinks.*/ + virtual QStringList symlinkProjectFiles(); + +protected: + + /** Default implementation of runDirectory method. + * Uses 'projectPluginName' to obtain configuration from + * project DOM and returns: + * + * if /<projectPluginName>/run/directoryradio == executable + * The directory where the executable is. + * if /<projectPluginName>/run/directoryradio == build + * The build directory. + * if /kdevautoproject/run/directoryradio == custom + * The custom directory absolute path. + * Derived classes are supposed to explicitly call this implementation + */ + QString defaultRunDirectory(const QString& projectPluginName) const; + +private slots: + void buildFileMap(); + void slotBuildFileMap(); + void slotAddFilesToFileMap(const QStringList & fileList ); + void slotRemoveFilesFromFileMap(const QStringList & fileList ); + +signals: + /**Emitted when a new list of files has been added to the + project. Provided for convenience when many files were added. + @param fileList The file names relative to the project directory.*/ + void addedFilesToProject(const QStringList& fileList); + + /**Emitted when a list of files has been removed from the project. + Provided for convenience when many files were removed. + @param fileList The file names relative to the project directory.*/ + void removedFilesFromProject(const QStringList& fileList); + + /**Emitted when a list of files has changed in the project. + @param fileList The file names relative to the project directory.*/ + void changedFilesInProject(const QStringList& fileList); + + /**Emitted when one compile related command (make, make install, make ...) ends sucessfuly. + Used to reparse the files after a sucessful compilation.*/ + void projectCompiled(); + + /**Emitted when the active directory of the project changes + * @param olddir The old active directory + * @param newdir The new active directory + */ + void activeDirectoryChanged( const QString& olddir, const QString& newdir ); + +private: + class Private; + Private *d; +}; + +#endif diff --git a/lib/interfaces/kdevprojectiface.cpp b/lib/interfaces/kdevprojectiface.cpp new file mode 100644 index 00000000..07cc78b1 --- /dev/null +++ b/lib/interfaces/kdevprojectiface.cpp @@ -0,0 +1,186 @@ + + +/* This file is part of the KDE project + Copyright (C) 2005 Ian Reinhart Geiser <geiseri@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "kdevprojectiface.h" +#include "kdevproject.h" +#include "domutil.h" + +KDevProjectIface::KDevProjectIface(KDevProject *prj) + : QObject(prj), DCOPObject("KDevProject"), m_prj(prj) +{ +} + + +KDevProjectIface::~KDevProjectIface() +{ +} + +void KDevProjectIface::openProject( const QString & dirName, const QString & projectName ) +{ + m_prj->openProject(dirName,projectName); +} + +void KDevProjectIface::closeProject( ) +{ + m_prj->closeProject(); +} + +uint KDevProjectIface::options( ) const +{ + return (uint)m_prj->options(); +} + +QString KDevProjectIface::projectDirectory( ) const +{ + return m_prj->projectDirectory(); +} + +QString KDevProjectIface::projectName( ) const +{ + return m_prj->projectName(); +} + +QString KDevProjectIface::mainProgram() const +{ + return m_prj->mainProgram(); +} + +QString KDevProjectIface::runDirectory( ) const +{ + return m_prj->runDirectory(); +} + +QString KDevProjectIface::activeDirectory( ) const +{ + return m_prj->activeDirectory(); +} + +QString KDevProjectIface::buildDirectory( ) const +{ + return m_prj->buildDirectory(); +} + +QStringList KDevProjectIface::allFiles( ) const +{ + return m_prj->allFiles(); +} + +QStringList KDevProjectIface::distFiles( ) const +{ + return m_prj->distFiles(); +} + +void KDevProjectIface::addFiles( const QStringList & fileList ) +{ + m_prj->addFiles(fileList); +} + +void KDevProjectIface::addFile( const QString & fileName ) +{ + m_prj->addFile(fileName); +} + +void KDevProjectIface::removeFiles( const QStringList & fileList ) +{ + m_prj->removeFiles(fileList); +} + +void KDevProjectIface::removeFile( const QString & fileName ) +{ + m_prj->removeFile(fileName); +} + +void KDevProjectIface::changedFiles( const QStringList & fileList ) +{ + m_prj->changedFiles(fileList); +} + +void KDevProjectIface::changedFile( const QString & fileName ) +{ + m_prj->changedFile(fileName); +} + +bool KDevProjectIface::isProjectFile( const QString & absFileName ) +{ + return m_prj->isProjectFile(absFileName); +} + +QString KDevProjectIface::relativeProjectFile( const QString & absFileName ) +{ + return m_prj->relativeProjectFile(absFileName); +} + +QStringList KDevProjectIface::symlinkProjectFiles( ) +{ + return m_prj->symlinkProjectFiles(); +} + +QString KDevProjectIface::readEntry( const QString & path, const QString & defaultEntry ) +{ + return DomUtil::readEntry( *m_prj->projectDom(), path, defaultEntry); +} + +int KDevProjectIface::readIntEntry( const QString & path, int defaultEntry ) +{ + return DomUtil::readIntEntry(*m_prj->projectDom(), path,defaultEntry); +} + +bool KDevProjectIface::readBoolEntry( const QString & path, bool defaultEntry ) +{ + return DomUtil::readBoolEntry(*m_prj->projectDom(), path, defaultEntry); +} + +QStringList KDevProjectIface::readListEntry( const QString & path, const QString & tag ) +{ + return DomUtil::readListEntry(*m_prj->projectDom(), path, tag); +} + +QMap< QString, QString > KDevProjectIface::readMapEntry( const QString & path ) +{ + return DomUtil::readMapEntry(*m_prj->projectDom(), path); +} + +void KDevProjectIface::writeEntry( const QString & path, const QString & value ) +{ + DomUtil::writeEntry(*m_prj->projectDom(), path, value); +} + +void KDevProjectIface::writeIntEntry( const QString & path, int value ) +{ + DomUtil::writeIntEntry(*m_prj->projectDom(), path, value); +} + +void KDevProjectIface::writeBoolEntry( const QString & path, bool value ) +{ + DomUtil::writeBoolEntry(*m_prj->projectDom(), path, value); +} + +void KDevProjectIface::writeListEntry( const QString & path, const QString & tag, const QStringList & value ) +{ + DomUtil::writeListEntry(*m_prj->projectDom(), path, tag, value); +} + +void KDevProjectIface::writeMapEntry( const QString & path, const QMap< QString, QString > & map ) +{ + DomUtil::writeMapEntry(*m_prj->projectDom(), path, map); +} + +#include "kdevprojectiface.moc" diff --git a/lib/interfaces/kdevprojectiface.h b/lib/interfaces/kdevprojectiface.h new file mode 100644 index 00000000..ba299dfc --- /dev/null +++ b/lib/interfaces/kdevprojectiface.h @@ -0,0 +1,81 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Ian Reinhart Geiser <geiseri@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef KDEVPROJECTIFACE_H +#define KDEVPROJECTIFACE_H + +#include <dcopobject.h> +#include <qstringlist.h> +#include <qmap.h> + +class KDevProject; + +/** +DCOP Interface for the @ref KDevProject object. + +@author KDevelop Authors +*/ +class KDevProjectIface : public QObject, public DCOPObject +{ + Q_OBJECT + K_DCOP +public: + KDevProjectIface(KDevProject *prj); + + ~KDevProjectIface(); +k_dcop: + void openProject(const QString &dirName, const QString &projectName); + void closeProject(); + uint options() const; + QString projectDirectory() const; + QString projectName() const; + QString mainProgram() const; + QString runDirectory() const; + QString activeDirectory() const; + QString buildDirectory() const; + QStringList allFiles() const; + QStringList distFiles() const; + void addFiles(const QStringList &fileList); + void addFile(const QString &fileName); + void removeFiles(const QStringList& fileList); + void removeFile(const QString &fileName); + void changedFiles(const QStringList &fileList); + void changedFile(const QString &fileName); + bool isProjectFile(const QString &absFileName); + QString relativeProjectFile(const QString &absFileName); + QStringList symlinkProjectFiles(); + + // Project DOM access + QString readEntry(const QString &path, const QString &defaultEntry = QString::null); + int readIntEntry(const QString &path, int defaultEntry = 0); + bool readBoolEntry(const QString &path, bool defaultEntry = false); + QStringList readListEntry(const QString &path, const QString &tag); + QMap<QString, QString> readMapEntry(const QString &path); + + void writeEntry(const QString &path, const QString &value); + void writeIntEntry(const QString &path, int value); + void writeBoolEntry( const QString &path, bool value); + void writeListEntry(const QString &path, const QString &tag, const QStringList &value); + void writeMapEntry(const QString& path, const QMap<QString,QString> &map); + + private: + KDevProject *m_prj; +}; + +#endif diff --git a/lib/util/Mainpage.dox b/lib/util/Mainpage.dox new file mode 100644 index 00000000..dad04de5 --- /dev/null +++ b/lib/util/Mainpage.dox @@ -0,0 +1,10 @@ +/** +@mainpage The KDevelop Utility Library + +This library contains utility classes for the KDevelop architecture. + +<b>Link with</b>: -lkdevelop + +<b>Include path</b>: -I\$(kde_includes)/kdevelop/util +*/ + diff --git a/lib/util/Makefile.am b/lib/util/Makefile.am new file mode 100644 index 00000000..c170787e --- /dev/null +++ b/lib/util/Makefile.am @@ -0,0 +1,21 @@ +INCLUDES = -I$(top_srcdir)/lib/compat -I$(top_srcdir)/lib/interfaces -I$(top_srcdir)/lib/sourceinfo $(all_includes) + +noinst_LTLIBRARIES = libkdevutil.la + +libkdevutil_la_SOURCES = blockingkprocess.cpp configwidgetproxy.cpp domutil.cpp \ + execcommand.cpp filetemplate.cpp kdeveditorutil.cpp kdevjobtimer.cpp \ + kdevshellwidget.cpp kscriptactionmanager.cpp rurl.cpp settings.cpp urlutil.cpp + +METASOURCES = AUTO + +kdevelopincludedir = $(includedir)/kdevelop/util +kdevelopinclude_HEADERS = domutil.h execcommand.h filetemplate.h urlutil.h \ + configwidgetproxy.h rurl.h kscriptactionmanager.h + +DOXYGEN_REFERENCES = dcop interfaces kdecore kdefx kdeui khtml kmdi kio kjs kparts kutils kdevextensions kdevinterfaces +DOXYGEN_PROJECTNAME = KDevelop Utility Library +DOXYGEN_DOCDIRPREFIX = kdev +include ../../Doxyfile.am + +noinst_HEADERS = blockingkprocess.h kdeveditorutil.h kdevjobtimer.h \ + kdevshellwidget.h settings.h diff --git a/lib/util/blockingkprocess.cpp b/lib/util/blockingkprocess.cpp new file mode 100644 index 00000000..f0d6e6c3 --- /dev/null +++ b/lib/util/blockingkprocess.cpp @@ -0,0 +1,105 @@ +/*************************************************************************** +* Copyright (C) 2006 by Andras Mantia * +* amantia@kde.org * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +***************************************************************************/ + +#include "blockingkprocess.h" + +#include <qapplication.h> +#include <qtimer.h> + +BlockingKProcess::BlockingKProcess(QObject *parent, const char *name) + : KProcess(parent, name) +{ + m_stdOut = ""; + m_stdErr = ""; + m_timeoutValue = 60; + m_timer = 0L; + + connect(this, SIGNAL(receivedStdout(KProcess *, char *, int)), + this, SLOT(slotReceivedStdOut(KProcess *, char *, int))); + connect(this, SIGNAL(receivedStderr(KProcess *, char *, int)), + this, SLOT(slotReceivedStdErr(KProcess *, char *, int))); + connect(this, SIGNAL(processExited(KProcess *)), + this, SLOT(slotProcessExited(KProcess *))); +} + +BlockingKProcess::BlockingKProcess() + : KProcess() +{ + m_stdOut = ""; + m_stdErr = ""; + m_timeoutValue = 60; + m_timer = 0L; + connect(this, SIGNAL(receivedStdout(KProcess *, char *, int)), + this, SLOT(slotReceivedStdOut(KProcess *, char *, int))); + connect(this, SIGNAL(receivedStderr(KProcess *, char *, int)), + this, SLOT(slotReceivedStdErr(KProcess *, char *, int))); + connect(this, SIGNAL(processExited(KProcess *)), + this, SLOT(slotProcessExited(KProcess *))); +} + + +BlockingKProcess::~BlockingKProcess() +{ +} +bool BlockingKProcess::start(RunMode runmode, Communication comm) +{ + if (KProcess::start(runmode, comm)) + { + m_timeout = false; + m_timer = new QTimer(); + connect(m_timer, SIGNAL(timeout()), this, SLOT(slotTimeOut())); + m_timer->start(m_timeoutValue*1000, true); + enter_loop(); + delete m_timer; + m_timer = 0L; + return !m_timeout; + } else + return false; +} + + +void BlockingKProcess::slotReceivedStdOut(KProcess *, char *buffer, int buflen) +{ + m_stdOut += QString::fromLatin1(buffer, buflen); +} + +void BlockingKProcess::slotReceivedStdErr(KProcess *, char *buffer, int buflen) +{ + m_stdErr += QString::fromLatin1(buffer, buflen); +} + +void BlockingKProcess::slotProcessExited(KProcess *) +{ + qApp->exit_loop(); +} + +void BlockingKProcess::slotTimeOut() +{ + m_timeout = true; + kill(); + qApp->exit_loop(); +} + + +void qt_enter_modal( QWidget *widget ); +void qt_leave_modal( QWidget *widget ); + +void BlockingKProcess::enter_loop() +{ + QWidget dummy(0,0,WType_Dialog | WShowModal); + dummy.setFocusPolicy( QWidget::NoFocus ); + qt_enter_modal(&dummy); + qApp->enter_loop(); + qt_leave_modal(&dummy); +} + + +#include "blockingkprocess.moc" diff --git a/lib/util/blockingkprocess.h b/lib/util/blockingkprocess.h new file mode 100644 index 00000000..c5c8f5d9 --- /dev/null +++ b/lib/util/blockingkprocess.h @@ -0,0 +1,92 @@ + +/*************************************************************************** +* Copyright (C) 2006 by Andras Mantia * +* amantia@kde.org * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +***************************************************************************/ + + +#ifndef BLOCKINGKPROCESS_H +#define BLOCKINGKPROCESS_H + +#include <kprocess.h> + +/** + * Blocking version of KProcess, which stores the stdout. + * Differences between start(KProcess::Block, KProcess::StdOut) and this + * class are: + * - the GUI update is not blocked why the external process is running + * - in case of problems there is a timeout (defaults to 60 seconds), after which the + * process is terminated. + * - the stdout is caught - it the caller request it - and can be read by the caller + * @author Andras Mantia <amantia@kde.org> +*/ + +class QTimer; +class BlockingKProcess : public KProcess +{ + Q_OBJECT + +public: + BlockingKProcess(QObject *parent, const char *name=0); + BlockingKProcess(); + + virtual ~BlockingKProcess(); + + /** + * Start the process. It waits until the process exits or the timeout is hit. + * @param runmode @see KProcess, use KProcess::NotifyOnExit to get proper behaviour, + * not KProcess::Block + * @param comm if Stdout is passed, it catches the output. For the rest @see KProcess + * @return true in case of success, false if there are problems to start the process + * or it was killed because of the timeout. + */ + virtual bool start(RunMode runmode=NotifyOnExit, Communication comm=NoCommunication); + + /** + * Get the output of the run process + * @return the output + */ + QString stdOut() { return m_stdOut;} + /** + * Clear the internal stdout buffer. Useful in case the class is reused. + */ + void clearStdOut() { m_stdOut = "";} + /** + * Get the error output of the run process + * @return the output + */ + QString stdErr() { return m_stdErr;} + /** + * Clear the internal stderr buffer. Useful in case the class is reused. + */ + void clearStdErr() { m_stdErr = "";} + + /** + * Sets the timeout + * @param timeout seconds after which the process is considered hung and killed. 0 disables the timeout. + */ + void setTimeOut(int timeout) { m_timeoutValue = timeout; } + +private slots: + void slotReceivedStdOut(KProcess *proc, char *buffer, int buflen); + void slotReceivedStdErr(KProcess *proc, char *buffer, int buflen); + void slotProcessExited(KProcess *proc); + void slotTimeOut(); + +private: + void enter_loop(); + + QString m_stdOut; + QString m_stdErr; + bool m_timeout; + int m_timeoutValue; + QTimer *m_timer; +}; + +#endif diff --git a/lib/util/configwidgetproxy.cpp b/lib/util/configwidgetproxy.cpp new file mode 100644 index 00000000..128fa2ad --- /dev/null +++ b/lib/util/configwidgetproxy.cpp @@ -0,0 +1,105 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Jens Dagerbo <jens.dagerbo@swipnet.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + + + +#include <qstring.h> +#include <qvbox.h> + +#include <kdebug.h> +#include <kdialogbase.h> +#include <kiconloader.h> + +#include <kdevcore.h> + + +#include "configwidgetproxy.h" + + +ConfigWidgetProxy::ConfigWidgetProxy( KDevCore * core ) +{ + connect( core, SIGNAL(configWidget(KDialogBase*)), this, SLOT(slotConfigWidget( KDialogBase*)) ); + connect( core, SIGNAL(projectConfigWidget(KDialogBase*)), this, SLOT(slotProjectConfigWidget( KDialogBase*)) ); +} + +ConfigWidgetProxy::~ConfigWidgetProxy() +{} + +void ConfigWidgetProxy::createGlobalConfigPage( QString const & title, unsigned int pagenumber, QString const & icon ) +{ + _globalTitleMap.insert( pagenumber, qMakePair( title, icon ) ); +} + +void ConfigWidgetProxy::createProjectConfigPage( QString const & title, unsigned int pagenumber, QString const & icon ) +{ + _projectTitleMap.insert( pagenumber, qMakePair( title, icon ) ); +} + +void ConfigWidgetProxy::removeConfigPage( int pagenumber ) +{ + _globalTitleMap.remove( pagenumber ); + _projectTitleMap.remove( pagenumber ); +} + +void ConfigWidgetProxy::slotConfigWidget( KDialogBase * dlg ) +{ + TitleMap::Iterator it = _globalTitleMap.begin(); + while ( it != _globalTitleMap.end() ) + { + _pageMap.insert( dlg->addVBoxPage( it.data().first, it.data().first, BarIcon( it.data().second, KIcon::SizeMedium ) ), it.key() ); + ++it; + } + + connect( dlg, SIGNAL(aboutToShowPage(QWidget*)), this, SLOT( slotAboutToShowPage(QWidget*)) ); + connect( dlg, SIGNAL(destroyed()), this, SLOT(slotConfigWidgetDestroyed()) ); +} + +void ConfigWidgetProxy::slotProjectConfigWidget( KDialogBase * dlg ) +{ + TitleMap::Iterator it = _projectTitleMap.begin(); + while ( it != _projectTitleMap.end() ) + { + _pageMap.insert( dlg->addVBoxPage( it.data().first, it.data().first, BarIcon( it.data().second, KIcon::SizeMedium ) ), it.key() ); + ++it; + } + + connect( dlg, SIGNAL(aboutToShowPage(QWidget*)), this, SLOT( slotAboutToShowPage(QWidget*)) ); + connect( dlg, SIGNAL(destroyed()), this, SLOT(slotConfigWidgetDestroyed()) ); +} + +void ConfigWidgetProxy::slotConfigWidgetDestroyed( ) +{ + _pageMap.clear(); +} + +void ConfigWidgetProxy::slotAboutToShowPage( QWidget * page ) +{ + if ( !page ) return; + + PageMap::Iterator it = _pageMap.find( page ); + if ( it != _pageMap.end() ) + { + emit insertConfigWidget( static_cast<KDialogBase*>(const_cast<QObject*>(sender())), page, it.data() ); + _pageMap.remove( it ); + } +} + +#include "configwidgetproxy.moc" + +// kate: space-indent off; indent-width 4; replace-tabs off; tab-width 4; diff --git a/lib/util/configwidgetproxy.h b/lib/util/configwidgetproxy.h new file mode 100644 index 00000000..6380d002 --- /dev/null +++ b/lib/util/configwidgetproxy.h @@ -0,0 +1,129 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Jens Dagerbo <jens.dagerbo@swipnet.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _CONFIGWIDGETPROXY_H +#define _CONFIGWIDGETPROXY_H + +#include <qobject.h> +#include <qstring.h> +#include <qmap.h> + +class KDevCore; +class KDialogBase; + +/** +@file configwidgetproxy.h +Configuration widget proxy class. +*/ + +/** +This class can be used to implement demand-loading of config pages. +In order to avoid the potentially heavy and unneccessary creation +of a config page that might not be needed, this class can be used +to delay the config page creation until the user explicitly asks +for it. + +A typical case looks like this: +@code +#define GLOBALDOC_OPTIONS 1 +#define PROJECTDOC_OPTIONS 2 + +_configProxy = new ConfigWidgetProxy( core() ); +_configProxy->createGlobalConfigPage( i18n("My Part"), GLOBALDOC_OPTIONS, info()->icon() ); +_configProxy->createProjectConfigPage( i18n("My Part"), PROJECTDOC_OPTIONS, info()->icon() ); +connect( _configProxy, SIGNAL(insertConfigWidget(const QObject*, QWidget*, unsigned int )), + this, SLOT(insertConfigWidget(const QObject*, QWidget*, unsigned int )) ); + +... +... + +void MyPart::insertConfigWidget( QObject const * dlg, QWidget * page, unsigned int pagenumber ) +{ + if ( pagenumber == PROJECTDOC_OPTIONS ) { + MyPartGlobalSettings * w = new MyPartGlobalSettings( this, page ); + connect( dlg, SIGNAL(okClicked()), w, SLOT(slotAccept()) ); + } else if ( pagenumber == PROJECTDOC_OPTIONS ) { + MyPartProjectSettings * w = new MyPartProjectSettings( this, page ); + connect( dlg, SIGNAL(okClicked()), w, SLOT(slotAccept()) ); + } +} +@endcode + +Note that this replaces the functionality of typical KDevCore::configWidget() and +KDevCore::projectConfigWidget() slots. +*/ +class ConfigWidgetProxy : public QObject +{ +Q_OBJECT + +public: + /**Constructor. + @param core An instance of KDevelop Core.*/ + ConfigWidgetProxy( KDevCore * core ); + virtual ~ConfigWidgetProxy(); + + /** + * Tells the proxy you want a page in the Global Settings. + * @param title The title of the config page, shown in the settings dialog. + * @param pagenumber A per-proxy unique identifier, used when responding to insertConfigWidget() signal. + * @param icon The name of the icon to use. + */ + void createGlobalConfigPage( QString const & title, unsigned int pagenumber, QString const & icon = "kdevelop" ); + + /** + * Tells the proxy you want a page in the Project Settings. + * @param title The title of the config page, shown in the settings dialog. + * @param pagenumber A per-proxy unique identifier, used when responding to insertConfigWidget() signal. + * @param icon The name of the icon to use. + */ + void createProjectConfigPage( QString const & title, unsigned int pagenumber, QString const & icon = "kdevelop" ); + + /** + * Removes a config page from the proxy. Next time the settings dialog opens, this page will not be available. + * @param pagenumber The identifier set in createGlobalConfigPage() or createProjectConfigPage(). + */ + void removeConfigPage( int pagenumber ); + +signals: + /** + * The proxy emits this signal to notify the client that a specific config page has been requested. + * @param dlg The settings dialog. The client should connect to its okClicked() signal. + * @param page The setting page. The client should use this as parent to the config widget. + * @param pagenumber The identifier set in createGlobalConfigPage() or createProjectConfigPage(). Identifies the requested config page. + */ + void insertConfigWidget( const KDialogBase * dlg, QWidget * page, unsigned int pagenumber ); + +private slots: + void slotConfigWidget( KDialogBase * ); + void slotProjectConfigWidget( KDialogBase * ); + void slotConfigWidgetDestroyed(); + void slotAboutToShowPage( QWidget * page ); + +private: + typedef QMap<unsigned int, QPair<QString,QString> > TitleMap; + typedef QMap<QWidget*, int> PageMap; + + TitleMap _globalTitleMap; + TitleMap _projectTitleMap; + PageMap _pageMap; +}; + +#endif + +// kate: space-indent off; indent-width 4; replace-tabs off; tab-width 4; diff --git a/lib/util/domutil.cpp b/lib/util/domutil.cpp new file mode 100644 index 00000000..b183717f --- /dev/null +++ b/lib/util/domutil.cpp @@ -0,0 +1,367 @@ +/*************************************************************************** + * Copyright (C) 2001-2002 by Bernd Gehrmann * + * bernd@kdevelop.org * + * default support: Eray Ozkural (exa) * + * additions: John Firebaugh <jfirebaugh@kde.org> * + * Jakob Simon-Gaarde <jakob@simon-gaarde.dk> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "domutil.h" + +#include <kdebug.h> +#include <qstringlist.h> +#include <qfile.h> + + +void DomUtil::makeEmpty( QDomElement& e ) +{ + while( !e.firstChild().isNull() ) + e.removeChild( e.firstChild() ); +} + +QDomElement DomUtil::elementByPath(const QDomDocument &doc, const QString &path) +{ + QStringList l = QStringList::split('/', path); + + QDomElement el; + if(&doc) el = doc.documentElement(); + QStringList::ConstIterator it; + for (it = l.begin(); it != l.end(); ++it) { + el = el.namedItem(*it).toElement(); + } + + return el; +} + + +QString DomUtil::readEntry(const QDomDocument &doc, const QString &path, const QString &defaultEntry) +{ + QDomElement el = elementByPath(doc, path); + if (el.isNull()) + return defaultEntry; + else + return el.firstChild().toText().data(); +} + +/// @todo consider whether it's okay to accept empty string == default value +/// if not use the below type +///typedef pair<bool,QString> EltInfo; + +QString DomUtil::readEntryAux(const QDomDocument &doc, const QString &path) +{ + QDomElement el = elementByPath(doc, path); + if (el.isNull()) + return QString::null; + else + return el.firstChild().toText().data(); +} + +int DomUtil::readIntEntry(const QDomDocument &doc, const QString &path, int defaultEntry) +{ + QString entry = readEntryAux(doc, path); + if (entry.isNull()) + return defaultEntry; + else + return entry.toInt(); +} + + +bool DomUtil::readBoolEntry(const QDomDocument &doc, const QString &path, bool defaultEntry) +{ + QString entry = readEntryAux(doc, path); + if (entry.isNull()) + return defaultEntry; + else + return entry == "TRUE" || entry == "true"; +} + + +QStringList DomUtil::readListEntry(const QDomDocument &doc, const QString &path, const QString &tag) +{ + QStringList list; + + QDomElement el = elementByPath(doc, path); + QDomElement subEl = el.firstChild().toElement(); + while (!subEl.isNull()) { + if (subEl.tagName() == tag) + list << subEl.firstChild().toText().data(); + subEl = subEl.nextSibling().toElement(); + } + + return list; +} + + +DomUtil::PairList DomUtil::readPairListEntry(const QDomDocument &doc, const QString &path, const QString &tag, + const QString &firstAttr, const QString &secondAttr) +{ + PairList list; + + QDomElement el = elementByPath(doc, path); + QDomElement subEl = el.firstChild().toElement(); + while (!subEl.isNull()) { + if (subEl.tagName() == tag) { + QString first = subEl.attribute(firstAttr); + QString second = subEl.attribute(secondAttr); + list << Pair(first, second); + } + subEl = subEl.nextSibling().toElement(); + } + + return list; +} + +QMap<QString, QString> DomUtil::readMapEntry(const QDomDocument &doc, const QString& path) +{ + QMap<QString, QString> map; + + QDomElement el = elementByPath(doc, path); + QDomElement subEl = el.firstChild().toElement(); + while (!subEl.isNull()) { + map[subEl.tagName()] = subEl.firstChild().toText().data(); + subEl = subEl.nextSibling().toElement(); + } + + return map; +} + +QDomElement DomUtil::namedChildElement( QDomElement& el, const QString& name ) +{ + QDomElement child = el.namedItem( name ).toElement(); + if (child.isNull()) { + child = el.ownerDocument().createElement( name ); + el.appendChild(child); + } + return child; +} + + +QDomElement DomUtil::createElementByPath(QDomDocument &doc, const QString &path) +{ + QStringList l = QStringList::split('/', path); + + QDomElement el; + if(&doc) el = doc.documentElement(); + QStringList::ConstIterator it; + for (it = l.begin(); it != l.end(); ++it) + el = DomUtil::namedChildElement( el, *it ); + + while (!el.firstChild().isNull()) + el.removeChild(el.firstChild()); + + return el; +} + + +void DomUtil::writeEntry(QDomDocument &doc, const QString &path, const QString &value) +{ + QDomElement el = createElementByPath(doc, path); + el.appendChild(doc.createTextNode(value)); +} + +void DomUtil::writeMapEntry(QDomDocument &doc, const QString &path, const QMap<QString, QString> &map) +{ + QString basePath( path + "/" ); + QMap<QString,QString>::ConstIterator it; + for (it = map.begin(); it != map.end(); ++it) + { + kdDebug( 9010 ) << "writing " << basePath << ";" << it.key() << ";" << it.data() << endl; + if( ! it.key().isEmpty() ) + writeEntry(doc, basePath + it.key(), it.data() ); + } +} + +void DomUtil::writeIntEntry(QDomDocument &doc, const QString &path, int value) +{ + writeEntry(doc, path, QString::number(value)); +} + + +void DomUtil::writeBoolEntry(QDomDocument &doc, const QString &path, bool value) +{ + writeEntry(doc, path, value? "true" : "false"); +} + + +void DomUtil::writeListEntry(QDomDocument &doc, const QString &path, const QString &tag, + const QStringList &value) +{ + QDomElement el = createElementByPath(doc, path); + + QStringList::ConstIterator it; + for (it = value.begin(); it != value.end(); ++it) { + QDomElement subEl = doc.createElement(tag); + subEl.appendChild(doc.createTextNode(*it)); + el.appendChild(subEl); + } +} + + +void DomUtil::writePairListEntry(QDomDocument &doc, const QString &path, const QString &tag, + const QString &firstAttr, const QString &secondAttr, + const PairList &value) +{ + QDomElement el = createElementByPath(doc, path); + + PairList::ConstIterator it; + for (it = value.begin(); it != value.end(); ++it) { + QDomElement subEl = doc.createElement(tag); + subEl.setAttribute(firstAttr, (*it).first); + subEl.setAttribute(secondAttr, (*it).second); + el.appendChild(subEl); + } +} + +DomPath DomUtil::resolvPathStringExt(const QString pathstring) +{ + // parse path + unsigned int i; + QStringList pathParts = QStringList::split('/',pathstring); + DomPath dompath; + for (i=0; i<pathParts.count(); i++) + { + QStringList pathElemParts = QStringList::split('|',pathParts[i],TRUE); + DomPathElement dompathelem; + dompathelem.tagName = pathElemParts[0].simplifyWhiteSpace(); + if (pathElemParts.count()>1) + { + // handle attributes + QStringList attrParts = QStringList::split(';',pathElemParts[1]); + for (unsigned int j=0; j<attrParts.count(); j++) + { + QStringList attribSet = QStringList::split('=',attrParts[j]); + if (attribSet.count()<2) + continue; + DomAttribute domattribute; + domattribute.name = attribSet[0].simplifyWhiteSpace(); + domattribute.value = attribSet[1].simplifyWhiteSpace(); + dompathelem.attribute.append(domattribute); + } + } + if (pathElemParts.count()>2) + dompathelem.matchNumber = pathElemParts[2].toInt(); + else + dompathelem.matchNumber = 0; // or else the first + dompath.append(dompathelem); + } + return dompath; +} + + +#define rightchild !wrongchild + +QDomElement DomUtil::elementByPathExt(QDomDocument &doc, const QString &pathstring) +{ + DomPath dompath = resolvPathStringExt(pathstring); + QDomElement elem = doc.documentElement(); + QDomNodeList children; + QDomElement nextElem = elem; + for (unsigned int j=0; j<dompath.count(); j++) + { + children = nextElem.childNodes(); + DomPathElement dompathelement= dompath[j]; + bool wrongchild = false; + int matchCount = 0; + for (unsigned int i=0; i<children.count(); i++) + { + wrongchild = false; + QDomElement child = children.item(i).toElement(); + QString tag = child.tagName(); + tag = dompathelement.tagName; + if (child.tagName() == dompathelement.tagName) + { + for (unsigned int k=0; k<dompathelement.attribute.count(); k++) + { + DomAttribute domattribute = dompathelement.attribute[k]; + QDomAttr domattr = child.attributeNode(domattribute.name); + if (domattr.isNull() || + domattr.value() != domattribute.value) + { + wrongchild = true; + break; + } + } + } + else + wrongchild=true; + if (rightchild) + { + if (dompathelement.matchNumber == matchCount++) + { + nextElem = child; + break; + } + } + } + if (wrongchild) + { + QDomElement nullDummy; + nullDummy.clear(); + return nullDummy; + } + } + return nextElem; +} + + +bool DomUtil::openDOMFile(QDomDocument &doc, QString filename) +{ + QFile file( filename ); + if ( !file.open( IO_ReadOnly ) ) + return false; + if ( !doc.setContent( &file ) ) { + file.close(); + return false; + } + file.close(); + return true; +} + +bool DomUtil::saveDOMFile(QDomDocument &doc, QString filename) +{ + QFile file( filename ); + if ( !file.open( IO_ReadWrite | IO_Truncate ) ) + return false; + QTextStream t( &file ); + t << doc.toString(); + file.close(); + return true; +} + +bool DomUtil::removeTextNodes(QDomDocument doc,QString pathExt) +{ + QDomElement elem = elementByPathExt(doc,pathExt); + if (elem.isNull()) + return false; + QDomNodeList children = elem.childNodes(); + for (unsigned int i=0;i<children.count();i++) + if (children.item(i).isText()) + elem.removeChild(children.item(i)); + return true; +} + + +bool DomUtil::appendText(QDomDocument doc, QString pathExt, QString text) +{ + QDomElement elem = elementByPathExt(doc,pathExt); + if (elem.isNull()) + return false; + elem.appendChild(doc.createTextNode(text)); + return true; +} + + +bool DomUtil::replaceText(QDomDocument doc, QString pathExt, QString text) +{ + if (removeTextNodes(doc,pathExt) && + appendText(doc,pathExt,text)) + return true; + else + return false; +} diff --git a/lib/util/domutil.h b/lib/util/domutil.h new file mode 100644 index 00000000..a301ef00 --- /dev/null +++ b/lib/util/domutil.h @@ -0,0 +1,226 @@ +/*************************************************************************** + * Copyright (C) 2001 by Bernd Gehrmann * + * bernd@kdevelop.org * + * jakob@simon-gaarde.dk * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef _DOMUTIL_H_ +#define _DOMUTIL_H_ + +#include <qdom.h> +#include <qpair.h> +#include <qstringlist.h> +#include <qvaluelist.h> +#include <qmap.h> + +/** +@file domutil.h +Utility functions to operate on %DOM. +*/ + +struct DomAttribute +{ + QString name; + QString value; +}; + +struct DomPathElement +{ + QString tagName; + QValueList<DomAttribute> attribute; + int matchNumber; // for use when more than one element matches the path +}; + +typedef QValueList<DomPathElement> DomPath; + +/** + * Utility class for conveniently accessing data in a %DOM tree. + */ +class DomUtil +{ +public: + typedef QPair<QString, QString> Pair; + typedef QValueList<Pair> PairList; + /** + * Remove all child elements from a given element. + */ + static void makeEmpty( QDomElement& ); + /** + * Reads a string entry. + */ + static QString readEntry(const QDomDocument &doc, const QString &path, const QString &defaultEntry = QString::null); + /** + * Reads a number entry. + */ + static int readIntEntry(const QDomDocument &doc, const QString &path, int defaultEntry = 0); + /** + * Reads a boolean entry. The strings "true" and "TRUE" are interpreted + * as true, all other as false. + */ + static bool readBoolEntry(const QDomDocument &doc, const QString &path, bool defaultEntry = false); + /** + * Reads a list entry. See writeListEntry(). + */ + static QStringList readListEntry(const QDomDocument &doc, const QString &path, const QString &tag); + /** + * Reads a list of string pairs. See writePairListEntry(). + */ + static PairList readPairListEntry(const QDomDocument &doc, const QString &path, const QString &tag, + const QString &firstAttr, const QString &secondAttr); + /** + * Reads a string to string map. See writeMapEntry() + */ + static QMap<QString, QString> readMapEntry(const QDomDocument &doc, const QString &path); + /** + * Retrieves an element by path, return null if any item along + * the path does not exist. + */ + static QDomElement elementByPath( const QDomDocument& doc, const QString& path ); + /** + * Retrieves an element by path, creating the necessary nodes. + */ + static QDomElement createElementByPath( QDomDocument& doc, const QString& path ); + /** + * Retrieves a child element, creating it if it does not exist. + * The return value is guaranteed to be non isNull() + */ + static QDomElement namedChildElement( QDomElement& el, const QString& name ); + /** + Writes a string entry. For example, + \verbatim + <code> + writeEntry(doc, "/general/special", "foo"); + </code> + \endverbatim creates the %DOM fragment: \verbatim + <code> + <general><special>foo</special></general> + </code> + \endverbatim + */ + static void writeEntry(QDomDocument &doc, const QString &path, const QString &value); + /** + * Writes a number entry. + */ + static void writeIntEntry(QDomDocument &doc, const QString &path, int value); + /** + * Writes a boolean entry. Booleans are stored as "true", "false" resp. + */ + static void writeBoolEntry(QDomDocument &doc, const QString &path, bool value); + /** + Writes a string list element. The list elements are separated by tag. For example, + \verbatim + <code> + QStringList l; l << "one" << "two"; + writeListEntry(doc, "/general/special", "el", l); + </code> + \endverbatim creates the %DOM fragment: \verbatim + <code> + <general><special><el>one</el><el>two</el></special></general> + </code> + \endverbatim + */ + static void writeListEntry(QDomDocument &doc, const QString &path, const QString &tag, + const QStringList &value); + /** + Writes a list of string pairs. The list elements are stored in the attributes + firstAttr and secondAttr of elements named tag. For example, + \verbatim + <code> + DomUtil::PairList l; + l << DomUtil::StringPair("one", "1"); + l << DomUtil::StringPair("two", "2"); + writePairListEntry(doc, "/general/special", "el", "first", "second", l); + </code> + \endverbatim creates the %DOM fragment: \verbatim + <code> + <general><special> + <el first="one" second="1"/> + <el first="two" second="2"/> + </special></general> + </code> + \endverbatim + */ + static void writePairListEntry(QDomDocument &doc, const QString &path, const QString &tag, + const QString &firstAttr, const QString &secondAttr, + const PairList &value); + /** + * Writes a string to string map. This map is stored in a way, that it can be read with + * readMapEntry() and readEntry() + */ + static void writeMapEntry(QDomDocument &doc, const QString& path, const QMap<QString,QString> &map); + + /** + * Resolves an extended path + * Extended path format: + * pathpart: tag[|attr1=value[;attr2=value;..][|matchNumber]] + * where matchNumber is zero-based + * path: pathpart[/pathpart/..] + */ + static DomPath resolvPathStringExt(const QString pathstring); + + /** + Retrieve an element specified with extended path + examples: + + - 1: "widget|class=QDialog/property|name=geometry" + or "widget|class=QDialog/property||1" + - 2: "widget/property|name=caption/string" + or "widget/property||2/string" + . + \verbatim + <widget class="QDialog"> + <property name="name"> + <cstring>KdevFormName</cstring> + </property> + <property name="geometry"> <-- 1. reaches this node + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>480</height> + </rect> + </property> + <property name="caption"> + <string>KdevFormCaption</string> <-- 2. reaches this node + </property> + </widget> + \endverbatim + */ + static QDomElement elementByPathExt(QDomDocument &doc, const QString &pathstring); + + /** + * Open file - filename - and set setContents of doc + */ + static bool openDOMFile(QDomDocument &doc, QString filename); + + /** + * Store contents of doc in file - filename. Existing file will be truncated! + */ + static bool saveDOMFile(QDomDocument &doc, QString filename); + + /** + * Remove all child text nodes of parent described in pathExt + */ + static bool removeTextNodes(QDomDocument doc,QString pathExt); + + /** + * Add child text node to parent described in pathExt + */ + static bool appendText(QDomDocument doc, QString pathExt, QString text); + + /** + * Replace all chilt text nodes of parent described in pathExt with one new. + */ + static bool replaceText(QDomDocument doc, QString pathExt, QString text); + +private: + static QString readEntryAux(const QDomDocument &doc, const QString &path); +}; + +#endif diff --git a/lib/util/execcommand.cpp b/lib/util/execcommand.cpp new file mode 100644 index 00000000..73fc9320 --- /dev/null +++ b/lib/util/execcommand.cpp @@ -0,0 +1,101 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Harald Fernengel <harry@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "execcommand.h" + +#include <kprocess.h> +#include <kprogress.h> +#include <klocale.h> +#include <kmessagebox.h> + +ExecCommand::ExecCommand( const QString& executable, const QStringList& args, + const QString& workingDir, const QStringList& env, + QObject* parent, const char* name ): + QObject( parent, name ), out( "" ) /* make sure out is not QString::null since that would mean "error" */ + +{ + progressDlg = 0; + + proc = new KProcess(); + proc->setWorkingDirectory( workingDir ); + for ( QStringList::ConstIterator it = env.begin(); it != env.end(); ++it ) + proc->setEnvironment( (*it).section( '=', 0, 0 ), (*it).section( '=', 1, 1 ) ); + *proc << executable; + *proc << args; + + connect( proc, SIGNAL(processExited(KProcess*)), + this, SLOT(processExited()) ); + connect( proc, SIGNAL(receivedStdout(KProcess*,char*,int)), + this, SLOT(receivedStdout(KProcess*,char*,int)) ); + connect( proc, SIGNAL(receivedStderr(KProcess*,char*,int)), + this, SLOT(receivedStderr(KProcess*,char*,int)) ); + + bool ok = proc->start( KProcess::NotifyOnExit, KProcess::AllOutput ); + + if ( !ok ) { + KMessageBox::error( 0, i18n("Could not invoke \"%1\". Please make sure it is installed correctly").arg( executable ), + i18n("Error Invoking Command") ); + + emit finished( QString::null, QString::null ); + deleteLater(); + + } else { + progressDlg = new KProgressDialog( 0, 0, i18n("Command running..."), + i18n("Please wait until the \"%1\" command finishes.").arg( executable ), false ); + connect( progressDlg, SIGNAL(cancelClicked()), + this, SLOT(cancelClicked()) ); + } +} + +void ExecCommand::receivedStdout (KProcess*, char *buffer, int buflen) +{ + out += QString::fromUtf8( buffer, buflen ); +} + +void ExecCommand::receivedStderr (KProcess*, char *buffer, int buflen) +{ + err += QString::fromUtf8( buffer, buflen ); +} + +void ExecCommand::processExited() +{ + delete progressDlg; + progressDlg = 0; + + emit finished( out, err ); + deleteLater(); +} + +void ExecCommand::cancelClicked() +{ + delete progressDlg; + progressDlg = 0; + proc->kill(); + + emit finished( QString::null, QString::null ); + deleteLater(); +} + +ExecCommand::~ExecCommand() +{ + delete proc; + delete progressDlg; +} + +#include "execcommand.moc" diff --git a/lib/util/execcommand.h b/lib/util/execcommand.h new file mode 100644 index 00000000..b5545e0d --- /dev/null +++ b/lib/util/execcommand.h @@ -0,0 +1,70 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Harald Fernengel <harry@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _EXECCOMMAND_H_ +#define _EXECCOMMAND_H_ + +#include <qobject.h> +#include <qstringlist.h> + +class KProcess; +class KProgressDialog; + +/** +@file execcommand.h +Command execution facilities. +*/ + +/** + * This class invokes a binary with the arguments passed in the constructor and + * emits the signal finished() with the output. It also displays + * a progress dialog with the possibility to cancel the command. + * + * If there was an error or the user pressed cancel, finished () + * will emit a QString::null, otherwise QStrings containing the stdout/stderr. + * + * The object will delete itself after the finished signal has been emitted. + * Additional environment can be set in the QStringList env via QStrings with the format "foo=blah" + */ +class ExecCommand : public QObject +{ + Q_OBJECT +public: + ExecCommand( const QString& executable, const QStringList& args, + const QString& workingDir = QString::null, + const QStringList& env = QStringList(), QObject* parent = 0, const char* name = 0 ); + ~ExecCommand(); + +signals: + void finished( const QString& output, const QString& errorOutput ); + +private slots: + void receivedStdout (KProcess *, char *buffer, int buflen); + void receivedStderr (KProcess *, char *buffer, int buflen); + void processExited(); + void cancelClicked(); + +private: + KProcess* proc; + KProgressDialog* progressDlg; + QString out; + QString err; +}; + +#endif diff --git a/lib/util/filetemplate.cpp b/lib/util/filetemplate.cpp new file mode 100644 index 00000000..82221891 --- /dev/null +++ b/lib/util/filetemplate.cpp @@ -0,0 +1,128 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Sandy Meier <smeier@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "filetemplate.h" + +#include <qdatetime.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qregexp.h> +#include <qtextstream.h> + +#include <kstandarddirs.h> + +#include "kdevplugin.h" +#include "kdevproject.h" +#include "domutil.h" + + +bool FileTemplate::exists(KDevPlugin *part, const QString &name, Policy p) +{ + //QString fileName = (p == Default) ? + // (part->project()->projectDirectory() + "/templates/" + name) : name; + + return QFile::exists( fullPathForName(part,name,p) ); +} + +QString FileTemplate::read(KDevPlugin *part, const QString &name, Policy p) +{ + + //KDevProject *project = part->project(); + //QString fileName = (p == Default) ? (project->projectDirectory() + +// "/templates/" + name) : name; + + return readFile(part, fullPathForName(part, name, p) ); +} + +QString FileTemplate::readFile(KDevPlugin *part, const QString &fileName) +{ + QDomDocument &dom = *part->projectDom(); + + QFile f(fileName); + if (!f.open(IO_ReadOnly)) + return QString::null; + QTextStream stream(&f); + QString str = stream.read(); + + return makeSubstitutions( dom, str ); +} + +QString FileTemplate::makeSubstitutions( QDomDocument & dom, const QString & text ) +{ + QString author = DomUtil::readEntry(dom, "/general/author"); + QString email = DomUtil::readEntry(dom, "/general/email"); + QString version = DomUtil::readEntry(dom, "/general/version"); + QString appname = DomUtil::readEntry(dom, "/general/projectname"); + QString date = QDate::currentDate().toString(); + QString year = QString::number(QDate::currentDate().year()); + + QString str = text; + str.replace(QRegExp("\\$EMAIL\\$"),email); + str.replace(QRegExp("\\$AUTHOR\\$"),author); + str.replace(QRegExp("\\$VERSION\\$"),version); + str.replace(QRegExp("\\$DATE\\$"),date); + str.replace(QRegExp("\\$YEAR\\$"),year); + str.replace(QRegExp("\\$APPNAME\\$"),appname); + str.replace(QRegExp("\\$APPNAME\\$"),appname); + str.replace(QRegExp("\\$APPNAMEUC\\$"),appname.upper()); + str.replace(QRegExp("\\$APPNAMELC\\$"),appname.lower()); + + return str; +} + + +bool FileTemplate::copy(KDevPlugin *part, const QString &name, + const QString &dest, Policy p) +{ + QString text = read(part, name, p); + + QFile f(dest); + if (!f.open(IO_WriteOnly)) + return false; + + QFileInfo fi(f); + QString module = fi.baseName(); + QString basefilename = fi.baseName(true); + text.replace(QRegExp("\\$MODULE\\$"),module); + text.replace(QRegExp("\\$FILENAME\\$"),basefilename); + + QTextStream stream(&f); + stream << text; + f.close(); + + return true; +} + +QString FileTemplate::fullPathForName(KDevPlugin *part, const QString &name, + Policy p) { + // if Policy is not default, full path is just the name + if (p!=Default) return name; + + QString fileName; + // first try project-specific + if (part->project()) + { + fileName = (part->project()->projectDirectory() + "/templates/" + name); + if (QFile::exists(fileName)) return fileName; + } + + // next try global + QString globalName = ::locate("data", "kdevfilecreate/file-templates/" + name); + return globalName.isNull() ? fileName : globalName; +} diff --git a/lib/util/filetemplate.h b/lib/util/filetemplate.h new file mode 100644 index 00000000..e126176d --- /dev/null +++ b/lib/util/filetemplate.h @@ -0,0 +1,88 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Bernd Gehrmann <bernd@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _FILETEMPLATE_H_ +#define _FILETEMPLATE_H_ + +#include <qstring.h> + +class KDevPlugin; +class QDomDocument; + +/** +@file filetemplate.h +FileTemplate class with utility methods to work with file templates. +*/ + +/** +Utilities to work with file templates in the current project. +*/ +class FileTemplate +{ +public: + + /**Policy of finding file templates.*/ + typedef enum { + Default /**<Checks for templates in project and also for global filecreate templates.*/, + Custom /**<Checks for templates in custom directories. This usually means that + full paths are given for FileTemplate methods.*/ + } Policy; + + /** + * @return Whether a template with the given name + * exists in the current project. File templates + * are stored in the "templates" subdirectory of a project or in application shared dirs. + */ + static bool exists(KDevPlugin *part, const QString &name, Policy p = Default); + + /** + * Reads a template with the given name (e.g. "cpp") + * and makes variable substitutions (like $AUTHOR$ etc.) + * in it. The resulting string is returned. + */ + static QString read(KDevPlugin *part, const QString &name, Policy p = Default); + + /** + * Reads a template with the given URL + * and makes variable substitutions (like $AUTHOR$ etc.) + * in it. The resulting string is returned. + */ + static QString readFile(KDevPlugin *part, const QString &fileName); + + /** + * Makes variable substitutions on a text, based on a specified QDomDocument + * describing a KDevelop project file. The resulting string is returned. + */ + static QString makeSubstitutions(QDomDocument &dom, const QString &text); + + /** + * Copies a file template with the given name to the + * file with the name dest and - while copying - + * performs variable substitutions. + */ + static bool copy(KDevPlugin *part, const QString &name, + const QString &dest, Policy p = Default); + /** + * Translates a template name into a full path, or suggests a full path + * for the template in the project directory if it doesn't exist. + */ + static QString fullPathForName(KDevPlugin *part, const QString &name, Policy p = Default); +}; + +#endif diff --git a/lib/util/kdeveditorutil.cpp b/lib/util/kdeveditorutil.cpp new file mode 100644 index 00000000..fdf044d5 --- /dev/null +++ b/lib/util/kdeveditorutil.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + * Copyright (C) 2007 by Jens Dagerbo * + * jens.dagerbo@swipnet.se * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kdeveditorutil.h" + +#include <ktexteditor/document.h> +#include <ktexteditor/view.h> +#include <ktexteditor/viewcursorinterface.h> +#include <ktexteditor/editinterface.h> +#include <ktexteditor/selectioninterface.h> + +bool KDevEditorUtil::currentPositionReal( unsigned int * line, unsigned int * col, KTextEditor::Document * doc, KTextEditor::View * view ) +{ + if ( !line || !col ) return false; + + KTextEditor::EditInterface * editIface = dynamic_cast<KTextEditor::EditInterface*>( doc ); + if ( !editIface ) return false; + + view = view ? view : dynamic_cast<KTextEditor::View*>( doc->widget() ); + + KTextEditor::ViewCursorInterface * cursorIface = dynamic_cast<KTextEditor::ViewCursorInterface*>( view ); + if ( !cursorIface ) return false; + + cursorIface->cursorPositionReal( line, col ); + return true; +} + +QString KDevEditorUtil::currentLine( KTextEditor::Document * doc, KTextEditor::View * view ) +{ + KTextEditor::EditInterface * editIface = dynamic_cast<KTextEditor::EditInterface*>( doc ); + if ( !editIface ) return QString(); + + view = view ? view : dynamic_cast<KTextEditor::View*>( doc->widget() ); + + KTextEditor::ViewCursorInterface * cursorIface = dynamic_cast<KTextEditor::ViewCursorInterface*>( view ); + if ( !cursorIface ) return QString(); + + uint line = 0; + uint col = 0; + cursorIface->cursorPositionReal(&line, &col); + + return editIface->textLine(line); +} + +QString KDevEditorUtil::currentWord( KTextEditor::Document * doc, KTextEditor::View * view ) +{ + KTextEditor::EditInterface * editIface = dynamic_cast<KTextEditor::EditInterface*>( doc ); + if ( !editIface ) return QString(); + + view = view ? view : dynamic_cast<KTextEditor::View*>( doc->widget() ); + + KTextEditor::ViewCursorInterface * cursorIface = dynamic_cast<KTextEditor::ViewCursorInterface*>( view ); + if ( !cursorIface ) return QString(); + + uint line = 0; + uint col = 0; + cursorIface->cursorPositionReal(&line, &col); + + QString linestr = editIface->textLine(line); + + int startPos = QMAX( QMIN( (int)col, (int)linestr.length()-1 ), 0 ); + int endPos = startPos; + startPos--; + while (startPos >= 0 && ( linestr[startPos].isLetterOrNumber() || linestr[startPos] == '_' || linestr[startPos] == '~') ) + startPos--; + while (endPos < (int)linestr.length() && ( linestr[endPos].isLetterOrNumber() || linestr[endPos] == '_' ) ) + endPos++; + + return ( ( startPos == endPos ) ? QString::null : linestr.mid( startPos+1, endPos-startPos-1 ) ); +} + + +QString KDevEditorUtil::currentSelection( KTextEditor::Document * doc ) +{ + KTextEditor::SelectionInterface * selectIface = dynamic_cast<KTextEditor::SelectionInterface*>( doc ); + return selectIface ? selectIface->selection() : QString(); +} + + +// kate: space-indent off; indent-width 4; tab-width 4; show-tabs on; + diff --git a/lib/util/kdeveditorutil.h b/lib/util/kdeveditorutil.h new file mode 100644 index 00000000..7b788fc3 --- /dev/null +++ b/lib/util/kdeveditorutil.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2007 by Jens Dagerbo * + * jens.dagerbo@swipnet.se * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KDEVEDITOR_H +#define KDEVEDITOR_H + +namespace KTextEditor +{ +class Document; +class View; +} + +#include <qstring.h> + +/** +Class with some common utility operations not currently supported by KTE + + @author Jens Dagerbo <jens.dagerbo@swipnet.se> +*/ +class KDevEditorUtil +{ + KDevEditorUtil() {} +public: + + /** + * + * @param line + * @param col + * @param doc + * @param view + * @return + */ + static bool currentPositionReal( unsigned int * line, unsigned int * col, KTextEditor::Document * doc, KTextEditor::View * view = 0 ); + + /** + * + * @param doc + * @param view + * @return + */ + static QString currentLine( KTextEditor::Document * doc, KTextEditor::View * view = 0 ); + + /** + * Call to get the text under the cursor of the currently active view. + * @return the text under the cursor of the currently active view + */ + static QString currentWord( KTextEditor::Document * doc, KTextEditor::View * view = 0 ); + + /** + * Call to get the selection in the currently active view + * @return the selection in the currently active view + */ + static QString currentSelection( KTextEditor::Document * doc ); +}; + +#endif + + +// kate: space-indent off; indent-width 4; tab-width 4; show-tabs on; diff --git a/lib/util/kdevjobtimer.cpp b/lib/util/kdevjobtimer.cpp new file mode 100644 index 00000000..b7f9e2ae --- /dev/null +++ b/lib/util/kdevjobtimer.cpp @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright (C) 2006 by Jens Dagerbo * + * jens.dagerbo@swipnet.se * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kdevjobtimer.h" + +KDevJobTimer::KDevJobTimer( void * payload, QObject *parent, const char *name) + : QTimer(parent, name) +{ + m_payload = payload; + connect( this, SIGNAL(timeout()), this, SLOT(slotTimeout()) ); +} + +KDevJobTimer::~KDevJobTimer() +{ +} + +void KDevJobTimer::singleShot(int msec, QObject * receiver, const char * member, void * payload) +{ + KDevJobTimer * p = new KDevJobTimer( payload ); + p->start( msec, true ); + connect( p, SIGNAL(timeout(void*)), receiver, member ); +} + +void KDevJobTimer::slotTimeout() +{ + emit timeout( m_payload ); + m_payload = 0; + deleteLater(); +} + +#include "kdevjobtimer.moc" diff --git a/lib/util/kdevjobtimer.h b/lib/util/kdevjobtimer.h new file mode 100644 index 00000000..972a654a --- /dev/null +++ b/lib/util/kdevjobtimer.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright (C) 2006 by Jens Dagerbo * + * jens.dagerbo@swipnet.se * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KDEVJOBTIMER_H +#define KDEVJOBTIMER_H + +#include <qtimer.h> + +class KDevJobTimer : public QTimer +{ +Q_OBJECT +public: + static void singleShot( int msec, QObject * receiver, const char * member, void * payload ); + +signals: + void timeout(void*); + +private: + KDevJobTimer( void * payload, QObject *parent = 0, const char *name = 0); + ~KDevJobTimer(); + +private slots: + void slotTimeout(); + +private: + void * m_payload; + +}; + + +#endif diff --git a/lib/util/kdevshellwidget.cpp b/lib/util/kdevshellwidget.cpp new file mode 100644 index 00000000..f9a61fea --- /dev/null +++ b/lib/util/kdevshellwidget.cpp @@ -0,0 +1,125 @@ +/*************************************************************************** + * Copyright (C) 2006 by Jens Dagerbo * + * jens.dagerbo@swipnet.se * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include <qtimer.h> +#include <qframe.h> + +#include <kdebug.h> +#include <kparts/part.h> +#include <klibloader.h> +#include <kde_terminal_interface.h> +#include <kprocess.h> + +#include "kdevshellwidget.h" + +KDevShellWidget::KDevShellWidget(QWidget *parent, const char *name) + : QVBox(parent, name), m_doAutoActivate( false ), m_isRunning( false ) +{ +} + + +KDevShellWidget::~KDevShellWidget() +{ +} + +void KDevShellWidget::setShell( const QString & shell, const QStrList & arguments ) +{ + m_shellName = shell; + m_shellArguments = arguments; +} + +void KDevShellWidget::activate( ) +{ + KLibFactory *factory = KLibLoader::self()->factory("libkonsolepart"); + if ( !factory ) return; + + m_konsolePart = (KParts::ReadOnlyPart *) factory->create( this, "libkonsolepart", "KParts::ReadOnlyPart" ); + if ( !m_konsolePart ) return; + + connect( m_konsolePart, SIGNAL( processExited(KProcess *) ), this, SLOT( processExited(KProcess *) ) ); + connect( m_konsolePart, SIGNAL( receivedData( const QString& ) ), this, SIGNAL( receivedData( const QString& ) ) ); + connect( m_konsolePart, SIGNAL(destroyed()), this, SLOT(partDestroyed()) ); + + m_konsolePart->widget()->setFocusPolicy( QWidget::WheelFocus ); + setFocusProxy( m_konsolePart->widget() ); + m_konsolePart->widget()->setFocus(); + + if ( m_konsolePart->widget()->inherits("QFrame") ) + ((QFrame*)m_konsolePart->widget())->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + + m_konsolePart->widget()->show(); + + TerminalInterface* ti = static_cast<TerminalInterface*>( m_konsolePart->qt_cast( "TerminalInterface" ) ); + if( !ti ) return; + + if ( !m_shellName.isEmpty() ) + ti->startProgram( m_shellName, m_shellArguments ); + + m_isRunning = true; + +} + +void KDevShellWidget::partDestroyed( ) +{ + if ( m_doAutoActivate ) + { + activate(); + } +} + +void KDevShellWidget::processExited( KProcess * proc ) +{ + m_isRunning = false; + + if ( !proc ) return; + + kdDebug(9000) << proc->args() << endl; + + if ( proc->normalExit() ) + emit shellExited( proc->exitStatus() ); + else if ( proc->signalled() ) + emit shellSignalled( proc->exitSignal() ); +} + +void KDevShellWidget::sendInput( const QString & text ) +{ + if ( !m_konsolePart ) return; + TerminalInterface* ti = static_cast<TerminalInterface*>( m_konsolePart->qt_cast( "TerminalInterface" ) ); + if( !ti ) return; + + ti->sendInput( text ); +} + +bool KDevShellWidget::isRunning( ) +{ + return m_isRunning; +} + +void KDevShellWidget::setAutoReactivateOnClose( bool doAutoActivate ) +{ + // to auto reactivate can be dangerous, do it like this to avoid + // reactivating with a non-working setting (the partDestroyed() + // slot will have ran before m_doAutoActivate is set) + if ( doAutoActivate ) + QTimer::singleShot( 3000, this, SLOT(setAutoReactivateOnCloseDelayed()) ); + else + m_doAutoActivate = false; +} + +void KDevShellWidget::setAutoReactivateOnCloseDelayed( ) +{ + m_doAutoActivate = true; +} + + +#include "kdevshellwidget.moc" + +// kate: space-indent off; indent-width 4; tab-width 4; show-tabs off; diff --git a/lib/util/kdevshellwidget.h b/lib/util/kdevshellwidget.h new file mode 100644 index 00000000..3f1500aa --- /dev/null +++ b/lib/util/kdevshellwidget.h @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright (C) 2006 by Jens Dagerbo * + * jens.dagerbo@swipnet.se * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + + +#ifndef KDEVSHELLWIDGET_H +#define KDEVSHELLWIDGET_H + +#include <qstrlist.h> +#include <qvbox.h> +#include <qguardedptr.h> + +class KProcess; +namespace KParts +{ + class ReadOnlyPart; +} + +class KDevShellWidget : public QVBox +{ + +Q_OBJECT + +public: + KDevShellWidget(QWidget *parent = 0, const char *name = 0); + virtual ~KDevShellWidget(); + + /** + * Stores the shell name and arguments, that will be used in @ref activate() + * @param shell The shell name, for example 'irb' or '/bin/bash' + * @param arguments Any optional arguments + */ + void setShell( const QString & shell, const QStrList & arguments = QStrList() ); + + /** + * Executes the previously set shell. If @ref setShell wasn't called before + * konsolepart will decide what to use. + */ + void activate(); + + /** + * Should we auto launch the shell again if it was terminated? + * @param doAutoActivate + */ + void setAutoReactivateOnClose( bool doAutoActivate ); + + /** + * Send text to the running shell + * @param text The text to send to the shell + */ + void sendInput( const QString & text ); + + /** + * Call to check if the shell is currently running + * @return true if the shell is currently running + */ + bool isRunning(); + +signals: + /** + * This signal will be emmitted when the started shell exits normally + * @param exitcode The return code of the process + */ + void shellExited( int exitcode ); + + /** + * This signal will be emitted when the started shell is terminated by a signal + * @param signalcode The signal the process was killed with + */ + void shellSignalled( int signalcode ); + + /** + * This signal will be emitted when the process receives data + * @param text received data + */ + void receivedData( const QString & ); + +private slots: + void partDestroyed(); + void processExited( KProcess * ); + void setAutoReactivateOnCloseDelayed( ); + +private: + QGuardedPtr<KParts::ReadOnlyPart> m_konsolePart; + QString m_shellName; + QStrList m_shellArguments; + bool m_doAutoActivate; + bool m_isRunning; +}; + +#endif + +// kate: space-indent off; indent-width 4; tab-width 4; show-tabs off; diff --git a/lib/util/kscriptactionmanager.cpp b/lib/util/kscriptactionmanager.cpp new file mode 100644 index 00000000..cf73e949 --- /dev/null +++ b/lib/util/kscriptactionmanager.cpp @@ -0,0 +1,177 @@ +/*************************************************************************** +* Copyright (C) 2004 by ian geiser * +* geiseri@sourcextreme.com * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#include "kscriptactionmanager.h" +#include <kparts/part.h> +#include <kparts/componentfactory.h> +#include <kapplication.h> +#include <kdesktopfile.h> +#include <kstandarddirs.h> + +#include <klocale.h> +#include <kmessagebox.h> +#include <kdebug.h> +#include <scriptinterface.h> +#include <kaction.h> +#include <qfileinfo.h> +#include <qtimer.h> + +KScriptAction::KScriptAction( const QString &scriptDesktopFile, QObject *interface, KActionCollection *ac ) + : QObject(interface), KScriptClientInterface( ) +{ + m_interface = 0L; + m_action = 0L; + m_isValid = false; + m_refs = 0; + // Read the desktop file + if(KDesktopFile::isDesktopFile(scriptDesktopFile)) + { + KDesktopFile desktop(scriptDesktopFile, true); + QFileInfo scriptPath(scriptDesktopFile); + + m_scriptFile = scriptPath.dirPath(true) + "/" + desktop.readEntry("X-KDE-ScriptName", ""); + m_scriptName = desktop.readName(); + m_scriptType = desktop.readType(); + QString scriptTypeQuery = "([X-KDE-Script-Runner] == '" + m_scriptType + "')"; + KTrader::OfferList offers = KTrader::self()->query( "KScriptRunner/KScriptRunner", scriptTypeQuery ); + if ( !offers.isEmpty() ) + { + m_action = new KAction(m_scriptName, KShortcut(), this, SLOT(activate()), ac, "script"); + m_isValid = true; + m_timeout = new QTimer(this); + QString icon = desktop.readIcon(); + m_action->setStatusText(desktop.readComment()); + if( !icon.isEmpty() ) + m_action->setIcon(icon); + m_action->setShortcutConfigurable(true); + connect( m_timeout, SIGNAL(timeout()), SLOT(cleanup()) ); + } + } +} + +KScriptAction::~KScriptAction() +{ + if( m_interface ) delete m_interface; + if( m_action ) delete m_action; +} + + + +KAction * KScriptAction::action( ) +{ + return m_action; +} + +void KScriptAction::activate( ) +{ + if( m_interface == 0L) + { + QString scriptTypeQuery = "([X-KDE-Script-Runner] == '" + m_scriptType + "')"; + m_interface= KParts::ComponentFactory::createInstanceFromQuery<KScriptInterface>( "KScriptRunner/KScriptRunner", scriptTypeQuery, this ); + if ( m_interface != 0L) + { + m_interface->ScriptClientInterface= this; + if( m_scriptMethod.isEmpty() ) + m_interface->setScript(m_scriptFile); + else + m_interface->setScript(m_scriptFile, m_scriptMethod); + connect(this, SIGNAL(done(KScriptClientInterface::Result, const QVariant &)), this, SLOT(scriptFinished())); + } + else + { + KMessageBox::sorry(0, i18n("Unable to get KScript Runner for type \"%1\".").arg(m_scriptType), i18n("KScript Error")); + kdDebug() << "Query string: " << scriptTypeQuery << endl; + return; + } + } + m_interface->run(parent(), QVariant()); + m_timeout->start(60000,FALSE); // after 1 minute unload + m_refs++; +} + +void KScriptAction::cleanup() +{ + if( m_interface && m_refs == 0) + { + delete m_interface; + m_interface = 0L; + } +} + +void KScriptAction::scriptFinished() +{ + m_refs--; +} + +KScriptActionManager::KScriptActionManager( QObject *parent, KActionCollection * ac ) : QObject(parent), m_ac(ac) +{ + m_actions.setAutoDelete(true); +} + +KScriptActionManager::~ KScriptActionManager( ) +{ + m_actions.clear(); +} + +QPtrList< KAction > KScriptActionManager::scripts( QObject * interface , const QStringList &dirs) const +{ + m_actions.clear(); + QPtrList<KAction> actions; + QStringList scripts; + + scripts += KGlobal::dirs()->findAllResources("data", + QString(kapp->name())+"/scripts/*.desktop", false, true ); + + for( QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) + { + scripts += KGlobal::dirs()->findAllResources("data", + (*it)+"/*.desktop", false, true ); + } + + for (QStringList::Iterator it = scripts.begin(); it != scripts.end(); ++it ) + { + kdDebug() << "Loading " << *it << endl; + KScriptAction *script = new KScriptAction(*it, interface, m_ac); + if( script->isValid()) + { + actions.append(script->action()); + m_actions.append(script); + connect(script, SIGNAL(error( const QString&)), this, + SIGNAL(scriptError( const QString&))); + connect(script, SIGNAL(warning( const QString&)), this, + SIGNAL(scriptWarning( const QString&))); + connect(script, SIGNAL(output( const QString&)), this, + SIGNAL(scriptOutput( const QString&))); + connect(script, SIGNAL(progress( int )), this, + SIGNAL(scriptProgress(int))); + connect(script, SIGNAL(done( KScriptClientInterface::Result, const QVariant &)),this, + SIGNAL(scriptDone( KScriptClientInterface::Result, const QVariant &))); + } + else + delete script; + } + return actions; +} + +bool KScriptAction::isValid( ) const +{ + return m_isValid; +} + +#include "kscriptactionmanager.moc" diff --git a/lib/util/kscriptactionmanager.h b/lib/util/kscriptactionmanager.h new file mode 100644 index 00000000..234e4a3c --- /dev/null +++ b/lib/util/kscriptactionmanager.h @@ -0,0 +1,139 @@ +/*************************************************************************** +* Copyright (C) 2004 by ian geiser * +* geiseri@sourcextreme.com * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#ifndef KScriptAction_H +#define KScriptAction_H + +#include <scriptclientinterface.h> +#include <qobject.h> +#include <qptrlist.h> + +class KAction; +class KActionCollection; +class KScriptInterface; +class KScriptActionManager; +class QTimer; + +/** +* Connects a KAction to a script runner. +* +* @author ian geiser geiseri@sourcextreme.com +*/ +class KScriptAction : public QObject, public KScriptClientInterface { + Q_OBJECT +public: + + KScriptAction( const QString &scriptDesktopFile, QObject *interface, KActionCollection *ac ); + + virtual ~KScriptAction(); + + /** + * KAction for the current script + */ + KAction *action(); + + /** + * Returns the validity of the current script. + */ + bool isValid() const; + +signals: + // Reimplemented from KScript + void error ( const QString &msg ); + void warning ( const QString &msg ); + void output ( const QString &msg ); + void progress ( int percent ); + void done ( KScriptClientInterface::Result result, const QVariant &returned ); + +signals: + void scriptError ( const QString &msg ); + void scriptWarning ( const QString &msg ); + void scriptOutput ( const QString &msg ); + void scriptProgress ( int percent ); + void scriptDone ( KScriptClientInterface::Result result, const QVariant &returned ); + +private slots: + void activate(); + void cleanup(); + void scriptFinished(); + +private: + KAction *m_action; + QString m_scriptName; + QString m_scriptType; + QString m_scriptFile; + QString m_scriptMethod; + KScriptInterface *m_interface; + bool m_isValid; + QTimer *m_timeout; + uint m_refs; +}; + +/** + * Provides an actionlist of scripts that are currently available. + * Scripts are not actually loaded until they are actually executed. + * @author ian geiser <geiseri@sourcextreme.com> + */ +class KScriptActionManager : public QObject { +Q_OBJECT + +public: + /** + * Create a script manager that is attached to an action collection. + */ + KScriptActionManager( QObject *parent, KActionCollection *ac ); + ~KScriptActionManager(); + + /** + * Return all currently loaded scripts in a direcotry and attaches them + * to a QObject interface. If the dirs are empty the current applications + * scripts directory is used. The dirs are actual directories to search + * in the $KDEPATH/data/ direcories. So if you add "coolapp/data" then + * the manager will search in $KDEPATH/data/coolapp/data for all desktop + * files that are scripts. + */ + QPtrList<KAction> scripts( QObject *interface, const QStringList &dirs = QStringList() ) const; + +signals: + /** + * Returns an error message from a script. + */ + void scriptError ( const QString &msg ); + /** + * Returns a warning message from a script. + */ + void scriptWarning ( const QString &msg ); + /** + * Returns a standard out message from a script. + */ + void scriptOutput ( const QString &msg ); + /** + * Returns the percentage complete of an operation in the script. + */ + void scriptProgress ( int percent ); + /** + * Notifies that the script has finished. + */ + void scriptDone( KScriptClientInterface::Result result, const QVariant &returned ); + +private: + mutable QPtrList<KScriptAction> m_actions; + KActionCollection *m_ac; +}; +#endif diff --git a/lib/util/rurl.cpp b/lib/util/rurl.cpp new file mode 100644 index 00000000..1ec23434 --- /dev/null +++ b/lib/util/rurl.cpp @@ -0,0 +1,369 @@ +#include <qstringlist.h> + +#include "rurl.h" + +namespace Relative{ + + +//class Name + +Name::Name( const QString & rurl, const Type type ) + :m_rurl(rurl), m_type(type) +{ + correct(); +} + +Name::Name( const char * rurl, const Type type ) + :m_rurl(rurl), m_type(type) +{ + correct(); +} + +void Name::correct() +{ + cleanRURL(); + if (m_rurl[0] == '/') + m_rurl = m_rurl.mid(1); + switch (m_type) + { + case File: + if (m_rurl.endsWith("/")) + m_rurl = m_rurl.mid(0, m_rurl.length()-1); + break; + case Directory: + if (!m_rurl.endsWith("/")) + m_rurl += "/"; + break; + case Auto: + if (m_rurl.endsWith("/")) + m_type = Directory; + else + m_type = File; + break; + } +} + +QString Name::correctName( const QString & rurl, const Type type ) +{ + QString temp = rurl; + temp = Name::cleanName(temp); + if (temp[0] == '/') + temp = temp.mid(1); + + switch (type) + { + case File: + if (temp.endsWith("/")) + temp = temp.mid(0, temp.length()-1); + break; + case Directory: + if (!temp.endsWith("/")) + temp += "/"; + break; + } + + return temp; +} + +void Name::setRURL( const QString & rurl, const Type type ) +{ + m_rurl = rurl; + m_type = type; + correct(); +} + +QString Name::rurl( ) const +{ + return m_rurl; +} + +void Name::addPath( const QString & addendum ) +{ + QString temp = correctName(addendum, Directory); + m_rurl = directory() + temp + fileName(); +} + +void Name::cleanRURL( ) +{ + m_rurl = cleanName(m_rurl); +} + +QString Name::cleanName( const QString & rurl ) +{ + QString temp; + bool wasSlash = false; + for (unsigned int i = 0; i < rurl.length(); ++i) + { + if (wasSlash && (rurl[i] == '/')) + continue; + + temp += rurl[i]; + if (rurl[i] == '/') + wasSlash = true; + else if (wasSlash) + wasSlash = false; + } + + return temp; +} + +QString Name::extension( bool complete ) const +{ + if (m_type == File) + { + QString temp = fileName(); + if (complete) + return temp.mid(temp.find('.')+1); + else + return temp.mid(temp.findRev('.')+1); + } + return QString::null; +} + +QString Name::fileName( ) const +{ + if (m_type == File) + return m_rurl.section('/', -1); + return QString::null; +} + +QString Name::directory( ) const +{ + if ( (m_type == File) && (m_rurl.findRev('/') == -1) ) + return QString::null; + + return m_rurl.mid(0, m_rurl.findRev('/')+1); +} + +bool Name::isFile( ) const +{ + return m_type == File; +} + +bool Name::isDirectory( ) const +{ + return m_type == Directory; +} + +bool Name::operator ==( const Name & rname ) +{ + return rname.rurl() == m_rurl; +} + +bool Name::operator !=( const Name & rname ) +{ + return rname.rurl() != m_rurl; +} + +bool Name::isValid( ) const +{ + if (m_rurl.startsWith("/")) + return false; + if (m_rurl.contains("//")) + return false; + if ( (m_rurl.endsWith("/")) && (m_type == File) ) + return false; + if ( (!m_rurl.endsWith("/")) && (m_type == Directory) ) + return false; + if (m_type == Auto) + return false; + + return true; +} + +Name::Type Name::type( ) const +{ + return m_type; +} + +void Name::setType( const Type type ) +{ + m_type = type; +} + +Name Name::relativeName( const QString &base, const QString &url ) +{ + QString dirUrl = base; + QString fileUrl = url; + + if (dirUrl.isEmpty() || (dirUrl == "/")) + return Name(fileUrl); + + QStringList dir = QStringList::split("/", dirUrl, false); + QStringList file = QStringList::split("/", fileUrl, false); + + QString resFileName = file.last(); + if (url.endsWith("/")) + resFileName += "/"; + file.remove(file.last()); + + uint i = 0; + while ( (i < dir.count()) && (i < (file.count())) && (dir[i] == file[i]) ) + i++; + + QString result_up; + QString result_down; + QString currDir; + QString currFile; + do + { + i >= dir.count() ? currDir = "" : currDir = dir[i]; + i >= file.count() ? currFile = "" : currFile = file[i]; +// qWarning("i = %d, currDir = %s, currFile = %s", i, currDir.latin1(), currFile.latin1()); + if (currDir.isEmpty() && currFile.isEmpty()) + break; + else if (currDir.isEmpty()) + result_down += file[i] + "/"; + else if (currFile.isEmpty()) + result_up += "../"; + else + { + result_down += file[i] + "/"; + result_up += "../"; + } + i++; + } + while ( (!currDir.isEmpty()) || (!currFile.isEmpty()) ); + + return result_up + result_down + resFileName; +} + + + +//class URL + +URL::URL( KURL base, KURL url, Type type ) + :Name(Name::relativeName(base.path(), url.path()).rurl(), type), m_base(base) +{ +} + +URL::URL( KURL base, QString url, bool isUrlRelative, Type type ) + :Name(isUrlRelative ? url : Name::relativeName(base.path(), url).rurl(), type), m_base(base) +{ +} + +void URL::setBase( const KURL & base ) +{ + m_base = base; +} + +void URL::setBase( const QString & base ) +{ + KURL url; + url.setPath(base); + m_base = url; +} + +KURL URL::base( ) const +{ + return m_base; +} + +QString URL::basePath( ) const +{ + return m_base.path(1); +} + +KURL URL::url( ) const +{ + KURL url = m_base; + url.addPath(rurl()); + url.cleanPath(); + return url; +} + +QString URL::urlPath( ) const +{ + KURL url = m_base; + url.addPath(rurl()); + int mod = 0; + if (type() == File) + mod = -1; + else if (type() == Directory) + mod = 1; + url.cleanPath(); + return url.path(mod); +} + +QString URL::urlDirectory( ) const +{ + KURL url = m_base; + url.addPath(rurl()); + url.cleanPath(); + return url.directory(false, false); +} + +URL URL::relativeTo( KURL base ) +{ + return URL(base, url(), type()); +} + +URL URL::relativeURL( KURL base, KURL url ) +{ + return URL(base, url); +} + +URL URL::relativeURL( KURL base, QString url, bool isUrlRelative ) +{ + return URL(base, url, isUrlRelative); +} + +bool Relative::URL::operator ==( const URL & url ) +{ + return (m_base == url.base()) && (rurl() == url.rurl()); +} + +bool Relative::URL::operator !=( const URL & url ) +{ + return (m_base != url.base()) || (rurl() != url.rurl()); +} + + + +// Directory class + +Directory::Directory( KURL base, KURL url ) + :URL(base, url, Name::Directory) +{ +} + +Directory::Directory( KURL base, QString url, bool isRelativeUrl ) + :URL(base, url, isRelativeUrl, Name::Directory) +{ +} + +void Directory::setRURL( QString rurl ) +{ + URL::setRURL(rurl, Name::Directory); +} + +void Directory::setRURL( QString rurl, Type type ) +{ + URL::setRURL(rurl, type); +} + + + +//File class + +File::File( KURL base, KURL url ) + :URL(base, url, Name::File) +{ +} + +File::File( KURL base, QString url, bool isRelativeUrl ) + :URL(base, url, isRelativeUrl, Name::File) +{ +} + +void File::setRURL( QString rurl, Type type ) +{ + URL::setRURL(rurl, type); +} + +void File::setRURL( QString rurl ) +{ + URL::setRURL(rurl, Name::File); +} + +} diff --git a/lib/util/rurl.h b/lib/util/rurl.h new file mode 100644 index 00000000..342b3e3d --- /dev/null +++ b/lib/util/rurl.h @@ -0,0 +1,182 @@ +#ifndef RURL_H_LIB +#define RURL_H_LIB + +#include <kurl.h> + +/** +@file rurl.h +Classes and functions to work with relative URLs. +*/ + +/**Classes and functions to work with relative URLs.*/ +namespace Relative +{ + +/**Relative name of a file or directory.*/ +class Name{ +public: + enum Type { File, Directory, Auto }; + + /**Constructor takes the relative name of a directory or file. + Leading slash in the name will be deleted. + If type is Auto names like: + name/name/ are directories + name/name are files. + Trailing slash will be deleted for files (type == File).*/ + Name(const QString &rurl, const Type type = Auto); + Name(const char *rurl, const Type type = Auto); + + /**Sets the relative name.*/ + void setRURL(const QString &rurl, const Type type); + /**Gets the relative name in form + dir/dir/ -> directory + or + dir/dir/file -> file.*/ + QString rurl() const; + + /**Adds addendum to the directory path. This honors file names so + if RName represents /dir1/dir2/fname.ext + addPath(dir3) will change RName to /dir1/dir2/dir3/fname.ext*/ + void addPath(const QString &addendum); + /**Removes "//" from the name.*/ + void cleanRURL(); + + /**Returns the extension of a file or QString::null for directories. + If complete is true then returns extensions like "tar.gz". + Else returns "gz".*/ + QString extension(bool complete = true) const; + /**Returns the name of the file without the path or QString::null + for directories.*/ + QString fileName() const; + /**Returns the name of the directory or QString::null if there are no dirs in path.*/ + QString directory() const; + + /**Returns true if the type of RName is file.*/ + bool isFile() const; + /**Returns true if the type of RName is directory.*/ + bool isDirectory() const; + /**Checks if RName is valid.*/ + bool isValid() const; + + /**Returns a type of the relative name - file or directory.*/ + Type type() const; + /**Sets a type of the relative name - file or directory. + If Auto is passed, nothing happens.*/ + void setType(const Type type); + + /**Creates and returns relative name between base and url. Base and url should be absolute. + Base is considered to be a directory.*/ + static Name relativeName(const QString &base, const QString &url); + /**Cleans rurl by removing extra slashes.*/ + static QString cleanName(const QString &rurl); + /**Corrects rurl according to the given type and returns corrected url. + Also cleans url (see @ref cleanRURL).*/ + static QString correctName(const QString &rurl, const Type type = Auto); + + bool operator == (const Name &rname); + bool operator != (const Name &rname); + +protected: + /**Corrects m_rurl and m_type according to the relative name storing policy, + i.e. removes leading slash, removes trailing slash for files, changes type + to be either File or Directory, but not Auto. Also cleans url (see @ref cleanRURL).*/ + void correct(); + +private: + QString m_rurl; + Type m_type; +}; + +/**Relative name of file or directory to some base location.*/ +class URL: public Name{ +public: + /**Evaluates the relative path between url and base and creates RURL object. + base should be an url to the directory or location, not a file. + The check is not performed. url should be the usual url. Only the + path of this url is taken into account when evaluating relative path.*/ + URL(KURL base, KURL url, Type type = Auto); + /**Creates RURL object with given base and relative or full url (according to + the isUrlRelative value).*/ + URL(KURL base, QString url, bool isUrlRelative, Type type = Auto); + + /**Sets a new base for a RURL.*/ + void setBase(const KURL &base); + /**Sets a new base for a RURL. Base is is considered to be a + directory and converted to KURL using KURL::setPath().*/ + void setBase(const QString &base); + /**Returns RURL base.*/ + KURL base() const; + /**Returns a path of a base KURL (using KURL::path()). Trailing slash is guaranteed.*/ + QString basePath() const; + + /**Returns a complete url to the RURL location. This is basically base + rurl. + This also resolves ".." components in path. + Directories always have trailing slash in KURL + (this means that if url() == "file:/test/dir/" then + url() != KURL("/test/dir") and + url() == KURL("/test/dir/").*/ + KURL url() const; + /**Returns a path of a complete url to the location. The url is basically base + rurl. + This method only returns a path part of the KURL (using KURL::path()). + Trailing slash is guaranteed for directories and no trailing slash - for files. + This also resolves ".." components in path.*/ + QString urlPath() const; + /**Returns a directory of a complete url to the location. The url is constructed as base + rurl. + Returns the same as urlPath() for directories. + This method uses KURL::directory to determine the directory. + Trailing slash is guaranteed. + This also resolves ".." components in path.*/ + QString urlDirectory() const; + + /**Returns a new URL that is relative to given base. Relative part is taken from + current URL object.*/ + URL relativeTo(KURL base); + + /**Returns a new relative URL constructed from base and given url.*/ + static URL relativeURL(KURL base, KURL url); + /**Returns a new relative URL constructed from base and given url. url parameter + is either relative or full (depends on isUrlRelative value).*/ + static URL relativeURL(KURL base, QString url, bool isUrlRelative); + + bool operator == (const URL &url); + bool operator != (const URL &url); + +private: + KURL m_base; +}; + +/**Relative directory name.*/ +class Directory: public URL{ +public: + /**Works as URL::URL(KURL, KURL), only implies Name::Directory mode.*/ + Directory(KURL base, KURL url); + /**Works as URL::URL(KURL, QString, bool), only implies Name::Directory mode.*/ + Directory(KURL base, QString url, bool isRelativeUrl); + + /**Works as URL::setRURL(QString), only implies Name::Directory mode.*/ + void setRURL(QString rurl); + +private: + void setRURL(QString rurl, Type type); + +}; + +/**Relative file name.*/ +class File: public URL{ +public: + /**Works as URL::URL(KURL, KURL), only implies Name::File mode.*/ + File(KURL base, KURL url); + /**Works as URL::URL(KURL, KURL), only implies Name::File mode.*/ + File(KURL base, QString url, bool isRelativeUrl); + + /**Works as URL::setRURL(QString), only implies Name::File mode.*/ + void setRURL(QString rurl); + +private: + void setRURL(QString rurl, Type type); + +}; + +} + +#endif diff --git a/lib/util/settings.cpp b/lib/util/settings.cpp new file mode 100644 index 00000000..4e246e5b --- /dev/null +++ b/lib/util/settings.cpp @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (C) 2005 by Jens Dagerbo * + * jens.dagerbo@swipnet.se * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include <kconfig.h> +#include <kglobal.h> +#include <kstandarddirs.h> + +#include "settings.h" + +QString Settings::terminalEmulatorName( KConfig & config ) +{ + config.setGroup("TerminalEmulator"); + bool useKDESetting = config.readBoolEntry( "UseKDESetting", true ); + QString terminal; + + if ( useKDESetting ) + { + KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") ); + terminal = confGroup.readEntry("TerminalApplication", QString::fromLatin1("konsole")); + } + else + { + terminal = config.readEntry( "TerminalApplication", QString::fromLatin1("konsole")); + } + return terminal; +} + +QString Settings::profileByAttributes(const QString &language, const QStringList &keywords) +{ + KConfig config(locate("data", "kdevelop/profiles/projectprofiles")); + config.setGroup(language); + + QStringList profileKeywords = QStringList::split("/", "Empty"); + if (config.hasKey("Keywords")) + profileKeywords = config.readListEntry("Keywords"); + + int idx = 0; + for (QStringList::const_iterator it = profileKeywords.constBegin(); + it != profileKeywords.constEnd(); ++it) + { + if (keywords.contains(*it)) + { + idx = profileKeywords.findIndex(*it); + break; + } + } + + QStringList profiles; + if (config.hasKey("Profiles")) + { + profiles = config.readListEntry("Profiles"); + return profiles[idx]; + } + return "KDevelop"; +} diff --git a/lib/util/settings.h b/lib/util/settings.h new file mode 100644 index 00000000..cd241cad --- /dev/null +++ b/lib/util/settings.h @@ -0,0 +1,26 @@ +/*************************************************************************** + * Copyright (C) 2005 by Jens Dagerbo * + * jens.dagerbo@swipnet.se * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef SETTINGS_H +#define SETTINGS_H + +class KConfig; + +/// Utility functions for settings +class Settings +{ +public: + static QString terminalEmulatorName( KConfig & config ); + static QString profileByAttributes(const QString &language, const QStringList &keywords); + +}; + +#endif diff --git a/lib/util/urlutil.cpp b/lib/util/urlutil.cpp new file mode 100644 index 00000000..942f1900 --- /dev/null +++ b/lib/util/urlutil.cpp @@ -0,0 +1,319 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Julian Rockey <linux@jrockey.com> + Copyright (C) 2003 Alexander Dymo <cloudtemple@mksat.net> + Copyright (C) 2003 Mario Scalas <mario.scalas@libero.it> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include <qstringlist.h> + +#include <qdir.h> +#include <qfileinfo.h> +#include <kdebug.h> + +#include <unistd.h> +#include <limits.h> +#include <stdlib.h> + +#include "urlutil.h" + +#include <kdeversion.h> + +/////////////////////////////////////////////////////////////////////////////// +// Namespace URLUtil +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::filename(const QString & name) { + int slashPos = name.findRev("/"); + return slashPos<0 ? name : name.mid(slashPos+1); +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::directory(const QString & name) { + int slashPos = name.findRev("/"); + return slashPos<0 ? QString("") : name.left(slashPos); +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::getRelativePath(const QString& basepath, const QString& destpath) +{ + QString relpath = "."; + if (!QFile::exists(basepath) || + !QFile::exists(destpath)) + return ""; + QStringList basedirs = QStringList::split(QString( QChar( QDir::separator() ) ),basepath); + QStringList destdirs = QStringList::split(QString( QChar( QDir::separator() ) ),destpath); + + int maxCompare=0; + if (basedirs.count()>=destdirs.count()) + maxCompare=destdirs.count(); + else + maxCompare=basedirs.count(); + int lastCommonDir=-1; + for (int i=0; i<maxCompare; i++) + { + if (basedirs[i] != destdirs[i]) + break; + lastCommonDir=i; + } + for (uint i=0;i<basedirs.count()-(lastCommonDir+1); i++) + relpath += QString( QChar( QDir::separator() ) )+QString(".."); + for (int i=0; i<lastCommonDir+1; i++) + destdirs.pop_front(); + if (destdirs.count()) + relpath += QString( QChar( QDir::separator() ) )+destdirs.join( QChar( QDir::separator() ) ); + return QDir::cleanDirPath(relpath); +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::relativePath(const KURL & parent, const KURL & child, uint slashPolicy) { + bool slashPrefix = slashPolicy & SLASH_PREFIX; + bool slashSuffix = slashPolicy & SLASH_SUFFIX; + if (parent.equals(child,true)) + return slashPrefix ? QString("/") : QString(""); + + if (!parent.isParentOf(child)) return QString(); + int a=slashPrefix ? -1 : 1; + int b=slashSuffix ? 1 : -1; + return child.path(b).mid(parent.path(a).length()); +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::relativePath(const QString & parent, const QString & child, uint slashPolicy) { + return relativePath(KURL(parent), KURL(child), slashPolicy); +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::upDir(const QString & path, bool slashSuffix) { + int slashPos = path.findRev("/"); + if (slashPos<1) return QString::null; + return path.mid(0,slashPos+ (slashSuffix ? 1 : 0) ); +} + +/////////////////////////////////////////////////////////////////////////////// + +KURL URLUtil::mergeURL(const KURL & source, const KURL & dest, const KURL & child) { + + // if already a child of source, then fine + if (source.isParentOf(child) || source.equals(child,true)) return child; + + // if not a child of dest, return blank URL (error) + if (!dest.isParentOf(child) && !dest.equals(child,true)) return KURL(); + + // if child is same as dest, return source + if (dest.equals(child,true)) return source; + + // calculate + QString childUrlStr = child.url(-1); + QString destStemStr = dest.url(1); + QString sourceStemStr = source.url(1); + return KURL(sourceStemStr.append( childUrlStr.mid( destStemStr.length() ) ) ); + +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::getExtension(const QString & path) { + int dotPos = path.findRev('.'); + if (dotPos<0) return QString(""); + return path.mid(dotPos+1); +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::extractPathNameRelative(const KURL &baseDirUrl, const KURL &url ) +{ + QString absBase = extractPathNameAbsolute( baseDirUrl ), + absRef = extractPathNameAbsolute( url ); + int i = absRef.find( absBase, 0, true ); + + if (i == -1) + return QString(); + + if (absRef == absBase) + return QString( "." ); + else + return absRef.replace( 0, absBase.length(), QString() ); +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::extractPathNameRelative(const QString &basePath, const KURL &url ) +{ + KURL baseDirUrl = KURL::fromPathOrURL( basePath ); + return extractPathNameRelative( baseDirUrl, url ); +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::extractPathNameRelative(const QString &basePath, const QString &absFilePath ) +{ + KURL baseDirUrl = KURL::fromPathOrURL( basePath ), + fileUrl = KURL::fromPathOrURL( absFilePath ); + return extractPathNameRelative( baseDirUrl, fileUrl ); +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::extractPathNameAbsolute( const KURL &url ) +{ + if (isDirectory( url )) + return url.path( +1 ); // with trailing "/" if none is present + else + { + // Ok, this is an over-tight pre-condition on "url" since I hope nobody will never + // stress this function with absurd cases ... but who knows? + /* + QString path = url.path(); + QFileInfo fi( path ); // Argh: QFileInfo is back ;)) + return ( fi.exists()? path : QString() ); + */ + return url.path(); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool URLUtil::isDirectory( const KURL &url ) +{ + return isDirectory( url.path() ); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool URLUtil::isDirectory( const QString &absFilePath ) +{ + return QDir( absFilePath ).exists(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void URLUtil::dump( const KURL::List &urls, const QString &aMessage ) +{ + if (!aMessage.isNull()) + { + kdDebug(9000) << aMessage << endl; + } + kdDebug(9000) << " List has " << urls.count() << " elements." << endl; + + for (size_t i = 0; i<urls.count(); ++i) + { + KURL url = urls[ i ]; +// kdDebug(9000) << " * Element = " << url.path() << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +QStringList URLUtil::toRelativePaths( const QString &baseDir, const KURL::List &urls) +{ + QStringList paths; + + for (size_t i=0; i<urls.count(); ++i) + { + paths << extractPathNameRelative( baseDir, urls[i] ); + } + + return paths; +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::relativePathToFile( const QString & dirUrl, const QString & fileUrl ) +{ + if (dirUrl.isEmpty() || (dirUrl == "/")) + return fileUrl; + + QStringList dir = QStringList::split("/", dirUrl, false); + QStringList file = QStringList::split("/", fileUrl, false); + + QString resFileName = file.last(); + file.remove(file.last()); + + uint i = 0; + while ( (i < dir.count()) && (i < (file.count())) && (dir[i] == file[i]) ) + i++; + + QString result_up; + QString result_down; + QString currDir; + QString currFile; + do + { + i >= dir.count() ? currDir = "" : currDir = dir[i]; + i >= file.count() ? currFile = "" : currFile = file[i]; + //qWarning("i = %d, currDir = %s, currFile = %s", i, currDir.latin1(), currFile.latin1()); + if (currDir.isEmpty() && currFile.isEmpty()) + break; + else if (currDir.isEmpty()) + result_down += file[i] + "/"; + else if (currFile.isEmpty()) + result_up += "../"; + else + { + result_down += file[i] + "/"; + result_up += "../"; + } + i++; + } + while ( (!currDir.isEmpty()) || (!currFile.isEmpty()) ); + + return result_up + result_down + resFileName; +} + +/////////////////////////////////////////////////////////////////////////////// + +//TODO: remove for KDE4 +QString URLUtil::canonicalPath( const QString & path ) +{ + QDir dir(path); + return dir.canonicalPath(); +} + +/////////////////////////////////////////////////////////////////////////////// + +//written by "Dawit A." <adawit@kde.org> +//borrowed from his patch to KShell +QString URLUtil::envExpand ( const QString& str ) +{ + uint len = str.length(); + + if (len > 1 && str[0] == '$') + { + int pos = str.find ('/'); + + if (pos < 0) + pos = len; + + char* ret = getenv( QConstString(str.unicode()+1, pos-1).string().local8Bit().data() ); + + if (ret) + { + QString expandedStr ( QFile::decodeName( ret ) ); + if (pos < (int)len) + expandedStr += str.mid(pos); + return expandedStr; + } + } + + return str; +} + diff --git a/lib/util/urlutil.h b/lib/util/urlutil.h new file mode 100644 index 00000000..4f9ddbab --- /dev/null +++ b/lib/util/urlutil.h @@ -0,0 +1,190 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Julian Rockey <linux@jrockey.com> + Copyright (C) 2003 Mario Scalas <mario.scalas@libero.it> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _URLUTIL_H_ +#define _URLUTIL_H_ + +#include <qstring.h> +#include <qvaluelist.h> +#include <kurl.h> + +/** +@file urlutil.h +Utility functions to operate on URLs. +*/ + +/**Utility functions to operate on URLs.*/ +namespace URLUtil +{ + /**Position of a slash in the URL.*/ + enum SlashesPosition { + SLASH_PREFIX = 1 /**<URL has slash as a prefix.*/, + SLASH_SUFFIX = 2 /**<URL has slash as a suffix.*/ + }; + + /** + * @return The filename part of a pathname (i.e. everything past the last slash). + * @param pathName The absolute path to a file. + */ + QString filename(const QString & pathName); + /** + * @return The directory part of a path (i.e. everything up to but not including the last slash) + * @param pathName The absolute path to a directory. + */ + QString directory(const QString & pathName); + /** + * @return The relative path between a parent and child URL, or blank if the specified + * child is not a child of parent. + * @param parent The parent URL. + * @param child The child URL. + * @param slashPolicy If parent and child are equal then the function returns "/" if + * slashPolicy contains SLASH_PREFIX and otherwise "".\n"/" is appended to a result + * if slashPolicy contains SLASH_SUFFIX.\n"/" is prepended to a result if + * slashPolicy contains SLASH_PREFIX. + */ + QString relativePath(const KURL & parent, const KURL & child, uint slashPolicy = SLASH_PREFIX); + /** + * @return The relative path between a parent and child URL, or blank if the specified + * child is not a child of parent. + * @param parent The parent URL. + * @param child The child URL. + * @param slashPolicy If parent and child are equal then the function returns "/" if + * slashPolicy contains SLASH_PREFIX and otherwise "".\n"/" is appended to a result + * if slashPolicy contains SLASH_SUFFIX.\n"/" is prepended to a result if + * slashPolicy contains SLASH_PREFIX. + */ + QString relativePath(const QString & parent, const QString & child, uint slashPolicy = SLASH_PREFIX); + /** + * @return The relative path between a base path and destination path or. + * @param base The base Path. + * @param dest The destination path. + */ + QString getRelativePath( const QString& base, const QString& dest ); + /** + * @param dirUrl An URL of a directory. + * @param fileUrl An URL of a file. + * @return The relative path between a directory and file. Should never return empty path.\n + * <pre>Example: + * dirUrl: /home/test/src + * fileUrl: /home/test/lib/mylib.cpp + * returns: ../lib/mylib.cpp</pre> + */ + QString relativePathToFile( const QString & dirUrl, const QString & fileUrl ); + /** + *@param path A path (absolute or relative). + *@param slashSuffix if true then "/" is appended to a path. + *@returns The path 'up one level' - the opposite of what filename returns. + */ + QString upDir(const QString & path, bool slashSuffix = false); + /** + * 'Merges' URLs - changes a URL that starts with dest to start with source instead.\n + * <pre>Example: + * source is /home/me/ + * dest is /home/you/ + * child is /home/you/dir1/file1 + * returns /home/me/dir1/fil1</pre> + * @param source An URL of a source. + * @param dest An URL of a destination. + * @param child An URL to change. + * @return The result of merge. + */ + KURL mergeURL(const KURL & source, const KURL & dest, const KURL & child); + /** + * @return The file extension for a filename or path. + * @param path Absolute or relative path. + */ + QString getExtension(const QString & path); + + /** + * Given a base directory url in @p baseDirUrl and the url referring to the + * sub-directory or file, it will return the path relative to @p baseDirUrl. + * If baseDirUrl == url.path() then it will return ".". + * @code + * KURL baseUrl, dirUrl; + * baseUrl.setPath( "/home/mario/src/kdevelop/" ); + * dirUrl.setPath( "/home/mario/src/kdevelop/parts/cvs/" ); + * QString relPathName = extractDirPathRelative( baseUrl, url ); // == "parts/cvs/" + * QString absPathName = extractDirPathAbsolute( url ); // == "/home/mario/src/kdevelop/parts/cvs/" + * @endcode + * Note that if you pass a file name in @p url (instead of a directory) or the + * @p baseUrl is not contained in @p url then the function will return "" (void string). + * + * @param baseDirUrl Base directory URL. + * @param url Base directory URL. + * @return The relative path between @p url and @p baseDirUrl. + */ + QString extractPathNameRelative(const KURL &baseDirUrl, const KURL &url ); + /**Same as above. @p basePath is QString.*/ + QString extractPathNameRelative(const QString &basePath, const KURL &url ); + /**Same as above. Both @p basePath and @p absFilePath are QStrings.*/ + QString extractPathNameRelative(const QString &basePath, const QString &absFilePath ); + + /** + * @param url The url to extract the absolute path from. + * @return The absolute path name referred in @p url. + * Look at @ref extractPathNameRelative documentation for an example. + */ + QString extractPathNameAbsolute( const KURL &url ); + + /** + * @param baseDir Base directory for relative URLs. + * @param urls The list of urls to extract the relative paths from. + * @return A QStringList of relative (to @p baseDir) paths from a list of KURLs in @p urls. + */ + QStringList toRelativePaths( const QString &baseDir, const KURL::List &urls); + + /** + * @param url The absolute URL. + * @return true if @p url is a directory, false otherwise. + */ + bool isDirectory( const KURL &url ); + /** + * @param absFilePath The absolute path. + * @return true if @p url is a directory, false otherwise. + */ + bool isDirectory( const QString &absFilePath ); + + /** + * Dumps the list of KURL @p urls on standard output, eventually printing @p aMessage if it + * is not null. + * @param urls URLs to dump. + * @param aMessage Message to be written onto a stdout. + */ + void dump( const KURL::List &urls, const QString &aMessage = QString::null ); + + /** + * Same as QDir::canonicalPath in later versions of Qt. Earlier versions of Qt + * had this broken, so it's reproduced here. + * Deprecated, use QDir::canonicalPath instead. + */ + QString canonicalPath( const QString & path ); + + /** + * Performs environment variable expansion on @p variable. + * + * @param variable The string with the environment variable to expand. + * @return The expanded environment variable value. if the variable + * cannot be expanded, @p variable itself is returned. + */ + QString envExpand ( const QString &variable ); + +} + +#endif diff --git a/lib/widgets/Mainpage.dox b/lib/widgets/Mainpage.dox new file mode 100644 index 00000000..82ae4551 --- /dev/null +++ b/lib/widgets/Mainpage.dox @@ -0,0 +1,10 @@ +/** +@mainpage The KDevelop Widgets Library + +This library contains a collection of widgets. + +<b>Link with</b>: -lkdevwidgets + +<b>Include path</b>: -I\$(kde_includes)/kdevelop/widgets +*/ + diff --git a/lib/widgets/Makefile.am b/lib/widgets/Makefile.am new file mode 100644 index 00000000..442f0c1c --- /dev/null +++ b/lib/widgets/Makefile.am @@ -0,0 +1,30 @@ +INCLUDES = -I$(top_srcdir)/lib/compat -I$(top_srcdir)/lib/interfaces $(all_includes) + +METASOURCES = AUTO + +lib_LTLIBRARIES = libkdevwidgets.la + +kdevwidgetsincludedir = $(includedir)/kdevelop/widgets + +libkdevwidgets_la_SOURCES = flagboxes.cpp qcomboview.cpp klistviewaction.cpp \ + kcomboview.cpp resizablecombo.cpp kdevhtmlpart.cpp processlinemaker.cpp \ + processwidget.cpp ksavealldialog.cpp fancylistviewitem.cpp + + +kdevwidgetsinclude_HEADERS = klistviewaction.h qcomboview.h flagboxes.h \ + ksavealldialog.h resizablecombo.h kcomboview.h kdevhtmlpart.h processlinemaker.h \ + processwidget.h fancylistviewitem.h + + +SUBDIRS = propeditor + +libkdevwidgets_la_LIBADD = $(top_builddir)/lib/interfaces/libkdevinterfaces.la +libkdevwidgets_la_LDFLAGS = -no-undefined $(all_libraries) +rcdir = $(kde_datadir)/kdevelop + +rc_DATA = kdevhtml_partui.rc + +DOXYGEN_REFERENCES = dcop interfaces kdecore kdefx kdeui khtml kmdi kio kjs kparts kutils kdevutil kdevinterfaces +DOXYGEN_PROJECTNAME = KDevelop Widgets Library +DOXYGEN_DOCDIRPREFIX = kdev +include ../../Doxyfile.am diff --git a/lib/widgets/fancylistviewitem.cpp b/lib/widgets/fancylistviewitem.cpp new file mode 100644 index 00000000..d2bdcd1e --- /dev/null +++ b/lib/widgets/fancylistviewitem.cpp @@ -0,0 +1,203 @@ +/* This file is part of the KDE project + Copyright (C) 2006 David Nolden <david.nolden.kdevelop@art-master.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <qpixmap.h> +#include <qapplication.h> +#include <qnamespace.h> +#include <kdeversion.h> + +#include "fancylistviewitem.h" + + +void FancyListViewItem::init(const QString& label1, const QString& /*label2*/) { + if( !label1.isEmpty() ) { + setText(0, label1); + } +} + + +void FancyListViewItem::setItem(int column, TextPaintItem item) { + if( column >= int(m_items.size()) ) { + m_items.append( item ); + }else{ + m_items[column] = item; + } +} + + +void FancyListViewItem::setText ( int column, const QString & text ) { + if( column >= int(m_items.size()) ) { + m_items.append( TextPaintItem(text) ); + }else{ + m_items[column] = TextPaintItem(text); + } +} + + +QString FancyListViewItem::text(int column) const { + if( m_items.isEmpty() ) return ""; + return (QString)m_items[column]; +} + + +QColor FancyListViewItem::backgroundColor(int col) { + + return KListViewItem::backgroundColor(col); +} + + +///this is a modified version of the original QListViewItem::paintCell from the qt source +///multiline is not supported! + +void FancyListViewItem::paintCell( QPainter *painter, const QColorGroup &cg, + int column, int width, int align) +{ + if(column < 0 || column >= int(m_items.size()) || m_items[column].items().isEmpty()) { + QListViewItem::paintCell(painter, cg, column, width, align); + return; + } + + painter->save(); + QColorGroup grp(cg); + + int styleNum = m_items[column].items()[0].style; + TextPaintStyleStore::Item& style = m_styles.getStyle( styleNum ); + ///currently only the first background-color is used + if( style.bgValid() ) { + grp.setColor( QColorGroup::Base, style.background ); + }else{ + if(backgroundColor(column).isValid()) + grp.setColor( QColorGroup::Base, backgroundColor(column) ); ///use the nice kde background-color + } + + QListView *lv = listView(); + if ( !lv ) + return; + QPainter* p = painter; + QFontMetrics fm( p->fontMetrics() ); + + QString t = text( column ); + + int marg = lv->itemMargin(); + int r = marg; + const QPixmap * icon = pixmap( column ); + + const BackgroundMode bgmode = lv->viewport()->backgroundMode(); + const QColorGroup::ColorRole crole = QPalette::backgroundRoleFromMode( bgmode ); + p->fillRect( 0, 0, width, height(), grp.brush( crole ) ); + + if ( isSelected() && (column == 0 || lv->allColumnsShowFocus()) ) { + p->fillRect( r - marg, 0, width - r + marg, height(), cg.brush( QColorGroup::Highlight ) ); + + if ( isEnabled() || !lv ) + p->setPen( cg.highlightedText() ); + else if ( !isEnabled() && lv) + p->setPen( lv->palette().disabled().highlightedText() ); + } + { + if ( isEnabled() || !lv ) + p->setPen( cg.text() ); + else if ( !isEnabled() && lv) + p->setPen( lv->palette().disabled().text() ); + + + int iconWidth = 0; + + if ( icon ) { + iconWidth = icon->width() + lv->itemMargin(); + int xo = r; + int yo = ( height() - icon->height() ) / 2; + + if ( align & AlignBottom ) + yo = height() - icon->height(); + else if ( align & AlignTop ) + yo = 0; + +// respect horizontal alignment when there is no text for an item. + if ( t.isEmpty() ) { + if ( align & AlignRight ) + xo = width - 2 * marg - iconWidth; + else if ( align & AlignHCenter ) + xo = ( width - iconWidth ) / 2; + } + p->drawPixmap( xo, yo, *icon ); + } + + + if ( !t.isEmpty() ) { + if ( !(align & AlignTop || align & AlignBottom) ) + align |= AlignVCenter; + + r += iconWidth; + + TextPaintItem::Chain::iterator it = m_items[column].items().begin(); + while(it != m_items[column].items().end()) + { + int styleNum = (*it).style; + TextPaintStyleStore::Item& style = m_styles.getStyle( styleNum ); + + painter->setFont(style.font); + p->drawText( r, 0, width-marg-r, height(), align, (*it).text ); + r += textWidth( style.font, (*it).text ); + ++it; + } + } + } + + painter->restore(); +} + + +int FancyListViewItem::textWidth(const QFont& font, const QString& text) +{ + QFontMetrics fm( font ); + if ( multiLinesEnabled() ) + return fm.size( AlignVCenter, text ).width(); + else + return fm.width( text ); + +} + + +int FancyListViewItem::width(const QFontMetrics &fm, const QListView *lv, int column) +{ + int width = 0; + if (column >= 0 && column < (int)m_items.size() && !multiLinesEnabled()) { + TextPaintItem::Chain::iterator it = m_items[column].items().begin(); + while(it != m_items[column].items().end()) { + int styleNum = (*it).style; + TextPaintStyleStore::Item& style = m_styles.getStyle( styleNum ); + + width += textWidth( style.font, (*it).text); + ++it; + } + width += lv->itemMargin() * 2;// - lv->d->minLeftBearing - lv->d->minRightBearing; + + const QPixmap * pm = pixmap( column ); + if ( pm ) + width += pm->width() + lv->itemMargin(); + + width = QMAX( width, QApplication::globalStrut().width() ); + } + else + width = QListViewItem::width(fm, lv, column); + return width; +} + + diff --git a/lib/widgets/fancylistviewitem.h b/lib/widgets/fancylistviewitem.h new file mode 100644 index 00000000..95375af4 --- /dev/null +++ b/lib/widgets/fancylistviewitem.h @@ -0,0 +1,162 @@ +/* This file is part of the KDE project + Copyright (C) 2006 David Nolden <david.nolden.kdevelop@art-master.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef FANCYLISTVIEWITEM +#define FANCYLISTVIEWITEM + +#include <qvaluevector.h> +#include <qpainter.h> +#include <qfont.h> +#include <qlistview.h> +#include <klistview.h> + +class TextPaintStyleStore { + public: + class Item { + public: + QFont font; + QColor color; + QColor background; + + Item(const QFont& f = QFont(), const QColor& c = QColor(), const QColor b = QColor() ) : font(f), color(c), background(b) { + } + + bool bgValid() { + return background.isValid(); + } + + bool colValid() { + return color.isValid(); + } + }; + + typedef QMap<int, Item> Store ; + + TextPaintStyleStore( QFont defaultFont=QFont() ) { + m_styles.insert( 0, Item( defaultFont ) ); + } + + Item& getStyle( int num ) { + Store::Iterator it = m_styles.find( num ); + if( it != m_styles.end() ) return *it; + return m_styles[0]; + } + + void addStyle( int num, Item& style ) { + m_styles[num] = style; + } + + void addStyle( int num, const QFont& font ) { + m_styles[num] = Item( font ); + } + + bool hasStyle( int num ) { + Store::Iterator it = m_styles.find( num ); + return ( it != m_styles.end() ); + } + + private: + Store m_styles; +}; + +class TextPaintItem { + public: + struct Item { + QString text; + int style; + + Item( const QString& t = "", int st = 0 ) : text(t), style(st) { + } + + }; + typedef QValueList<Item> Chain; + + Chain& items() { + return m_chain; + } + + TextPaintItem(const QString& text="") { + addItem( text ); + } + + Item& addItem(const QString& item, int style = 0) { + m_chain.append( Item(item, style) ); + return m_chain.back(); + } + + void clear() { + m_chain.clear(); + } + + operator QString () const { + QString ret; + Chain::const_iterator it = m_chain.begin(); + while(it != m_chain.end()) { + ret += (*it).text; + ++it; + } + return ret; + } + + private: + Chain m_chain; +}; + +///does not support multiple column, a "column" represents a part of the real first column +///KListViewItem is only needed for the background-color + +class FancyListViewItem : public KListViewItem +{ + public: + FancyListViewItem(TextPaintStyleStore& styles, QListView *parent, const QString &label1, const QString &label2="") : KListViewItem(parent, label1, label2), m_styles(styles) { + init(label1, label2); + } + + FancyListViewItem(TextPaintStyleStore& styles, QListViewItem *parent, const QString &label1, const QString &label2="") : KListViewItem(parent, label1, label2), m_styles(styles) { + init(label1, label2); + } + + virtual void paintCell(QPainter *painter, const QColorGroup &cg, int column, int width, int align); + virtual int width(const QFontMetrics &fm, const QListView *lv, int column); + virtual void setText ( int column, const QString & text ); + virtual QString text(int column) const; + + inline void clear() { + m_items.clear(); + } + + inline TextPaintItem& item(int column = 0) { + if(m_items.isEmpty()) { + m_items.append( TextPaintItem() ); + } + + return m_items[column]; + } + + void setItem(int column, TextPaintItem item); + private: + virtual QColor backgroundColor(int col); + void init(const QString& label1, const QString& label2); + int textWidth(const QFont& font, const QString& text); + QValueVector<TextPaintItem> m_items; + protected: + TextPaintStyleStore& m_styles; +}; + +#endif diff --git a/lib/widgets/flagboxes.cpp b/lib/widgets/flagboxes.cpp new file mode 100644 index 00000000..dbbae793 --- /dev/null +++ b/lib/widgets/flagboxes.cpp @@ -0,0 +1,643 @@ +/* This file is part of the KDE project + Copyright (C) 2000-2001 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2003 Alexander Dymo <cloudtemple@mksat.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include <klocale.h> +#include <kdebug.h> +#include <kurlrequester.h> +#include <klineedit.h> +#include <kdialogbase.h> +#include <kdeversion.h> + +#include <qapplication.h> +#include <qtooltip.h> +#include <qheader.h> +#include <qstringlist.h> +#include <qlayout.h> +#include <qlabel.h> +#include <qregexp.h> +#include <qspinbox.h> + +#include "flagboxes.h" + +// partial copy of Qt-3.1 for back-compatibility to KDE-3.0 +QString QRegExp_escape( const QString& str ) +{ + static const char meta[] = "$()*+.?[\\]^{|}"; + QString quoted = str; + int i = 0; + + while ( i < (int) quoted.length() ) { + if ( strchr(meta, quoted[i].latin1()) != 0 ) + quoted.insert( i++, "\\" ); + i++; + } + return quoted; +} + + +class FlagListToolTip : public QToolTip +{ +public: + FlagListToolTip(QWidget *parent); +protected: + void maybeTip(const QPoint &p); +}; + + +FlagListToolTip::FlagListToolTip(QWidget *parent) + : QToolTip(parent) +{} + + +void FlagListToolTip::maybeTip(const QPoint &pos) +{ + FlagListBox *listbox = static_cast<FlagListBox*>(parentWidget()); + QListViewItem *item = listbox->itemAt(pos); + FlagListItem *flitem = static_cast<FlagListItem*>(item); + + if (item) + tip(listbox->itemRect(item), flitem->desc); +} + + +FlagListItem::FlagListItem(FlagListBox *parent, const QString &flagstr, + const QString &description) + : QCheckListItem(parent, flagstr, QCheckListItem::CheckBox), + flag(flagstr), desc(description) +{} + + +FlagListItem::FlagListItem(FlagListBox *parent, const QString &flagstr, + const QString &description, const QString &offstr) + : QCheckListItem(parent, flagstr, QCheckListItem::CheckBox), + flag(flagstr), off(offstr), desc(description) +{} + + +FlagListBox::FlagListBox(QWidget *parent, const char *name) + : QListView(parent, name) +{ + setResizeMode(LastColumn); + header()->hide(); + addColumn(i18n("Flags")); + (void) new FlagListToolTip(this); +} + + +void FlagListBox::readFlags(QStringList *list) +{ + QListViewItem *item = firstChild(); + for (; item; item = item->nextSibling()) { + FlagListItem *flitem = static_cast<FlagListItem*>(item); + QStringList::Iterator sli = list->find(flitem->flag); + if (sli != list->end()) { + flitem->setOn(true); + list->remove(sli); + } + sli = list->find(flitem->off); + if (sli != list->end()) { + flitem->setOn(false); + list->remove(sli); + } + } +} + + +void FlagListBox::writeFlags(QStringList *list) +{ + QListViewItem *item = firstChild(); + for (; item; item = item->nextSibling()) { + FlagListItem *flitem = static_cast<FlagListItem*>(item); + if (flitem->isOn()) + (*list) << flitem->flag; + } +} + + +FlagCheckBox::FlagCheckBox(QWidget *parent, FlagCheckBoxController *controller, + const QString &flagstr, const QString &description) + : QCheckBox(description, parent), flag(flagstr), includeOff(false), useDef(false), defSet(false) +{ + QToolTip::add(this, flagstr); + controller->addCheckBox(this); +} + + +FlagCheckBox::FlagCheckBox(QWidget *parent, FlagCheckBoxController *controller, + const QString &flagstr, const QString &description, + const QString &offstr) + : QCheckBox(description, parent), flag(flagstr), off(offstr), includeOff(false), useDef(false), defSet(false) +{ + QToolTip::add(this, flagstr); + controller->addCheckBox(this); +} + +FlagCheckBox::FlagCheckBox(QWidget *parent, FlagCheckBoxController *controller, + const QString &flagstr, const QString &description, + const QString &offstr, const QString &defstr) + : QCheckBox(description, parent), flag(flagstr), off(offstr), def(defstr), includeOff(false), useDef(true), defSet(false) +{ + QToolTip::add(this, flagstr); + controller->addCheckBox(this); +} + +FlagCheckBoxController::FlagCheckBoxController(QStringList multiKeys) + :m_multiKeys(multiKeys) +{ +} + + +void FlagCheckBoxController::addCheckBox(FlagCheckBox *item) +{ + cblist.append(item); +} + + +void FlagCheckBoxController::readFlags(QStringList *list) +{ + //handle keys like -vxyz -> transform they into -vx -vy -vz + //very "effective" algo :( +/* QStringList addons; + for (QStringList::Iterator mk = m_multiKeys.begin(); mk != m_multiKeys.end(); ++ mk) + { + kdDebug() << "multikey " << *mk << endl; + for (QStringList::Iterator sli = list->begin(); sli != list->end(); ++sli) + { + QString key = *sli; + kdDebug() << "current key: " << key << endl; + if ( (key.length() > 3) && (key.startsWith(*mk)) ) + { + list->remove(sli); + key = key.remove(*mk); + kdDebug() << "refined key " << key << endl; + for (int i = 0; i < key.length(); ++i) + { + kdDebug() << "adding key " << *mk + key[i] << endl; + addons << *mk + key[i]; + } + } + } + } + kdDebug() << "good" << endl; + *list += addons; + + for (QStringList::Iterator sli = list->begin(); sli != list->end(); ++sli) + { + kdDebug() << "KEYS: " << *sli << endl; + } +*/ + QPtrListIterator<FlagCheckBox> it(cblist); + for (; it.current(); ++it) { + FlagCheckBox *fitem = it.current(); + QStringList::Iterator sli = list->find(fitem->flag); + if (sli != list->end()) { + fitem->setChecked(true); + fitem->useDef = false; + list->remove(sli); + } + sli = list->find(fitem->off); + if (sli != list->end()) { + fitem->setChecked(false); + fitem->includeOff = true; + fitem->useDef = false; + list->remove(sli); + } + if (!fitem->def.isEmpty()) + if (fitem->useDef && (fitem->def == fitem->flag)) + { + fitem->setChecked(true); + fitem->defSet = true; + } + else + fitem->useDef = false; + } +} + + +void FlagCheckBoxController::writeFlags(QStringList *list) +{ + QPtrListIterator<FlagCheckBox> it(cblist); + for (; it.current(); ++it) { + FlagCheckBox *fitem = it.current(); + if (fitem->isChecked() && (!fitem->useDef)) + { + (*list) << fitem->flag; + } + else if ((!fitem->off.isEmpty()) && fitem->includeOff) + (*list) << fitem->off; + else if ((fitem->def == fitem->flag) && (!fitem->isChecked())) + (*list) << fitem->off; + else if ((fitem->def == fitem->off) && (fitem->isChecked())) + (*list) << fitem->flag; + } +} + + FlagPathEditController::FlagPathEditController( ) +{ +} + + FlagPathEditController::~ FlagPathEditController( ) +{ +} + +void FlagPathEditController::readFlags( QStringList * list ) +{ +// kdDebug() << "read path flags" << endl; + QPtrListIterator<FlagPathEdit> it(plist); + for (; it.current(); ++it) { + FlagPathEdit *peitem = it.current(); + + QStringList::Iterator sli = list->begin(); + while ( sli != list->end() ) + { + // kdDebug() << "option: " << (*sli) << " flag is: " << peitem->flag << endl; + if ((*sli).startsWith(peitem->flag)) + { +// kdDebug() << "Processing.." << endl; + peitem->setText((*sli).replace(QRegExp(QRegExp_escape(peitem->flag)),"")); + sli = list->remove(sli); + continue; + } + ++sli; + } +/* QStringList::Iterator sli = list->find(peitem->flag); + if (sli != list->end()) { + peitem->setText((*sli).remove(peitem->flag)); + list->remove(sli); + }*/ + } +} + +void FlagPathEditController::writeFlags( QStringList * list ) +{ + QPtrListIterator<FlagPathEdit> it(plist); + for (; it.current(); ++it) { + FlagPathEdit *pitem = it.current(); + if (!pitem->isEmpty()) + (*list) << pitem->flag + pitem->text(); + } +} + +void FlagPathEditController::addPathEdit( FlagPathEdit * item ) +{ + plist.append(item); +} + +FlagPathEdit::FlagPathEdit( QWidget * parent, QString pathDelimiter, + FlagPathEditController * controller, const QString & flagstr, const QString & description, + KFile::Mode mode ) + : QWidget(parent), delimiter(pathDelimiter), flag(flagstr), m_description(description) +{ + QBoxLayout *topLayout = new QVBoxLayout(this, 0, 1); + topLayout->addWidget(new QLabel(description, this)); + QBoxLayout *layout = new QHBoxLayout(topLayout, KDialog::spacingHint()); + + if (delimiter.isEmpty()) + { + url = new KURLRequester(this); + url->setMode(mode); + layout->addWidget(url); + } + else + { + edit = new KLineEdit(this); + layout->addWidget(edit); + details = new QPushButton("...", this); + details->setMaximumWidth(30); + connect(details, SIGNAL(clicked()), this, SLOT(showPathDetails())); + layout->addWidget(details); + } + + QApplication::sendPostedEvents(this, QEvent::ChildInserted); + + QToolTip::add(this, flagstr); + controller->addPathEdit(this); +} + +void FlagPathEdit::showPathDetails( ) +{ + KDialogBase *dia = new KDialogBase(0, "flag_path_edit_dia", true, m_description, + KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true); + + QBoxLayout *diaLayout = new QVBoxLayout(dia, KDialog::marginHint(), KDialog::spacingHint()); + diaLayout->setAutoAdd(true); + + KURLRequester *req = new KURLRequester( dia ); + req->setMode(KFile::Directory); + KEditListBox::CustomEditor pCustomEditor; + pCustomEditor = req->customEditor(); + KEditListBox *elb = new KEditListBox( "", pCustomEditor, dia ); + dia->setMainWidget(elb); + + elb->insertStringList(QStringList::split(delimiter, text())); + + if (dia->exec() == QDialog::Accepted) + { + setText(elb->items().join(delimiter)); + } + + delete dia; +} + +void FlagPathEdit::setText( const QString text ) +{ + if (delimiter.isEmpty()) + url->setURL(text); + else + edit->setText(text); +} + +QString FlagPathEdit::text( ) +{ + if (delimiter.isEmpty()) + return url->url(); + else + return edit->text(); +} + +bool FlagPathEdit::isEmpty( ) +{ + if (delimiter.isEmpty()) + return url->url().isEmpty(); + else + return edit->text().isEmpty(); +} + +FlagRadioButton::FlagRadioButton( QWidget * parent, FlagRadioButtonController * controller, const QString & flagstr, const QString & description ) + : QRadioButton(description, parent), flag(flagstr) +{ + QToolTip::add(this, flagstr); + controller->addRadioButton(this); +} + +FlagRadioButtonController::FlagRadioButtonController(QStringList multiKeys) + :m_multiKeys(multiKeys) +{ +} + +void FlagRadioButtonController::addRadioButton(FlagRadioButton *item) +{ + cblist.append(item); +} + + +void FlagRadioButtonController::readFlags(QStringList *list) +{ + //handle keys like -vxyz -> transform they into -vx -vy -vz + //very "effective" algo :( +/* QStringList addons; + for (QStringList::Iterator mk = m_multiKeys.begin(); mk != m_multiKeys.end(); ++ mk) + { + kdDebug() << "multikey " << *mk << endl; + for (QStringList::Iterator sli = list->begin(); sli != list->end(); ++sli) + { + QString key = *sli; + kdDebug() << "current key: " << key << endl; + if ( (key.length() > 3) && (key.startsWith(*mk)) ) + { + list->remove(sli); + key = key.remove(*mk); + kdDebug() << "refined key " << key << endl; + for (int i = 0; i < key.length(); ++i) + { + kdDebug() << "adding key " << *mk + key[i] << endl; + addons << *mk + key[i]; + } + } + } + } + kdDebug() << "good" << endl; + *list += addons; + + for (QStringList::Iterator sli = list->begin(); sli != list->end(); ++sli) + { + kdDebug() << "KEYS: " << *sli << endl; + } +*/ + QPtrListIterator<FlagRadioButton> it(cblist); + for (; it.current(); ++it) { + FlagRadioButton *fitem = it.current(); + QStringList::Iterator sli = list->find(fitem->flag); + if (sli != list->end()) { + fitem->setChecked(true); + list->remove(sli); + } + } +} + + +void FlagRadioButtonController::writeFlags(QStringList *list) +{ + QPtrListIterator<FlagRadioButton> it(cblist); + for (; it.current(); ++it) { + FlagRadioButton *fitem = it.current(); + if (fitem->isChecked()) + (*list) << fitem->flag; + } +} + + FlagEditController::FlagEditController( ) +{ +} + + FlagEditController::~ FlagEditController( ) +{ +} + +void FlagEditController::readFlags( QStringList * list ) +{ + QPtrListIterator<FlagListEdit> it(plist); + for (; it.current(); ++it) { + FlagListEdit *peitem = it.current(); + + QStringList::Iterator sli = list->begin(); + while (sli != list->end()) + { + if ((*sli).startsWith(peitem->flag)) + { + peitem->appendText((*sli).replace(QRegExp(QRegExp_escape(peitem->flag)),"")); + sli = list->remove(sli); + continue; + } + ++sli; + } + } + + + QPtrListIterator<FlagSpinEdit> it2(slist); + for (; it2.current(); ++it2) { + FlagSpinEdit *sitem = it2.current(); + + QStringList::Iterator sli = list->begin(); + while ( sli != list->end() ) + { + if ((*sli).startsWith(sitem->flag)) + { + sitem->setText((*sli).replace(QRegExp(QRegExp_escape(sitem->flag)),"")); + sli = list->remove(sli); + continue; + } + ++sli; + } + } +} + +void FlagEditController::writeFlags( QStringList * list ) +{ + QPtrListIterator<FlagListEdit> it(plist); + for (; it.current(); ++it) { + FlagListEdit *pitem = it.current(); + if (!pitem->isEmpty()) + (*list) += pitem->flags(); + } + + QPtrListIterator<FlagSpinEdit> it2(slist); + for (; it2.current(); ++it2) { + FlagSpinEdit *sitem = it2.current(); + if (!sitem->isDefault()) + (*list) << sitem->flags(); + } +} + +void FlagEditController::addListEdit( FlagListEdit * item ) +{ + plist.append(item); +} + +void FlagEditController::addSpinBox(FlagSpinEdit *item) +{ + slist.append(item); +} + + +FlagListEdit::FlagListEdit( QWidget * parent, QString listDelimiter, FlagEditController * controller, + const QString & flagstr, const QString & description) + : QWidget(parent), delimiter(listDelimiter), flag(flagstr), m_description(description) +{ + QBoxLayout *topLayout = new QVBoxLayout(this, 0, 1); + topLayout->addWidget(new QLabel(description, this)); + QBoxLayout *layout = new QHBoxLayout(topLayout, KDialog::spacingHint()); + + edit = new KLineEdit(this); + layout->addWidget(edit); + if (! listDelimiter.isEmpty()) + { + details = new QPushButton("...", this); + details->setMaximumWidth(30); + connect(details, SIGNAL(clicked()), this, SLOT(showListDetails())); + layout->addWidget(details); + } + + QApplication::sendPostedEvents(this, QEvent::ChildInserted); + + QToolTip::add(this, flagstr); + controller->addListEdit(this); +} + +void FlagListEdit::setText( const QString text ) +{ + edit->setText(text); +} + +bool FlagListEdit::isEmpty( ) +{ + return edit->text().isEmpty(); +} + +QString FlagListEdit::text( ) +{ + return edit->text(); +} + +void FlagListEdit::showListDetails( ) +{ + KDialogBase *dia = new KDialogBase(0, "flag_list_edit_dia", true, m_description, + KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true); + + QBoxLayout *diaLayout = new QVBoxLayout(dia, KDialog::marginHint(), KDialog::spacingHint()); + diaLayout->setAutoAdd(true); + + KEditListBox *elb = new KEditListBox( "", dia ); + dia->setMainWidget(elb); + + elb->insertStringList(QStringList::split(delimiter, text())); + + if (dia->exec() == QDialog::Accepted) + { + setText(elb->items().join(delimiter)); + } + + delete dia; +} + +void FlagListEdit::appendText( const QString text ) +{ + edit->setText(edit->text() + (edit->text().isEmpty()?QString(""):delimiter) + text); +} + +QStringList FlagListEdit::flags( ) +{ + QStringList fl = QStringList::split(delimiter, text()); + for (QStringList::iterator it = fl.begin(); it != fl.end(); ++it) + { + (*it).prepend(flag); + } + return fl; +} + +FlagSpinEdit::FlagSpinEdit( QWidget * parent, int minVal, int maxVal, int incr, int defaultVal, FlagEditController * controller, const QString & flagstr, const QString & description ) + :QWidget(parent), m_defaultVal(defaultVal), flag(flagstr) +{ + QBoxLayout *topLayout = new QVBoxLayout(this, 0, 1); + topLayout->addWidget(new QLabel(description, this)); + + spb = new QSpinBox(minVal, maxVal, incr, this); + spb->setValue(defaultVal); + topLayout->addWidget(spb); + + QApplication::sendPostedEvents(this, QEvent::ChildInserted); + + QToolTip::add(this, flagstr); + controller->addSpinBox(this); +} + +void FlagSpinEdit::setText( const QString text ) +{ + spb->setValue(text.toInt()); +} + +QString FlagSpinEdit::text( ) +{ + return QString("%1").arg(spb->value()); +} + +QString FlagSpinEdit::flags( ) +{ + return flag + text(); +} + +bool FlagSpinEdit::isDefault( ) +{ + if (spb->value() == m_defaultVal) + return true; + return false; +} + + +#include "flagboxes.moc" diff --git a/lib/widgets/flagboxes.h b/lib/widgets/flagboxes.h new file mode 100644 index 00000000..a64a47eb --- /dev/null +++ b/lib/widgets/flagboxes.h @@ -0,0 +1,283 @@ +/* This file is part of the KDE project + Copyright (C) 2000-2001 Bernd Gehrmann <bernd@kdevelop.org> + Copyright (C) 2003 Alexander Dymo <cloudtemple@mksat.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _FLAGBOXES_H_ +#define _FLAGBOXES_H_ + +#include <kfile.h> + +/** +@file flagboxes.h +Support classes for compiler plugins. +*/ + +#include <qlistview.h> +#include <qcheckbox.h> +#include <qradiobutton.h> +#include <qptrlist.h> +#include <qstringlist.h> + +class QSpinBox; +class FlagListBox; +class FlagListToolTip; +class FlagCheckBoxController; +class FlagRadioButtonController; +class FlagPathEditController; +class FlagEditController; +class KLineEdit; +class QPushButton; +class KURLRequester; + +/**List item holding a compiler flag.*/ +class FlagListItem : public QCheckListItem +{ +public: + FlagListItem(FlagListBox *parent, const QString &flagstr, + const QString &description); + FlagListItem(FlagListBox *parent, const QString &flagstr, + const QString &description, const QString &offstr); + ~FlagListItem() + {} +private: + QString flag; + QString off; + QString desc; + friend class FlagListToolTip; + friend class FlagListBox; +}; + + +/**List box item holding a compiler flag.*/ +class FlagListBox : public QListView +{ + Q_OBJECT +public: + FlagListBox( QWidget *parent=0, const char *name=0 ); + ~FlagListBox() + {} + + void readFlags(QStringList *list); + void writeFlags(QStringList *list); +}; + + +/**Check box item holding a compiler flag.*/ +class FlagCheckBox : public QCheckBox +{ + Q_OBJECT +public: + FlagCheckBox(QWidget *parent, FlagCheckBoxController *controller, + const QString &flagstr, const QString &description); + FlagCheckBox(QWidget *parent, FlagCheckBoxController *controller, + const QString &flagstr, const QString &description, + const QString &offstr); + FlagCheckBox(QWidget *parent, FlagCheckBoxController *controller, + const QString &flagstr, const QString &description, + const QString &offstr, const QString &defstr); + ~FlagCheckBox() + {} + +private: + QString flag; + QString off; + QString def; + bool includeOff; + bool useDef; + bool defSet; + friend class FlagCheckBoxController; +}; + +/**Radiobutton item holding an option of a compiler flag.*/ +class FlagRadioButton : public QRadioButton +{ + Q_OBJECT +public: + FlagRadioButton(QWidget *parent, FlagRadioButtonController *controller, + const QString &flagstr, const QString &description); + ~FlagRadioButton() + {} + +private: + QString flag; + friend class FlagRadioButtonController; +}; + +/**Path editor if path is a compiler flag.*/ +class FlagPathEdit: public QWidget +{ + Q_OBJECT +public: + /**If the pathDelimiter is not empty then path edit can contain a list of paths*/ + FlagPathEdit(QWidget *parent, QString pathDelimiter, FlagPathEditController *controller, + const QString &flagstr, const QString &description, KFile::Mode mode = KFile::Directory); + + ~FlagPathEdit() {} + + void setText(const QString text); + bool isEmpty(); + QString text(); + +private slots: + void showPathDetails(); + +private: + KLineEdit *edit; + QPushButton *details; + KURLRequester *url; + + QString delimiter; + QString flag; + QString m_description; + friend class FlagPathEditController; +}; + +/**List editor if list is a compiler flag.*/ +class FlagListEdit: public QWidget +{ + Q_OBJECT +public: + /**If the listDelimiter is not empty then list edit can contain a list of entries*/ + FlagListEdit(QWidget *parent, QString listDelimiter, FlagEditController *controller, + const QString &flagstr, const QString &description); + + ~FlagListEdit() {} + + void setText(const QString text); + void appendText(const QString text); + bool isEmpty(); + QString text(); + QStringList flags(); + +private slots: + void showListDetails(); + +private: + KLineEdit *edit; + QPushButton *details; + + QString delimiter; + QString flag; + QString m_description; + friend class FlagEditController; +}; + +/**Spin editor of a compiler flag.*/ +class FlagSpinEdit: public QWidget +{ +public: + FlagSpinEdit(QWidget *parent, int minVal, int maxVal, int incr, int defaultVal, FlagEditController *controller, + const QString &flagstr, const QString &description); + ~FlagSpinEdit() {} + + void setText(const QString text); + QString text(); + bool isDefault(); + + QString flags(); + +private: + int m_defaultVal; + QString flag; + + QSpinBox *spb; + + friend class FlagEditController; +}; + +/**Controller for path editors.*/ +class FlagPathEditController +{ +public: + FlagPathEditController(); + ~FlagPathEditController(); + + void readFlags(QStringList *list); + void writeFlags(QStringList *list); + +private: + void addPathEdit(FlagPathEdit *item); + QPtrList<FlagPathEdit> plist; + friend class FlagPathEdit; +}; + +/**Controller for flag editors.*/ +class FlagEditController +{ +public: + FlagEditController(); + ~FlagEditController(); + + void readFlags(QStringList *list); + void writeFlags(QStringList *list); + +private: + void addListEdit(FlagListEdit *item); + void addSpinBox(FlagSpinEdit *item); + QPtrList<FlagListEdit> plist; + QPtrList<FlagSpinEdit> slist; + friend class FlagListEdit; + friend class FlagSpinEdit; +}; + +/**Controller for check box editors.*/ +class FlagCheckBoxController +{ +public: + /**"multi key" is a list of options like -vxyz (-vx -vy -vz) + multiKeys must contain a list of option names like {-v} + in the above example. + */ + FlagCheckBoxController(QStringList multiKeys = QStringList()); + ~FlagCheckBoxController() + {} + + void readFlags(QStringList *list); + void writeFlags(QStringList *list); + + void addCheckBox(FlagCheckBox *item); +private: + QPtrList<FlagCheckBox> cblist; + + QStringList m_multiKeys; +}; + +/**Controller for radiobutton editors.*/ +class FlagRadioButtonController +{ +public: + /**multiKeys is a list of options like -vxyz (-vx -vy -vz) + multiKeys must contain a list of option names like {-v} + in the above example. + */ + FlagRadioButtonController(QStringList multiKeys = QStringList()); + ~FlagRadioButtonController() + {} + + void readFlags(QStringList *list); + void writeFlags(QStringList *list); + + void addRadioButton(FlagRadioButton *item); +private: + QPtrList<FlagRadioButton> cblist; + + QStringList m_multiKeys; +}; + +#endif diff --git a/lib/widgets/kcomboview.cpp b/lib/widgets/kcomboview.cpp new file mode 100644 index 00000000..ecaf4593 --- /dev/null +++ b/lib/widgets/kcomboview.cpp @@ -0,0 +1,101 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Alexander Dymo <cloudtemple@mksat.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include <klineedit.h> +#include <klistview.h> +#include <kcompletionbox.h> +#include <qheader.h> +#include <qmap.h> + +#include "kcomboview.h" + + +KComboView::KComboView( bool rw, int defaultWidth, QWidget* parent, const char* name , CustomCompleter* comp) + :QComboView(rw, parent, name), m_comp( comp ), m_defaultWidth(defaultWidth) +{ + if (rw) + { + KLineEdit *ed = new KLineEdit(this, "combo edit"); + ed->setCompletionMode(KGlobalSettings::CompletionPopup); + ed->setCompletionObject(m_comp); + ed->completionBox()->setHScrollBarMode(QListBox::Auto); + setLineEdit(ed); + } + setMinimumWidth(defaultWidth); +} + +KComboView::~KComboView() { + delete m_comp; +} + +void KComboView::addItem(QListViewItem *it) +{ + m_comp->addItem(it->text(0)); +} + +void KComboView::removeItem(QListViewItem *it) +{ + if (it == currentItem()) + { + setCurrentItem(0); + setCurrentText(m_defaultText); + } + m_comp->removeItem(it->text(0)); + delete it; +} + +void KComboView::renameItem(QListViewItem *it, const QString &newName) +{ + m_comp->removeItem(it->text(0)); + it->setText(0, newName); + m_comp->addItem(newName); +} + +void KComboView::clear( ) +{ + m_comp->clear(); + QComboView::clear(); + + setCurrentText(m_defaultText); +} + +int KComboView::defaultWidth( ) +{ + return m_defaultWidth; +} + +void KComboView::setDefaultText( const QString & text ) +{ + m_defaultText = text; +} + +void KComboView::setUpListView() +{ + KListView *listView = new KListView( this, "in-combo" ); + listView->setRootIsDecorated( false ); + listView->setAllColumnsShowFocus(true); + listView->addColumn(""); + listView->setResizeMode(QListView::LastColumn); + + listView->header()->hide(); + + setListView(listView); +} + + +#include "kcomboview.moc" diff --git a/lib/widgets/kcomboview.h b/lib/widgets/kcomboview.h new file mode 100644 index 00000000..9696d328 --- /dev/null +++ b/lib/widgets/kcomboview.h @@ -0,0 +1,80 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Alexander Dymo <cloudtemple@mksat.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KCOMBOVIEW_H +#define KCOMBOVIEW_H + +#include <kcompletion.h> + +#include "qcomboview.h" + +/** +@file kcomboview.h +KComboView class. +*/ + +/** +KComboView - a combo with a QListView as a popup widget. +KComboView provides text completion. +@sa QComboView for a description. +*/ + + +///This can be used to insert own KCompletion-implementations +class CustomCompleter : public KCompletion { + public: + virtual void addItem (const QString &item) { + KCompletion::addItem( item ); + } + + virtual void removeItem (const QString &item) { + KCompletion::removeItem( item ); + } + + virtual void clear() { + KCompletion::clear(); + } +}; + + +class KComboView: public QComboView +{ + Q_OBJECT +public: + ///The combo-view takes the ownership of the completer and deletes it on destruction + KComboView( bool rw, int defaultWidth = 100, QWidget* parent=0, const char* name=0, CustomCompleter* completer = new CustomCompleter() ); + virtual ~KComboView(); + + virtual void addItem(QListViewItem *it); + virtual void removeItem(QListViewItem *it); + virtual void renameItem(QListViewItem *it, const QString &newName); + + virtual void setDefaultText(const QString &text); + + virtual void clear(); + + int defaultWidth(); +private: + virtual void setUpListView(); + + CustomCompleter* m_comp; + int m_defaultWidth; + QString m_defaultText; +}; + +#endif diff --git a/lib/widgets/kdevhtml_partui.rc b/lib/widgets/kdevhtml_partui.rc new file mode 100644 index 00000000..5a55b7bf --- /dev/null +++ b/lib/widgets/kdevhtml_partui.rc @@ -0,0 +1,18 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui version="3" name="KDevHTMLPart" > + +<MenuBar> + <Menu name="file" noMerge="0"><text>&File</text> + <Action name="print_doc" group="print_merge"/> + </Menu> + <Menu name="edit" noMerge="0"> + <Action name="copy_doc_selection" group="edit_paste_merge"/> + </Menu> +</MenuBar> + +<ToolBar name="browserToolBar"> + <Action name="browser_back" group="browser_operations"/> + <Action name="browser_forward" group="browser_operations"/> +</ToolBar> + +</kpartgui> diff --git a/lib/widgets/kdevhtmlpart.cpp b/lib/widgets/kdevhtmlpart.cpp new file mode 100644 index 00000000..688e373a --- /dev/null +++ b/lib/widgets/kdevhtmlpart.cpp @@ -0,0 +1,512 @@ +#include <qfile.h> +#include <qclipboard.h> +#include <qapplication.h> + +#include <kxmlguiclient.h> +#include <kaction.h> +#include <kstdaction.h> +#include <kstandarddirs.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kiconloader.h> +#include <kmainwindow.h> +#include <khtmlview.h> +#include <khtml_settings.h> +#include <kconfig.h> + +#include <kdevmainwindow.h> + + +#include "kdevhtmlpart.h" + +KDevHTMLPart::KDevHTMLPart() + : KHTMLPart(0L, 0L, 0L, "KDevHTMLPart", DefaultGUI ) +{ + setXMLFile(locate("data", "kdevelop/kdevhtml_partui.rc"), true); + + connect(browserExtension(), SIGNAL(openURLRequestDelayed(const KURL &,const KParts::URLArgs &)), + this, SLOT(openURLRequest(const KURL &)) ); + + connect(this, SIGNAL(started(KIO::Job *)), this, SLOT(slotStarted(KIO::Job* ))); + connect(this, SIGNAL(completed()), this, SLOT(slotCompleted())); + connect(this, SIGNAL(canceled(const QString &)), this, SLOT(slotCancelled(const QString &))); + + KActionCollection * actions = actionCollection();// new KActionCollection( this ); + reloadAction = new KAction( i18n( "Reload" ), "reload", 0, + this, SLOT( slotReload() ), actions, "doc_reload" ); + reloadAction->setWhatsThis(i18n("<b>Reload</b><p>Reloads the current document.")); + stopAction = new KAction( i18n( "Stop" ), "stop", 0, + this, SLOT( slotStop() ), actions, "doc_stop" ); + stopAction->setWhatsThis(i18n("<b>Stop</b><p>Stops the loading of current document.")); + duplicateAction = new KAction( i18n( "Duplicate Tab" ), "window_new", 0, + this, SLOT( slotDuplicate() ), actions, "doc_dup" ); + duplicateAction->setWhatsThis(i18n("<b>Duplicate window</b><p>Opens current document in a new window.")); + printAction = KStdAction::print(this, SLOT(slotPrint()), actions, "print_doc"); + copyAction = KStdAction::copy(this, SLOT(slotCopy()), actions, "copy_doc_selection"); + + connect( this, SIGNAL(popupMenu(const QString &, const QPoint &)), this, SLOT(popup(const QString &, const QPoint &))); + connect(this, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged())); + +//BEGIN documentation history stuff + + m_backAction = new KToolBarPopupAction(i18n("Back"), "back", 0, + this, SLOT(slotBack()), + actions, "browser_back"); + m_backAction->setEnabled( false ); + m_backAction->setToolTip(i18n("Back")); + m_backAction->setWhatsThis(i18n("<b>Back</b><p>Moves backwards one step in the <b>documentation</b> browsing history.")); + + connect(m_backAction->popupMenu(), SIGNAL(aboutToShow()), + this, SLOT(slotBackAboutToShow())); + connect(m_backAction->popupMenu(), SIGNAL(activated(int)), + this, SLOT(slotPopupActivated(int))); + + m_forwardAction = new KToolBarPopupAction(i18n("Forward"), "forward", 0, + this, SLOT(slotForward()), + actions, "browser_forward"); + m_forwardAction->setEnabled( false ); + m_forwardAction->setToolTip(i18n("Forward")); + m_forwardAction->setWhatsThis(i18n("<b>Forward</b><p>Moves forward one step in the <b>documentation</b> browsing history.")); + + connect(m_forwardAction->popupMenu(), SIGNAL(aboutToShow()), + this, SLOT(slotForwardAboutToShow())); + connect(m_forwardAction->popupMenu(), SIGNAL(activated(int)), + this, SLOT(slotPopupActivated(int))); + + m_restoring = false; + m_Current = m_history.end(); +//END documentation history stuff + + //settings: + KConfig *appConfig = KGlobal::config(); + appConfig->setGroup("KHTMLPart"); + setStandardFont(appConfig->readEntry("StandardFont", + settings()->stdFontName())); + setFixedFont(appConfig->readEntry("FixedFont", + settings()->fixedFontName())); + setZoomFactor(appConfig->readEntry("Zoom", "100").toInt()); +} + +void KDevHTMLPart::popup( const QString & url, const QPoint & p ) +{ +// KPopupMenu popup( i18n( "Documentation Viewer" ), this->widget() ); + KPopupMenu popup(this->widget()); + + bool needSep = false; + int idNewWindow = -2; + if (!url.isEmpty() && (m_options & CanOpenInNewWindow)) + { + idNewWindow = popup.insertItem(SmallIcon("window_new"),i18n("Open in New Tab")); + popup.setWhatsThis(idNewWindow, i18n("<b>Open in new window</b><p>Opens current link in a new window.")); + needSep = true; + } + if (m_options & CanDuplicate) + { + duplicateAction->plug(&popup); + needSep = true; + } + if (needSep) + popup.insertSeparator(); + + m_backAction->plug( &popup ); + m_forwardAction->plug( &popup ); + reloadAction->plug(&popup); +// stopAction->plug(&popup); + popup.insertSeparator(); + + copyAction->plug( &popup ); + popup.insertSeparator(); + + printAction->plug(&popup); + popup.insertSeparator(); + + KAction * incFontAction = this->action("incFontSizes"); + KAction * decFontAction = this->action("decFontSizes"); + if ( incFontAction && decFontAction ) + { + incFontAction->plug( &popup ); + decFontAction->plug( &popup ); + popup.insertSeparator(); + } + + KAction *ac = action("setEncoding"); + if (ac) + ac->plug(&popup); + + int r = popup.exec(p); + + if (r == idNewWindow) + { + KURL kurl; + if (!KURL(url).path().startsWith("/")) + { + kdDebug() << "processing relative url: " << url << endl; + if (url.startsWith("#")) + { + kurl = KURL(KDevHTMLPart::url()); + kurl.setRef(url.mid(1)); + } + else + kurl = KURL(KDevHTMLPart::url().upURL().url(true)+url); + } + else + kurl = KURL(url); + + if (kurl.isValid()) + slotOpenInNewWindow(kurl); + } +} + +void KDevHTMLPart::setContext(const QString &context) +{ + m_context = context; +} + + +QString KDevHTMLPart::context() const +{ + return m_context; +} + + +// Note: this function is a copy of code in kdecore/kconfigbase.cpp ;) +static bool isUtf8(const char *buf) { + int i, n; + register unsigned char c; + bool gotone = false; + +#define F 0 /* character never appears in text */ +#define T 1 /* character appears in plain ASCII text */ +#define I 2 /* character appears in ISO-8859 text */ +#define X 3 /* character appears in non-ISO extended ASCII (Mac, IBM PC) */ + + static const unsigned char text_chars[256] = { + /* BEL BS HT LF FF CR */ + F, F, F, F, F, F, F, T, T, T, T, F, T, T, F, F, /* 0x0X */ + /* ESC */ + F, F, F, F, F, F, F, F, F, F, F, T, F, F, F, F, /* 0x1X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x2X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x3X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x4X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x5X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x6X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, F, /* 0x7X */ + /* NEL */ + X, X, X, X, X, T, X, X, X, X, X, X, X, X, X, X, /* 0x8X */ + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x9X */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xaX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xbX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xcX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xdX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xeX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I /* 0xfX */ + }; + + /* *ulen = 0; */ + for (i = 0; (c = buf[i]); i++) { + if ((c & 0x80) == 0) { /* 0xxxxxxx is plain ASCII */ + /* + * Even if the whole file is valid UTF-8 sequences, + * still reject it if it uses weird control characters. + */ + + if (text_chars[c] != T) + return false; + + } else if ((c & 0x40) == 0) { /* 10xxxxxx never 1st byte */ + return false; + } else { /* 11xxxxxx begins UTF-8 */ + int following; + + if ((c & 0x20) == 0) { /* 110xxxxx */ + following = 1; + } else if ((c & 0x10) == 0) { /* 1110xxxx */ + following = 2; + } else if ((c & 0x08) == 0) { /* 11110xxx */ + following = 3; + } else if ((c & 0x04) == 0) { /* 111110xx */ + following = 4; + } else if ((c & 0x02) == 0) { /* 1111110x */ + following = 5; + } else + return false; + + for (n = 0; n < following; n++) { + i++; + if (!(c = buf[i])) + goto done; + + if ((c & 0x80) == 0 || (c & 0x40)) + return false; + } + gotone = true; + } + } +done: + return gotone; /* don't claim it's UTF-8 if it's all 7-bit */ +} +#undef F +#undef T +#undef I +#undef X + +QString KDevHTMLPart::resolveEnvVarsInURL(const QString& url) +{ + // check for environment variables and make necessary translations + QString path = url; + int nDollarPos = path.find( '$' ); + + // Note: the while loop below is a copy of code in kdecore/kconfigbase.cpp ;) + while( nDollarPos != -1 && nDollarPos+1 < static_cast<int>(path.length())) { + // there is at least one $ + if( (path)[nDollarPos+1] == '(' ) { + uint nEndPos = nDollarPos+1; + // the next character is no $ + while ( (nEndPos <= path.length()) && (path[nEndPos]!=')') ) + nEndPos++; + nEndPos++; + QString cmd = path.mid( nDollarPos+2, nEndPos-nDollarPos-3 ); + + QString result; + FILE *fs = popen(QFile::encodeName(cmd).data(), "r"); + if (fs) + { + QTextStream ts(fs, IO_ReadOnly); + result = ts.read().stripWhiteSpace(); + pclose(fs); + } + path.replace( nDollarPos, nEndPos-nDollarPos, result ); + } else if( (path)[nDollarPos+1] != '$' ) { + uint nEndPos = nDollarPos+1; + // the next character is no $ + QString aVarName; + if (path[nEndPos]=='{') + { + while ( (nEndPos <= path.length()) && (path[nEndPos]!='}') ) + nEndPos++; + nEndPos++; + aVarName = path.mid( nDollarPos+2, nEndPos-nDollarPos-3 ); + } + else + { + while ( nEndPos <= path.length() && (path[nEndPos].isNumber() + || path[nEndPos].isLetter() || path[nEndPos]=='_' ) ) + nEndPos++; + aVarName = path.mid( nDollarPos+1, nEndPos-nDollarPos-1 ); + } + const char* pEnv = 0; + if (!aVarName.isEmpty()) + pEnv = getenv( aVarName.ascii() ); + if( pEnv ) { + // !!! Sergey A. Sukiyazov <corwin@micom.don.ru> !!! + // A environment variables may contain values in 8bit + // locale cpecified encoding or in UTF8 encoding. + if (isUtf8( pEnv )) + path.replace( nDollarPos, nEndPos-nDollarPos, QString::fromUtf8(pEnv) ); + else + path.replace( nDollarPos, nEndPos-nDollarPos, QString::fromLocal8Bit(pEnv) ); + } else + path.remove( nDollarPos, nEndPos-nDollarPos ); + } else { + // remove one of the dollar signs + path.remove( nDollarPos, 1 ); + nDollarPos++; + } + nDollarPos = path.find( '$', nDollarPos ); + } + + return path; +} + +bool KDevHTMLPart::openURL(const KURL &url) +{ + QString path = resolveEnvVarsInURL(url.url()); + KURL newUrl(path); + + bool retval = KHTMLPart::openURL(newUrl); + if ( retval ) + { + emit fileNameChanged(this); + if ( !m_restoring ) + { + addHistoryEntry(); + } + } + + m_backAction->setEnabled( m_Current != m_history.begin() ); + m_forwardAction->setEnabled( m_Current != m_history.fromLast() ); + + return retval; +} + +void KDevHTMLPart::openURLRequest(const KURL &url) +{ + openURL( url ); +} + +void KDevHTMLPart::slotReload( ) +{ + openURL( url() ); +} + +void KDevHTMLPart::slotStop( ) +{ + closeURL(); +} + +void KDevHTMLPart::slotStarted( KIO::Job * ) +{ + stopAction->setEnabled(true); +} + +void KDevHTMLPart::slotCompleted( ) +{ + stopAction->setEnabled(false); +} + +void KDevHTMLPart::slotCancelled( const QString & /*errMsg*/ ) +{ + stopAction->setEnabled(false); +} + +/*void KDevHTMLPart::slotDuplicate( ) +{ + PartController::getInstance()->showDocument(url(), true); +}*/ + +void KDevHTMLPart::slotPrint( ) +{ + view()->print(); +} + +void KDevHTMLPart::slotBack() +{ + if ( m_Current != m_history.begin() ) + { + --m_Current; + m_restoring = true; + openURL( (*m_Current).url ); + m_restoring = false; + } +} + +void KDevHTMLPart::slotForward() +{ + if ( m_Current != m_history.fromLast() ) + { + ++m_Current; + m_restoring = true; + openURL( (*m_Current).url ); + m_restoring = false; + } +} + +void KDevHTMLPart::slotBackAboutToShow() +{ + KPopupMenu *popup = m_backAction->popupMenu(); + popup->clear(); + + if ( m_Current == m_history.begin() ) return; + + QValueList<DocumentationHistoryEntry>::Iterator it = m_Current; + --it; + + int i = 0; + while( i < 10 ) + { + if ( it == m_history.begin() ) + { + popup->insertItem( (*it).url.url(), (*it).id ); + return; + } + + popup->insertItem( (*it).url.url(), (*it).id ); + ++i; + --it; + } +} + +void KDevHTMLPart::slotForwardAboutToShow() +{ + KPopupMenu *popup = m_forwardAction->popupMenu(); + popup->clear(); + + if ( m_Current == m_history.fromLast() ) return; + + QValueList<DocumentationHistoryEntry>::Iterator it = m_Current; + ++it; + + int i = 0; + while( i < 10 ) + { + if ( it == m_history.fromLast() ) + { + popup->insertItem( (*it).url.url(), (*it).id ); + return; + } + + popup->insertItem( (*it).url.url(), (*it).id ); + ++i; + ++it; + } +} + +void KDevHTMLPart::slotPopupActivated( int id ) +{ + kdDebug(9000) << "id: " << id << endl; + + QValueList<DocumentationHistoryEntry>::Iterator it = m_history.begin(); + while( it != m_history.end() ) + { + kdDebug(9000) << "(*it).id: " << (*it).id << endl; + if ( (*it).id == id ) + { + m_Current = it; + m_restoring = true; + openURL( (*m_Current).url ); + m_restoring = false; + return; + } + ++it; + } +} + +void KDevHTMLPart::addHistoryEntry() +{ + QValueList<DocumentationHistoryEntry>::Iterator it = m_Current; + + // if We're not already the last entry, we truncate the list here before adding an entry + if ( it != m_history.end() && it != m_history.fromLast() ) + { + m_history.erase( ++it, m_history.end() ); + } + + DocumentationHistoryEntry newEntry( url() ); + + // Only save the new entry if it is different from the last + if ( newEntry.url != (*m_Current).url ) + { + m_history.append( newEntry ); + m_Current = m_history.fromLast(); + } +} + +void KDevHTMLPart::slotCopy( ) +{ + QString text = selectedText(); + text.replace( QChar( 0xa0 ), ' ' ); + QClipboard *cb = QApplication::clipboard(); + disconnect( cb, SIGNAL( selectionChanged() ), this, SLOT( slotClearSelection() ) ); + cb->setText(text); + connect( cb, SIGNAL( selectionChanged() ), this, SLOT( slotClearSelection() ) ); +} + +void KDevHTMLPart::slotSelectionChanged( ) +{ + if (selectedText().isEmpty()) + copyAction->setEnabled(false); + else + copyAction->setEnabled(true); +} + +#include "kdevhtmlpart.moc" diff --git a/lib/widgets/kdevhtmlpart.h b/lib/widgets/kdevhtmlpart.h new file mode 100644 index 00000000..944f1e9b --- /dev/null +++ b/lib/widgets/kdevhtmlpart.h @@ -0,0 +1,111 @@ +#ifndef __KDEVHTMLPART_H__ +#define __KDEVHTMLPART_H__ + +#include <stdlib.h> + +#include <qdatetime.h> + +#include <khtml_part.h> + +/** +@file kdevhtmlpart.h +Customized KHTML part for KDevelop. +*/ + +class KAction; +class KToolBarPopupAction; +class KParts::ReadOnlyPart; + +struct DocumentationHistoryEntry { + KURL url; + int id; + + DocumentationHistoryEntry() {} + DocumentationHistoryEntry( const KURL& u ): url( u ) + { + id = abs( QTime::currentTime().msecsTo( QTime() ) ); // nasty, but should provide a reasonably unique number + } +}; + +/** +Customized KHTML part for KDevelop. +Used as HTML documentation and file viewer. + +Represents customized BrowserViewGUI mode of KHTMLPart. Provides also actions for: +- reload; +- stop; +- duplicate; +- print; +- copy text; +- back; +- forward. +. +It has it's own popup menu and font/zoom settings. +*/ +class KDevHTMLPart : public KHTMLPart +{ + Q_OBJECT + +public: + + enum Options { CanDuplicate=1, CanOpenInNewWindow=2 }; + + KDevHTMLPart(); + + void setContext(const QString &context); + QString context() const; + virtual bool openURL(const KURL &url); + static QString resolveEnvVarsInURL(const QString& url); + + void setOptions(int options) { m_options = options; } + +signals: + void fileNameChanged(KParts::ReadOnlyPart *part); + +protected slots: + + void slotStarted(KIO::Job *); + void slotCompleted(); + void slotCancelled(const QString &errMsg); + + void openURLRequest(const KURL &url); + void popup( const QString & url, const QPoint & p ); + + void slotReload(); + void slotStop(); + virtual void slotDuplicate() = 0; + virtual void slotOpenInNewWindow(const KURL &url) = 0; + void slotPrint(); + void slotCopy(); + void slotSelectionChanged(); + + void slotBack(); + void slotForward(); + void slotBackAboutToShow(); + void slotForwardAboutToShow(); + + void slotPopupActivated( int id ); + void addHistoryEntry(); + + +private: + + QValueList< DocumentationHistoryEntry > m_history; + QValueList< DocumentationHistoryEntry >::Iterator m_Current; + + KToolBarPopupAction* m_backAction; + KToolBarPopupAction* m_forwardAction; + + bool m_restoring; + + QString m_context; + KAction *stopAction; + KAction *reloadAction; + KAction *duplicateAction; + KAction *printAction; + KAction *copyAction; + + int m_options; +}; + +#endif diff --git a/lib/widgets/kdevtabwidget.cpp b/lib/widgets/kdevtabwidget.cpp new file mode 100644 index 00000000..49925115 --- /dev/null +++ b/lib/widgets/kdevtabwidget.cpp @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (C) 2001-2003 * + * The KDevelop Team * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include <qpopupmenu.h> + +#include <kglobal.h> +#include <klocale.h> +#include <kicontheme.h> +#include <kiconloader.h> + +#include "kdevtabwidget.h" + +KDevTabWidget::KDevTabWidget(QWidget *parent, const char *name) : QTabWidget(parent,name) +{ + m_pTabBar = new KTabBar(this, "tabbar"); + setTabBar(m_pTabBar); + connect(m_pTabBar, SIGNAL(closeWindow(const QWidget*)), this, SIGNAL(closeWindow(const QWidget*))); + connect(m_pTabBar, SIGNAL(closeOthers(QWidget*)), this, SIGNAL(closeOthers(QWidget*))); +} + +KTabBar::KTabBar(QWidget *parent, const char *name) : QTabBar(parent,name) +{ + m_pPopupMenu = new QPopupMenu(this); + + QPixmap closePixmap = KGlobal::instance()->iconLoader()->loadIcon( "tab_remove", KIcon::Small, 0, KIcon::DefaultState, 0, true ); + if (closePixmap.isNull()) + closePixmap = SmallIcon("fileclose"); + + m_pPopupMenu->insertItem(closePixmap, i18n("&Close"), this, SLOT(closeWindowSlot())); + m_pPopupMenu->insertItem(i18n("Close &Others"), this, SLOT(closeOthersSlot())); +} + +void KTabBar::closeWindowSlot() +{ + emit closeWindow(m_pPage); +} + +void KTabBar::closeOthersSlot() +{ + emit closeOthers(m_pPage); +} + +void KTabBar::mousePressEvent(QMouseEvent *e) +{ + if(e->button() == Qt::RightButton) { + + QTab *tab = selectTab(e->pos() ); + if( tab == 0L ) return; + + m_pPage = ((KDevTabWidget*)parent())->page(indexOf(tab->identifier() ) ); + if(m_pPage == 0L) return; + + m_pPopupMenu->exec(mapToGlobal(e->pos())); + } + QTabBar::mousePressEvent(e); +} + +#include "kdevtabwidget.moc" diff --git a/lib/widgets/klistviewaction.cpp b/lib/widgets/klistviewaction.cpp new file mode 100644 index 00000000..f9e4e1b9 --- /dev/null +++ b/lib/widgets/klistviewaction.cpp @@ -0,0 +1,86 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Alexander Dymo <cloudtemple@mksat.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "klistviewaction.h" +#include "kcomboview.h" +#include "resizablecombo.h" + +#include <qtooltip.h> +#include <qwhatsthis.h> + +#include <kconfig.h> +#include <kglobal.h> + +KListViewAction::~KListViewAction() +{ + KConfig *config = KGlobal::config(); + if (config && m_view->name()) + { + config->setGroup("KListViewAction"); + config->writeEntry(m_view->name(), m_view->width()); + } + delete m_view; +} + +KListViewAction::KListViewAction(KComboView *view, const QString & text, const KShortcut & cut, + const QObject * receiver, const char * slot, KActionCollection * parent, const char * name ): + KWidgetAction(view, text, cut, receiver, slot, parent, name), m_view(view) +{ + m_view->setDuplicatesEnabled(false); + m_view->setInsertionPolicy(KComboView::NoInsertion); + + loadComboWidth(); +} + +KListViewAction::KListViewAction( KComboView * view, const QString & text, const KShortcut & cut, + const QObject * receiver, const char * slot, KActionCollection * parent, const char * name, const bool /*dummy*/ ): + KWidgetAction(new ResizableCombo(view), text, cut, receiver, slot, parent, name), m_view(view) +{ + m_view->setDuplicatesEnabled(false); + m_view->setInsertionPolicy(KComboView::NoInsertion); + + loadComboWidth(); +} + +KComboView * KListViewAction::view( ) const +{ + return m_view; +} + +void KListViewAction::setToolTip( const QString & str ) +{ + QToolTip::remove(m_view); + QToolTip::add(m_view, str); +} + +void KListViewAction::setWhatsThis( const QString & str ) +{ + QWhatsThis::remove(m_view); + QWhatsThis::add(m_view, str); +} + +void KListViewAction::loadComboWidth( ) +{ + KConfig *config = KGlobal::config(); + if (config && m_view->name()) + { + config->setGroup("KListViewAction"); + m_view->setMinimumWidth(config->readNumEntry(m_view->name(), m_view->defaultWidth())); + } +} + diff --git a/lib/widgets/klistviewaction.h b/lib/widgets/klistviewaction.h new file mode 100644 index 00000000..1dbacf09 --- /dev/null +++ b/lib/widgets/klistviewaction.h @@ -0,0 +1,56 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Alexander Dymo <cloudtemple@mksat.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KLISTVIEWACTION_H +#define KLISTVIEWACTION_H + +#include <kxmlguiclient.h> +#include <kdeversion.h> +#include <kaction.h> + +class KComboView; + +/** +@file klistviewaction.h +Widget action with KComboView. +*/ + + +/** +Widget action with KComboView. +Can be used on toolbars. It appears as @ref ResizableCombo. +*/ +class KListViewAction: public KWidgetAction +{ +public: + KListViewAction(KComboView *view, const QString &text, const KShortcut &cut, const QObject *receiver, const char *slot, KActionCollection *parent, const char *name); + KListViewAction(KComboView *view, const QString &text, const KShortcut &cut, const QObject *receiver, const char *slot, KActionCollection *parent, const char *name, const bool); + + ~KListViewAction(); + + KComboView *view() const; + void setToolTip(const QString & str); + void setWhatsThis(const QString &str); + +private: + void loadComboWidth(); + + KComboView *m_view; +}; + +#endif diff --git a/lib/widgets/ksavealldialog.cpp b/lib/widgets/ksavealldialog.cpp new file mode 100644 index 00000000..dec2a80c --- /dev/null +++ b/lib/widgets/ksavealldialog.cpp @@ -0,0 +1,189 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Harald Fernengel <harry@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <qvbox.h> +#include <qlabel.h> +#include <qheader.h> + +#include <klocale.h> +#include <kpushbutton.h> +#include <klistbox.h> +#include <klistview.h> +#include <kstdguiitem.h> + +#include "ksavealldialog.h" + +namespace +{ + +class CheckURL : public QCheckListItem +{ +public: + CheckURL( QListView * lv, KURL const & url ) + : QCheckListItem( lv, url.path(), QCheckListItem::CheckBox), + _url( url ) + {} + + KURL const & url() const { return _url; } + +private: + KURL _url; +}; + +} + + +KSaveSelectDialog::KSaveSelectDialog( KURL::List const & filelist, KURL::List const & ignorelist, QWidget * parent ) : + KDialogBase( parent, "SaveAllDialog", true, i18n("Save Modified Files?"), + Ok | User1 | Close ) +{ + QVBox *top = makeVBoxMainWidget(); + + (void)new QLabel( i18n("The following files have been modified. Save them?"), top ); + + _listview = new KListView( top ); + _listview->addColumn( "" ); + _listview->header()->hide(); + _listview->setResizeMode( QListView::LastColumn ); + + setButtonOK( KGuiItem(i18n("Save &Selected"), QString::null, i18n("Saves all selected files")) ); + setButtonText( User1, i18n("Save &None") ); + setButtonText( Close, KStdGuiItem::cancel().text() ); + setButtonTip( User1, i18n("Lose all modifications") ); + setButtonTip( Close, i18n("Cancels the action") ); + + KURL::List::ConstIterator it = filelist.begin(); + while ( it != filelist.end() ) + { + if ( !ignorelist.contains( *it ) ) + { + QCheckListItem * x = new CheckURL( _listview, *it ); + x->setOn( true ); + } + ++it; + } + + connect( this, SIGNAL(closeClicked()), this, SLOT(cancel()) ); + connect( this, SIGNAL(okClicked()), this, SLOT(save()) ); + connect( this, SIGNAL(user1Clicked()), this, SLOT(saveNone()) ); +} + +KSaveSelectDialog::~KSaveSelectDialog() {} + +void KSaveSelectDialog::saveNone( ) +{ + // deselect all + CheckURL * item = static_cast<CheckURL*>( _listview->firstChild() ); + while ( item ) + { + item->setOn( false ); + item = static_cast<CheckURL*>( item->nextSibling() ); + } + + QDialog::accept(); +} + +void KSaveSelectDialog::save( ) +{ + QDialog::accept(); +} + +void KSaveSelectDialog::cancel( ) +{ + QDialog::reject(); +} + +KURL::List KSaveSelectDialog::filesToSave( ) +{ + KURL::List filelist; + CheckURL const * item = static_cast<CheckURL*>( _listview->firstChild() ); + while ( item ) + { + if ( item->isOn() ) + { + filelist << item->url(); + } + item = static_cast<CheckURL*>( item->nextSibling() ); + } + return filelist; +} + +KURL::List KSaveSelectDialog::filesNotToSave( ) +{ + KURL::List filelist; + CheckURL const * item = static_cast<CheckURL*>( _listview->firstChild() ); + while ( item ) + { + if ( ! item->isOn() ) + { + filelist << item->url(); + } + item = static_cast<CheckURL*>( item->nextSibling() ); + } + return filelist; +} + + +KSaveAllDialog::KSaveAllDialog( const QStringList& filenames, QWidget* parent ) : + KDialogBase( parent, "SaveAllDialog", true, i18n("Save Modified Files?"), + Ok | User1 | Close ) +{ + m_result = Cancel; + + QVBox *top = makeVBoxMainWidget(); + + (void)new QLabel( i18n("The following files have been modified. Save them?"), top ); + KListBox* lb = new KListBox( top ); + lb->setMinimumHeight( lb->fontMetrics().height() * 5 ); + lb->insertStringList( filenames ); + + setButtonOK( KGuiItem(i18n("Save &All"), QString::null, i18n("Saves all modified files")) ); + setButtonText( User1, i18n("Save &None") ); + setButtonText( Close, KStdGuiItem::cancel().text() ); + setButtonTip( User1, i18n("Lose all modifications") ); + setButtonTip( Close, i18n("Cancels the action") ); + + connect( this, SIGNAL(closeClicked()), this, SLOT(cancel()) ); + connect( this, SIGNAL(okClicked()), this, SLOT(saveAll()) ); + connect( this, SIGNAL(user1Clicked()), this, SLOT(revert()) ); +} + +KSaveAllDialog::~KSaveAllDialog() +{ +} + +void KSaveAllDialog::revert() +{ + m_result = Revert; + QDialog::accept(); +} + +void KSaveAllDialog::saveAll() +{ + m_result = SaveAll; + QDialog::accept(); +} + +void KSaveAllDialog::cancel() +{ + m_result = Cancel; + QDialog::reject(); +} + +#include "ksavealldialog.moc" diff --git a/lib/widgets/ksavealldialog.h b/lib/widgets/ksavealldialog.h new file mode 100644 index 00000000..9d204bb7 --- /dev/null +++ b/lib/widgets/ksavealldialog.h @@ -0,0 +1,83 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Harald Fernengel <harry@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef SAVEALLDIALOG_H +#define SAVEALLDIALOG_H + +#include <qstringlist.h> +#include <kdialogbase.h> +#include <kurl.h> + +class KListView; +/** +@file ksavealldialog.h +Dialogs to save multiple files. +*/ + +/** +Dialog to save selected files. +*/ +class KSaveSelectDialog : public KDialogBase +{ + Q_OBJECT + +public: + KSaveSelectDialog( KURL::List const & filelist, KURL::List const & ignorelist, QWidget * parent ); + virtual ~KSaveSelectDialog(); + + KURL::List filesToSave(); + KURL::List filesNotToSave(); + +private slots: + void saveNone(); + void save(); + void cancel(); + +private: + KListView * _listview; + +}; + + +/** +Dialog to save all files. +*/ +class KSaveAllDialog : public KDialogBase +{ + Q_OBJECT + +public: + enum SaveAllResult{ SaveAll, Cancel, Revert }; + + KSaveAllDialog( const QStringList& filenames, QWidget* parent ); + virtual ~KSaveAllDialog(); + + SaveAllResult result() const { return m_result; } + +private slots: + void revert(); + void saveAll(); + void cancel(); + +private: + SaveAllResult m_result; + +}; + +#endif diff --git a/lib/widgets/processlinemaker.cpp b/lib/widgets/processlinemaker.cpp new file mode 100644 index 00000000..9cb19971 --- /dev/null +++ b/lib/widgets/processlinemaker.cpp @@ -0,0 +1,93 @@ +/* This file is part of the KDE project + Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "processlinemaker.h" +#include "processlinemaker.moc" + +#include <kprocess.h> + +ProcessLineMaker::ProcessLineMaker() +{ +} + +ProcessLineMaker::ProcessLineMaker( const KProcess* proc ) +{ + connect(proc, SIGNAL(receivedStdout(KProcess*,char*,int)), + this, SLOT(slotReceivedStdout(KProcess*,char*,int)) ); + + connect(proc, SIGNAL(receivedStderr(KProcess*,char*,int)), + this, SLOT(slotReceivedStderr(KProcess*,char*,int)) ); +} + +void ProcessLineMaker::slotReceivedStdout( const char *buffer ) +{ + stdoutbuf += buffer; + int pos; + while ( (pos = stdoutbuf.find('\n')) != -1) { + QCString line = stdoutbuf.left( pos ); + emit receivedStdoutLine(line); + stdoutbuf.remove(0, pos+1); + } + if( !stdoutbuf.isEmpty() ) { + emit receivedPartialStdoutLine( stdoutbuf ); + stdoutbuf.truncate(0); + } +} + +void ProcessLineMaker::slotReceivedStdout( KProcess*, char *buffer, int ) +{ + slotReceivedStdout(buffer); // It is zero-terminated +} + + +void ProcessLineMaker::slotReceivedStderr( const char *buffer ) +{ + stderrbuf += buffer; + int pos; + while ( (pos = stderrbuf.find('\n')) != -1) { + QCString line = stderrbuf.left( pos ); + emit receivedStderrLine(line); + stderrbuf.remove(0, pos+1); + } + if( !stderrbuf.isEmpty() ) { + emit receivedPartialStderrLine( stderrbuf ); + stderrbuf.truncate(0); + } +} + +void ProcessLineMaker::slotReceivedStderr( KProcess*, char *buffer, int ) +{ + slotReceivedStderr(buffer); // It is zero-terminated +} + +void ProcessLineMaker::clearBuffers( ) +{ + stderrbuf.truncate(0); + stdoutbuf.truncate(0); +} + +void ProcessLineMaker::flush() +{ + if( !stderrbuf.isEmpty() ) + emit receivedStderrLine(stderrbuf); + if( !stdoutbuf.isEmpty() ) + emit receivedStdoutLine(stdoutbuf); + clearBuffers(); +} + diff --git a/lib/widgets/processlinemaker.h b/lib/widgets/processlinemaker.h new file mode 100644 index 00000000..a529467f --- /dev/null +++ b/lib/widgets/processlinemaker.h @@ -0,0 +1,67 @@ +/* This file is part of the KDE project + Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _PROCESSLINEMAKER_H_ +#define _PROCESSLINEMAKER_H_ + +#include <qobject.h> + +/** +@file processlinemaker.h +Utility objects for process output views. +*/ + +class KProcess; + +/** +Convenience class to catch output of KProcess. +*/ +class ProcessLineMaker : public QObject +{ +Q_OBJECT + +public: + ProcessLineMaker(); + ProcessLineMaker( const KProcess* ); + + void clearBuffers(); + void flush(); + +public slots: + void slotReceivedStdout(const char*); + void slotReceivedStderr(const char*); + +protected slots: + void slotReceivedStdout(KProcess *, char *buffer, int buflen); + void slotReceivedStderr(KProcess *, char *buffer, int buflen); + +signals: + void receivedStdoutLine( const QCString& line ); + void receivedStderrLine( const QCString& line ); + void receivedPartialStdoutLine( const QCString& line ); + void receivedPartialStderrLine( const QCString& line ); + +private: + QCString stdoutbuf; + QCString stderrbuf; +}; + +#endif + +//kate: replace-tabs off; indent-spaces off; diff --git a/lib/widgets/processwidget.cpp b/lib/widgets/processwidget.cpp new file mode 100644 index 00000000..54dd1751 --- /dev/null +++ b/lib/widgets/processwidget.cpp @@ -0,0 +1,283 @@ +/* This file is part of the KDE project + Copyright (C) 1999-2001 Bernd Gehrmann <bernd@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "processwidget.h" +#include "processlinemaker.h" + +#include <kdeversion.h> +#include <qdir.h> +#include <kdebug.h> +#include <klocale.h> +#include <kprocess.h> +#include <qpainter.h> +#include <qapplication.h> + + +ProcessListBoxItem::ProcessListBoxItem(const QString &s, Type type) + : QListBoxText(s), t(type) +{ + QString clean = s; + clean.replace( QChar('\t'), QString(" ") ); + clean.replace( QChar('\n'), QString() ); + clean.replace( QChar('\r'), QString() ); + setText( clean ); + + setCustomHighlighting(true); +} + + +bool ProcessListBoxItem::isCustomItem() +{ + return false; +} + +static inline unsigned char normalize(int a) +{ + return (a < 0 ? 0 : a > 255 ? 255 : a); +} + +static inline double blend1(double a, double b, double k) +{ + return a + (b - a) * k; +} + +QColor ProcessListBoxItem::blend(const QColor &c1, const QColor &c2, double k) const +{ + if (k < 0.0) return c1; + if (k > 1.0) return c2; + + int r = normalize((int)blend1((double)c1.red(), (double)c2.red(), k)); + int g = normalize((int)blend1((double)c1.green(), (double)c2.green(), k)); + int b = normalize((int)blend1((double)c1.blue(), (double)c2.blue(), k)); + + return QColor(qRgb(r, g, b)); +} + +void ProcessListBoxItem::paint(QPainter *p) +{ + QColor dim, warn, err, back; + if (listBox()) { + const QColorGroup& group = listBox()->palette().active(); + if (isSelected()) { + back = group.button(); + warn = group.buttonText(); + } + else + { + back = group.base(); + warn = group.text(); + } + err = group.linkVisited(); + dim = blend(warn, back); + } + else + { + warn = Qt::black; + dim = Qt::darkBlue; + err = Qt::darkRed; + if (isSelected()) + back = Qt::lightGray; + else + back = Qt::white; + } + p->fillRect(p->window(), QBrush(back)); + p->setPen((t==Error)? err : + (t==Diagnostic)? warn : dim); + QListBoxText::paint(p); +} + + +ProcessWidget::ProcessWidget(QWidget *parent, const char *name) + : KListBox(parent, name) +{ + setFocusPolicy(QWidget::NoFocus); + + // Don't override the palette, as that can mess up styles. Instead, draw + // the background ourselves (see ProcessListBoxItem::paint). + + + childproc = new KProcess(); + childproc->setUseShell(true); + + procLineMaker = new ProcessLineMaker( childproc ); + + connect( procLineMaker, SIGNAL(receivedStdoutLine(const QCString&)), + this, SLOT(insertStdoutLine(const QCString&) )); + connect( procLineMaker, SIGNAL(receivedStderrLine(const QCString&)), + this, SLOT(insertStderrLine(const QCString&) )); + connect( procLineMaker, SIGNAL(receivedPartialStdoutLine(const QCString&)), + this, SLOT(addPartialStdoutLine(const QCString&) )); + connect( procLineMaker, SIGNAL(receivedPartialStderrLine(const QCString&)), + this, SLOT(addPartialStderrLine(const QCString&) )); + + connect(childproc, SIGNAL(processExited(KProcess*)), + this, SLOT(slotProcessExited(KProcess*) )) ; +} + + +ProcessWidget::~ProcessWidget() +{ + delete childproc; + delete procLineMaker; +} + + +void ProcessWidget::startJob(const QString &dir, const QString &command) +{ + procLineMaker->clearBuffers(); + procLineMaker->blockSignals( false ); + + clear(); + insertItem(new ProcessListBoxItem(command, ProcessListBoxItem::Diagnostic)); + childproc->clearArguments(); + if (!dir.isNull()) { + childproc->setWorkingDirectory( dir ); + } + + *childproc << command; + childproc->start(KProcess::OwnGroup, KProcess::AllOutput); +} + + +void ProcessWidget::killJob( int signo ) +{ + procLineMaker->blockSignals( true ); + + childproc->kill( signo ); +} + + +bool ProcessWidget::isRunning() +{ + return childproc->isRunning(); +} + + +void ProcessWidget::slotProcessExited(KProcess *) +{ + procLineMaker->flush(); + if( !stdoutbuf.isEmpty() ) + insertStdoutLine(""); + if( !stderrbuf.isEmpty() ) + insertStderrLine(""); + childFinished(childproc->normalExit(), childproc->exitStatus()); + maybeScrollToBottom(); + emit processExited(childproc); +} + + +void ProcessWidget::insertStdoutLine(const QCString &line) +{ + if( !stdoutbuf.isEmpty() ) + { + stdoutbuf += line; + insertItem( new ProcessListBoxItem( QString::fromLocal8Bit(stdoutbuf), + ProcessListBoxItem::Normal ), + lastRowStdout+1 ); + stdoutbuf.truncate( 0 ); + }else + { + insertItem( new ProcessListBoxItem( QString::fromLocal8Bit( line ), + ProcessListBoxItem::Normal) ); + } + lastRowStdout = count() - 1; + maybeScrollToBottom(); +} + + +void ProcessWidget::insertStderrLine(const QCString &line) +{ + if( !stderrbuf.isEmpty() ) + { + stderrbuf += line; + insertItem( new ProcessListBoxItem( QString::fromLocal8Bit( stderrbuf ), + ProcessListBoxItem::Error ), + lastRowStderr+1 ); + stderrbuf.truncate( 0 ); + } else + { + insertItem( new ProcessListBoxItem( QString::fromLocal8Bit( line ), + ProcessListBoxItem::Error) ); + } + lastRowStderr = count() - 1; + maybeScrollToBottom(); +} + + +void ProcessWidget::childFinished(bool normal, int status) +{ + QString s; + ProcessListBoxItem::Type t; + + if (normal) { + if (status) { + s = i18n("*** Exited with status: %1 ***").arg(status); + t = ProcessListBoxItem::Error; + } else { + s = i18n("*** Exited normally ***"); + t = ProcessListBoxItem::Diagnostic; + } + } else { + if ( childproc->signalled() && childproc->exitSignal() == SIGSEGV ) + { + s = i18n("*** Process aborted. Segmentation fault ***"); + } + else + { + s = i18n("*** Process aborted ***"); + } + t = ProcessListBoxItem::Error; + } + + insertItem(new ProcessListBoxItem(s, t)); +} + + +QSize ProcessWidget::minimumSizeHint() const +{ + // I'm not sure about this, but when I don't use override minimumSizeHint(), + // the initial size in clearly too small + + return QSize( QListBox::sizeHint().width(), + (fontMetrics().lineSpacing()+2)*4 ); +} + +/** Should be called right after an insertItem(), + will automatic scroll the listbox if it is already at the bottom + to prevent automatic scrolling when the user has scrolled up +*/ +void ProcessWidget::maybeScrollToBottom() +{ + if ( verticalScrollBar()->value() == verticalScrollBar()->maxValue() ) + { + setBottomItem( count() -1 ); + } +} + +void ProcessWidget::addPartialStderrLine(const QCString& linepart) +{ + stderrbuf += linepart; +} + +void ProcessWidget::addPartialStdoutLine(const QCString& linepart) +{ + stdoutbuf += linepart; +} + +#include "processwidget.moc" diff --git a/lib/widgets/processwidget.h b/lib/widgets/processwidget.h new file mode 100644 index 00000000..ac9693e2 --- /dev/null +++ b/lib/widgets/processwidget.h @@ -0,0 +1,127 @@ +/* This file is part of the KDE project + Copyright (C) 1999-2001 Bernd Gehrmann <bernd@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _PROCESSWIDGET_H_ +#define _PROCESSWIDGET_H_ + +#include <klistbox.h> +#include <kprocess.h> + +/** +@file processwidget.h +Widgets for various output views. +*/ + +class ProcessLineMaker; + +/** +Listbox item for process widgets. +*/ +class ProcessListBoxItem : public QListBoxText +{ +public: + enum Type { Diagnostic, Normal, Error }; + + ProcessListBoxItem(const QString &s, Type type); + + virtual bool isCustomItem(); + +protected: + QColor blend(const QColor &c1, const QColor &c2, double k = 0.25) const; + +private: + virtual void paint(QPainter *p); + Type t; +}; + + +/** + * This class is designed to share code between the + * grep and application output widgets. + */ +class ProcessWidget : public KListBox +{ + Q_OBJECT + +public: + ProcessWidget(QWidget *parent, const char *name=0); + ~ProcessWidget(); + + /** + * Returns whether a process is running in this view. + */ + bool isRunning(); + +public slots: + /** + * Starts the child process. + */ + void startJob(const QString &dir, const QString &command); + /** + * Kills the child processss. + */ + void killJob( int signo = SIGTERM ); + /** + * Inserts one line from stdin into the listbox. This can + * be overridden by subclasses to implement + * syntax highlighting. + */ + virtual void insertStdoutLine(const QCString &line); + /** + * Inserts one line from stderr into the listbox. This can + * be overridden by subclasses to implement + * syntax highlighting. By default, a ProcessListBoxItem + * is used. + */ + virtual void insertStderrLine(const QCString &line); + + virtual void addPartialStdoutLine(const QCString&); + virtual void addPartialStderrLine(const QCString&); + +protected: + /** + * This is called when the child process exits. + * The flag 'normal' is true if the process exited + * normally (i.e. not by a signal or similar), otherwise + * the exit status can be taken from 'status'. + */ + virtual void childFinished(bool normal, int status); + +signals: + void processExited(KProcess *); + void rowSelected(int row); + +protected: + virtual QSize minimumSizeHint() const; + void maybeScrollToBottom(); + +protected slots: + void slotProcessExited(KProcess*); + +private: + KProcess *childproc; + ProcessLineMaker* procLineMaker; + QCString stderrbuf; + QCString stdoutbuf; + int lastRowStdout; + int lastRowStderr; +}; + + +#endif diff --git a/lib/widgets/propeditor/Mainpage.dox b/lib/widgets/propeditor/Mainpage.dox new file mode 100644 index 00000000..6bc81be0 --- /dev/null +++ b/lib/widgets/propeditor/Mainpage.dox @@ -0,0 +1,116 @@ +/** +@mainpage The KDevelop Property Editing Library + +<b>Link with</b>: -lkdevelop + +<b>Include path</b>: -I\$(kde_includes)/kdevelop/propeditor + + +\section whatis What is Property Editor? + +%Property editor is a collection of facilities to store and edit the +properties of an object. For example, look at %Qt Designer. Each widget +has a list of properties that can be edited in a nice table form. +Same ideology is used to edit properties in Kugar Report Designer +(from KOffice distribution). In KDevelop project manager can also display +the properties of currently selected build item in property editor. + +\section over Library Overview + +This PropertyEditor library is a redesign of Kugar property editing library +with the goal to be more generic and extensible. + +Library provides a @ref PropertyLib::Property class which stores property name, value and some +more important information like description or the list of possible values. +Using @ref PropertyLib::Property class adds more overhead over Q_PROPERTY but provides more +flexibility. You can subclass @ref PropertyLib::Property and create your custom properties. +Custom properties can have either predefined type (see @ref PropertyLib::Property::PropertyType) or +custom type. Custom type should be used if a custom property editor widget is +necessary. + +Properties are organized into lists. @ref PropertyLib::PropertyList is designed +to store such lists in most efficient manner. It also allows to group +properties (for example think about "geometrical" properties like "x", "y", etc.). + +Property lists can be displayed in @ref PropertyLib::PropertyEditor widget which will +display them in a table form. @ref PropertyLib::PropertyEditor takes a pointer to a @ref PropertyLib::PropertyList object so @ref PropertyLib::PropertyBuffer can be used too. + +@ref PropertyLib::PropertyBuffer is designed to provide a method to access an intersection +of property lists. For example, let's consider object A with property list a_list +abd object B with list b_list. Now let's imagine we want to display common properties +from a_list and b_list in one @ref PropertyLib::PropertyEditor widget. Obviously, we need to +"intersect" a_list with b_list and display the result of intersection. +This is why @ref PropertyLib::PropertyBuffer is used for editing. If we change +the value of a property in the editor, @ref PropertyLib::PropertyBuffer will update +both properties from underlying a_list and b_list. + +@ref PropertyLib::PropertyEditor at the same time shows only one editor for selected +property in the list. Each @ref PropertyLib::Property::PropertyType has corresponding @ref PropertyLib::PropertyWidget +which displays property editor or draws a property in the list if it is not edited. +More exactly, if @ref PropertyLib::PropertyEditor needs to display editor widget, it displays +@ref PropertyLib::PropertyWidget, else it calls @ref PropertyLib::PropertyWidget::drawViewer function. +Custom property widgets should be subclasses of @ref PropertyLib::PropertyWidget. + +To create property widgets at runtime, a factory is used. Factory class is +called @ref PropertyLib::PropertyMachineFactory. Static function @ref PropertyLib::PropertyMachineFactory::getInstance +can be used to obtain the reference to the factory instance. Factory creates and returns +so-called @ref PropertyLib::Machine for each registered property type (either predefined or user defined). + +@ref PropertyLib::Machine contains @ref PropertyLib::PropertyWidget and a list of "detailed" machines. +Usually only property widget is necessary for a property but there are +complex properties like "Font" for example. We would like to see separate editors +for font family, size, etc. and a button to choose all of these in the dialog. +For that "Font" property, a PropertyWidget with a "choose font" button +and also number of detailed widgets like "font family" combo, etc. can be created. + +\section Examples +A simple example on how to create a property editor and use it with one property list: +\code + using namespace PropertyLib; + + PropertyEditor *m_editor = new PropertyEditor(this); + + PropertyList *list = new PropertyList; + list->addProperty("My Group", new Property(Property::Integer, "First Property", + "This is my first property", -5)); + list->addProperty("My Group", new Property(Property::String, "Second Property", + "This is my second property", "Hello")); + list->addProperty(new Property(Property::Color, "Third Property", + "This is my third property", QColor("green"))); + + m_editor->populateProperties(*list); +\endcode +\image html propeditor1.png "Simple property editor" + +More advanced example with property buffers and list intersection: +\code + using namespace PropertyLib; + + PropertyEditor *m_editor = new PropertyEditor(this); + + PropertyList *list = new PropertyList; + list->addProperty("My Group", new Property(Property::Integer, "First Property", + "This is my first property", -5)); + list->addProperty("My Group", new Property(Property::String, "Second Property", + "This is my second property", "Hello")); + list->addProperty(new Property(Property::Color, "Third Property", + "This is my third property", QColor("green"))); + + PropertyList *list2 = new PropertyList; + list2->addProperty("My Group", new Property(Property::Integer, "First Property", + "This is my first property", -7)); + list2->addProperty("My Group", new Property(Property::String, "Second Property", + "This is my second property", "Hello")); + list2->addProperty(new Property(Property::String, "Third Property", + "This is my third property", "green")); + + PropertyBuffer buf(list); + buf.intersect(list2); + + m_editor->populateProperties(&buf); +\endcode +\image html propeditor2.png "Result of property list intersection" +In this example only properties named "First Property" and "Second Property" will be shown in editor. +"Third Property" has different types in list and list2 and will not be included in intersection. + +*/ diff --git a/lib/widgets/propeditor/Makefile.am b/lib/widgets/propeditor/Makefile.am new file mode 100644 index 00000000..bd055733 --- /dev/null +++ b/lib/widgets/propeditor/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = -I$(top_srcdir)/lib/compat $(all_includes) +METASOURCES = AUTO + +lib_LTLIBRARIES = libkdevpropertyeditor.la +libkdevpropertyeditor_la_LIBADD = $(LIB_KIO) +libkdevpropertyeditor_la_LDFLAGS = -no-undefined $(all_libraries) +libkdevpropertyeditor_la_SOURCES = childproperty.cpp pcombobox.cpp pdummywidget.cpp ppointedit.cpp propertymachinefactory.cpp pstringlistedit.cpp\ +multiproperty.cpp pcursoredit.cpp pfontbutton.cpp prectedit.cpp propertywidget.cpp psymbolcombo.cpp\ +pcheckbox.cpp pdateedit.cpp pfontcombo.cpp property.cpp psizeedit.cpp purledit.cpp\ +pcolorbutton.cpp pdatetimeedit.cpp plineedit.cpp propertyeditor.cpp psizepolicyedit.cpp pyesnobutton.cpp\ +pcolorcombo.cpp pdoublenuminput.cpp ppixmapedit.cpp propertylist.cpp pspinbox.cpp propertywidgetproxy.cpp plinestyleedit.cpp qeditlistbox.cpp + +kdevpropeditorincludedir = $(includedir)/kdevelop/propeditor +kdevpropeditorinclude_HEADERS = childproperty.h pcombobox.h pdummywidget.h ppointedit.h propertymachinefactory.h pcursoredit.h pfontbutton.h prectedit.h propertywidget.h pdateedit.h pfontcombo.h property.h psizeedit.h pdatetimeedit.h plineedit.h propertyeditor.h psizepolicyedit.h pdoublenuminput.h ppixmapedit.h propertylist.h pspinbox.h propertywidgetproxy.h multiproperty.h pyesnobutton.h purledit.h psymbolcombo.h pstringlistedit.h pcolorcombo.h pcolorbutton.h pcheckbox.h plinestyleedit.h + +DOXYGEN_REFERENCES = dcop interfaces kdecore kdefx kdeui khtml kmdi kio kjs kparts kutils +DOXYGEN_PROJECTNAME = KDevelop Property Editing Library +include ../../../Doxyfile.am diff --git a/lib/widgets/propeditor/childproperty.cpp b/lib/widgets/propeditor/childproperty.cpp new file mode 100644 index 00000000..72cfb053 --- /dev/null +++ b/lib/widgets/propeditor/childproperty.cpp @@ -0,0 +1,145 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "childproperty.h" + +#include <qsize.h> +#include <qpoint.h> +#include <qrect.h> +#include <qsizepolicy.h> + +#include "multiproperty.h" + +namespace PropertyLib{ + +ChildProperty::ChildProperty(MultiProperty *parent, int type, ChildPropertyType childType, const QString &name, + const QString &description, const QVariant &value, bool save, bool readOnly) + :Property(type, name, description, value, save, readOnly), m_parent(parent), m_childType(childType) +{ +} + +ChildProperty::ChildProperty(MultiProperty *parent, const QString & name, ChildPropertyType childType, + const QMap<QString, QVariant> &v_valueList, const QString &description, + const QVariant &value, bool save, bool readOnly) + :Property(name, v_valueList, description, value, save, readOnly), m_parent(parent), m_childType(childType) +{ +} + +void ChildProperty::setValue(const QVariant &value, bool // rememberOldValue + ) +{ + qWarning("ChildProperty::setValue"); + if (!m_parent->valid()) + return; + switch (m_parent->type()) + { + case Size: + { + qWarning("ChildProperty::setValue for QSize"); + QSize v = m_parent->value().toSize(); + if (m_childType == Size_Height) + v.setHeight(value.toInt()); + else if (m_childType == Size_Width) + v.setWidth(value.toInt()); + m_parent->setValue(v); + break; + } + case Point: + { + qWarning("ChildProperty::setValue for QPoint"); + QPoint v = m_parent->value().toPoint(); + if (m_childType == Point_X) + v.setX(value.toInt()); + else if (m_childType == Point_Y) + v.setY(value.toInt()); + m_parent->setValue(v); + break; + } + case Rect: + { + qWarning("ChildProperty::setValue for QRect"); + QRect v = m_parent->value().toRect(); + if (m_childType == Rect_X) + v.setX(value.toInt()); + else if (m_childType == Rect_Y) + v.setY(value.toInt()); + else if (m_childType == Rect_Width) + v.setWidth(value.toInt()); + else if (m_childType == Rect_Height) + v.setHeight(value.toInt()); + m_parent->setValue(v); + break; + } + case SizePolicy: + { + qWarning("ChildProperty::setValue for QSizePolicy"); + QSizePolicy v = m_parent->value().toSizePolicy(); + if (m_childType == SizePolicy_HorData) + v.setHorData(QSizePolicy::SizeType(value.toInt())); + else if (m_childType == SizePolicy_VerData) + v.setVerData(QSizePolicy::SizeType(value.toInt())); + else if (m_childType == SizePolicy_HorStretch) + v.setHorStretch(value.toInt()); + else if (m_childType == SizePolicy_VerStretch) + v.setVerStretch(value.toInt()); + m_parent->setValue(v); + break; + } + } +} + +QVariant ChildProperty::value( ) const +{ + if (!m_parent->valid()) + return QVariant(); + switch (m_parent->type()) + { + case Size: + if (m_childType == Size_Height) + return m_parent->value().toSize().height(); + else if (m_childType == Size_Width) + return m_parent->value().toSize().width(); + case Point: + if (m_childType == Point_X) + return m_parent->value().toPoint().x(); + else if (m_childType == Point_Y) + return m_parent->value().toPoint().y(); + case Rect: + if (m_childType == Rect_X) + return m_parent->value().toRect().x(); + else if (m_childType == Rect_Y) + return m_parent->value().toRect().y(); + else if (m_childType == Rect_Width) + return m_parent->value().toRect().width(); + else if (m_childType == Rect_Height) + return m_parent->value().toRect().height(); + case SizePolicy: + if (m_childType == SizePolicy_HorData) + return m_parent->value().toSizePolicy().horData(); + else if (m_childType == SizePolicy_VerData) + return m_parent->value().toSizePolicy().verData(); + else if (m_childType == SizePolicy_HorStretch) + return m_parent->value().toSizePolicy().horStretch(); + else if (m_childType == SizePolicy_VerStretch) + return m_parent->value().toSizePolicy().verStretch(); + } + return QVariant(); +} + +} diff --git a/lib/widgets/propeditor/childproperty.h b/lib/widgets/propeditor/childproperty.h new file mode 100644 index 00000000..13399a08 --- /dev/null +++ b/lib/widgets/propeditor/childproperty.h @@ -0,0 +1,83 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CHILDPROPERTY_H +#define CHILDPROPERTY_H + +#include "property.h" + +namespace PropertyLib{ + +class MultiProperty; + +/** +@short Child property + +Child property is a detailed property for complex parent properties. +For example, to edit a property of Point type one can request two +child properties for "x" and "y" components of a point. + +Child properties instances usually belong to MultiProperty class +which is used to fetch and display them in the property editor. +Child properties are created by a PropertyMachineFactory. +Factory in such case does not only creates a machine for a property, +but also fills corrresponding multiproperty with a list of child +properties. +*/ +class ChildProperty: public Property{ +public: + enum ChildPropertyType { + Size_Height, + Size_Width, + Point_X, + Point_Y, + Rect_X, + Rect_Y, + Rect_Width, + Rect_Height, + SizePolicy_HorData, + SizePolicy_VerData, + SizePolicy_HorStretch, + SizePolicy_VerStretch + }; + + /**Constructs empty property.*/ + ChildProperty() {} + /**Constructs property.*/ + ChildProperty(MultiProperty *parent, int type, ChildPropertyType childType, const QString &name, + const QString &description, const QVariant &value = QVariant(), + bool save = true, bool readOnly = false); + /**Constructs property with @ref ValueFromList type.*/ + ChildProperty(MultiProperty *parent, const QString &name, ChildPropertyType childType, + const QMap<QString, QVariant> &v_valueList, const QString &description, + const QVariant &value = QVariant(), bool save = true, bool readOnly = false); + + /**@return the value of the property.*/ + virtual QVariant value() const; + /**Sets the value of the property.*/ + virtual void setValue(const QVariant &value, bool rememberOldValue = true); + +private: + MultiProperty *m_parent; + ChildPropertyType m_childType; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/compat_tools.h b/lib/widgets/propeditor/compat_tools.h new file mode 100644 index 00000000..1a1a8735 --- /dev/null +++ b/lib/widgets/propeditor/compat_tools.h @@ -0,0 +1,6 @@ +#ifndef COMPAT_TOOLS_H +#define COMPAT_TOOLS_H + +#define i18n QObject::tr + +#endif diff --git a/lib/widgets/propeditor/multiproperty.cpp b/lib/widgets/propeditor/multiproperty.cpp new file mode 100644 index 00000000..7e32345a --- /dev/null +++ b/lib/widgets/propeditor/multiproperty.cpp @@ -0,0 +1,273 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo <cloudtemple@mskat.net> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "multiproperty.h" + +#include "propertylist.h" + +namespace PropertyLib{ + +MultiProperty::MultiProperty(Property *prop) + :m_propertyList(0) +{ + list.append(prop); +} + +MultiProperty::MultiProperty(PropertyList *propertyList) + :m_propertyList(propertyList) +{ +} + +MultiProperty::MultiProperty(PropertyList *propertyList, Property *prop) + :m_propertyList(propertyList) +{ + list.append(prop); +} + +MultiProperty::~MultiProperty() +{ +} + +QString MultiProperty::name() const +{ + if (list.count() >= 1) + return list.getFirst()->name(); + return QString::null; +} + +int MultiProperty::type() const +{ + if (list.count() >= 1) + return list.getFirst()->type(); + return QVariant::Invalid; +} + +QVariant MultiProperty::value() const +{ + QVariant value; + if (list.count() >= 1) + value = list.getFirst()->value(); + + QPtrListIterator<Property> it(list); + Property *property; + while ((property = it.current()) != 0) + { + if (property->value() != value) + return QVariant::Invalid; + ++it; + } + + return value; +} + +QString MultiProperty::description() const +{ + QString description; + if (list.count() >= 1) + description = list.getFirst()->description(); + + QPtrListIterator<Property> it(list); + Property *property; + while ((property = it.current()) != 0) + { + if (property->description() != description) + return QString::null; + ++it; + } + + return description; +} + +bool MultiProperty::readOnly() const +{ + bool v = true; + if (list.count() >= 1) + v = list.getFirst()->readOnly(); + + QPtrListIterator<Property> it(list); + Property *property; + while ((property = it.current()) != 0) + { + if (property->readOnly() != v) + return false; + ++it; + } + + return v; +} + +bool MultiProperty::visible() const +{ + bool v = true; + if (list.count() >= 1) + v = list.getFirst()->readOnly(); + + QPtrListIterator<Property> it(list); + Property *property; + while ((property = it.current()) != 0) + { + if (property->visible() != v) + return false; + ++it; + } + + return v; +} + +QMap<QString, QVariant> MultiProperty::valueList() const +{ + if (list.count() >= 1) + return list.getFirst()->valueList; + return QMap<QString, QVariant>(); +} + +void MultiProperty::setDescription(const QString &description) +{ + Property *property; + for (property = list.first(); property; property = list.next()) + property->setDescription(description); +} + +/*void MultiProperty::setName(const QString &name) +{ +} + +void MultiProperty::setType(int type) +{ +} +*/ +void MultiProperty::setValue(const QVariant &value) +{ + Property *property; + for (property = list.first(); property; property = list.next()) + { + property->setValue(value); + if (m_propertyList) + { +// qWarning("emit change"); + emit m_propertyList->propertyValueChanged(property); + } + } +} + +void MultiProperty::setValue(const QVariant &value, bool emitChange) +{ + Property *property; + for (property = list.first(); property; property = list.next()) + { + property->setValue(value); + if (emitChange && m_propertyList) + emit m_propertyList->propertyValueChanged(property); + } +} + +void MultiProperty::setValueList(const QMap<QString, QVariant> &valueList) +{ + Property *property; + for (property = list.first(); property; property = list.next()) + property->setValueList(valueList); +} + +void MultiProperty::addProperty(Property *prop) +{ + list.append(prop); +} + +void MultiProperty::removeProperty(Property *prop) +{ +/* qWarning("op >> removing %s", prop->name().ascii()); + qWarning("op >> list is %d", list.count());*/ + /*bool b = */list.remove(prop); +/* qWarning("op >> list is %d", list.count()); + qWarning("op >> removal is %s", b?"true":"false"); */ +} + +bool MultiProperty::operator ==(const MultiProperty &prop) const +{ + if ( (type() == prop.type()) && (name() == prop.name()) ) + return true; + return false; +} + +bool MultiProperty::operator ==(const Property &prop) const +{ +/* qWarning("MultiProperty::operator == for %s = %s", name().ascii(), prop.name().ascii()); + qWarning("MultiProperty::operator == for %d = %d", type(), prop.type());*/ + if ( (type() == prop.type()) && (name() == prop.name()) ) + return true; + return false; +} + +void MultiProperty::addProperty( MultiProperty *prop) +{ + Property *property; + for (property = prop->list.first(); property; property = prop->list.next()) + addProperty(property); +} + +void MultiProperty::removeProperty( MultiProperty *prop) +{ + Property *property; + for (property = prop->list.first(); property; property = prop->list.next()) + removeProperty(property); +} + +QVariant MultiProperty::findValueDescription() const +{ + QVariant val = value(); + if (type() != Property::ValueFromList) + return val; + QMap<QString, QVariant> vl = valueList(); + for (QMap<QString, QVariant>::const_iterator it = vl.begin(); it != vl.end(); ++ it) + { + if (it.data() == val) + return it.key(); + } + return ""; +} + +QVariant MultiProperty::findValueDescription(QVariant val) const +{ + if (type() != Property::ValueFromList) + return val; + QMap<QString, QVariant> vl = valueList(); + for (QMap<QString, QVariant>::const_iterator it = vl.begin(); it != vl.end(); ++ it) + { + if (it.data() == val) + return it.key(); + } + return ""; +} + +bool MultiProperty::valid() const +{ + return list.count() != 0; +} + +void MultiProperty::undo() +{ + Property *property; + for (property = list.first(); property; property = list.next()) + { + property->setValue(property->oldValue(), false); + if (m_propertyList) + emit m_propertyList->propertyValueChanged(property); + } +} + +} diff --git a/lib/widgets/propeditor/multiproperty.h b/lib/widgets/propeditor/multiproperty.h new file mode 100644 index 00000000..e6402027 --- /dev/null +++ b/lib/widgets/propeditor/multiproperty.h @@ -0,0 +1,126 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo <cloudtemple@mskat.net> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef MULTIPROPERTY_H +#define MULTIPROPERTY_H + +#include <qptrlist.h> +#include <qmap.h> +#include <qvariant.h> + +#include "property.h" +#include "childproperty.h" + +namespace PropertyLib{ + +class PropertyList; + +/** @file multiproperty.h +@short Contains @ref PropertyLib::MultiProperty class. +*/ + +/** +@short Holds a list of properties with the same name and type. + +It is used by @ref PropertyLib::PropertyList to store properties. Generally, @ref PropertyLib::PropertyList +stores a list of multiproperties instead of properties. This allows working +with many properties of the same name and type at the same type. + +MultiProperty is also responsible for storing detailed %property editors (instances +of @ref ChildProperty class. It's too much overhead to store child properties +with their parent properties. MultiProperty provides a way to store child properties +only once for all properties with the same name and same type. +*/ +class MultiProperty +{ +public: + /**Constructs multiproperty with one property which is not connected to a property list.*/ + MultiProperty(Property *prop); + /**Constructs empty multiproperty.*/ + MultiProperty(PropertyList *propertyList); + /**Constructs multiproperty with one Property in the list.*/ + MultiProperty(PropertyList *propertyList, Property *prop); + ~MultiProperty(); + + /**Compares two multiproperties.*/ + bool operator ==(const MultiProperty &prop) const; + /**Compares %multiproperty with %property.*/ + bool operator ==(const Property &prop) const; + + /**Adds %property to the list.*/ + void addProperty(Property *prop); + /**Removes %property from the list.*/ + void removeProperty(Property *prop); + /**Adds all properties from the %multiproperty prop.*/ + void addProperty(MultiProperty *prop); + /**Removes all properties that exists in the %multiproperty prop.*/ + void removeProperty(MultiProperty *prop); + + /**Returns the name of a %property.*/ + QString name() const; + /**Returns the type of a %property.*/ + int type() const; + /**Returns the value of a %property.*/ + QVariant value() const; + /**Returns the description of a %property.*/ + QString description() const; + /**Returns the readonly attribute of a %property.*/ + bool readOnly() const; + /**Returns the visibility attribute of a %property.*/ + bool visible() const; + /**The string-to-value correspondence list of the %property.*/ + QMap<QString, QVariant> valueList() const; + + /**Sets the value of a %property.*/ + void setValue(const QVariant& value); + /**Sets the value of a %property. + @param value new value of thus %multiproperty + @param emitChange if set to true then %property list which owns this %multiproperty + emits propertyValueChanged signal.*/ + void setValue(const QVariant& value, bool emitChange); + /**Sets the description of a %property.*/ + void setDescription(const QString &description); + /**Sets the list of possible values of a %property.*/ + void setValueList(const QMap< QString, QVariant >& valueList); + + /**Finds string description for a value.*/ + QVariant findValueDescription() const; + /**Finds string description for a value.*/ + QVariant findValueDescription(QVariant val) const; + + /**Returns true if the %multiproperty has no properties in the list (i.e. it's invalid).*/ + bool valid() const; + + /**Reverts the property value to previous setting.*/ + void undo(); + + /**The list of child properties.*/ + QValueList<ChildProperty> details; + +private: + QPtrList<Property> list; + + PropertyList *m_propertyList; + +friend class PropertyList; +friend class PropertyBuffer; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/pcheckbox.cpp b/lib/widgets/propeditor/pcheckbox.cpp new file mode 100644 index 00000000..7b0b187e --- /dev/null +++ b/lib/widgets/propeditor/pcheckbox.cpp @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "pcheckbox.h" + +#include <qlayout.h> +#include <qcheckbox.h> +#include <qpainter.h> + +#ifndef PURE_QT +#include <klocale.h> +#else +#include "compat_tools.h" +#endif + +namespace PropertyLib{ + +PCheckBox::PCheckBox(MultiProperty *property, QWidget *parent, const char *name) + :PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); + m_edit = new QCheckBox(this); + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + l->addWidget(m_edit); + + connect(m_edit, SIGNAL(toggled(bool)), this, SLOT(updateProperty(bool))); +} + +QVariant PCheckBox::value() const +{ + return QVariant(m_edit->isChecked()); +} + +void PCheckBox::setValue(const QVariant &value, bool emitChange) +{ + disconnect(m_edit, SIGNAL(toggled(bool)), this, SLOT(updateProperty(bool))); + m_edit->setChecked(value.toBool()); + connect(m_edit, SIGNAL(toggled(bool)), this, SLOT(updateProperty(bool))); + if (emitChange) + emit propertyChanged(m_property, value); +} + +void PCheckBox::updateProperty(bool val) +{ + emit propertyChanged(m_property, QVariant(val)); +} + +void PCheckBox::drawViewer(QPainter *p, const QColorGroup &cg, const QRect &r, const QVariant &value) +{ + p->setBrush(cg.background()); + p->setPen(Qt::NoPen); + p->drawRect(r); + p->drawText(r, Qt::AlignLeft | Qt::AlignVCenter | Qt::SingleLine, value.toBool() ? i18n("true") : i18n("false")); +} + +} + +#ifndef PURE_QT +#include "pcheckbox.moc" +#endif diff --git a/lib/widgets/propeditor/pcheckbox.h b/lib/widgets/propeditor/pcheckbox.h new file mode 100644 index 00000000..374617f7 --- /dev/null +++ b/lib/widgets/propeditor/pcheckbox.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PCHECKBOX_H +#define PCHECKBOX_H + +#include "propertywidget.h" + +class QCheckBox; + +namespace PropertyLib{ + +/** +@short %Property editor with checkbox. +*/ +class PCheckBox: public PropertyWidget{ + Q_OBJECT +public: + PCheckBox(MultiProperty *property, QWidget *parent = 0, const char *name = 0); + + /**@return the value currently entered in the editor widget.*/ + virtual QVariant value() const; + /**Sets the value shown in the editor widget. Set emitChange to false + if you don't want to emit propertyChanged signal.*/ + virtual void setValue(const QVariant &value, bool emitChange=true); + /**Function to draw a property viewer when the editor isn't shown.*/ + virtual void drawViewer(QPainter *p, const QColorGroup &cg, const QRect &r, const QVariant &value); + +private slots: + void updateProperty(bool val); + +private: + QCheckBox *m_edit; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/pcolorbutton.cpp b/lib/widgets/propeditor/pcolorbutton.cpp new file mode 100644 index 00000000..7404e1de --- /dev/null +++ b/lib/widgets/propeditor/pcolorbutton.cpp @@ -0,0 +1,120 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "pcolorbutton.h" + +#include <qlayout.h> +#include <qpainter.h> + +#ifndef PURE_QT +#include <kcolorbutton.h> +#else +#include <qpushbutton.h> +#include <qpixmap.h> +#include <qiconset.h> +#endif +#include <qcolordialog.h> + +namespace PropertyLib { + +PColorButton::PColorButton(MultiProperty* property, QWidget* parent, const char* name) + :PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); +#ifndef PURE_QT + m_edit = new KColorButton(this); + connect(m_edit, SIGNAL(changed(const QColor&)), this, SLOT(updateProperty(const QColor&))); +#else + m_edit = new QPushButton(this); + connect(m_edit, SIGNAL(clicked()), this, SLOT(changeColor())); +#endif + + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + l->addWidget(m_edit); +} + +QVariant PColorButton::value() const +{ +#ifndef PURE_QT + return QVariant(m_edit->color()); +#else + return QVariant(m_color); +#endif +} + +void PColorButton::drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value) +{ +/* p->setBrush(value.toColor()); + p->setPen(Qt::NoPen); + p->drawRect(r);*/ + p->setPen(Qt::NoPen); + p->setBrush(cg.background()); + p->drawRect(r); + + p->setBrush(value.toColor()); + p->setPen(Qt::SolidLine); + QRect r2(r); + r2.setTopLeft(r.topLeft() + QPoint(5,5)); + r2.setBottomRight(r.bottomRight() - QPoint(5,5)); + p->drawRect(r2); +} + +void PColorButton::setValue(const QVariant& value, bool emitChange) +{ +#ifndef PURE_QT + disconnect(m_edit, SIGNAL(changed(const QColor&)), this, SLOT(updateProperty(const QColor&))); + m_edit->setColor(value.toColor()); + connect(m_edit, SIGNAL(changed(const QColor&)), this, SLOT(updateProperty(const QColor&))); +#else + m_color = value.toColor(); + m_edit->setText(m_color.name()); + QPixmap px; + px.resize(14,14); + px.fill(m_color); + m_edit->setIconSet(px); +#endif + if (emitChange) + emit propertyChanged(m_property, value); + +} + +void PColorButton::updateProperty(const QColor &// color + ) +{ + emit propertyChanged(m_property, value()); +} + +void PColorButton::changeColor() +{ +#ifdef PURE_QT + m_color = QColorDialog::getColor(m_color,this); + updateProperty(m_color); + m_edit->setText(m_color.name()); + QPixmap px; + px.resize(14,14); + px.fill(m_color); + m_edit->setIconSet(px); + +#endif +} + +} +#ifndef PURE_QT +#include "pcolorbutton.moc" +#endif diff --git a/lib/widgets/propeditor/pcolorbutton.h b/lib/widgets/propeditor/pcolorbutton.h new file mode 100644 index 00000000..67e02ef9 --- /dev/null +++ b/lib/widgets/propeditor/pcolorbutton.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PCOLORBUTTON_H +#define PCOLORBUTTON_H + +#include "propertywidget.h" + +#ifndef PURE_QT +class KColorButton; +#else +class QPushButton; +#include <qcolor.h> +#endif +namespace PropertyLib{ + +/** +@short %Property editor with color chooser button. +*/ +class PColorButton: public PropertyWidget +{ + Q_OBJECT +public: + PColorButton(MultiProperty *property, QWidget *parent = 0, const char *name = 0); + + virtual QVariant value() const; + virtual void drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value); + virtual void setValue(const QVariant& value, bool emitChange); + +protected slots: + void updateProperty(const QColor& color); + + void changeColor(); + +private: +#ifndef PURE_QT + KColorButton *m_edit; +#else + QPushButton *m_edit; + QColor m_color; +#endif +}; + +} + +#endif diff --git a/lib/widgets/propeditor/pcolorcombo.cpp b/lib/widgets/propeditor/pcolorcombo.cpp new file mode 100644 index 00000000..4d09a10f --- /dev/null +++ b/lib/widgets/propeditor/pcolorcombo.cpp @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "pcolorcombo.h" + +#include <qlayout.h> +#include <qpainter.h> + +#include <kcolorcombo.h> + +namespace PropertyLib{ + +PColorCombo::PColorCombo(MultiProperty *property, QWidget *parent, const char *name) + :PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); + m_edit = new KColorCombo(this); + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + l->addWidget(m_edit); + + connect(m_edit, SIGNAL(activated(int)), this, SLOT(updateProperty(int))); +} + +QVariant PColorCombo::value() const +{ + return QVariant(m_edit->color()); +} + +void PColorCombo::setValue(const QVariant &value, bool emitChange) +{ + disconnect(m_edit, SIGNAL(activated(int)), this, SLOT(updateProperty(int))); + m_edit->setColor(value.toColor()); + connect(m_edit, SIGNAL(activated(int)), this, SLOT(updateProperty(int))); + if (emitChange) + emit propertyChanged(m_property, value); +} + +void PColorCombo::updateProperty(int /*val*/) +{ + emit propertyChanged(m_property, value()); +} + +void PColorCombo::drawViewer(QPainter *p, const QColorGroup &cg, const QRect &r, const QVariant &value) +{ + p->setPen(Qt::NoPen); + p->setBrush(cg.background()); + p->drawRect(r); + + p->setBrush(value.toColor()); + p->setPen(Qt::SolidLine); + QRect r2(r); + r2.setTopLeft(r.topLeft() + QPoint(5,5)); + r2.setBottomRight(r.bottomRight() - QPoint(5,5)); + p->drawRect(r2); +} + +} + +#ifndef PURE_QT +#include "pcolorcombo.moc" +#endif + diff --git a/lib/widgets/propeditor/pcolorcombo.h b/lib/widgets/propeditor/pcolorcombo.h new file mode 100644 index 00000000..512fa9ad --- /dev/null +++ b/lib/widgets/propeditor/pcolorcombo.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PCOLORCOMBO_H +#define PCOLORCOMBO_H + +#include "propertywidget.h" + +class KColorCombo; + +namespace PropertyLib{ + +/** +@short %Property editor with color combobox. +*/ +class PColorCombo: public PropertyWidget{ + Q_OBJECT +public: + PColorCombo(MultiProperty *property, QWidget *parent = 0, const char *name = 0); + + /**@return the value currently entered in the editor widget.*/ + virtual QVariant value() const; + /**Sets the value shown in the editor widget. Set emitChange to false + if you don't want to emit propertyChanged signal.*/ + virtual void setValue(const QVariant &value, bool emitChange=true); + /**Function to draw a property viewer when the editor isn't shown.*/ + virtual void drawViewer(QPainter *p, const QColorGroup &cg, const QRect &r, const QVariant &value); + +private slots: + void updateProperty(int val); + +private: + KColorCombo *m_edit; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/pcombobox.cpp b/lib/widgets/propeditor/pcombobox.cpp new file mode 100644 index 00000000..9c1619cf --- /dev/null +++ b/lib/widgets/propeditor/pcombobox.cpp @@ -0,0 +1,110 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "pcombobox.h" + +#include <qcombobox.h> +#include <qlayout.h> + +namespace PropertyLib{ + +PComboBox::PComboBox(MultiProperty *property, const QMap<QString, QVariant> &list, QWidget *parent, const char *name) + :PropertyWidget(property, parent, name), m_valueList(list) +{ + init(false); +} + +PComboBox::PComboBox(MultiProperty *property, const QMap<QString, QVariant> &list, bool rw, QWidget *parent, const char *name) + :PropertyWidget(property, parent, name), m_valueList(list) +{ + init(rw); +} + +void PComboBox::init(bool rw) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); + m_edit = new QComboBox(rw, this); + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + l->addWidget(m_edit); + + fillBox(); + + connect(m_edit, SIGNAL(activated(int)), this, SLOT(updateProperty(int))); +} + +void PComboBox::fillBox() +{ + for (QMap<QString, QVariant>::const_iterator it = m_valueList.begin(); it != m_valueList.end(); it++) + { + m_edit->insertItem(it.key()); + } +} + +QVariant PComboBox::value() const +{ + QMap<QString, QVariant>::const_iterator it = m_valueList.find(m_edit->currentText()); + if (it == m_valueList.end()) + return QVariant(""); + return QVariant(it.data()); +} + +void PComboBox::setValue(const QVariant &value, bool emitChange) +{ +#if QT_VERSION >= 0x030100 + if (!value.isNull()) +#else + if (value.canCast(QVariant::String)) +#endif + { + disconnect(m_edit, SIGNAL(activated(int)), this, SLOT(updateProperty(int))); + m_edit->setCurrentText(findDescription(value)); + connect(m_edit, SIGNAL(activated(int)), this, SLOT(updateProperty(int))); + if (emitChange) + emit propertyChanged(m_property, value); + } +} + +void PComboBox::updateProperty(int /*val*/) +{ + emit propertyChanged(m_property, value()); +} + +QString PComboBox::findDescription(const QVariant &value) +{ + for (QMap<QString, QVariant>::const_iterator it = m_valueList.begin(); it != m_valueList.end(); ++ it) + { + if (it.data() == value) + return it.key(); + } + return ""; +} + +void PComboBox::setValueList(const QMap<QString, QVariant> &valueList) +{ + m_valueList = valueList; + m_edit->clear(); + fillBox(); +} + +} + +#ifndef PURE_QT +#include "pcombobox.moc" +#endif + diff --git a/lib/widgets/propeditor/pcombobox.h b/lib/widgets/propeditor/pcombobox.h new file mode 100644 index 00000000..c2f8dc2f --- /dev/null +++ b/lib/widgets/propeditor/pcombobox.h @@ -0,0 +1,72 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PCOMBOBOX_H +#define PCOMBOBOX_H + +#include <qmap.h> + +#include <qcombobox.h> + +#include "propertywidget.h" + +class QComboBox; + +namespace PropertyLib{ + +/** +@short %Property editor with combobox. +*/ +class PComboBox: public PropertyWidget{ + Q_OBJECT +public: + /**This constructor is used for read-only selection combo. It provides a value from valueList*/ + PComboBox(MultiProperty *property, const QMap<QString, QVariant> &list, QWidget *parent = 0, const char *name = 0); + /**This constructor is used for read-write selection combo. It provides a value from valueList*/ + PComboBox(MultiProperty *property, const QMap<QString, QVariant> &list, bool rw, QWidget *parent = 0, const char *name = 0); + + /**@return the value currently entered in the editor widget.*/ + virtual QVariant value() const; + /**Sets the value shown in the editor widget. Set emitChange to false + if you don't want to emit propertyChanged signal.*/ + virtual void setValue(const QVariant &value, bool emitChange=true); + /**Sets the list of possible values shown in the editor widget. This method + does not emit propertyChanged signal. Reimplemented because combobox is used + to display possible values from valueList.*/ + virtual void setValueList(const QMap<QString, QVariant> &valueList); + +protected: + QString findDescription(const QVariant &value); + +protected slots: + void updateProperty(int val); + +private: + virtual void fillBox(); + void init(bool rw = false); + + /** map<description, value>*/ + QMap<QString, QVariant> m_valueList; + + QComboBox *m_edit; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/pcursoredit.cpp b/lib/widgets/propeditor/pcursoredit.cpp new file mode 100644 index 00000000..5667421d --- /dev/null +++ b/lib/widgets/propeditor/pcursoredit.cpp @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "pcursoredit.h" + +#include <qpainter.h> + +namespace PropertyLib{ + +PCursorEdit::PCursorEdit(MultiProperty* property, const QMap<QString, QVariant> &spValues, + QWidget* parent, const char* name) + :PComboBox(property, spValues, parent, name) +{ +} + +void PCursorEdit::drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value) +{ + PropertyWidget::drawViewer(p, cg, r, findDescription(value)); +} + +} + +#ifndef PURE_QT +#include "pcursoredit.moc" +#endif diff --git a/lib/widgets/propeditor/pcursoredit.h b/lib/widgets/propeditor/pcursoredit.h new file mode 100644 index 00000000..0a9ccb9a --- /dev/null +++ b/lib/widgets/propeditor/pcursoredit.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PCURSOREDIT_H +#define PCURSOREDIT_H + +#include "pcombobox.h" + +namespace PropertyLib{ + +/** +@short %Property editor for QCursor values. +*/ +class PCursorEdit: public PComboBox +{ + Q_OBJECT +public: + PCursorEdit(MultiProperty* property, const QMap<QString, QVariant> &spValues, + QWidget* parent = 0, const char* name = 0); + + virtual void drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value); +}; + +} + +#endif diff --git a/lib/widgets/propeditor/pdateedit.cpp b/lib/widgets/propeditor/pdateedit.cpp new file mode 100644 index 00000000..48a020e8 --- /dev/null +++ b/lib/widgets/propeditor/pdateedit.cpp @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "pdateedit.h" + +#include <qdatetimeedit.h> +#include <qpainter.h> +#include <qlayout.h> + +namespace PropertyLib{ + +PDateEdit::PDateEdit(MultiProperty* property, QWidget* parent, const char* name) + :PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); + m_edit = new QDateEdit(this); + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + l->addWidget(m_edit); + + connect(m_edit, SIGNAL(valueChanged(const QDate&)), this, SLOT(updateProperty(const QDate&))); +} + +QVariant PDateEdit::value() const +{ + return QVariant(m_edit->date()); +} + +void PDateEdit::drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value) +{ + p->setPen(Qt::NoPen); + p->setBrush(cg.background()); + p->drawRect(r); + p->drawText(r, Qt::AlignLeft | Qt::AlignVCenter | Qt::SingleLine, value.toDate().toString(Qt::LocalDate)); +} + +void PDateEdit::setValue(const QVariant& value, bool emitChange) +{ + disconnect(m_edit, SIGNAL(valueChanged(const QDate&)), this, SLOT(updateProperty(const QDate&))); + m_edit->setDate(value.toDate()); + connect(m_edit, SIGNAL(valueChanged(const QDate&)), this, SLOT(updateProperty(const QDate&))); + if (emitChange) + emit propertyChanged(m_property, value); +} + +void PDateEdit::updateProperty(const QDate &val) +{ + emit propertyChanged(m_property, QVariant(val)); +} + +} + +#ifndef PURE_QT +#include "pdateedit.moc" +#endif diff --git a/lib/widgets/propeditor/pdateedit.h b/lib/widgets/propeditor/pdateedit.h new file mode 100644 index 00000000..166e1e3f --- /dev/null +++ b/lib/widgets/propeditor/pdateedit.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PDATEEDIT_H +#define PDATEEDIT_H + +#include "propertywidget.h" + +class QDateEdit; + +namespace PropertyLib{ + +/** +@short %Property editor for QDate values. +*/ +class PDateEdit : public PropertyWidget +{ +Q_OBJECT +public: + PDateEdit(MultiProperty* property, QWidget* parent=0, const char* name=0); + + virtual QVariant value() const; + virtual void drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value); + virtual void setValue(const QVariant& value, bool emitChange); + +private slots: + void updateProperty(const QDate &val); + +private: + QDateEdit *m_edit; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/pdatetimeedit.cpp b/lib/widgets/propeditor/pdatetimeedit.cpp new file mode 100644 index 00000000..ec9025ec --- /dev/null +++ b/lib/widgets/propeditor/pdatetimeedit.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "pdatetimeedit.h" + +#include <qdatetimeedit.h> +#include <qpainter.h> +#include <qlayout.h> + +namespace PropertyLib{ + +PDateTimeEdit::PDateTimeEdit(MultiProperty* property, QWidget* parent, const char* name): PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); + m_edit = new QDateTimeEdit(this); + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + l->addWidget(m_edit); + + connect(m_edit, SIGNAL(valueChanged(const QDateTime&)), this, SLOT(updateProperty(const QDateTime&))); +} + +QVariant PDateTimeEdit::value() const +{ + return QVariant(m_edit->dateTime()); +} + +void PDateTimeEdit::drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value) +{ + p->setPen(Qt::NoPen); + p->setBrush(cg.background()); + p->drawRect(r); + p->drawText(r, Qt::AlignLeft | Qt::AlignVCenter | Qt::SingleLine, value.toDateTime().toString(Qt::LocalDate)); +} + +void PDateTimeEdit::setValue(const QVariant& value, bool emitChange) +{ + disconnect(m_edit, SIGNAL(valueChanged(const QDateTime&)), this, SLOT(updateProperty(const QDateTime&))); + m_edit->setDateTime(value.toDateTime()); + connect(m_edit, SIGNAL(valueChanged(const QDateTime&)), this, SLOT(updateProperty(const QDateTime&))); + if (emitChange) + emit propertyChanged(m_property, value); +} + +void PDateTimeEdit::updateProperty(const QDateTime &val) +{ + emit propertyChanged(m_property, QVariant(val)); +} + +} + +#ifndef PURE_QT +#include "pdatetimeedit.moc" +#endif diff --git a/lib/widgets/propeditor/pdatetimeedit.h b/lib/widgets/propeditor/pdatetimeedit.h new file mode 100644 index 00000000..6421ef03 --- /dev/null +++ b/lib/widgets/propeditor/pdatetimeedit.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PDATETIMEEDIT_H +#define PDATETIMEEDIT_H + +#include "propertywidget.h" + +class QDateTimeEdit; + +namespace PropertyLib{ + +/** +@short %Property editor for QDateTime values. +*/ +class PDateTimeEdit : public PropertyWidget +{ +Q_OBJECT +public: + PDateTimeEdit(MultiProperty* property, QWidget* parent=0, const char* name=0); + + virtual QVariant value() const; + virtual void drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value); + virtual void setValue(const QVariant& value, bool emitChange); + +private slots: + void updateProperty(const QDateTime &val); + +private: + QDateTimeEdit *m_edit; + +}; + +} + +#endif diff --git a/lib/widgets/propeditor/pdoublenuminput.cpp b/lib/widgets/propeditor/pdoublenuminput.cpp new file mode 100644 index 00000000..5be997e4 --- /dev/null +++ b/lib/widgets/propeditor/pdoublenuminput.cpp @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "pdoublenuminput.h" + +#ifndef PURE_QT +#include <knuminput.h> +#else +#include "qfloatinput.h" +#endif + +#include <limits.h> +#include <math.h> +#include <qlayout.h> + +namespace PropertyLib{ + +PDoubleNumInput::PDoubleNumInput(MultiProperty *property, QWidget *parent, const char *name) + :PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); +#ifndef PURE_QT + m_edit = new KDoubleNumInput(-999999.0, 999999.0, 0.0, 0.01, 2, this); + m_edit->setLabel(QString::null); + connect(m_edit, SIGNAL(valueChanged(double)), this, SLOT(updateProperty(double))); +#else + m_edit = new QFloatInput(-999999, 999999, 0.01, 2, this ); + connect(m_edit, SIGNAL(valueChanged(int)), this, SLOT(updateProperty(int))); +#endif + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); +/* m_edit->setMinValue(-999999999); + m_edit->setMaxValue(+999999999); + m_edit->setPrecision(2);*/ + l->addWidget(m_edit); + +} + +QVariant PDoubleNumInput::value() const +{ + return QVariant(m_edit->value()); +} + +void PDoubleNumInput::setValue(const QVariant &value, bool emitChange) +{ +#ifndef PURE_QT + disconnect(m_edit, SIGNAL(valueChanged(double)), this, SLOT(updateProperty(double))); + m_edit->setValue(value.toDouble()); + connect(m_edit, SIGNAL(valueChanged(double)), this, SLOT(updateProperty(double))); +#else + disconnect(m_edit, SIGNAL(valueChanged(int)), this, SLOT(updateProperty(int))); + m_edit->setValue(int(value.toDouble()*pow(m_edit->digits(),10))); + connect(m_edit, SIGNAL(valueChanged(int)), this, SLOT(updateProperty(int))); +#endif + if (emitChange) + emit propertyChanged(m_property, value); +} + +void PDoubleNumInput::updateProperty(double val) +{ + emit propertyChanged(m_property, QVariant(val)); +} +void PDoubleNumInput::updateProperty(int val) +{ +#ifdef PURE_QT + QString format = QString("%.%1f").arg( m_edit->digits() ); + QString strVal = QString().sprintf(format.latin1(), + (val/(float)pow(m_edit->digits(),10)) ); + emit propertyChanged(m_property, QVariant(strVal)); +#else + Q_UNUSED(val); +#endif +} + +} + +#ifndef PURE_QT +#include "pdoublenuminput.moc" +#endif diff --git a/lib/widgets/propeditor/pdoublenuminput.h b/lib/widgets/propeditor/pdoublenuminput.h new file mode 100644 index 00000000..f38622ed --- /dev/null +++ b/lib/widgets/propeditor/pdoublenuminput.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PDOUBLENUMINPUT_H +#define PDOUBLENUMINPUT_H + +#include "propertywidget.h" + +#ifndef PURE_QT +class KDoubleNumInput; +#else +class QFloatInput; +#endif + +namespace PropertyLib{ + +/** +@short %Property editor with double num input box. +*/ +class PDoubleNumInput: public PropertyWidget +{ +Q_OBJECT +public: + PDoubleNumInput(MultiProperty *property, QWidget *parent = 0, const char *name = 0); + + /**@return the value currently entered in the editor widget.*/ + virtual QVariant value() const; + /**Sets the value shown in the editor widget. Set emitChange to false + if you don't want to emit propertyChanged signal.*/ + virtual void setValue(const QVariant &value, bool emitChange=true); + +private slots: + //because of a bug in moc which doesn't detect conditional slots + //we need them both + void updateProperty(double val); + void updateProperty(int val); + +private: +#ifndef PURE_QT + KDoubleNumInput *m_edit; +#else + QFloatInput *m_edit; +#endif +}; + +} + +#endif diff --git a/lib/widgets/propeditor/pdummywidget.cpp b/lib/widgets/propeditor/pdummywidget.cpp new file mode 100644 index 00000000..ddf09d12 --- /dev/null +++ b/lib/widgets/propeditor/pdummywidget.cpp @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "pdummywidget.h" + +#include <qpainter.h> + +namespace PropertyLib{ + +PDummyWidget::PDummyWidget(MultiProperty *property, QWidget *parent, const char *name) + :PropertyWidget(property, parent, name) +{ +} + +QVariant PDummyWidget::value() const +{ + return m_value; +} + +void PDummyWidget::setValue(const QVariant &value, bool emitChange) +{ + m_value = value; + if (emitChange) + emit propertyChanged(m_property, value); +} + +void PDummyWidget::drawViewer(QPainter *p, const QColorGroup &cg, const QRect &r, const QVariant &/*value*/) +{ + p->setBrush(cg.background()); + p->setPen(Qt::NoPen); + p->drawRect(r); +} + +} + +#ifndef PURE_QT +#include "pdummywidget.moc" +#endif diff --git a/lib/widgets/propeditor/pdummywidget.h b/lib/widgets/propeditor/pdummywidget.h new file mode 100644 index 00000000..24a30837 --- /dev/null +++ b/lib/widgets/propeditor/pdummywidget.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PDUMMYWIDGET_H +#define PDUMMYWIDGET_H + +#include "propertywidget.h" +#include "multiproperty.h" + +class QWidget; +class QVariant; +class QPainter; +class QColorGroup; +class QRect; + +namespace PropertyLib{ + +/** +@short %Property editor with empty widget. + +This is usefull for properties which can't be edited in a generic way +like QValueList's or QMap's stored in a variant. +*/ +class PDummyWidget: public PropertyWidget +{ + Q_OBJECT +public: + PDummyWidget(MultiProperty *property, QWidget *parent = 0, const char *name = 0); + + /**@return the value currently entered in the editor widget.*/ + virtual QVariant value() const; + /**Sets the value shown in the editor widget. Set emitChange to false + if you don't want to emit propertyChanged signal.*/ + virtual void setValue(const QVariant& value, bool emitChange); + /**Function to draw a property viewer when the editor isn't shown.*/ + virtual void drawViewer(QPainter *p, const QColorGroup &cg, const QRect &r, const QVariant &value); + +private: + QVariant m_value; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/pfontbutton.cpp b/lib/widgets/propeditor/pfontbutton.cpp new file mode 100644 index 00000000..3b999dd3 --- /dev/null +++ b/lib/widgets/propeditor/pfontbutton.cpp @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "pfontbutton.h" + +#include <qlayout.h> +#include <qpainter.h> +#include <qpushbutton.h> + +#include <kfontrequester.h> + +#ifndef PURE_QT +#include <klocale.h> +#else +#include "compat_tools.h" +#endif + +namespace PropertyLib{ + +PFontButton::PFontButton(MultiProperty* property, QWidget* parent, const char* name) + :PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); + m_edit = new KFontRequester(this); + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); +#ifndef PURE_QT + m_edit->button()->setText(i18n("...")); +#endif + l->addWidget(m_edit); + + connect(m_edit, SIGNAL(fontSelected(const QFont& )), this, SLOT(updateProperty(const QFont& ))); +} + +QVariant PFontButton::value() const +{ + return QVariant(m_edit->font()); +} + +void PFontButton::drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value) +{ + p->setPen(Qt::NoPen); + p->setBrush(cg.background()); + p->drawRect(r); + QFontInfo fi(value.toFont()); + p->drawText(r, Qt::AlignLeft | Qt::AlignVCenter | Qt::SingleLine, + fi.family() + (fi.bold() ? i18n(" Bold") : QString("")) + + (fi.italic() ? i18n(" Italic") : QString("")) + + " " + QString("%1").arg(fi.pointSize()) ); +} + +void PFontButton::setValue(const QVariant& value, bool emitChange) +{ + disconnect(m_edit, SIGNAL(fontSelected(const QFont&)), this, SLOT(updateProperty(const QFont&))); + m_edit->setFont(value.toFont()); + connect(m_edit, SIGNAL(fontSelected(const QFont& )), this, SLOT(updateProperty(const QFont& ))); + if (emitChange) + emit propertyChanged(m_property, value); +} + +void PFontButton::updateProperty(const QFont &// font + ) +{ + emit propertyChanged(m_property, value()); +} + +} + +#ifndef PURE_QT +#include "pfontbutton.moc" +#endif diff --git a/lib/widgets/propeditor/pfontbutton.h b/lib/widgets/propeditor/pfontbutton.h new file mode 100644 index 00000000..20dcd566 --- /dev/null +++ b/lib/widgets/propeditor/pfontbutton.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PFONTBUTTON_H +#define PFONTBUTTON_H + +#include "propertywidget.h" + +class KFontRequester; + +namespace PropertyLib{ + +/** +@short %Property editor with font chooser button. +*/ +class PFontButton : public PropertyWidget +{ +Q_OBJECT +public: + PFontButton(MultiProperty* property, QWidget* parent = 0, const char* name = 0); + + virtual QVariant value() const; + virtual void drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value); + virtual void setValue(const QVariant& value, bool emitChange); + +protected slots: + void updateProperty(const QFont& font); + +private: + KFontRequester *m_edit; + +}; + +} + +#endif diff --git a/lib/widgets/propeditor/pfontcombo.cpp b/lib/widgets/propeditor/pfontcombo.cpp new file mode 100644 index 00000000..7c648a30 --- /dev/null +++ b/lib/widgets/propeditor/pfontcombo.cpp @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "pfontcombo.h" + +#ifndef PURE_QT +#include <kfontcombo.h> +#else +#include <qcombobox.h> +#endif + +#include <qlayout.h> + +#ifdef PURE_QT +#include <qfontdatabase.h> +#endif + +namespace PropertyLib{ + +PFontCombo::PFontCombo(MultiProperty *property, QWidget *parent, const char *name) + :PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); + m_edit = new KFontCombo(this); + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + l->addWidget(m_edit); + + /*adymo: KFontCombo seems to have a bug: when it is not editable, the signals + activated(int) and textChanged(const QString &) are not emitted*/ +#ifdef PURE_QT + QFontDatabase fonts; + m_edit->insertStringList(fonts.families()); + connect(m_edit, SIGNAL(activated(const QString &)), this, SLOT(updateProperty(const QString&))); +#else + connect(m_edit, SIGNAL(textChanged(const QString&)), this, SLOT(updateProperty(const QString&))); +#endif +} + +QVariant PFontCombo::value() const +{ +#ifndef PURE_QT + return QVariant(m_edit->currentFont()); +#else + return QVariant(m_edit->currentText()); +#endif +} + +void PFontCombo::setValue(const QVariant &value, bool emitChange) +{ + disconnect(m_edit, SIGNAL(textChanged(const QString&)), this, SLOT(updateProperty(const QString&))); +#ifndef PURE_QT + m_edit->setCurrentFont(value.toString()); +#else + m_edit->setCurrentText(value.toString()); +#endif + connect(m_edit, SIGNAL(textChanged(const QString&)), this, SLOT(updateProperty(const QString&))); + if (emitChange) + emit propertyChanged(m_property, value); +} + +void PFontCombo::updateProperty(const QString &val) +{ + emit propertyChanged(m_property, QVariant(val)); +} + +} + +#ifndef PURE_QT +#include "pfontcombo.moc" +#endif diff --git a/lib/widgets/propeditor/pfontcombo.h b/lib/widgets/propeditor/pfontcombo.h new file mode 100644 index 00000000..be2671db --- /dev/null +++ b/lib/widgets/propeditor/pfontcombo.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PFONTCOMBO_H +#define PFONTCOMBO_H + +#include "propertywidget.h" + +#ifdef PURE_QT +#define KFontCombo QComboBox +#endif + +class KFontCombo; + +namespace PropertyLib{ + +/** +@short %Property editor with font combo box. +*/ +class PFontCombo: public PropertyWidget{ + Q_OBJECT +public: + PFontCombo(MultiProperty *property, QWidget *parent = 0, const char *name = 0); + + /**@return the value currently entered in the editor widget.*/ + virtual QVariant value() const; + /**Sets the value shown in the editor widget. Set emitChange to false + if you don't want to emit propertyChanged signal.*/ + virtual void setValue(const QVariant &value, bool emitChange=true); + +private slots: + void updateProperty(const QString &val); + +private: + KFontCombo *m_edit; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/plineedit.cpp b/lib/widgets/propeditor/plineedit.cpp new file mode 100644 index 00000000..ac5d7c2a --- /dev/null +++ b/lib/widgets/propeditor/plineedit.cpp @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "plineedit.h" + +#include <klineedit.h> +#include <qlayout.h> + +namespace PropertyLib{ + +PLineEdit::PLineEdit(MultiProperty *property, QWidget *parent, const char *name) + :PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); + m_edit = new KLineEdit(this); + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + l->addWidget(m_edit); + + connect(m_edit, SIGNAL(textChanged(const QString&)), this, SLOT(updateProperty(const QString&))); +} + +QVariant PLineEdit::value() const +{ + return QVariant(m_edit->text()); +} + +void PLineEdit::setValue(const QVariant &value, bool emitChange) +{ + disconnect(m_edit, SIGNAL(textChanged(const QString&)), this, SLOT(updateProperty(const QString&))); + m_edit->setText(value.toString()); + connect(m_edit, SIGNAL(textChanged(const QString&)), this, SLOT(updateProperty(const QString&))); + if (emitChange) + emit propertyChanged(m_property, value); +} + +void PLineEdit::updateProperty(const QString& val) +{ + emit propertyChanged(m_property, QVariant(val)); +} + +} + +#ifndef PURE_QT +#include "plineedit.moc" +#endif diff --git a/lib/widgets/propeditor/plineedit.h b/lib/widgets/propeditor/plineedit.h new file mode 100644 index 00000000..ea6f0bd9 --- /dev/null +++ b/lib/widgets/propeditor/plineedit.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PLINEEDIT_H +#define PLINEEDIT_H + +#include "propertywidget.h" + +class KLineEdit; + +namespace PropertyLib{ + +/** +@short %Property editor with line edit. +*/ +class PLineEdit: public PropertyWidget{ + Q_OBJECT +public: + PLineEdit(MultiProperty *property, QWidget *parent = 0, const char *name = 0); + + /**@return the value currently entered in the editor widget.*/ + virtual QVariant value() const; + /**Sets the value shown in the editor widget. Set emitChange to false + if you don't want to emit propertyChanged signal.*/ + virtual void setValue(const QVariant &value, bool emitChange=true); + +private slots: + void updateProperty(const QString &val); + +private: + KLineEdit *m_edit; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/plinestyleedit.cpp b/lib/widgets/propeditor/plinestyleedit.cpp new file mode 100644 index 00000000..d596ad93 --- /dev/null +++ b/lib/widgets/propeditor/plinestyleedit.cpp @@ -0,0 +1,205 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "plinestyleedit.h" + +#include <qpainter.h> +#include <qpixmap.h> +#include <qcombobox.h> +#include <qlayout.h> + +namespace PropertyLib { + + const char *nopen[]={ + "48 16 1 1", + ". c None", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................"}; + const char *solid[]={ + "48 16 2 1", + ". c None", + "# c #000000", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + ".###########################################....", + ".###########################################....", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................"}; + const char *dash[]={ + "48 16 2 1", + ". c None", + "# c #000000", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + ".#########..#########..#########..##########....", + ".#########..#########..#########..##########....", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................"}; + const char *dashdot[]={ + "48 16 2 1", + ". c None", + "# c #000000", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + ".#########..##..#########..##..#########..##....", + ".#########..##..#########..##..#########..##....", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................"}; + const char *dashdotdot[]={ + "48 16 2 1", + ". c None", + "# c #000000", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + ".#########..##..##..#########..##..##..#####....", + ".#########..##..##..#########..##..##..#####....", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................", + "................................................"}; + + +PLineStyleEdit::PLineStyleEdit(MultiProperty* property, QWidget* parent, const char* name): PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); + m_edit = new QComboBox(this); + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + l->addWidget(m_edit); + + m_edit->insertItem(QPixmap(nopen)); + m_edit->insertItem(QPixmap(solid)); + m_edit->insertItem(QPixmap(dash)); + m_edit->insertItem(QPixmap(dashdot)); + m_edit->insertItem(QPixmap(dashdotdot)); + + connect(m_edit, SIGNAL(activated(int)), this, SLOT(updateProperty(int))); +} + +QVariant PLineStyleEdit::value() const +{ + return m_edit->currentItem(); +} + +void PLineStyleEdit::drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value) +{ + p->setPen(Qt::NoPen); + p->setBrush(cg.background()); + p->drawRect(r); + + if (!value.canCast(QVariant::Int)) + if ((value.toInt() > 5) || (value.toInt() < 0)) + return; + + switch (value.toInt()) { + case 0: + p->drawPixmap(r, QPixmap(nopen)); + break; + case 1: + p->drawPixmap(r, QPixmap(solid)); + break; + case 2: + p->drawPixmap(r, QPixmap(dash)); + break; + case 3: + p->drawPixmap(r, QPixmap(dashdot)); + break; + case 4: + p->drawPixmap(r, QPixmap(dashdot)); + break; + case 5: + p->drawPixmap(r, QPixmap(dashdotdot)); + break; + } +} + +void PLineStyleEdit::setValue(const QVariant& value, bool emitChange) +{ + if (!value.canCast(QVariant::Int)) + return; + if ((value.toInt() > 5) || (value.toInt() < 0)) + return; + disconnect(m_edit, SIGNAL(activated(int)), this, SLOT(updateProperty(int))); + m_edit->setCurrentItem(value.toInt()); + connect(m_edit, SIGNAL(activated(int)), this, SLOT(updateProperty(int))); + if (emitChange) + emit propertyChanged(m_property, value); +} + +void PLineStyleEdit::updateProperty(int val) +{ + emit propertyChanged(m_property, QVariant(val)); +} + +} + +#ifndef PURE_QT +#include "plinestyleedit.moc" +#endif diff --git a/lib/widgets/propeditor/plinestyleedit.h b/lib/widgets/propeditor/plinestyleedit.h new file mode 100644 index 00000000..1992efba --- /dev/null +++ b/lib/widgets/propeditor/plinestyleedit.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PROPERTYLIBPLINESTYLEEDIT_H +#define PROPERTYLIBPLINESTYLEEDIT_H + +#include "propertywidget.h" + +class QComboBox; + +namespace PropertyLib { + +/** +@short Line style editor +*/ +class PLineStyleEdit : public PropertyWidget { + Q_OBJECT +public: + PLineStyleEdit(MultiProperty* property, QWidget* parent = 0, const char* name = 0); + + virtual QVariant value() const; + virtual void drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value); + virtual void setValue(const QVariant& value, bool emitChange); + +private slots: + void updateProperty(int val); + +private: + QComboBox *m_edit; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/ppixmapedit.cpp b/lib/widgets/propeditor/ppixmapedit.cpp new file mode 100644 index 00000000..b20cc7b3 --- /dev/null +++ b/lib/widgets/propeditor/ppixmapedit.cpp @@ -0,0 +1,149 @@ +/*************************************************************************** + * Copyright (C) 2003 Cedric Pasteur * + * <cedric.pasteur@free.fr> * + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "ppixmapedit.h" + +#include <qlayout.h> +#include <qpainter.h> +#include <qlabel.h> +#include <qcursor.h> + +#ifndef PURE_QT +#include <klocale.h> +#else +#include "compat_tools.h" +#endif + +#ifndef PURE_QT +#include <kfiledialog.h> +#else +#include <qfiledialog.h> +#endif +#include <qpushbutton.h> + +namespace PropertyLib{ + +PPixmapEdit::PPixmapEdit(MultiProperty* property, QWidget* parent, const char* name) + :PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); + m_edit = new QLabel(this); + m_edit->setAlignment(Qt::AlignTop); + m_edit->resize(width(), height()-1); + m_edit->setBackgroundMode(Qt::PaletteBase); + m_edit->installEventFilter(this); + + m_button = new QPushButton(i18n("..."), this); + m_button->resize(height(), height()-8); + m_button->move(width() - m_button->width() -1, 0); + m_button->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); + l->addWidget(m_edit); + l->addWidget(m_button); + m_popup = new QLabel(0, 0, Qt::WStyle_NoBorder|Qt::WX11BypassWM|WStyle_StaysOnTop); + m_popup->hide(); + + + connect(m_button, SIGNAL(clicked()), this, SLOT(updateProperty())); +} + +QVariant PPixmapEdit::value() const +{ + return QVariant(*(m_edit->pixmap())); +} + +void PPixmapEdit::drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value) +{ + p->setPen(Qt::NoPen); + p->setBrush(cg.background()); + p->drawRect(r); + p->drawPixmap(r.topLeft().x(), r.topLeft().y(), value.toPixmap()); +} + +void PPixmapEdit::setValue(const QVariant& value, bool emitChange) +{ + m_edit->setPixmap(value.toPixmap()); + if (emitChange) + emit propertyChanged(m_property, value); +} + +void PPixmapEdit::updateProperty() +{ +#ifndef PURE_QT + KURL url = KFileDialog::getImageOpenURL(QString::null, this); + if (!url.isEmpty()) + { + m_edit->setPixmap(QPixmap(url.path())); + emit propertyChanged(m_property, value()); + } +#else + QString url = QFileDialog::getOpenFileName(); + if (!url.isEmpty()) + { + m_edit->setPixmap(QPixmap(url)); + emit propertyChanged(m_property, value()); + } +#endif +} + +void PPixmapEdit::resizeEvent(QResizeEvent *ev) +{ + m_edit->resize(ev->size().width(), ev->size().height()-1); + m_button->move(ev->size().width() - m_button->width(), 0); + m_edit->setMaximumHeight(m_button->height()); +} + +bool PPixmapEdit::eventFilter(QObject *o, QEvent *ev) +{ + if(o == m_edit) + { + if(ev->type() == QEvent::MouseButtonPress) + { + if(m_edit->pixmap()->size().height() < height()-2 + && m_edit->pixmap()->size().width() < width()-20) + return false; + m_popup->setPixmap(*(m_edit->pixmap())); + m_popup->resize(m_edit->pixmap()->size()); + m_popup->move(QCursor::pos()); + m_popup->show(); + } + if(ev->type() == QEvent::MouseButtonRelease) + { + if(m_popup->isVisible()) + m_popup->hide(); + } + if(ev->type() == QEvent::KeyPress) + { + QKeyEvent* e = static_cast<QKeyEvent*>(ev); + if((e->key() == Key_Enter) || (e->key()== Key_Space) || (e->key() == Key_Return)) + { + m_button->animateClick(); + return true; + } + } + } + return PropertyWidget::eventFilter(o, ev); +} + +} + +#ifndef PURE_QT +#include "ppixmapedit.moc" +#endif diff --git a/lib/widgets/propeditor/ppixmapedit.h b/lib/widgets/propeditor/ppixmapedit.h new file mode 100644 index 00000000..e61dc73c --- /dev/null +++ b/lib/widgets/propeditor/ppixmapedit.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PPIXMAPEDIT_H +#define PPIXMAPEDIT_H + +#include "propertywidget.h" + +class QLabel; +class QPushButton; + +namespace PropertyLib{ + +/** +@short %Property editor which shows a pixmap and allows to load it from file. +*/ +class PPixmapEdit : public PropertyWidget +{ + Q_OBJECT +public: + PPixmapEdit(MultiProperty* property, QWidget* parent = 0, const char* name = 0); + + virtual QVariant value() const; + virtual void drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value); + virtual void setValue(const QVariant& value, bool emitChange); + + virtual void resizeEvent(QResizeEvent *ev); + virtual bool eventFilter(QObject *o, QEvent *ev); + +protected slots: + void updateProperty(); + +private: + QLabel *m_edit; + QLabel *m_popup; + QPushButton *m_button; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/ppointedit.cpp b/lib/widgets/propeditor/ppointedit.cpp new file mode 100644 index 00000000..f9f37bf0 --- /dev/null +++ b/lib/widgets/propeditor/ppointedit.cpp @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "ppointedit.h" + +#include <klineedit.h> +#include <qlayout.h> +#include <qpainter.h> + +namespace PropertyLib{ + +PPointEdit::PPointEdit(MultiProperty* property, QWidget* parent, const char* name): PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); + m_edit = new KLineEdit(this); + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + l->addWidget(m_edit); + + m_edit->setReadOnly(true); +} + +QVariant PPointEdit::value() const +{ + return m_value; +} + +void PPointEdit::drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value) +{ + p->setPen(Qt::NoPen); + p->setBrush(cg.background()); + p->drawRect(r); + p->drawText(r, Qt::AlignLeft | Qt::AlignVCenter | Qt::SingleLine, QString("[ %1, %2 ]").arg(value.toPoint().x()).arg(value.toPoint().y())); +} + +void PPointEdit::setValue(const QVariant& value, bool emitChange) +{ + m_value = value; + m_edit->setText(QString("[ %1, %2 ]").arg(value.toPoint().x()).arg(value.toPoint().y())); + + if (emitChange) + emit propertyChanged(m_property, value); +} + +} + +#ifndef PURE_QT +#include "ppointedit.moc" +#endif diff --git a/lib/widgets/propeditor/ppointedit.h b/lib/widgets/propeditor/ppointedit.h new file mode 100644 index 00000000..9b571ced --- /dev/null +++ b/lib/widgets/propeditor/ppointedit.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PPOINTEDIT_H +#define PPOINTEDIT_H + +#include "propertywidget.h" + +class KLineEdit; + +namespace PropertyLib{ + +/** +@short %Property editor for QPoint values. +*/ +class PPointEdit : public PropertyWidget +{ +Q_OBJECT +public: + PPointEdit(MultiProperty* property, QWidget* parent=0, const char* name=0); + + virtual QVariant value() const; + virtual void drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value); + virtual void setValue(const QVariant& value, bool emitChange); + +private: + KLineEdit *m_edit; + QVariant m_value; + +}; + +} + +#endif diff --git a/lib/widgets/propeditor/prectedit.cpp b/lib/widgets/propeditor/prectedit.cpp new file mode 100644 index 00000000..c049b5d6 --- /dev/null +++ b/lib/widgets/propeditor/prectedit.cpp @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "prectedit.h" + +#include <klineedit.h> +#include <qlayout.h> +#include <qpainter.h> + +namespace PropertyLib{ + +PRectEdit::PRectEdit(MultiProperty* property, QWidget* parent, const char* name): PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); + m_edit = new KLineEdit(this); + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + l->addWidget(m_edit); + + m_edit->setReadOnly(true); +} + +QVariant PRectEdit::value() const +{ + return m_value; +} + +void PRectEdit::drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value) +{ + p->setPen(Qt::NoPen); + p->setBrush(cg.background()); + p->drawRect(r); + p->drawText(r, Qt::AlignLeft | Qt::AlignVCenter | Qt::SingleLine, QString("[ %1, %2, %3, %4 ]").arg(value.toRect().x()).arg(value.toRect().y()).arg(value.toRect().width()).arg(value.toRect().height())); +} + +void PRectEdit::setValue(const QVariant& value, bool emitChange) +{ + m_value = value; + m_edit->setText(QString("[ %1, %2, %3, %4 ]").arg(value.toRect().x()).arg(value.toRect().y()).arg(value.toRect().width()).arg(value.toRect().height())); + + if (emitChange) + emit propertyChanged(m_property, value); +} + +} + +#ifndef PURE_QT +#include "prectedit.moc" +#endif diff --git a/lib/widgets/propeditor/prectedit.h b/lib/widgets/propeditor/prectedit.h new file mode 100644 index 00000000..44bcfed4 --- /dev/null +++ b/lib/widgets/propeditor/prectedit.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PRECTEDIT_H +#define PRECTEDIT_H + +#include "propertywidget.h" + +class KLineEdit; + +namespace PropertyLib{ + +/** +@short %Property editor for QRect values. +*/ +class PRectEdit : public PropertyWidget +{ + Q_OBJECT +public: + PRectEdit(MultiProperty* property, QWidget* parent=0, const char* name=0); + + virtual QVariant value() const; + virtual void drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value); + virtual void setValue(const QVariant& value, bool emitChange); + +private: + KLineEdit *m_edit; + QVariant m_value; + +}; + +} + +#endif diff --git a/lib/widgets/propeditor/property.cpp b/lib/widgets/propeditor/property.cpp new file mode 100644 index 00000000..d53af8cf --- /dev/null +++ b/lib/widgets/propeditor/property.cpp @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo <cloudtemple@mskat.net> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "property.h" + +#include <qstring.h> + +namespace PropertyLib{ + +Property::Property(int type, const QString &name, const QString &description, + const QVariant &value, bool save, bool readOnly): + m_type(type), m_name(name), m_description(description), m_value(value), m_save(save), + m_readOnly(readOnly), m_visible(true) +{ +} + +Property::Property(const QString &name, const QMap<QString, QVariant> &v_valueList, + const QString &description, const QVariant &value, bool save, bool readOnly): + valueList(v_valueList), m_type(ValueFromList), m_name(name), + m_description(description), m_value(value), m_save(save), m_readOnly(readOnly), + m_visible(true) +{ +} + +Property::~Property() +{ +} + +bool Property::allowSaving() const +{ + return m_save; +} + +bool Property::operator<(const Property &prop) const +{ + if ((type() < prop.type()) && (name() < prop.name())) + return true; + else + return false; +} + +QString Property::name() const +{ + return m_name; +} + +void Property::setName(const QString &name) +{ + m_name = name; +} + +int Property::type() const +{ + return m_type; +} + +void Property::setType(int type) +{ + m_type = type; +} + +QVariant Property::value() const +{ + return m_value; +} + +void Property::setValue(const QVariant &value, bool rememberOldValue) +{ + if (rememberOldValue) + m_oldValue = m_value; + else + m_oldValue = value; + m_value = value; +} + +QString Property::description() const +{ + return m_description; +} + +void Property::setDescription(const QString &description) +{ + m_description = description; +} + +void Property::setValueList(const QMap<QString, QVariant> &v_valueList) +{ + valueList = v_valueList; +} + +bool Property::readOnly() const +{ + return m_readOnly; +} + +bool Property::visible() const +{ + return m_visible; +} + +void Property::setVisible( const bool visible ) +{ + m_visible = visible; +} + +QVariant Property::oldValue() const +{ + if (m_oldValue.isNull()) + return m_value; + else + return m_oldValue; +} + +} diff --git a/lib/widgets/propeditor/property.h b/lib/widgets/propeditor/property.h new file mode 100644 index 00000000..13a1ad8b --- /dev/null +++ b/lib/widgets/propeditor/property.h @@ -0,0 +1,181 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo <cloudtemple@mskat.net> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PROPERTY_H +#define PROPERTY_H + +#include <qvariant.h> + +#include <qmap.h> + +class QWidget; +class QString; + +/**Namespace which contain property editing classes.*/ +namespace PropertyLib{ + +/** @file property.h +@short Contains @ref PropertyLib::Property class and @ref PropertyLib::Property::PropertyType enum. +*/ + +/** +@short Property. + +It includes support for QStringList properties, an i18n'ed label and stores an old value to allow undo. + +Contains name, type and value. + +Type can be one of predefined types (including standard @ref QVariant types) by @ref PropertyLib::Property::PropertyType +enum or custom user type. User defined types should have values more than 3000. + +Value is a @ref QVariant. + +Property can optionally have a list of possible values. +In that case use @ref ValueFromList type and valueList member. +Use @ref description for i18n'ed label. + +Examples: +creating property: +\code +Property *property = new Property(String, name, description, value) +\endcode +using convenience constructor to create property of ValueFromList type: +\code +Property *property = new Property(name, possibleValuesList, description, value); +\endcode +*/ +class Property { +public: + /** PropertyType. + Integers that represent the type of the property. */ + enum PropertyType { + //standard supported QVariant types + Invalid = QVariant::Invalid /**<invalid property type*/, + Map = QVariant::Map /**<QMap<QString, QVariant>*/, + List = QVariant::List /**<QValueList<QVariant>*/, + String = QVariant::String /**<string*/, + StringList = QVariant::StringList /**<string list*/, + Font = QVariant::Font /**<font*/, + Pixmap = QVariant::Pixmap /**<pixmap*/, + //@todo implement QVariant::Brush + Rect = QVariant::Rect /**<rectangle (x,y, width, height)*/, + Size = QVariant::Size /**<size (width, height)*/, + Color = QVariant::Color /**<color*/, + //@todo implement QVariant::Palette + //@todo implement QVariant::ColorGroup + //@todo implement QVariant::IconSet + Point = QVariant::Point /**<point (x,y)*/, + //@todo implement QVariant::Image + Integer = QVariant::Int /**<integer*/, + //@todo implement QVariant::UInt + Boolean = QVariant::Bool /**<boolean*/, + Double = QVariant::Double /**<double*/, + //@todo implement QVariant::CString + //@todo implement QVariant::PointArray + //@todo implement QVariant::Region + //@todo implement QVariant::Bitmap + Cursor = QVariant::Cursor /**<cursor*/, + SizePolicy = QVariant::SizePolicy /**<size policy (horizontal, vertical)*/, + Date = QVariant::Date /**<date*/, + //@todo implement QVariant::Time + DateTime = QVariant::DateTime /**<date and time*/, + //@todo implement QVariant::ByteArray + //@todo implement QVariant::BitArray + //@todo implement QVariant::KeySequence + //@todo implement QVariant::Pen + //@todo implement QVariant::Long + //@todo implement QVariant::LongLong + //@todo implement QVariant::ULongLong + + + //predefined custom types + ValueFromList = 2000 /**<string value from a list*/, + Symbol = 2001 /**<unicode symbol code*/, + FontName = 2002 /**<font name, e.g. "times new roman"*/, + FileURL = 2003 /**<url of a file*/, + DirectoryURL = 2004 /**<url of a directory*/, + LineStyle = 2005 /**<line style*/, + + UserDefined = 3000 /**<plugin defined properties should start here*/ + }; + + /**Constructs empty property.*/ + Property() {} + /**Constructs property.*/ + Property(int type, const QString &name, const QString &description, + const QVariant &value = QVariant(), bool save = true, bool readOnly = false); + /**Constructs property with @ref ValueFromList type.*/ + Property(const QString &name, const QMap<QString, QVariant> &v_valueList, + const QString &description, const QVariant &value = QVariant(), bool save = true, bool readOnly = false); + virtual ~Property(); + + virtual bool operator<(const Property &prop) const; + + /**@return the name of the property.*/ + virtual QString name() const; + /**Sets the name of the property.*/ + virtual void setName(const QString &name); + /**@return the type of the property.*/ + virtual int type() const; + /**Sets the type of the property.*/ + virtual void setType(int type); + /**@return the value of the property.*/ + virtual QVariant value() const; + /**Sets the value of the property.*/ + virtual void setValue(const QVariant &value, bool rememberOldValue = true); + /**@return the description of the property.*/ + virtual QString description() const; + /**Sets the description of the property.*/ + virtual void setDescription(const QString &description); + /**Sets the string-to-value correspondence list of the property. + This is used to create comboboxes-like property editors.*/ + virtual void setValueList(const QMap<QString, QVariant> &list); + /**The string-to-value correspondence list of the property.*/ + QMap<QString, QVariant> valueList; + + /**Tells if the property can be saved to a stream, xml, etc. + There is a possibility to use "GUI" properties that aren't + stored but used only in a GUI.*/ + virtual bool allowSaving() const; + /**Tells if the property is read only.*/ + virtual bool readOnly() const; + /**Tells if the property is visible.*/ + virtual bool visible() const; + /**Set the visibility.*/ + virtual void setVisible(const bool visible); + + /**Gets the previous property value.*/ + virtual QVariant oldValue() const; + +private: +// Property(Property &property) {}; +// void operator=(Property &property) {}; + + int m_type; + QString m_name; + QString m_description; + QVariant m_value; + QVariant m_oldValue; + bool m_save; + bool m_readOnly; + bool m_visible; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/propertyeditor.cpp b/lib/widgets/propeditor/propertyeditor.cpp new file mode 100644 index 00000000..58c2b936 --- /dev/null +++ b/lib/widgets/propeditor/propertyeditor.cpp @@ -0,0 +1,480 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "propertyeditor.h" + +#ifndef PURE_QT +#include <klocale.h> +#include <kdebug.h> +#include <kiconloader.h> +#else +#include "compat_tools.h" +#endif + +#include <qtable.h> +#include <qlayout.h> +#include <qpainter.h> +#include <qptrlist.h> +#include <qvaluelist.h> +#include <qpushbutton.h> + +#include "property.h" +#include "multiproperty.h" +#include "propertymachinefactory.h" + +namespace PropertyLib{ + +class PropertyItem: public KListViewItem{ +public: + PropertyItem(PropertyEditor *parent, MultiProperty *property) + :KListViewItem(parent, property->description()), m_editor(parent), m_property(property), + m_changed(false) + { + } + + PropertyItem(PropertyEditor *editor, KListViewItem *parent, MultiProperty *property) + :KListViewItem(parent, property->description()), m_editor(editor), + m_property(property), m_changed(false) + { + } + +/* int type() const + { + return m_property->type(); + } + + QString name() const + { + return m_property->name(); + } + */ + MultiProperty *property() const + { + return m_property; + } + + virtual void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align) + { + if ((column == 0) && m_changed) + { + QFont font; + font.setBold(true); + p->setFont(font); + p->setBrush(cg.highlight()); + p->setPen(cg.highlightedText()); + } + if (column == 1) + { + QRect r(0, 0, m_editor->header()->sectionSize(1), height()); + //FIXME: this is ugly, but how else can we deal with ValueFromList properties? + QVariant valueToDraw; + if (m_property->type() == Property::ValueFromList) + valueToDraw = m_property->findValueDescription(); + else + valueToDraw = m_property->value(); + QColorGroup icg(cg); +#ifndef PURE_QT + icg.setColor(QColorGroup::Background, backgroundColor()); +#else + icg.setColor(QColorGroup::Background, white); +#endif + m_editor->machine(m_property)->propertyEditor->drawViewer(p, icg, r, valueToDraw); + return; + } + KListViewItem::paintCell(p, cg, column, width, align); + } + + virtual void setup() + { + KListViewItem::setup(); + setHeight(static_cast<int>(height()*1.5)); + } + + void setChanged(bool changed) + { + m_changed = changed; + } + +private: + PropertyEditor *m_editor; + MultiProperty *m_property; + bool m_changed; +}; + + +class PropertyGroupItem: public KListViewItem{ +public: + PropertyGroupItem(KListView *parent, const QString &name) + :KListViewItem(parent, name) + { + init(); + } + PropertyGroupItem(KListViewItem *parent, const QString &name) + :KListViewItem(parent, name) + { + init(); + } + + virtual void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align) + { + if (column == 0) + { + QFont font; + font.setBold(true); + p->setFont(font); + p->setBrush(cg.highlight()); + p->setPen(cg.highlightedText()); + } + KListViewItem::paintCell(p, cg, column, width, align); + } + virtual void setup() + { + KListViewItem::setup(); + setHeight(static_cast<int>(height()*1.4)); + } + +private: + void init() + { + setOpen(true); + } +}; + +class SeparatorItem: public KListViewItem{ +public: + SeparatorItem(KListView *parent) + :KListViewItem(parent) + { + setSelectable(false); + } +}; +PropertyEditor::PropertyEditor(QWidget *parent, const char *name) + :KListView(parent, name) +{ + setSorting(-1); + + addColumn(i18n("Name")); + addColumn(i18n("Value")); + setAllColumnsShowFocus(true); + setColumnWidthMode(0, QListView::Maximum); + setResizeMode(QListView::LastColumn); + + header()->setClickEnabled(false); + + connect(header(), SIGNAL(sizeChange(int, int, int)), + this, SLOT(updateEditorSize())); + connect(this, SIGNAL(currentChanged(QListViewItem*)), + this, SLOT(slotClicked(QListViewItem*))); + + m_currentEditItem = 0; + m_doubleClickForEdit = true; + m_lastClickedItem = 0; + m_currentEditWidget = 0; + m_list = 0; + + m_currentEditArea = new QWidget(viewport()); + m_currentEditArea->hide(); + m_undoButton = new QPushButton(m_currentEditArea); +#ifndef PURE_QT + m_undoButton->setPixmap(SmallIcon("undo")); +#else + m_undoButton->setPixmap( QPixmap("undo.xpm") ); +#endif + m_undoButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding); + m_undoButton->resize(m_undoButton->height(), m_undoButton->height()); + m_undoButton->hide(); + connect(m_undoButton, SIGNAL(clicked()), this, SLOT(undo())); + m_currentEditLayout = new QGridLayout(m_currentEditArea, 1, 2, 0, 0); +// m_currentEditLayout->addWidget(m_undoButton, 0, 1); +} + +PropertyEditor::~PropertyEditor() +{ + clearMachineCache(); +} + +void PropertyEditor::populateProperties(PropertyList *list) +{ + if (list == 0) + return; + m_list = list; + connect(m_list, SIGNAL(propertyValueChanged(Property*)), this, SLOT(propertyValueChanged(Property*))); + const QValueList<QPair<QString, QValueList<QString> > >& groups = m_list->propertiesOfGroup(); + for (QValueList<QPair<QString, QValueList<QString> > >::const_iterator it = groups.begin(); + it != groups.end(); ++it) + { +// qWarning("PropertyEditor::populateProperties: adding group %s", (*it).first.ascii()); + PropertyGroupItem *group = 0; + if ( (!(*it).first.isEmpty()) && ((*it).second.count() > 0) ) + group = new PropertyGroupItem(this, (*it).first); + const QValueList<QString> &properties = (*it).second; + for (QValueList<QString>::const_iterator it2 = properties.begin(); it2 != properties.end(); ++it2) + { +// qWarning("PropertyEditor::populateProperties: adding property %s", (*it2).ascii()); + if (group) + addProperty(group, *it2); + else + addProperty(*it2); + } + } + if (firstChild()) + { + setCurrentItem(firstChild()); + setSelected(firstChild(), true); + slotClicked(firstChild()); + } +} + +void PropertyEditor::addProperty(PropertyGroupItem *group, const QString &name) +{ + if ((*m_list)[name] == 0) + return; +// qWarning("%s = name : object null ", name.ascii()); + PropertyItem *pitem = new PropertyItem(this, group, (*m_list)[name]); + addChildProperties(pitem); +} + +void PropertyEditor::addProperty(const QString &name) +{ + if ((*m_list)[name] == 0) + return; +// qWarning("%s = name : object null ", name.ascii()); + PropertyItem *pitem = new PropertyItem(this, (*m_list)[name]); + addChildProperties(pitem); +} + +void PropertyEditor::addChildProperties(PropertyItem *parent) +{ + MultiProperty *prop = parent->property(); + //force machine creation to get detailed properties appended to current multiproperty + if ( !m_registeredForType.contains(prop->name()) + && (PropertyMachineFactory::getInstance()->hasDetailedEditors(prop->type())) ) + { + //FIXME: find better solution + machine(prop); + } + +// qWarning("seeking children: count: %d", prop->details.count()); + + parent->setOpen(true); + for (QValueList<ChildProperty>::iterator it = prop->details.begin(); it != prop->details.end(); ++it) + { +// qWarning("found child %s", (*it).name().ascii()); + new PropertyItem(this, parent, new MultiProperty(&m_detailedList, &(*it))); + } +} + +void PropertyEditor::clearProperties() +{ + m_detailedList.clear(); + if (!m_list) + return; + + hideEditor(); + + disconnect(m_list, SIGNAL(propertyValueChanged(Property*)), this, SLOT(propertyValueChanged(Property*))); + clear(); + delete m_list; + m_list = 0; +} + +void PropertyEditor::propertyValueChanged(Property *property) +{ +// qWarning("PropertyEditor::propertyValueChanged"); + if (m_currentEditWidget->propertyName() == property->name()) + m_currentEditWidget->setValue(property->value(), false); + else + { +// repaint all items + QListViewItemIterator it(this); + while (it.current()) + { + repaintItem(it.current()); + ++it; + } + } +} + +void PropertyEditor::propertyChanged(MultiProperty *property, const QVariant &value) +{ + if (!property) + return; + + qWarning("editor: assign %s to %s", property->name().latin1(), value.toString().latin1()); + property->setValue(value, false); + + //highlight changed properties + if (m_currentEditItem && (m_currentEditItem->property() == property)) + { + m_currentEditItem->setChanged(true); + repaintItem(m_currentEditItem); + } + + emit changed(); + +/* if (m_list->contains(name)) + { + (*m_list)[name]->setValue(value, false); +// else if (m_detailedList->contains(*/ +} + +void PropertyEditor::hideEditor() +{ + m_lastClickedItem = 0; + m_currentEditItem = 0; + if (m_currentEditWidget) + { + m_currentEditLayout->remove(m_currentEditWidget); + m_currentEditWidget->hide(); + } + m_currentEditLayout->remove(m_undoButton); + m_undoButton->hide(); + m_currentEditArea->hide(); + m_currentEditWidget = 0; +} + +void PropertyEditor::showEditor(PropertyItem *item) +{ + m_currentEditItem = item; + placeEditor(item); + m_currentEditWidget->show(); + m_undoButton->show(); + m_currentEditArea->show(); +} + +void PropertyEditor::placeEditor(PropertyItem *item) +{ + QRect r = itemRect(item); + if (!r.size().isValid()) + { + ensureItemVisible(item); + r = itemRect(item); + } + + r.setX(header()->sectionPos(1)); + r.setWidth(header()->sectionSize(1)); + + // check if the column is fully visible + if (visibleWidth() < r.right()) + r.setRight(visibleWidth()); + + r = QRect(viewportToContents(r.topLeft()), r.size()); + + if (item->pixmap(1)) + { + r.setX(r.x() + item->pixmap(1)->width()); + } + + if (PropertyWidget* editor = prepareEditor(item)) + { + m_currentEditLayout->addWidget(editor, 0, 0); + m_currentEditLayout->addWidget(m_undoButton, 0, 1); + m_currentEditArea->resize(r.size()); +// m_currentEditLayout->invalidate(); + moveChild(m_currentEditArea, r.x(), r.y()); + m_currentEditWidget = editor; + } +} + +PropertyWidget* PropertyEditor::prepareEditor(PropertyItem *item) +{ + PropertyWidget *editorWidget = 0; +/* if (item->depth() >= 2) + { + editorWidget = machine(item->name())->propertyEditor; + editorWidget->setValue(m_accessor->value(item->name()), false); + } + else + {*/ + editorWidget = machine(item->property())->propertyEditor; + editorWidget->setProperty(item->property()); + if (item->property()->type() == Property::ValueFromList) + editorWidget->setValueList(item->property()->valueList()); + editorWidget->setValue(item->property()->value(), false); + //} + return editorWidget; +} + +void PropertyEditor::updateEditorSize() +{ + if (m_currentEditItem) + placeEditor(m_currentEditItem); +} + +void PropertyEditor::slotClicked(QListViewItem *item) +{ + if (item == 0) + { + hideEditor(); + return; + } + if (item != m_lastClickedItem) + { + hideEditor(); + PropertyItem *it = dynamic_cast<PropertyItem*>(item); + if (it) + { + showEditor(it); + } + } + + m_lastClickedItem = item; +} + +Machine *PropertyEditor::machine(MultiProperty *property) +{ +// int type = property->type(); + QString name = property->name(); + QMap<QString, QVariant> values = property->valueList(); + if (m_registeredForType[name] == 0) + { + m_registeredForType[name] = PropertyMachineFactory::getInstance()->machineForProperty(property); + connect(m_registeredForType[name]->propertyEditor, SIGNAL(propertyChanged(MultiProperty*, const QVariant&)), + this, SLOT(propertyChanged(MultiProperty*, const QVariant&))); + m_registeredForType[name]->propertyEditor->reparent(m_currentEditArea, 0, m_currentEditArea->childrenRect().topLeft()); + m_registeredForType[name]->propertyEditor->hide(); + } + return m_registeredForType[name]; +} + +void PropertyEditor::clearMachineCache() +{ + for (QMap<QString, Machine* >::iterator it = m_registeredForType.begin(); it != m_registeredForType.end(); ++it) + { + delete it.data(); + } + m_registeredForType.clear(); +} + +void PropertyEditor::undo() +{ + if ((m_currentEditItem == 0) || (m_currentEditWidget == 0) + || (!m_currentEditWidget->isVisible())) + return; + + m_currentEditWidget->undo(); + m_currentEditItem->setChanged(false); + repaintItem(m_currentEditItem); +} + +} + +#ifndef PURE_QT +#include "propertyeditor.moc" +#endif diff --git a/lib/widgets/propeditor/propertyeditor.h b/lib/widgets/propeditor/propertyeditor.h new file mode 100644 index 00000000..f641118c --- /dev/null +++ b/lib/widgets/propeditor/propertyeditor.h @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PROPERTYEDITOR_H +#define PROPERTYEDITOR_H + +#ifndef PURE_QT +#include <klistview.h> +#else +#include <qlistview.h> +#define KListView QListView +#define KListViewItem QListViewItem +#endif + +#include "propertylist.h" + +class QPushButton; +class QGridLayout; + +namespace PropertyLib{ + +class PropertyItem; +class PropertyGroupItem; +class PropertyWidget; +class Property; +class MultiProperty; +struct Machine; + +/** @file propertyeditor.h +@short Contains @ref PropertyLib::PropertyEditor class. +*/ + +/** +@short %Property editor. + +Displays a list of properties in a table form. Also performs grouping and +creation of property widgets from the machine factory. +@see PropertyWidget +@see Machine +@see PropertyMachineFactory +*/ +class PropertyEditor: public KListView{ + Q_OBJECT +public: + /**Constructs the property editor.*/ + PropertyEditor(QWidget *parent = 0, const char *name = 0); + ~PropertyEditor(); + + /**@return @ref Machine for given property. + Uses cache to store created machines. + Cache will be cleared only with @ref clearMachineCache.*/ + Machine *machine(MultiProperty *property); + +public slots: + /**Shows properties from a list.*/ + void populateProperties(PropertyList *list); + /**Clears property list, disconnects accessor from the editor and deletes it.*/ + void clearProperties(); + /**Deletes cached machines.*/ + void clearMachineCache(); + +signals: + /**Emitted when something is changed in property editor.*/ + void changed(); + +protected slots: + /**Updates property widget in the editor.*/ + void propertyValueChanged(Property* property); + /**Updates property in the list when new value is selected in the editor.*/ + void propertyChanged(MultiProperty *property, const QVariant &value); + + /**Shows property editor.*/ + void slotClicked(QListViewItem* item); + void updateEditorSize(); + + /**Undoes the last change in property editor.*/ + void undo(); + +protected: + void editItem(QListViewItem*, int); + void hideEditor(); + void showEditor(PropertyItem *item); + void placeEditor(PropertyItem *item); + PropertyWidget *prepareEditor(PropertyItem *item); + + void addGroup(const QString &name); + void addProperty(PropertyGroupItem *group, const QString &name); + void addProperty(const QString &name); + void addChildProperties(PropertyItem *parent); + +private: + PropertyList *m_list; + PropertyList m_detailedList; + + //machines cache for property types, machines will be deleted + QMap<QString, Machine* > m_registeredForType; + + PropertyItem *m_currentEditItem; + PropertyWidget *m_currentEditWidget; + QWidget *m_currentEditArea; + QGridLayout *m_currentEditLayout; + + bool m_doubleClickForEdit; + QListViewItem* m_lastClickedItem; + + QPushButton *m_undoButton; + +friend class PropertyItem; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/propertyeditor.pro b/lib/widgets/propeditor/propertyeditor.pro new file mode 100644 index 00000000..125ce510 --- /dev/null +++ b/lib/widgets/propeditor/propertyeditor.pro @@ -0,0 +1,40 @@ +TEMPLATE = lib +CONFIG += debug +DEFINES += PURE_QT + +SOURCES += childproperty.cpp pcombobox.cpp \ + pdummywidget.cpp ppointedit.cpp \ + propertymachinefactory.cpp pstringlistedit.cpp \ + multiproperty.cpp pcursoredit.cpp \ + prectedit.cpp propertywidget.cpp \ + psymbolcombo.cpp pcheckbox.cpp \ + pdateedit.cpp pfontcombo.cpp \ + property.cpp psizeedit.cpp \ + pdatetimeedit.cpp \ + plineedit.cpp propertyeditor.cpp \ + psizepolicyedit.cpp pyesnobutton.cpp \ + ppixmapedit.cpp \ + propertylist.cpp pspinbox.cpp \ + propertywidgetproxy.cpp plinestyleedit.cpp \ + qeditlistbox.cpp pdoublenuminput.cpp \ + qfloatinput.cpp pcolorbutton.cpp \ + purledit.cpp + +HEADERS += childproperty.h pcombobox.h \ + pdummywidget.h ppointedit.h \ + propertymachinefactory.h pcursoredit.h \ + prectedit.h propertywidget.h \ + pdateedit.h pfontcombo.h \ + property.h psizeedit.h \ + pdatetimeedit.h plineedit.h \ + propertyeditor.h psizepolicyedit.h \ + ppixmapedit.h propertylist.h \ + pspinbox.h propertywidgetproxy.h \ + multiproperty.h pyesnobutton.h \ + psymbolcombo.h pstringlistedit.h \ + pcheckbox.h plinestyleedit.h \ + qeditlistbox.h pdoublenuminput.h \ + qfloatinput.h pcolorbutton.h \ + purledit.h + +IMAGES += undo.xpm diff --git a/lib/widgets/propeditor/propertylist.cpp b/lib/widgets/propeditor/propertylist.cpp new file mode 100644 index 00000000..3ea79a87 --- /dev/null +++ b/lib/widgets/propeditor/propertylist.cpp @@ -0,0 +1,369 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "propertylist.h" + +#include "property.h" +#include "multiproperty.h" + +namespace PropertyLib{ + +PropertyList::PropertyList() + :QObject(0, 0), m_propertyOwner(true) +{ +} + +PropertyList::PropertyList(bool propertyOwner) + :QObject(0, 0), m_propertyOwner(propertyOwner) +{ +} + +PropertyList::~PropertyList() +{ + clear(); +} + +MultiProperty *PropertyList::operator[](const QString &name) +{ + if (m_list.contains(name)) + return m_list[name]; + else + return new MultiProperty(this); +} + +MultiProperty *PropertyList::property( const QString &name ) +{ + if (m_list.contains(name)) + return m_list[name]; + else + return new MultiProperty(this); +} + +void PropertyList::addProperty(Property *property) +{ + if (property == 0) + return; + MultiProperty *mp = 0; + if ( m_list.contains(property->name()) ) + { + mp = m_list[property->name()]; + mp->addProperty(property); + } + else + { + mp = new MultiProperty(this, property); + m_list[property->name()] = mp; + addToGroup("", mp); + } +} + +void PropertyList::addProperty(const QString &group, Property *property) +{ + if (property == 0) + return; + + MultiProperty *mp = 0; + if (m_list.contains(property->name())) + { + mp = m_list[property->name()]; + mp->addProperty(property); + } + else + { + mp = new MultiProperty(this, property); + m_list[property->name()] = mp; + addToGroup(group, mp); + } +} + +void PropertyList::removeProperty(Property *property) +{ + if (property == 0) + return; + + if (m_propertyOwner) + emit aboutToDeleteProperty(property); + + MultiProperty *mp = m_list[property->name()]; + QString group = m_groupOfProperty[mp]; + removeFromGroup(mp); + QString pname = property->name(); + mp->removeProperty(property); + if (m_propertyOwner) + delete property; + if (mp->list.count() == 0) + { +// qWarning("rp: removing mp for %s itself", pname.ascii()); + m_list.remove(pname); + delete mp; + } + else + addToGroup(group, mp); +} + +void PropertyList::removeProperty(const QString &name) +{ + if (m_list.contains(name)) + { + QString group = m_groupOfProperty[m_list[name]]; + removeFromGroup(m_list[name]); + Property *property; + for (property = m_list[name]->list.first(); property; property = m_list[name]->list.next()) + { + if (m_propertyOwner) + emit aboutToDeleteProperty(property); + + m_list[property->name()]->removeProperty(property); + if (m_propertyOwner) + delete property; + } + if (m_list[name]->list.count() == 0) + { +// qWarning("rp2: removing mp for %s itself", name.ascii()); + delete m_list[name]; + m_list.remove(name); + } + else + { + addToGroup(group, m_list[name]); + } + } +} + +const QValueList<QPair<QString, QValueList<QString> > >& PropertyList::propertiesOfGroup() const +{ + return m_propertiesOfGroup; +} + +const QMap<MultiProperty*, QString>& PropertyList::groupOfProperty() const +{ + return m_groupOfProperty; +} + +void PropertyList::addToGroup(const QString &group, MultiProperty *property) +{ + if (!property) + return; + + //do not add same property to the group twice + if (m_groupOfProperty.contains(property) && (m_groupOfProperty[property] == group)) + return; + + QPair<QString, QValueList<QString> > *groupPair = 0; + for(QValueList<QPair<QString, QValueList<QString> > >::iterator it = m_propertiesOfGroup.begin(); + it != m_propertiesOfGroup.end(); ++it) + { + if ((*it).first == group) + { + groupPair = &(*it); + break; + } + } + if (groupPair == 0) + { + groupPair = new QPair<QString, QValueList<QString> >(); + groupPair->first = group; + groupPair->second.append(property->name()); + m_propertiesOfGroup.append(*groupPair); + m_groupOfProperty[property] = group; + return; + } + //check if group already contains property with the same name + if (!groupPair->second.contains(property->name())) + groupPair->second.append(property->name()); + + m_groupOfProperty[property] = group; +} + +void PropertyList::removeFromGroup(MultiProperty *property) +{ + QString group = m_groupOfProperty[property]; +// qWarning("removeFromGroup group=%s", group.ascii()); + + for(QValueList<QPair<QString, QValueList<QString> > >::iterator it = m_propertiesOfGroup.begin(); + it != m_propertiesOfGroup.end(); ++it) + { +// qWarning("removeFromGroup checking %s", (*it).first.ascii()); + if ((*it).first == group) + { +// qWarning("removeFromGroup removing %s", property->name().ascii()); + (*it).second.remove(property->name()); + break; + } + } + + m_groupOfProperty.remove(property); +} + +void PropertyList::clear( ) +{ + for (QMap<QString, MultiProperty*>::iterator it = m_list.begin(); it != m_list.end(); ++it) + removeProperty(it.key()); +} + +bool PropertyList::contains( const QString & name ) +{ + if (m_list.contains(name)) + return true; + return false; +} + +QPtrList<Property> PropertyList::properties(const QString &name) +{ + if (m_list.contains(name)) + return m_list[name]->list; + return QPtrList<Property>(); +} + +PropertyList::Iterator PropertyList::begin() +{ + return Iterator(this); +} + +PropertyList::Iterator PropertyList::end() +{ + return Iterator(this, true); +} + +//PropertyList::Iterator class + +PropertyList::Iterator::Iterator(PropertyList *list) + :m_list(list) +{ + current = m_list->m_list.begin(); +} + +PropertyList::Iterator::Iterator(PropertyList *list, bool // end + ) + :m_list(list) +{ + current = m_list->m_list.end(); +} + +void PropertyList::Iterator::operator ++() +{ + next(); +} + +void PropertyList::Iterator::operator ++(int) +{ + next(); +} + +void PropertyList::Iterator::next() +{ + ++current; +} + +MultiProperty *PropertyList::Iterator::operator *() +{ + return data(); +} + +QString PropertyList::Iterator::key() +{ + return current.key(); +} + +MultiProperty *PropertyList::Iterator::data() +{ + return current.data(); +} + +bool PropertyList::Iterator::operator !=(Iterator it) +{ + return current != it.current; +} + + +// PropertyBuffer class + + + + + +PropertyBuffer::PropertyBuffer( ) + :PropertyList(false) +{ +} + +void PropertyBuffer::intersect(const PropertyList *list) +{ + qWarning("PropertyBuffer::intersect"); + for (QMap<QString, MultiProperty*>::iterator it = m_list.begin(); it != m_list.end(); ++it) + { +// qWarning("intersect:: for mp = %s", it.data()->name().ascii()); + if (list->m_list.contains(it.key())) + { +/* qWarning("intersect:: list contains %s", it.key().ascii()); + if ( (*(it.data()) == *(list->m_list[it.key()]))) + qWarning("intersect:: equal properties"); + else + qWarning("intersect:: not equal properties");*/ + if ( ((*it.data()) == *(list->m_list[it.key()])) + && (list->m_groupOfProperty[list->m_list[it.key()]] == m_groupOfProperty[it.data()]) ) + { +// qWarning("intersect:: equal properties, adding"); + it.data()->addProperty(list->m_list[it.key()]); + continue; + } + } +// qWarning("intersect:: removing %s from intersection", it.key().ascii()); + removeProperty(it.key()); + } + connect(list, SIGNAL(propertyValueChanged(Property*)), this, SLOT(intersectedValueChanged(Property*))); +} + +void PropertyBuffer::intersectedValueChanged(Property *property) +{ +// qWarning("PropertyBuffer::intersectedValueChanged"); + QString propertyName = property->name(); + if (!contains(propertyName)) + return; + + MultiProperty mp(property); + if (mp == *m_list[propertyName]) + { + Property *prop; + QPtrList<Property> props = properties(propertyName); + for (prop = props.first(); prop; prop = props.next()) + emit propertyValueChanged(prop); + } +} + +PropertyBuffer::PropertyBuffer(PropertyList *list) + :PropertyList(false) +{ + //deep copy of m_list + for (QMap<QString, MultiProperty*>::const_iterator it = list->m_list.begin(); + it != list->m_list.end(); ++it) + { + MultiProperty *mp = new MultiProperty(*it.data()); + mp->m_propertyList = this; + addToGroup(list->m_groupOfProperty[it.data()], mp); + m_list[it.key()] = mp; + } + connect(list, SIGNAL(propertyValueChanged(Property*)), this, SLOT(intersectedValueChanged(Property*))); +} + +} + +#ifndef PURE_QT +#include "propertylist.moc" +#endif diff --git a/lib/widgets/propeditor/propertylist.h b/lib/widgets/propeditor/propertylist.h new file mode 100644 index 00000000..c07cebbd --- /dev/null +++ b/lib/widgets/propeditor/propertylist.h @@ -0,0 +1,200 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PROPERTYLIST_H +#define PROPERTYLIST_H + +#include <qobject.h> +#include <qmap.h> +#include <qptrlist.h> +#include <qvaluelist.h> +#include <qpair.h> + +namespace PropertyLib{ + +class Property; +class MultiProperty; + +/** @file propertylist.h +@short Contains @ref PropertyLib::PropertyList class. +*/ + +/** +@short The list of properties. + +Every object in a program should operate with properties through +this list in order to: +- be informed about property changes +- allow property lists intersections +- display properties in the property editor widget (see @ref PropertyLib::PropertyEditor). +. + +PropertyList owns properties and deletes them itself. For a list that does not own +it's properties, look at @ref PropertyLib::PropertyBuffer class. + +PropertyList is also capable of grouping properties. +You can have unsorted list of groups of properties or a plain +alphabetically sorted list of properties or both at the same time. +*/ +class PropertyList: public QObject +{ + Q_OBJECT + +public: + class Iterator { + public: + void operator ++(); + void operator ++(int); + + MultiProperty *operator *(); + + bool operator != (Iterator it); + + QString key(); + MultiProperty *data(); + + private: + Iterator(PropertyList *list); + Iterator(PropertyList *list, bool end); + + void next(); + QMap<QString, MultiProperty*>::iterator current; + + PropertyList *m_list; + friend class PropertyList; + }; + + typedef Iterator iterator; + + PropertyList(); + virtual ~PropertyList(); + + /**Accesses a property by it's name. All property modifications are allowed + trough this method. For example, to set a value of a property, use: + /code + PropertyList list; + ... + list["My Property"]->setValue("My Value"); + /endcode + @return @ref MultiProperty with given name.*/ + virtual MultiProperty *operator[](const QString &name); + /**Accesses a property by it's name. All property modifications are allowed + trough this method. For example, to set a value of a property + */ + MultiProperty *property( const QString &name ); + + /**Adds the property to the list to the "common" group.*/ + virtual void addProperty(Property *property); + /**Adds the property to the list in group.*/ + virtual void addProperty(const QString &group, Property *property); + /**Removes property from the list. Emits aboutToDeleteProperty before removing.*/ + virtual void removeProperty(Property *property); + /**Removes property with the given name from the list. + Emits @ref aboutToDeleteProperty before removing.*/ + virtual void removeProperty(const QString &name); + + /**@return the list of grouped properties.*/ + virtual const QValueList<QPair<QString, QValueList<QString> > >& propertiesOfGroup() const; + /**@return the map: property - group name.*/ + virtual const QMap<MultiProperty*, QString>& groupOfProperty() const; + + /**Clears the list of properties.*/ + virtual void clear(); + /**Returns true if the list of properties contains property with given name.*/ + virtual bool contains(const QString &name); + + /**The list of properties with given name.*/ + QPtrList<Property> properties(const QString &name); + + Iterator begin(); + Iterator end(); + +signals: + /**Emitted when the value of the property is changed.*/ + void propertyValueChanged(Property* property); + /**Emitted when property is about to be deleted.*/ + void aboutToDeleteProperty(Property* property); + +protected: + /**Constructs a list which owns or does not own it's properties.*/ + PropertyList(bool propertyOwner); + + /**Adds property to a group.*/ + void addToGroup(const QString &group, MultiProperty *property); + /**Removes property from a group.*/ + void removeFromGroup(MultiProperty *property); + +private: + //sorted list of properties in form name: property + QMap<QString, MultiProperty*> m_list; + + //groups of properties: + // list of group name: (list of property names) + QValueList<QPair<QString, QValueList<QString> > > m_propertiesOfGroup; + // map of property: group + QMap<MultiProperty*, QString> m_groupOfProperty; + + //indicates that this list will delete properties after removeProperty() + //and also in destructor + bool m_propertyOwner; + +friend class MultiProperty; +friend class PropertyBuffer; +friend class Iterator; +}; + + +/** +@short The list of properties which does not own them. + +This class acts as @ref PropertyLib::PropertyList but it does not delete properties +in destructor (i.e. it does not own properties). +This class should be used to store results of property intersections. + +Example: +/code +PropertyList *list = new PropertyList(); +PropertyList *list2 = new PropertyList(); +PropertyList *list3 = new PropertyList(); +... +PropertyBuffer *buf = new PropertyBuffer(list); +buf->intersect(list2); +buf->intersect(list3); +... +/endcode +*/ +class PropertyBuffer: public PropertyList{ + Q_OBJECT +public: + /**Constructs a buffer from given property list.*/ + PropertyBuffer(PropertyList *list); + /**Constructs an empty property buffer.*/ + PropertyBuffer(); + + /**Intersects with other @ref PropertyLib::PropertyList.*/ + virtual void intersect(const PropertyList *list); + +protected slots: + void intersectedValueChanged(Property *property); + +}; + +} + +#endif diff --git a/lib/widgets/propeditor/propertymachinefactory.cpp b/lib/widgets/propeditor/propertymachinefactory.cpp new file mode 100644 index 00000000..222aa930 --- /dev/null +++ b/lib/widgets/propeditor/propertymachinefactory.cpp @@ -0,0 +1,207 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "propertymachinefactory.h" + +#ifndef PURE_QT +#include <klocale.h> +#else +#define i18n QObject::tr +#endif + +#include <qmap.h> + +#include "property.h" +#include "childproperty.h" +#include "multiproperty.h" +#include "plineedit.h" +#include "pspinbox.h" +#include "pdoublenuminput.h" +#include "pcheckbox.h" +#include "pstringlistedit.h" +#include "pdummywidget.h" +#include "pcombobox.h" +#include "psymbolcombo.h" +#include "pfontcombo.h" +#include "psizeedit.h" +#include "pdateedit.h" +#include "pdatetimeedit.h" +#include "ppointedit.h" +#include "prectedit.h" +#include "psizepolicyedit.h" +#include "pcolorbutton.h" +#include "pyesnobutton.h" +#include "ppixmapedit.h" +#include "pcursoredit.h" +#include "plinestyleedit.h" +#include "purledit.h" + + +#ifndef PURE_QT +#include "pfontbutton.h" +#include "pcolorcombo.h" +#endif + +namespace PropertyLib{ + +PropertyMachineFactory *PropertyMachineFactory::m_factory = 0; + +PropertyMachineFactory::PropertyMachineFactory() +{ +} + +PropertyMachineFactory::~PropertyMachineFactory() +{ +} + +Machine *PropertyMachineFactory::machineForProperty(MultiProperty *property) +{ + int type = property->type(); + QString propertyName = property->name(); + QMap<QString, QVariant> valueList = property->valueList(); + + if (m_registeredForType.contains(propertyName)) + return (*m_registeredForType[propertyName])(); + + switch (type) + { + case Property::String: + return new Machine(new PLineEdit(property)); + case Property::Integer: + return new Machine(new PSpinBox(property)); + case Property::Boolean: + return new Machine(new PYesNoButton(property)); + case Property::Date: + return new Machine(new PDateEdit(property)); + case Property::DateTime: + return new Machine(new PDateTimeEdit(property)); + case Property::StringList: + return new Machine(new PStringListEdit(property)); + case Property::Color: + return new Machine(new PColorButton(property)); +#ifndef PURE_QT + case Property::Font: + return new Machine(new PFontButton(property)); +#endif + case Property::FileURL: + return new Machine(new PUrlEdit(PUrlEdit::File, property)); + case Property::DirectoryURL: + return new Machine(new PUrlEdit(PUrlEdit::Directory, property)); + + case Property::Double: + return new Machine(new PDoubleNumInput(property)); + case Property::Pixmap: + return new Machine(new PPixmapEdit(property)); + + case Property::ValueFromList: + return new Machine(new PComboBox(property, valueList)); + case Property::Symbol: + return new Machine(new PSymbolCombo(property)); + case Property::FontName: + return new Machine(new PFontCombo(property)); + case Property::LineStyle: + return new Machine(new PLineStyleEdit(property)); + + case Property::Size: + { + Machine *mach = new Machine(new PSizeEdit(property)); + property->details.append(ChildProperty(property, Property::Integer, ChildProperty::Size_Width, i18n("Width"), i18n("Width"))); + property->details.append(ChildProperty(property, Property::Integer, ChildProperty::Size_Height, i18n("Height"), i18n("Height"))); + return mach; + } + case Property::Point: + { + Machine *mach = new Machine(new PPointEdit(property)); + property->details.append(ChildProperty(property, Property::Integer, ChildProperty::Point_X, i18n("x"), i18n("x"))); + property->details.append(ChildProperty(property, Property::Integer, ChildProperty::Point_Y, i18n("y"), i18n("y"))); + return mach; + } + case Property::Rect: + { + Machine *mach = new Machine(new PRectEdit(property)); + property->details.append(ChildProperty(property, Property::Integer, ChildProperty::Rect_X, i18n("x"), i18n("x"))); + property->details.append(ChildProperty(property, Property::Integer, ChildProperty::Rect_Y, i18n("y"), i18n("y"))); + property->details.append(ChildProperty(property, Property::Integer, ChildProperty::Rect_Width, i18n("Width"), i18n("Width"))); + property->details.append(ChildProperty(property, Property::Integer, ChildProperty::Rect_Height, i18n("Height"), i18n("Height"))); + return mach; + } + case Property::SizePolicy: + { + QMap<QString, QVariant> spValues; + spValues[i18n("Fixed")] = QSizePolicy::Fixed; + spValues[i18n("Minimum")] = QSizePolicy::Minimum; + spValues[i18n("Maximum")] = QSizePolicy::Maximum; + spValues[i18n("Preferred")] = QSizePolicy::Preferred; + spValues[i18n("Expanding")] = QSizePolicy::Expanding; + spValues[i18n("Minimum Expanding")] = QSizePolicy::MinimumExpanding; + spValues[i18n("Ignored")] = QSizePolicy::Ignored; + + Machine *mach = new Machine(new PSizePolicyEdit(property, spValues)); + property->details.append(ChildProperty(property, i18n("hSizeType"), ChildProperty::SizePolicy_HorData, spValues, i18n("Horizontal Size Type"))); + property->details.append(ChildProperty(property, i18n("vSizeType"), ChildProperty::SizePolicy_VerData, spValues, i18n("Vertical Size Type"))); + property->details.append(ChildProperty(property, Property::Integer, ChildProperty::SizePolicy_HorStretch, i18n("hStretch"), i18n("Horizontal Stretch"))); + property->details.append(ChildProperty(property, Property::Integer, ChildProperty::SizePolicy_VerStretch, i18n("vStretch"), i18n("Vertical Stretch"))); + return mach; + } + case Property::Cursor: + { + QMap<QString, QVariant> spValues; + spValues[i18n("Arrow")] = Qt::ArrowCursor; + spValues[i18n("Up Arrow")] = Qt::UpArrowCursor; + spValues[i18n("Cross")] = Qt::CrossCursor; + spValues[i18n("Waiting")] = Qt::WaitCursor; + spValues[i18n("iBeam")] = Qt::IbeamCursor; + spValues[i18n("Size Vertical")] = Qt::SizeVerCursor; + spValues[i18n("Size Horizontal")] = Qt::SizeHorCursor; + spValues[i18n("Size Slash")] = Qt::SizeBDiagCursor; + spValues[i18n("Size Backslash")] = Qt::SizeFDiagCursor; + spValues[i18n("Size All")] = Qt::SizeAllCursor; + spValues[i18n("Blank")] = Qt::BlankCursor; + spValues[i18n("Split Vertical")] = Qt::SplitVCursor; + spValues[i18n("Split Horizontal")] = Qt::SplitHCursor; + spValues[i18n("Pointing Hand")] = Qt::PointingHandCursor; + spValues[i18n("Forbidden")] = Qt::ForbiddenCursor; + spValues[i18n("What's this")] = Qt::WhatsThisCursor; + Machine *mach = new Machine(new PCursorEdit(property, spValues)); + return mach; + } + + case Property::List: + case Property::Map: + default: + return new Machine(new PDummyWidget(property)); + } +} + +PropertyMachineFactory *PropertyMachineFactory::getInstance() +{ + if (m_factory == 0) + m_factory = new PropertyMachineFactory(); + return m_factory; +} + +bool PropertyMachineFactory::hasDetailedEditors( int type ) +{ + if ( (type==Property::Size) || (type==Property::Point) || + (type==Property::Rect) || (type==Property::SizePolicy) ) + return true; + return 0; +} + +} diff --git a/lib/widgets/propeditor/propertymachinefactory.h b/lib/widgets/propeditor/propertymachinefactory.h new file mode 100644 index 00000000..fe776b63 --- /dev/null +++ b/lib/widgets/propeditor/propertymachinefactory.h @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PROPERTYMACHINEFACTORY_H +#define PROPERTYMACHINEFACTORY_H + +#include <qmap.h> + +#include "propertywidget.h" + +class QWidget; + +namespace PropertyLib{ + +class Property; +class MultiProperty; +class ChildProperty; + +/** @file propertymachinefactory.h +@short Contains @ref PropertyLib::PropertyMachineFactory class and @ref PropertyLib::Machine structure. +*/ + +/** +@short Machine for a property type. + +Contains a pointer to a property viewer, +property editor and a list of detailed property +editors and viewers. +*/ +struct Machine{ + Machine() + { + } + Machine(PropertyWidget *widget) + { + propertyEditor = widget; + } + ~Machine() + { + delete propertyEditor; + } + + /**Property viewer and editor widget.*/ + PropertyWidget *propertyEditor; +}; + +/**A pointer to factory function which creates and returns machine for a property.*/ +typedef Machine *(*createMachine)(); + +/** +@short Factory to create property editors and property viewers. +*/ +class PropertyMachineFactory{ +public: + /**Registers property editor factory function for a type. + This factory functions are considered before defaults + when @ref machineForProperty is called.*/ + void registerEditor(int type, createMachine creator); + + /**Creates and returns the editor for given property type. + Warning: editor and viewer widgets won't have parent widget. %Property editor + cares about reparenting and deletion of returned widgets in machines.*/ + Machine *machineForProperty(MultiProperty *property); + bool hasDetailedEditors(int type); + + /**@return a pointer to a property machine factory instance.*/ + static PropertyMachineFactory *getInstance(); + + static PropertyMachineFactory *m_factory; + +private: + PropertyMachineFactory(); + virtual ~PropertyMachineFactory(); + + //registered machines for property types + QMap<QString, createMachine > m_registeredForType; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/propertywidget.cpp b/lib/widgets/propeditor/propertywidget.cpp new file mode 100644 index 00000000..7895acb1 --- /dev/null +++ b/lib/widgets/propeditor/propertywidget.cpp @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "propertywidget.h" + +#include <qpainter.h> + +namespace PropertyLib{ + +PropertyWidget::PropertyWidget(MultiProperty *property, QWidget *parent, const char *name) + :QWidget(parent, name), m_property(property) +{ +} + +QString PropertyWidget::propertyName() const +{ + return m_property->name(); +} + +void PropertyWidget::setProperty(MultiProperty *property) +{ + m_property = property; +} + +void PropertyWidget::drawViewer(QPainter *p, const QColorGroup &cg, const QRect &r, const QVariant &value) +{ + p->setPen(Qt::NoPen); + p->setBrush(cg.background()); + p->drawRect(r); + p->drawText(r, Qt::AlignLeft | Qt::AlignVCenter | Qt::SingleLine, value.toString()); +} + +void PropertyWidget::setValueList(const QMap<QString, QVariant> &// valueList + ) +{ + //this does nothing +} + +void PropertyWidget::undo() +{ + m_property->undo(); +} + +} + +#ifndef PURE_QT +#include "propertywidget.moc" +#endif diff --git a/lib/widgets/propeditor/propertywidget.h b/lib/widgets/propeditor/propertywidget.h new file mode 100644 index 00000000..3383a206 --- /dev/null +++ b/lib/widgets/propeditor/propertywidget.h @@ -0,0 +1,88 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PROPERTYWIDGET_H +#define PROPERTYWIDGET_H + +#include <qwidget.h> +#include <qvariant.h> + +/** @file propertywidget.h +@short Contains @ref PropertyLib::PropertyWidget class. +*/ + +#include "multiproperty.h" + +namespace PropertyLib{ + +/** +@short An abstract base class of property viewer and editor vidget. + +Subclass this class to create custom property viewer and editor widget. + +Descendants should implement value() and setValue() methods. + +Hint: in case you want to implement your property editor widget using +existing widgets like QLineEdit, QComboBox, etc. you can't use multiple +inheritance from two QObject descendants due to Qt library restriction. +Therefore use line edits and combo boxes as child widgets. + +A set of predefined widgets for predefined property types are available +in the library. +*/ +class PropertyWidget: public QWidget{ + Q_OBJECT +public: + /**Constructs widget for property with name "propertyName".*/ + PropertyWidget(MultiProperty *property, QWidget *parent = 0, const char *name = 0); + virtual ~PropertyWidget() {} + + /**@return the value currently entered in the editor widget.*/ + virtual QVariant value() const = 0; + /**Sets the value shown in the editor widget. Set emitChange to false + if you don't want to emit propertyChanged signal.*/ + virtual void setValue(const QVariant &value, bool emitChange=true) = 0; + /**@return the name of edited property.*/ + virtual QString propertyName() const; + /**Sets the name of edited property.*/ + virtual void setProperty(MultiProperty *property); + /**Sets the list of possible values shown in the editor widget. This method + does not emit propertyChanged signal.*/ + virtual void setValueList(const QMap<QString, QVariant> &valueList); + + /**Function to draw a property viewer when the editor isn't shown.*/ + virtual void drawViewer(QPainter *p, const QColorGroup &cg, const QRect &r, const QVariant &value); + + /**Reverts the property value to previous setting.*/ + virtual void undo(); + +signals: + /**Emit this signal when property value is changed. Probably you want + to emit it only from @ref setValue() method. + @ref PropertyLib::PropertyEditor widget will connect this to the appropriate slot which + will make updates to the @ref PropertyLib::PropertyList that hold propeties.*/ + void propertyChanged(MultiProperty *property, const QVariant &value); + +protected: + MultiProperty *m_property; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/propertywidgetproxy.cpp b/lib/widgets/propeditor/propertywidgetproxy.cpp new file mode 100644 index 00000000..35fdc73b --- /dev/null +++ b/lib/widgets/propeditor/propertywidgetproxy.cpp @@ -0,0 +1,106 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "propertywidgetproxy.h" + +#include <qlayout.h> + +#include "propertywidget.h" +#include "propertymachinefactory.h" + +namespace PropertyLib{ + +PropertyWidgetProxy::PropertyWidgetProxy(QWidget *parent, const char *name) + :QWidget(parent, name), mp(0), m_propertyType(Property::Invalid), m_editor(0) +{ + p = new Property(); + m_layout = new QHBoxLayout(this, 0, 0); +} + +PropertyWidgetProxy::~PropertyWidgetProxy() +{ + delete mp; + delete p; +} + +void PropertyWidgetProxy::setPropertyType(int propertyType) +{ + m_propertyType = static_cast<PropertyType>(propertyType); + setWidget(); +} + +void PropertyWidgetProxy::setPropertyType2(PropertyType propertyType) +{ + m_propertyType = propertyType; + setWidget(); +} + +void PropertyWidgetProxy::setWidget() +{ + if (m_editor) + delete m_editor; + p->setType(m_propertyType); + mp = new MultiProperty(p); + m_editor = PropertyMachineFactory::getInstance()->machineForProperty(mp)->propertyEditor; + if (m_editor) + { + m_editor->reparent(this, QPoint(0,0), true); + m_layout->addWidget(m_editor); + } +} + +QVariant PropertyWidgetProxy::value() const +{ + if (m_editor) + return m_editor->value(); + else + return QVariant(); +} + +void PropertyWidgetProxy::setValue(const QVariant &value) +{ + if (m_editor) + m_editor->setValue(value, false); +} + +bool PropertyWidgetProxy::setProperty( const char * name, const QVariant & value ) +{ + if( strcmp( name, "value") == 0 ) + { + setPropertyType((int) value.type() ); + setValue( value ); + return true; + } + else + return QWidget::setProperty(name, value); +} + +QVariant PropertyWidgetProxy::property( const char * name ) const +{ + if( strcmp( name, "value") == 0 ) + return value( ); + else + return QWidget::property(name); +} + +} + +#ifndef PURE_QT +#include "propertywidgetproxy.moc" +#endif diff --git a/lib/widgets/propeditor/propertywidgetproxy.h b/lib/widgets/propeditor/propertywidgetproxy.h new file mode 100644 index 00000000..6a88b8b1 --- /dev/null +++ b/lib/widgets/propeditor/propertywidgetproxy.h @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PROPERTYWIDGETPROXY_H +#define PROPERTYWIDGETPROXY_H + +#include <qwidget.h> +#include <qvariant.h> + +#include "multiproperty.h" + +class QHBoxLayout; + +namespace PropertyLib{ + +class PropertyWidget; + +#define PropertyType Property::PropertyType + +/** +Property Widget Proxy. +It is sometimes useful to create single property editor widgets instead of having them +all in the property editor. Proxy creates an empty widget and shows the property editor +depending on the property type. +*/ +class PropertyWidgetProxy: public QWidget +{ +Q_OBJECT +Q_PROPERTY( int propertyType READ propertyType WRITE setPropertyType DESIGNABLE true ) +Q_PROPERTY( PropertyType propertyType2 READ propertyType2 WRITE setPropertyType2 DESIGNABLE false ) +public: + PropertyWidgetProxy(QWidget *parent = 0, const char *name = 0); + ~PropertyWidgetProxy(); + + /**Sets the type of a property editor to appear.*/ + void setPropertyType(int propertyType); + int propertyType() const { return m_propertyType; } + /**Sets the type of a property editor to appear.*/ + void setPropertyType2(PropertyType propertyType); + PropertyType propertyType2() const { return m_propertyType; } + + QVariant value() const; + void setValue(const QVariant &value); + + /**Sets the type of an editor basing on the @p value if the name is "value". + Otherwise works as QWidget::setProperty.*/ + bool setProperty( const char *name, const QVariant &value); + QVariant property( const char *name) const; + +protected: + virtual void setWidget(); + +private: + Property *p; + MultiProperty *mp; + + PropertyType m_propertyType; + PropertyWidget *m_editor; + + QHBoxLayout *m_layout; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/psizeedit.cpp b/lib/widgets/propeditor/psizeedit.cpp new file mode 100644 index 00000000..7e322ba5 --- /dev/null +++ b/lib/widgets/propeditor/psizeedit.cpp @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "psizeedit.h" + +#include <klineedit.h> +#include <qlayout.h> +#include <qpainter.h> + +namespace PropertyLib{ + +PSizeEdit::PSizeEdit(MultiProperty *property, QWidget *parent, const char *name) + :PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); + m_edit = new KLineEdit(this); + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + l->addWidget(m_edit); + + m_edit->setReadOnly(true); +} + +QVariant PSizeEdit::value() const +{ + return m_value; +} + +void PSizeEdit::drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value) +{ + p->setPen(Qt::NoPen); + p->setBrush(cg.background()); + p->drawRect(r); + p->drawText(r, Qt::AlignLeft | Qt::AlignVCenter | Qt::SingleLine, QString("[ %1, %2 ]").arg(value.toSize().width()).arg(value.toSize().height())); +} + +void PSizeEdit::setValue(const QVariant& value, bool emitChange) +{ + m_value = value; + m_edit->setText(QString("[ %1, %2 ]").arg(value.toSize().width()).arg(value.toSize().height())); + + if (emitChange) + emit propertyChanged(m_property, value); +} + +} + +#ifndef PURE_QT +#include "psizeedit.moc" +#endif diff --git a/lib/widgets/propeditor/psizeedit.h b/lib/widgets/propeditor/psizeedit.h new file mode 100644 index 00000000..53ea5201 --- /dev/null +++ b/lib/widgets/propeditor/psizeedit.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PSIZEEDIT_H +#define PSIZEEDIT_H + +#include "propertywidget.h" +#include "multiproperty.h" + +class KLineEdit; +class QPainter; + +namespace PropertyLib{ + +/** +@short %Property editor for QSize values. +*/ +class PSizeEdit: public PropertyWidget +{ + Q_OBJECT +public: + PSizeEdit(MultiProperty *property, QWidget *parent = 0, const char *name = 0); + + virtual QVariant value() const; + virtual void drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value); + virtual void setValue(const QVariant& value, bool emitChange); + +private: + KLineEdit *m_edit; + QVariant m_value; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/psizepolicyedit.cpp b/lib/widgets/propeditor/psizepolicyedit.cpp new file mode 100644 index 00000000..d903c95a --- /dev/null +++ b/lib/widgets/propeditor/psizepolicyedit.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "psizepolicyedit.h" + +#include <klineedit.h> +#include <qlayout.h> +#include <qpainter.h> +#include <qsizepolicy.h> + +namespace PropertyLib{ + +PSizePolicyEdit::PSizePolicyEdit(MultiProperty* property, const QMap<QString, QVariant> &spValues, QWidget* parent, const char* name) + :PropertyWidget(property, parent, name), m_spValues(spValues) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); + m_edit = new KLineEdit(this); + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + l->addWidget(m_edit); + + m_edit->setReadOnly(true); +} + +QVariant PSizePolicyEdit::value() const +{ + return m_value; +} + +void PSizePolicyEdit::drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value) +{ + p->setPen(Qt::NoPen); + p->setBrush(cg.background()); + p->drawRect(r); + p->drawText(r, Qt::AlignLeft | Qt::AlignVCenter | Qt::SingleLine, QString("%1/%2/%3/%4").arg(findValueDescription(value.toSizePolicy().horData())).arg(findValueDescription(value.toSizePolicy().verData())).arg(value.toSizePolicy().horStretch()).arg(value.toSizePolicy().verStretch())); +} + +void PSizePolicyEdit::setValue(const QVariant& value, bool emitChange) +{ + m_value = value; + m_edit->setText(QString("%1/%2/%3/%4").arg(findValueDescription(value.toSizePolicy().horData())).arg(findValueDescription(value.toSizePolicy().verData())).arg(value.toSizePolicy().horStretch()).arg(value.toSizePolicy().verStretch())); + + if (emitChange) + emit propertyChanged(m_property, value); +} + +QString PSizePolicyEdit::findValueDescription(QVariant val) const +{ +// qWarning("PSizePolicyEdit::findValueDescription : %d", val.toInt()); + for (QMap<QString, QVariant>::const_iterator it = m_spValues.begin(); it != m_spValues.end(); ++ it) + { + if (it.data() == val) + return it.key(); + } + return ""; +} + +} + +#ifndef PURE_QT +#include "psizepolicyedit.moc" +#endif diff --git a/lib/widgets/propeditor/psizepolicyedit.h b/lib/widgets/propeditor/psizepolicyedit.h new file mode 100644 index 00000000..42651eac --- /dev/null +++ b/lib/widgets/propeditor/psizepolicyedit.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PSIZEPOLICYEDIT_H +#define PSIZEPOLICYEDIT_H + +#include "propertywidget.h" + +#include <qmap.h> + +class KLineEdit; + +namespace PropertyLib{ + +/** +@short %Property editor for QSizePolicy values. +*/ +class PSizePolicyEdit : public PropertyWidget +{ +Q_OBJECT +public: + PSizePolicyEdit(MultiProperty* property, const QMap<QString, QVariant> &spValues, QWidget* parent=0, const char* name=0); + + virtual QVariant value() const; + virtual void drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value); + virtual void setValue(const QVariant& value, bool emitChange); + + QString findValueDescription(QVariant val) const; + +private: + KLineEdit *m_edit; + QVariant m_value; + QMap<QString, QVariant> m_spValues; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/pspinbox.cpp b/lib/widgets/propeditor/pspinbox.cpp new file mode 100644 index 00000000..2e83f66c --- /dev/null +++ b/lib/widgets/propeditor/pspinbox.cpp @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "pspinbox.h" + +#include <limits.h> + +#include <qspinbox.h> +#include <qlayout.h> + +namespace PropertyLib{ + +PSpinBox::PSpinBox(MultiProperty *property, QWidget *parent, const char *name) + :PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); + m_edit = new QSpinBox(INT_MIN, INT_MAX, 1, this); + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + l->addWidget(m_edit); + + connect(m_edit, SIGNAL(valueChanged(int)), this, SLOT(updateProperty(int))); +} + +PSpinBox::PSpinBox(MultiProperty *property, int minValue, int maxValue, int step, QWidget *parent, const char *name) + :PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); + m_edit = new QSpinBox(minValue, maxValue, step, this); + l->addWidget(m_edit); + + connect(m_edit, SIGNAL(valueChanged(int)), this, SLOT(updateProperty(int))); +} + +QVariant PSpinBox::value() const +{ + return QVariant(m_edit->cleanText().toInt()); +} + +void PSpinBox::setValue(const QVariant &value, bool emitChange) +{ + disconnect(m_edit, SIGNAL(valueChanged(int)), this, SLOT(updateProperty(int))); + m_edit->setValue(value.toInt()); + connect(m_edit, SIGNAL(valueChanged(int)), this, SLOT(updateProperty(int))); + if (emitChange) + emit propertyChanged(m_property, value); +} + +void PSpinBox::updateProperty(int val) +{ + emit propertyChanged(m_property, QVariant(val)); +} + +} + +#ifndef PURE_QT +#include "pspinbox.moc" +#endif diff --git a/lib/widgets/propeditor/pspinbox.h b/lib/widgets/propeditor/pspinbox.h new file mode 100644 index 00000000..1ca839b8 --- /dev/null +++ b/lib/widgets/propeditor/pspinbox.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PSPINBOX_H +#define PSPINBOX_H + +#include "propertywidget.h" + +class QSpinBox; + +namespace PropertyLib{ + +/** +@short %Property editor with integer num input box. +*/ +class PSpinBox: public PropertyWidget{ + Q_OBJECT +public: + PSpinBox(MultiProperty *property, QWidget *parent = 0, const char *name = 0); + PSpinBox(MultiProperty *property, int minValue, int maxValue, int step = 1, QWidget *parent = 0, const char *name = 0); + + /**@return the value currently entered in the editor widget.*/ + virtual QVariant value() const; + /**Sets the value shown in the editor widget. Set emitChange to false + if you don't want to emit propertyChanged signal.*/ + virtual void setValue(const QVariant &value, bool emitChange=true); + +private slots: + void updateProperty(int val); + +private: + QSpinBox *m_edit; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/pstringlistedit.cpp b/lib/widgets/propeditor/pstringlistedit.cpp new file mode 100644 index 00000000..227050f9 --- /dev/null +++ b/lib/widgets/propeditor/pstringlistedit.cpp @@ -0,0 +1,121 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "pstringlistedit.h" + +#include <qlayout.h> +#include <qdialog.h> +#include <qpainter.h> +#include <klineedit.h> + +#ifndef PURE_QT +#include <keditlistbox.h> +#include <kpushbutton.h> +#include <kstdguiitem.h> +#else +#include "qeditlistbox.h" +#include <qpushbutton.h> +#include "compat_tools.h" +#endif + +namespace PropertyLib{ + +PStringListEdit::PStringListEdit(MultiProperty *property, QWidget *parent, const char *name) + :PropertyWidget(property, parent, name) +{ + l = new QHBoxLayout(this); + + edit = new KLineEdit(this); + edit->setReadOnly(true); + edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + l->addWidget(edit); + pbSelect = new QPushButton("...", this); + pbSelect->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding); + l->addWidget(pbSelect); + + connect(pbSelect, SIGNAL(clicked()), this, SLOT(showEditor())); +} + +QVariant PStringListEdit::value() const +{ + return QVariant(m_list); +} + +void PStringListEdit::setValue(const QVariant &value, bool emitChange) +{ + m_list = value.toStringList(); + edit->setText(value.toStringList().join(", ")); + if (emitChange) + emit propertyChanged(m_property, value); +} + +void PStringListEdit::showEditor() +{ + QDialog* dia = new QDialog(this, "stringlist_dialog", true); + QVBoxLayout *dv = new QVBoxLayout(dia, 2); + +#ifdef PURE_QT + QEditListBox *select = new QEditListBox(dia, "select_char"); +#else + KEditListBox *select = new KEditListBox(dia, "select_char"); +#endif + dv->addWidget(select); + + QHBoxLayout *dh = new QHBoxLayout(dv, 6); +#ifndef PURE_QT + KPushButton *pbOk = new KPushButton(KStdGuiItem::ok(), dia); + KPushButton *pbCancel = new KPushButton(KStdGuiItem::cancel(), dia); +#else + QPushButton *pbOk = new QPushButton(i18n("Ok"), dia); + QPushButton *pbCancel = new QPushButton(i18n("Cancel"), dia); +#endif + QSpacerItem *si = new QSpacerItem(30, 0, QSizePolicy::Expanding, QSizePolicy::Expanding); + + connect(pbOk, SIGNAL(clicked()), dia, SLOT(accept())); + connect(pbCancel, SIGNAL(clicked()), dia, SLOT(reject())); + + dh->addItem(si); + dh->addWidget(pbOk); + dh->addWidget(pbCancel); + + select->insertStringList(m_list); + + if (dia->exec() == QDialog::Accepted) + { + m_list = select->items(); + edit->setText(select->items().join(", ")); + } + delete dia; + + emit propertyChanged(m_property, m_list); +} + +void PStringListEdit::drawViewer(QPainter *p, const QColorGroup &cg, const QRect &r, const QVariant &value) +{ + p->setPen(Qt::NoPen); + p->setBrush(cg.background()); + p->drawRect(r); + p->drawText(r, Qt::AlignLeft | Qt::AlignVCenter | Qt::SingleLine, value.toStringList().join(", ")); +} + +} + +#ifndef PURE_QT +#include "pstringlistedit.moc" +#endif diff --git a/lib/widgets/propeditor/pstringlistedit.h b/lib/widgets/propeditor/pstringlistedit.h new file mode 100644 index 00000000..25d6db19 --- /dev/null +++ b/lib/widgets/propeditor/pstringlistedit.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PSTRINGLISTEDIT_H +#define PSTRINGLISTEDIT_H + +#include "propertywidget.h" + + +class KLineEdit; +class QPushButton; +class QHBoxLayout; + +namespace PropertyLib{ + +/** +@short %Property editor with string list editor. +*/ +class PStringListEdit: public PropertyWidget +{ + Q_OBJECT +public: + PStringListEdit(MultiProperty *property, QWidget *parent = 0, const char *name = 0); + + /**@return the value currently entered in the editor widget.*/ + virtual QVariant value() const; + /**Sets the value shown in the editor widget. Set emitChange to false + if you don't want to emit propertyChanged signal.*/ + virtual void setValue(const QVariant &value, bool emitChange=true); + /**Function to draw a property viewer when the editor isn't shown.*/ + virtual void drawViewer(QPainter *p, const QColorGroup &cg, const QRect &r, const QVariant &value); + +private slots: + void showEditor(); + +private: + QPushButton *pbSelect; + QHBoxLayout *l; + KLineEdit *edit; + + QStringList m_list; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/psymbolcombo.cpp b/lib/widgets/propeditor/psymbolcombo.cpp new file mode 100644 index 00000000..70dd7ba9 --- /dev/null +++ b/lib/widgets/propeditor/psymbolcombo.cpp @@ -0,0 +1,131 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include <qlayout.h> +#include <qpainter.h> +#include <qpushbutton.h> +#include <klineedit.h> + +#ifndef PURE_QT +#include <kcharselect.h> +#include <klocale.h> +#include <kpushbutton.h> +#include <kstdguiitem.h> +#include <qdialog.h> +#endif + +#include "psymbolcombo.h" + +namespace PropertyLib{ + +PSymbolCombo::PSymbolCombo(MultiProperty *property, QWidget *parent, const char *name) + :PropertyWidget(property, parent, name) +{ + l = new QHBoxLayout(this); + m_edit = new KLineEdit(this); + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + m_edit->setMaxLength(1); + l->addWidget(m_edit); + m_select = new QPushButton("...", this); + m_select->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding); + l->addWidget(m_select); + +#ifdef PURE_QT + m_select->hide(); +#endif + + connect(m_select, SIGNAL(clicked()), this, SLOT(selectChar())); + connect(m_edit, SIGNAL(textChanged(const QString&)), this, SLOT(updateProperty(const QString&))); +} + +QVariant PSymbolCombo::value() const +{ + if (!(m_edit->text().isNull())) + return QVariant(QString("%1").arg(m_edit->text().at(0).unicode())); + else + return QVariant(0); +} + +void PSymbolCombo::setValue(const QVariant &value, bool emitChange) +{ +#if QT_VERSION >= 0x030100 + if (!(value.isNull())) +#else + if (value.canCast(QVariant::Int)) +#endif + { + disconnect(m_edit, SIGNAL(textChanged(const QString&)), this, SLOT(updateProperty(const QString&))); + m_edit->setText(QChar(value.toInt())); + connect(m_edit, SIGNAL(textChanged(const QString&)), this, SLOT(updateProperty(const QString&))); + if (emitChange) + emit propertyChanged(m_property, value); + } +} + +void PSymbolCombo::selectChar() +{ +#ifndef PURE_QT + QDialog* dia = new QDialog(this, "select_dialog", true); + QVBoxLayout *dv = new QVBoxLayout(dia, 2); + + KCharSelect *select = new KCharSelect(dia, "select_char"); + dv->addWidget(select); + + QHBoxLayout *dh = new QHBoxLayout(dv, 6); + KPushButton *pbOk = new KPushButton(KStdGuiItem::ok(), dia); + KPushButton *pbCancel = new KPushButton(KStdGuiItem::cancel(), dia); + QSpacerItem *si = new QSpacerItem(30, 0, QSizePolicy::Expanding, QSizePolicy::Expanding); + + connect(pbOk, SIGNAL(clicked()), dia, SLOT(accept())); + connect(pbCancel, SIGNAL(clicked()), dia, SLOT(reject())); + + dh->addItem(si); + dh->addWidget(pbOk); + dh->addWidget(pbCancel); + + if (!(m_edit->text().isNull())) + select->setChar(m_edit->text().at(0)); + + if (dia->exec() == QDialog::Accepted) + { + m_edit->setText(select->chr()); + } + delete dia; +#endif +} + +void PSymbolCombo::updateProperty(const QString& val) +{ + emit propertyChanged(m_property, QVariant(QString("%1").arg(val.at(0).unicode()))); +} + +void PSymbolCombo::drawViewer(QPainter *p, const QColorGroup &cg, const QRect &r, const QVariant &value) +{ + p->setBrush(cg.background()); + p->setPen(Qt::NoPen); + p->drawRect(r); + p->drawText(r, Qt::AlignLeft | Qt::AlignVCenter | Qt::SingleLine, QChar(value.toInt())); +} + +} + +#ifndef PURE_QT +#include "psymbolcombo.moc" +#endif diff --git a/lib/widgets/propeditor/psymbolcombo.h b/lib/widgets/propeditor/psymbolcombo.h new file mode 100644 index 00000000..fd6e57ec --- /dev/null +++ b/lib/widgets/propeditor/psymbolcombo.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2002-2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PSYMBOLCOMBO_H +#define PSYMBOLCOMBO_H + +#include "propertywidget.h" + +class KLineEdit; +class QPushButton; +class QHBoxLayout; + +namespace PropertyLib{ + + +/** +@short %Property editor with char selector. +*/ +class PSymbolCombo: public PropertyWidget{ + Q_OBJECT +public: + PSymbolCombo(MultiProperty *property, QWidget *parent = 0, const char *name = 0); + + /**@return the value currently entered in the editor widget.*/ + virtual QVariant value() const; + /**Sets the value shown in the editor widget. Set emitChange to false + if you don't want to emit propertyChanged signal.*/ + virtual void setValue(const QVariant &value, bool emitChange=true); + /**Function to draw a property viewer when the editor isn't shown.*/ + virtual void drawViewer(QPainter *p, const QColorGroup &cg, const QRect &r, const QVariant &value); + +public slots: + void selectChar(); + +private slots: + void updateProperty(const QString &val); + +private: + KLineEdit *m_edit; + QPushButton *m_select; + QHBoxLayout *l; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/purledit.cpp b/lib/widgets/propeditor/purledit.cpp new file mode 100644 index 00000000..9d90273a --- /dev/null +++ b/lib/widgets/propeditor/purledit.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "purledit.h" + +#ifndef PURE_QT +#include <kurlrequester.h> +#else +#include <qpushbutton.h> +#include <qlineedit.h> +#endif +#include <qfiledialog.h> +#include <qlayout.h> + +namespace PropertyLib{ + +PUrlEdit::PUrlEdit(Mode mode, MultiProperty* property, QWidget* parent, const char* name) + :PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); +#ifndef PURE_QT + m_edit = new KURLRequester(this); + l->addWidget(m_edit); + m_edit->setMode((KFile::Mode)mode); + connect(m_edit, SIGNAL(textChanged(const QString&)), this, SLOT(updateProperty(const QString&))); +#else + m_edit = new KLineEdit(this); + m_select = new QPushButton("...",this); + l->addWidget(m_edit); + l->addWidget(m_select); + m_mode = mode; + connect( m_select, SIGNAL(clicked()),this,SLOT(select())); +#endif + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); +} + +QVariant PUrlEdit::value() const +{ +#ifndef PURE_QT + return QVariant(m_edit->url()); +#else + return QVariant(m_url); +#endif +} + +void PUrlEdit::setValue(const QVariant& value, bool emitChange) +{ +#ifndef PURE_QT + disconnect(m_edit, SIGNAL(textChanged(const QString&)), this, SLOT(updateProperty(const QString&))); + m_edit->setURL(value.toString()); + connect(m_edit, SIGNAL(textChanged(const QString&)), this, SLOT(updateProperty(const QString&))); +#else + m_edit->setText(value.toString()); +#endif + if (emitChange) + emit propertyChanged(m_property, value); +} + +void PUrlEdit::updateProperty(const QString &val) +{ + emit propertyChanged(m_property, QVariant(val)); +} + +void PUrlEdit::select() +{ +#ifdef PURE_QT + QString path = m_url; + if( m_mode == Directory ) + m_url = QFileDialog::getExistingDirectory( m_url,this); + else + m_url = QFileDialog::getOpenFileName(m_url, QString::null, this); + updateProperty(m_url); + m_edit->setText(m_url); +#endif +} + +} + +#ifndef PURE_QT +#include "purledit.moc" +#endif diff --git a/lib/widgets/propeditor/purledit.h b/lib/widgets/propeditor/purledit.h new file mode 100644 index 00000000..5da0108d --- /dev/null +++ b/lib/widgets/propeditor/purledit.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PURLEDIT_H +#define PURLEDIT_H + +#include "propertywidget.h" + +#ifndef PURE_QT +#include <kfile.h> +class KURLRequester; +#else +#include <klineedit.h> +class QPushButton; +#endif + + +namespace PropertyLib{ + +/** +@short %Property editor with an url editor to choose the location of file or directory. +*/ +class PUrlEdit : public PropertyWidget +{ +Q_OBJECT +public: +#ifndef PURE_QT + enum Mode {File = KFile::File,Directory = KFile::Directory}; +#else + enum Mode {File,Directory}; +#endif + + PUrlEdit(Mode mode, MultiProperty* property, QWidget* parent=0, const char* name=0); + + virtual QVariant value() const; + virtual void setValue(const QVariant& value, bool emitChange); + +private slots: + void updateProperty(const QString &val); + void select(); +private: +#ifndef PURE_QT + KURLRequester *m_edit; +#else + KLineEdit *m_edit; + QPushButton *m_select; + QString m_url; + Mode m_mode; +#endif +}; + +} + +#endif diff --git a/lib/widgets/propeditor/pyesnobutton.cpp b/lib/widgets/propeditor/pyesnobutton.cpp new file mode 100644 index 00000000..c27f4a60 --- /dev/null +++ b/lib/widgets/propeditor/pyesnobutton.cpp @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "pyesnobutton.h" + +#include <qlayout.h> +#include <qpainter.h> +#include <qpushbutton.h> + +#ifndef PURE_QT +#include <klocale.h> +#else +#include "compat_tools.h" +#endif + +namespace PropertyLib{ + +PYesNoButton::PYesNoButton(MultiProperty* property, QWidget* parent, const char* name) + :PropertyWidget(property, parent, name) +{ + QHBoxLayout *l = new QHBoxLayout(this, 0, 0); + m_edit = new QPushButton(this); + m_edit->setToggleButton(true); + m_edit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + l->addWidget(m_edit); + + connect(m_edit, SIGNAL(toggled(bool)), this, SLOT(updateProperty(bool))); +} + +QVariant PYesNoButton::value() const +{ + return QVariant(m_edit->isOn()); +} + +void PYesNoButton::drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value) +{ + PropertyWidget::drawViewer(p, cg, r, value.toBool() ? i18n("Yes") : i18n("No")); +} + +void PYesNoButton::setValue(const QVariant& value, bool emitChange) +{ + disconnect(m_edit, SIGNAL(toggled(bool)), this, SLOT(updateProperty(bool))); + m_edit->setDown(value.toBool()); + value.toBool() ? m_edit->setText(i18n("Yes")) : m_edit->setText(i18n("No")); + connect(m_edit, SIGNAL(toggled(bool)), this, SLOT(updateProperty(bool))); + if (emitChange) + emit propertyChanged(m_property, value); +} + +void PYesNoButton::updateProperty(bool toggled) +{ + toggled ? m_edit->setText(i18n("Yes")) : m_edit->setText(i18n("No")); + emit propertyChanged(m_property, value()); +} + +} + +#ifndef PURE_QT +#include "pyesnobutton.moc" +#endif diff --git a/lib/widgets/propeditor/pyesnobutton.h b/lib/widgets/propeditor/pyesnobutton.h new file mode 100644 index 00000000..f0596a8f --- /dev/null +++ b/lib/widgets/propeditor/pyesnobutton.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2004 by Alexander Dymo * + * cloudtemple@mskat.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef PYESNOBUTTON_H +#define PYESNOBUTTON_H + +#include <propertywidget.h> + +class QPushButton; + +namespace PropertyLib{ + +/** +@short %Property editor with yes-no button to edit boolean values. +*/ +class PYesNoButton : public PropertyWidget +{ + Q_OBJECT +public: + PYesNoButton(MultiProperty* property, QWidget* parent = 0, const char* name = 0); + + virtual QVariant value() const; + virtual void drawViewer(QPainter* p, const QColorGroup& cg, const QRect& r, const QVariant& value); + virtual void setValue(const QVariant& value, bool emitChange); + +protected slots: + void updateProperty(bool toggled); + +private: + QPushButton *m_edit; +}; + +} + +#endif diff --git a/lib/widgets/propeditor/qeditlistbox.cpp b/lib/widgets/propeditor/qeditlistbox.cpp new file mode 100644 index 00000000..e9b17d54 --- /dev/null +++ b/lib/widgets/propeditor/qeditlistbox.cpp @@ -0,0 +1,401 @@ +/* This file is part of the KDE libraries + Copyright (C) 2000 David Faure <faure@kde.org>, Alexander Neundorf <neundorf@kde.org> + 2000, 2002 Carsten Pfeiffer <pfeiffer@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "qeditlistbox.h" + +#include "compat_tools.h" + +#include <klineedit.h> + +#include <qpushbutton.h> +#include <qlayout.h> +#include <qgroupbox.h> +#include <qlistbox.h> +#include <qwhatsthis.h> +#include <qlabel.h> +#include <qcombobox.h> +#include <qapplication.h> +#include <qstringlist.h> + +#include <assert.h> + +//same as kdialog.cpp ones +#define MarginSize 11 +#define SpazingSize 6 + +class QEditListBoxPrivate +{ +public: + bool m_checkAtEntering; + int buttons; +}; + +QEditListBox::QEditListBox(QWidget *parent, const char *name, + bool checkAtEntering, int buttons ) + :QGroupBox(parent, name ) +{ + init( checkAtEntering, buttons ); +} + +QEditListBox::QEditListBox(const QString& title, QWidget *parent, + const char *name, bool checkAtEntering, int buttons) + :QGroupBox(title, parent, name ) +{ + init( checkAtEntering, buttons ); +} + +QEditListBox::QEditListBox(const QString& title, const CustomEditor& custom, + QWidget *parent, const char *name, + bool checkAtEntering, int buttons) + :QGroupBox(title, parent, name ) +{ + m_lineEdit = custom.lineEdit(); + init( checkAtEntering, buttons, custom.representationWidget() ); +} + +QEditListBox::~QEditListBox() +{ + delete d; + d=0; +} + +void QEditListBox::init( bool checkAtEntering, int buttons, + QWidget *representationWidget ) +{ + d=new QEditListBoxPrivate; + d->m_checkAtEntering=checkAtEntering; + d->buttons = buttons; + + int lostButtons = 0; + if ( (buttons & Add) == 0 ) + lostButtons++; + if ( (buttons & Remove) == 0 ) + lostButtons++; + if ( (buttons & UpDown) == 0 ) + lostButtons += 2; + + + servNewButton = servRemoveButton = servUpButton = servDownButton = 0L; + setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, + QSizePolicy::MinimumExpanding)); + + QWidget * gb = this; + QGridLayout * grid = new QGridLayout(gb, 7 - lostButtons, 2, + MarginSize, + SpazingSize); + grid->addRowSpacing(0, fontMetrics().lineSpacing()); + for ( int i = 1; i < 7 - lostButtons; i++ ) + grid->setRowStretch(i, 1); + + grid->setMargin(15); + + if ( representationWidget ) + representationWidget->reparent( gb, QPoint(0,0) ); + else + m_lineEdit=new KLineEdit(gb); + + m_listBox = new QListBox(gb); + + QWidget *editingWidget = representationWidget ? + representationWidget : m_lineEdit; + grid->addMultiCellWidget(editingWidget,1,1,0,1); + grid->addMultiCellWidget(m_listBox, 2, 6 - lostButtons, 0, 0); + int row = 2; + if ( buttons & Add ) { + servNewButton = new QPushButton(i18n("&Add"), gb); + servNewButton->setEnabled(false); + connect(servNewButton, SIGNAL(clicked()), SLOT(addItem())); + + grid->addWidget(servNewButton, row++, 1); + } + + if ( buttons & Remove ) { + servRemoveButton = new QPushButton(i18n("&Remove"), gb); + servRemoveButton->setEnabled(false); + connect(servRemoveButton, SIGNAL(clicked()), SLOT(removeItem())); + + grid->addWidget(servRemoveButton, row++, 1); + } + + if ( buttons & UpDown ) { + servUpButton = new QPushButton(i18n("Move &Up"), gb); + servUpButton->setEnabled(false); + connect(servUpButton, SIGNAL(clicked()), SLOT(moveItemUp())); + + servDownButton = new QPushButton(i18n("Move &Down"), gb); + servDownButton->setEnabled(false); + connect(servDownButton, SIGNAL(clicked()), SLOT(moveItemDown())); + + grid->addWidget(servUpButton, row++, 1); + grid->addWidget(servDownButton, row++, 1); + } + + connect(m_lineEdit,SIGNAL(textChanged(const QString&)),this,SLOT(typedSomething(const QString&))); + + connect(m_lineEdit,SIGNAL(returnPressed()),this,SLOT(addItem())); + connect(m_listBox, SIGNAL(highlighted(int)), SLOT(enableMoveButtons(int))); + + // maybe supplied lineedit has some text already + typedSomething( m_lineEdit->text() ); +} + +void QEditListBox::typedSomething(const QString& text) +{ + if(currentItem() >= 0) { + if(currentText() != m_lineEdit->text()) + { + // IMHO changeItem() shouldn't do anything with the value + // of currentItem() ... like changing it or emitting signals ... + // but TT disagree with me on this one (it's been that way since ages ... grrr) + bool block = m_listBox->signalsBlocked(); + m_listBox->blockSignals( true ); + m_listBox->changeItem(text, currentItem()); + m_listBox->blockSignals( block ); + emit changed(); + } + } + + if ( !servNewButton ) + return; + + if (!d->m_checkAtEntering) + servNewButton->setEnabled(!text.isEmpty()); + else + { + if (text.isEmpty()) + { + servNewButton->setEnabled(false); + } + else + { + StringComparisonMode mode = (StringComparisonMode) (ExactMatch | CaseSensitive ); + bool enable = (m_listBox->findItem( text, mode ) == 0L); + servNewButton->setEnabled( enable ); + } + } +} + +void QEditListBox::moveItemUp() +{ + if (!m_listBox->isEnabled()) + { + qDebug("beep"); + return; + } + + unsigned int selIndex = m_listBox->currentItem(); + if (selIndex == 0) + { + qDebug("beep"); + return; + } + + QListBoxItem *selItem = m_listBox->item(selIndex); + m_listBox->takeItem(selItem); + m_listBox->insertItem(selItem, selIndex-1); + m_listBox->setCurrentItem(selIndex - 1); + + emit changed(); +} + +void QEditListBox::moveItemDown() +{ + if (!m_listBox->isEnabled()) + { + qDebug("beep"); + return; + } + + unsigned int selIndex = m_listBox->currentItem(); + if (selIndex == m_listBox->count() - 1) + { + qDebug("beep"); + return; + } + + QListBoxItem *selItem = m_listBox->item(selIndex); + m_listBox->takeItem(selItem); + m_listBox->insertItem(selItem, selIndex+1); + m_listBox->setCurrentItem(selIndex + 1); + + emit changed(); +} + +void QEditListBox::addItem() +{ + // when m_checkAtEntering is true, the add-button is disabled, but this + // slot can still be called through Key_Return/Key_Enter. So we guard + // against this. + if ( !servNewButton || !servNewButton->isEnabled() ) + return; + + const QString& currentTextLE=m_lineEdit->text(); + bool alreadyInList(false); + //if we didn't check for dupes at the inserting we have to do it now + if (!d->m_checkAtEntering) + { + // first check current item instead of dumb iterating the entire list + if ( m_listBox->currentText() == currentTextLE ) + alreadyInList = true; + else + { + StringComparisonMode mode = (StringComparisonMode) (ExactMatch | CaseSensitive ); + alreadyInList =(m_listBox->findItem(currentTextLE, mode) != 0); + } + } + + if ( servNewButton ) + servNewButton->setEnabled(false); + + bool block = m_lineEdit->signalsBlocked(); + m_lineEdit->blockSignals(true); + m_lineEdit->clear(); + m_lineEdit->blockSignals(block); + + m_listBox->setSelected(currentItem(), false); + + if (!alreadyInList) + { + block = m_listBox->signalsBlocked(); + m_listBox->blockSignals( true ); + m_listBox->insertItem(currentTextLE); + m_listBox->blockSignals( block ); + emit changed(); + emit added( currentTextLE ); + } +} + +int QEditListBox::currentItem() const +{ + int nr = m_listBox->currentItem(); + if(nr >= 0 && !m_listBox->item(nr)->isSelected()) return -1; + return nr; +} + +void QEditListBox::removeItem() +{ + int selected = m_listBox->currentItem(); + + if ( selected >= 0 ) + { + QString removedText = m_listBox->currentText(); + + m_listBox->removeItem( selected ); + if ( count() > 0 ) + m_listBox->setSelected( QMIN( selected, count() - 1 ), true ); + + emit changed(); + emit removed( removedText ); + } + + if ( servRemoveButton && m_listBox->currentItem() == -1 ) + servRemoveButton->setEnabled(false); +} + +void QEditListBox::enableMoveButtons(int index) +{ + // Update the lineEdit when we select a different line. + if(currentText() != m_lineEdit->text()) + m_lineEdit->setText(currentText()); + + bool moveEnabled = servUpButton && servDownButton; + + if (moveEnabled ) + { + if (m_listBox->count() <= 1) + { + servUpButton->setEnabled(false); + servDownButton->setEnabled(false); + } + else if ((uint) index == (m_listBox->count() - 1)) + { + servUpButton->setEnabled(true); + servDownButton->setEnabled(false); + } + else if (index == 0) + { + servUpButton->setEnabled(false); + servDownButton->setEnabled(true); + } + else + { + servUpButton->setEnabled(true); + servDownButton->setEnabled(true); + } + } + + if ( servRemoveButton ) + servRemoveButton->setEnabled(true); +} + +void QEditListBox::clear() +{ + m_lineEdit->clear(); + m_listBox->clear(); + emit changed(); +} + +void QEditListBox::insertStringList(const QStringList& list, int index) +{ + m_listBox->insertStringList(list,index); +} + +void QEditListBox::insertStrList(const QStrList* list, int index) +{ + m_listBox->insertStrList(list,index); +} + +void QEditListBox::insertStrList(const QStrList& list, int index) +{ + m_listBox->insertStrList(list,index); +} + +void QEditListBox::insertStrList(const char ** list, int numStrings, int index) +{ + m_listBox->insertStrList(list,numStrings,index); +} + +QStringList QEditListBox::items() const +{ + QStringList list; + for ( uint i = 0; i < m_listBox->count(); i++ ) + list.append( m_listBox->text( i )); + + return list; +} + +void QEditListBox::setItems(const QStringList& items) +{ + m_listBox->clear(); + m_listBox->insertStringList(items, 0); +} + +void QEditListBox::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// + +QEditListBox::CustomEditor::CustomEditor( QComboBox *combo ) +{ + m_representationWidget = combo; + m_lineEdit = dynamic_cast<KLineEdit*>( combo->lineEdit() ); + assert( m_lineEdit ); +} diff --git a/lib/widgets/propeditor/qeditlistbox.h b/lib/widgets/propeditor/qeditlistbox.h new file mode 100644 index 00000000..0b95a919 --- /dev/null +++ b/lib/widgets/propeditor/qeditlistbox.h @@ -0,0 +1,255 @@ +/* This file is part of the KDE libraries + Copyright (C) 2000 David Faure <faure@kde.org>, Alexander Neundorf <neundorf@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef QEDITLISTBOX_H +#define QEDITLISTBOX_H + +#include <qgroupbox.h> +#include <qlistbox.h> + +class KLineEdit; +class QComboBox; +class QPushButton; + +/** + * An editable listbox + * + * This class provides a editable listbox ;-), this means + * a listbox which is accompanied by a line edit to enter new + * items into the listbox and pushbuttons to add and remove + * items from the listbox and two buttons to move items up and down. + * + * \image html keditlistbox.png "KDE Edit List Box Widget" + * + */ + + +class QEditListBoxPrivate; + +class QEditListBox : public QGroupBox +{ + Q_OBJECT + + Q_PROPERTY( QStringList items READ items WRITE setItems ) + +public: + // @since 3.1 + class CustomEditor + { + public: + CustomEditor() + : m_representationWidget( 0L ), + m_lineEdit( 0L ) {} + CustomEditor( QWidget *repWidget, KLineEdit *edit ) + : m_representationWidget( repWidget ), + m_lineEdit( edit ) {} + CustomEditor( QComboBox *combo ); + + void setRepresentationWidget( QWidget *repWidget ) { + m_representationWidget = repWidget; + } + void setLineEdit( KLineEdit *edit ) { + m_lineEdit = edit; + } + + virtual QWidget *representationWidget() const { + return m_representationWidget; + } + virtual KLineEdit *lineEdit() const { + return m_lineEdit; + } + + protected: + QWidget *m_representationWidget; + KLineEdit *m_lineEdit; + }; + + public: + + /** + * Enumeration of the buttons, the listbox offers. Specify them in the + * constructor in the buttons parameter. + */ + enum Button { Add = 1, Remove = 2, UpDown = 4, All = Add|Remove|UpDown }; + + /** + * Create an editable listbox. + * + * If @p checkAtEntering is true, after every character you type + * in the line edit QEditListBox will enable or disable + * the Add-button, depending whether the current content of the + * line edit is already in the listbox. Maybe this can become a + * performance hit with large lists on slow machines. + * If @p checkAtEntering is false, + * it will be checked if you press the Add-button. It is not + * possible to enter items twice into the listbox. + */ + QEditListBox(QWidget *parent = 0, const char *name = 0, + bool checkAtEntering=false, int buttons = All ); + /** + * Create an editable listbox. + * + * The same as the other constructor, additionally it takes + * @p title, which will be the title of the frame around the listbox. + */ + QEditListBox(const QString& title, QWidget *parent = 0, + const char *name = 0, bool checkAtEntering=false, + int buttons = All ); + + /** + * Another constructor, which allows to use a custom editing widget + * instead of the standard QLineEdit widget. E.g. you can use a + * KURLRequester or a QComboBox as input widget. The custom + * editor must consist of a lineedit and optionally another widget that + * is used as representation. A QComboBox or a KURLRequester have a + * QLineEdit as child-widget for example, so the QComboBox is used as + * the representation widget. + * + * @see KURLRequester::customEditor() + * @since 3.1 + */ + QEditListBox( const QString& title, + const CustomEditor &customEditor, + QWidget *parent = 0, const char *name = 0, + bool checkAtEntering = false, int buttons = All ); + + virtual ~QEditListBox(); + + /** + * Return a pointer to the embedded QListBox. + */ + QListBox* listBox() const { return m_listBox; } + /** + * Return a pointer to the embedded KLineEdit. + */ + KLineEdit* lineEdit() const { return m_lineEdit; } + /** + * Return a pointer to the Add button + */ + QPushButton* addButton() const { return servNewButton; } + /** + * Return a pointer to the Remove button + */ + QPushButton* removeButton() const { return servRemoveButton; } + /** + * Return a pointer to the Up button + */ + QPushButton* upButton() const { return servUpButton; } + /** + * Return a pointer to the Down button + */ + QPushButton* downButton() const { return servDownButton; } + + /** + * See QListBox::count() + */ + int count() const { return int(m_listBox->count()); } + /** + * See QListBox::insertStringList() + */ + void insertStringList(const QStringList& list, int index=-1); + /** + * See QListBox::insertStringList() + */ + void insertStrList(const QStrList* list, int index=-1); + /** + * See QListBox::insertStrList() + */ + void insertStrList(const QStrList& list, int index=-1); + /** + * See QListBox::insertStrList() + */ + void insertStrList(const char ** list, int numStrings=-1, int index=-1); + /** + * See QListBox::insertItem() + */ + void insertItem(const QString& text, int index=-1) {m_listBox->insertItem(text,index);} + /** + * Clears both the listbox and the line edit. + */ + void clear(); + /** + * See QListBox::text() + */ + QString text(int index) const { return m_listBox->text(index); } + /** + * See QListBox::currentItem() + */ + int currentItem() const; + /** + * See QListBox::currentText() + */ + QString currentText() const { return m_listBox->currentText(); } + + /** + * @returns a stringlist of all items in the listbox + */ + QStringList items() const; + + /** + * Clears the listbox and sets the contents to @p items + * + * @since 3.4 + */ + void setItems(const QStringList& items); + + signals: + void changed(); + + /** + * This signal is emitted when the user adds a new string to the list, + * the parameter is the added string. + * @since 3.2 + */ + void added( const QString & text ); + + /** + * This signal is emitted when the user removes a string from the list, + * the parameter is the removed string. + * @since 3.2 + */ + void removed( const QString & text ); + + protected slots: + //the names should be self-explaining + void moveItemUp(); + void moveItemDown(); + void addItem(); + void removeItem(); + void enableMoveButtons(int index); + void typedSomething(const QString& text); + + private: + QListBox *m_listBox; + QPushButton *servUpButton, *servDownButton; + QPushButton *servNewButton, *servRemoveButton; + KLineEdit *m_lineEdit; + + //this is called in both ctors, to avoid code duplication + void init( bool checkAtEntering, int buttons, + QWidget *representationWidget = 0L ); + + protected: + virtual void virtual_hook( int id, void* data ); + private: + //our lovely private d-pointer + QEditListBoxPrivate *d; +}; + +#endif diff --git a/lib/widgets/propeditor/qfloatinput.cpp b/lib/widgets/propeditor/qfloatinput.cpp new file mode 100644 index 00000000..ea3b17f1 --- /dev/null +++ b/lib/widgets/propeditor/qfloatinput.cpp @@ -0,0 +1,51 @@ +/* + * qfloatinput.cpp + * + * Copyright (C) 2004 David Faure <faure@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ +#include "qfloatinput.h" + +#include <math.h> + +QFloatInput::QFloatInput( int min, int max, float step, int digits, + QWidget *parent, const char *name ) + : QSpinBox( (int) (min*pow(digits,10)), + (int) (max*pow(digits,10)), + (int) (step*pow(digits,10)), parent, name ), + m_digits( digits ) +{ + setValue( (int) (min*pow(digits,10)) ); + delete validator(); + QDoubleValidator* validator = + new QDoubleValidator( min, max, m_digits, this ); + setValidator( validator ); +} + +QString QFloatInput::mapValueToText( int value ) +{ + QString format = QString("%.%1f").arg( m_digits ); + return QString().sprintf(format.latin1(), + (value/(float)pow(m_digits,10)) ); +} + +int QFloatInput::mapTextToValue( bool* ok ) +{ + return int(cleanText().toFloat(ok)*pow(m_digits,10)); +} + + diff --git a/lib/widgets/propeditor/qfloatinput.h b/lib/widgets/propeditor/qfloatinput.h new file mode 100644 index 00000000..72977dd5 --- /dev/null +++ b/lib/widgets/propeditor/qfloatinput.h @@ -0,0 +1,48 @@ +/** + * qfloatinput.h + * + * Copyright (C) 2004 David Faure <faure@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ +#ifndef QFLOATINPUT_H +#define QFLOATINPUT_H + +#include <qapplication.h> +#include <qspinbox.h> +#include <qvalidator.h> + +class QFloatInput : public QSpinBox +{ +public: + QFloatInput( int min, int max, float step, int digits, + QWidget *parent, const char *name = 0 ); + + virtual QString mapValueToText( int value ); + + virtual int mapTextToValue( bool* ok ); + + int digits() const + { + return m_digits; + } + +private: + int m_digits; +}; + + +#endif diff --git a/lib/widgets/propeditor/test.cpp b/lib/widgets/propeditor/test.cpp new file mode 100644 index 00000000..db49addc --- /dev/null +++ b/lib/widgets/propeditor/test.cpp @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2004 by SourceXtreme, Inc * + * oss@sourcextreme.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This 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 Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include <qapplication.h> + +#include "propertyeditor.h" +#include "propertylist.h" +#include "multiproperty.h" + +using namespace PropertyLib; + +int main( int argc, char **argv ) +{ + QApplication app( argc, argv ); + + PropertyEditor *editor = new PropertyEditor( 0 ); + + PropertyList *currentList = new PropertyList(); + + currentList->addProperty( + new Property(Property::String, "Sample string", + "Sample description", "value" ) ); + currentList->addProperty( + new Property(Property::Color, "Color", + "Sample color description", Qt::red ) ); + currentList->addProperty( + new Property( Property::Pixmap, "Pixmap", + "sample pixmap description" ) ); + currentList->addProperty( + new Property( Property::Rect, "Rectangle", + "sample rectangle", QRect( 10, 11, 30, 40 ) ) ); + currentList->addProperty( + new Property( Property::Point, "Point", + "sample point", QPoint( 20, 30 ) ) ); + currentList->addProperty( + new Property( Property::Boolean, "Boolean", + "sample bool", false ) ); + currentList->addProperty( + new Property( Property::Integer, "Integer", + "sample integer", 7 ) ); + + currentList->addProperty( + new Property( Property::Double, "Double", + "sample double", 7.0 ) ); + + QStringList things; + things += "Thing 1"; + things += "Thing 2"; + + currentList->addProperty( + new Property( Property::StringList, "StringList", + "sample stringlist", things ) ); + + + currentList->addProperty( + new Property( Property::DirectoryURL, "Directory", + "sample dir", "C:/" ) ); + + currentList->addProperty( + new Property( Property::FileURL, "File", + "sample file", "C:/" ) ); + + editor->populateProperties( currentList ); + + app.setMainWidget( editor ); + editor->show(); + + return app.exec(); +} + diff --git a/lib/widgets/propeditor/test.pro b/lib/widgets/propeditor/test.pro new file mode 100755 index 00000000..c5b53846 --- /dev/null +++ b/lib/widgets/propeditor/test.pro @@ -0,0 +1,40 @@ +TEMPLATE = app +CONFIG += release +DEFINES += PURE_QT + +SOURCES += childproperty.cpp pcombobox.cpp \ + pdummywidget.cpp ppointedit.cpp \ + propertymachinefactory.cpp pstringlistedit.cpp \ + multiproperty.cpp pcursoredit.cpp \ + prectedit.cpp propertywidget.cpp \ + psymbolcombo.cpp pcheckbox.cpp \ + pdateedit.cpp pfontcombo.cpp \ + property.cpp psizeedit.cpp \ + pdatetimeedit.cpp \ + plineedit.cpp propertyeditor.cpp \ + psizepolicyedit.cpp pyesnobutton.cpp \ + ppixmapedit.cpp \ + propertylist.cpp pspinbox.cpp \ + propertywidgetproxy.cpp plinestyleedit.cpp \ + qeditlistbox.cpp pcolorbutton.cpp \ + pdoublenuminput.cpp qfloatinput.cpp \ + purledit.cpp test.cpp + +HEADERS += childproperty.h pcombobox.h \ + pdummywidget.h ppointedit.h \ + propertymachinefactory.h pcursoredit.h \ + prectedit.h propertywidget.h \ + pdateedit.h pfontcombo.h \ + property.h psizeedit.h \ + pdatetimeedit.h plineedit.h \ + propertyeditor.h psizepolicyedit.h \ + ppixmapedit.h propertylist.h \ + pspinbox.h propertywidgetproxy.h \ + multiproperty.h pyesnobutton.h \ + psymbolcombo.h pstringlistedit.h \ + pcheckbox.h plinestyleedit.h \ + qeditlistbox.h pcolorbutton.h \ + pdoublenuminput.h qfloatinput.h \ + compat_tools.h purledit.h + +IMAGES += undo.xpm diff --git a/lib/widgets/propeditor/undo.xpm b/lib/widgets/propeditor/undo.xpm new file mode 100644 index 00000000..42cbaed0 --- /dev/null +++ b/lib/widgets/propeditor/undo.xpm @@ -0,0 +1,44 @@ +/* XPM */ +static char *undo[] = { +/* columns rows colors chars-per-pixel */ +"16 15 23 1", +" c #000100010001", +". c #0170016F0162", +"X c #02B802E802C8", +"o c #037D026B0273", +"O c #048802AC02C1", +"+ c #055D031C0338", +"@ c #08D206B006B1", +"# c #14B10C350C95", +"$ c #20CA134E13E8", +"% c #216B13B71451", +"& c #230A14A51546", +"* c #2EDD2EF02D48", +"= c #3F0D25332654", +"- c #4EA039B73A18", +"; c #542530E9329F", +": c #4FBD4FDA4D09", +"> c #4FDD506B4D88", +", c #558351244ECB", +"< c #7CAF498E4BCC", +"1 c #7CF25FC15FA7", +"2 c #CB51C348BD5B", +"3 c #CF18CD55C654", +"4 c None", +/* pixels */ +"4444444444444444", +"4444444444444444", +"4444444444444444", +"44444 444444444", +"44444 444444444", +"4444 ,-@@ 44444", +"444 *321;& 44444", +"4444 ,>X%<#44444", +"44444 4 &= 4444", +"44444 44 O 4444", +"444444444 4444", +"444444444 44444", +"4444444444444444", +"4444444444444444", +"4444444444444444" +}; diff --git a/lib/widgets/qcomboview.cpp b/lib/widgets/qcomboview.cpp new file mode 100644 index 00000000..2cab4fea --- /dev/null +++ b/lib/widgets/qcomboview.cpp @@ -0,0 +1,1505 @@ +/********************************************************************** +** +** +** Implementation of QComboView widget class +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** Copyright (C) 2003 Alexander Dymo <cloudtemple@mksat.net> +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +**********************************************************************/ + +#include "qcomboview.h" +#include <kdeversion.h> +#ifndef QT_NO_COMBOBOX +#include "qpopupmenu.h" +#include "qlistview.h" +#include "qpainter.h" +#include "qdrawutil.h" +#include "qstrlist.h" +#include "qpixmap.h" +#include "qtimer.h" +#include "qapplication.h" +#include "qlineedit.h" +#include "qbitmap.h" +#include "private/qeffects_p.h" +#include "qstringlist.h" +#include "qcombobox.h" +#include "qstyle.h" +#include "qheader.h" +#include <limits.h> + +class QComboViewData +{ +public: + QComboViewData( QComboView *cb ): current(0), lView( 0 ), combo( cb ) + { + duplicatesEnabled = TRUE; + cb->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) ); + } + + inline QListView * listView() { return lView; } + void updateLinedGeometry(); + + void setListView( QListView *l ) { lView = l ; + l->setMouseTracking( TRUE );} + + QListViewItem *current; + int maxCount; + int sizeLimit; + QComboView::Policy p; + bool autoresize; + bool poppedUp; + bool mouseWasInsidePopup; + bool arrowPressed; + bool arrowDown; + bool discardNextMousePress; + bool shortClick; + bool useCompletion; + bool completeNow; + int completeAt; + bool duplicatesEnabled; + int fullHeight, currHeight; + + QLineEdit * ed; // /bin/ed rules! + QTimer *completionTimer; + + QSize sizeHint; + +private: + bool usinglView; + QListView *lView; + QComboView *combo; + +}; + +void QComboViewData::updateLinedGeometry() +{ + if ( !ed || !combo ) + return; + QRect r = QStyle::visualRect( combo->style().querySubControlMetrics(QStyle::CC_ComboBox, combo, + QStyle::SC_ComboBoxEditField), combo ); + +// qWarning("updateLinedGeometry(): currentItem is %d", combo->currentItem() == 0 ? 0 : 1); + const QPixmap *pix = combo->currentItem() ? combo->currentItem()->pixmap(0) : 0; + if ( pix && pix->width() < r.width() ) + r.setLeft( r.left() + pix->width() + 4 ); + if ( r != ed->geometry() ) + ed->setGeometry( r ); +} + +static inline bool checkInsertIndex( const char *method, const char * name, + int count, int *index) +{ + bool range_err = (*index > count); +#if defined(QT_CHECK_RANGE) + if ( range_err ) + qWarning( "QComboView::%s: (%s) Index %d out of range", + method, name ? name : "<no name>", *index ); +#else + Q_UNUSED( method ) + Q_UNUSED( name ) +#endif + if ( *index < 0 ) // append + *index = count; + return !range_err; +} + + +static inline bool checkIndex( const char *method, const char * name, + int count, int index ) +{ + bool range_err = (index >= count); +#if defined(QT_CHECK_RANGE) + if ( range_err ) + qWarning( "QComboView::%s: (%s) Index %i out of range", + method, name ? name : "<no name>", index ); +#else + Q_UNUSED( method ) + Q_UNUSED( name ) +#endif + return !range_err; +} + + +/*! + Constructs a combobox with a maximum size and either Motif 2.0 or + Windows look and feel. + + The input field can be edited if \a rw is TRUE, otherwise the user + may only choose one of the items in the combobox. + + The \a parent and \a name arguments are passed on to the QWidget + constructor. +*/ + + +QComboView::QComboView( bool rw, QWidget *parent, const char *name ) + : QWidget( parent, name, WResizeNoErase ) +{ + d = new QComboViewData( this ); + setUpListView(); + + d->current = 0; + d->maxCount = INT_MAX; + setSizeLimit(10); + d->p = AtBottom; + d->autoresize = FALSE; + d->poppedUp = FALSE; + d->arrowDown = FALSE; + d->discardNextMousePress = FALSE; + d->shortClick = FALSE; + d->useCompletion = FALSE; + d->completeAt = 0; + d->completeNow = FALSE; + d->completionTimer = new QTimer( this ); + + setFocusPolicy( StrongFocus ); + + d->ed = 0; + if ( rw ) + setUpLineEdit(); + setBackgroundMode( PaletteButton, PaletteBase ); +} + + + +/*! + Destroys the combobox. +*/ + +QComboView::~QComboView() +{ + delete d; +} + +void QComboView::setDuplicatesEnabled( bool enable ) +{ + d->duplicatesEnabled = enable; +} + +bool QComboView::duplicatesEnabled() const +{ + return d->duplicatesEnabled; +} + +int QComboView::childCount() const +{ + return d->listView()->childCount(); +} + + +/*! + Removes all comboview items. +*/ + +void QComboView::clear() +{ + d->listView()->resize( 0, 0 ); + d->listView()->clear(); + + d->current = 0; + if ( d->ed ) { + d->ed->setText( QString::fromLatin1("") ); + d->updateLinedGeometry(); + } + currentChanged(); +} + +QListViewItem *QComboView::currentItem() const +{ + return d->current; +} + +void QComboView::setCurrentItem( QListViewItem *item ) +{ + if ( item == d->current && !d->ed ) { + return; + } + + if (!item) + { + d->current = 0; + if ( d->ed ) { +// d->ed->setText( "" ); + d->updateLinedGeometry(); + } + return; + } + + d->current = item; + d->completeAt = 0; + if ( d->ed ) { + d->ed->setText( item->text(0) ); +// qWarning("setCurrentItem( %s )", item->text(0).latin1()); + d->updateLinedGeometry(); + } + if ( d->listView() ) { + d->listView()->setCurrentItem( item ); + } else { + internalHighlight( item ); + // internalActivate( item ); ### this leads to weird behavior, as in 3.0.1 + } + + currentChanged(); + + d->listView()->ensureItemVisible(item); +} + +bool QComboView::autoResize() const +{ + return d->autoresize; +} + +void QComboView::setAutoResize( bool enable ) +{ + if ( (bool)d->autoresize != enable ) { + d->autoresize = enable; + if ( enable ) + adjustSize(); + } +} + + +/*! + reimp + + This implementation caches the size hint to avoid resizing when + the contents change dynamically. To invalidate the cached value + call setFont(). +*/ +QSize QComboView::sizeHint() const +{ + if ( isVisible() && d->sizeHint.isValid() ) + return d->sizeHint; + + constPolish(); +// int i, w; + QFontMetrics fm = fontMetrics(); + + int maxW = childCount() ? 18 : 7 * fm.width(QChar('x')) + 18; + int maxH = QMAX( fm.lineSpacing(), 14 ) + 2; + +/* for( i = 0; i < count(); i++ ) { + w = d->listView()->item( i )->width( d->listView() ); + if ( w > maxW ) + maxW = w; + } +*/ + d->sizeHint = (style().sizeFromContents(QStyle::CT_ComboBox, this, + QSize(maxW, maxH)).expandedTo(QApplication::globalStrut())); + + return d->sizeHint; +} + + +/*! + \internal + Receives activated signals from an internal popup list and emits + the activated() signal. +*/ + +void QComboView::internalActivate( QListViewItem * item ) +{ + if (!item) + { + d->current = 0; + if ( d->ed ) { +// d->ed->setText( "" ); + d->updateLinedGeometry(); + } + return; + } + popDownListView(); + d->poppedUp = FALSE; + + d->current = item; + + QString t( item->text(0) ); + if ( d->ed ) { + d->ed->setText( t ); +// qWarning("internalActivate( %s )", item->text(0).latin1()); + d->updateLinedGeometry(); + } + emit activated( item ); + emit activated( t ); + +// item->setOpen(true); +} + +/*! + \internal + Receives highlighted signals from an internal popup list and emits + the highlighted() signal. +*/ + +void QComboView::internalHighlight( QListViewItem * item ) +{ + if (!item) + { + d->current = 0; + if ( d->ed ) { +// d->ed->setText( "" ); + d->updateLinedGeometry(); + } + return; + } + emit highlighted( item ); + QString t = item->text(0); + if ( !t.isNull() ) + emit highlighted( t ); +} + +/*! + \internal + Receives timeouts after a click. Used to decide if a Motif style + popup should stay up or not after a click. +*/ +void QComboView::internalClickTimeout() +{ + d->shortClick = FALSE; +} + +/*! + Sets the palette for both the combobox button and the combobox + popup list to \a palette. +*/ + +void QComboView::setPalette( const QPalette &palette ) +{ + QWidget::setPalette( palette ); + if( d ) { + if(d->listView()) + d->listView()->setPalette( palette ); + } +} + +/*! + Sets the font for both the combobox button and the combobox popup + list to \a font. +*/ + +void QComboView::setFont( const QFont &font ) +{ + d->sizeHint = QSize(); // invalidate size hint + QWidget::setFont( font ); + d->listView()->setFont( font ); + if (d->ed) + d->ed->setFont( font ); + if ( d->autoresize ) + adjustSize(); +} + + +/*!reimp +*/ + +void QComboView::resizeEvent( QResizeEvent * e ) +{ + if ( d->ed ) + d->updateLinedGeometry(); + d->listView()->resize( width(), d->listView()->height() ); + QWidget::resizeEvent( e ); +} + +/*!reimp +*/ + +void QComboView::paintEvent( QPaintEvent * ) +{ + QPainter p( this ); + const QColorGroup & g = colorGroup(); + p.setPen(g.text()); + + QStyle::SFlags flags = QStyle::Style_Default; + if (isEnabled()) + flags |= QStyle::Style_Enabled; + if (hasFocus()) + flags |= QStyle::Style_HasFocus; + + if ( width() < 5 || height() < 5 ) { + qDrawShadePanel( &p, rect(), g, FALSE, 2, + &g.brush( QColorGroup::Button ) ); + return; + } + +// bool reverse = QApplication::reverseLayout(); + style().drawComplexControl( QStyle::CC_ComboBox, &p, this, rect(), g, + flags, QStyle::SC_All, + (d->arrowDown ? + QStyle::SC_ComboBoxArrow : + QStyle::SC_None )); + + QRect re = style().querySubControlMetrics( QStyle::CC_ComboBox, this, + QStyle::SC_ComboBoxEditField ); + re = QStyle::visualRect(re, this); + p.setClipRect( re ); + + if ( !d->ed ) { + QListViewItem * item = d->current; + if ( item ) { + // we calculate the QListBoxTexts height (ignoring strut) + int itemh = d->listView()->fontMetrics().lineSpacing() + 2; + p.translate( re.x(), re.y() + (re.height() - itemh)/2 ); + item->paintCell( &p, d->listView()->colorGroup(), 0, width(), AlignLeft | AlignVCenter ); + } + } else if ( d->listView() && d->listView()->currentItem( ) && d->current ) { + QListViewItem * item = d->current ; + const QPixmap *pix = item->pixmap(0); + if ( pix ) { + p.fillRect( re.x(), re.y(), pix->width() + 4, re.height(), + colorGroup().brush( QColorGroup::Base ) ); + p.drawPixmap( re.x() + 2, re.y() + + ( re.height() - pix->height() ) / 2, *pix ); + } + } + p.setClipping( FALSE ); +} + + +/*!reimp +*/ + +void QComboView::mousePressEvent( QMouseEvent *e ) +{ + if ( e->button() != LeftButton ) + return; + if ( d->discardNextMousePress ) { + d->discardNextMousePress = FALSE; + return; + } + QRect arrowRect = style().querySubControlMetrics( QStyle::CC_ComboBox, this, + QStyle::SC_ComboBoxArrow); + arrowRect = QStyle::visualRect(arrowRect, this); + + // Correction for motif style, where arrow is smaller + // and thus has a rect that doesn't fit the button. + arrowRect.setHeight( QMAX( height() - (2 * arrowRect.y()), arrowRect.height() ) ); + + if ( childCount() && ( !editable() || arrowRect.contains( e->pos() ) ) ) { + d->arrowPressed = FALSE; + listView()->blockSignals( TRUE ); + qApp->sendEvent( listView(), e ); // trigger the listbox's autoscroll + listView()->blockSignals( FALSE ); + popup(); + if ( arrowRect.contains( e->pos() ) ) { + d->arrowPressed = TRUE; + d->arrowDown = TRUE; + repaint( FALSE ); + } + QTimer::singleShot( 200, this, SLOT(internalClickTimeout())); + d->shortClick = TRUE; + } +} + +/*!reimp +*/ + +void QComboView::mouseMoveEvent( QMouseEvent * ) +{ +} + +/*!reimp +*/ + +void QComboView::mouseReleaseEvent( QMouseEvent * ) +{ +} + +/*!reimp +*/ + +void QComboView::mouseDoubleClickEvent( QMouseEvent *e ) +{ + mousePressEvent( e ); +} + + +/*!reimp +*/ + +void QComboView::keyPressEvent( QKeyEvent *e ) +{ + QListViewItem *c = currentItem(); + if ( ( e->key() == Key_F4 && e->state() == 0 ) || + ( e->key() == Key_Down && (e->state() & AltButton) ) || + ( !d->ed && e->key() == Key_Space ) ) { + if ( childCount() ) { + popup(); + } + return; + } else if ( e->key() == Key_Up ) { +/* if ((!c) && (listView()->firstChild())) + setCurrentItem(listView()->firstChild());*/ + if (c && c->itemAbove() ) + setCurrentItem( c->itemAbove() ); + else + return; + } else if ( e->key() == Key_Down ) { + if ((!c) && (listView()->firstChild())) + { + setCurrentItem(listView()->firstChild()); + return; + } + if ( c && c->itemBelow() ) + setCurrentItem( c->itemBelow() ); + else + return; + } else if ( e->key() == Key_Home && ( !d->ed || !d->ed->hasFocus() ) ) { + if (listView()->firstChild()) + setCurrentItem( listView()->firstChild() ); + else + return; + } else if ( e->key() == Key_End && ( !d->ed || !d->ed->hasFocus() ) ) { + if (listView()->lastItem()) + setCurrentItem( listView()->lastItem() ); + else + return; + } else if ( !d->ed && e->ascii() >= 32 && !e->text().isEmpty() ) { + if ( !d->completionTimer->isActive() ) { + d->completeAt = 0; + c = completionIndex( e->text(), c->itemBelow() ); + if ( c ) { + setCurrentItem( c ); + d->completeAt = e->text().length(); + } + else + return; + } else { + d->completionTimer->stop(); + QString ct = currentText().left( d->completeAt ) + e->text(); + c = completionIndex( ct, c ); + if ( c == 0 && d->completeAt > 0 ) { + c = completionIndex( e->text(), listView()->firstChild() ); + ct = e->text(); + } + d->completeAt = 0; + if ( c ) { + setCurrentItem( c ); + d->completeAt = ct.length(); + } + else + return; + } + d->completionTimer->start( 400, TRUE ); + } else { + e->ignore(); + return; + } + + c = currentItem(); + if ( childCount() && c && !c->text(0).isNull() ) + emit activated( c->text(0) ); + emit activated( c ); +} + +QString QComboView::currentText() const +{ + if ( d->ed ) + return d->ed->text(); + else if ( d->current ) + return currentItem()->text(0); + else + return QString::null; +} + +/*!reimp +*/ + +void QComboView::focusInEvent( QFocusEvent * e ) +{ + QWidget::focusInEvent( e ); + d->completeNow = FALSE; + d->completeAt = 0; + + emit focusGranted(); +} + +/*!reimp +*/ + +void QComboView::focusOutEvent( QFocusEvent * e ) +{ + QWidget::focusOutEvent( e ); + d->completeNow = FALSE; + d->completeAt = 0; + + emit focusLost(); +} + +/*!reimp +*/ + +void QComboView::wheelEvent( QWheelEvent *e ) +{ + if ( d->poppedUp ) { + QApplication::sendEvent( d->listView(), e ); + } else { + if ( e->delta() > 0 ) { + QListViewItem *c = currentItem(); + if ( c && c->itemAbove() ) { + setCurrentItem( c->itemAbove() ); + emit activated( currentItem() ); + emit activated( currentText() ); + } + } else { + QListViewItem *c = currentItem(); + if ( c && c->itemBelow() ) { + setCurrentItem( c->itemBelow() ); + emit activated( currentItem() ); + emit activated( currentText() ); + } + } + e->accept(); + } +} + +int childCount(QListViewItem *it) +{ + int count = 1; + QListViewItem * myChild = it->firstChild(); + while( myChild ) { + count += childCount(myChild); + myChild = myChild->nextSibling(); + } + return count; +} + +int childCount(QListView *lv) +{ + int count = 0; + QListViewItem * myChild = lv->firstChild(); + while( myChild ) { + count += childCount(myChild); +// count += 1; + myChild = myChild->nextSibling(); + } + return count; +} + +/*! + \internal + Calculates the listbox height needed to contain all items, or as + many as the list box is supposed to contain. +*/ +static int listHeight( QListView *l, int /*sl*/ ) +{ +/* if ( l->childCount() > 0 ) + return QMIN( l->childCount(), (uint)sl) * l->firstChild()->height(); + else*/ + + int prefH = 0; + int ch = childCount(l); + ch = QMIN(ch, 10); + if (l->firstChild()) + { + prefH = ch * l->firstChild()->height(); + } + else + prefH = l->sizeHint().height(); + + if (l->header()->isVisible()) + prefH += l->header()->sizeHint().height(); + +// return prefH < l->sizeHint().height() ? prefH : l->sizeHint().height(); + + return prefH+2; +} + +/*! + Pops up the combobox popup list. + + If the list is empty, no items appear. +*/ + +void QComboView::popup() +{ + if ( !childCount() ) + return; + + // Send all listbox events to eventFilter(): + QListView* lb = d->listView(); + lb->triggerUpdate( ); + lb->installEventFilter( this ); + lb->viewport()->installEventFilter( this ); + d->mouseWasInsidePopup = FALSE; +// int w = lb->variableWidth() ? lb->sizeHint().width() : width(); + int w = width(); + int h = listHeight( lb, d->sizeLimit ); + QRect screen = QApplication::desktop()->availableGeometry( const_cast<QComboView*>(this) ); + + int sx = screen.x(); // screen pos + int sy = screen.y(); + int sw = screen.width(); // screen width + int sh = screen.height(); // screen height + QPoint pos = mapToGlobal( QPoint(0,height()) ); + // ## Similar code is in QPopupMenu + int x = pos.x(); + int y = pos.y(); + + // the complete widget must be visible + if ( x + w > sx + sw ) + x = sx+sw - w; + if ( x < sx ) + x = sx; + if (y + h > sy+sh && y - h - height() >= 0 ) + y = y - h - height(); + QRect rect = + style().querySubControlMetrics( QStyle::CC_ComboBox, this, + QStyle::SC_ComboBoxListBoxPopup, + QStyleOption( x, y, w, h ) ); + if ( rect.isNull() ) + rect.setRect( x, y, w, h ); + lb->setGeometry( rect ); + + lb->raise(); + bool block = lb->signalsBlocked(); + lb->blockSignals( TRUE ); + QListViewItem *currentLBItem = d->current ; + lb->setCurrentItem( currentLBItem ); + // set the current item to also be the selected item if it isn't already + if ( currentLBItem && currentLBItem->isSelectable() && !currentLBItem->isSelected() ) + lb->setSelected( currentLBItem, TRUE ); + lb->blockSignals( block ); + lb->setVScrollBarMode(QScrollView::Auto); + +//#ifndef QT_NO_EFFECTS +/* if ( QApplication::isEffectEnabled( UI_AnimateCombo ) ) { + if ( lb->y() < mapToGlobal(QPoint(0,0)).y() ) + qScrollEffect( lb, QEffects::UpScroll ); + else + qScrollEffect( lb ); + } else*/ +//#endif + lb->show(); + d->poppedUp = TRUE; +} + + +/*! + reimp +*/ +void QComboView::updateMask() +{ + QBitmap bm( size() ); + bm.fill( color0 ); + + { + QPainter p( &bm, this ); + style().drawComplexControlMask(QStyle::CC_ComboBox, &p, this, rect()); + } + + setMask( bm ); +} + +/*! + \internal + Pops down (removes) the combobox popup list box. +*/ +void QComboView::popDownListView() +{ + d->listView()->removeEventFilter( this ); + d->listView()->viewport()->removeEventFilter( this ); + d->listView()->hide(); + d->listView()->setCurrentItem( d->current ); + if ( d->arrowDown ) { + d->arrowDown = FALSE; + repaint( FALSE ); + } + d->poppedUp = FALSE; +} + + +/*! + \internal + Re-indexes the identifiers in the popup list. +*/ + +void QComboView::reIndex() +{ +} + +/*! + \internal + Repaints the combobox. +*/ + +void QComboView::currentChanged() +{ + if ( d->autoresize ) + adjustSize(); + update(); +} + +/*! reimp + + \internal + + The event filter steals events from the popup or listbox when they + are popped up. It makes the popup stay up after a short click in + motif style. In windows style it toggles the arrow button of the + combobox field, and activates an item and takes down the listbox + when the mouse button is released. +*/ + +bool QComboView::eventFilter( QObject *object, QEvent *event ) +{ + if ( !event ) + return TRUE; + else if ( object == d->ed ) { + if ( event->type() == QEvent::KeyPress ) { + bool isAccepted = ( (QKeyEvent*)event )->isAccepted(); + keyPressEvent( (QKeyEvent *)event ); + if ( ((QKeyEvent *)event)->isAccepted() ) { + d->completeNow = FALSE; + return TRUE; + } else if ( ((QKeyEvent *)event)->key() != Key_End ) { + d->completeNow = TRUE; + d->completeAt = d->ed->cursorPosition(); + } + if ( isAccepted ) + ( (QKeyEvent*)event )->accept(); + else + ( (QKeyEvent*)event )->ignore(); + } else if ( event->type() == QEvent::KeyRelease ) { + d->completeNow = FALSE; + keyReleaseEvent( (QKeyEvent *)event ); + return ((QKeyEvent *)event)->isAccepted(); + } else if ( event->type() == QEvent::FocusIn ) { + focusInEvent( (QFocusEvent *)event ); + } else if ( event->type() == QEvent::FocusOut ) { + focusOutEvent( (QFocusEvent *)event ); + } else if ( d->useCompletion && d->completeNow ) { + if ( !d->ed->text().isNull() && + d->ed->cursorPosition() > d->completeAt && + d->ed->cursorPosition() == (int)d->ed->text().length() ) { + d->completeNow = FALSE; + QString ct( d->ed->text() ); + QListViewItem *i = completionIndex( ct, currentItem() ); + if ( i ) { + QString it = i->text(0); + d->ed->validateAndSet( it, ct.length(), + ct.length(), it.length() ); + } + } + } + } else if ( ( object == d->listView() || + object == d->listView()->viewport() )) { + QMouseEvent *e = (QMouseEvent*)event; + switch( event->type() ) { + case QEvent::MouseMove: + if ( !d->mouseWasInsidePopup ) { +// qWarning("!d->mouseWasInsidePopup"); + QPoint pos = e->pos(); + if ( d->listView()->rect().contains( pos ) ) + d->mouseWasInsidePopup = TRUE; + // Check if arrow button should toggle + if ( d->arrowPressed ) { + QPoint comboPos; + comboPos = mapFromGlobal( d->listView()->mapToGlobal(pos) ); + QRect arrowRect = + style().querySubControlMetrics( QStyle::CC_ComboBox, this, + QStyle::SC_ComboBoxArrow); + arrowRect = QStyle::visualRect(arrowRect, this); + if ( arrowRect.contains( comboPos ) ) { + if ( !d->arrowDown ) { + d->arrowDown = TRUE; + repaint( FALSE ); + } + } else { + if ( d->arrowDown ) { + d->arrowDown = FALSE; + repaint( FALSE ); + } + } + } + } else if ((e->state() & ( RightButton | LeftButton | MidButton ) ) == 0 && + style().styleHint(QStyle::SH_ComboBox_ListMouseTracking, this)) { +// qWarning("event filter:: emu"); + QWidget *mouseW = QApplication::widgetAt( e->globalPos(), TRUE ); +// if ( mouseW == d->listView()->viewport() ) { //### + if ( mouseW == d->listView()->viewport() ) { + QListViewItem *sel = d->listView()->itemAt(e->pos()); + if (sel) + { + d->listView()->setCurrentItem(sel); + d->listView()->setSelected(sel, true); + } + return TRUE; + } + } + + break; + case QEvent::MouseButtonRelease: + if ( d->listView()->rect().contains( e->pos() ) ) { + QMouseEvent tmp( QEvent::MouseButtonDblClick, + e->pos(), e->button(), e->state() ) ; + // will hide popup + QApplication::sendEvent( object, &tmp ); + return TRUE; + } else { + if ( d->mouseWasInsidePopup ) { + popDownListView(); + } else { + d->arrowPressed = FALSE; + if ( d->arrowDown ) { + d->arrowDown = FALSE; + repaint( FALSE ); + } + } + } + break; + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonPress: + if ( !d->listView()->rect().contains( e->pos() ) ) { + QPoint globalPos = d->listView()->mapToGlobal(e->pos()); + if ( QApplication::widgetAt( globalPos, TRUE ) == this ) { + d->discardNextMousePress = TRUE; + // avoid popping up again + } + popDownListView(); + return TRUE; + } + break; + case QEvent::KeyPress: + switch( ((QKeyEvent *)event)->key() ) { + case Key_Up: + case Key_Down: + if ( !(((QKeyEvent *)event)->state() & AltButton) ) + break; + case Key_F4: + case Key_Escape: + if ( d->poppedUp ) { + popDownListView(); + return TRUE; + } + break; + case Key_Enter: + case Key_Return: + // work around QDialog's enter handling + return FALSE; + default: + break; + } + break; + case QEvent::Hide: + popDownListView(); + break; + default: + break; + } + } + return QWidget::eventFilter( object, event ); +} + + +/*! + Returns the index of the first item \e after \a startingAt of + which \a prefix is a case-insensitive prefix. Returns -1 if no + items start with \a prefix. +*/ + +QListViewItem *QComboView::completionIndex( const QString & prefix, + QListViewItem *startingAt ) const +{ + QListViewItem *start = startingAt; +/* if ( start < 0 || start >= count() ) + start = 0; + if ( start >= count() ) + return -1;*/ + if (!start) + start = listView()->firstChild(); + if (!start) + return 0; +/* if (!start->itemBelow()) + return 0;*/ + QString match = prefix.lower(); + if ( match.length() < 1 ) + return start; + + QString current; + QListViewItem *i = start; + do { + current = i->text(0).lower(); + if ( current.startsWith( match ) ) + return i; + i = i->itemBelow(); + if ( i ) + i = listView()->firstChild(); + } while ( i != start ); + return 0; +} + + +int QComboView::sizeLimit() const +{ + return d ? d->sizeLimit : INT_MAX; +} + +void QComboView::setSizeLimit( int lines ) +{ + d->sizeLimit = lines; +} + + +/*int QComboView::maxCount() const +{ + return d ? d->maxCount : INT_MAX; +} + +void QComboView::setMaxCount( int count ) +{ + int l = this->count(); + while( --l > count ) + removeItem( l ); + d->maxCount = count; +} +*/ +QComboView::Policy QComboView::insertionPolicy() const +{ + return d->p; +} + +void QComboView::setInsertionPolicy( Policy policy ) +{ + d->p = policy; +} + + + +/*! + Internal slot to keep the line editor up to date. +*/ + +void QComboView::returnPressed() +{ + QString s( d->ed->text() ); + + if ( s.isEmpty() ) + return; + + QListViewItem *c = 0; + bool doInsert = TRUE; + if ( !d->duplicatesEnabled ) { + c = listView()->findItem(s, 0); + if ( c ) + doInsert = FALSE; + } + + if ( doInsert ) { + if ( insertionPolicy() != NoInsertion ) { +/* int cnt = count(); + while ( cnt >= d->maxCount ) { + removeItem( --cnt ); + }*/ + } + + switch ( insertionPolicy() ) { + case AtCurrent: + if ( s != currentItem()->text(0) ) + currentItem()->setText(0, s); + emit activated( currentItem() ); + emit activated( s ); + return; + case NoInsertion: + emit activated( s ); + return; + case AtTop: + c = 0; + return; +// break; + case AtBottom: + c = new QListViewItem(listView(), listView()->lastItem(), s); + break; + case BeforeCurrent: + if (currentItem() && currentItem()->itemAbove()) + c = new QListViewItem(listView(), currentItem()->itemAbove(), s); + else + { + c = 0; + return; + } + break; + case AfterCurrent: + if (currentItem() && currentItem()->itemBelow()) + c = new QListViewItem(listView(), currentItem()->itemBelow(), s); + else + { + c = 0; + return; + } + break; + } + } + + if (c) + { + setCurrentItem( c ); + emit activated( c ); + emit activated( s ); + } +} + + +/*! reimp +*/ + +void QComboView::setEnabled( bool enable ) +{ + QWidget::setEnabled( enable ); +} + + + +/*! + Applies the validator \a v to the combobox so that only text which + is valid according to \a v is accepted. + + This function does nothing if the combobox is not editable. + + \sa validator() clearValidator() QValidator +*/ + +void QComboView::setValidator( const QValidator * v ) +{ + if ( d && d->ed ) + d->ed->setValidator( v ); +} + + +/*! + Returns the validator which constrains editing for this combobox + if there is one; otherwise returns 0. + + \sa setValidator() clearValidator() QValidator +*/ + +const QValidator * QComboView::validator() const +{ + return d && d->ed ? d->ed->validator() : 0; +} + + +/*! + This slot is equivalent to setValidator( 0 ). +*/ + +void QComboView::clearValidator() +{ + if ( d && d->ed ) + d->ed->setValidator( 0 ); +} + + +/*! + Sets the combobox to use \a newListBox instead of the current list + box or popup. As a side effect, it clears the combobox of its + current contents. + + \warning QComboView assumes that newListBox->text(n) returns + non-null for 0 \<= n \< newListbox->count(). This assumption is + necessary because of the line edit in QComboView. +*/ + +void QComboView::setListView( QListView * newListView ) +{ + clear(); + + delete d->listView(); + + newListView->reparent( this, WType_Popup, QPoint(0,0), FALSE ); + d->setListView( newListView ); + d->listView()->setFont( font() ); + d->listView()->setPalette( palette() ); +/* d->listView()->setVScrollBarMode(QScrollView::AlwaysOff); + d->listView()->setHScrollBarMode(QScrollView::AlwaysOff);*/ + d->listView()->setFrameStyle( QFrame::Box | QFrame::Plain ); + d->listView()->setLineWidth( 1 ); +/* d->listView()->setRootIsDecorated( true ); + d->listView()->setAllColumnsShowFocus(true);*/ + d->listView()->resize( 100, 10 ); + + if (d->listView()->firstChild()) + d->current = d->listView()->firstChild(); + +// d->listView()->header()->hide(); + + +/* d->listView()->setFont( font() ); + d->listView()->setPalette( palette() ); + d->listView()->setVScrollBarMode( QScrollView::AlwaysOff ); + d->listView()->setHScrollBarMode( QScrollView::AlwaysOff ); + d->listView()->setFrameStyle( QFrame::Box | QFrame::Plain ); + d->listView()->setLineWidth( 1 ); + d->listView()->setRootIsDecorated( true ); + d->listView()->setAllColumnsShowFocus(true); + d->listView()->addColumn(""); + d->listView()->resize( 100, 10 ); +*/ + + connect( d->listView(), SIGNAL(returnPressed(QListViewItem*)), + SLOT(internalActivate(QListViewItem*))); + connect( d->listView(), SIGNAL(doubleClicked(QListViewItem*)), + SLOT(internalActivate(QListViewItem*))); + connect( d->listView(), SIGNAL(doubleClicked(QListViewItem*)), + SLOT(checkState(QListViewItem*))); + connect( d->listView(), SIGNAL(currentChanged(QListViewItem*)), + SLOT(internalHighlight(QListViewItem*))); + connect( d->listView(), SIGNAL(selectionChanged(QListViewItem*)), + SLOT(internalHighlight(QListViewItem*))); +} + + +/*! + Returns the current list box, or 0 if there is no list box. + (QComboView can use QPopupMenu instead of QListBox.) Provided to + match setlistView(). + + \sa setlistView() +*/ + +QListView * QComboView::listView() const +{ + return d ? d->listView() : 0; +} + +/*! + Returns the line edit, or 0 if there is no line edit. + + Only editable listboxes have a line editor. +*/ +QLineEdit* QComboView::lineEdit() const +{ + return d->ed; +} + + + +/*! + Clears the line edit without changing the combobox's contents. + Does nothing if the combobox isn't editable. + + This is particularly useful when using a combobox as a line edit + with history. For example you can connect the combobox's + activated() signal to clearEdit() in order to present the user + with a new, empty line as soon as Enter is pressed. + + \sa setEditText() +*/ + +void QComboView::clearEdit() +{ + if ( d && d->ed ) + d->ed->clear(); +} + + +/*! + Sets the text in the line edit to \a newText without changing the + combobox's contents. Does nothing if the combobox isn't editable. + + This is useful e.g. for providing a good starting point for the + user's editing and entering the change in the combobox only when + the user presses Enter. + + \sa clearEdit() insertItem() +*/ + +void QComboView::setEditText( const QString &newText ) +{ + if ( d && d->ed ) { + d->updateLinedGeometry(); + d->ed->setText( newText ); + } +} + +void QComboView::setAutoCompletion( bool enable ) +{ + d->useCompletion = enable; + d->completeNow = FALSE; +} + + +bool QComboView::autoCompletion() const +{ + return d->useCompletion; +} + +/*!reimp + */ +void QComboView::styleChange( QStyle& s ) +{ + d->sizeHint = QSize(); // invalidate size hint... + if ( d->ed ) + d->updateLinedGeometry(); + QWidget::styleChange( s ); +} + +bool QComboView::editable() const +{ + return d->ed != 0; +} + +void QComboView::setEditable( bool y ) +{ + if ( y == editable() ) + return; + if ( y ) { + setUpListView(); + setUpLineEdit(); + d->ed->show(); + if ( currentItem() ) + setEditText( currentText() ); + } else { + delete d->ed; + d->ed = 0; + } + + setFocusPolicy( StrongFocus ); + updateGeometry(); + update(); +} + + +void QComboView::setUpListView() +{ + d->setListView( new QListView( this, "in-combo", WType_Popup ) ); + + d->listView()->setFont( font() ); + d->listView()->setPalette( palette() ); +/* d->listView()->setVScrollBarMode( QScrollView::AlwaysOff ); + d->listView()->setHScrollBarMode( QScrollView::AlwaysOff );*/ + d->listView()->setFrameStyle( QFrame::Box | QFrame::Plain ); + d->listView()->setLineWidth( 1 ); + d->listView()->setRootIsDecorated( false ); + d->listView()->setAllColumnsShowFocus(true); + d->listView()->addColumn(""); + d->listView()->resize( 100, 10 ); + d->listView()->setResizeMode(QListView::LastColumn); + + if (d->listView()->firstChild()) + d->current = d->listView()->firstChild(); + + d->listView()->header()->hide(); + + connect( d->listView(), SIGNAL(returnPressed(QListViewItem*)), + SLOT(internalActivate(QListViewItem*))); + connect( d->listView(), SIGNAL(doubleClicked(QListViewItem*)), + SLOT(internalActivate(QListViewItem*))); + connect( d->listView(), SIGNAL(doubleClicked(QListViewItem*)), + SLOT(checkState(QListViewItem*))); + connect( d->listView(), SIGNAL(currentChanged(QListViewItem*)), + SLOT(internalHighlight(QListViewItem*))); + connect( d->listView(), SIGNAL(selectionChanged(QListViewItem*)), + SLOT(internalHighlight(QListViewItem*))); +} + + +void QComboView::setUpLineEdit() +{ + if ( !d->ed ) + setLineEdit( new QLineEdit( this, "combo edit" ) ); +} + +/*! + Sets the line edit to use \a edit instead of the current line edit. +*/ + +void QComboView::setLineEdit( QLineEdit *edit ) +{ + if ( !edit ) { +#if defined(QT_CHECK_NULL) + Q_ASSERT( edit != 0 ); +#endif + return; + } + + edit->setText( currentText() ); + if ( d->ed ) { + int start = 0, end = 0; + d->ed->getSelection( &start, &end ); + edit->setSelection( start, end ); + edit->setCursorPosition( d->ed->cursorPosition() ); + edit->setEdited( d->ed->edited() ); + delete d->ed; + } + + d->ed = edit; + + if ( edit->parent() != this ) { + edit->reparent( this, QPoint(0,0), FALSE ); + edit->setFont( font() ); + } + + connect (edit, SIGNAL( textChanged( const QString& ) ), + this, SIGNAL( textChanged( const QString& ) ) ); + connect( edit, SIGNAL(returnPressed()), SLOT(returnPressed()) ); + + edit->setFrame( FALSE ); + d->updateLinedGeometry(); + edit->installEventFilter( this ); + setFocusProxy( edit ); + setFocusPolicy( StrongFocus ); + + setUpListView(); + + if ( isVisible() ) + edit->show(); + + updateGeometry(); + update(); +} + +void QComboView::setCurrentText( const QString& txt ) +{ + QListViewItem *i; + i = listView()->findItem(txt, 0); + if ( i ) + setCurrentItem( i ); + else if ( d->ed ) + d->ed->setText( txt ); + else if (currentItem()) + currentItem()->setText(0, txt); +} + +void QComboView::checkState( QListViewItem * item) +{ + item->setOpen(!item->isOpen()); +} + +void QComboView::setCurrentActiveItem( QListViewItem * item ) +{ + if ( item == d->current && !d->ed ) { + return; + } + + d->current = item; + d->completeAt = 0; + if ( d->ed ) { + d->ed->setText( item->text(0) ); + d->ed->setCursorPosition(0); +// qWarning("setCurrentActiveItem( %s )", item->text(0).latin1()); + d->updateLinedGeometry(); + } + if ( d->listView() ) { + d->listView()->setCurrentItem( item ); + emit activated( item ); + emit activated( item->text(0) ); + } else { + internalHighlight( item ); + internalActivate( item ); + } + + currentChanged(); + + d->listView()->ensureItemVisible(item); +} + +#include "qcomboview.moc" + +#endif // QT_NO_COMBOBOX + diff --git a/lib/widgets/qcomboview.h b/lib/widgets/qcomboview.h new file mode 100644 index 00000000..8f618f50 --- /dev/null +++ b/lib/widgets/qcomboview.h @@ -0,0 +1,172 @@ +/********************************************************************** +** +** +** Definition of QComboView class +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** Copyright (C) 2003 Alexander Dymo <cloudtemple@mksat.net> +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +**********************************************************************/ + +#ifndef QCOMBOVIEW_H +#define QCOMBOVIEW_H + +#ifndef QT_H +#include "qwidget.h" +#endif // QT_H + +#ifndef QT_NO_COMBOBOX + +/** +@file qcomboview.h +QComboView class. +*/ + +class QStrList; +class QStringList; +class QLineEdit; +class QValidator; +class QListView; +class QListViewItem; +class QComboViewData; + +/** +QComboView - a combo with a QListView as a popup widget. +This means that you can have a treeview inside of a combo. Otherwise it works +in the same way as QComboBox and have similar API. +*/ +class Q_EXPORT QComboView : public QWidget +{ + Q_OBJECT + Q_ENUMS( Policy ) + Q_PROPERTY( bool editable READ editable WRITE setEditable ) +// Q_PROPERTY( int count READ count ) + Q_PROPERTY( QString currentText READ currentText WRITE setCurrentText DESIGNABLE false ) +// Q_PROPERTY( QListView *currentItem READ currentItem WRITE setCurrentItem ) + Q_PROPERTY( bool autoResize READ autoResize WRITE setAutoResize DESIGNABLE false ) + Q_PROPERTY( int sizeLimit READ sizeLimit WRITE setSizeLimit ) +// Q_PROPERTY( int maxCount READ maxCount WRITE setMaxCount ) + Q_PROPERTY( Policy insertionPolicy READ insertionPolicy WRITE setInsertionPolicy ) + Q_PROPERTY( bool autoCompletion READ autoCompletion WRITE setAutoCompletion ) + Q_PROPERTY( bool duplicatesEnabled READ duplicatesEnabled WRITE setDuplicatesEnabled ) + Q_OVERRIDE( bool autoMask DESIGNABLE true SCRIPTABLE true ) + +public: +// QComboView( QWidget* parent=0, const char* name=0 ); + QComboView( bool rw, QWidget* parent=0, const char* name=0 ); + ~QComboView(); + + int childCount() const; + + QListViewItem *currentItem() const; + virtual void setCurrentItem( QListViewItem * ); + virtual void setCurrentActiveItem( QListViewItem * ); + + bool autoResize() const; + virtual void setAutoResize( bool ); + QSize sizeHint() const; + + void setPalette( const QPalette & ); + void setFont( const QFont & ); + void setEnabled( bool ); + + virtual void setSizeLimit( int ); + int sizeLimit() const; + +/* virtual void setMaxCount( int ); + int maxCount() const;*/ + + enum Policy { NoInsertion, AtTop, AtCurrent, AtBottom, + AfterCurrent, BeforeCurrent }; + + virtual void setInsertionPolicy( Policy policy ); + Policy insertionPolicy() const; + + virtual void setValidator( const QValidator * ); + const QValidator * validator() const; + + virtual void setListView( QListView * ); + QListView * listView() const; + + virtual void setLineEdit( QLineEdit *edit ); + QLineEdit* lineEdit() const; + + virtual void setAutoCompletion( bool ); + bool autoCompletion() const; + + bool eventFilter( QObject *object, QEvent *event ); + + void setDuplicatesEnabled( bool enable ); + bool duplicatesEnabled() const; + + bool editable() const; + void setEditable( bool ); + + virtual void popup(); + + QString currentText() const; + void setCurrentText( const QString& ); + +public slots: + virtual void clear(); + void clearValidator(); + void clearEdit(); + virtual void setEditText( const QString &); + +signals: + void activated( QListViewItem * item ); + void highlighted( QListViewItem * item ); + void activated( const QString &); + void highlighted( const QString &); + void textChanged( const QString &); + void focusGranted(); + void focusLost(); + +private slots: + void internalActivate( QListViewItem * ); + void internalHighlight( QListViewItem * ); + void internalClickTimeout(); + void returnPressed(); + void checkState(QListViewItem*); + +protected: + void paintEvent( QPaintEvent * ); + void resizeEvent( QResizeEvent * ); + void mousePressEvent( QMouseEvent * ); + void mouseMoveEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseDoubleClickEvent( QMouseEvent * ); + void keyPressEvent( QKeyEvent *e ); + void focusInEvent( QFocusEvent *e ); + void focusOutEvent( QFocusEvent *e ); + void wheelEvent( QWheelEvent *e ); + void styleChange( QStyle& ); + + void updateMask(); + +private: + virtual void setUpListView(); + void setUpLineEdit(); + void popDownListView(); + void reIndex(); + void currentChanged(); + QListViewItem *completionIndex( const QString &, QListViewItem * ) const; + + QComboViewData *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QComboView( const QComboView & ); + QComboView &operator=( const QComboView & ); +#endif +}; + + +#endif // QT_NO_COMBOBOX + +#endif // QCOMBOVIEW_H diff --git a/lib/widgets/resizablecombo.cpp b/lib/widgets/resizablecombo.cpp new file mode 100644 index 00000000..436125a9 --- /dev/null +++ b/lib/widgets/resizablecombo.cpp @@ -0,0 +1,98 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Alexander Dymo <cloudtemple@mksat.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "resizablecombo.h" + +#include "kcomboview.h" + +#include <qevent.h> +#include <qlayout.h> +#include <qpixmap.h> +#include <qapplication.h> +#include <qwhatsthis.h> + +#include <klocale.h> + +static const char * resize_xpm[] = { +"9 18 2 1", +" c None", +". c #000000", +" . ", +" ", +" . ", +" ", +" . ", +" ", +" . . . ", +" .. .. ", +".........", +" .. .. ", +" . . . ", +" ", +" . ", +" ", +" . ", +" ", +" . ", +" "}; + +ResizableCombo::ResizableCombo(KComboView *view, QWidget *parent, const char *name): + QWidget(parent, name), m_sizer(0), m_combo(view) +{ + QHBoxLayout *l = new QHBoxLayout(this); + view->reparent(this, QPoint(0,0)); + l->addWidget(view); + + m_sizer = new MyPushButton(this); + m_sizer->setPixmap(QPixmap(resize_xpm)); + QWhatsThis::add(m_sizer, i18n("Drag this to resize the combobox.")); + l->addWidget(m_sizer); +} + +void MyPushButton::mousePressEvent( QMouseEvent * e ) +{ + m_resizing = true; + m_pressedPos = e->globalPos(); + m_width = m_combo->m_combo->width(); + QPushButton::mousePressEvent(e); +} + +void MyPushButton::mouseReleaseEvent( QMouseEvent * e ) +{ + m_resizing = false; + QPushButton::mouseReleaseEvent(e); +} + +void MyPushButton::mouseMoveEvent( QMouseEvent * e ) +{ + if (m_resizing) + m_combo->m_combo->setMinimumWidth(m_width + e->globalPos().x() - pressedPos().x()); + + QPushButton::mouseMoveEvent(e); +} + +MyPushButton::MyPushButton( ResizableCombo * parent, const char * name ) + :QPushButton(parent, name), m_resizing(false), m_combo(parent) +{ + setFocusPolicy(NoFocus); + setFlat(true); + m_width = m_combo->m_combo->width(); +} + + +#include "resizablecombo.moc" diff --git a/lib/widgets/resizablecombo.h b/lib/widgets/resizablecombo.h new file mode 100644 index 00000000..587712d0 --- /dev/null +++ b/lib/widgets/resizablecombo.h @@ -0,0 +1,72 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Alexander Dymo <cloudtemple@mksat.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef RESIZABLECOMBO_H +#define RESIZABLECOMBO_H + +#include <qpushbutton.h> +#include <qpoint.h> + +class KComboView; +class QMouseEvent; +class MyPushButton; + +/** +@file resizablecombo.h +Resizable combo box. +*/ + +/** +Resizable combo box. +Used to place resizable KComboBox onto toolbars. +*/ +class ResizableCombo: public QWidget{ + Q_OBJECT +public: + ResizableCombo(KComboView *view, QWidget *parent = 0, const char *name = 0); + +private: + MyPushButton *m_sizer; + KComboView *m_combo; + +friend class MyPushButton; +}; + +class MyPushButton: public QPushButton +{ +public: + MyPushButton(ResizableCombo *parent = 0, const char *name = 0 ); + + QPoint pressedPos() + { + return m_pressedPos; + } + +protected: + virtual void mouseReleaseEvent ( QMouseEvent * e ); + virtual void mousePressEvent ( QMouseEvent * e ); + virtual void mouseMoveEvent ( QMouseEvent * e ); + +private: + bool m_resizing; + QPoint m_pressedPos; + int m_width; + ResizableCombo *m_combo; +}; + +#endif diff --git a/lib/widgets/sticky.xpm b/lib/widgets/sticky.xpm new file mode 100644 index 00000000..f6c5c5af --- /dev/null +++ b/lib/widgets/sticky.xpm @@ -0,0 +1,13 @@ +/* XPM */ +static const char * const sticky[] = { +/* width height num_colors chars_per_pixel */ +" 4 4 2 1", +/* colors */ +". c #ffffff", +"x c #000000", +/* pixels */ +".xx.", +"xxxx", +"xxxx", +".xx." +}; |