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 | 90825e2392b2d70e43c7a25b8a3752299a933894 (patch) | |
tree | e33aa27f02b74604afbfd0ea4f1cfca8833d882a /qtruby | |
download | tdebindings-90825e2392b2d70e43c7a25b8a3752299a933894.tar.gz tdebindings-90825e2392b2d70e43c7a25b8a3752299a933894.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/kdebindings@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'qtruby')
197 files changed, 27359 insertions, 0 deletions
diff --git a/qtruby/AUTHORS b/qtruby/AUTHORS new file mode 100644 index 00000000..3b98da28 --- /dev/null +++ b/qtruby/AUTHORS @@ -0,0 +1,54 @@ +/*************************************************************************** + * (C) 2003-2004 Richard Dale All rights reserved. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +Chief Wreck On The Highway + Richard Dale <Richard_Dale@tipitina.demon.co.uk> + +Bug fixes, enhancements, sample code + Alex Kellett + +Other contributions (from the ChangeLog), bug reports, fixes etc: + Alex Zepeda + Mikhail Yakshin + Imobach Sosa + Marek Janukowicz + Jochen Immend�fer + Dominique Devriese + Han Holl + Thibauld Favre + Zack Rusin + Jim Menard (RuBoids) + Ian Monroe + Raph Bauduin + Stephan Oehlert + David Crosby + Dave M + Caleb Tennis + Blackshack + Armin Joellenbeck + Dmitry Morozhnikor + Michael Doppler + Daniel Morris + Ryutaro Amano + +QtRuby is a ruby version of the PerlQt/Smoke project, written by: + Ashley Winters + Germain Garand + David Faure + +Equipment + 800 Mhz iBook with Yellow Dog 3.0, Macally Micro 3 button mouse + +Books + 'Programming with Qt' by Mathias Kalle Dalheimer + 'Programming Ruby' by David Thomas and Andrew Hunt + 'The Ruby Way' by Hal Fulton + 'Ruby In a Nutshell' by Yukihiro Matsumoto + 'Advanced Perl Programming' by Sriram Srinivasan diff --git a/qtruby/COPYING b/qtruby/COPYING new file mode 100644 index 00000000..6ab293f6 --- /dev/null +++ b/qtruby/COPYING @@ -0,0 +1,297 @@ +Trademarks +---------- + +"[QtRuby": "[Qt]" name usage by permission of Trolltech AS, owner of the +Qt(TM) trademark + + +License +------- + + +NOTE! The GPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the kde programs) are copyrighted +by the authors who actually wrote it. + +--------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/qtruby/ChangeLog b/qtruby/ChangeLog new file mode 100644 index 00000000..89e65292 --- /dev/null +++ b/qtruby/ChangeLog @@ -0,0 +1,1640 @@ +2007-02-19 Richard Dale <rdale@foton.es> + + * Fixed a bug where the sort method of Qt::ListView and Qt::ListViewItem + was no longer working, as the classes are 'Enumerable' and ruby was + calling the ruby sort method instead. Thanks to kelko for reporting it + on #kde-ruby. + +2006-12-05 Richard Dale <rdale@foton.es> + + * Backported some improvements to Qt::Variants from Qt4 QtRuby + * When marhalling QMap types with QVariant values, if the Ruby value isn't + a Qt::Variant then one is created + * Qt::Variants can now be constructed with Hash's of String/Qt::Variant + pairs, and from Arrays of Qt::Variants + * When marshalling QValueList<QVariant> types from Ruby to C++, if any elements + aren't Ruby Qt::Variants, they are automatically converted. Hence, the + following will work: + + v = Qt::Variant.new([1, 2, 3]) + + The Qt::Variant v will contain a list of 3 QVariants with typeName 'int' + * Change all instances of strcmp() to qstrcmp() + +2006-11-20 Richard Dale <rdale@foton.es> + + * Made Qt::ListView, Qt::ListViewItem, Qt::BoxLayout, Qt::HBoxLayout, + Qt::VBoxLayout and Qt::GridLayout Enumerable with implementations + of each() so they don't need to use the Qt External iterators like + Qt::LayoutIterator anymore. For instance: + + lv = Qt::ListView.new do + ["one", "two", "three", "four"].each do |label| + Qt::ListViewItem.new(self, label, "zzz") + end + end + + lv.each do |item| + p item.inspect + pp item.inspect + end + + * Add inspect() and pretty_print() methods to Qt::ListViewItem so that + they show the text of their columns + +2006-09-19 Richard Dale <rdale@foton.es> + + * Upped the QtRuby version to 1.0.13 for the KDE 3.5.5 release + + * Fixed a crash when a slot was inherited by a subclass, and the + subclass had no slots or signals of its own + +2006-09-17 Richard Dale <rdale@foton.es> + + * Fixed bug reported by Caleb Tennis where temporary QString const arguments + were only being deleting after marshalling if they weren't const types. + +2006-09-14 Richard Dale <rdale@foton.es> + + * Added a new variant of connect, which takes a SIGNAL as an argument, along with + a block. For example: + + quit.connect(SIGNAL(:clicked)) { puts 'quit pressed' } + + The block is called in the context of where the connect call was made, and 'self' + needn't be a Qt::Object. It is similar to the signal_connect() method in + ruby-gnome. This was suggested by rickdangerous on the #qtruby irc channel. + +2006-09-13 Richard Dale <rdale@foton.es> + + * Blocks can now be used as slots in Qt::Object.connect() calls. There are two + versions, a singleton method and an instance method. + + * Here is an example of the class method connect() call with a block as a target: + + app = Qt::Application.new(ARGV) + quit = Qt::PushButton.new('Quit') + Qt::Object.connect(quit, SIGNAL('clicked()'), app) { puts 'quit clicked' } + + The block is executed in the context of the target instance, 'app' in this + case. + + * And the instance method form: + + class MyButton < Qt::Button + def initialize(text) + super(text) + connect(self, SIGNAL('clicked()')) { puts 'button clicked' } + end + ... + + The block is executed in the context of self - the instance making the + connect() call. + + * The Rails version of the Ruby 1.9 method Object#instance_exec was used + to invoke the block. Thanks to kelko on the #kde-ruby channel for the + idea and the code, and #rickdangerous for further discussion. + +2006-08-29 Richard Dale <rdale@foton.es> + + * Backported some memory leak fixes from Qt4 QtRuby + +2006-08-10 Richard Dale <rdale@foton.es> + + * The Ruby VALUE to 'uchar *' marshaller wasn't working correctly if the + Ruby string contained nulls. Fixed by Dirk Mueller (thanks) and also + applied for 'char *' types. + +2006-07-12 Richard Dale <rdale@foton.es> + + * The Ruby String to 'char *' and String to 'unsigned char *' were using + the pointer within the Ruby String directly which meant they were + deleted when the Ruby String was gc'd. So they are copied with + strdup () instead. + +2006-06-05 Richard Dale <rdale@foton.es> + + * The metaObject methods for slots and signals are no longer added when + a Qt::Object is constructed, but when slots or signals are added to + a class. This means that signals as well as slots can be added to an + existing instance. + +2006-06-04 Richard Dale <rdale@foton.es> + + * For Qt::QObject classes which are immediate subclasses of Qt::Base, + don't add C metaObject() and qt_invoke() methods as they aren't + needed. This means that a QMetaObject is no longer constructed for + these classes, and the one that the corresponding C++ class has is + returned instead. + +2006-05-20 Richard Dale <rdale@foton.es> + + * Fix regression for dynamic class creation via QMetaObject info causing + a crash. + * A list of properties is now returned via Qt::MetaObject.propertyNames + for a Qt::Object with properties for the inspect and pretty_print + methods. + +2006-05-15 Richard Dale <rdale@foton.es> + + * Removed the customized version of Kernel.exec, Kernel.open etc + as they aren't needed anymore. + +2006-05-14 Richard Dale <rdale@foton.es> + + * When an unknown C++ class is found, a corresponding Ruby class is now + created. For instance, if a KPresenter KPart is loaded, a + KDE::PresenterDoc class is created. + * It is now possible to set and get properties without needing to use + Qt::Object.property() and Qt::Object.setProperty(). For instance: + + factory = KDE::LibLoader.self().factory("libkpresenterpart") + @kpresenter = factory.create(self) + p @kpresenter.presentationDuration + @kpresenter.presentationDuration = true + + * A Qt::Variant.to_ruby method has been added which returns a Ruby value + corresponding to the current value of a Qt::Variant + * A boolean Qt::Variant can now be constructed with a Qt::Variant.new(true) + call, and a dummy second arg (as for C++) is no longer needed. + +2006-05-07 Richard Dale <rdale@foton.es> + + * An an type of Q_UINT16 wasn't working with QtRuby. Fixes problem + reported by maelclerambault. + +2006-05-03 Richard Dale <rdale@foton.es> + + * The qt_emit() and qt_invoke() methods are overriden by the QtRuby + runtime. When they are called the runtime looks for a Ruby slot or + signal matching the call, and invokes it if found. If a Ruby version + wasn't found for a signal however, the qt_invoke() method was being + called in the Smoke library instead of qt_emit(). This occasionally + caused a crash in code using KDE::HTMLPart. + +2006-04-17 Richard Dale <rdale@foton.es> + + * Made :foobar a synonym for 'foobar()' for slots and signals + * Added some calls for Qt::Event.type methods to force them + to go via method_missing() + +2006-03-29 Richard Dale <rdale@foton.es> + + * Don't special case open() in virtual method callbacks, because all + the Qt classes with open methods now have explicit open() method + calls. So it isn't possible that Kernel.open() will be called + wrongly anymore. + +2006-03-29 Richard Dale <rdale@foton.es> + + * Added a KDE::KonsolePart class for when a konsolePart KPart is + dynamically loaded. + +2006-03-21 Richard Dale <rdale@foton.es> + + * Added various explicit calls to method_missing() for methods which + are defined in Kernel and Object, such as 'exec', 'select', 'format' + etc. Otherwise, the corresponding methods in the Smoke library were + not being invoked correctly. + * Removed a virtual destructor compile warning + * Removed obsolete qtruby-only.patch and kdevelop project file + +2006-03-14 Richard Dale <rdale@foton.es> + + * Fixed bugs in const lists marshallers, where the items in the ruby + Array were wrongly being updated after a method call + +2006-02-09 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The Kernel#select method was being redefined as it clashed with + Qt::SqlCursor#select and Qt::SqlSelectCursor#select methods. This + caused a problem when QtRuby was used with Rails ActiveRecord which + also has a select method. So the methods are now defined in the Sql + classes, and use method_missing() to explictly invoke the methods in + the Smoke library. Fixes problem reported by Imo Sosa. + +2006-01-18 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Improved the debug logging of virtual method callbacks so that the + arg types are shown too + +2005-12-16 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The logger_backend() function has been removed and replaced with + qWarning() calls. This was because of problems with getting the + logger_backend() code to work on Windows. + +2005-12-16 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Improved the code to call a C++ slot via qt_invoke() when a ruby slot hasn't + been defined. It now invokes the method in the Smoke lib directly, rather + than going via method_missing(). + +2005-12-08 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The ruby display() method was clashing with a display() method in some + QtRuby classes, and so it was aliased to _display(). However, this caused + problems with the ruby RMagick extension. The display methods are now + special cased in Qt::LCDNumber, Qt::WhatsThis and Qt::TimeEdit. Fixes + problem reported by David Corbin. + * The slots and signals methods are now module methods of Qt::Base, + rather than defined as ordinary methods in class Module, reducing name + space pollution. Thanks to Esteban Manchado for pointing this out. + +2005-12-06 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * QtRuby didn't work with versions of ruby > 1.8.3, as it didn't call + initialize methods correctly. It used the rb_respond_to() method + to check it a newly created qt instance responded to :initialize. + However, in newer versions of ruby rb_responds_to() ignores + private methods such as initialize(). The solution was to just remove + the test, as it was redundant anyway. + * Fixes problem reported by Hans Fugel and Caleb Tennis. + +2005-11-21 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * When dispose() was used to delete a ruby instance, the mapping between + the C++ ptr and corresponding ruby instance was not being removed, and + this caused a crash. Fixes problem reported by Christopher Chan-Nui. + +2005-11-04 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * 'signed int&' types were not being matched or marshalled correctly + +2005-11-02 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * A Qt::Canvas owned by a Qt::CanvasItem is marked as in use + and not needing garbage collection + +2005-10-21 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Argument types of 'unsigned short int' were not working. Fixes + problem reported by Ian Monroe + +2005-10-05 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * More fixes from the Qt4 version + * The Qt::ByteArray class is now a normal Smoke class, + rather than a special case, as it's easier to make a + Qt::ByteArray look like a ruby String, than the other way + round. + * The overloaded method resolution for enum arg types has been + improved + * Added missing relational operators to Qt::Integer + + +2005-09-26 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added some fixes from the Qt4 version of QtRuby + * There was a clash between operator methods in Kernel + for '>', '>=', '<' and '<=' and the ones in the Smoke lib. + * Fixed a Qt::ByteArray marshalling problem + +2005-09-12 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed various problems with the rbuic code generation for database + .ui forms reported by Daniel Morris + +2005-09-11 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed problem with Qt::SqlCursor.select() reported by Daniel Morris + +2005-09-02 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added a Qt::Char.to_s method for converting a Qt::Char to a ruby + string + +2005-08-09 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Caleb Tennis wrote: + One nice feature would be to allow Qt::Object::inherits() to use the QtRuby + naming scheme for valid lookups. For example, right now: + + irb(main):001:0> w = Qt::Widget.new(nil) + irb(main):002:0> w.inherits("Qt::Widget") + => true + irb(main):003:0> w.inherits("Qt::Object") + => false + irb(main):004:0> w.inherits("QWidget") + => true + irb(main):005:0> w.inherits("QObject") + => true + + * Inherits now works for "QObject", and for "Qt::Object" as well. + +2005-08-04 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added a file called 'COPYING' to the qtruby project, with a note that + the 'Qt' trademark in the QtRuby name is used with Trolltech's permission, + followed by the text of the GPL v2 license. + +2005-07-30 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Qt::version and Qt::ruby_version are now module methods + +2005-07-15 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Qt::Socket started working correctly when I and regenerated and rebuilt + my Smoke library. Before then it was calling the wrong version of + QSocket::at() for some reason, and wasn't discarding bytes that had + already been read. + * Removed comment from the client.rb example about Qt::Socket.canReadLine + always returning true now it works. + +2005-07-14 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added example programs for client/server programming with Qt::Socket + and associated classes. client.rb illustrates current bugs in QtRuby + * Qt::Socket.canReadLine() always returns true + * Qt::readLine() seg faults when called a second time + * A memory leak and seg faulting problems like the above were reported + by Caleb Tennis + +2005-07-09 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * When a Qt method returned a QString value type, such as: + QString QSocket::readLine() + A temporary QString was being created that wasn't deleted and caused a + memory leak. Fixes problem reported by Caleb Tennis. + +2005-06-28 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Improved display of Qt::Enums in the KDevelop debugger. With a p + command it just looks like a constant, but a pretty print pp command + shows the type of the underlying C++ enum: + + (rdb:1) p AlignTop + 16 + + (rdb:1) pp AlignTop + #<Qt::Enum:0x1825db68 @type=Qt::AlignmentFlags, @value=16> + +2005-06-04 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Upped the version to 1.0.10 + +2005-06-04 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The Object.id method was clashing with the one in Qt::WidgetStack. + Both methods now work correctly. Fixes problem reported by Dave M. + +2005-05-29 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The rbuic '-subimpl' option now works correctly + +2005-05-29 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The initialize methods in code generated by the rbuic tool, now have + named parameters with default values, rather than '*k' + +2005-05-29 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed bug where the rbuic tool was generating incorrect code + for an array of strings used as a combo box value + +2005-05-28 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added support for the QScintilla text editing widget, including an optional + '--enable-qscintilla=yes' configure option. The QScintilla classes are included in a + Qext:: namespace. Thanks to Blackshack for the idea and configure code. + * The KDE namespace modules are only created if the Korundum extension is used. + Otherwise the Object namespace was being polluted with unused constants. + * Some enums in the QScintilla headers had a spurious comma after + the last enum value, and kalyptus was failing with a parse error. + The comma is now ignored. + +2005-05-25 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * '!=' operator methods in the Smoke library were being shown via ruby introspection when they + can't actually be written in ruby, and are derived from the corresponding '==' operator method. + * '==' operator methods can now be called. + +2005-05-22 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed bug in Smoke code generation where spaces were not being removed from operator + methods such as 'operator *' + * Operator methods are now displayed correctly via ruby introspection + +2005-05-21 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Improved the format of enums displayed by rbqtapi and rbkdeapi + +2005-05-20 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Removed unused Qt::Internal functions + * Added KNS:: to the namespaces expected by the rbkdeapi tool + * Introspection via Object#methods, public_methods, protected_methods and singleton_methods + or Module#constants, instance_methods, protected_instance_methods and public_instance_methods + now all include methods from the Smoke runtime. This fixes part of the request in bug 105772, but + not enabling 'respond_to?' to be used with Smoke methods. + +2005-05-16 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The Kernel.format() method was clashing with the Qt::MimeSource.format() method. + The correct method is now called according to the type of the first argument. Fixes + problem reported by Armin Joellenbeck + +2005-04-29 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Removed superfluous "require 'pp'" statement + +2005-04-25 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Upped the version to 1.0.9 for the RubyForge release + +2005-04-09 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed regressions in the rbqtapi and rbkdeapi utilities caused by the Qt::Internal namespace + tidy up + * Qt::version and Qt::ruby_version had wrongly been moved to the Qt::Internal module + +2005-04-03 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Upped the version to 1.0.8 for the RubyForge release + +2005-03-30 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * An 'include Qt' statement in qtruby.rb where a couple of methods were being added to + class Module was causing all the Qt methods to be added to Module. Oops, this a really + serious bug. Various methods in qtruby.rb are now module methods in the Qt::Internal + module. Big thanks to Eric Veensta and Samir Patel for pointing out this can of worms. + + * It also fixes a problem reported by David Crosby where a "require 'time'" statement was + incompatible with a "require 'Qt'" statement. As the cause was unknown, a hacky workround + had to be added, which is no longer needed. + +2005-03-24 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * When a subclass of QObject is instantiated in the C++ world, and the exact class doesn't + exist in ruby, then scan up the inheritance heirarchy via QObject::metaObject() until a + class is found which does have a ruby equivalent. Fixes problem reported by Dmitry Morozhnikor + where a KViewPart was being returned as a Qt::Object, rather than a KParts::ReadOnlyPart. + * The internal method 'cast_object_to()' now takes a class literal as a second parameter, + rather than the class name as a string + +2005-03-21 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Moved Qt.rb from qtruby/lib/Qt to qtruby/lib. Fixes problem building qtruby on Mac OS X + with extconf.rb reported by Michael Doppler + * Rename 'README.build' as INSTALL + +2005-02-21 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Upped the version to 1.0.7 for the KDE 3.4 release + * Removed the khtml:: namespace as it wasn't being used + +2005-02-07 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added a KNS:: namespace for the KNewStuff library + +2005-02-06 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The KDE::Win::WindowInfo nested class is included in the Korundum runtime + +2005-01-27 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Made some changes to get code generated by the rbkconfig_compiler + to work. When an argument is a non-const reference to a primitive + type, or a QString or QStringList, then don't delete it after the + method call. This is because a class like KConfigSkeleton takes + references, and then 'squirrels them away' - before the references + were just pointing to junk on the stack. + * The method 'isImmutable' is added to KDE::ConfigSkeletonItems + +2005-01-26 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * If a ruby Array is passed as an argument to an overloaded method, give + priority to QStringList args. + +2005-01-21 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * rbuic was giving widgets names containing a '@' to match the ruby instance variable + name. However, this doesn't work with KDE::ConfigDialog which expects the names to + match the ones generated in a KDE::ConfigSkeleton by rbkconfig_compiler so '@' is no + longer added. + +2005-01-20 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed bug reported by Ian Monroe where the KIO.copy() method wasn't being found in + the Smoke runtime. Modules methods are now trapped with method_missing and + despatched to call Smoke methods correctly + * Added support for the KDE::ConfigSkeleton and subclasses. Constructors for nested + classes can now be called correctly + + CCMAIL: ian.monroe@gmail.com + +2005-01-16 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added a Qt::Integer.coerce method so that Qt::Integers and Qt::Enums can be + combined in arithmetic expressions with ruby Integers. + +2005-01-04 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Upped the version to 1.0.6 for the KDE 3.4 beta 1 release + +2005-01-02 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The Qt::Object pretty_print method now shows the class name of enum properties + +2004-12-30 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed an interesting bug reported by Stephan Oehlert. Kernel has a method called + open() which takes a String as the first argument. But when a call is made to an open() + method in the Qt classes, it messes up the method_missing() logic to divert it to the + Smoke library. Instead it attempts to call the Kernel method with the wrong arg types. + + * The problem is fixed by calling the appropriate method based on the + type of the first arg. However, it is no longer possible to override virtual methods + called 'open'. + + CCMAIL: stephan.oehlert@gmx.net + +2004-12-28 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added a parent attribute to the Qt::Object pretty_print() method + * Removed all the properties from the Qt::Object inspect() method except name, + x, y, width, height (the last four for for Qt::Widgets only). This speeds up fetching + the details of Qt::Objects that have more than just a handful of children. + The full details can still be fetched with a pretty_print() call via a debugger 'pp' + command. + +2004-12-21 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The qtruby runtime needs to be able to run the code for an initialize() method up + to the point where the C++ instance has been constructed and wrapped, and then + jump out. It then re-runs initialize() with the wrapped instance. Before a callcc() call + had been used for the jumping which worked fine. However, it made the frame stack + look strange when debugging code with the KDevelop debugger. The fix is to use + catch and throw instead, as they leave the stack in a more normal looking state. + +2004-12-20 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added a work round for a bug caused by an incompatibility between QtRuby + the 'require time' statement, reported by David Crosby + + CCMAIL: dcrosby42@gmail.com + +2004-12-18 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added a 'receivers' property to the Qt::Object inspector. This allows the active signal + connections for a Qt::Object instance to be viewed in the KDevelop debugger as a + Hash. The hash keys are the signal names, and the hash values are arrays of the target + signals/slots as Qt::Connection instances. + +2004-12-17 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added 'metaObject' as an expandable property for Qt::Objects. This allows the + Qt::MetaClass class heirarchy to be browsed, and signals/slots declarations inspected. + +2004-12-17 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed bug in lower case/underscore method naming to camel case conversion + +2004-12-15 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added an attribute of 'children' to the Qt::Object inspect method, which is an array + of the Qt::Object's children + * The QObjects in a QObjectList were not being created with the exact ruby class + if they hadn't been allocated from within the ruby code, and just left as Qt::Objects + +2004-12-14 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Qt::Object properities of type enum are shown by name in the KDevelop debugger, + and not as numeric literals + +2004-12-14 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Changed the format of the Qt::Color inspect string to #<Qt::Color:0x0 #ffffff> + * Added some more attributes to the Qt::Font inspector + * Added a Qt::Cursor inspector + +2004-12-12 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed a bug where the debugger was calling an inspect method on an instance + which wasn't fully initialized yet, and not of type T_DATA. + +2004-12-10 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added inspect() and pretty_print() methods for Qt::SizePolicy + * Greatly improved the Qt::Object Qt property based inspect() and pretty_print() + methods. Property types such as Qt::Point, Qt::Font and Qt::Rect can be + viewed as expandable items in the KDevelop debugger variable tree. + +2004-12-09 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * More inspect() and pretty_print() methods for common classes to improve debugging - + Qt::Color, Qt::Font, Qt::Point, Qt::Rect, Qt::Size, Qt::Date, Qt::DateTime and Qt::Time + +2004-12-08 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added inspect() and pretty_print() methods for Qt::Objects that get the QObject properties and + show them as 'name=value' pairs. This means the KDevelop debugger can make Qt::Object + variables expandable and show their properties. + +2004-11-29 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Upped the QtRuby version to 1.0.5 for the KDE 3.3.2 release + +2004-10-30 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * qError(), qWarning() and qFatal() are now Object instance methods, rather than Qt module + methods. This means they don't need to be referenced with an ugly 'Qt.' module scope anymore. + +2004-10-21 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * If a class method was called before super, it was wrongly throwing an exception + as an error. For instance, this call to i18n(): + + def initialize() + super(TreeList, i18n("My App Preferences"), + Help|Default|Ok|Apply|Cancel, Ok) + ... + + +2004-10-16 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Until super has been called in an initialize() method, Qt methods can't be called on + it. An exception is now thrown 'Instance not initialized', instead of it causing a seg fault. + * For instance: + + class MyWidget < Qt::ComboBox + def initialize + # Must call super first + insertItem('abc') + super + end + end + + * Fixes problem reported by Han Holl + CCMAIL: kde-bindings@kde.org + +2004-10-16 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Upped the version to 1.0.4 for the RubyForge release + * Added some names to AUTHORS from the ChangeLog + +2004-10-14 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * If the smokeruby_mark() function was called for an instance of a QObject, it should + mark all the instances below it in the QObject tree, as not needing garbage collection. + However, if a node in the tree didn't have a mapping onto a ruby instance the marking + process stopped there, even though the grandchildren and their descendants might + have a valid mapping onto ruby instances. + * The solution is to always traverse the entire tree. Fixes problem reported by Han Holl. + + CCMAIL: kde-bindings@kde.org + +2004-10-13 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * All the Qt::CanvasItems owned by a Qt::Canvas are marked with rb_gc_mark() to + prevent them being garbage collected + * The top level widgets are no longer disposed() on Qt::Application exit + * Fixed some bugs in the chart example. + +2004-10-13 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed arg matching to give priority to 'int' or 'signed' when passed a ruby Integer, or + 'double' when passed a ruby Float + * The chart example can now save and load chart files + +2004-10-13 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added greater than and less than operators to Qt::Enum, so that enums can be compared with + Integers + * Added the chart example for Qt Tutorial #2 + +2004-10-12 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added Qt::Application.ARGV. It returns the original ruby ARGV array with Qt command line + options removed. + +2004-10-11 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added a global flag 'application_terminated'. Once this is set the QtRuby runtime will no longer + delete any C++ instances. This will hopefully fix crash on application exit problems reported + by Thibauld Favre. + + CCMAIL: kde-bindings@kde.org + +2004-10-10 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The recent fix for checking invalid arg types, didn't work with nil passed as a value + +2004-10-10 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * A QRgb[] color table was being wrongly deleted after marshalling + +2004-10-10 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * If a ruby method overriding a virtual method returned a primitive type when an instance of a class + was expected, it caused a seg fault. A ruby exception is now thrown instead. Fixes problem reported by + Han Holl. + + * For instance, + class ContainerGrid < Qt::Widget + def sizeHint + # next line should return a Qt::Size, not an integer + 100 + end + end + + Now gives the following error: + + qsize.rb:12:in `method_missing': Invalid type, expecting QSize (ArgumentError) + from qsize.rb:12 + + CCMAIL: kde-bindings@kde.org + +2004-10-10 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The smokeruby_mark() function was only marking the immediate children of a + Qt::Object for not being garbage collected. It now marks all the Qt::Objects + in the object tree below it. + +2004-10-07 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added Qt Designer Tutorial 'Creating Dialogs' translated into QtRuby. It + shows how you can combine ruby code generated from .ui files with the rbuic + tool, with your own code. + + * The .ui files and images are identical to the original C++ versions. + + * It features a simple Makefile to run rbuic when you change the .ui files, and + regenerate the ruby sources. + +2004-10-06 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed rbuic '-embed' option which had been broken after adding DCOP suppot + to Korundum, after changes in the Qt::ByteArray class. + * Fixed QRgb* marshalling problem where the ruby value was overflowing a signed int. + The target for marshalling is now an array of unsigned ints. + * When a Qt::Application exits after returning for an Qt::Application.exec() call, the top + level widgets are deleted as well as the Qt::Application itself. This fixes a problem where + ruby does garbage collection in an arbitrary order after the ruby app has exited. It destroys + a ruby Hash of QMetaData info that the Qt::Application or Qt::MainWindow need to clean up. + * The layout of the ruby code generated by rbuic has been improved with better indenting. + * attr_reader attribute accessors have been added for the most important instance variables + in an rbuic generated class to make them more easily accessed from another class. + +2004-10-05 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Given a C++ instance and an approximate classname, the QtRuby runtime uses the + various Qt rtti mechanisms such as QObject::className() to improve the resolution + of the name. However, the numeric classId into the array of classnames in the Smoke + runtime was not being updated in line with the more accurate name. + * This caused problems with method argument matching which uses the numeric classId + rather than the ruby object's classname, and so QtRuby wrongly assumed that a an + instance of a Qt::Widget was a Qt::Object. + * Fixes problem reported by Han Holl + + CCMAIL: kde-bindings@kde.org + +2004-10-05 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed a couple of errors in the rbuic generated code found as a result of + the recently improved stricter arg type matching. + +2004-10-04 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * When a ruby app exits, rb_gc_call_finalizer_at_exit() is called and all the ruby + instances are garbage collected. The problem is that this is done in an arbitrary + order, and Qt::Application was occasionally crashing in its destructor because + QMetaObject info it still needed was being deleted before then. + + * Fixes problem reported by Thibauld Favre + + CCMAIL: <tfavre@mandrakesoft.com> + +2004-10-04 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * When a QMetaData object was contructed the class name was a pointer from a ruby + string, and was being corrupted when the string was freed. The string is now copied. + +2004-10-03 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Han Holl's report about a when you pass an incorrect arg type to a QtRuby + method, it caused a crash has opened a 'can of worms'. This was because there + was no arg type checking if there was only one candidate method in the + Smoke runtime. Now that arg type checking is applied to all QtRuby method calls, not + not just those that after lookup in Smoke map onto a single method, the overloaded + method resolution via the arg types has had to be greatly improved. This has + been done, and so the arg type matching is now extremely fussy. + + CCMAIL: kde-bindings@kde.org + +2004-10-03 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * An optimization in the overloaded method resolution matching causes a crash; + instead of throwing a ruby exception when a programming error is made. + If there is only one method found in the Smoke runtime, it assumes that it must + be the right one and just takes that. + + * For example: + + lay = Qt::HBoxLayout.new(self) + ed = Qt::LineEdit.new('blah',self) + # The next line should be: lay.addWidget(ed) + lay.addLayout(ed) + + * Fixes problem reported by Han Holl + + CCMAIL: kde-bindings@kde.org + +2004-10-03 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * A common programming error is to accidently leave off the 'new' method call when + creating an instance. The QtRuby runtime wasn't correctly trapping an attempt to + call an instance method on a class object, and was seg faulting rather than + throwing an exception. + + * For example: + + # The next line should be: lay = Qt::HBoxLayout.new(self) + lay = Qt::HBoxLayout + ed = Qt::LineEdit.new('blah',self) + lay.addWidget(ed) + + * Fixes problem reported by Han Holl + + CCMAIL: kde-bindings@kde.org + +2004-10-03 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Upped version to 1.0.3 for the KDE 3.3.1 release + +2004-10-02 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added Ruby Array to QPair<int,int>& marshaller + +2004-09-30 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The resolve_classname() function in handlers.cpp uses the various Qt rtti mechanisms to + get a more accurate classname to instantiate as a ruby instance. It has now been extended + with a callback to the Korundum library to do the same for KDE classes. + + CCMAIL: zack@kde.org + +2004-09-29 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added Jim Menard's ruboids as an OpenGL/Qt::GL* Widgets example + * Improved instructions and exconf.rb for building the gui extension from Michal 'hramrach' Suchanek + +2004-09-13 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Upped the version to 1.0.2 for Rubyforge release + +2004-09-12 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added a 'qui' extension for reading in .ui Qt Designer files at runtime + * For example: + + require 'Qt' + require 'qui' + + a = Qt::Application.new(ARGV) + if ARGV.length == 0 + exit + end + + if ARGV.length == 2 + QUI::WidgetFactory.loadImages( ARGV[ 0 ] ) + w = QUI::WidgetFactory.create( ARGV[ 1 ] ) + if w.nil? + exit + end + w.show() + a.connect( a, SIGNAL('lastWindowClosed()'), a, SLOT('quit()') ) + a.exec() + end + + +2004-09-07 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Upped the version to 1.0.1 for the current Rubyforge release + +2004-09-05 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added Kate:: and KTextEdit:: namespaces for writing Kate plugins + +2004-09-03 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Some newer changes from the uic were added to the rbuic tool. + +2004-09-03 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Brought the rbuic code to uncompress zip files in line with the current uic code + * Added a qmake project file for building the rbuic tool on Mac OS X, and some + notes on how to build QtRuby on a Mac. + +2004-08-29 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added Kontact module to provide a namespace for the kontact plugin api + +2004-08-25 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Underscore naming for can be used for method names instead of camel case if + preferred. Any underscores in method names are removed, and the following + character is capitalised. For example, either of these two forms can be used + to call the same method: + + create_standard_status_bar_action() + + createStandardStatusBarAction() + +2004-08-23 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * A 'thing?' ruby method can now be used as a synonym for either isThing() or hasThing() in the Smoke runtime + +2004-08-04 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Upped the QtRuby version to 1.0.0 - it must work now then.. + +2004-08-02 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added 'long long' and 'unsigned long long' marshallers + +2004-07-29 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The smokeruby_mark() gc marking f'n now marks the QTableItems owned + by a QTable so they don't get garbage collected, even if there are no + remaining references in the user code. + +2004-07-29 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added a template based method for QValueList marshallers, and several + QValueList types. + +2004-07-28 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Removed any marshaller types that weren't in the Smoke runtime from the + type name to marshaller function lookup table. + +2004-07-28 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * If a class doesn't have a virtual destructor, then no mapping was being + kept from the C++ instance to the corresponding ruby value. If the class + had virtual method callbacks, this meant that the ruby instance couldn't + be found, and the callback couldn't be made. + + * Hence, the Qt::ToolTip callback in examples/qt-examples/tooltip didn't + work, as that class doesn't have a virtual destructor. + + * Added an 'isEnum()' function to use when matching args in overloaded + method resolution. + + * QCString arg types are chosen in preference to QByteArray ones, matching + against ruby strings, when resolving an overloaded method call. + + * Qt::Enums and Qt::Integers can be marshalled to uints, longs and ulongs as + well as ints. + + * Added a '==' operator to Qt::Enums so they can be compared with ruby Integers + +2004-07-27 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Qt::Integer arithmetic and bit operations return Qt::Integers, rather + than ruby Integers so that ops can be nested as for Qt::Enums. + +2004-07-27 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The recently added Qt::Integer bit operators were returning a ruby Integer + type. When they were nested, the Integer didn't know how to convert the + Enum it was being or'd with to an Integer. + + * The solution is to add bit operators to the Enum class which return Enums + rather than Integers. + + * The following code didn't work: + + def initialize(message) + super(nil, "passivedlg", + Qt::WStyle_Customize | Qt::WX11BypassWM | Qt::WStyle_StaysOnTop | + Qt::WStyle_Tool | Qt::WStyle_NoBorder) + +2004-07-27 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Replaced QString casts to 'const char *' with latin1() calls + +2004-07-27 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The Qt::Enum class was missing bit operations, so various bit methods + were added to Qt::Enum's superclass, Qt::Integer. + + * This was causing this line from examples/uimodules/uidialogs.rb to fail: + + dlg = KDE::DialogBase.new(parent, "sample_dialog", false, caption, + KDE::DialogBase::Ok | KDE::DialogBase::Cancel, + KDE::DialogBase::Ok, true ) + +2004-07-27 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added error messages for invalid slot or signal declarations + +2004-07-27 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The standard ruby error message for missing constants and methods is + now used when they can't be found in the Smoke library. Removed Alex's + comment about the previous approach, now I agree this is the best way + to show errors. + + CCMAIL: me@lypanov.ne + +2004-07-27 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added qDebug(), qFatal() and qWarning() module methods + +2004-07-26 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The parsing of types passed to slots, (or returned from dcop slots) didn't + work with template types containing commas such as 'QMap<QString,QString>'. + * Added 'QMap<QString,QString>&' and 'QMap<QString,QVariant>&' to the + handlers.cpp string to marshaller lookup table. + +2004-07-26 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added marshallers for QMap<QString,QString> and QMap<QString,QVariant> to and from ruby hashes + +2004-07-26 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The error messages for missing methods or missing constants now give the + name of the missing item. + * For example, this incorrect line: + + color_group.setColor( ColorGroup::MyMissingConst, blue ) + + Gives this error: + + splitter.rb:16:in `const_missing': unresolved constant + reference MyMissingConst (ArgumentError) + + * Implements suggestion from Jeff on the kdebindings list + + CCMAIL: kde-bindings@kde.org + +2004-07-19 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added Qt::Enum type. Before a C++ enum was being marshalled to a ruby + Integer, and the type name of the enum was lost. A Qt::Enum is a subclass + of Integer with an extra type name. + * This fixes a problem with overloaded method resolution where two methods differ + only be an enum type such as this: + + # KPasswordEdit(EchoMode echoMode, QWidget *parent, const char *name); + # KPasswordEdit(EchoModes echoMode, QWidget *parent, const char *name); + + pw2 = KDE::PasswordEdit.new(KDE::PasswordEdit::ThreeStars, page, "") + +2004-07-17 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * After a non-const string arg was passed to a method, the value of the QString + is copied into the ruby value. But it wasn't being correctly converted to the + correct string format according to $KCODE. + * QString to ruby string conversions in QStringLists were not localised either. + +2004-07-14 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Removed superfluous '(void *)' cast which was causing compilation + problems with gcc 3.4 + * Fixes problem reported by Jochen Immend�fer on comp.lang.ruby + CCMAIL: kde-bindings@kde.org + +2004-07-11 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Qt eucJP and Shift-JIS codecs used to support ruby $KCODE values of 's' + for SJIS and 'e' for EUC + +2004-07-08 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added support for strings in QtRuby programs being written in UTF-8. + Use the '-Ku' command line option or '$KCODE=u' in the program. + * Removed recently added QChar marshalling as it wasn't very i18n friendly + +2004-07-07 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added QChar marshalling + +2004-07-06 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fix for passing C++ 'bool*' and 'bool&' argument types + There is a similar problem for bool arg types as with ints, and the mutable + Qt::Boolean class can be used like this: + + # QFont getFont(bool * ok, const QFont&initial, QWidget* parent = 0); + + ok = Qt::Boolean.new + font = Qt::FontDialog.getFont(ok, Qt::Font.new("Helvetica [Cronyx]", 10), self) + if !ok.nil? + # font is set to the font the user selected + else + # the user canceled the dialog + end + + Use 'nil?' to test the value returned in the Boolean + * Signal and slot type signatures are 'normalized' and any unwanted white space + removed + * Fixed problem with QByteArray arg type matching in overloaded method resolution + +2004-07-04 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fix for passing C++ 'int*' and 'int&' argument types + Ruby passes numeric values by value, and so they can't be changed when passed to a + method. The Qt::Integer class provides a mutable numeric type which does get updated + when passed as an argument. For example, this C++ method 'findByFileContent()': + + # static Ptr findByFileContent( const QString &fileName, int *accuracy=0 ); + + acc = Qt::Integer.new(0) + fc = KDE::MimeType.findByFileContent("mimetype.rb", acc) + + It supports the arithmetic operators, and so expressions such as 'acc + 3' + will work. + +2004-07-02 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Replaced obsolete STR2CSTR() calls with StringValuePtr() + +2004-06-29 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The internal findAllMethods() method now returns nil rather than an empty array + if the given classid is greater than 'smoke->numClasses'. Otherwise, it wasn't + possible to distinguish between a class with no methods, or reaching the end of + the classes array. + +2004-06-29 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added QStrList marshalling + +2004-06-29 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Improved Garbage Collection, dispose() and isDisposed() methods + - When a ruby instance is garbage collected, the underlying C++ instance will only be + deleted if it isn't 'owned' by a parent object. Normally this will 'just work', but + there are occasions when you need to delete the C++ ahead of garbage collection, and + whether or not it has a parent. Use the dispose() and isDisposed() methods like + this: + + item0.takeItem(item1) + item0.insertItem(item1) + + item2.dispose + if item2.isDisposed + puts "item2 is disposed" + end + + - Fixes problem reported by Hans Fugel + +2004-06-26 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The marshalling TypeHandler function pointers are now looked up in a + QAsciiDict, rather than a ruby Hash. + * Some unused functions removed + * QtRuby version upped to 0.9.8 + +2004-06-09 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * New flags were added for Smoke methods - mf_internal and mf_copyctor. + This allows copy constructors which are only used internally by the ruby + runtime, to be excluded from the standard api. + + +2004-05-12 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * When ARGV was passed to the Qt::Application constructor, it was being + altered, and the name of the ruby program added as a first entry. The + constructor now uses a copy of the array, and ARGV is left unchanged. + +2004-05-03 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added a '-kde' option to the rbuic tool to generate require 'Korundum' + instead of require 'Qt' statements, and use KDE widgets. + +2004-04-30 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Applied patch from Marek Janukowicz. + * The patch fixes some perlisms, that caused errors on loading a Ruby file + generated from .ui file for Qt::MainWindow subclass + +2004-04-20 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The rbuic now correctly names KDE widgets with a KDE:: prefix + * If the rbuic -x option is used, a KDE top level is generated if any + KDE widgets have been found. + +2004-04-17 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed bug in rbuic generated code for setResizeMode() + +2004-04-05 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Improved classname resolution for QListViewItem and QTableItem subclasses using rtti() calls + +2004-03-26 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Ruby slots and signals are now inherited correctly + * This causes problems with code generated by the rbuic utility + * Fixes bug 78273 reported by Imobach Sosa + +2004-03-10 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * When a mutable, non-const QString, int*, int&, bool* or bool& is passed + to a method, the corresponding ruby value is now updated after the + method call. + * Some f'ns are no longer static, so that the korundum extension can link + against them. + +2004-03-03 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The f'n new_qobject is no longer static, and can be called from korundum + +2004-03-01 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed bugs in QCString, QString and QByteArray marshalling. + - When a C++ method with a mutable, non-const QCString arg type + is called from ruby, the C++ arg value is copied back into the + corresponding ruby arg VALUE after the call. + - A pointer to a locally allocated QString was being returned, + giving memory corruption problems. + * The default debug level in qtruby.rb is DebugLevel::OFF, otherwise + very verbose error messages are produced. + +2004-01-28 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed bug where a QCString was being ranked equally with a QString, + when resolving ambiguous method overloading. Caused the KDE::URL + constructor to fail with a string arg. + +2003-11-29 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added DOM:: namespace for the DOM:: classes in the Smoke library. + * The scope operator in nested classnames is now '::' rather + than '__', so changed the qtruby runtime to expect that. + +2003-11-13 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added the KillerFilter event filtering example from chapter 16 + of 'Programming with Qt' + * Improved classname resolution via the Qt rtti mechanisms in + QObject, QEvent and QCanvasItem. This fixed a problem in the + KillerFilter example when a QMouseEvent was passed to the ruby + event handler, it was being instantiated as a ruby Qt::Event, + rather than a Qt::MouseEvent. + +2003-11-11 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Improved nil argument matching, and nil can match any type now + not just classes. Translated the code from the Qt.pm in PerlQt, + after discussion on the kde-perl list. + * Fixed bug in rbuic where a C++ 'QString::null' was "" in ruby, and + should have been a nil. + +2003-11-08 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Finally fixed huge leak, causing the progress.rb example to grow by + 1 Mb a minute. + * Added a cache from ruby classname to Smoke class id + * Speeded up method selector look ups + * Put some C++ code inside blocks to ensure that the destructor clean + up code was called, when the current f'n longjmp's up the stack rather + than returns normally. + * QtRuby looking good, raised the version to 0.9.6 to celebrate + +2003-11-07 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed some memory leaks + * Ensured that any instance 'owned' by ruby, ie with the the allocated + flag set to true, is always added to the pointer_map. + Otherwise, the same C++ instance can be wrapped twice and will be deleted + twice. + +2003-11-03 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * When marshalling a const ref type to a ruby VALUE, it is now copied + * Applied some fixes for construct_copy() from the PerlQt version of + handlers.cpp + * Fixed some minor bugs in the progress.rb Qt example + +2003-11-03 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added method selector caching for class methods and constructors, + although performance still 'sedate' compared with C++ or Perl. + +2003-10-29 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The smokeruby_mark() f'n now marks the QListViewItems in a QListView + * Fixed a QLayoutItem crash in smokeruby_free() + +2003-10-27 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The smokeruby_mark() f'n was completely wrong, as it is only called if + the current object is already marked. So marking the current object + doesn't make a lot of sense. Instead, if the instance is a kind of + QObject then its childeren are marked. + * The smokeruby_delete() object doesn't delete instances which have + parents of one sort or another. + * Made some fixes to the tutorial examples + * Removed equality '==' operator overloading as it only expects a single + arg in ruby, and not two. + +2003-10-22 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Changed some error messages in do_method_missing() to be 'debug only' for now. + +2003-10-22 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Got the checklists.rb example working after bug report from Mikhail Yakshin + - Corrected some coding errors in the example itself. + - The arg matching code in method_missing() has been improved and simplified. + - In the overloaded arg type resolution matching, an enum is type 'long' + - A class which matches exactly is a better match than a subclass. + Multiple matches are allowed as long as there is a 'best match'. + - Operator overloading now looks for methods of the form 'operator+=', + 'operator-=' etc in the Smoke runtime. + +2003-10-14 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed serious random crash problem + - qt_invoke() and qt_emit() were not calling super if a ruby signal or + slot hadn't been found, which stopped C++ signals from working. + - This prevented destroy() signals from invoking event filter list clean + up when a QObject was deleted, leaving deleted instances in the list. + - 'QUObject*' args are now marshalled as a ruby list with a single + entry of a VALUE wrapping the pointer. + * The ruby ALLOCA_N macro is no longer used as it is based on alloca(), + which doesn't seem a good idea. malloc(), calloc() and free() are used + instead + +2003-10-11 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added khtml:: namespace, although it isn't in the SmokeKDE runtime yet + * Improved method_missing error messages if a method can't be resolved + +2003-10-09 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added KIO:: and KParts:: namespaces for the new classes in libsmokekde + +2003-10-08 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Korundum KDE ruby extension + - made various changes so it can be linked against the QtRuby code + +2003-09-18 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Removed leading 'K' from class names of when adding to KDE:: namespace. + As per Germain Garand's suggestion on the kde-bindings list: + + ..actually, I used the same scheme as for Qt when possible, that is: + $class =~ s/^Q/Qt::/ or + $class =~ s/^K/KDE::/ or + $class = "KDE::" . $class + unless $class eq "Qt"; + +2003-09-18 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added support for KDE classes under module KDE:: for use with + the new libsmokekde.so. You can now write KDE programs in Ruby, + here is Hello World: + + about = KDE::KAboutData.new("one", "two", "three") + KDE::KCmdLineArgs.init(1, ["four"], about) + a = KDE::KApplication.new() + hello = KDE::KPushButton.new(nil) { setText "Hello World" } + a.setMainWidget(hello) + hello.show() + a.exec() + +2003-09-14 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The rbuic -embed option finally works. Fixed QByteArray marshalling. + +2003-09-13 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Improved the rbuic -embed option, and added some fixes + +2003-09-12 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * More passing blocks to constructors fun! You can now also pass + an arg to the block, and it will be run in the context of the arg: + + w = MyWidget.new { |theWidget| theWidget.setCaption "foobar" } + + I got this idea from the FXRuby bindings, from the ChangeLog: + + "This approach has the advantage that the initialization code now has + access to the outer scope, as blocks normally would. Thanks to + Rich Kilmer for this suggestion." + + If you don't pass an arg to the block, the block is run in the context + of the new instance as before. + + * Debugging left in handlers.cpp (must stop doing this) + +2003-09-12 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Marshallers now return Qnil, rather than Qundef (for internal use only) + +2003-09-10 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Improved garbage collection with various 'ad hoc' rules from the + QtJava bindings about when it's ok/not ok to destruct an instance. + Not always just a 'parent() != 0' check in Qt. + +2003-09-10 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added QByteArray <-> Ruby string marshaller + +2003-09-09 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Blocks can now be passed to constructors, like this: + w = MyWidget.new { setCaption("foobar") } + +2003-09-08 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added method selector caching, scribbling may be slightly faster.. + +2003-09-08 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * GC getting closer to working. Debugging code improved. + +2003-09-08 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * From below 'watch out for extra crashes!' - indeed! Have now disabled + smokeruby_free() and smokeruby_mark() until garbage collection works. + +2003-09-08 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * The pointer_map is now a QPtrDict rather than a ruby Hash, and + the entries are weak references. An implementation was tried + using the ruby WeakRef class, but it didn't work because rb_gc_mark() + didn't prevent them being garbage collected. + * smokeruby_free() and smokeruby_mark() have been implemented + * The C++ instance for a ruby object is deleted in smokeruby_free(), + so watch out for extra crashes! + * Improved README with more details about QtRuby + +2003-09-07 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Improved error message handling, changed rb_error() calls to rb_raise() + * Decided changing method calls like foobar? to isFoobar() not a good idea, + as the Qt property could just as also be hasFoobar() or foobar() + +2003-09-06 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Set methods such as autoMask= are converted to setAutoMask() + * Get methods such as modal? are converted to isModal() + +2003-08-31 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * rbuic code generation brought up to date wrt Qt 3.2 uic + Main change is that a 'languageChanged()' method is generated + +2003-08-30 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * rbuic() code generation fixes + +2003-08-30 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added 'Run Selection' menu option to the QtRuby shell + +2003-08-30 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Operator methods are now called 'operator..' in QGlobalSpace and not renamed + * Added unary minus, and a test in opoverloading.rb + +2003-08-29 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Updated TODO list, improved rbuic code generation for images + +2003-08-28 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Improved operator overloading to work with operators not in QGlobalSpace + +2003-08-27 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Changed the operator overloading implementation to be more like PerlQt + +2003-08-27 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Translated the rbqtsh filePrint() method from perl + +2003-08-26 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added 'changed' attribute to the MetaInfo class, so that the C++ metaObject + is reconstructed if the slots or signals are changed. + * Changed window title on rbqtsh to 'QtRuby'. The example slot now works + correctly. Only just tried this utility - Wow Alex!! + +2003-08-26 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * findMethod() now looks in the QGlobalSpace pseudo class as well as the normal target class. + * The bitBlt() code in the scribble example works correctly + +2003-08-25 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Removed ugly _current_object global variable. + The current target object is now passed to the MethodCall constructor. + +2003-08-25 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Removed obsolete rb_enable_super() calls + * Removed test for _current_object in class_method_missing() + * Fixed missing index in signalInfo() method + * Added Qt scribble example - TODO add Qt::PaintDevice.bitBlt() calls to SMOKE runtime + +2003-08-23 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added rbuic tool for ruby Qt Designer support + +2003-08-12 Alexander Kellett <lypanov@kde.org> + + * Add debug level setting via Qt::debug_level + * When calling .new on a Qt object with a incorrect prototype + the list of appropriate constructors is printed + * Fix a number of cases in which imperfect code would cause a crash + +2003-08-11 Alex Zepeda <zipzippy@sonic.net> + + * Various fixes to get QStringList marshalling working + * Treat Ruby strings as UTF-8 strings in order to fix the QFont examples + +2003-08-09 Alexander Kellett <lypanov@kde.org> + + * Added support for operator overloading + +2003-08-07 Alexander Kellett <lypanov@kde.org> + + * Added rbqtapi and rbqt tools (port of the PerlQt tools of the same name) + +2003-08-06 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added some TODO list entries, added Alex to the AUTHORS list + +2003-08-06 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed 'int&' marshalling so script name appears in window title + +2003-08-12 Alexander Kellett <lypanov@kde.org> + + * Add several new marshalling types - QCanvasItemList for example, + unfortuantely due to several improvements in Qt 3.2 these + improvements will not be seen when compiling against Qt 3.1.2 + +2003-08-05 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Moved SLOT(), SIGNAL() and emit() to global scope + +2003-08-05 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Removed redundant 'rb_eval_string("include Qt")' call from extension initialization. + +2003-08-05 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Qt::Application constructor safer, but program name still not appearing in the window title + +2003-08-05 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed bug in resolution of overloaded Qt::Popup.insertItem() methods + +2003-08-05 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Use new const_missing from ruby 1.8.x to allow for constant access from base class, for example "Qt::RichText" + * QString::null now maps onto Qnil, rather than a zero length ruby string + +2003-08-04 Alexander Kellett <lypanov@kde.org> + + * Allow for accumulative slots/signals declarations in a class + * Minor build fixes + +2003-08-02 Alexander Kellett <lypanov@kde.org> + + * Fix several deprecation warnings when running under 1.8.x + * Several more build fixes + +2003-08-01 Alexander Kellett <lypanov@kde.org> + + * Slightly improve ease of debugging of qtruby programs + which subclass Qt base classes by print out some useful + debugging informationn when/if method_missing ever + fails to find a matching function in the baseclass. + +2003-08-01 Alex Zepeda <zipzippy@sonic.net> + + * Remove need to manually run extconf.rb by some automation via a configure.in.in + * Various other build fixes + +2003-07-31 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed bug in marshalling QString::null to a ruby VALUE + +2003-07-31 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Changed require in Qt.cpp to 'Qt/Qt.rb' instead of 'lib/Qt/Qt.rb' + +2003-07-31 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Fixed problem with non-working installed version, lib/Qt.rb moved to lib/Qt/Qt.rb + +2003-07-30 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * QtRuby - a Ruby SMOKE adaptor for Qt, initial checkin + + diff --git a/qtruby/INSTALL b/qtruby/INSTALL new file mode 100644 index 00000000..86e05d3d --- /dev/null +++ b/qtruby/INSTALL @@ -0,0 +1,144 @@ +Dependencies +------------ + +- ruby 1.8 or greater +- automake 1.7 or greater +- Qt 3.1 or greater +- KDE 3.1 or greater (for korundum) + +CVS Snapshot +------------ + +If building from a CVS snapshot, this initial make command in needed: + +$ make -f Makefile.cvs + +Configure flags needed +---------------------- + +In order to build korundum: (this is the default in fact, so you can just use ./configure) + ./configure --with-smoke="qt kde" +in order to build just qtruby: + ./configure --with-smoke="qt" + +Building the Qt extension without KDE build process +--------------------------------------------------- + +To build the Qt.so extension, provide 'extconf.rb' with the directories where +the Qt and Smoke libraries are installed: + +$ cd qtruby/rubylib/qtruby +$ ruby extconf.rb --with-smoke-dir=/opt/kde3 \ + --with-qt-dir=/home/duke/src/kde/HEAD/qt-copy +$ make +$ make install + +Building on Mac OS X +-------------------- + +Automake/autoconf don't work very well on Mac OS X, and so qmake and +exconf.rb must be used instead. You can use the native Aqua/Quartz Qt/Mac +version of Qt - QtRuby doesn't require X11. + +Build Qt/Mac and install in /Developer/qt. These configure flags work well: + +./configure -system-zlib -qt-gif -plugin-imgfmt-mng -thread -no-stl +-no-exceptions -fast + +Configure QtRuby and build the Smoke library: +$ ./configure '--with-qt-dir=/Developer/qt' '--enable-mac' +$ cd smoke/qt +# Generate the Smoke library sources: +$ perl generate.pl +# Overwrite the automake generated Makefile: +$ qmake -makefile +# Build the Smoke library and install into /usr/lib +$ sudo make +... + +# Build the 'Qt' extension: +$ cd qtruby/rubylib/qtruby +$ ruby extconf.rb --with-qt-dir=/Developer/qt --with-smoke-dir=/usr \ + --with-smoke-include=../../../smoke +$ make +$ sudo make install +... + +# Build the rbuic tool and install into /usr/bin +$ cd qtruby/rubylib/designer/rbuic +$ qmake -makefile +$ sudo make +... + +# Build the 'qui' QWidgetFactory extension +$ cd qtruby/rubylib/designer/uilib +$ ruby extconf.rb --with-qtruby-include=../../qtruby --with-qt-dir=/Developer/qt +$ make +$ sudo make install + +To get Mac menus to appear, when a QtRuby program is run from the command line, +link ruby against the carbon resource lib and call it 'rubyw', see: + +http://www.rubygarden.org/ruby?RubyTkOnOSX + +"Create rubyw executable for MacOS?: the Win32 distribution of ruby includes a +'rubyw.exe' so I decided to attempt creating a 'rubyw' executable: + +On my machine, ruby is installed at /usr/local/bin/ruby, so I did sudo +cp /usr/local/bin/ruby /usr/local/bin/rubyw +Using the suggestion directly above (on this page), I then ran +sudo /Developer/Tools/Rez -t APPL Carbon.r -o rubyw +I was then able to successfully run an example RubyTk app by +running /usr/local/bin/rubyw ruby-1.8.0/ext/tk/sample/tkmenubutton.rb +Everything worked as expected, and I was able to interact with the UI without +any SetFrontProcess errors. " + +Ryutaro Amano wrote this about creating a double clickable bundle: + +"Thanks all, I have succeeded make rubyw. +sample script is launched in foreground. +And I have known the following steps to launch qt-ruby script by double clicking +from Finder. + +As an example, I take qtruby-1.0.11/qtruby/rubylib/tutorial/t14 + +cd qtruby-1.0.11/qtruby/rubylib/tutorial/t14 +chmod 755 t14.rb +replace first line of t14.rb with "#!/usr/local/bin/rubyw" +mkdir -p Shoot.app/Contents/MacOS +mv t14.rb Shoot.app/Contents/MacOS/Shoot +cp {cannon.rb,gamebrd.rb,lcdrange.rb} Shoot.app/Contents/MacOS +cp Info.plist Shoot.app/Contents + +Info.plist is + +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> +<plist version="0.9"> +<dict> + <key>CFBundleExecutable</key> + <string>Shoot</string> + <key>CFBundleIdentifier</key> + <string>jp.co.ryutaro.qtquit</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleSignature</key> + <string>????</string> +</dict> +</plist> + +echo -n "APPL????" > Shoot.app/Contents/PkgInfo + +Double click Shoot.app, Qt-Window appears in foreground." + +QScintilla text editing widget support +-------------------------------------- + +QScintilla is a text editing widget with syntax highlighting for a +number of languages including ruby. It can be downloaded from here: + +http://www.riverbankcomputing.co.uk/qscintilla/download.php + +Use the '--enable-qscintilla=yes' configure option to build QtRuby with +QScintilla support. The classes in a 'Qext::' module, with names such as +Qext::Scintilla. diff --git a/qtruby/Makefile.am b/qtruby/Makefile.am new file mode 100644 index 00000000..fc090dfa --- /dev/null +++ b/qtruby/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = rubylib bin + +EXTRA_DIST = rubylib.kdevprj AUTHORS ChangeLog README TODO rubylib.lsm diff --git a/qtruby/README b/qtruby/README new file mode 100644 index 00000000..22dee743 --- /dev/null +++ b/qtruby/README @@ -0,0 +1,259 @@ +/*************************************************************************** + * (C) 2003 Richard Dale All rights reserved. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + ***************************************************************************/ + +Here is a Ruby SMOKE adaptor for Qt + +Why ruby? From the rubytalk list + +On 8/28/03 8:56 PM, "Scott Thompson" wrote: + +>> : Can anyone give me a good reason why I would want to use Ruby over +>> Python? +>> +>> Ruby smells better than Python. Also, it has cuter girls. +>> +>> Python sometimes tastes better if you prepare it right. +> +> I hadn't noticed the odor thing. It does have a faintly floral aroma +> doesn't it. +> +> Of course it is no surprise that you can get more and cuter girls with +> Rubies than you can with Pythons. +> +> Scott + +So there you have it! :) + +Here is 'Hello World' in QtRuby: + +#!/usr/bin/ruby -w + +require 'Qt' + +a = Qt::Application.new(ARGV) +hello = Qt::PushButton.new("Hello World!", nil) +hello.resize(100, 30) +a.setMainWidget(hello) +hello.show() +a.exec() + +Ruby 1.8 is unfortunately implicitly required as with 1.6.x it is not possible to: + + Make dynamic constants available (thus forcing syntax such as Qt.RichText rather than Qt::RichText)<br> + Call super in the initialize method thus making subclassing of non trivial classes impossible + +QtRuby features a very complete coverage of the Qt api: + + - You can call all Qt public and protected methods, and all friend methods + such as bitBlt() etc + + - Virtual methods + All virtual methods can be overriden, not just event handlers + + - Properties + 'fooBar = 5' is a synonym for 'setFooBar(5)' + + - Predicates + 'if foo?' is a synonym for 'if isFoo()' or 'if hasFoo()' + + - Use underscore naming for method names instead of camel case if you + prefer. Any underscores in method names are removed, and the following + character is capitalised. For example, you can use either of these two + forms to call the same method: + + create_standard_status_bar_action() + + createStandardStatusBarAction() + + - Operator overloading + The full range of Qt operator methods is available, for example: + + p1 = Qt::Point.new(5,5) => (5, 5) + p2 = Qt::Point.new(20,20) => (20, 20) + p1 + p2 => (25, 25) + + - Declare signals and slots + Signals and slots are declared as list of strings like this: + + slots 'setColor(QColor)', 'slotLoad(const QString&)'.. + signals 'clicked()'.. + + Currently C++ type signatures must be used, a future version of QtRuby + will allow ruby type signatures instead. + + Connect slots and signals like this: + + Qt::Object.connect( @_colormenu, SIGNAL( "activated( int )" ), + self, SLOT( "slotColorMenu( int )" ) ) + + And emit signals like this: + + emit colorChanged( black ) + + - Constructors + You can call constructors in the conventional style: + + quit = Qt::PushButton.new("Quit", self, "quit") + + Or you can pass a block if you prefer: + + w = MyWidget.new { setCaption("foobar") } + + The block will be called in the context of the newly created instance. + + Ordinary arguments can be provided as well as a block at the end: + + w = MyWidget.new(nil) { setCaption("foobar") } + + They are run in the context of the new instance. + + And there's more! You can also pass an arg to the block, and it will + be run in the context of the arg: + + w = MyWidget.new { |theWidget| theWidget.setCaption "foobar" } + + - Garbage Collection + When a ruby instance is garbage collected, the underlying C++ instance will only be + deleted if it isn't 'owned' by a parent object. Normally this will 'just work', but + there are occasions when you need to delete the C++ ahead of garbage collection, and + whether or not it has a parent. Use the dispose() and isDisposed() methods like this: + + item2.dispose + if item2.isDisposed + puts "item2 is disposed" + end + + - C++ 'int*' and 'int&' argument types + Ruby passes numeric values by value, and so they can't be changed when passed to a + method. The Qt::Integer class provides a mutable numeric type which does get updated + when passed as an argument. For example, this C++ method 'findByFileContent()': + + # static Ptr findByFileContent( const QString &fileName, int *accuracy=0 ); + + acc = Qt::Integer.new(0) + fc = KDE::MimeType.findByFileContent("mimetype.rb", acc) + + It supports the arithmetic operators, and so expressions such as 'acc + 3' will work. + + - C++ 'bool*' and 'bool&' argument types + There is a similar problem for bool arg types, and the mutable Qt::Boolean class can be + used like this: + + # QFont getFont(bool * ok, const QFont&initial, QWidget* parent = 0, const char *name = 0); + + ok = Qt::Boolean.new + font = Qt::FontDialog.getFont(ok, Qt::Font.new("Helvetica [Cronyx]", 10), self) + if !ok.nil? + # font is set to the font the user selected + else + # the user canceled the dialog + end + + Use 'nil?' to test the value returned in the Boolean + + - Debugging + If a method call can't be matched in the Smoke library giving a 'method_missing' + error, you can turn on debugging to trace the matching process: + + a = Qt::Application.new(ARGV) + Qt.debug_level = Qt::DebugLevel::High + a.loadLibrary("foo") # Non existent method + + Will give the following output: + + classname == QApplication + :: method == loadLibrary$ + -> methodIds == [] + candidate list: + Possible prototypes: + static QWidget* QApplication::widgetAt(int, int, bool) + ... + + Here, the list of candidate methods 'methodIds' is empty + + Another debugging mechanism allows various trace 'channels' to be switched on. + + You can trace virtual method callbacks: + Qt::Internal::setDebug(Qt::QtDebugChannel::QTDB_VIRTUAL) + + Or trace QtRuby garbage collection: + Qt::Internal::setDebug(Qt::QtDebugChannel::QTDB_GC) + + - String i18n + + QtRuby supports $KCODE values of 'u', 'e' and + 's' or the corresponding '-K' options from the command line. Qt Designer + .ui files have UTF-8 strings so if you use any 8 bit UTF-8 characters, you + will need to set $KCODE='u' or use the -Ku command line option. + + - Qt Designer + A 'rbuic' tool is included in qtruby/rubylib/designer/rbuic to compile + .ui files into ruby code. As described above, Qt Designer uses UTF-8. + In addition to the options in the original uic C++ utility an '-x' flag + has been added. This will generate a top level stub in the code: + + $ rbuic mainform.ui -x -o mainform.rb + + Will add this to the end of the generated code: + + if $0 == __FILE__ + a = Qt::Application.new(ARGV) + w = MainForm.new + a.setMainWidget(w) + w.show + a.exec + end + + Then you can test the example code straight away: + + $ ruby mainform.rb + + - Loading .ui files at runtime with QWidgetFactory + You can load a Qt Designer .ui file at runtime with the qui extension, + for example: + + require 'Qt' + require 'qui' + + a = Qt::Application.new(ARGV) + if ARGV.length == 0 + exit + end + + if ARGV.length == 2 + QUI::WidgetFactory.loadImages( ARGV[ 0 ] ) + w = QUI::WidgetFactory.create( ARGV[ 1 ] ) + if w.nil? + exit + end + w.show() + a.connect( a, SIGNAL('lastWindowClosed()'), a, SLOT('quit()') ) + a.exec() + end + + - QtRuby shell + You can use the QtRuby shell in bin/rbqtsh to create widgets + interactively from the command line. + + - API reference + Use the bin/rbqtapi tool to discover which methods are available in + the QtRuby api. + + - Example programs + The best way to start programming QtRuby is to look at some existing + code and start messing with it.. + The are various samples under qtruby/rubylib/examples. + + - Optional QScintilla text editing widget support + Great for building your own ruby IDE.. + +Have Fun! + +-- Richard diff --git a/qtruby/README.1st b/qtruby/README.1st new file mode 100644 index 00000000..00c2cd4e --- /dev/null +++ b/qtruby/README.1st @@ -0,0 +1,17 @@ +Startup Crashes +--------------- + +For some strange reason ruby occasionally needs to be compiled +with --enable-pthread. Gentoo at the very least doesn't default +this to using this unfortunately. Even worse, using this flag +makes programs using the gnome2 bindings randomly crash so its +highly unlikely it'll get into distributions / ruby by default. + +There is a workaround however: + +With the program krubyinit from korundum/bin installed it is +possible to simply replace a programs shebang line with: + #!/usr/bin/env krubyinit +Incidently as well as majorly fixing up pthread related startup +crashes, this also makes it possible for kdeinit_wrapper usage, +therefore improving startup speed yet further. diff --git a/qtruby/TODO b/qtruby/TODO new file mode 100644 index 00000000..aca08299 --- /dev/null +++ b/qtruby/TODO @@ -0,0 +1,13 @@ + * Implement the special case method in Qt::Image constructor + + * Allow 'String' and other ruby types to be used in SIGNALs/SLOTs + + * More documentation or example code - contributions welcome! + + * See the separate TODO list for the rbuic tool + + * Write a rubyeditor plugin for Qt Designer + + * Add an option to rbqtapi to show the api in ruby format, rather than C++ + + * Write a graphic class browser based on rbqtapi diff --git a/qtruby/bin/Makefile.am b/qtruby/bin/Makefile.am new file mode 100644 index 00000000..e53bd0fc --- /dev/null +++ b/qtruby/bin/Makefile.am @@ -0,0 +1,9 @@ +INCLUDES = $(all_includes) -I$(RUBY_ARCHDIR) + +bin_PROGRAMS = qtrubyinit +qtrubyinit_LDFLAGS = -module $(all_libraries) -version-info 0:0:0 -L$(top_srcdir)/smoke/qt/ -L$(RUBY_LIBDIR) +qtrubyinit_METASOURCES = AUTO +qtrubyinit_SOURCES = qtrubyinit.cpp +qtrubyinit_LDADD = $(LIB_QT) $(RUBY_LIBRUBYARG) $(top_builddir)/smoke/qt/libsmokeqt.la $(top_builddir)/qtruby/rubylib/qtruby/libqtrubyinternal.la + +bin_SCRIPTS = rbqtapi rbqtsh diff --git a/qtruby/bin/qtrubyinit.cpp b/qtruby/bin/qtrubyinit.cpp new file mode 100644 index 00000000..2451e002 --- /dev/null +++ b/qtruby/bin/qtrubyinit.cpp @@ -0,0 +1,29 @@ +#include <ruby.h> + +/*************************************************************************** +qrubyinit - makes use of kdeinit_wrapper possible for ruby programs +------------------- + begin : Wed Jan 7 2004 + copyright : (C) 2004 by Alexander Kellett + email : lypanov@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. * + * * + ***************************************************************************/ + +// this name can be used to allow apps +// to detect what they were started with +static const char* script_name = "qrubyinit_app"; + +int main(int argc, char **argv) { + ruby_init(); + ruby_script((char*)script_name); + ruby_options(argc, argv); + ruby_run(); +} diff --git a/qtruby/bin/rbqtapi b/qtruby/bin/rbqtapi new file mode 100755 index 00000000..30735b48 --- /dev/null +++ b/qtruby/bin/rbqtapi @@ -0,0 +1,103 @@ +#!/usr/bin/env ruby + +# Note: this program is part of qtruby and makes use of its internal functions. +# You should not rely on those in your own programs. + +require 'getopts' +getopts('r:hvimp') + +case File.basename $0 +when "rbqtapi" + require 'Qt' +when "rbkdeapi" + require 'Korundum' +end + +if $OPT_v + # TODO add and use version number #{Qt::VERSION} + print "qtruby using Qt-#{Qt::version}\n" + exit 0 +elsif $OPT_h + print $usage + exit 0 +end + +if $OPT_m + while 1 + line = STDIN.readline.chomp + line.gsub!(/^Q(?=[A-Z])/,'Qt::') + line.gsub!(/^K/,'KDE::') unless line =~ /^(KDE)|(KIO)|(KParts)|(KNS)/ + classid = Qt::Internal::find_pclassid($_) + puts "__START__" + if classid + a = Qt::Internal::findAllMethods(classid) + ids = (a.keys.sort.map{|k|a[k]}).flatten + candidates = Qt::dumpCandidates(ids) + sup = [] + Qt::Internal::getAllParents(classid, sup) + sup.each { + |sup_item| + a = Qt::Internal::findAllMethods(sup_item) + ids = (a.keys.sort.map{|k|a[k]}).flatten + candidates << Qt::Internal::dumpCandidates(ids) + } + candidates.gsub("\t","") # erm. whats the "s" mean on s/\t//gs ? + print candidates + end + puts "__END__" + end +end + +search_string = ARGV[0] ? ARGV[0].dup : nil +search_string.gsub!(/^Q(?=[A-Z])/,'Qt::') if search_string +# search_string.gsub!(/^K(?=[^D][^E])/,'KDE::') if search_string +search_string.gsub!(/^K/,'KDE::') unless search_string.nil? or search_string =~ /^(KDE)|(KIO)|(KParts)|(KNS)/ +classid = search_string ? Qt::Internal::find_pclassid(search_string) : 1 +if classid == 0 + puts "Class #{search_string} not found" + exit 1 +end +regexp = nil +regexp = ( $OPT_i ? Regexp.new($OPT_r, Regexp::IGNORECASE) : Regexp.new($OPT_r) ) if $OPT_r +candidates = "" +while true + a = Qt::Internal::findAllMethods(classid) + break if a.nil? + ids = (a.keys.sort.map{|k|a[k]}).flatten + candidates = Qt::Internal::dumpCandidates(ids) + if $OPT_p and !search_string.empty? and classid + sup = [] + Qt::Internal::getAllParents(classid, sup) + sup.each { + |sup_item| + a = Qt::Internal::findAllMethods(sup_item) + ids = (a.keys.sort.map{|k|a[k]}).flatten + candidates << Qt::Internal::dumpCandidates(ids) + } + end + if regexp + candidates.split("\n").each { + |candidate| + puts candidate if candidate =~ regexp + } + else + print candidates + end + break unless search_string.nil? + classid += 1 +end + +BEGIN { +$usage = <<USAGE +rbqtapi - a qtruby introspection tool\t(c) Germain Garand 2003 <germain\@ebooksfrance.org> + +usage: rbqtapi [-r <re>] [<class>] + +options: +\t-r <re> : find all functions matching regular expression/keyword <re> +\t-i : together with -r, performs a case insensitive search +\t-p : display also inherited methods for <class>. +\t-v : print qtruby and Qt versions +\t-h : print this help message +USAGE +} diff --git a/qtruby/bin/rbqtsh b/qtruby/bin/rbqtsh new file mode 100755 index 00000000..b9d8ee9e --- /dev/null +++ b/qtruby/bin/rbqtsh @@ -0,0 +1,627 @@ +#!/usr/bin/env ruby + +# rbqtsh : a graphical shell for qtruby. +# +# author: Germain Garand <germain@ebooksfrance.org> +# ruby port: Alexander Kellett <lypanov@kde.org> +# license: GNU Public License v2 +# + +require 'stringio' + +case File.basename $0 +when "rbqtsh" + require 'Qt' +when "rbkdesh" + require 'Korundum' +end + +include Qt + +$image0_data = +["22 22 7 1", +". c None", +"# c #000000", +"b c #292c29", +"c c #5a5d5a", +"d c #838583", +"e c #c5c2c5", +"a c #ffffff", +"......................", +"....##########........", +"....#aaaaaaa#b#.......", +"....#aaaaaaa#cb#......", +"....#aaaaaaa#dcb#.....", +"....#aaaaaaa#edcb#....", +"....#aaaaaaa#aedcb#...", +"....#aaaaaaa#######...", +"....#aaaaaaaaaaaaa#...", +"....#aaaaaaaaaaaaa#...", +"....#aaaaaaaaaaaaa#...", +"....#aaaaaaaaaaaaa#...", +"....#aaaaaaaaaaaaa#...", +"....#aaaaaaaaaaaaa#...", +"....#aaaaaaaaaaaaa#...", +"....#aaaaaaaaaaaaa#...", +"....#aaaaaaaaaaaaa#...", +"....#aaaaaaaaaaaaa#...", +"....#aaaaaaaaaaaaa#...", +"....###############...", +"......................", +"......................"] + +$image1_data = +["22 22 5 1", +". c None", +"# c #000000", +"c c #838100", +"a c #ffff00", +"b c #ffffff", +"......................", +"......................", +"......................", +"............####....#.", +"...........#....##.##.", +"..................###.", +".................####.", +".####...........#####.", +"#abab##########.......", +"#babababababab#.......", +"#ababababababa#.......", +"#babababababab#.......", +"#ababab###############", +"#babab##cccccccccccc##", +"#abab##cccccccccccc##.", +"#bab##cccccccccccc##..", +"#ab##cccccccccccc##...", +"#b##cccccccccccc##....", +"###cccccccccccc##.....", +"##cccccccccccc##......", +"###############.......", +"......................"] + +$image2_data = +["22 22 5 1", +". c None", +"# c #000000", +"a c #838100", +"b c #c5c2c5", +"c c #cdb6d5", +"......................", +".####################.", +".#aa#bbbbbbbbbbbb#bb#.", +".#aa#bbbbbbbbbbbb#bb#.", +".#aa#bbbbbbbbbcbb####.", +".#aa#bbbccbbbbbbb#aa#.", +".#aa#bbbccbbbbbbb#aa#.", +".#aa#bbbbbbbbbbbb#aa#.", +".#aa#bbbbbbbbbbbb#aa#.", +".#aa#bbbbbbbbbbbb#aa#.", +".#aa#bbbbbbbbbbbb#aa#.", +".#aaa############aaa#.", +".#aaaaaaaaaaaaaaaaaa#.", +".#aaaaaaaaaaaaaaaaaa#.", +".#aaa#############aa#.", +".#aaa#########bbb#aa#.", +".#aaa#########bbb#aa#.", +".#aaa#########bbb#aa#.", +".#aaa#########bbb#aa#.", +".#aaa#########bbb#aa#.", +"..##################..", +"......................"] + +$image3_data = +["22 22 88 2", +"Qt c None", +".2 c #000000", +".S c #08ff08", +"#v c #100810", +".U c #101010", +"#c c #101018", +".M c #181018", +"#e c #181818", +".A c #181820", +".L c #201820", +"#l c #202020", +".z c #202029", +"#m c #292029", +"#u c #292829", +"#n c #292831", +".R c #29ff29", +"#o c #312831", +".T c #313031", +"#p c #313039", +".Z c #31ff31", +"#q c #393039", +"#t c #393839", +".y c #393841", +"#s c #413841", +".o c #414041", +"#h c #4a4852", +".n c #5a505a", +"#r c #5a5962", +".I c #5ace5a", +"#b c #6a616a", +".p c #6a696a", +".x c #6a6973", +".Y c #6aff62", +".l c #736973", +".t c #7b717b", +".s c #7b7183", +".0 c #7bff7b", +".r c #837983", +".u c #83798b", +"#g c #83858b", +".v c #8b7994", +"#i c #8b858b", +".w c #8b8594", +"#j c #8b8d8b", +".8 c #8b8d94", +".m c #948d94", +"#k c #948d9c", +"#f c #949594", +".q c #94959c", +".J c #94c694", +"#d c #9c959c", +"#a c #9c95a4", +".k c #9c9d9c", +".N c #9c9da4", +".H c #9ccea4", +".K c #a49da4", +"#. c #a49dac", +".i c #a4a5a4", +".3 c #a4a5ac", +"## c #ac9dac", +".V c #aca5ac", +".d c #acaeac", +".j c #acaeb4", +".9 c #b4aeb4", +".# c #b4b6b4", +".a c #bdbebd", +".7 c #bdd6bd", +".c c #c5c6c5", +".5 c #cdc6cd", +".b c #cdcecd", +".4 c #cdced5", +".F c #d5ced5", +".G c #d5cede", +".h c #d5d6d5", +".E c #d5d6de", +".Q c #d5ffd5", +".B c #ded6de", +".1 c #ded6e6", +".g c #dedede", +".D c #dedee6", +".6 c #e6dee6", +".f c #e6e6e6", +".C c #e6e6ee", +".X c #e6ffe6", +".O c #eee6ee", +".e c #eeeeee", +".W c #f6f6f6", +".P c #ffffff", +"QtQtQtQtQtQt.#.a.b.b.b.b.c.c.a.a.d.aQtQtQtQt", +"QtQtQtQtQtQt.a.e.f.f.f.f.f.e.e.e.g.aQtQtQtQt", +"QtQtQtQtQtQt.a.c.c.c.b.b.c.c.c.c.a.cQtQtQtQt", +"QtQtQtQtQtQt.#.a.a.a.a.#.a.a.#.#.d.aQtQtQtQt", +"QtQtQtQtQt.c.d.c.a.c.c.c.a.a.a.c.#QtQtQtQtQt", +"QtQtQtQtQt.a.a.#.a.a.a.a.a.a.c.c.#QtQtQtQtQt", +"QtQtQtQtQt.a.#.c.a.a.a.a.a.c.a.c.dQtQtQtQtQt", +"QtQtQtQtQt.c.a.a.a.a.a.a.a.a.a.a.#QtQtQtQtQt", +"QtQtQtQtQt.d.b.f.g.g.g.g.g.g.h.g.i.i.jQtQtQt", +"QtQtQt.a.k.l.#.h.b.h.b.h.b.h.g.g.m.n.o.p.#Qt", +"QtQt.a.q.r.s.t.t.t.t.t.t.t.u.v.w.x.y.z.A.o.i", +"Qt.a.k.B.C.D.B.E.E.E.E.F.G.H.I.J.K.o.L.L.M.y", +".a.N.O.P.P.P.P.P.P.P.P.P.Q.R.S.R.b.v.T.A.U.L", +".V.W.P.P.P.P.P.P.P.P.P.P.X.Y.Z.0.P.1.t.A.2.L", +".3.E.4.5.4.h.E.E.g.6.D.B.D.E.7.F.4.5.8.M.2.A", +".m.9.j.V.3#..3.K#.#..i#..K#.###a.q.8#b#c.2.L", +".m.j.j#..3.K.K.K.N.K.N.N.N.N#a#d#d.w#b#c.2#e", +"#f#.#..K.N.K.N.N.N#a.k#a#d#d#d#a.m#g#b.M.2#h", +".m.3.K.K#a.k#a#d#a.k#a#d#a#d.q.m.8#i.x#c#e.d", +"#f#g#i.w#j.w#i.8.w#i.8.8.m.8.m#k.8.w#b#e#fQt", +".#.l.z.A#l.z#m#m#m#n#o#o#p#p#q#q#p#o#p#fQtQt", +"QtQt.d#r#s#s#t#p.T.T.T#u#u.z#e#e#v.o.kQtQtQt"] + + +class QtShellControl < Qt::MainWindow + attr_accessor :menubar, :fileMenu, :helpMenu, :toolBar, :fileName, :fileOpenAction + attr_accessor :fileSaveAction, :fileSaveAsAction, :filePrintAction, :fileExitAction + attr_accessor :helpExampleAction, :comboBox, :sessionLog, :executedLines, :printer + slots 'fileOpen()', 'fileOpen()', 'fileSave()', 'fileSaveAs()' + slots 'filePrint()', 'fileExit()', 'runSelection()', 'helpExample()' + signals 'fileNeedsEval(QString)', 'selection(QString)' + + def initialize(*k) + super(*k) + image0 = Qt::Pixmap.new($image0_data) + image1 = Qt::Pixmap.new($image1_data) + image2 = Qt::Pixmap.new($image2_data) + image3 = Qt::Pixmap.new($image3_data) + box = VBox.new(self) + @sessionLog = TextEdit.new(box, "sessionLog") + @sessionLog.setTextFormat(Qt::RichText) + @sessionLog.setReadOnly(true) + @comboBox = ComboBox.new(box, "comboBox") + @comboBox.setEditable(true) + @comboBox.setAutoCompletion(true) + self.setCentralWidget(box) + @comboBox.setFocus + self.resize(500,300) + self.setCaption("QtRuby Shell") + def trUtf8(k) + return k + end + @fileOpenAction = Qt::Action.new(self, "fileOpenAction") + @fileOpenAction.setIconSet(Qt::IconSet.new(image1)) + @fileOpenAction.setText(trUtf8("Open")) + @fileOpenAction.setMenuText(trUtf8("&Open...")) + @fileOpenAction.setAccel(KeySequence.new(trUtf8("Ctrl+O"))) + @fileSaveAction = Qt::Action.new(self, "fileSaveAction") + @fileSaveAction.setIconSet(Qt::IconSet.new(image2)) + @fileSaveAction.setText(trUtf8("Save")) + @fileSaveAction.setMenuText(trUtf8("&Save")) + @fileSaveAction.setAccel(KeySequence.new(trUtf8("Ctrl+S"))) + @fileSaveAsAction = Qt::Action.new(self, "fileSaveAsAction") + @fileSaveAsAction.setText(trUtf8("Save As")) + @fileSaveAsAction.setMenuText(trUtf8("Save &As...")) + @fileSaveAsAction.setAccel(KeySequence.new(trUtf8("Ctrl+A"))) + @filePrintAction = Qt::Action.new(self, "filePrintAction") + @filePrintAction.setIconSet(Qt::IconSet.new(image3)) + @filePrintAction.setText(trUtf8("Print")) + @filePrintAction.setMenuText(trUtf8("&Print...")) + @filePrintAction.setAccel(KeySequence.new(trUtf8("Ctrl+P"))) + @fileExitAction = Qt::Action.new(self, "fileExitAction") + @fileExitAction.setText(trUtf8("Exit")) + @fileExitAction.setMenuText(trUtf8("E&xit")) + @fileExitAction.setAccel(KeySequence.new(trUtf8("Ctrl+E"))) + + @runAction = Qt::Action.new(self, "runAction"); + @runAction.setText(trUtf8("Run Selection")); + @runAction.setMenuText(trUtf8("&Run Selection")); + @runAction.setAccel(KeySequence.new(trUtf8("Ctrl+R"))); + + @helpExampleAction = Qt::Action.new(self, "helpExampleAction"); + @helpExampleAction.setText(trUtf8("Example")); + @helpExampleAction.setMenuText(trUtf8("Examp&le")); + @helpExampleAction.setAccel(KeySequence.new(trUtf8("Ctrl+L"))); + + @toolBar = Qt::ToolBar.new("", self, DockTop) + @toolBar.setLabel(trUtf8("Tools")) + + @fileOpenAction.addTo(toolBar) + @fileSaveAction.addTo(toolBar) + @filePrintAction.addTo(toolBar) + + @menubar= Qt::MenuBar.new(self, "menubar") + + @fileMenu= Qt::PopupMenu.new(self) + @fileOpenAction.addTo(fileMenu) + @fileSaveAction.addTo(fileMenu) + @fileSaveAsAction.addTo(fileMenu) + @fileMenu.insertSeparator + @filePrintAction.addTo(fileMenu) + @fileMenu.insertSeparator + @fileExitAction.addTo(fileMenu) + @menubar.insertItem(trUtf8("&File"), fileMenu) + + @menubar.insertSeparator + + @runMenu= Qt::PopupMenu.new(self) + @runAction.addTo(@runMenu) + @menubar.insertItem(trUtf8("Run"), @runMenu) + + @menubar.insertSeparator + + @helpMenu= Qt::PopupMenu.new(self) + @helpExampleAction.addTo(helpMenu) + @menubar.insertItem(trUtf8("&Help"), helpMenu) + + connect(@fileOpenAction, SIGNAL('activated()'), self, SLOT('fileOpen()')) + connect(@fileSaveAction, SIGNAL('activated()'), self, SLOT('fileSave()')) + connect(@fileSaveAsAction, SIGNAL('activated()'), self, SLOT('fileSaveAs()')) + connect(@filePrintAction, SIGNAL('activated()'), self, SLOT('filePrint()')) + connect(@fileExitAction, SIGNAL('activated()'), self, SLOT('fileExit()')) + connect(@runAction, SIGNAL('activated()'), self, SLOT('runSelection()')) + connect(@helpExampleAction, SIGNAL('activated()'), self, SLOT('helpExample()')) + + @executedLines = [] + end + + def fileOpen + fname = Qt::FileDialog::getOpenFileName( + ".", + "Rbqtsh Session (*.rbqts)", + self, + "open session", + "Choose a file to open") + return if fname.nil? + emit fileNeedsEval(fname) + end + + def getFileName + @fileName = Qt::FileDialog::getSaveFileName( + ".", + "Rbqtsh Session (*.rbqts)", + self, + "save session", + "Choose a filename" ) + unless @fileName =~ /\.rbqts$/ + @fileName << ".rbqts" + end + @fileName + end + + def save(fname) + file = File.new(fname, "w") + if file.nil + # TODO fix ": unknown" to give a reason + Qt::MessageBox::critical( + self, + "Error" , + "Couldn't open #{fname} for writing: unknown", + Qt::MessageBox::Ok, + Qt::MessageBox::NoButton ) + return + end + @executedLines.each { + |line| + next if line =~ /^\s*$/ + line.chomp! + line << ";" unless line =~ /;\s*$/ + file.puts(line) + } + file.close + end + + def fileSave + return if emptySession + fname = @fileName || getFileName + return if fname.nil? + save fname + end + + def fileSaveAs(cond = 0) + return if emptySession + fname = getFileName + return if fname.nil? + ret = nil + if File.exists(fname) + cond += 1 + ret = Qt::MessageBox::warning( + self, + "Warning" , + "File exists, overwrite ?", + Qt::MessageBox::Yes, + Qt::MessageBox::No ) + else + cond = 0 + end + fileSaveAs(cond) if (cond == 0) and ret == Qt::MessageBox::No + save(fname) + end + + def filePrint() + margin = 10 + pageNo = 1 + emptySession() and return + printer = Qt::Printer.new() + if printer.setup(self) + statusBar().message( "Printing..." ) + p = Qt::Painter.new() + if ! p.begin( printer ) + statusBar().message( "An error occured..." ) + return + end + + p.setFont( sessionLog.font() ) + yPos = 0 + fm = p.fontMetrics + metrics = Qt::PaintDeviceMetrics.new( printer ) + + for i in 0..@executedLines.length-1 + if margin + yPos > metrics.height() - margin + msg ="Printing (page "+ ++pageNo + ")..." + statusBar().message( msg ) + printer.newPage() + yPos = 0 + end + p.drawText( margin, margin + yPos, + metrics.width(), fm.lineSpacing(), + ExpandTabs | DontClip, + "#{@executedLines[ i ]}" ) + yPos = yPos + fm.lineSpacing() + end + p.end() + statusBar().message( "Printing completed", 3000 ) + else + statusBar().message( "Printing aborted", 3000 ) + end + end + + def fileExit + emit $qApp.quit if confirmExit + end + + def closeEvent(e) + if confirmExit + e.accept + else + e.ignore + end + end + + def confirmExit + ret = 0 + return true if @executedLines.empty? + ret = Qt::MessageBox::warning( + self, + "Warning" , + "A session is opened, quit anyway ?", + Qt::MessageBox::Yes, + Qt::MessageBox::No) + return ret != Qt::MessageBox::No + end + + def emptySession + empty = @executedLines.empty? + statusBar.message("Session is empty...", 3000) if empty + empty + end + + def runSelection + emit selection(@sessionLog.selectedText()) + end + + def helpExample + emit fileNeedsEval("__DATA__") + end + +end + +class QtShell < Qt::MainWindow + slots 'evalInput()', 'evalFile(QString)', 'evalSelection(QString)' + attr_accessor :shellWindow + @@binding = binding +def bind +@@binding +end + +def initialize(*k) + super(*k) + + @shellWindow = QtShellControl.new(nil, "shellWindow") + self.resize(350,350) + self.move(Point.new(10,10)) + @shellWindow.move(Point.new(300,200)) + self.show + @shellWindow.show + + connect(@shellWindow.comboBox.lineEdit, SIGNAL('returnPressed()'), self, SLOT('evalInput()')) + + @prompt = '<b><font color="blue">$></font></b>' + self.setCaption("MainWindow - this") + @shellWindow.sessionLog.setText("Ready.<br>") + connect(@shellWindow, SIGNAL('fileNeedsEval(QString)'), self, SLOT('evalFile(QString)')) + connect(@shellWindow, SIGNAL('selection(QString)'), self, SLOT('evalSelection(QString)')) +end + +def logAppend(str) + tmp = @shellWindow.sessionLog.text << str + @shellWindow.sessionLog.setText tmp +end + +def evalInput + evalOneLine @shellWindow.comboBox.currentText +end + +=begin + +sub TIEHANDLE { my ( $classnm, $widg, $color) = @_; + my $h = { widg => $widg, color => $color}; + bless $h, $classnm; +} + +sub PRINT { + my $me = shift; + my $color = $me->{color}; + my $printed = join $/, @_; + $printed =~ s/</</gs; + $printed =~ s/>/>/gs; + $printed =~ s/\n/<br>/gs; + $me->{widg}->setText( $me->{widg}->text . "<font color=\"$color\">$printed</font>" ); +} + +#tie *STDOUT, 'Qt::TextHandle', $shw->sessionLog, 'black'; +#tie *STDERR, 'Qt::TextHandle', $shw->sessionLog, 'red'; + +=end + +def evalOneLine(line) + prot = ln = line + prot.gsub('<', '<') # /gs ??? + prot.gsub('>', '>') # /gs ??? + + logAppend( "#{@prompt}#{prot}" ) + + ostdout, ostderr = $stdout, $stderr + strio = StringIO.new + $stdout, $stderr = strio, strio + begin + if line =~ /^\$(.*)$/ + eval $1, bind + else + eval line + end + rescue StandardError, ScriptError => e + prot = e.to_str + prot.gsub(/</, '<') # /gs ??? + prot.gsub(/>/, '>') # /gs ??? + c = @shellWindow.sessionLog.color + prot.gsub(/\n/, '<br>') # /gs ??? + logAppend '<font color="red">' << prot << '</font><br>' + shellWindow.sessionLog.setColor c + end + logAppend '<font color="blue">' << strio.string << '</font><br>' + @shellWindow.executedLines << line + @shellWindow.comboBox.clearEdit + @shellWindow.comboBox.setFocus + $stdout, $stderr = ostdout, ostderr + + @shellWindow.sessionLog.scrollToBottom +end + +def evalFile(fname) + if fname == "__DATA__" + file = StringIO.new + file.string = $example + else + file = File.open(fname, "r") + if file.nil? + # TODO use $! -> error message! + Qt::MessageBox::warning( + self, + "Error" , + "Couldn't open @{$fn}: $!", + Qt::MessageBox::Ok, + Qt::MessageBox::NoButton) + return + end + indata = file + end + file.each_line { + |line| + evalOneLine(line) + } + file.close +end + +def evalSelection(selection) + evalOneLine(selection.sub(/.*span>/, "")) unless selection.nil? +end + +end + +app = Qt::Application.new(ARGV) +w = QtShell.new(nil, "mainWindow") +app.setMainWidget(w.shellWindow) +app.exec + +BEGIN { +$example = <<EXAMPLE +$ attr_accessor :datetime, :button, :textedit, :sample, :vbox +statusBar.message("Hello World !") +@vbox = VBox.new(self) +@datetime = DateTimeEdit.new(vbox) +@textedit = TextEdit.new(vbox) +@button = PushButton.new("Hello World!", vbox) +self.setCentralWidget(vbox) +self.resize(220,240) +@vbox.show +@sample = Qt::PopupMenu.new(self) +$ slots 'there()' +@sample.insertItem("&There", self, SLOT('there()')) +self.menuBar.insertItem("&Here", sample) +$ def there; statusBar.message("There...", 2000); end +EXAMPLE +} diff --git a/qtruby/rubylib/Makefile.am b/qtruby/rubylib/Makefile.am new file mode 100644 index 00000000..1c60934e --- /dev/null +++ b/qtruby/rubylib/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = qtruby designer diff --git a/qtruby/rubylib/designer/Makefile.am b/qtruby/rubylib/designer/Makefile.am new file mode 100644 index 00000000..80ec1c71 --- /dev/null +++ b/qtruby/rubylib/designer/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = rbuic uilib diff --git a/qtruby/rubylib/designer/examples/colortool/Makefile b/qtruby/rubylib/designer/examples/colortool/Makefile new file mode 100644 index 00000000..df79b963 --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/Makefile @@ -0,0 +1,30 @@ +RBUIC=rbuic
+
+RBUICIMPLS=mainform.rb optionsform.rb colornameform.rb \
+ findform.rb qmake_image_collection.rb
+
+all: $(RBUICIMPLS)
+
+mainform.rb:
+ $(RBUIC) mainform.ui -o mainform.rb
+
+optionsform.rb: optionsform.ui
+ $(RBUIC) optionsform.ui -o optionsform.rb
+
+colornameform.rb: colornameform.ui
+ $(RBUIC) colornameform.ui -o colornameform.rb
+
+findform.rb: findform.ui
+ $(RBUIC) findform.ui -o findform.rb
+
+qmake_image_collection.rb:
+ $(RBUIC) -embed colortool images/filenew.png images/fileopen.png \
+ images/filesave.png images/editcut.png images/editcopy.png \
+ images/searchfind.png images/tabwidget.png images/table.png \
+ images/iconview.png images/richtextedit.png images/widgetstack.png \
+ images/editraise.png -o qmake_image_collection.rb
+
+clean:
+ rm -f $(RBUICIMPLS)
+
+
diff --git a/qtruby/rubylib/designer/examples/colortool/README b/qtruby/rubylib/designer/examples/colortool/README new file mode 100644 index 00000000..99d2174b --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/README @@ -0,0 +1,8 @@ +This is the Qt Designer Tutorial 'Creating Dialogs' translated into QtRuby. It
+shows how you can combine ruby code generated from .ui files with the rbuic
+tool, with your own code.
+
+The .ui files and images are identical to the original C++ versions.
+
+It features a simple Makefile to run rbuic when you change the .ui files, and
+regenerate the ruby sources.
diff --git a/qtruby/rubylib/designer/examples/colortool/colornameform.ui b/qtruby/rubylib/designer/examples/colortool/colornameform.ui new file mode 100644 index 00000000..50f2d6de --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/colornameform.ui @@ -0,0 +1,168 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>ColorNameForm</class> +<widget class="QDialog"> + <property name="name"> + <cstring>ColorNameForm</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>348</width> + <height>105</height> + </rect> + </property> + <property name="caption"> + <string>Color Tool - Color Name</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>colorLabel</cstring> + </property> + <property name="minimumSize"> + <size> + <width>80</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="pixmap"> + <pixmap>editraise.png</pixmap> + </property> + <property name="scaledContents"> + <bool>true</bool> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout4</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel2</cstring> + </property> + <property name="text"> + <string>&Name:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>colorLineEdit</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>colorLineEdit</cstring> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>Spacer2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>Spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>0</width> + <height>11</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>okPushButton</cstring> + </property> + <property name="text"> + <string>&OK</string> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>cancelPushButton</cstring> + </property> + <property name="text"> + <string>&Cancel</string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </hbox> +</widget> +<connections> + <connection> + <sender>cancelPushButton</sender> + <signal>clicked()</signal> + <receiver>ColorNameForm</receiver> + <slot>reject()</slot> + </connection> + <connection> + <sender>okPushButton</sender> + <signal>clicked()</signal> + <receiver>ColorNameForm</receiver> + <slot>validate()</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in implementation">colornameform.ui.h</include> +</includes> +<slots> + <slot>validate()</slot> +</slots> +<functions> + <function>setColors( const QMap<QString, QColor> & colors )</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/qtruby/rubylib/designer/examples/colortool/colornameform.ui.rb b/qtruby/rubylib/designer/examples/colortool/colornameform.ui.rb new file mode 100644 index 00000000..db671b77 --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/colornameform.ui.rb @@ -0,0 +1,19 @@ +class ColorNameForm + +@colors = {} + +def setColors( colors ) + @colors = colors +end + +def validate() + name = @colorLineEdit.text() + if ! name.empty? && + ( @colors.empty? || ! @colors.has_key?( name ) ) + accept() + else + @colorLineEdit.selectAll() + end +end + +end diff --git a/qtruby/rubylib/designer/examples/colortool/findform.ui b/qtruby/rubylib/designer/examples/colortool/findform.ui new file mode 100644 index 00000000..3e627945 --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/findform.ui @@ -0,0 +1,141 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>FindForm</class> +<widget class="QDialog"> + <property name="name"> + <cstring>FindForm</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>301</width> + <height>99</height> + </rect> + </property> + <property name="caption"> + <string>Color Tool - Find Color</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout11</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel3</cstring> + </property> + <property name="text"> + <string>&Look for:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>findLineEdit</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>findLineEdit</cstring> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>Spacer4</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout10</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>Spacer7</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>0</width> + <height>9</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>findPushButton</cstring> + </property> + <property name="text"> + <string>&Find</string> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>closePushButton</cstring> + </property> + <property name="text"> + <string>&Close</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>closePushButton</sender> + <signal>clicked()</signal> + <receiver>FindForm</receiver> + <slot>accept()</slot> + </connection> + <connection> + <sender>findPushButton</sender> + <signal>clicked()</signal> + <receiver>FindForm</receiver> + <slot>find()</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in implementation">findform.ui.h</include> +</includes> +<signals> + <signal>lookfor(const QString&)</signal> +</signals> +<slots> + <slot>find()</slot> +</slots> +<functions> + <function>notfound()</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/qtruby/rubylib/designer/examples/colortool/findform.ui.rb b/qtruby/rubylib/designer/examples/colortool/findform.ui.rb new file mode 100644 index 00000000..6bfb4f0a --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/findform.ui.rb @@ -0,0 +1,11 @@ +class FindForm + +def find() + emit lookfor( @findLineEdit.text() ) +end + +def notfound() + @findLineEdit.selectAll() +end + +end diff --git a/qtruby/rubylib/designer/examples/colortool/images/editcopy b/qtruby/rubylib/designer/examples/colortool/images/editcopy Binary files differnew file mode 100644 index 00000000..7b334ca4 --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/editcopy diff --git a/qtruby/rubylib/designer/examples/colortool/images/editcopy.png b/qtruby/rubylib/designer/examples/colortool/images/editcopy.png Binary files differnew file mode 100644 index 00000000..abfe86e2 --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/editcopy.png diff --git a/qtruby/rubylib/designer/examples/colortool/images/editcut b/qtruby/rubylib/designer/examples/colortool/images/editcut Binary files differnew file mode 100644 index 00000000..60abc586 --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/editcut diff --git a/qtruby/rubylib/designer/examples/colortool/images/editcut.png b/qtruby/rubylib/designer/examples/colortool/images/editcut.png Binary files differnew file mode 100644 index 00000000..98efe27a --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/editcut.png diff --git a/qtruby/rubylib/designer/examples/colortool/images/editraise.png b/qtruby/rubylib/designer/examples/colortool/images/editraise.png Binary files differnew file mode 100644 index 00000000..02415689 --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/editraise.png diff --git a/qtruby/rubylib/designer/examples/colortool/images/filenew b/qtruby/rubylib/designer/examples/colortool/images/filenew Binary files differnew file mode 100644 index 00000000..9de6e839 --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/filenew diff --git a/qtruby/rubylib/designer/examples/colortool/images/filenew.png b/qtruby/rubylib/designer/examples/colortool/images/filenew.png Binary files differnew file mode 100644 index 00000000..8577f068 --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/filenew.png diff --git a/qtruby/rubylib/designer/examples/colortool/images/fileopen b/qtruby/rubylib/designer/examples/colortool/images/fileopen Binary files differnew file mode 100644 index 00000000..a21f9466 --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/fileopen diff --git a/qtruby/rubylib/designer/examples/colortool/images/fileopen.png b/qtruby/rubylib/designer/examples/colortool/images/fileopen.png Binary files differnew file mode 100644 index 00000000..85dab435 --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/fileopen.png diff --git a/qtruby/rubylib/designer/examples/colortool/images/filesave b/qtruby/rubylib/designer/examples/colortool/images/filesave Binary files differnew file mode 100644 index 00000000..f6d9af92 --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/filesave diff --git a/qtruby/rubylib/designer/examples/colortool/images/filesave.png b/qtruby/rubylib/designer/examples/colortool/images/filesave.png Binary files differnew file mode 100644 index 00000000..21309aa6 --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/filesave.png diff --git a/qtruby/rubylib/designer/examples/colortool/images/iconview.png b/qtruby/rubylib/designer/examples/colortool/images/iconview.png Binary files differnew file mode 100644 index 00000000..d755399d --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/iconview.png diff --git a/qtruby/rubylib/designer/examples/colortool/images/richtextedit.png b/qtruby/rubylib/designer/examples/colortool/images/richtextedit.png Binary files differnew file mode 100644 index 00000000..9f75c258 --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/richtextedit.png diff --git a/qtruby/rubylib/designer/examples/colortool/images/searchfind b/qtruby/rubylib/designer/examples/colortool/images/searchfind Binary files differnew file mode 100644 index 00000000..7aaefe22 --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/searchfind diff --git a/qtruby/rubylib/designer/examples/colortool/images/searchfind.png b/qtruby/rubylib/designer/examples/colortool/images/searchfind.png Binary files differnew file mode 100644 index 00000000..8f7d8adb --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/searchfind.png diff --git a/qtruby/rubylib/designer/examples/colortool/images/table.png b/qtruby/rubylib/designer/examples/colortool/images/table.png Binary files differnew file mode 100644 index 00000000..663b32f0 --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/table.png diff --git a/qtruby/rubylib/designer/examples/colortool/images/tabwidget.png b/qtruby/rubylib/designer/examples/colortool/images/tabwidget.png Binary files differnew file mode 100644 index 00000000..3a160446 --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/tabwidget.png diff --git a/qtruby/rubylib/designer/examples/colortool/images/widgetstack.png b/qtruby/rubylib/designer/examples/colortool/images/widgetstack.png Binary files differnew file mode 100644 index 00000000..979409cf --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/images/widgetstack.png diff --git a/qtruby/rubylib/designer/examples/colortool/main.rb b/qtruby/rubylib/designer/examples/colortool/main.rb new file mode 100644 index 00000000..9ebe84cb --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/main.rb @@ -0,0 +1,20 @@ +require 'Qt'
+
+require 'mainform.rb'
+require 'mainform.ui.rb'
+
+require 'colornameform.rb'
+require 'colornameform.ui.rb'
+
+require 'optionsform.rb'
+
+require 'findform.rb'
+require 'findform.ui.rb'
+
+require 'qmake_image_collection.rb'
+
+a = Qt::Application.new(ARGV)
+w = MainForm.new
+a.mainWidget = w
+w.show
+a.exec
diff --git a/qtruby/rubylib/designer/examples/colortool/mainform.ui b/qtruby/rubylib/designer/examples/colortool/mainform.ui new file mode 100644 index 00000000..6c89baca --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/mainform.ui @@ -0,0 +1,601 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>MainForm</class> +<widget class="QMainWindow"> + <property name="name"> + <cstring>MainForm</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>480</height> + </rect> + </property> + <property name="caption"> + <string>Color Tool</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QWidgetStack"> + <property name="name"> + <cstring>colorWidgetStack</cstring> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>Plain</enum> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tablePage</cstring> + </property> + <attribute name="id"> + <number>0</number> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QTable"> + <column> + <property name="text"> + <string>Name</string> + </property> + </column> + <column> + <property name="text"> + <string>Hex</string> + </property> + </column> + <column> + <property name="text"> + <string>Web</string> + </property> + </column> + <property name="name"> + <cstring>colorTable</cstring> + </property> + <property name="numRows"> + <number>0</number> + </property> + <property name="numCols"> + <number>3</number> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>iconsPage</cstring> + </property> + <attribute name="id"> + <number>1</number> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QIconView"> + <property name="name"> + <cstring>colorIconView</cstring> + </property> + <property name="gridX"> + <number>100</number> + </property> + <property name="resizeMode"> + <enum>Adjust</enum> + </property> + </widget> + </vbox> + </widget> + </widget> + </hbox> +</widget> +<menubar> + <property name="name"> + <cstring>menubar</cstring> + </property> + <item text="&File" name="fileMenu"> + <action name="fileNewAction"/> + <action name="fileOpenAction"/> + <action name="fileSaveAction"/> + <action name="fileSaveAsAction"/> + <separator/> + <action name="fileExitAction"/> + </item> + <item text="&Edit" name="editMenu"> + <action name="editAddAction"/> + <action name="editCutAction"/> + <action name="editCopyAction"/> + <separator/> + <action name="editFindAction"/> + <separator/> + <action name="optionsAction"/> + </item> + <item text="&View" name="viewMenu"> + <action name="viewTableAction"/> + <action name="viewIconsAction"/> + </item> + <item text="&Help" name="helpMenu"> + <action name="helpContentsAction"/> + <action name="helpIndexAction"/> + <separator/> + <action name="helpAboutAction"/> + </item> +</menubar> +<toolbars> + <toolbar dock="2"> + <property name="name"> + <cstring>toolBar</cstring> + </property> + <property name="label"> + <string>Tools</string> + </property> + <action name="fileNewAction"/> + <action name="fileOpenAction"/> + <action name="fileSaveAction"/> + <separator/> + <action name="editAddAction"/> + <action name="editCutAction"/> + <action name="editCopyAction"/> + <action name="editFindAction"/> + <separator/> + <action name="optionsAction"/> + <separator/> + <action name="viewTableAction"/> + <action name="viewIconsAction"/> + </toolbar> +</toolbars> +<actions> + <action> + <property name="name"> + <cstring>fileNewAction</cstring> + </property> + <property name="iconSet"> + <iconset>filenew.png</iconset> + </property> + <property name="text"> + <string>New</string> + </property> + <property name="menuText"> + <string>&New</string> + </property> + <property name="accel"> + <string>Ctrl+N</string> + </property> + </action> + <action> + <property name="name"> + <cstring>fileOpenAction</cstring> + </property> + <property name="iconSet"> + <iconset>fileopen.png</iconset> + </property> + <property name="text"> + <string>Open</string> + </property> + <property name="menuText"> + <string>&Open...</string> + </property> + <property name="accel"> + <string>Ctrl+O</string> + </property> + </action> + <action> + <property name="name"> + <cstring>fileSaveAction</cstring> + </property> + <property name="iconSet"> + <iconset>filesave.png</iconset> + </property> + <property name="text"> + <string>Save</string> + </property> + <property name="menuText"> + <string>&Save</string> + </property> + <property name="accel"> + <string>Ctrl+S</string> + </property> + </action> + <action> + <property name="name"> + <cstring>fileSaveAsAction</cstring> + </property> + <property name="text"> + <string>Save As</string> + </property> + <property name="menuText"> + <string>Save &As...</string> + </property> + <property name="accel"> + <string></string> + </property> + </action> + <action> + <property name="name"> + <cstring>fileExitAction</cstring> + </property> + <property name="text"> + <string>Exit</string> + </property> + <property name="menuText"> + <string>E&xit</string> + </property> + <property name="accel"> + <string></string> + </property> + </action> + <action> + <property name="name"> + <cstring>editCutAction</cstring> + </property> + <property name="iconSet"> + <iconset>editcut.png</iconset> + </property> + <property name="text"> + <string>Delete</string> + </property> + <property name="menuText"> + <string>&Delete</string> + </property> + <property name="accel"> + <string>Ctrl+X</string> + </property> + </action> + <action> + <property name="name"> + <cstring>editCopyAction</cstring> + </property> + <property name="iconSet"> + <iconset>editcopy.png</iconset> + </property> + <property name="text"> + <string>Copy</string> + </property> + <property name="menuText"> + <string>&Copy</string> + </property> + <property name="accel"> + <string>Ctrl+C</string> + </property> + </action> + <action> + <property name="name"> + <cstring>editFindAction</cstring> + </property> + <property name="iconSet"> + <iconset>searchfind.png</iconset> + </property> + <property name="text"> + <string>Find</string> + </property> + <property name="menuText"> + <string>&Find...</string> + </property> + <property name="accel"> + <string>Ctrl+F</string> + </property> + </action> + <action> + <property name="name"> + <cstring>helpContentsAction</cstring> + </property> + <property name="text"> + <string>Contents</string> + </property> + <property name="menuText"> + <string>&Contents...</string> + </property> + <property name="accel"> + <string></string> + </property> + </action> + <action> + <property name="name"> + <cstring>helpIndexAction</cstring> + </property> + <property name="text"> + <string>Index</string> + </property> + <property name="menuText"> + <string>&Index...</string> + </property> + <property name="accel"> + <string></string> + </property> + </action> + <action> + <property name="name"> + <cstring>helpAboutAction</cstring> + </property> + <property name="text"> + <string>About</string> + </property> + <property name="menuText"> + <string>&About</string> + </property> + <property name="accel"> + <string></string> + </property> + </action> + <action> + <property name="name"> + <cstring>optionsAction</cstring> + </property> + <property name="iconSet"> + <iconset>tabwidget.png</iconset> + </property> + <property name="text"> + <string>Options</string> + </property> + <property name="menuText"> + <string>&Options...</string> + </property> + </action> + <actiongroup> + <property name="name"> + <cstring>viewActionGroup</cstring> + </property> + <property name="text"> + <string>View</string> + </property> + <property name="menuText"> + <string>View</string> + </property> + <property name="usesDropDown"> + <bool>false</bool> + </property> + <action> + <property name="name"> + <cstring>viewTableAction</cstring> + </property> + <property name="toggleAction"> + <bool>true</bool> + </property> + <property name="on"> + <bool>true</bool> + </property> + <property name="iconSet"> + <iconset>table.png</iconset> + </property> + <property name="text"> + <string>View Table</string> + </property> + <property name="menuText"> + <string>View &Table</string> + </property> + <property name="toolTip"> + <string>View Table (Ctrl+T)</string> + </property> + <property name="accel"> + <string>Ctrl+T</string> + </property> + </action> + <action> + <property name="name"> + <cstring>viewIconsAction</cstring> + </property> + <property name="toggleAction"> + <bool>true</bool> + </property> + <property name="iconSet"> + <iconset>iconview.png</iconset> + </property> + <property name="text"> + <string>View Icons</string> + </property> + <property name="menuText"> + <string>View &Icons</string> + </property> + <property name="toolTip"> + <string>View Icons (Ctrl+I)</string> + </property> + <property name="accel"> + <string>Ctrl+I</string> + </property> + </action> + </actiongroup> + <action> + <property name="name"> + <cstring>editAddAction</cstring> + </property> + <property name="iconSet"> + <iconset>widgetstack.png</iconset> + </property> + <property name="text"> + <string>Add</string> + </property> + <property name="menuText"> + <string>&Add...</string> + </property> + <property name="accel"> + <string>Ctrl+A</string> + </property> + </action> +</actions> +<connections> + <connection> + <sender>fileNewAction</sender> + <signal>activated()</signal> + <receiver>MainForm</receiver> + <slot>fileNew()</slot> + </connection> + <connection> + <sender>fileOpenAction</sender> + <signal>activated()</signal> + <receiver>MainForm</receiver> + <slot>fileOpen()</slot> + </connection> + <connection> + <sender>fileSaveAction</sender> + <signal>activated()</signal> + <receiver>MainForm</receiver> + <slot>fileSave()</slot> + </connection> + <connection> + <sender>fileSaveAsAction</sender> + <signal>activated()</signal> + <receiver>MainForm</receiver> + <slot>fileSaveAs()</slot> + </connection> + <connection> + <sender>fileExitAction</sender> + <signal>activated()</signal> + <receiver>MainForm</receiver> + <slot>fileExit()</slot> + </connection> + <connection> + <sender>editCutAction</sender> + <signal>activated()</signal> + <receiver>MainForm</receiver> + <slot>editCut()</slot> + </connection> + <connection> + <sender>editCopyAction</sender> + <signal>activated()</signal> + <receiver>MainForm</receiver> + <slot>editCopy()</slot> + </connection> + <connection> + <sender>editFindAction</sender> + <signal>activated()</signal> + <receiver>MainForm</receiver> + <slot>editFind()</slot> + </connection> + <connection> + <sender>helpIndexAction</sender> + <signal>activated()</signal> + <receiver>MainForm</receiver> + <slot>helpIndex()</slot> + </connection> + <connection> + <sender>helpContentsAction</sender> + <signal>activated()</signal> + <receiver>MainForm</receiver> + <slot>helpContents()</slot> + </connection> + <connection> + <sender>helpAboutAction</sender> + <signal>activated()</signal> + <receiver>MainForm</receiver> + <slot>helpAbout()</slot> + </connection> + <connection> + <sender>colorTable</sender> + <signal>currentChanged(int,int)</signal> + <receiver>MainForm</receiver> + <slot>changedTableColor(int,int)</slot> + </connection> + <connection> + <sender>colorIconView</sender> + <signal>currentChanged(QIconViewItem*)</signal> + <receiver>MainForm</receiver> + <slot>changedIconColor(QIconViewItem*)</slot> + </connection> + <connection> + <sender>viewActionGroup</sender> + <signal>selected(QAction*)</signal> + <receiver>MainForm</receiver> + <slot>changeView(QAction*)</slot> + </connection> + <connection> + <sender>editAddAction</sender> + <signal>activated()</signal> + <receiver>MainForm</receiver> + <slot>editAdd()</slot> + </connection> + <connection> + <sender>optionsAction</sender> + <signal>activated()</signal> + <receiver>MainForm</receiver> + <slot>editOptions()</slot> + </connection> + <connection> + <sender>colorWidgetStack</sender> + <signal>aboutToShow(int)</signal> + <receiver>MainForm</receiver> + <slot>aboutToShow()</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in declaration">findform.h</include> + <include location="local" impldecl="in implementation">qsettings.h</include> + <include location="local" impldecl="in implementation">qradiobutton.h</include> + <include location="local" impldecl="in implementation">qcheckbox.h</include> + <include location="local" impldecl="in implementation">colornameform.h</include> + <include location="local" impldecl="in implementation">qcolordialog.h</include> + <include location="local" impldecl="in implementation">qregexp.h</include> + <include location="local" impldecl="in implementation">qfile.h</include> + <include location="local" impldecl="in implementation">qfiledialog.h</include> + <include location="local" impldecl="in implementation">qapplication.h</include> + <include location="local" impldecl="in implementation">qcolor.h</include> + <include location="local" impldecl="in implementation">qstring.h</include> + <include location="local" impldecl="in implementation">qpainter.h</include> + <include location="local" impldecl="in implementation">qstatusbar.h</include> + <include location="local" impldecl="in implementation">qmessagebox.h</include> + <include location="local" impldecl="in implementation">qclipboard.h</include> + <include location="local" impldecl="in implementation">qlabel.h</include> + <include location="local" impldecl="in implementation">qlineedit.h</include> + <include location="local" impldecl="in implementation">optionsform.h</include> + <include location="local" impldecl="in implementation">mainform.ui.h</include> +</includes> +<forwards> + <forward>class QString;</forward> + <forward>class QColor;</forward> +</forwards> +<variables> + <variable>QStringList m_comments;</variable> + <variable>QString m_filename;</variable> + <variable>bool m_changed;</variable> + <variable>bool m_table_dirty;</variable> + <variable>bool m_icons_dirty;</variable> + <variable>int m_clip_as;</variable> + <variable>bool m_show_web;</variable> + <variable>QClipboard *clipboard;</variable> + <variable>FindForm *findForm;</variable> + <variable>QMap<QString,QColor> m_colors;</variable> +</variables> +<slots> + <slot>fileNew()</slot> + <slot>fileOpen()</slot> + <slot>fileSave()</slot> + <slot>fileSaveAs()</slot> + <slot>closeEvent( QCloseEvent * )</slot> + <slot>fileExit()</slot> + <slot>editCut()</slot> + <slot>editCopy()</slot> + <slot>editFind()</slot> + <slot>lookfor( const QString & text )</slot> + <slot>helpIndex()</slot> + <slot>helpContents()</slot> + <slot>helpAbout()</slot> + <slot>changedTableColor( int row, int )</slot> + <slot>changedIconColor( QIconViewItem * item )</slot> + <slot>changeView( QAction * action )</slot> + <slot>editAdd()</slot> + <slot>editOptions()</slot> + <slot>aboutToShow()</slot> +</slots> +<functions> + <function access="private">init()</function> + <function>clearData( bool fillWithDefaults )</function> + <function>populate()</function> + <function returnType="QPixmap">colorSwatch( const QColor color )</function> + <function>load( const QString & filename )</function> + <function returnType="bool">okToClear()</function> + <function>changedColor( const QString & name )</function> + <function returnType="bool">isWebColor( QColor color )</function> + <function>loadSettings()</function> + <function>saveSettings()</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/qtruby/rubylib/designer/examples/colortool/mainform.ui.rb b/qtruby/rubylib/designer/examples/colortool/mainform.ui.rb new file mode 100644 index 00000000..471233bd --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/mainform.ui.rb @@ -0,0 +1,530 @@ +class MainForm + + CLIP_AS_HEX = 0 + CLIP_AS_NAME = 1 + CLIP_AS_RGB = 2 + COL_NAME = 0 + COL_HEX = 1 + COL_WEB = 2 + WINDOWS_REGISTRY = "/QtExamples" + APP_KEY = "/ColorTool/" + +def init() + @clipboard = Qt::Application.clipboard() + if @clipboard.supportsSelection() + @clipboard.selectionMode = true + end + + findForm = 0 + loadSettings() + @filename = nil + @changed = false + @table_dirty = true + @icons_dirty = true + @colors = {} + @comments = {} + clearData( true ) +end + +def clearData( fillWithDefaults ) + setCaption( "Color Tool" ) + + @colors.clear() + @comments.clear() + + if fillWithDefaults + @colors["black"] = Qt::black + @colors["blue"] = Qt::blue + @colors["cyan"] = Qt::cyan + @colors["darkblue"] = Qt::darkBlue + @colors["darkcyan"] = Qt::darkCyan + @colors["darkgray"] = Qt::darkGray + @colors["darkgreen"] = Qt::darkGreen + @colors["darkmagenta"] = Qt::darkMagenta + @colors["darkred"] = Qt::darkRed + @colors["darkyellow"] = Qt::darkYellow + @colors["gray"] = Qt::gray + @colors["green"] = Qt::green + @colors["lightgray"] = Qt::lightGray + @colors["magenta"] = Qt::magenta + @colors["red"] = Qt::red + @colors["white"] = Qt::white + @colors["yellow"] = Qt::yellow + end + + populate() +end + +def populate() + if @table_dirty + (0...@colorTable.numRows).each do |r| + (0...@colorTable.numCols).each do |c| + @colorTable.clearCell( r, c ) + end + end + + @colorTable.numRows = @colors.length + if ! @colors.empty? + pixmap = Qt::Pixmap.new( 22, 22 ) + row = 0 + @colors.sort.each do |pair| + key = pair[0] + color = pair[1] + pixmap.fill( color ) + @colorTable.setText( row, COL_NAME, key ) + @colorTable.setPixmap( row, COL_NAME, pixmap ); + @colorTable.setText( row, COL_HEX, color.name().upcase() ) + if @show_web + item = Qt::CheckTableItem.new( @colorTable, "" ) + item.checked = webColor?( color ) + @colorTable.setItem( row, COL_WEB, item ) + end + row += 1 + end + @colorTable.setCurrentCell( 0, 0 ) + end + @colorTable.adjustColumn( COL_NAME ) + @colorTable.adjustColumn( COL_HEX ) + if @show_web + @colorTable.showColumn( COL_WEB ) + @colorTable.adjustColumn( COL_WEB ) + else + @colorTable.hideColumn( COL_WEB ) + end + @table_dirty = FALSE; + end + + if @icons_dirty + @colorIconView.clear() + + @colors.each do |key, data| + Qt::IconViewItem.new( @colorIconView, key, colorSwatch(data) ) + end + @icons_dirty = false + end +end + +def colorSwatch( color ) + pixmap = Qt::Pixmap.new( 80, 80 ) + pixmap.fill( white ) + painter = Qt::Painter.new + painter.begin( pixmap ) + painter.pen = NoPen + painter.brush = color + painter.drawEllipse( 0, 0, 80, 80 ) + painter.end() + return pixmap +end + +def fileNew() + if okToClear() + @filename = nil + @changed = false + @table_dirty = true + @icons_dirty = true + clearData( false ) + end +end + +def fileOpen() + if ! okToClear() + return + end + + filename = Qt::FileDialog.getOpenFileName( + nil, "Colors (*.txt)", self, + "file open", "Color Tool -- File Open" ) + if ! filename.nil? + load( filename ) + else + statusBar().message( "File Open abandoned", 2000 ) + end +end + +def fileSave() + if @filename.nil? + fileSaveAs() + return + end + + file = Qt::File.new( @filename ) + if file.open( Qt::IO_WriteOnly ) + stream = Qt::TextStream.new( file ) + if ! @comments.empty? + stream << @comments + "\n" << "\n" + end + + @colors.each do |key, color| + stream << "%3d %3d %3d \t\t#{key}" % [color.red, color.green, color.blue] << "\n" + end + file.close() + setCaption( "Color Tool -- #{@filename}" ) + statusBar().message( "Saved #{@colors.length} colors to '#{@filename}'", 3000 ) + @changed = false; + else + statusBar().message( "Failed to save '#{@filename}'", 3000 ) + end +end + +def fileSaveAs() + filename = Qt::FileDialog.getSaveFileName( + nil, "Colors (*.txt)", self, + "file save as", "Color Tool -- File Save As" ) + if ! filename.nil? + ans = 0 + if Qt::File.exists( filename ) + ans = Qt::MessageBox.warning( + self, "Color Tool -- Overwrite File", + "Overwrite\n'#{filename}'?" , + "&Yes", "&No", nil, 1, 1 ) + end + if ans == 0 + @filename = filename + fileSave() + return + end + end + statusBar().message( "Saving abandoned", 2000 ) +end + +def load( filename ) + clearData( false ) + @filename = filename + regex = Regexp.new( "^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\S+.*)$" ) + file = Qt::File.new( filename ) + if file.open( Qt::IO_ReadOnly ) + statusBar().message( "Loading '#{filename}'..." ) + stream = Qt::TextStream.new( file ) + while ! stream.eof() + line = stream.readLine() + m = regex.match( line ) + if m.nil? + @comments += line + else + @colors[m[4]] = Qt::Color.new(m[1].to_i,m[2].to_i,m[3].to_i ) + end + end + file.close() + @filename = filename + setCaption( "Color Tool -- #{@filename}" ) + statusBar().message( "Loaded '#{@filename}'", 3000 ) + visible = @colorWidgetStack.visibleWidget() + @icons_dirty = ! ( @table_dirty = ( visible == @tablePage ) ) + populate() + @icons_dirty = ! ( @table_dirty = ( visible != @tablePage ) ) + @changed = false + else + statusBar().message( "Failed to load '#{@filename}'", 3000 ) + end +end + + +def okToClear() + if @changed + if @filename.nil? + msg = "Unnamed colors " + else + msg = "Colors '#{@filename}'\n" + end + msg += "has been changed." + ans = Qt::MessageBox.information( + self, + "Color Tool -- Unsaved Changes", + msg, "&Save", "Cancel", "&Abandon", + 0, 1 ) + if ans == 0 + fileSave() + elsif ans == 1 + return false + end + end + + return true +end + +def closeEvent( e ) + fileExit() +end + +def fileExit() + if okToClear() + saveSettings() + Qt::Application.exit( 0 ) + end +end + +def editCut() + visible = @colorWidgetStack.visibleWidget() + statusBar().message( "Deleting '#{name}'" ) + + if visible == @tablePage && @colorTable.numRows() > 0 + row = @colorTable.currentRow() + name = @colorTable.text( row, 0 ) + @colorTable.removeRow( @colorTable.currentRow() ) + if row < @colorTable.numRows() + @colorTable.setCurrentCell( row, 0 ) + elsif @colorTable.numRows() > 0 + @colorTable.setCurrentCell( @colorTable.numRows() - 1, 0 ) + end + @icons_dirty = true + elsif visible == @iconsPage && @colorIconView.currentItem() + item = colorIconView.currentItem() + name = item.text() + if @colorIconView.count() == 1 + @colorIconView.clear() + else + current = item.nextItem() + if ! current + current = item.prevItem() + end + item.dispose + if current + @colorIconView.currentItem = current + end + @colorIconView.arrangeItemsInGrid() + end + @table_dirty = true + end + + if ! name.nil? + @colors.delete( name ) + @changed = true + statusBar().message( "Deleted '#{name}'", 5000 ) + else + statusBar().message( "Failed to delete '#{name}'", 5000 ) + end +end + +def editCopy() + visible = @colorWidgetStack.visibleWidget() + + if visible == @tablePage && @colorTable.numRows() + row = @colorTable.currentRow() + text = @colorTable.text( row, 0 ) + elsif visible == @iconsPage && ! @colorIconView.currentItem().nil? + item = @colorIconView.currentItem() + text = item.text() + end + if ! text.nil? + color = @colors[text] + case @clip_as + when CLIP_AS_HEX then text = color.name() + when CLIP_AS_NAME then + when CLIP_AS_RGB + text = "#{color.red},#{color.green},#{color.blue}" + end + @clipboard.text = text + statusBar().message( "Copied '" + text + "' to the clipboard" ) + end +end + +def editFind() + if ! @findForm + @findForm = FindForm.new( self ) + connect( @findForm, SIGNAL( 'lookfor(const QString&)' ), + self, SLOT( 'lookfor(const QString&)' ) ) + end + @findForm.show() +end + +def lookfor( text ) + if text.empty? + return + end + ltext = text.downcase() + visible = colorWidgetStack.visibleWidget() + found = false + + if visible == @tablePage && @colorTable.numRows() > 0 + row = @colorTable.currentRow() + (row+1...@colorTable.numRows).each do |i| + if @colorTable.text( i, 0 ).downcase().include?( ltext ) + @colorTable.setCurrentCell( i, 0 ) + @colorTable.clearSelection() + @colorTable.selectRow( i ) + found = true + break + end + end + if ! found + @colorTable.setCurrentCell( row, 0 ) + end + elsif visible == @iconsPage + start = @colorIconView.currentItem() + item = start.nextItem() unless start.nil? + while !item.nil? + if item.text().downcase().include?( ltext ) + @colorIconView.currentItem = item + @colorIconView.ensureItemVisible( item ) + found = true + break + end + item = item.nextItem() + end + if ! found && !start.nil? + @colorIconView.currentItem = start + end + end + if ! found + statusBar().message( "Could not find '#{text}' after here" ) + @findForm.notfound() + end +end + + + +def helpIndex() +end + +def helpContents() +end + +def helpAbout() +end + + +def changedTableColor( row, i ) + changedColor( @colorTable.text( row, COL_NAME ) ) +end + +def changedIconColor( item ) + changedColor( item.text() ) +end + +def changedColor( name ) + color = @colors[name] + r = color.red() + g = color.green() + b = color.blue() + statusBar().message( "%s \"%s\" (%d,%d,%d) %s {%.3f %.3f %.3f}" % + [name, color.name.upcase, + r, g, b, webColor?( color ) ? ' web' : '', + r / 255.0, g / 255.0, b / 255.0] ) +end + +def changeView(action) + if action == @viewTableAction + @colorWidgetStack.raiseWidget( @tablePage ) + else + @colorWidgetStack.raiseWidget( @iconsPage ) + end +end + +def webColor?( color ) + r = color.red() + g = color.green() + b = color.blue() + + return ( ( r == 0 || r == 51 || r == 102 || + r == 153 || r == 204 || r == 255 ) && + ( g == 0 || g == 51 || g == 102 || + g == 153 || g == 204 || g == 255 ) && + ( b == 0 || b == 51 || b == 102 || + b == 153 || b == 204 || b == 255 ) ) +end + + +def editAdd() + color = Qt::white + if ! @colors.empty? + visible = @colorWidgetStack.visibleWidget() + if visible == @tablePage + color = Qt::Color.new(@colorTable.text( @colorTable.currentRow(), + @colorTable.currentColumn() )) + else + color = Qt::Color.new(@colorIconView.currentItem().text()) + end + end + color = Qt::ColorDialog.getColor( color, self ) + if color.valid? + pixmap = Qt::Pixmap.new( 80, 10 ) + pixmap.fill( color ) + colorForm = ColorNameForm.new( self, "color", true ) + colorForm.setColors( @colors ) + colorForm.colorLabel.setPixmap( pixmap ) + if colorForm.exec() + name = colorForm.colorLineEdit.text() + @colors[name] = color + pixmap = Qt::Pixmap.new( 22, 22 ) + pixmap.fill( color ) + row = @colorTable.currentRow() + @colorTable.insertRows( row, 1 ) + @colorTable.setText( row, COL_NAME, name ) + @colorTable.setPixmap( row, COL_NAME, pixmap ) + @colorTable.setText( row, COL_HEX, color.name().upcase() ) + if @show_web + item = Qt::CheckTableItem.new( @colorTable, "" ) + item.checked = webColor?( color ) + @colorTable.setItem( row, COL_WEB, item ) + end + @colorTable.setCurrentCell( row, 0 ) + + Qt::IconViewItem.new( @colorIconView, name, + colorSwatch( color ) ) + @changed = true + end + end +end + +def editOptions() + options = OptionsForm.new( self, "options", true ) + case @clip_as + when CLIP_AS_HEX + options.hexRadioButton.checked = true + when CLIP_AS_NAME + options.nameRadioButton.checked = true + when CLIP_AS_RGB + options.rgbRadioButton.checked = true + end + options.webCheckBox.checked = @show_web + + if options.exec() + if options.hexRadioButton.checked? + @clip_as = CLIP_AS_HEX + elsif options.nameRadioButton.checked? + @clip_as = CLIP_AS_NAME + elsif options.rgbRadioButton.checked? + @clip_as = CLIP_AS_RGB + end + @table_dirty = @show_web != options.webCheckBox.checked? + @show_web = options.webCheckBox.checked? + populate() + end +end + +def loadSettings() + settings = Qt::Settings.new + settings.insertSearchPath( Qt::Settings::Windows, WINDOWS_REGISTRY ) + windowWidth = settings.readNumEntry( APP_KEY + "WindowWidth", 550 ) + windowHeight = settings.readNumEntry( APP_KEY + "WindowHeight", 500 ) + windowX = settings.readNumEntry( APP_KEY + "WindowX", 0 ) + windowY = settings.readNumEntry( APP_KEY + "WindowY", 0 ) + @clip_as = settings.readNumEntry( APP_KEY + "ClipAs", CLIP_AS_HEX ) + @show_web = settings.readBoolEntry( APP_KEY + "ShowWeb", true ) + if ! settings.readBoolEntry( APP_KEY + "View", true ) + @colorWidgetStack.raiseWidget( @iconsPage ) + @viewIconsAction.on = true + end + + resize( windowWidth, windowHeight ) + move( windowX, windowY ) +end + +def saveSettings() + settings = Qt::Settings.new + settings.insertSearchPath( Qt::Settings::Windows, WINDOWS_REGISTRY ) + settings.writeEntry( APP_KEY + "WindowWidth", width() ) + settings.writeEntry( APP_KEY + "WindowHeight", height() ) + settings.writeEntry( APP_KEY + "WindowX", x() ) + settings.writeEntry( APP_KEY + "WindowY", y() ) + settings.writeEntry( APP_KEY + "ClipAs", @clip_as ) + settings.writeEntry( APP_KEY + "ShowWeb", @show_web ) + settings.writeEntry( APP_KEY + "View", @colorWidgetStack.visibleWidget() == @tablePage ) +end + + +def aboutToShow() + populate() +end + +end diff --git a/qtruby/rubylib/designer/examples/colortool/optionsform.ui b/qtruby/rubylib/designer/examples/colortool/optionsform.ui new file mode 100644 index 00000000..1bb7e8ef --- /dev/null +++ b/qtruby/rubylib/designer/examples/colortool/optionsform.ui @@ -0,0 +1,153 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>OptionsForm</class> +<widget class="QDialog"> + <property name="name"> + <cstring>OptionsForm</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>306</width> + <height>226</height> + </rect> + </property> + <property name="caption"> + <string>Color Tool - Options</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>GroupBox2</cstring> + </property> + <property name="title"> + <string>Table View</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>webCheckBox</cstring> + </property> + <property name="text"> + <string>Indicate &Web colors</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>ButtonGroup1</cstring> + </property> + <property name="title"> + <string>Copy to Clipboard As</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>hexRadioButton</cstring> + </property> + <property name="text"> + <string>&Hex, e.g. #AB347F</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>nameRadioButton</cstring> + </property> + <property name="text"> + <string>&Name, e.g. light blue</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>rgbRadioButton</cstring> + </property> + <property name="text"> + <string>&RGB, e.g. 51,255,102</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout5</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>Spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>0</width> + <height>10</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>okPushButton</cstring> + </property> + <property name="text"> + <string>&OK</string> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>cancelPushButton</cstring> + </property> + <property name="text"> + <string>&Cancel</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>okPushButton</sender> + <signal>clicked()</signal> + <receiver>OptionsForm</receiver> + <slot>accept()</slot> + </connection> + <connection> + <sender>cancelPushButton</sender> + <signal>clicked()</signal> + <receiver>OptionsForm</receiver> + <slot>reject()</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in implementation">optionsform.ui.h</include> +</includes> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/qtruby/rubylib/designer/rbuic/LICENSE.GPL b/qtruby/rubylib/designer/rbuic/LICENSE.GPL new file mode 100644 index 00000000..3e51afd8 --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/LICENSE.GPL @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/qtruby/rubylib/designer/rbuic/Makefile.am b/qtruby/rubylib/designer/rbuic/Makefile.am new file mode 100644 index 00000000..a8eef55d --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/Makefile.am @@ -0,0 +1,24 @@ +AM_CPPFLAGS = -DUIC -DQT_INTERNAL_XML +AM_CXXFLAGS = $(KDE_CXXFLAGS) + +bin_PROGRAMS = rbuic +noinst_LTLIBRARIES = librbuic.la + +INCLUDES = $(all_includes) + +METASOURCES = AUTO + +librbuic_la_SOURCES = widgetdatabase.cpp uic.cpp subclassing.cpp parser.cpp object.cpp form.cpp embed.cpp domtool.cpp +librbuic_la_LIBADD = $(LIB_QT) $(LIBZ) + +rbuic_SOURCES = main.cpp +rbuic_LDADD = librbuic.la $(all_libraries) +rbuic_LDFLAGS = + +EXTRA_DIST = domtool.cpp domtool.h embed.cpp form.cpp globaldefs.h main.cpp object.cpp parser.cpp parser.h subclassing.cpp uic.cpp uic.h widgetdatabase.cpp widgetdatabase.h widgetinterface.h + +messages: + LIST=`find . -name \*.h -o -name \*.hh -o -name \*.H -o -name \*.hxx -o -name \*.hpp -o -name \*.cpp -o -name \*.cc -o -name \*.cxx -o -name \*.ecpp -o -name \*.C`; \ + if test -n "$$LIST"; then \ + $(XGETTEXT) $$LIST -o $(podir)/puic.pot; \ + fi diff --git a/qtruby/rubylib/designer/rbuic/TODO b/qtruby/rubylib/designer/rbuic/TODO new file mode 100644 index 00000000..8e780973 --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/TODO @@ -0,0 +1,4 @@ + +- Database code needs more testing? + + diff --git a/qtruby/rubylib/designer/rbuic/domtool.cpp b/qtruby/rubylib/designer/rbuic/domtool.cpp new file mode 100644 index 00000000..1c30486f --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/domtool.cpp @@ -0,0 +1,453 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qt Designer. +** +** 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. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include "domtool.h" + +#include <qsizepolicy.h> +#include <qcolor.h> +#include <qcursor.h> +#include <qdatetime.h> +#include <qrect.h> +#include <qsize.h> +#include <qfont.h> +#include <qdom.h> + +/*! + \class DomTool domtool.h + \brief Tools for the dom + + A collection of static functions used by Resource (part of the + designer) and Uic. + +*/ + +/*! + Returns the contents of property \a name of object \a e as + variant or the variant passed as \a defValue if the property does + not exist. + + \sa hasProperty() +*/ +QVariant DomTool::readProperty( const QDomElement& e, const QString& name, const QVariant& defValue, QString& comment ) +{ + QDomElement n; + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "property" ) { + if ( n.attribute( "name" ) != name ) + continue; + return elementToVariant( n.firstChild().toElement(), defValue, comment ); + } + } + return defValue; +} + + +/*! + \overload + */ +QVariant DomTool::readProperty( const QDomElement& e, const QString& name, const QVariant& defValue ) +{ + QString comment; + return readProperty( e, name, defValue, comment ); +} + +/*! + Returns wheter object \a e defines property \a name or not. + + \sa readProperty() + */ +bool DomTool::hasProperty( const QDomElement& e, const QString& name ) +{ + QDomElement n; + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "property" ) { + if ( n.attribute( "name" ) != name ) + continue; + return true; + } + } + return false; +} + +QStringList DomTool::propertiesOfType( const QDomElement& e, const QString& type ) +{ + QStringList result; + QDomElement n; + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "property" ) { + QDomElement n2 = n.firstChild().toElement(); + if ( n2.tagName() == type ) + result += n.attribute( "name" ); + } + } + return result; +} + + +QVariant DomTool::elementToVariant( const QDomElement& e, const QVariant& defValue ) +{ + QString dummy; + return elementToVariant( e, defValue, dummy ); +} + +/*! + Interprets element \a e as variant and returns the result of the interpretation. + */ +QVariant DomTool::elementToVariant( const QDomElement& e, const QVariant& defValue, QString &comment ) +{ + QVariant v; + if ( e.tagName() == "rect" ) { + QDomElement n3 = e.firstChild().toElement(); + int x = 0, y = 0, w = 0, h = 0; + while ( !n3.isNull() ) { + if ( n3.tagName() == "x" ) + x = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "y" ) + y = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "width" ) + w = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "height" ) + h = n3.firstChild().toText().data().toInt(); + n3 = n3.nextSibling().toElement(); + } + v = QVariant( QRect( x, y, w, h ) ); + } else if ( e.tagName() == "point" ) { + QDomElement n3 = e.firstChild().toElement(); + int x = 0, y = 0; + while ( !n3.isNull() ) { + if ( n3.tagName() == "x" ) + x = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "y" ) + y = n3.firstChild().toText().data().toInt(); + n3 = n3.nextSibling().toElement(); + } + v = QVariant( QPoint( x, y ) ); + } else if ( e.tagName() == "size" ) { + QDomElement n3 = e.firstChild().toElement(); + int w = 0, h = 0; + while ( !n3.isNull() ) { + if ( n3.tagName() == "width" ) + w = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "height" ) + h = n3.firstChild().toText().data().toInt(); + n3 = n3.nextSibling().toElement(); + } + v = QVariant( QSize( w, h ) ); + } else if ( e.tagName() == "color" ) { + v = QVariant( readColor( e ) ); + } else if ( e.tagName() == "font" ) { + QDomElement n3 = e.firstChild().toElement(); + QFont f( defValue.toFont() ); + while ( !n3.isNull() ) { + if ( n3.tagName() == "family" ) + f.setFamily( n3.firstChild().toText().data() ); + else if ( n3.tagName() == "pointsize" ) + f.setPointSize( n3.firstChild().toText().data().toInt() ); + else if ( n3.tagName() == "bold" ) + f.setBold( n3.firstChild().toText().data().toInt() ); + else if ( n3.tagName() == "italic" ) + f.setItalic( n3.firstChild().toText().data().toInt() ); + else if ( n3.tagName() == "underline" ) + f.setUnderline( n3.firstChild().toText().data().toInt() ); + else if ( n3.tagName() == "strikeout" ) + f.setStrikeOut( n3.firstChild().toText().data().toInt() ); + n3 = n3.nextSibling().toElement(); + } + v = QVariant( f ); + } else if ( e.tagName() == "string" ) { + v = QVariant( e.firstChild().toText().data() ); + QDomElement n = e; + n = n.nextSibling().toElement(); + if ( n.tagName() == "comment" ) + comment = n.firstChild().toText().data(); + } else if ( e.tagName() == "cstring" ) { + v = QVariant( QCString( e.firstChild().toText().data() ) ); + } else if ( e.tagName() == "number" ) { + bool ok = true; + v = QVariant( e.firstChild().toText().data().toInt( &ok ) ); + if ( !ok ) + v = QVariant( e.firstChild().toText().data().toDouble() ); + } else if ( e.tagName() == "bool" ) { + QString t = e.firstChild().toText().data(); + v = QVariant( t == "true" || t == "1", 0 ); + } else if ( e.tagName() == "pixmap" ) { + v = QVariant( e.firstChild().toText().data() ); + } else if ( e.tagName() == "iconset" ) { + v = QVariant( e.firstChild().toText().data() ); + } else if ( e.tagName() == "image" ) { + v = QVariant( e.firstChild().toText().data() ); + } else if ( e.tagName() == "enum" ) { + v = QVariant( e.firstChild().toText().data() ); + } else if ( e.tagName() == "set" ) { + v = QVariant( e.firstChild().toText().data() ); + } else if ( e.tagName() == "sizepolicy" ) { + QDomElement n3 = e.firstChild().toElement(); + QSizePolicy sp; + while ( !n3.isNull() ) { + if ( n3.tagName() == "hsizetype" ) + sp.setHorData( (QSizePolicy::SizeType)n3.firstChild().toText().data().toInt() ); + else if ( n3.tagName() == "vsizetype" ) + sp.setVerData( (QSizePolicy::SizeType)n3.firstChild().toText().data().toInt() ); + else if ( n3.tagName() == "horstretch" ) + sp.setHorStretch( n3.firstChild().toText().data().toInt() ); + else if ( n3.tagName() == "verstretch" ) + sp.setVerStretch( n3.firstChild().toText().data().toInt() ); + n3 = n3.nextSibling().toElement(); + } + v = QVariant( sp ); + } else if ( e.tagName() == "cursor" ) { + v = QVariant( QCursor( e.firstChild().toText().data().toInt() ) ); + } else if ( e.tagName() == "stringlist" ) { + QStringList lst; + QDomElement n; + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) + lst << n.firstChild().toText().data(); + v = QVariant( lst ); + } else if ( e.tagName() == "date" ) { + QDomElement n3 = e.firstChild().toElement(); + int y, m, d; + y = m = d = 0; + while ( !n3.isNull() ) { + if ( n3.tagName() == "year" ) + y = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "month" ) + m = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "day" ) + d = n3.firstChild().toText().data().toInt(); + n3 = n3.nextSibling().toElement(); + } + v = QVariant( QDate( y, m, d ) ); + } else if ( e.tagName() == "time" ) { + QDomElement n3 = e.firstChild().toElement(); + int h, m, s; + h = m = s = 0; + while ( !n3.isNull() ) { + if ( n3.tagName() == "hour" ) + h = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "minute" ) + m = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "second" ) + s = n3.firstChild().toText().data().toInt(); + n3 = n3.nextSibling().toElement(); + } + v = QVariant( QTime( h, m, s ) ); + } else if ( e.tagName() == "datetime" ) { + QDomElement n3 = e.firstChild().toElement(); + int h, mi, s, y, mo, d ; + h = mi = s = y = mo = d = 0; + while ( !n3.isNull() ) { + if ( n3.tagName() == "hour" ) + h = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "minute" ) + mi = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "second" ) + s = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "year" ) + y = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "month" ) + mo = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "day" ) + d = n3.firstChild().toText().data().toInt(); + n3 = n3.nextSibling().toElement(); + } + v = QVariant( QDateTime( QDate( y, mo, d ), QTime( h, mi, s ) ) ); + } + return v; +} + + +/*! Returns the color which is returned in the dom element \a e. + */ + +QColor DomTool::readColor( const QDomElement &e ) +{ + QDomElement n = e.firstChild().toElement(); + int r= 0, g = 0, b = 0; + while ( !n.isNull() ) { + if ( n.tagName() == "red" ) + r = n.firstChild().toText().data().toInt(); + else if ( n.tagName() == "green" ) + g = n.firstChild().toText().data().toInt(); + else if ( n.tagName() == "blue" ) + b = n.firstChild().toText().data().toInt(); + n = n.nextSibling().toElement(); + } + + return QColor( r, g, b ); +} + +/*! + Returns the contents of attribute \a name of object \a e as + variant or the variant passed as \a defValue if the attribute does + not exist. + + \sa hasAttribute() + */ +QVariant DomTool::readAttribute( const QDomElement& e, const QString& name, const QVariant& defValue, QString& comment ) +{ + QDomElement n; + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "attribute" ) { + if ( n.attribute( "name" ) != name ) + continue; + return elementToVariant( n.firstChild().toElement(), defValue, comment ); + } + } + return defValue; +} + +/*! + \overload +*/ +QVariant DomTool::readAttribute( const QDomElement& e, const QString& name, const QVariant& defValue ) +{ + QString comment; + return readAttribute( e, name, defValue, comment ); +} + +/*! + Returns wheter object \a e defines attribute \a name or not. + + \sa readAttribute() + */ +bool DomTool::hasAttribute( const QDomElement& e, const QString& name ) +{ + QDomElement n; + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "attribute" ) { + if ( n.attribute( "name" ) != name ) + continue; + return true; + } + } + return false; +} + +static bool toBool( const QString& s ) +{ + return s == "true" || s.toInt() != 0; +} + +/*! + Convert Qt 2.x format to Qt 3.0 format if necessary +*/ +void DomTool::fixDocument( QDomDocument& doc ) +{ + QDomElement e; + QDomNode n; + QDomNodeList nl; + int i = 0; + + e = doc.firstChild().toElement(); + if ( e.tagName() != "UI" ) + return; + + // latest version, don't do anything + if ( e.hasAttribute("version") && e.attribute("version").toDouble() > 3.0 ) + return; + + nl = doc.elementsByTagName( "property" ); + + // in 3.0, we need to fix a spelling error + if ( e.hasAttribute("version") && e.attribute("version").toDouble() == 3.0 ) { + for ( i = 0; i < (int) nl.length(); i++ ) { + QDomElement el = nl.item(i).toElement(); + QString s = el.attribute( "name" ); + if ( s == "resizeable" ) { + el.removeAttribute( "name" ); + el.setAttribute( "name", "resizable" ); + } + } + return; + } + + + // in versions smaller than 3.0 we need to change more + e.setAttribute( "version", 3.0 ); + + e.setAttribute("stdsetdef", 1 ); + for ( i = 0; i < (int) nl.length(); i++ ) { + e = nl.item(i).toElement(); + QString name; + QDomElement n2 = e.firstChild().toElement(); + if ( n2.tagName() == "name" ) { + name = n2.firstChild().toText().data(); + if ( name == "resizeable" ) + e.setAttribute( "name", "resizable" ); + else + e.setAttribute( "name", name ); + e.removeChild( n2 ); + } + bool stdset = toBool( e.attribute( "stdset" ) ); + if ( stdset || name == "toolTip" || name == "whatsThis" || + name == "buddy" || + e.parentNode().toElement().tagName() == "item" || + e.parentNode().toElement().tagName() == "spacer" || + e.parentNode().toElement().tagName() == "column" + ) + e.removeAttribute( "stdset" ); + else + e.setAttribute( "stdset", 0 ); + } + + nl = doc.elementsByTagName( "attribute" ); + for ( i = 0; i < (int) nl.length(); i++ ) { + e = nl.item(i).toElement(); + QString name; + QDomElement n2 = e.firstChild().toElement(); + if ( n2.tagName() == "name" ) { + name = n2.firstChild().toText().data(); + e.setAttribute( "name", name ); + e.removeChild( n2 ); + } + } + + nl = doc.elementsByTagName( "image" ); + for ( i = 0; i < (int) nl.length(); i++ ) { + e = nl.item(i).toElement(); + QString name; + QDomElement n2 = e.firstChild().toElement(); + if ( n2.tagName() == "name" ) { + name = n2.firstChild().toText().data(); + e.setAttribute( "name", name ); + e.removeChild( n2 ); + } + } + + nl = doc.elementsByTagName( "widget" ); + for ( i = 0; i < (int) nl.length(); i++ ) { + e = nl.item(i).toElement(); + QString name; + QDomElement n2 = e.firstChild().toElement(); + if ( n2.tagName() == "class" ) { + name = n2.firstChild().toText().data(); + e.setAttribute( "class", name ); + e.removeChild( n2 ); + } + } + +} + diff --git a/qtruby/rubylib/designer/rbuic/domtool.h b/qtruby/rubylib/designer/rbuic/domtool.h new file mode 100644 index 00000000..61b4269a --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/domtool.h @@ -0,0 +1,53 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qt Designer. +** +** 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. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef DOMTOOL_H +#define DOMTOOL_H + +#include <qvariant.h> +#include <qnamespace.h> + +class QDomElement; +class QDomDocument; + +class DomTool : public Qt +{ +public: + static QVariant readProperty( const QDomElement& e, const QString& name, const QVariant& defValue ); + static QVariant readProperty( const QDomElement& e, const QString& name, const QVariant& defValue, QString& comment ); + static bool hasProperty( const QDomElement& e, const QString& name ); + static QStringList propertiesOfType( const QDomElement& e, const QString& type ); + static QVariant elementToVariant( const QDomElement& e, const QVariant& defValue ); + static QVariant elementToVariant( const QDomElement& e, const QVariant& defValue, QString &comment ); + static QVariant readAttribute( const QDomElement& e, const QString& name, const QVariant& defValue ); + static QVariant readAttribute( const QDomElement& e, const QString& name, const QVariant& defValue, QString& comment ); + static bool hasAttribute( const QDomElement& e, const QString& name ); + static QColor readColor( const QDomElement &e ); + static void fixDocument( QDomDocument& ); +}; + + +#endif // DOMTOOL_H diff --git a/qtruby/rubylib/designer/rbuic/embed.cpp b/qtruby/rubylib/designer/rbuic/embed.cpp new file mode 100644 index 00000000..89985968 --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/embed.cpp @@ -0,0 +1,297 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** Copyright (c) 2001 Phil Thompson <phil@river-bank.demon.co.uk> +** Copyright (c) 2002 Riverbank Computing Limited <info@riverbankcomputing.co.uk> +** Copyright (c) 2002 Germain Garand <germain@ebooksfrance.com> +** +** This file is part of Qt Designer. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ +/* +** 06/2002 : Initial release of puic, the PerlQt User Interface Compiler, +** a work derivated from uic (the Qt User Interface Compiler) +** and pyuic (the PyQt User Interface Compiler). +** +** G.Garand +** +** 08/2003 : Initial release of rbuic, the QtRuby User Interface Compiler, +** a work derived from the PerlQt puic. +** +** Richard Dale +** +**********************************************************************/ + +#include "uic.h" +#include <qfile.h> +#include <qimage.h> +#include <qstringlist.h> +#include <qdatetime.h> +#include <qfileinfo.h> +#define NO_STATIC_COLORS +#include <globaldefs.h> +#include <qregexp.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> + +struct EmbedImage +{ + int width, height, depth; + int numColors; + QRgb* colorTable; + QString name; + QString cname; + bool alpha; +}; + +static QString convertToCIdentifier( const char *s ) +{ + QString r = s; + int len = r.length(); + if ( len > 0 && !isalpha( (char)r[0].latin1() ) ) + r[0] = '_'; + for ( int i=1; i<len; i++ ) { + if ( !isalnum( (char)r[i].latin1() ) ) + r[i] = '_'; + } + return r; +} + + +static ulong embedData( QTextStream& out, const uchar* input, int nbytes ) +{ +#ifndef QT_NO_IMAGE_COLLECTION_COMPRESSION + QByteArray bazip( qCompress( input, nbytes ) ); + ulong len = bazip.size(); +#else + ulong len = nbytes; +#endif + static const char hexdigits[] = "0123456789abcdef"; + QString s; + for ( int i=0; i<(int)len; i++ ) { + if ( (i%14) == 0 ) { + s += "\n "; + out << (const char*)s; + s.truncate( 0 ); + } + uint v = (uchar) +#ifndef QT_NO_IMAGE_COLLECTION_COMPRESSION + bazip +#else + input +#endif + [i]; + s += "0x"; + s += hexdigits[(v >> 4) & 15]; + s += hexdigits[v & 15]; + if ( i < (int)len-1 ) + s += ','; + } + if ( s.length() ) + out << (const char*)s; + return len; +} + +static void embedData( QTextStream& out, const QRgb* input, int n ) +{ + out << hex; + const QRgb *v = input; + for ( int i=0; i<n; i++ ) { + if ( (i%14) == 0 ) + out << "\n "; + out << "0x"; + out << hex << *v++; + if ( i < n-1 ) + out << ','; + } + out << dec; // back to decimal mode +} + +void Uic::embed( QTextStream& out, const char* project, const QStringList& images ) +{ + + QString cProject = convertToCIdentifier( project ); + + QStringList::ConstIterator it; + out << "# Image collection for project '" << project << "'." << endl; + out << "#" << endl; + out << "# Generated from reading image files: " << endl; + for ( it = images.begin(); it != images.end(); ++it ) + out << "# " << *it << endl; + out << "#" << endl; + out << "# Created: " << QDateTime::currentDateTime().toString() << endl; + out << "# by: The QtRuby User Interface Compiler (rbuic)" << endl; + out << "#" << endl; + out << "# WARNING! All changes made in this file will be lost!" << endl; + out << endl; + if (hasKDEwidget) { + out << "require 'Korundum'" << endl; + } else { + out << "require 'Qt'" << endl; + } + out << endl; + + out << indent << "class MimeSourceFactory_" << cProject << " < Qt::MimeSourceFactory" << endl; + out << endl; + + QPtrList<EmbedImage> list_image; + int image_count = 0; + for ( it = images.begin(); it != images.end(); ++it ) { + QImage img; + if ( !img.load( *it ) ) { + fprintf( stderr, "rbuic: cannot load image file %s\n", (*it).latin1() ); + continue; + } + EmbedImage *e = new EmbedImage; + e->width = img.width(); + e->height = img.height(); + e->depth = img.depth(); + e->numColors = img.numColors(); + e->colorTable = new QRgb[e->numColors]; + e->alpha = img.hasAlphaBuffer(); + memcpy(e->colorTable, img.colorTable(), e->numColors*sizeof(QRgb)); + QFileInfo fi( *it ); + e->name = fi.fileName(); + e->cname = QString("@@image_%1").arg( image_count++); + list_image.append( e ); + out << "# " << *it << endl; + QString imgname = (const char *)e->cname; + + QString s; + if ( e->depth == 1 ) + img = img.convertBitOrder(QImage::BigEndian); + out << indent << imgname << "_data = ["; + embedData( out, img.bits(), img.numBytes() ); + out << "]\n\n"; + if ( e->numColors ) { + out << indent << imgname << "_ctable = ["; + embedData( out, e->colorTable, e->numColors ); + out << "]\n\n"; + } + } + + ++indent; + if ( !list_image.isEmpty() ) { + out << indent << "@@embed_images = {\n"; + ++indent; + EmbedImage *e = list_image.first(); + while ( e ) + { + out << indent << "\"" << e->name << "\"" << " => [" << e->cname << "_data, " + << e->width << ", " << e->height << ", " << e->depth << ", " + << (e->numColors ? e->cname + "_ctable" : QString::fromLatin1( "[]" ) ) << ", " + << (e->alpha ? "true" : "false") << "]," << endl; + e = list_image.next(); + } + --indent; + out << indent << "}" << endl; + + out << endl; + out << indent << "@@images = Hash.new" << endl; + out << endl; + out << indent << "def uic_findImage( name )" << endl; + ++indent; + out << indent << "if !@@images[name].nil?" << endl; + ++indent; + out << indent << "return @@images[name]" << endl; + --indent; + out << indent << "end" << endl; + + out << indent << "if @@embed_images[name].nil?" << endl; + ++indent; + out << indent << "return Qt::Image.new()" << endl; + --indent; + out << indent << "end" << endl; + out << indent << endl; +#ifndef QT_NO_IMAGE_COLLECTION_COMPRESSION + out << indent << "baunzip = qUncompress( @@embed_images[name][0].pack(\"C*\")," << endl; + out << indent << " @@embed_images[name][0].length )" << endl; + out << indent << "img = Qt::Image.new( baunzip.data," << endl; + out << indent << " @@embed_images[name][1]," << endl; + out << indent << " @@embed_images[name][2]," << endl; + out << indent << " @@embed_images[name][3]," << endl; + out << indent << " @@embed_images[name][4]," << endl; + out << indent << " @@embed_images[name][4].length," << endl; + out << indent << " Qt::Image::BigEndian )" << endl; +#else + out << indent << "img = Qt::Image.new( @@embed_images[name][0].pack(\"C*\")," << endl; + out << indent << " @@embed_images[name][1]," << endl; + out << indent << " @@embed_images[name][2]," << endl; + out << indent << " @@embed_images[name][3]," << endl; + out << indent << " @@embed_images[name][4]," << endl; + out << indent << " @@embed_images[name][4].length," << endl; + out << indent << " Qt::Image::BigEndian )" << endl; +#endif + out << indent << "if @@embed_images[name][5]" << endl; + ++indent; + out << indent << "img.setAlphaBuffer(true)" << endl; + --indent; + out << indent << "end" << endl; + out << indent << "@@images[name] = img" << endl; + out << indent << "return img" << endl; + --indent; + out << indent << "end" << endl; + out << endl; + out << indent << "def data( abs_name )" << endl; + ++indent; + out << indent << "img = uic_findImage(abs_name)" << endl; + out << indent << "if img.nil?" << endl; + ++indent; + out << indent << "Qt::MimeSourceFactory.removeFactory(self)" << endl; + out << indent << "s = Qt::MimeSourceFactory.defaultFactory().data(abs_name);" << endl; + out << indent << "Qt::MimeSourceFactory.addFactory(self)" << endl; + out << indent << "return s" << endl; + --indent; + out << indent << "end" << endl; + out << indent << "Qt::MimeSourceFactory.defaultFactory().setImage(abs_name, img)" << endl; + out << indent << "return Qt::MimeSourceFactory.defaultFactory().data(abs_name)" << endl; + --indent; + out << indent << "end" << endl; + --indent; + out << indent << "end" << endl; + + out << endl; + out << endl; + + out << indent << "module StaticInitImages_" << cProject << endl; + ++indent; + out << indent << "@@factories = Hash.new" << endl; + out << indent << endl; + out << indent << "def StaticInitImages_" << cProject << ".qInitImages" << endl; + ++indent; + out << indent << "factory = MimeSourceFactory_" << cProject << ".new()" << endl; + out << indent << "Qt::MimeSourceFactory.defaultFactory().addFactory(factory)" << endl; + out << indent << "@@factories['MimeSourceFactory_" << cProject << "'] = factory" << endl; + --indent; + out << indent << "end" << endl; + out << endl; + out << indent << "def StaticInitImages_" << cProject << ".qCleanupImages" << endl; + ++indent; + out << indent << "for values in @@factories" << endl; + ++indent; + out << indent << "Qt::MimeSourceFactory.defaultFactory().removeFactory(values)" << endl; + --indent; + out << indent << "end" << endl; + out << indent << "@@factories = nil" << endl; + --indent; + out << indent << "end" << endl; + out << endl; + out << indent << "StaticInitImages_" << cProject << ".qInitImages" << endl; + --indent; + out << indent << "end" << endl; + out << endl; + } +} diff --git a/qtruby/rubylib/designer/rbuic/form.cpp b/qtruby/rubylib/designer/rbuic/form.cpp new file mode 100644 index 00000000..7efbacd1 --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/form.cpp @@ -0,0 +1,1017 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** Copyright (c) 2002 Germain Garand <germain@ebooksfrance.com> +** +** This file is part of Qt Designer. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ +/* +** 06/2002 : Initial release of puic, the PerlQt User Interface Compiler, +** a work derivated from uic (the Qt User Interface Compiler) +** and pyuic (the PyQt User Interface Compiler). +** +** G.Garand +** +**********************************************************************/ + +#include "uic.h" +#include "parser.h" +#include "widgetdatabase.h" +#include "domtool.h" + +#include <qstringlist.h> +#include <qregexp.h> +#include <qfile.h> +#include <qfileinfo.h> + +#define NO_STATIC_COLORS +#include <globaldefs.h> + +#include <zlib.h> + +static QByteArray unzipXPM( QString data, ulong& length ) +{ + const int lengthOffset = 4; + int baSize = data.length() / 2 + lengthOffset; + uchar *ba = new uchar[ baSize ]; + for ( int i = lengthOffset; i < baSize; ++i ) { + char h = data[ 2 * (i-lengthOffset) ].latin1(); + char l = data[ 2 * (i-lengthOffset) + 1 ].latin1(); + uchar r = 0; + if ( h <= '9' ) + r += h - '0'; + else + r += h - 'a' + 10; + r = r << 4; + if ( l <= '9' ) + r += l - '0'; + else + r += l - 'a' + 10; + ba[ i ] = r; + } + // qUncompress() expects the first 4 bytes to be the expected length of the + // uncompressed data + ba[0] = ( length & 0xff000000 ) >> 24; + ba[1] = ( length & 0x00ff0000 ) >> 16; + ba[2] = ( length & 0x0000ff00 ) >> 8; + ba[3] = ( length & 0x000000ff ); + QByteArray baunzip = qUncompress( ba, baSize ); + delete[] ba; + return baunzip; +} + +static QString imageDataName(QString name) { + QString result = name + "_data"; + result.replace("@", "@@"); + return result; +} + +/*! + Creates an implementation ( cpp-file ) for the form given in \a e + + \sa createFormDecl(), createObjectImpl() + */ +void Uic::createFormImpl( const QDomElement &e ) +{ + QDomElement n; + QDomNodeList nl; + int i; + QString objClass = getClassName( e ); + if ( objClass.isEmpty() ) + return; + QString objName = getObjectName( e ); + + if (hasKDEwidget) { + out << indent << "require 'Korundum'" << endl << endl; + } else { + out << indent << "require 'Qt'" << endl << endl; + } + + // generate local and local includes required + QStringList globalIncludes, localIncludes; + QStringList::Iterator it; + QStringList sqlClasses; + + QMap<QString, CustomInclude> customWidgetIncludes; + QMap<QString, QString> functionImpls; + + // find additional slots + QStringList extraSlots; + QStringList extraSlotTypes; + nl = e.parentNode().toElement().elementsByTagName( "slot" ); + for ( i = 0; i < (int) nl.length(); i++ ) { + n = nl.item(i).toElement(); + if ( n.parentNode().toElement().tagName() != "slots" + && n.parentNode().toElement().tagName() != "connections" ) + continue; + if ( n.attribute( "language", "C++" ) != "C++" ) + continue; + QString slotName = n.firstChild().toText().data().stripWhiteSpace(); + if ( slotName.endsWith( ";" ) ) + slotName = slotName.left( slotName.length() - 1 ); + + extraSlots += Parser::cleanArgs(slotName); + extraSlotTypes += n.attribute( "returnType", "void" ); + } + + // find signals + QStringList extraSignals; + nl = e.parentNode().toElement().elementsByTagName( "signal" ); + for ( i = 0; i < (int) nl.length(); i++ ) { + n = nl.item(i).toElement(); + if ( n.parentNode().toElement().tagName() != "signals" + && n.parentNode().toElement().tagName() != "connections" ) + continue; + if ( n.attribute( "language", "C++" ) != "C++" ) + continue; + QString sigName = n.firstChild().toText().data().stripWhiteSpace(); + if ( sigName.endsWith( ";" ) ) + sigName = sigName.left( sigName.length() - 1 ); + extraSignals += sigName; + } + + //find additional functions + QStringList extraFunctions; + for ( n = e; !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "functions" ) { // compatibility + for ( QDomElement n2 = n.firstChild().toElement(); !n2.isNull(); n2 = n2.nextSibling().toElement() ) { + if ( n2.tagName() == "function" ) { + QString fname; + if( !n2.attribute("name").isNull() ) + { + fname = n2.attribute( "name" ); + fname = Parser::cleanArgs( fname ); + functionImpls.insert( fname, n2.firstChild().toText().data() ); + } + else + { + fname = n2.text(); + fname = Parser::cleanArgs( fname ); + } + extraFunctions += fname; + } + } + } else if ( n.tagName() == "customwidgets" ) { + QDomElement n2 = n.firstChild().toElement(); + while ( !n2.isNull() ) { + if ( n2.tagName() == "customwidget" ) { + QDomElement n3 = n2.firstChild().toElement(); + QString cl, header; + WidgetDatabaseRecord *r = new WidgetDatabaseRecord; + while ( !n3.isNull() ) { + if ( n3.tagName() == "class" ) { + cl = n3.firstChild().toText().data(); + r->name = cl; + } else if ( n3.tagName() == "header" ) { + CustomInclude ci; + ci.header = n3.firstChild().toText().data(); + ci.location = n3.attribute( "location", "global" ); + r->includeFile = ci.header; + header = ci.header; + customWidgetIncludes.insert( cl, ci ); + } + WidgetDatabase::append( r ); + n3 = n3.nextSibling().toElement(); + } + + if (cl.isEmpty()) + cl = "UnnamedCustomClass"; + + int ext = header.findRev('.'); + + if (ext >= 0) + header.truncate(ext); + + if (header.isEmpty()) + header = cl.lower(); + +// if (!nofwd) +// out << "use " << cl << ";" << endl; // FIXME: what about header ? + } + n2 = n2.nextSibling().toElement(); + } + } + } + + out << "class " << nameOfClass << " < " << objClass << endl << endl; + + // QtRuby sig/slot declaration + + ++indent; + + if ( !extraSlots.isEmpty() ) { + out << indent << "slots 'languageChange()'"; + for ( it = extraSlots.begin(); it != extraSlots.end(); ++it ) { + if (it == extraSlots.begin()) { + out << "," << endl; + } + rubySlot( it ); + out << ( ((*it) == extraSlots.last()) ? "":",") << endl; + } + out << endl; + } + + // create signals + if ( !extraSignals.isEmpty() ) { + out << indent << "signals "; + --indent; + for ( it = extraSignals.begin(); it != extraSignals.end(); ++it ) { + rubySlot( it ); + if (it == extraSignals.begin()) { + ++indent; + } + out << ( ((*it) == extraSignals.last()) ? "":",") << endl; + } + out << endl; + } + + + // children + if( !objectNames.isEmpty() ) + qWarning("WARNING : objectNames should be empty at "__FILE__" line %d\n", __LINE__); + nl = e.parentNode().toElement().elementsByTagName( "widget" ); + for ( i = 1; i < (int) nl.length(); i++ ) + { // start at 1, 0 is the toplevel widget + n = nl.item(i).toElement(); + createAttrDecl( n ); + } + objectNames.clear(); + + ++indent; + + // additional attributes (from Designer) + QStringList publicVars, protectedVars, privateVars; + nl = e.parentNode().toElement().elementsByTagName( "variable" ); + for ( i = 0; i < (int)nl.length(); i++ ) { + n = nl.item( i ).toElement(); + // Because of compatibility the next lines have to be commented out. + // Someday it should be uncommented. + //if ( n.parentNode().toElement().tagName() != "variables" ) + // continue; + QString access = n.attribute( "access", "protected" ); + QString var = n.firstChild().toText().data().stripWhiteSpace(); + if ( var.endsWith( ";" ) ) + var.truncate(var.length() - 1); + if ( access == "public" ) + publicVars += var; + else if ( access == "private" ) + privateVars += var; + else + protectedVars += var; + } + + // Databases Connection holders + + registerDatabases( e ); + dbConnections = unique( dbConnections ); + for ( it = dbConnections.begin(); it != dbConnections.end(); ++it ) { + if ( !(*it).isEmpty() && (*it) != "(default)") { + out << indent << (*it) << "Connection" << endl; + } + } + + --indent; + + // additional includes (local or global ) and forward declaractions + nl = e.parentNode().toElement().elementsByTagName( "include" ); + for ( i = 0; i < (int) nl.length(); i++ ) { + QDomElement n2 = nl.item(i).toElement(); + QString s = n2.firstChild().toText().data(); + if ( n2.attribute( "location" ) != "local" ) { + if ( s.right( 5 ) == ".ui.h" && !QFile::exists( s ) ) + continue; + if ( n2.attribute( "impldecl", "in implementation" ) != "in implementation" ) + continue; + globalIncludes += s; + } + } + + // do the local includes afterwards, since global includes have priority on clashes + for ( i = 0; i < (int) nl.length(); i++ ) { + QDomElement n2 = nl.item(i).toElement(); + QString s = n2.firstChild().toText().data(); + if ( n2.attribute( "location" ) == "local" &&!globalIncludes.contains( s ) ) { + if ( s.right( 5 ) == ".ui.h" && !QFile::exists( s ) ) + continue; + if ( n2.attribute( "impldecl", "in implementation" ) != "in implementation" ) + continue; + localIncludes += s; + } + } + + // additional custom widget headers + nl = e.parentNode().toElement().elementsByTagName( "header" ); + for ( i = 0; i < (int) nl.length(); i++ ) { + QDomElement n2 = nl.item(i).toElement(); + QString s = n2.firstChild().toText().data(); + if ( n2.attribute( "location" ) != "local" ) + globalIncludes += s; + else + localIncludes += s; + } + + + // grab slots/funcs defined in ui.h files + for(QStringList::Iterator it = localIncludes.begin(); it != localIncludes.end(); ++it) + { + if((*it).right( 5 ) == ".ui.h") + { + QFile f((*it)); + if( f.open( IO_ReadOnly ) ) + { + QRegExp re("^def\\s+([a-zA-Z0-9_]+)\\s.*$"); + QRegExp re2("^end\\s*$"); + QTextStream t( &f ); + QString s, s2, s3; + while ( !t.eof() ) + { + s = t.readLine(); + int pos = re.search(s); + if(pos == -1) + continue; + s2 = re.cap(1); + s2 += "()"; +// s2 = Parser::cleanArgs(s2); + s3 = "{"; + while( !t.eof() ) + { + s = t.readLine(); + if(re2.search(s) != -1) + break; + s3 += s + "\n"; + } + s3 += "}"; + functionImpls.insert( s2, s3 ); + if( t.eof() ) break; + } + f.close(); + } + } + } + + // includes for child widgets + for ( it = tags.begin(); it != tags.end(); ++it ) { + nl = e.parentNode().toElement().elementsByTagName( *it ); + for ( i = 1; i < (int) nl.length(); i++ ) { // start at 1, 0 is the toplevel widget + QString name = getClassName( nl.item(i).toElement() ); + if ( name == "Spacer" ) { + globalIncludes += "qlayout.h"; + globalIncludes += "qapplication.h"; + continue; + } + if ( name.mid( 4 ) == "ListView" ) + globalIncludes += "qheader.h"; + if ( name != objClass ) { + int wid = WidgetDatabase::idFromClassName( name.replace( QRegExp("^Qt::"), "Q" ) ); + QMap<QString, CustomInclude>::Iterator it = customWidgetIncludes.find( name ); + if ( it == customWidgetIncludes.end() ) + globalIncludes += WidgetDatabase::includeFile( wid ); + } + } + } + + dbConnections = unique( dbConnections ); + if ( dbConnections.count() ) + sqlClasses += "Qt::SqlDatabase"; + if ( dbCursors.count() ) + sqlClasses += "Qt::SqlCursor"; + bool dbForm = false; + if ( dbForms[ "(default)" ].count() ) + dbForm = true; + bool subDbForms = false; + for ( it = dbConnections.begin(); it != dbConnections.end(); ++it ) { + if ( !(*it).isEmpty() && (*it) != "(default)" ) { + if ( dbForms[ (*it) ].count() ) { + subDbForms = true; + break; + } + } + } + if ( dbForm || subDbForms ) { + sqlClasses += "Qt::SqlForm"; + sqlClasses += "Qt::SqlRecord"; + } + + if (globalIncludes.findIndex("qdatatable.h") >= 0) + sqlClasses += "Qt::DataTable"; + + if (globalIncludes.findIndex("qtableview.h") >= 0) + sqlClasses += "Qt::TableView"; + + if (globalIncludes.findIndex("qdatabrowser.h") >= 0) + sqlClasses += "Qt::DataBrowser"; + + out << endl; + + // find out what images are required + QStringList requiredImages; + static const char *imgTags[] = { "pixmap", "iconset", 0 }; + for ( i = 0; imgTags[i] != 0; i++ ) { + nl = e.parentNode().toElement().elementsByTagName( imgTags[i] ); + for ( int j = 0; j < (int) nl.length(); j++ ) { + QString img = "@"; + requiredImages += (img + nl.item(j).firstChild().toText().data()); + } + } + + // register the object and unify its name + QString loadFunction(objName); + objName = registerObject( objName ); + + QStringList images; + QStringList xpmImages; + if ( pixmapLoaderFunction.isEmpty() && !externPixmaps ) + { + // create images + for ( n = e; !n.isNull(); n = n.nextSibling().toElement() ) + { + if ( n.tagName() == "images" ) + { + nl = n.elementsByTagName( "image" ); + for ( i = 0; i < (int) nl.length(); i++ ) + { + QString img = registerObject( nl.item(i).toElement().attribute( "name" ) ); + if ( !requiredImages.contains( img ) ) + continue; + QDomElement tmp = nl.item(i).firstChild().toElement(); + if ( tmp.tagName() != "data" ) + continue; + QString format = tmp.attribute("format", "PNG" ); + QString data = tmp.firstChild().toText().data(); + if ( format == "XPM.GZ" ) + { + xpmImages += img; + ulong length = tmp.attribute("length").toULong(); + QByteArray baunzip = unzipXPM( data, length ); + // shouldn't we test the initial `length' against the + // resulting `length' to catch corrupt UIC files? + int a = 0; + out << indent << imageDataName(img) << " =\n["; + + while ( baunzip[a] != '\"' ) + a++; + for ( ; a < (int) length; a++ ) + { + char ch; + + if ((ch = baunzip[a]) == '}') + { + out << "]\n" << endl; + + break; + } + + out << ch; + } + } + else + { + images += img; + out << indent << imageDataName(img) << " = [ " << endl; + ++indent; + int a ; + for ( a = 0; a < (int) (data.length()/2)-1; a++ ) { + out << "0x" << QString(data[2*a]) << QString(data[2*a+1]) << ","; + if ( a % 12 == 11 ) + out << endl << " "; + else + out << " "; + } + out << "0x" << QString(data[2*a]) << QString(data[2*a+1]) << " ].pack \"C*\"" << endl; + --indent; + out << endl; + } + } + } + } + out << endl; + } + else if ( externPixmaps ) + { + /* + out << indent << "def uic_load_pixmap_" << loadFunction << "( data )" << endl; + ++indent; + out << indent << "pix = Qt::Pixmap.new()" << endl; + out << indent << "m = Qt::MimeSourceFactory.defaultFactory().data(data)" << endl; + out << endl; + out << indent << "if ! m.nil?" << endl; + ++indent; + out << indent << "Qt::ImageDrag.decode(m, pix)" << endl; + --indent; + out << indent << "end" << endl; + out << endl; + out << indent << "return pix" << endl; + --indent; + out << indent << "end" << endl; + out << endl; + out << endl; + pixmapLoaderFunction = "uic_load_pixmap_" + loadFunction; + */ + pixmapLoaderFunction = "Qt::Pixmap.fromMimeSource"; + } + + + // constructor(s) + if ( objClass == "Qt::Dialog" || objClass == "Qt::Wizard" ) { + out << indent << "def initialize(parent = nil, name = nil, modal = false, fl = 0)" << endl; + ++indent; + out << indent << "super" << endl; + } else if ( objClass == "Qt::Widget") { + out << indent << "def initialize(parent = nil, name = nil, fl = 0)" << endl; + ++indent; + out << indent << "super" << endl; + } else if ( objClass == "Qt::MainWindow" ) { + out << indent << "def initialize(parent = nil, name = nil, fl = WType_TopLevel)" << endl; + ++indent; + out << indent << "super" << endl; + isMainWindow = true; + } else { + out << indent << "def initialize(parent = nil, name = nil)" << endl; + ++indent; + out << indent << "super" << endl; + } + + out << endl; + + // create pixmaps for all images + if ( !images.isEmpty() ) { + QStringList::Iterator it; + for ( it = images.begin(); it != images.end(); ++it ) { + out << indent << (*it) << " = Qt::Pixmap.new()" << endl; + out << indent << (*it) << ".loadFromData(" << imageDataName(*it) << ", " << imageDataName(*it) << ".length, \"PNG\")" << endl; + } + out << endl; + } + // create pixmaps for all images + if ( !xpmImages.isEmpty() ) { + for ( it = xpmImages.begin(); it != xpmImages.end(); ++it ) { + out << indent << (*it) << " = Qt::Pixmap.new(" << imageDataName(*it) << ")" << endl; + } + out << endl; + } + + if ( isMainWindow ) + out << indent << "statusBar()" << endl; + + // set the properties + QSize geometry( 0, 0 ); + + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "property" ) { + bool stdset = stdsetdef; + if ( n.hasAttribute( "stdset" ) ) + stdset = toBool( n.attribute( "stdset" ) ); + QString prop = n.attribute("name"); + QDomElement n2 = n.firstChild().toElement(); + QString value = setObjectProperty( objClass, QString::null, prop, n2, stdset ); + if ( value.isEmpty() ) + continue; + + if ( prop == "geometry" && n2.tagName() == "rect") { + QDomElement n3 = n2.firstChild().toElement(); + while ( !n3.isNull() ) { + if ( n3.tagName() == "width" ) + geometry.setWidth( n3.firstChild().toText().data().toInt() ); + else if ( n3.tagName() == "height" ) + geometry.setHeight( n3.firstChild().toText().data().toInt() ); + n3 = n3.nextSibling().toElement(); + } + } else { + QString call; + if ( stdset ) { + call = mkStdSet( prop ) + "(" + value + ")"; + } else { + call = "setProperty(\"" + prop + "\", Qt::Variant.new(" + value + "))"; + } + + if ( n2.tagName() == "string" ) { + trout << indent << call << endl; + } else if ( prop == "name" ) { + out << indent << "if name.nil?" << endl; + out << indent << "\t" << call << endl; + out << indent << "end" << endl; + } else { + out << indent << call << endl; + } + } + } + } + + out << endl; + + // create all children, some forms have special requirements + + if ( objClass == "Qt::Wizard" ) + { + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) + { + if ( tags.contains( n.tagName() ) ) + { + QString page = createObjectImpl( n, objClass, "self" ); + QString comment; + QString label = DomTool::readAttribute( n, "title", "", comment ).toString(); + out << indent << "addPage(" << page << ", "<< trcall( label ) << ")" << endl; + trout << indent << "setTitle( " << page << ", " << trcall( label, comment ) << " )" << endl; + QVariant def( false, 0 ); + if ( DomTool::hasAttribute( n, "backEnabled" ) ) + out << indent << "setBackEnabled(" << page << "," << mkBool( DomTool::readAttribute( n, "backEnabled", def).toBool() ) << ");" << endl; + if ( DomTool::hasAttribute( n, "nextEnabled" ) ) + out << indent << "setNextEnabled(" << page << "," << mkBool( DomTool::readAttribute( n, "nextEnabled", def).toBool() ) << ");" << endl; + if ( DomTool::hasAttribute( n, "finishEnabled" ) ) + out << indent << "setFinishEnabled(" << page << "," << mkBool( DomTool::readAttribute( n, "finishEnabled", def).toBool() ) << ");" << endl; + if ( DomTool::hasAttribute( n, "helpEnabled" ) ) + out << indent << "setHelpEnabled(" << page << "," << mkBool( DomTool::readAttribute( n, "helpEnabled", def).toBool() ) << ");" << endl; + if ( DomTool::hasAttribute( n, "finish" ) ) + out << indent << "setFinish( " << page << "," << mkBool( DomTool::readAttribute( n, "finish", def).toBool() ) << ");" << endl; + } + } + } + else + { // standard widgets + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) + { + if ( tags.contains( n.tagName() ) ) + createObjectImpl( n, objName, "self" ); + } + } + + // database support + dbConnections = unique( dbConnections ); + if ( dbConnections.count() ) + out << endl; + for ( it = dbConnections.begin(); it != dbConnections.end(); ++it ) { + if ( !(*it).isEmpty() && (*it) != "(default)") { + out << indent << (*it) << "Connection = Qt::SqlDatabase.database(\"" <<(*it) << "\");" << endl; + } + } + + nl = e.parentNode().toElement().elementsByTagName( "widget" ); + for ( i = 1; i < (int) nl.length(); i++ ) { // start at 1, 0 is the toplevel widget + n = nl.item(i).toElement(); + QString s = getClassName( n ); + if ( s == "Qt::DataBrowser" || s == "Qt::DataView" ) { + QString objName = getObjectName( n ); + QString tab = getDatabaseInfo( n, "table" ); + QString con = getDatabaseInfo( n, "connection" ); + out << indent << objName << "Form = Qt::SqlForm.new(self, \"" << objName << "Form\")" << endl; + QDomElement n2; + for ( n2 = n.firstChild().toElement(); !n2.isNull(); n2 = n2.nextSibling().toElement() ) + createFormImpl( n2, objName, con, tab ); + out << indent << objName << ".setForm(" << objName << "Form)" << endl; + } + } + + // actions, toolbars, menubar + bool needEndl = false; + for ( n = e; !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "actions" ) { + if ( !needEndl ) + out << endl; + createActionImpl( n.firstChild().toElement(), "self" ); + needEndl = true; + } + } + if ( needEndl ) + out << endl; + needEndl = false; + for ( n = e; !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "toolbars" ) { + if ( !needEndl ) + out << endl; + createToolbarImpl( n, objClass, objName ); + needEndl = true; + } + } + if ( needEndl ) + out << endl; + needEndl = false; + for ( n = e; !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "menubar" ) { + if ( !needEndl ) + out << endl; + createMenuBarImpl( n, objClass, objName ); + needEndl = true; + } + } + if ( needEndl ) + out << endl; + + out << indent << "languageChange()" << endl; + + // take minimumSizeHint() into account, for height-for-width widgets + if ( !geometry.isNull() ) { + out << indent << "resize( Qt::Size.new(" << geometry.width() << ", " + << geometry.height() << ").expandedTo(minimumSizeHint()) )" << endl; + out << indent << "clearWState( WState_Polished )" << endl; + } + + for ( n = e; !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "connections" ) { + // setup signals and slots connections + out << endl; + nl = n.elementsByTagName( "connection" ); + for ( i = 0; i < (int) nl.length(); i++ ) { + QString sender, receiver, signal, slot; + for ( QDomElement n2 = nl.item(i).firstChild().toElement(); !n2.isNull(); n2 = n2.nextSibling().toElement() ) { + if ( n2.tagName() == "sender" ) + sender = n2.firstChild().toText().data(); + else if ( n2.tagName() == "receiver" ) + receiver = n2.firstChild().toText().data(); + else if ( n2.tagName() == "signal" ) + signal = n2.firstChild().toText().data(); + else if ( n2.tagName() == "slot" ) + slot = n2.firstChild().toText().data(); + } + if ( sender.isEmpty() || receiver.isEmpty() || signal.isEmpty() || slot.isEmpty() ) + continue; + else if ( sender[0] == '<' || + receiver[0] == '<' || + signal[0] == '<' || + slot[0] == '<' ) + continue; + sender = registeredName( sender ); + receiver = registeredName( receiver ); + + if ( sender == objName ) + sender = "self"; + if ( receiver == objName ) + receiver = "self"; + + out << indent << "Qt::Object.connect(" << sender + << ", SIGNAL(\"" << signal << "\"), "<< receiver << ", SLOT(\"" << slot << "\") )" << endl; + } + } else if ( n.tagName() == "tabstops" ) { + // setup tab order + out << endl; + QString lastName; + QDomElement n2 = n.firstChild().toElement(); + while ( !n2.isNull() ) { + if ( n2.tagName() == "tabstop" ) { + QString name = n2.firstChild().toText().data(); + name = registeredName( name ); + if ( !lastName.isEmpty() ) + out << indent << "setTabOrder(" << lastName << ", " << name << ")" << endl; + lastName = name; + } + n2 = n2.nextSibling().toElement(); + } + } + } + +// QtRuby - FIXME: what the heck is this ? + // buddies + bool firstBuddy = true; + for ( QValueList<Buddy>::Iterator buddy = buddies.begin(); buddy != buddies.end(); ++buddy ) { + if ( isObjectRegistered( (*buddy).buddy ) ) { + if ( firstBuddy ) { + out << endl; + } + out << indent << (*buddy).key << ".setBuddy(" << registeredName( (*buddy).buddy ) << ")" << endl; + firstBuddy = false; + } + + } + if ( extraSlots.find( "init()" ) != extraSlots.end() || + extraFunctions.find( "init()" ) != extraFunctions.end()) + out << endl << indent << "init()" << endl; + + // end of constructor + --indent; + out << indent << "end" << endl; + out << endl; + + + + // handle application events if required + bool needFontEventHandler = false; + bool needSqlTableEventHandler = false; + bool needSqlDataBrowserEventHandler = false; + nl = e.elementsByTagName( "widget" ); + for ( i = 0; i < (int) nl.length(); i++ ) { + if ( !DomTool::propertiesOfType( nl.item(i).toElement() , "font" ).isEmpty() ) + needFontEventHandler = true; + QString s = getClassName( nl.item(i).toElement() ); + if ( s == "Qt::DataTable" || s == "Qt::DataBrowser" ) { + if ( !isFrameworkCodeGenerated( nl.item(i).toElement() ) ) + continue; + if ( s == "Qt::DataTable" ) + needSqlTableEventHandler = true; + if ( s == "Qt::DataBrowser" ) + needSqlDataBrowserEventHandler = true; + } + if ( needFontEventHandler && needSqlTableEventHandler && needSqlDataBrowserEventHandler ) + break; + } + +// PerlQt - TODO: is this needed ? +// Seems not.. let's ifzero for now... + + if ( 0 && needFontEventHandler) { + // indent = "\t"; // increase indentation for if-clause below + out << endl; + out << "# Main event handler. Reimplemented to handle" << endl; + out << "# application font changes" << endl; + out << endl; + out << "def event( ev )" << endl; + out << " ret = super( ev ) " << endl; + if ( needFontEventHandler ) { + ++indent; + out << " if ev.type() == Qt::Event::ApplicationFontChange " << endl; + for ( i = 0; i < (int) nl.length(); i++ ) { + n = nl.item(i).toElement(); + QStringList list = DomTool::propertiesOfType( n, "font" ); + for ( it = list.begin(); it != list.end(); ++it ) + createExclusiveProperty( n, *it ); + } + out << " end" << endl; + --indent; + } + out << "end" << endl; + out << endl; + } + + if ( needSqlTableEventHandler || needSqlDataBrowserEventHandler ) { + out << endl; + out << indent << "# Widget polish. Reimplemented to handle default data" << endl; + if ( needSqlTableEventHandler ) + out << indent << "# table initialization." << endl; + if ( needSqlDataBrowserEventHandler ) + out << indent << "# browser initialization." << endl; + out << indent << "def polish" << endl; + ++indent; + if ( needSqlTableEventHandler ) { + for ( i = 0; i < (int) nl.length(); i++ ) { + QString s = getClassName( nl.item(i).toElement() ); + if ( s == "Qt::DataTable" ) { + n = nl.item(i).toElement(); + QString c = QString("@") + getObjectName( n ); + QString conn = getDatabaseInfo( n, "connection" ); + QString tab = getDatabaseInfo( n, "table" ); + if ( !( conn.isEmpty() || tab.isEmpty() ) ) { + out << indent << "if " << "!" << c << ".nil?" << endl; + ++indent; + out << indent << "cursor = " << c << ".sqlCursor()" << endl; + out << endl; + out << indent << "if !cursor.nil?" << endl; + ++indent; + if ( conn == "(default)" ) + out << indent << "cursor = Qt::SqlCursor.new(\"" << tab << "\")" << endl; + else + out << indent << "cursor = Qt::SqlCursor.new(\"" << tab << "\", true, " << conn << "Connection)" << endl; + out << indent << "if " << c << ".readOnly? " << endl; + ++indent; + out << indent << "cursor.mode = Qt::SqlCursor::ReadOnly" << endl; + --indent; + out << indent << "end " << endl; + out << indent << c << ".setSqlCursor(cursor, false, true)" << endl; + --indent; + out << endl; + out << indent << "end" << endl; + out << indent << "if !cursor.active?" << endl; + ++indent; + out << indent << c << ".refresh(Qt::DataTable::RefreshAll)" << endl; + --indent; + out << indent << "end" << endl; + --indent; + out << indent << "end" << endl; + } + } + } + } + if ( needSqlDataBrowserEventHandler ) { + nl = e.elementsByTagName( "widget" ); + for ( i = 0; i < (int) nl.length(); i++ ) { + QString s = getClassName( nl.item(i).toElement() ); + if ( s == "Qt::DataBrowser" ) { + QString obj = getObjectName( nl.item(i).toElement() ); + QString tab = getDatabaseInfo( nl.item(i).toElement(), + "table" ); + QString conn = getDatabaseInfo( nl.item(i).toElement(), + "connection" ); + if ( !(tab).isEmpty() ) { + out << indent << "if " << obj << endl; + ++indent; + out << indent << "if !" << obj << ".sqlCursor()" << endl; + ++indent; + if ( conn == "(default)" ) + out << indent << "cursor = Qt::SqlCursor.new(\"" << tab << "\")" << endl; + else + out << indent << "cursor = Qt::SqlCursor.new(\"" << tab << "\", true, " << conn << "Connection)" << endl; + out << indent << obj << ".setSqlCursor(cursor, true)" << endl; + out << indent << obj << ".refresh()" << endl; + out << indent << obj << ".first()" << endl; + --indent; + out << indent << "end" << endl; + --indent; + out << indent << "end" << endl; + } + } + } + } + out << indent << "super()" << endl; + --indent; + out << indent << "end" << endl; + } + + out << indent << "#" << endl; + out << indent << "# Sets the strings of the subwidgets using the current" << endl; + out << indent << "# language." << endl; + out << indent << "#" << endl; + out << indent << "def " << "languageChange()" << endl; + out << languageChangeBody; + out << indent << "end" << endl; + out << indent << "protected :languageChange" << endl; + out << endl; + + if ( !extraSlots.isEmpty() && writeSlotImpl ) { + for ( it = extraSlots.begin(); it != extraSlots.end(); ++it ) { + out << endl; + int astart = (*it).find('('); + out << indent << "def " << (*it).left(astart) << "(*k)" << endl; + bool createWarning = true; + QString fname = Parser::cleanArgs( *it ); + QMap<QString, QString>::Iterator fit = functionImpls.find( fname ); + if ( fit != functionImpls.end() ) { + int begin = (*fit).find( "{" ); + QString body = (*fit).mid( begin + 1, (*fit).findRev( "}" ) - begin - 1 ); + createWarning = body.simplifyWhiteSpace().isEmpty(); + if ( !createWarning ) + out << body << endl; + } + if ( createWarning ) { + ++indent; + if ( *it != "init()" && *it != "destroy()" ) + out << indent << "print(\"" << nameOfClass << "." << (*it) << ": Not implemented yet.\\n\")" << endl; + --indent; + } + out << indent << "end" << endl; + + } + } + + if ( !extraFunctions.isEmpty() ) { + for ( it = extraFunctions.begin(); it != extraFunctions.end(); ++it ) { + out << endl; + int astart = (*it).find('('); + out << indent << "def " << (*it).left(astart) << "(*k)" << endl; + QString fname = Parser::cleanArgs( *it ); + QMap<QString, QString>::Iterator fit = functionImpls.find( fname ); + if ( fit != functionImpls.end() ) { + int begin = (*fit).find( "{" ); + QString body = (*fit).mid( begin + 1, (*fit).findRev( "}" ) - begin - 1 ); + body.simplifyWhiteSpace().isEmpty(); + out << body << endl; + } + out << indent << "end" << endl; + + } + } + + + out << endl; + out << "end" << endl; +} + + +/*! Creates form support implementation code for the widgets given + in \a e. + + Traverses recursively over all children. + */ + +void Uic::createFormImpl( const QDomElement& e, const QString& form, const QString& connection, const QString& table ) +{ + if ( e.tagName() == "widget" && + e.attribute( "class" ) != "Qt::DataTable" ) { + QString field = getDatabaseInfo( e, "field" ); + if ( !field.isEmpty() ) { + if ( isWidgetInTable( e, connection, table ) ) + out << indent << form << "Form.insert(" << getObjectName( e ) << ", " << fixString( field ) << ")" << endl; + } + } + QDomElement n; + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + createFormImpl( n, form, connection, table ); + } +} + + +// Generate a QtRuby signal/slot definition. + +void Uic::rubySlot(QStringList::Iterator &it) +{ + out << indent << "'" << (*it) << "'"; +} diff --git a/qtruby/rubylib/designer/rbuic/globaldefs.h b/qtruby/rubylib/designer/rbuic/globaldefs.h new file mode 100644 index 00000000..cc6ed400 --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/globaldefs.h @@ -0,0 +1,45 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef GLOBALDEFS_H +#define GLOBALDEFS_H + +#include <qcolor.h> + +#define BOXLAYOUT_DEFAULT_MARGIN 11 +#define BOXLAYOUT_DEFAULT_SPACING 6 + +#ifndef NO_STATIC_COLORS +static QColor *backColor1 = 0; +static QColor *backColor2 = 0; +static QColor *selectedBack = 0; + +static void init_colors() +{ + if ( backColor1 ) + return; + backColor1 = new QColor( 236, 245, 255 ); + backColor2 = new QColor( 250, 250, 250 ); + selectedBack = new QColor( 221, 221, 221 ); +} + +#endif + +#endif diff --git a/qtruby/rubylib/designer/rbuic/main.cpp b/qtruby/rubylib/designer/rbuic/main.cpp new file mode 100644 index 00000000..a80d8785 --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/main.cpp @@ -0,0 +1,293 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** Copyright (c) 2001 Phil Thompson <phil@river-bank.demon.co.uk> +** Copyright (c) 2002 Germain Garand <germain@ebooksfrance.com> +** +** This file is part of Qt Designer. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ +/* +** 06/2002 : Initial release of puic, the PerlQt User Interface Compiler, +** a work derivated from uic (the Qt User Interface Compiler) +** and pyuic (the PyQt User Interface Compiler). +** +** G.Garand +** +** 08/2003 : Initial release of rbuic, the QtRuby User Interface Compiler, +** a work derived from the PerlQt puic. +** +** Richard Dale +** +**********************************************************************/ +#include "uic.h" +#include "parser.h" +#include "widgetdatabase.h" +#include "domtool.h" +#include <qapplication.h> +#include <qfile.h> +#include <qstringlist.h> +#include <qdatetime.h> +#define NO_STATIC_COLORS +#include <globaldefs.h> +#include <qregexp.h> +#include <stdio.h> +#include <stdlib.h> +#include <zlib.h> +#define RBUIC_VERSION "0.9" + +void getDBConnections( Uic& uic, QString& s); + +int main( int argc, char * argv[] ) +{ + RubyIndent indent; + bool execCode = false; + bool subcl = false; + bool imagecollection = false; + QStringList images; + const char *error = 0; + const char* fileName = 0; + const char* className = 0; + const char* outputFile = 0; + const char* projectName = 0; + const char* trmacro = 0; + bool nofwd = false; + bool useKDE = false; + bool fix = false; + QApplication app(argc, argv, false); + QString uicClass; + + + for ( int n = 1; n < argc && error == 0; n++ ) { + QCString arg = argv[n]; + if ( arg[0] == '-' ) { // option + QCString opt = &arg[1]; + if ( opt[0] == 'o' ) { // output redirection + if ( opt[1] == '\0' ) { + if ( !(n < argc-1) ) { + error = "Missing output-file name"; + break; + } + outputFile = argv[++n]; + } else + outputFile = &opt[1]; + } else if ( opt[0] == 'e' || opt == "embed" ) { + imagecollection = true; + if ( opt == "embed" || opt[1] == '\0' ) { + if ( !(n < argc-1) ) { + error = "Missing name of project."; + break; + } + projectName = argv[++n]; + } else + projectName = &opt[1]; + } else if ( opt == "nofwd" ) { + nofwd = true; + } else if ( opt == "kde" ) { + useKDE = true; + } else if ( opt == "subimpl" ) { + subcl = true; + if ( !(n < argc-1) ) { + error = "Missing class name."; + break; + } + className = argv[++n]; + } else if ( opt == "tr" ) { + if ( opt == "tr" || opt[1] == '\0' ) { + if ( !(n < argc-1) ) { + error = "Missing tr macro."; + break; + } + trmacro = argv[++n]; + } else { + trmacro = &opt[1]; + } + } else if ( opt == "version" ) { + fprintf( stderr, + "QtRuby User Interface Compiler v%s for Qt version %s\n", RBUIC_VERSION, + QT_VERSION_STR ); + exit( 1 ); + } else if ( opt == "help" ) { + break; + } else if ( opt == "fix" ) { + fix = true; + } else if ( opt[0] == 'p' ) { + uint tabstop; + bool ok; + + if ( opt[1] == '\0' ) { + if ( !(n < argc-1) ) { + error = "Missing indent"; + break; + } + tabstop = QCString(argv[++n]).toUInt(&ok); + } else + tabstop = opt.mid(1).toUInt(&ok); + + if (ok) + indent.setTabStop(tabstop); + else + error = "Invalid indent"; + } else if ( opt == "x" ) { + execCode = true; + } else { + error = "Unrecognized option"; + } + } else { + if ( imagecollection ) + images << argv[n]; + else if ( fileName ) // can handle only one file + error = "Too many input files specified"; + else + fileName = argv[n]; + } + } + + if ( argc < 2 || error || (!fileName && !imagecollection ) ) { + fprintf( stderr, "QtRuby user interface compiler.\n" ); + if ( error ) + fprintf( stderr, "rbuic: %s\n", error ); + + fprintf( stderr, "Usage: %s [options] [mode] <uifile>\n" + "\nGenerate implementation:\n" + " %s [options] <uifile>\n" + "Generate image collection:\n" + " %s [options] -embed <project> <image1> <image2> <image3> ...\n" + "\t<project>\tproject name\n" + "\t<image[0..n]>\timage files\n" + "Generate subclass implementation:\n" + " %s [options] -subimpl <classname> <uifile>\n" + "\t<classname>\tname of the subclass to generate\n" + "Options:\n" + "\t-o file\t\tWrite output to file rather than stdout\n" + "\t-p indent\tSet the indent in spaces (0 to use a tab)\n" + "\t-nofwd\t\tOmit imports of custom widgets\n" + "\t-kde\t\tUse kde widgets, require 'Korundum' extension\n" + "\t-tr func\tUse func(...) rather than trUtf8(...) for i18n\n" + "\t-x\t\tGenerate extra code to test the class\n" + "\t-version\tDisplay version of rbuic\n" + "\t-help\t\tDisplay this information\n" + , argv[0], argv[0], argv[0], argv[0]); + exit( 1 ); + } + + Uic::setIndent(indent); + + QFile fileOut; + if ( outputFile ) { + fileOut.setName( outputFile ); + if (!fileOut.open( IO_WriteOnly ) ) + qFatal( "rbuic: Could not open output file '%s'", outputFile ); + } else { + fileOut.open( IO_WriteOnly, stdout ); + } + QTextStream out( &fileOut ); + + if ( imagecollection ) { + out.setEncoding( QTextStream::Latin1 ); + Uic::embed( out, projectName, images ); + return 0; + } + + + out.setEncoding( QTextStream::UnicodeUTF8 ); + QFile file( fileName ); + if ( !file.open( IO_ReadOnly ) ) + qFatal( "rbuic: Could not open file '%s' ", fileName ); + + QDomDocument doc; + QString errMsg; + int errLine; + if ( !doc.setContent( &file, &errMsg, &errLine ) ) + qFatal( QString("rbuic: Failed to parse %s: ") + errMsg + QString (" in line %d\n"), fileName, errLine ); + + DomTool::fixDocument( doc ); + + if ( fix ) { + out << doc.toString(); + return 0; + } + + if ( !subcl ) { + out << "# Form implementation generated from reading ui file '" << fileName << "'" << endl; + out << "#" << endl; + out << "# Created: " << QDateTime::currentDateTime().toString() << endl; + out << "# by: The QtRuby User Interface Compiler (rbuic)" << endl; + out << "#" << endl; + out << "# WARNING! All changes made in this file will be lost!" << endl; + out << endl; + } + out << endl; + + Uic uic( fileName, out, doc, subcl, trmacro ? trmacro : "trUtf8", className, nofwd, uicClass, useKDE ); + + if (execCode) { + out << endl; + out << indent << "if $0 == __FILE__" << endl; + ++indent; + if (uic.hasKDEwidget) { + out << indent << "about = KDE::AboutData.new(\"" << uicClass.lower() << "\", \"" << uicClass << "\", \"0.1\")" << endl; + out << indent << "KDE::CmdLineArgs.init(ARGV, about)" << endl; + out << indent << "a = KDE::Application.new" << endl; + } else { + out << indent << "a = Qt::Application.new(ARGV)" << endl; + } + QString s; + getDBConnections( uic, s); + out << s; + out << indent << "w = " << (subcl? QString::fromLatin1(className) : uicClass) << ".new" << endl; + out << indent << "a.mainWidget = w" << endl; + out << indent << "w.show" << endl; + out << indent << "a.exec" << endl; + --indent; + out << indent << "end" << endl; + } + + return 0; +} + +void getDBConnections( Uic& uic, QString& s) +{ + int num = 0; + for ( QStringList::Iterator it = uic.dbConnections.begin(); it != uic.dbConnections.end(); ++it ) { + if ( !(*it).isEmpty()) { + QString inc = (num ? QString::number(num+1) : QString::null); + s += "\n # Connection to database " + (*it) + "\n\n"; + s += " DRIVER" + inc + " =\t\t'QMYSQL3'" + (inc?"":" # appropriate driver") + "\n"; + s += " DATABASE" + inc + " =\t\t'foo'" + (inc?"":" # name of your database") + "\n"; + s += " USER" + inc + "=\t\t'john'" + (inc?"":" # username") + "\n"; + s += " PASSWORD" + inc + "=\t\t'ZxjGG34s'" + (inc?"":" # password for USER") + "\n"; + s += " HOST" + inc + "=\t\t'localhost'" + (inc?"":" # host on which the database is running") + "\n"; + s += "\n"; + s += " $db" + inc + " = Qt::SqlDatabase.addDatabase( DRIVER" + inc; + if (inc) + s+= ", '" + (*it) + "'"; + s += " )\n"; + s += " $db" + inc + ".databaseName = DATABASE" + inc + "\n"; + s += " $db" + inc + ".userName = USER" + inc + "\n"; + s += " $db" + inc + ".password = PASSWORD" + inc + "\n"; + s += " $db" + inc + ".hostName = HOST" + inc + "\n"; + s += "\n"; + s += " if !$db" + inc + ".open\n"; + s += " Qt::MessageBox.information( nil, 'Unable to open database',\n"; + s += " $db" + inc + ".lastError().databaseText() + \"\\n\")\n"; + s += " exit 1\n"; + s += " end\n"; + s += "\n"; + num++; + } + } +} + diff --git a/qtruby/rubylib/designer/rbuic/object.cpp b/qtruby/rubylib/designer/rbuic/object.cpp new file mode 100644 index 00000000..ba8e38b7 --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/object.cpp @@ -0,0 +1,750 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +** 08/2003 : Initial release of rbuic, the QtRuby User Interface Compiler, +** a work derived from the PerlQt puic. +** +** Richard Dale +** +**********************************************************************/ + +#include "uic.h" +#include "parser.h" +#include "widgetdatabase.h" +#include "domtool.h" +#include <qregexp.h> +#include <qsizepolicy.h> +#include <qstringlist.h> +#define NO_STATIC_COLORS +#include <globaldefs.h> +#include <zlib.h> + +/*! + Creates a declaration for the object given in \a e. + + Children are not traversed recursively. + + \sa createObjectImpl() + */ +void Uic::createObjectDecl( const QDomElement& e ) +{ + if ( e.tagName() == "vbox" || e.tagName() == "hbox" || e.tagName() == "grid" ) { + out << indent << registerObject(getLayoutName(e) ) << endl; + } else { + QString objClass = getClassName( e ); + if ( objClass.isEmpty() ) + return; + QString objName = getObjectName( e ); + if ( objName.isEmpty() ) + return; + // ignore QLayoutWidgets + if ( objClass == "Qt::LayoutWidget" ) + return; + + // register the object and unify its name + objName = registerObject( objName ); + out << indent << objName << endl; + } +} + +/*! + Creates a QtRuby attribute declaration for the object given in \a e. + + Children are not traversed recursively. + + */ +void Uic::createAttrDecl( const QDomElement& e ) +{ + if ( e.tagName() == "vbox" || e.tagName() == "hbox" || e.tagName() == "grid" ) { +// out << indent << registerObject(getLayoutName(e) ) << endl; + } else { + QString objClass = getClassName( e ); + if ( objClass.isEmpty() ) + return; + QString objName = getObjectName( e ); + if ( objName.isEmpty() ) + return; + // ignore QLayoutWidgets + if ( objClass == "Qt::LayoutWidget" ) + return; + // register the object and unify its name + objName = registerObject( objName ); + QString attr(objName); + attr.replace(QChar('@'), "attr_reader :"); + + out << indent << attr << endl; + QDomElement n = getObjectProperty( e, "font"); +// if ( !n.isNull() ) +// out << indent << objName + "_font" << endl; + } +} + + +/*! + Creates an implementation for the object given in \a e. + + Traverses recursively over all children. + + Returns the name of the generated child object. + + \sa createObjectDecl() + */ + +static bool createdCentralWidget = false; + +QString Uic::createObjectImpl( const QDomElement &e, const QString& parentClass, const QString& par, const QString& layout ) +{ + QString parent( par ); + if ( parent == "self" && isMainWindow ) { + if ( !createdCentralWidget ) + out << indent << "setCentralWidget(Qt::Widget.new(self, \"qt_central_widget\"))" << endl; + createdCentralWidget = true; + parent = "centralWidget()"; + } + QDomElement n; + QString objClass, objName, fullObjName; + int numItems = 0; + int numColumns = 0; + int numRows = 0; + + if ( layouts.contains( e.tagName() ) ) + return createLayoutImpl( e, parentClass, parent, layout ); + + objClass = getClassName( e ); + if ( objClass.isEmpty() ) + return objName; + objName = getObjectName( e ); + + QString definedName = objName; + bool isTmpObject = objName.isEmpty() || objClass == "Qt::LayoutWidget"; + if ( isTmpObject ) { + if ( objClass[0] == 'Q' ) + objName = objClass.mid( 4 ); + else + objName = objClass.lower(); + } + + bool isLine = objClass == "Line"; + if ( isLine ) + objClass = "Qt::Frame"; + + out << endl; + if ( objClass == "Qt::LayoutWidget" ) { + if ( layout.isEmpty() ) { + // register the object and unify its name + objName = registerObject( objName ); + out << indent << (isTmpObject ? QString::fromLatin1("") : QString::null) << objName << " = Qt::Widget.new(" << parent << ", '" << objName << "')" << endl; + } else { + // the layout widget is not necessary, hide it by creating its child in the parent + QString result; + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if (tags.contains( n.tagName() ) ) + result = createObjectImpl( n, parentClass, parent, layout ); + } + return result; + } + + // Layouts don't go into the class instance dictionary. + // FIXME PerlQt: fullObjName isn't used anymore => remove + fullObjName = objName; + } else if ( objClass != "Qt::ToolBar" && objClass != "Qt::MenuBar" ) { + // register the object and unify its name + objName = registerObject( objName ); + + // Temporary objects don't go into the class instance dictionary. + fullObjName = objName; + + out << indent << fullObjName << " = " << createObjectInstance( objClass, parent, objName ) << endl; + } + else + fullObjName = objName; + + if ( objClass == "Qt::AxWidget" ) { + QString controlId; + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "property" && n.attribute( "name" ) == "control" ) { + controlId = n.firstChild().toElement().text(); + } + } + out << " "; + out << fullObjName << ".setControl(\"" << controlId << "\")" << endl; + } + + lastItem = "nil"; + // set the properties and insert items + bool hadFrameShadow = false; + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "property" ) { + bool stdset = stdsetdef; + if ( n.hasAttribute( "stdset" ) ) + stdset = toBool( n.attribute( "stdset" ) ); + QString prop = n.attribute("name"); + if ( prop == "database" ) + continue; + QString value = setObjectProperty( objClass, objName, prop, n.firstChild().toElement(), stdset ); + if ( value.isEmpty() ) + continue; + if ( prop == "name" ) + continue; + if ( isLine && prop == "frameShadow" ) + hadFrameShadow = true; + if ( prop == "buddy" && value[0] == '\"' && value[(int)value.length()-1] == '\"' ) { + buddies << Buddy( objName, value.mid(1, value.length() - 2 ) ); + continue; + } + if ( isLine && prop == "orientation" ) { + prop = "frameShape"; + if ( value.right(10) == "Horizontal" ) + value = "Qt::Frame::HLine"; + else + value = "Qt::Frame::VLine"; + if ( !hadFrameShadow ) { + prop = "frameStyle"; + value += " | Qt::Frame::Sunken"; + } + } + if ( prop == "buttonGroupId" ) { + if ( parentClass == "Qt::ButtonGroup" ) + out << indent << parent << ".insert( " << fullObjName << "," << value << ")" << endl; + continue; + } + if ( prop == "frameworkCode" ) + continue; + if ( objClass == "Qt::MultiLineEdit" && + QRegExp("echoMode|hMargin|maxLength|maxLines|undoEnabled").exactMatch(prop) ) + continue; + + QString call = fullObjName + "."; + if (! call.startsWith("@")) { + call.prepend("@"); + } + + if ( stdset ) { + call += mkStdSet( prop ) + "( "; + call += value + " )"; + } else { + call += "setProperty( \"" + prop + "\", Qt::Variant.new(" ; + call += value + " ) )"; + } + + if ( n.firstChild().toElement().tagName() == "string" || + prop == "currentItem" ) { + trout << indent << call << endl; + } else { + out << indent << call << endl; + } + } else if ( n.tagName() == "item" ) { + QString call; + QString value; + + if ( objClass.mid( 4 ) == "ListBox" ) { + call = createListBoxItemImpl( n, fullObjName, &value ); + if ( !call.isEmpty() ) { + if ( numItems == 0 ) + trout << indent << fullObjName << ".clear()" << endl; + trout << indent << call << endl; + } + } else if ( objClass.mid( 4 ) == "ComboBox" ) { + call = createListBoxItemImpl( n, fullObjName, &value ); + if ( !call.isEmpty() ) { + if ( numItems == 0 ) + trout << indent << fullObjName << ".clear()" << endl; + trout << indent << call << endl; + } + } else if ( objClass.mid( 4 ) == "IconView" ) { + call = createIconViewItemImpl( n, fullObjName ); + if ( !call.isEmpty() ) { + if ( numItems == 0 ) + trout << indent << fullObjName << ".clear()" << endl; + trout << indent << call << endl; + } + } else if ( objClass.mid( 4 ) == "ListView" ) { + call = createListViewItemImpl( n, fullObjName, QString::null ); + if ( !call.isEmpty() ) { + if ( numItems == 0 ) + trout << indent << fullObjName << ".clear()" << endl; + trout << call << endl; + } + } + if ( !call.isEmpty() ) + numItems++; + } else if ( n.tagName() == "column" || n.tagName() == "row" ) { + QString call; + QString value; + + if ( objClass.mid( 4 ) == "ListView" ) { + call = createListViewColumnImpl( n, fullObjName, &value ); + if ( !call.isEmpty() ) { + out << call; + trout << indent << fullObjName << ".header().setLabel( " + << numColumns++ << ", " << value << " )\n"; + } + } else if ( objClass == "Qt::Table" || objClass == "Qt::DataTable" ) { + bool isCols = ( n.tagName() == "column" ); + call = createTableRowColumnImpl( n, fullObjName, &value ); + if ( !call.isEmpty() ) { + out << call; + trout << indent << fullObjName << "." + << ( isCols ? "horizontalHeader" : "verticalHeader" ) + << "().setLabel( " + << ( isCols ? numColumns++ : numRows++ ) + << ", " << value << " )\n"; + } + } + } + } + + // create all children, some widgets have special requirements + + if ( objClass == "Qt::TabWidget" ) { + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( tags.contains( n.tagName() ) ) { + QString page = createObjectImpl( n, objClass, fullObjName ); + QString comment; + QString label = DomTool::readAttribute( n, "title", "", comment ).toString(); + out << indent << fullObjName << ".insertTab(" << page << ", " << trcall( label ) << ")" << endl; + trout << indent << fullObjName << ".changeTab( " << page << ", " + << trcall( label, comment ) << " )" << endl; + } + } + } else if ( objClass == "Qt::WidgetStack" ) { + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( tags.contains( n.tagName() ) ) { + QString page = createObjectImpl( n, objClass, objName ); + int id = DomTool::readAttribute( n, "id", "" ).toInt(); + out << indent << fullObjName << ".addWidget( " << page << ", " << id << " )" << endl; + } + } + } else if ( objClass == "Qt::ToolBox" ) { + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( tags.contains( n.tagName() ) ) { + QString page = createObjectImpl( n, objClass, objName ); + QString comment; + QString label = DomTool::readAttribute( n, "label", comment ).toString(); + out << indent << fullObjName << ".addItem( " << page << ", \"\" )" << endl; + trout << indent << fullObjName << ".setItemLabel( " << fullObjName + << ".indexOf(" << page << "), " << trcall( label, comment ) + << " )" << endl; + } + } + } else if ( objClass != "Qt::ToolBar" && objClass != "Qt::MenuBar" ) { // standard widgets + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( tags.contains( n.tagName() ) ) + createObjectImpl( n, objClass, fullObjName ); + } + } + + return fullObjName; +} + + + +/*! + Creates a set-call for property \a exclusiveProp of the object + given in \a e. + + If the object does not have this property, the function does nothing. + + Exclusive properties are used to generate the implementation of + application font or palette change handlers in createFormImpl(). + + */ +void Uic::createExclusiveProperty( const QDomElement & e, const QString& exclusiveProp ) +{ + QDomElement n; + QString objClass = getClassName( e ); + if ( objClass.isEmpty() ) + return; + QString objName = getObjectName( e ); + if ( objClass.isEmpty() ) + return; + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "property" ) { + bool stdset = stdsetdef; + if ( n.hasAttribute( "stdset" ) ) + stdset = toBool( n.attribute( "stdset" ) ); + QString prop = n.attribute("name"); + if ( prop != exclusiveProp ) + continue; + QString value = setObjectProperty( objClass, objName, prop, n.firstChild().toElement(), stdset ); + if ( value.isEmpty() ) + continue; + out << indent << indent << objName << ".setProperty(\"" << prop << "\", Qt::Variant.new(" << value << "))" << endl; + } + } +} + + +/*! Attention: this function has to be in sync with + Resource::saveProperty() and DomTool::elementToVariant. If you + change one, change all. + */ +QString Uic::setObjectProperty( const QString& objClass, const QString& obj, const QString &prop, const QDomElement &e, bool stdset ) +{ + QString v; + if ( e.tagName() == "rect" ) { + QDomElement n3 = e.firstChild().toElement(); + int x = 0, y = 0, w = 0, h = 0; + while ( !n3.isNull() ) { + if ( n3.tagName() == "x" ) + x = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "y" ) + y = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "width" ) + w = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "height" ) + h = n3.firstChild().toText().data().toInt(); + n3 = n3.nextSibling().toElement(); + } + v = "Qt::Rect.new(%1, %2, %3, %4)"; + v = v.arg(x).arg(y).arg(w).arg(h); + + } else if ( e.tagName() == "point" ) { + QDomElement n3 = e.firstChild().toElement(); + int x = 0, y = 0; + while ( !n3.isNull() ) { + if ( n3.tagName() == "x" ) + x = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "y" ) + y = n3.firstChild().toText().data().toInt(); + n3 = n3.nextSibling().toElement(); + } + v = "Qt::Point.new(%1, %2)"; + v = v.arg(x).arg(y); + } else if ( e.tagName() == "size" ) { + QDomElement n3 = e.firstChild().toElement(); + int w = 0, h = 0; + while ( !n3.isNull() ) { + if ( n3.tagName() == "width" ) + w = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "height" ) + h = n3.firstChild().toText().data().toInt(); + n3 = n3.nextSibling().toElement(); + } + v = "Qt::Size.new(%1, %2)"; + v = v.arg(w).arg(h); + } else if ( e.tagName() == "color" ) { + QDomElement n3 = e.firstChild().toElement(); + int r= 0, g = 0, b = 0; + while ( !n3.isNull() ) { + if ( n3.tagName() == "red" ) + r = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "green" ) + g = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "blue" ) + b = n3.firstChild().toText().data().toInt(); + n3 = n3.nextSibling().toElement(); + } + v = "Qt::Color.new(%1, %2, %3)"; + v = v.arg(r).arg(g).arg(b); + } else if ( e.tagName() == "font" ) { + QDomElement n3 = e.firstChild().toElement(); + QString fontname; + if ( !obj.isEmpty() ) { + fontname = obj + "_font"; + out << indent << fontname << " = Qt::Font.new(" << obj << ".font())" << endl; + } else { + fontname = registerObject( "f" ); + out << indent << fontname << " = Qt::Font.new(font())" << endl; + } + while ( !n3.isNull() ) { + if ( n3.tagName() == "family" ) + out << indent << fontname << ".setFamily(\"" << n3.firstChild().toText().data() << "\")" << endl; + else if ( n3.tagName() == "pointsize" ) + out << indent << fontname << ".setPointSize(" << n3.firstChild().toText().data() << ")" << endl; + else if ( n3.tagName() == "bold" ) + out << indent << fontname << ".setBold(" << mkBool( n3.firstChild().toText().data() ) << ")" << endl; + else if ( n3.tagName() == "italic" ) + out << indent << fontname << ".setItalic(" << mkBool( n3.firstChild().toText().data() ) << ")" << endl; + else if ( n3.tagName() == "underline" ) + out << indent << fontname << ".setUnderline(" << mkBool( n3.firstChild().toText().data() ) << ")" << endl; + else if ( n3.tagName() == "strikeout" ) + out << indent << fontname << ".setStrikeOut(" << mkBool( n3.firstChild().toText().data() ) << ")" << endl; + n3 = n3.nextSibling().toElement(); + } + + if ( prop == "font" ) { + if ( !obj.isEmpty() ) + out << indent << obj << ".setFont(" << fontname << ")" << endl; + else + out << indent << "setFont(" << fontname << ")" << endl; + } else { + v = fontname; + } + } else if ( e.tagName() == "string" ) { + QString txt = e.firstChild().toText().data(); + QString com = getComment( e.parentNode() ); + + if ( prop == "toolTip" && objClass != "Qt::Action" && objClass != "Qt::ActionGroup" ) { + if ( !obj.isEmpty() ) + trout << indent << "Qt::ToolTip.add( " << obj << ", " + << trcall( txt, com ) << " )" << endl; + else + trout << indent << "Qt::ToolTip.add( self, " + << trcall( txt, com ) << " )" << endl; + } else if ( prop == "whatsThis" && objClass != "Qt::Action" && objClass != "Qt::ActionGroup" ) { + if ( !obj.isEmpty() ) + trout << indent << "Qt::WhatsThis.add(" << obj << ", " << trcall( txt, com ) << ")" << endl; + else + trout << indent << "Qt::WhatsThis.add(self," << trcall( txt, com ) << ")" << endl; + } else if (e.parentNode().toElement().attribute("name") == "accel") { + v = "Qt::KeySequence.new(" + trcall( txt, com ) + ")"; + } else { + v = trcall( txt, com ); + } + } else if ( e.tagName() == "cstring" ) { + v = "\"%1\""; + v = v.arg( e.firstChild().toText().data() ); + } else if ( e.tagName() == "number" ) { + // FIXME: hack. QtRuby needs a QKeySequence to build an accel + if( e.parentNode().toElement().attribute("name") == "accel" ) + v = "Qt::KeySequence.new(%1)"; + else + v = "%1"; + v = v.arg( e.firstChild().toText().data() ); + } else if ( e.tagName() == "bool" ) { + if ( stdset ) + v = "%1"; + else + v = "Qt::Variant.new(%1, 0)"; + v = v.arg( mkBool( e.firstChild().toText().data() ) ); + } else if ( e.tagName() == "pixmap" ) { + v = e.firstChild().toText().data(); + if( !externPixmaps ) + v.prepend( '@' ); + if ( !pixmapLoaderFunction.isEmpty() ) { + v.prepend( pixmapLoaderFunction + "(" + QString( externPixmaps ? "\"" : "" ) ); + v.append( QString( externPixmaps ? "\"" : "" ) + ")" ); + } + } else if ( e.tagName() == "iconset" ) { + v = "Qt::IconSet.new(%1)"; + QString s = e.firstChild().toText().data(); + if ( !pixmapLoaderFunction.isEmpty() ) { + s.prepend( pixmapLoaderFunction + "(" + QString( externPixmaps ? "\"" : "" ) ); + s.append( QString( externPixmaps ? "\"" : "" ) + ")" ); + } else { + s.prepend("@"); + } + v = v.arg( s ); + } else if ( e.tagName() == "image" ) { + v = e.firstChild().toText().data() + ".convertToImage()"; + } else if ( e.tagName() == "enum" ) { + v = "%1::%2"; + QString oc = objClass; + QString ev = e.firstChild().toText().data(); + if ( oc == "Qt::ListView" && ev == "Manual" ) // #### workaround, rename QListView::Manual of WithMode enum in 3.0 + oc = "Qt::ScrollView"; + v = v.arg( oc ).arg( ev ); + } else if ( e.tagName() == "set" ) { + QString keys( e.firstChild().toText().data() ); + QStringList lst = QStringList::split( '|', keys ); + v = ""; +#if defined(Q_CC_EDG) + // workaround for EDG bug reproduced with MIPSpro C++ 7.3.? + // and KAI C++ 4.0e that will be fixed in KAI C++ 4.0f + QStringList::Iterator it = lst.begin(); + for ( ; it != lst.end(); ++it ) { +#else + for ( QStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) { +#endif + v += objClass + "::" + *it; + if ( it != lst.fromLast() ) + v += " | "; + } + v += ""; + } else if ( e.tagName() == "sizepolicy" ) { + QDomElement n3 = e.firstChild().toElement(); + QSizePolicy sp; + while ( !n3.isNull() ) { + if ( n3.tagName() == "hsizetype" ) + sp.setHorData( (QSizePolicy::SizeType)n3.firstChild().toText().data().toInt() ); + else if ( n3.tagName() == "vsizetype" ) + sp.setVerData( (QSizePolicy::SizeType)n3.firstChild().toText().data().toInt() ); + else if ( n3.tagName() == "horstretch" ) + sp.setHorStretch( n3.firstChild().toText().data().toInt() ); + else if ( n3.tagName() == "verstretch" ) + sp.setVerStretch( n3.firstChild().toText().data().toInt() ); + n3 = n3.nextSibling().toElement(); + } + QString tmp = (obj.isEmpty() ? QString::fromLatin1("self") : obj) + "."; + v = "Qt::SizePolicy.new(%1, %2, %3, %4, " + tmp + "sizePolicy().hasHeightForWidth())"; + v = v.arg( (int)sp.horData() ).arg( (int)sp.verData() ).arg( sp.horStretch() ).arg( sp.verStretch() ); + } else if ( e.tagName() == "palette" ) { + QPalette pal; + bool no_pixmaps = e.elementsByTagName( "pixmap" ).count() == 0; + QDomElement n; + if ( no_pixmaps ) { + n = e.firstChild().toElement(); + while ( !n.isNull() ) { + QColorGroup cg; + if ( n.tagName() == "active" ) { + cg = loadColorGroup( n ); + pal.setActive( cg ); + } else if ( n.tagName() == "inactive" ) { + cg = loadColorGroup( n ); + pal.setInactive( cg ); + } else if ( n.tagName() == "disabled" ) { + cg = loadColorGroup( n ); + pal.setDisabled( cg ); + } + n = n.nextSibling().toElement(); + } + } + if ( no_pixmaps && pal == QPalette( pal.active().button(), pal.active().background() ) ) { + v = "Qt::Palette.new(Qt::Color.new(%1,%2,%3), Qt::Color.new(%1,%2,%3))"; + v = v.arg( pal.active().button().red() ).arg( pal.active().button().green() ).arg( pal.active().button().blue() ); + v = v.arg( pal.active().background().red() ).arg( pal.active().background().green() ).arg( pal.active().background().blue() ); + } else { + QString palette = "pal"; + if ( !pal_used ) { + out << indent << palette << " = Qt::Palette.new()" << endl; + pal_used = true; + } + QString cg = "cg"; + if ( !cg_used ) { + out << indent << cg << " = Qt::ColorGroup.new()" << endl; + cg_used = true; + } + n = e.firstChild().toElement(); + while ( !n.isNull() && n.tagName() != "active" ) + n = n.nextSibling().toElement(); + createColorGroupImpl( cg, n ); + out << indent << palette << ".setActive(" << cg << ")" << endl; + + n = e.firstChild().toElement(); + while ( !n.isNull() && n.tagName() != "inactive" ) + n = n.nextSibling().toElement(); + createColorGroupImpl( cg, n ); + out << indent << palette << ".setInactive(" << cg << ")" << endl; + + n = e.firstChild().toElement(); + while ( !n.isNull() && n.tagName() != "disabled" ) + n = n.nextSibling().toElement(); + createColorGroupImpl( cg, n ); + out << indent << palette << ".setDisabled(" << cg << ")" << endl; + v = palette; + } + } else if ( e.tagName() == "cursor" ) { + v = "Qt::Cursor.new(%1)"; + v = v.arg( e.firstChild().toText().data() ); + } else if ( e.tagName() == "date" ) { + QDomElement n3 = e.firstChild().toElement(); + int y, m, d; + y = m = d = 0; + while ( !n3.isNull() ) { + if ( n3.tagName() == "year" ) + y = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "month" ) + m = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "day" ) + d = n3.firstChild().toText().data().toInt(); + n3 = n3.nextSibling().toElement(); + } + v = "Qt::Date.new(%1,%2,%3)"; + v = v.arg(y).arg(m).arg(d); + } else if ( e.tagName() == "time" ) { + QDomElement n3 = e.firstChild().toElement(); + int h, m, s; + h = m = s = 0; + while ( !n3.isNull() ) { + if ( n3.tagName() == "hour" ) + h = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "minute" ) + m = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "second" ) + s = n3.firstChild().toText().data().toInt(); + n3 = n3.nextSibling().toElement(); + } + v = "Qt::Time.new(%1, %2, %3)"; + v = v.arg(h).arg(m).arg(s); + } else if ( e.tagName() == "datetime" ) { + QDomElement n3 = e.firstChild().toElement(); + int h, mi, s, y, mo, d; + h = mi = s = y = mo = d = 0; + while ( !n3.isNull() ) { + if ( n3.tagName() == "hour" ) + h = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "minute" ) + mi = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "second" ) + s = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "year" ) + y = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "month" ) + mo = n3.firstChild().toText().data().toInt(); + else if ( n3.tagName() == "day" ) + d = n3.firstChild().toText().data().toInt(); + n3 = n3.nextSibling().toElement(); + } + v = "Qt::DateTime.new(Qt::Date.new(%1, %2, %3), Qt::Time.new(%4, %5, %6))"; + v = v.arg(y).arg(mo).arg(d).arg(h).arg(mi).arg(s); + } else if ( e.tagName() == "stringlist" ) { + QStringList l; + QDomElement n3 = e.firstChild().toElement(); + QString listname; + if ( !obj.isEmpty() ) { + listname = obj + "_strlist"; + out << indent << listname << " = ["; + } else { + listname = "strlist"; + out << indent << listname << " = ["; + } + int i = 0; + while ( true ) { + if ( n3.tagName() == "string" ) + { + out << "'" << n3.firstChild().toText().data().simplifyWhiteSpace() << "'"; + n3 = n3.nextSibling().toElement(); + i++; + if( n3.isNull() ) + break; + else if( (i%3) == 0 ) + { + ++indent; + out << "," << endl << indent; + --indent; + } + else if( n3.isNull() ) + break; + else + out << ", "; + } + } + out << "]" << endl; + v = listname; + } + return v; +} + + + + +/*! Extracts a named object property from \a e. + */ +QDomElement Uic::getObjectProperty( const QDomElement& e, const QString& name ) +{ + QDomElement n; + for ( n = e.firstChild().toElement(); + !n.isNull(); + n = n.nextSibling().toElement() ) { + if ( n.tagName() == "property" && n.toElement().attribute("name") == name ) + return n; + } + return n; +} + diff --git a/qtruby/rubylib/designer/rbuic/parser.cpp b/qtruby/rubylib/designer/rbuic/parser.cpp new file mode 100644 index 00000000..b7869f75 --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/parser.cpp @@ -0,0 +1,71 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +** 08/2003 : Initial release of rbuic, the QtRuby User Interface Compiler, +** a work derived from the PerlQt puic. +** +** Richard Dale +** +**********************************************************************/ + +#include "parser.h" +#include <qobject.h> +#include <qstringlist.h> + +class NormalizeObject : public QObject +{ +public: + NormalizeObject() : QObject() {} + static QCString normalizeSignalSlot( const char *signalSlot ) { return QObject::normalizeSignalSlot( signalSlot ); } +}; + +QString Parser::cleanArgs( const QString &func ) +{ + QString slot( func ); + int begin = slot.find( "(" ) + 1; + QString args = slot.mid( begin ); + args = args.left( args.find( ")" ) ); + QStringList lst = QStringList::split( ',', args ); + QString res = slot.left( begin ); + for ( QStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) { + if ( it != lst.begin() ) + res += ","; + QString arg = *it; + int pos = 0; + if ( ( pos = arg.find( "&" ) ) != -1 ) { + arg = arg.left( pos + 1 ); + } else if ( ( pos = arg.find( "*" ) ) != -1 ) { + arg = arg.left( pos + 1 ); + } else { + arg = arg.simplifyWhiteSpace(); + if ( ( pos = arg.find( ':' ) ) != -1 ) + arg = arg.left( pos ).simplifyWhiteSpace() + ":" + arg.mid( pos + 1 ).simplifyWhiteSpace(); + QStringList l = QStringList::split( ' ', arg ); + if ( l.count() == 2 ) { + if ( l[ 0 ] != "const" && l[ 0 ] != "unsigned" && l[ 0 ] != "var" ) + arg = l[ 0 ]; + } else if ( l.count() == 3 ) { + arg = l[ 0 ] + " " + l[ 1 ]; + } + } + res += arg; + } + res += ")"; + + return QString::fromLatin1( NormalizeObject::normalizeSignalSlot( res.latin1() ) ); +} diff --git a/qtruby/rubylib/designer/rbuic/parser.h b/qtruby/rubylib/designer/rbuic/parser.h new file mode 100644 index 00000000..5a5671ad --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/parser.h @@ -0,0 +1,33 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef PARSER_H +#define PARSER_H + +#include <qstring.h> + +class Parser +{ +public: + static QString cleanArgs( const QString &func ); + +}; + +#endif diff --git a/qtruby/rubylib/designer/rbuic/rbuic.pro b/qtruby/rubylib/designer/rbuic/rbuic.pro new file mode 100644 index 00000000..a9869ab0 --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/rbuic.pro @@ -0,0 +1,24 @@ +TEMPLATE = app +CONFIG += qt console warn_on release professional +HEADERS = uic.h \ + widgetdatabase.h \ + domtool.h \ + parser.h \ + widgetinterface.h + +SOURCES = main.cpp uic.cpp form.cpp object.cpp \ + subclassing.cpp embed.cpp\ + widgetdatabase.cpp \ + domtool.cpp \ + parser.cpp + +DEFINES += QT_INTERNAL_XML +#include( ../../../src/qt_professional.pri ) + +TARGET = rbuic +DEFINES += UIC +DESTDIR = /usr/bin + +#target.path=$$bins.path +#INSTALLS += target + diff --git a/qtruby/rubylib/designer/rbuic/subclassing.cpp b/qtruby/rubylib/designer/rbuic/subclassing.cpp new file mode 100644 index 00000000..a3ad25c3 --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/subclassing.cpp @@ -0,0 +1,197 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include "uic.h" +#include "parser.h" +#include "widgetdatabase.h" +#include "domtool.h" +#include <qfile.h> +#include <qstringlist.h> +#include <qdatetime.h> +#define NO_STATIC_COLORS +#include <globaldefs.h> +#include <qregexp.h> +#include <stdio.h> +#include <stdlib.h> +#include <zlib.h> + + +/*! + Creates an implementation for a subclass \a subClass of the form + given in \a e + + \sa createSubDecl() + */ +void Uic::createSubImpl( const QDomElement &e, const QString& subClass ) +{ + QDomElement n; + QDomNodeList nl; + int i; + QStringList::Iterator it, it2, it3; + + QString objClass = getClassName( e ); + if ( objClass.isEmpty() ) + return; + if (hasKDEwidget) { + out << indent << "require 'Korundum'" << endl; + } else { + out << indent << "require 'Qt'" << endl; + } + out << endl; + out << indent << "class " << subClass << " < " << nameOfClass << endl; + + out << endl; + ++indent; + + // constructor + if ( objClass == "Qt::Dialog" || objClass == "Qt::Wizard" ) { + out << indent << "# Constructs a " << subClass << " which is a child of 'parent', with the " << endl; + out << indent << "# name 'name' and widget flags set to 'fl' " << endl; + out << indent << "# " << endl; + out << indent << "# The " << objClass.mid(4).lower() << " will by default be modeless, unless you set 'modal' to" << endl; + out << indent << "# true to construct a modal " << objClass.mid(4).lower() << "." << endl; + out << indent << "def initialize(parent = nil, name = nil, modal = false, fl = 0)" << endl; + ++indent; + out << indent << "super" << endl; + } else { + out << indent << "# Constructs a " << subClass << " which is a child of 'parent', with the " << endl; + out << indent << "# name 'name' and widget flags set to 'fl' " << endl; + out << indent << "def initialize(parent = nil, name = nil, fl = 0)" << endl; + ++indent; + out << indent << "super" << endl; + } + --indent; + out << indent << "end" << endl; + out << endl; + + // find additional slots + QStringList publicSlots, protectedSlots, privateSlots; + QStringList publicSlotTypes, protectedSlotTypes, privateSlotTypes; + QStringList publicSlotSpecifier, protectedSlotSpecifier, privateSlotSpecifier; + QMap<QString, QString> functionImpls; + nl = e.parentNode().toElement().elementsByTagName( "slot" ); + for ( i = 0; i < (int) nl.length(); i++ ) { + n = nl.item(i).toElement(); + if ( n.parentNode().toElement().tagName() != "slots" + && n.parentNode().toElement().tagName() != "connections" ) + continue; + if ( n.attribute( "language", "C++" ) != "C++" ) + continue; + QString returnType = n.attribute( "returnType", "void" ); + QString slotName = n.firstChild().toText().data().stripWhiteSpace(); + if ( slotName.endsWith( ";" ) ) + slotName = slotName.left( slotName.length() - 1 ); + QString specifier = n.attribute( "specifier" ); + QString access = n.attribute( "access" ); + if ( access == "protected" ) { + protectedSlots += slotName; + protectedSlotTypes += returnType; + protectedSlotSpecifier += specifier; + } else if ( access == "private" ) { + privateSlots += slotName; + privateSlotTypes += returnType; + privateSlotSpecifier += specifier; + } else { + publicSlots += slotName; + publicSlotTypes += returnType; + publicSlotSpecifier += specifier; + } + } + + + // compatibility with early 3.0 betas + nl = e.parentNode().toElement().elementsByTagName( "function" ); + for ( i = 0; i < (int) nl.length(); i++ ) { + QString fname = n.attribute( "name" ); + fname = Parser::cleanArgs( fname ); + functionImpls.insert( fname, n.firstChild().toText().data() ); + } + // create stubs for public additional slots + if ( !publicSlots.isEmpty() ) { + for ( it = publicSlots.begin(), it2 = publicSlotTypes.begin(), it3 = publicSlotSpecifier.begin(); + it != publicSlots.end(); ++it, ++it2, ++it3 ) { + QString pure; + QString type = *it2; + if ( type.isEmpty() ) + type = "void"; + if ( *it3 == "non virtual" ) + continue; + if ( isEmptyFunction( *it ) ) { + out << endl; + int astart = (*it).find('('); + out << indent << "def " << (*it).left(astart)<< "(*k)" << endl; + ++indent; + out << indent << "print(\"" << subClass << "." << (*it) << ": Not implemented yet.\\n\")" << endl; + --indent; + out << indent << "end" << endl; + } + } + } + + // create stubs for protected additional slots + if ( !protectedSlots.isEmpty() ) { + for ( it = protectedSlots.begin(), it2 = protectedSlotTypes.begin(), it3 = protectedSlotSpecifier.begin(); + it != protectedSlots.end(); ++it, ++it2, ++it3 ) { + QString pure; + QString type = *it2; + if ( type.isEmpty() ) + type = "void"; + if ( *it3 == "non virtual" ) + continue; + if ( isEmptyFunction( *it ) ) { + out << endl; + int astart = (*it).find('('); + out << indent << "def " << (*it).left(astart)<< "(*k)" << endl; + ++indent; + out << indent << "print(\"" << subClass << "." << (*it) << ": (Protected) Not implemented yet.\\n\")" << endl; + --indent; + out << indent << "end" << endl; + out << indent << "protected :" << (*it).left(astart)<< endl; + } + } + } + + + // create stubs for private additional slots + if ( !privateSlots.isEmpty() ) { + for ( it = privateSlots.begin(), it2 = privateSlotTypes.begin(), it3 = privateSlotSpecifier.begin(); + it != privateSlots.end(); ++it, ++it2, ++it3 ) { + QString pure; + QString type = *it2; + if ( type.isEmpty() ) + type = "void"; + if ( *it3 == "non virtual" ) + continue; + if ( isEmptyFunction( *it ) ) { + out << endl; + int astart = (*it).find('('); + out << indent << "def " << (*it).left(astart)<< "(*k)" << endl; + ++indent; + out << indent << "print(\"" << subClass << "." << (*it) << ": (Private) Not implemented yet.\\n\")" << endl; + --indent; + out << indent << "end" << endl; + out << indent << "private :" << (*it).left(astart)<< endl; + } + } + } + + --indent; + out << indent << "end" << endl; +} diff --git a/qtruby/rubylib/designer/rbuic/uic.cpp b/qtruby/rubylib/designer/rbuic/uic.cpp new file mode 100644 index 00000000..e4f8eed7 --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/uic.cpp @@ -0,0 +1,1104 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** Copyright (c) 2001 Phil Thompson <phil@river-bank.demon.co.uk> +** Copyright (c) 2002 Germain Garand <germain@ebooksfrance.com> +** +** This file is part of Qt Designer. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ +/* +** 06/2002 : Initial release of puic, the PerlQt User Interface Compiler, +** a work derivated from uic (the Qt User Interface Compiler) +** and pyuic (the PyQt User Interface Compiler). +** +** G.Garand +** +** 08/2003 : Initial release of rbuic, the QtRuby User Interface Compiler, +** a work derived from the PerlQt puic. +** +** Richard Dale +** +**********************************************************************/ + +#include "uic.h" +#include "parser.h" +#include "widgetdatabase.h" +#include "domtool.h" +#include <qfile.h> +#include <qstringlist.h> +#include <qdatetime.h> +#define NO_STATIC_COLORS +#include <globaldefs.h> +#include <qregexp.h> +#include <stdio.h> +#include <stdlib.h> +#include <zlib.h> + +bool Uic::hasKDEwidget = false; +bool Uic::isMainWindow = false; +RubyIndent Uic::indent; + + +// Re-calculate the indent string based on the current number and nature of the +// indent. +void RubyIndent::calc() +{ + indstr.truncate(0); + + for (uint i = current; i > 0; --i) + if (tabStop == 0) + indstr += '\t'; + else + for (uint t = 0; t < tabStop; ++t) + indstr += ' '; +} + + +QString Uic::getComment( const QDomNode& n ) +{ + QDomNode child = n.firstChild(); + while ( !child.isNull() ) { + if ( child.toElement().tagName() == "comment" ) + return child.toElement().firstChild().toText().data(); + child = child.nextSibling(); + } + return QString::null; +} + +QString Uic::mkBool( bool b ) +{ + return b? "true" : "false"; +} + +QString Uic::mkBool( const QString& s ) +{ + return mkBool( s == "true" || s == "1" ); +} + +bool Uic::toBool( const QString& s ) +{ + return s == "true" || s.toInt() != 0; +} + +QString Uic::fixString( const QString &str ) +{ + QString s( str ); + s.replace( QRegExp( "\\\\" ), "\\\\" ); + s.replace( QRegExp( "\"" ), "\\\"" ); + s.replace( QRegExp( "\r?\n" ), "\\n\" +\n" + indent + "\"" ); + return "\"" + s + "\""; +} + +QString Uic::trcall( const QString& sourceText, const QString& comment ) +{ + if ( sourceText.isEmpty() && comment.isEmpty() ) + return "nil"; + + if ( comment.isEmpty() ) + return trmacro + "(" + fixString( sourceText ) + ")"; + return trmacro + "(" + fixString( sourceText ) + "," + fixString( comment ) + ")"; +} + +QString Uic::mkStdSet( const QString& prop ) +{ + return QString( "set" ) + prop[0].upper() + prop.mid(1); +} + + + +bool Uic::isEmptyFunction( const QString& fname ) +{ + QMap<QString, QString>::Iterator fit = functionImpls.find( Parser::cleanArgs( fname ) ); + if ( fit != functionImpls.end() ) { + int begin = (*fit).find( "{" ); + QString body = (*fit).mid( begin + 1, (*fit).findRev( "}" ) - begin - 1 ); + return body.simplifyWhiteSpace().isEmpty(); + } + // For now ruby functions are always empty, until a rubyeditor Qt Designer plugin exists.. + return true; +} + + + +/*! + \class Uic uic.h + \brief User Interface Compiler + + The class Uic encapsulates the user interface compiler (uic). + */ +Uic::Uic( const QString &fn, QTextStream &outStream, QDomDocument doc, + bool subcl, const QString &trm, const QString& subClass, + bool omitForwardDecls, QString &uicClass, bool useKDE ) + : out( outStream ), trout( &languageChangeBody ), + trmacro( trm ), nofwd( omitForwardDecls ) +{ + Uic::hasKDEwidget = useKDE; + fileName = fn; + writeSlotImpl = true; + defMargin = BOXLAYOUT_DEFAULT_MARGIN; + defSpacing = BOXLAYOUT_DEFAULT_SPACING; + externPixmaps = false; + + item_used = cg_used = pal_used = 0; + + layouts << "hbox" << "vbox" << "grid"; + tags = layouts; + tags << "widget"; + + pixmapLoaderFunction = getPixmapLoaderFunction( doc.firstChild().toElement() ); + nameOfClass = getFormClassName( doc.firstChild().toElement() ); + + uiFileVersion = doc.firstChild().toElement().attribute("version"); + stdsetdef = toBool( doc.firstChild().toElement().attribute("stdsetdef") ); + + QDomElement e = doc.firstChild().firstChild().toElement(); + QDomElement widget; + while ( !e.isNull() ) { + if ( e.tagName() == "widget" ) { + widget = e; + } else if ( e.tagName() == "pixmapinproject" ) { + externPixmaps = true; + } else if ( e.tagName() == "layoutdefaults" ) { + defSpacing = e.attribute( "spacing", QString::number( defSpacing ) ).toInt(); + defMargin = e.attribute( "margin", QString::number( defMargin ) ).toInt(); + } + e = e.nextSibling().toElement(); + } + e = widget; + + if ( nameOfClass.isEmpty() ) + nameOfClass = getObjectName( e ); + + uicClass = nameOfClass; + + if ( subcl ) { + createSubImpl( e, subClass ); + } else { + createFormImpl( e ); + } +} + +/*! Extracts a pixmap loader function from \a e + */ +QString Uic::getPixmapLoaderFunction( const QDomElement& e ) +{ + QDomElement n; + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "pixmapfunction" ) + return n.firstChild().toText().data(); + } + return QString::null; +} + + +/*! Extracts the forms class name from \a e + */ +QString Uic::getFormClassName( const QDomElement& e ) +{ + QDomElement n; + QString cn; + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "class" ) { + QString s = n.firstChild().toText().data(); + int i; + while ( ( i = s.find(' ' )) != -1 ) + s[i] = '_'; + cn = s; + } + } + return cn; +} + +/*! Extracts a Ruby class name from \a e. + */ +QString Uic::getClassName( const QDomElement& e ) +{ + QString s = e.attribute( "class" ); + if ( s.isEmpty() && e.tagName() == "toolbar" ) + s = "Qt::ToolBar"; + else if ( s.isEmpty() && e.tagName() == "menubar" ) + s = "Qt::MenuBar"; + else + { + QRegExp r("^([QK])(\\S+)"); + if( r.search( s ) != -1 ) { + if (r.cap(1) == "K") { + hasKDEwidget = true; + s = "KDE::" + r.cap(2); + } else { + if (s.startsWith("Qext")) { + s = "Qext::" + r.cap(2).mid(4); + } else { + s = "Qt::" + r.cap(2); + } + } + } + } + return s; +} + + + +/*! Returns TRUE if database framework code is generated, else FALSE. +*/ + +bool Uic::isFrameworkCodeGenerated( const QDomElement& e ) +{ + QDomElement n = getObjectProperty( e, "frameworkCode" ); + if ( n.attribute("name") == "frameworkCode" && + !DomTool::elementToVariant( n.firstChild().toElement(), QVariant( true, 0 ) ).toBool() ) + return false; + return true; +} + +/*! Extracts an object name from \a e. It's stored in the 'name' + property. + */ +QString Uic::getObjectName( const QDomElement& e ) +{ + QDomElement n = getObjectProperty( e, "name" ); + if ( n.firstChild().toElement().tagName() == "cstring" ) + return n.firstChild().toElement().firstChild().toText().data(); + return QString::null; +} + +/*! Extracts an layout name from \a e. It's stored in the 'name' + property of the preceeding sibling (the first child of a QLayoutWidget). + */ +QString Uic::getLayoutName( const QDomElement& e ) +{ + QDomElement p = e.parentNode().toElement(); + QString tail = QString::null; + + if ( getClassName(p) != "Qt::LayoutWidget" ) + tail = "Layout"; + + QDomElement n = getObjectProperty( p, "name" ); + if ( n.firstChild().toElement().tagName() == "cstring" ) + return n.firstChild().toElement().firstChild().toText().data() + tail; + return e.tagName(); +} + + +QString Uic::getDatabaseInfo( const QDomElement& e, const QString& tag ) +{ + QDomElement n; + QDomElement n1; + int child = 0; + // database info is a stringlist stored in this order + if ( tag == "connection" ) + child = 0; + else if ( tag == "table" ) + child = 1; + else if ( tag == "field" ) + child = 2; + else + return QString::null; + n = getObjectProperty( e, "database" ); + if ( n.firstChild().toElement().tagName() == "stringlist" ) { + // find correct stringlist entry + QDomElement n1 = n.firstChild().firstChild().toElement(); + for ( int i = 0; i < child && !n1.isNull(); ++i ) + n1 = n1.nextSibling().toElement(); + if ( n1.isNull() ) + return QString::null; + return n1.firstChild().toText().data(); + } + return QString::null; +} + + +void Uic::registerLayouts( const QDomElement &e ) +{ + if ( layouts.contains(e.tagName()) ) + createObjectDecl(e); + + QDomNodeList nl = e.childNodes(); + for ( int i = 0; i < (int) nl.length(); ++i ) + registerLayouts( nl.item(i).toElement() ); +} + + +/*! + Returns include file for class \a className or a null string. + */ +QString Uic::getInclude( const QString& className ) +{ + int wid = WidgetDatabase::idFromClassName( className ); + if ( wid != -1 ) + return WidgetDatabase::includeFile( wid ); + return QString::null; +} + +void Uic::createActionDecl( const QDomElement& e ) +{ + QString objName = getObjectName( e ); + if ( objName.isEmpty() ) + return; + out << indent << objName << endl; + if ( e.tagName() == "actiongroup" ) { + for ( QDomElement n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "action" || n.tagName() == "actiongroup" ) + createActionDecl( n ); + } + } +} + +void Uic::createActionImpl( const QDomElement &n, const QString &parent ) +{ + for ( QDomElement ae = n; !ae.isNull(); ae = ae.nextSibling().toElement() ) { + QString objName = registerObject( getObjectName( ae ) ); + if ( ae.tagName() == "action" ) + out << indent << objName << "= Qt::Action.new(" << parent << ", \"" << objName.mid(1) << "\")" << endl; + else if ( ae.tagName() == "actiongroup" ) + out << indent << objName << "= Qt::ActionGroup.new(" << parent << ", \"" << objName.mid(1) << "\")" << endl; + else + continue; + bool subActionsDone = false; + bool hasMenuText = false; + QString actionText; + for ( QDomElement n2 = ae.firstChild().toElement(); !n2.isNull(); n2 = n2.nextSibling().toElement() ) { + if ( n2.tagName() == "property" ) { + bool stdset = stdsetdef; + if ( n2.hasAttribute( "stdset" ) ) + stdset = toBool( n2.attribute( "stdset" ) ); + QString prop = n2.attribute("name"); + if ( prop == "name" ) + continue; + QString value = setObjectProperty( "Qt::Action", objName, prop, n2.firstChild().toElement(), stdset ); + if ( value.isEmpty() ) + continue; + + QString call = objName + "."; + if ( stdset ) { + call += mkStdSet( prop ) + "(" + value + ")"; + } else { + call += "setProperty( \"" + prop + "\", "; + call += "Qt::Variant.new(" + value + "))"; + } + + if (prop == "menuText") + hasMenuText = true; + else if (prop == "text") + actionText = value; + + if ( n2.firstChild().toElement().tagName() == "string" ) { + trout << indent << call << endl; + } else { + out << indent << call << endl; + } + } else if ( !subActionsDone && ( n2.tagName() == "actiongroup" || n2.tagName() == "action" ) ) { + createActionImpl( n2, objName ); + subActionsDone = true; + } + } + // workaround for loading pre-3.3 files expecting bogus QAction behavior + if (!hasMenuText && !actionText.isEmpty() && uiFileVersion < "3.3") + trout << indent << objName << ".setMenuText(" << actionText << ")" << endl; + } +} + +QString get_dock( const QString &d ) +{ + if ( d == "0" ) + return "DockUnmanaged"; + if ( d == "1" ) + return "DockTornOff"; + if ( d == "2" ) + return "DockTop"; + if ( d == "3" ) + return "DockBottom"; + if ( d == "4" ) + return "DockRight"; + if ( d == "5" ) + return "DockLeft"; + if ( d == "6" ) + return "DockMinimized"; + return ""; +} + +void Uic::createToolbarImpl( const QDomElement &n, const QString &parentClass, const QString &parent ) +{ + QDomNodeList nl = n.elementsByTagName( "toolbar" ); + for ( int i = 0; i < (int) nl.length(); i++ ) { + QDomElement ae = nl.item( i ).toElement(); + QString dock = get_dock( ae.attribute( "dock" ) ); + QString objName = "@" + getObjectName( ae ); + out << indent << objName << " = Qt::ToolBar.new(\"\", self, " << dock << ")" << endl; + createObjectImpl( ae, parentClass, parent ); + for ( QDomElement n2 = ae.firstChild().toElement(); !n2.isNull(); n2 = n2.nextSibling().toElement() ) { + if ( n2.tagName() == "action" ) { + out << indent << "@" << n2.attribute( "name" ) << ".addTo( " << objName << " )" << endl; + } else if ( n2.tagName() == "separator" ) { + out << indent << objName << ".addSeparator;" << endl; + } else if ( n2.tagName() == "widget" ) { + if ( n2.attribute( "class" ) != "Spacer" ) { + createObjectImpl( n2, "Qt::ToolBar", objName ); + } else { + QString child = createSpacerImpl( n2, parentClass, parent, objName ); + out << indent << "Qt::Application.sendPostedEvents( " << objName + << ", Qt::Event::ChildInserted)" << endl; + out << indent << objName << ".boxLayout().addItem(" << child << ")" << endl; + } + } + } + } +} + +void Uic::createMenuBarImpl( const QDomElement &n, const QString &parentClass, const QString &parent ) +{ + QString objName = "@" + getObjectName( n ); + out << indent << objName << " = Qt::MenuBar.new( self, \"" << objName.mid(1) << "\" )" << endl; + createObjectImpl( n, parentClass, parent ); + int i = 0; + QDomElement c = n.firstChild().toElement(); + while ( !c.isNull() ) { + if ( c.tagName() == "item" ) { + QString itemName = "@" + c.attribute( "name" ); + out << endl; + out << indent << itemName << " = Qt::PopupMenu.new( self )" << endl; + createPopupMenuImpl( c, parentClass, itemName ); + out << indent << objName << ".insertItem( \"\", " << itemName << ", " << i << " )" << endl; + QString findItem(objName + ".findItem(%1)"); + findItem = findItem.arg(i); + trout << indent << "if !" << findItem << ".nil?" << endl; + trout << indent << indent << findItem << ".setText( " << trcall( c.attribute( "text" ) ) << " )" << endl; + trout << indent << "end" << endl; + } else if ( c.tagName() == "separator" ) { + out << endl; + out << indent << objName << ".insertSeparator( " << i << " )" << endl; + } + c = c.nextSibling().toElement(); + i++; + } +} + +void Uic::createPopupMenuImpl( const QDomElement &e, const QString &parentClass, const QString &parent ) +{ + int i = 0; + for ( QDomElement n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "action" || n.tagName() == "actiongroup" ) { + QDomElement n2 = n.nextSibling().toElement(); + if ( n2.tagName() == "item" ) { // the action has a sub menu + QString itemName = "@" + n2.attribute( "name" ); + QString itemText = n2.attribute( "text" ); + out << indent << itemName << " = Qt::PopupMenu.new( self )" << endl; + out << indent << indent << parent << ".insertItem( @" << n.attribute( "name" ) << ".iconSet(),"; + out << trcall( itemText ) << ", " << itemName << " )" << endl; + trout << indent << parent << ".changeItem( " << parent << ".idAt( " << i << " ), "; + trout << trcall( itemText ) << " )" << endl; + createPopupMenuImpl( n2, parentClass, itemName ); + n = n2; + } else { + out << indent << "@" << n.attribute( "name" ) << ".addTo( " << parent << " )" << endl; + } + } else if ( n.tagName() == "separator" ) { + out << indent << parent << ".insertSeparator()" << endl; + } + ++i; + } +} + +/*! + Creates implementation of an listbox item tag. +*/ + +QString Uic::createListBoxItemImpl( const QDomElement &e, const QString &parent, + QString *value ) +{ + QDomElement n = e.firstChild().toElement(); + QString txt; + QString com; + QString pix; + while ( !n.isNull() ) { + if ( n.tagName() == "property" ) { + QString attrib = n.attribute("name"); + QVariant v = DomTool::elementToVariant( n.firstChild().toElement(), QVariant() ); + if ( attrib == "text" ) { + txt = v.toString(); + com = getComment( n ); + } else if ( attrib == "pixmap" ) { + pix = v.toString(); + if (!pix.isEmpty()) { + pix.prepend("@"); + } + if ( !pix.isEmpty() && !pixmapLoaderFunction.isEmpty() ) { + pix.prepend( pixmapLoaderFunction + "(" + QString( externPixmaps ? "\"" : "" ) ); + pix.append( QString( externPixmaps ? "\"" : "" ) + ")" ); + } + } + } + n = n.nextSibling().toElement(); + } + + if ( value ) + *value = trcall( txt, com ); + + if ( pix.isEmpty() ) + return parent + ".insertItem(" + trcall( txt, com ) + ")"; + + return parent + ".insertItem(" + pix + ", " + trcall( txt, com ) + ")"; +} + +/*! + Creates implementation of an iconview item tag. +*/ + +QString Uic::createIconViewItemImpl( const QDomElement &e, const QString &parent ) +{ + QDomElement n = e.firstChild().toElement(); + QString txt; + QString com; + QString pix; + while ( !n.isNull() ) { + if ( n.tagName() == "property" ) { + QString attrib = n.attribute("name"); + QVariant v = DomTool::elementToVariant( n.firstChild().toElement(), QVariant() ); + if ( attrib == "text" ) { + txt = v.toString(); + com = getComment( n ); + } else if ( attrib == "pixmap" ) { + pix = v.toString(); + if (!pix.isEmpty()) { + pix.prepend("@"); + } + if ( !pix.isEmpty() && !pixmapLoaderFunction.isEmpty() ) { + pix.prepend( pixmapLoaderFunction + "(" + QString( externPixmaps ? "\"" : "" ) ); + pix.append( QString( externPixmaps ? "\"" : "" ) + ")" ); + } + } + } + n = n.nextSibling().toElement(); + } + + if ( pix.isEmpty() ) + return "Qt::IconViewItem.new(" + parent + ", " + trcall( txt, com ) + ")"; + else + return "Qt::IconViewItem.new(" + parent + ", " + trcall( txt, com ) + ", " + pix + ")"; +} + +/*! + Creates implementation of an listview item tag. +*/ + +QString Uic::createListViewItemImpl( const QDomElement &e, const QString &parent, + const QString &parentItem ) +{ + QString s; + + QDomElement n = e.firstChild().toElement(); + + bool hasChildren = e.elementsByTagName( "item" ).count() > 0; + QString item; + + if ( hasChildren ) { + item = registerObject( "item" ); + s = indent + item + " = "; + } else { + if (! item_used) { + // This is here to make the ruby generated names for 'item', + // the same as the original C++ + item = registerObject( "item" ); + item_used = true; + } + item = "item"; + s = indent + item + " = "; + } + + if ( !parentItem.isEmpty() ) + s += "Qt::ListViewItem.new(" + parentItem + ", " + lastItem + ")\n"; + else + s += "Qt::ListViewItem.new(" + parent + ", " + lastItem + ")\n"; + + QStringList textes; + QStringList pixmaps; + while ( !n.isNull() ) { + if ( n.tagName() == "property" ) { + QString attrib = n.attribute("name"); + QVariant v = DomTool::elementToVariant( n.firstChild().toElement(), QVariant() ); + if ( attrib == "text" ) + textes << v.toString(); + else if ( attrib == "pixmap" ) { + QString pix = v.toString(); + if (!pix.isEmpty()) { + pix.prepend("@"); + } + if ( !pix.isEmpty() && !pixmapLoaderFunction.isEmpty() ) { + pix.prepend( pixmapLoaderFunction + "(" + QString( externPixmaps ? "\"" : "" ) ); + pix.append( QString( externPixmaps ? "\"" : "" ) + ")" ); + } + pixmaps << pix; + } + } else if ( n.tagName() == "item" ) { + s += indent + item + ".setOpen(true)\n"; + s += createListViewItemImpl( n, parent, item ); + } + n = n.nextSibling().toElement(); + } + + for ( int i = 0; i < (int)textes.count(); ++i ) { + if ( !textes[ i ].isEmpty() ) + s += indent + item + ".setText(" + QString::number( i ) + ", " + trcall( textes[ i ] ) + ")\n"; + if ( !pixmaps[ i ].isEmpty() ) + s += indent + item + ".setPixmap(" + QString::number( i ) + ", " + pixmaps[ i ] + ")\n"; + } + + lastItem = item; + return s; +} + +/*! + Creates implementation of an listview column tag. +*/ + +QString Uic::createListViewColumnImpl( const QDomElement &e, const QString &parent, + QString *value ) +{ + QDomElement n = e.firstChild().toElement(); + QString txt; + QString com; + QString pix; + bool clickable = false, resizeable = false; + while ( !n.isNull() ) { + if ( n.tagName() == "property" ) { + QString attrib = n.attribute("name"); + QVariant v = DomTool::elementToVariant( n.firstChild().toElement(), QVariant() ); + if ( attrib == "text" ) { + txt = v.toString(); + com = getComment( n ); + } else if ( attrib == "pixmap" ) { + pix = v.toString(); + if (!pix.isEmpty()) { + pix.prepend("@"); + } + if ( !pix.isEmpty() && !pixmapLoaderFunction.isEmpty() ) { + pix.prepend( pixmapLoaderFunction + "(" + QString( externPixmaps ? "\"" : "" ) ); + pix.append( QString( externPixmaps ? "\"" : "" ) + ")" ); + } + } else if ( attrib == "clickable" ) + clickable = v.toBool(); + else if ( attrib == "resizable" || attrib == "resizeable" ) + resizeable = v.toBool(); + } + n = n.nextSibling().toElement(); + } + + if ( value ) + *value = trcall( txt, com ); + + QString s; + s = indent + parent + ".addColumn(" + trcall( txt, com ) + ")\n"; + if ( !pix.isEmpty() ) + s += indent + parent + ".header().setLabel(" + parent + ".header().count() - 1," + pix + ", " + trcall( txt, com ) + ")\n"; + if ( !clickable ) + s += indent + parent + ".header().setClickEnabled( false, " + parent + ".header().count() - 1 )\n"; + if ( !resizeable ) + s += indent + parent + ".header().setResizeEnabled( false, " + parent + ".header().count() - 1 )\n"; + + return s; +} + +QString Uic::createTableRowColumnImpl( const QDomElement &e, const QString &parent, + QString *value ) +{ + QString objClass = getClassName( e.parentNode().toElement() ); + QDomElement n = e.firstChild().toElement(); + QString txt; + QString com; + QString pix; + QString field; + bool isRow = e.tagName() == "row"; + while ( !n.isNull() ) { + if ( n.tagName() == "property" ) { + QString attrib = n.attribute("name"); + QVariant v = DomTool::elementToVariant( n.firstChild().toElement(), QVariant() ); + if ( attrib == "text" ) { + txt = v.toString(); + com = getComment( n ); + } else if ( attrib == "pixmap" ) { + pix = v.toString(); + if ( !pix.isEmpty() && !pixmapLoaderFunction.isEmpty() ) { + pix.prepend( pixmapLoaderFunction + "(" + QString( externPixmaps ? "\"" : "" ) ); + pix.append( QString( externPixmaps ? "\"" : "" ) + ")" ); + } + } else if ( attrib == "field" ) + field = v.toString(); + } + n = n.nextSibling().toElement(); + } + + if ( value ) + *value = trcall( txt, com ); + + // ### This generated code sucks! We have to set the number of + // rows/cols before and then only do setLabel/() + // ### careful, though, since QDataTable has an API which makes this code pretty good + + QString s; + if ( isRow ) { + s = indent + parent + ".setNumRows(" + parent + ".numRows() + 1 )\n"; + if ( pix.isEmpty() ) + s += indent + parent + ".verticalHeader().setLabel(" + parent + ".numRows() - 1, " + + trcall( txt, com ) + ")\n"; + else + s += indent + parent + ".verticalHeader().setLabel(" + parent + ".numRows() - 1, Qt::IconSet.new(@" + + pix + " ), " + trcall( txt, com ) + ")\n"; + } else { + if ( objClass == "Qt::Table" ) { + s = indent + parent + ".setNumCols(" + parent + ".numCols() + 1)\n"; + if ( pix.isEmpty() ) + s += indent + parent + ".horizontalHeader().setLabel(" + parent + ".numCols() - 1, " + + trcall( txt, com ) + ")\n"; + else + s += indent + parent + ".horizontalHeader().setLabel(" + parent + ".numCols() - 1, Qt::IconSet.new(@" + + pix + " ), " + trcall( txt, com ) + ")\n"; + } else if ( objClass == "Qt::DataTable" ) { + if ( !txt.isEmpty() && !field.isEmpty() ) { + if ( pix.isEmpty() ) + out << indent << parent << ".addColumn(" << fixString( field ) << ", " << trcall( txt, com ) << ")" << endl; + else + out << indent << parent << ".addColumn(" << fixString( field ) << ", " << trcall( txt, com ) << ", Qt::IconSet.new(@" << pix << "))" << endl; + } + } + } + return s; +} + +/*! + Creates the implementation of a layout tag. Called from createObjectImpl(). + */ +QString Uic::createLayoutImpl( const QDomElement &e, const QString& parentClass, const QString& parent, const QString& layout ) +{ + QDomElement n; + QString objClass, objName; + objClass = e.tagName(); + + QString qlayout = "Qt::VBoxLayout.new"; + if ( objClass == "hbox" ) + qlayout = "Qt::HBoxLayout.new"; + else if ( objClass == "grid" ) + qlayout = "Qt::GridLayout.new"; + + bool isGrid = e.tagName() == "grid" ; + objName = registerObject( getLayoutName( e ) ); + layoutObjects += objName; + + QString margin = DomTool::readProperty( e, "margin", defMargin ).toString(); + QString spacing = DomTool::readProperty( e, "spacing", defSpacing ).toString(); + QString resizeMode = DomTool::readProperty( e, "resizeMode", QString::null ).toString(); + + QString optcells; + if ( isGrid ) + optcells = "1, 1, "; + if ( (parentClass == "Qt::GroupBox" || parentClass == "Qt::ButtonGroup") && layout.isEmpty() ) { + // special case for group box + out << indent << parent << ".setColumnLayout( 0, Qt::Vertical )" << endl; + out << indent << parent << ".layout().setSpacing(" << spacing << ")" << endl; + out << indent << parent << ".layout().setMargin(" << margin << ")" << endl; + out << indent << objName << " = " << qlayout << "(" << parent << ".layout() )" << endl; + out << indent << objName << ".setAlignment( AlignTop )" << endl; + } else { + out << indent << objName << " = " << qlayout << "("; + if ( layout.isEmpty() ) + out << parent; + else { + out << "nil"; + if ( !DomTool::hasProperty( e, "margin" ) ) + margin = "0"; + } + out << ", " << optcells << margin << ", " << spacing << ", '" << objName.mid(1) << "')" << endl; + } + if ( !resizeMode.isEmpty() ) + out << indent << objName << ".setResizeMode( Qt::Layout::" << resizeMode << " )" << endl; + + if ( !isGrid ) { + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + if ( n.tagName() == "spacer" ) { + QString child = createSpacerImpl( n, parentClass, parent, objName ); + out << indent << objName << ".addItem(" << child << ")" << endl; + } else if ( tags.contains( n.tagName() ) ) { + QString child = createObjectImpl( n, parentClass, parent, objName ); + if ( isLayout( child ) ) + out << indent << objName << ".addLayout(" << child << ")" << endl; + else + out << indent << objName << ".addWidget(" << child << ")" << endl; + } + } + } else { + for ( n = e.firstChild().toElement(); !n.isNull(); n = n.nextSibling().toElement() ) { + QDomElement ae = n; + int row = ae.attribute( "row" ).toInt(); + int col = ae.attribute( "column" ).toInt(); + int rowspan = ae.attribute( "rowspan" ).toInt(); + int colspan = ae.attribute( "colspan" ).toInt(); + if ( rowspan < 1 ) + rowspan = 1; + if ( colspan < 1 ) + colspan = 1; + if ( n.tagName() == "spacer" ) { + QString child = createSpacerImpl( n, parentClass, parent, objName ); + if ( rowspan * colspan != 1 ) + out << indent << objName << ".addMultiCell(" << child << ", " + << row << ", " << row + rowspan - 1 << ", " << col << ", " << col + colspan - 1 << ")" << endl; + else + out << indent << objName << ".addItem(" << child << ", " + << row << ", " << col << ")" << endl; + } else if ( tags.contains( n.tagName() ) ) { + QString child = createObjectImpl( n, parentClass, parent, objName ); + out << endl; + QString o = "Widget"; + if ( isLayout( child ) ) + o = "Layout"; + if ( rowspan * colspan != 1 ) + out << indent << objName << ".addMultiCell" << o << "(" << child << ", " + << row << ", " << row + rowspan - 1 << ", " << col << ", " << col + colspan - 1 << ")" << endl; + else + out << indent << objName << ".add" << o << "(" << child << ", " + << row << ", " << col << ")" << endl; + } + } + } + + return objName; +} + + + +QString Uic::createSpacerImpl( const QDomElement &e, const QString& /*parentClass*/, const QString& /*parent*/, const QString& /*layout*/) +{ + QDomElement n; + QString objClass, objName; + objClass = e.tagName(); + objName = registerObject( getObjectName( e ) ); + + QSize size = DomTool::readProperty( e, "sizeHint", QSize( 0, 0 ) ).toSize(); + QString sizeType = DomTool::readProperty( e, "sizeType", "Expanding" ).toString(); + bool isVspacer = DomTool::readProperty( e, "orientation", "Horizontal" ) == "Vertical"; + + if ( sizeType != "Expanding" && sizeType != "MinimumExpanding" && + DomTool::hasProperty( e, "geometry" ) ) { // compatibility Qt 2.2 + QRect geom = DomTool::readProperty( e, "geometry", QRect(0,0,0,0) ).toRect(); + size = geom.size(); + } + + if ( isVspacer ) + out << indent << objName << " = Qt::SpacerItem.new(" + << size.width() << ", " << size.height() + << ", Qt::SizePolicy::Minimum, Qt::SizePolicy::" << sizeType << ")" << endl; + else + out << indent << objName << " = Qt::SpacerItem.new(" + << size.width() << ", " << size.height() + << ", Qt::SizePolicy::" << sizeType << ", Qt::SizePolicy::Minimum)" << endl; + + return objName; +} + +static const char* const ColorRole[] = { + "Foreground", "Button", "Light", "Midlight", "Dark", "Mid", + "Text", "BrightText", "ButtonText", "Base", "Background", "Shadow", + "Highlight", "HighlightedText", "Link", "LinkVisited", 0 +}; + + +/*! + Creates a colorgroup with name \a name from the color group \a cg + */ +void Uic::createColorGroupImpl( const QString& name, const QDomElement& e ) +{ + QColorGroup cg; + int r = -1; + QDomElement n = e.firstChild().toElement(); + QString color; + while ( !n.isNull() ) { + if ( n.tagName() == "color" ) { + r++; + QColor col = DomTool::readColor( n ); + color = "Qt::Color.new(%1,%2,%3)"; + color = color.arg( col.red() ).arg( col.green() ).arg( col.blue() ); + if ( col == white ) + color = "white"; + else if ( col == black ) + color = "black"; + if ( n.nextSibling().toElement().tagName() != "pixmap" ) { + out << indent << name << ".setColor(Qt::ColorGroup::" << ColorRole[r] << ", " << color << ")" << endl; + } + } else if ( n.tagName() == "pixmap" ) { + QString pixmap = n.firstChild().toText().data(); + pixmap.prepend("@"); + if ( !pixmapLoaderFunction.isEmpty() ) { + pixmap.prepend( pixmapLoaderFunction + "(" + QString( externPixmaps ? "\"" : "" ) ); + pixmap.append( QString( externPixmaps ? "\"" : "" ) + ")" ); + } + out << indent << name << ".setBrush(Qt::ColorGroup::" + << ColorRole[r] << ", Qt::Brush.new(" << color << ", " << pixmap << "))" << endl; + } + n = n.nextSibling().toElement(); + } +} + +/*! + Auxiliary function to load a color group. The colorgroup must not + contain pixmaps. + */ +QColorGroup Uic::loadColorGroup( const QDomElement &e ) +{ + QColorGroup cg; + int r = -1; + QDomElement n = e.firstChild().toElement(); + QColor col; + while ( !n.isNull() ) { + if ( n.tagName() == "color" ) { + r++; + cg.setColor( (QColorGroup::ColorRole)r, (col = DomTool::readColor( n ) ) ); + } + n = n.nextSibling().toElement(); + } + return cg; +} + +/*! Returns TRUE if the widget properties specify that it belongs to + the database \a connection and \a table. +*/ + +bool Uic::isWidgetInTable( const QDomElement& e, const QString& connection, const QString& table ) +{ + QString conn = getDatabaseInfo( e, "connection" ); + QString tab = getDatabaseInfo( e, "table" ); + if ( conn == connection && tab == table ) + return true; + return false; +} + +/*! + Registers all database connections, cursors and forms. +*/ + +void Uic::registerDatabases( const QDomElement& e ) +{ + QDomElement n; + QDomNodeList nl; + int i; + nl = e.parentNode().toElement().elementsByTagName( "widget" ); + for ( i = 0; i < (int) nl.length(); ++i ) { + n = nl.item(i).toElement(); + QString conn = getDatabaseInfo( n, "connection" ); + QString tab = getDatabaseInfo( n, "table" ); + QString fld = getDatabaseInfo( n, "field" ); + if ( !conn.isNull() ) { + dbConnections += conn; + if ( !tab.isNull() ) { + dbCursors[conn] += tab; + if ( !fld.isNull() ) + dbForms[conn] += tab; + } + } + } +} + +/*! + Registers an object with name \a name. + + The returned name is a valid variable identifier, as similar to \a + name as possible and guaranteed to be unique within the form. + + \sa registeredName(), isObjectRegistered() + */ +QString Uic::registerObject( const QString& name ) +{ + if ( objectNames.isEmpty() ) { + // some temporary variables we need + objectNames += "img"; + objectNames += "item"; + objectNames += "cg"; + objectNames += "pal"; + } + QString result("@"); + result += name; + int i; + while ( ( i = result.find(' ' )) != -1 ) { + result[i] = '_'; + } + + if ( objectNames.contains( result ) ) { + int i = 2; + while ( objectNames.contains( result + "_" + QString::number(i) ) ) + i++; + result += "_"; + result += QString::number(i); + } + objectNames += result; + objectMapper.insert( name, result ); + return result; +} + +/*! + Returns the registered name for the original name \a name + or \a name if \a name wasn't registered. + + \sa registerObject(), isObjectRegistered() + */ +QString Uic::registeredName( const QString& name ) +{ + if ( !objectMapper.contains( name ) ) + return name; + return objectMapper[name]; +} + +/*! + Returns whether the object \a name was registered yet or not. + */ +bool Uic::isObjectRegistered( const QString& name ) +{ + return objectMapper.contains( name ); +} + + +/*! + Unifies the entries in stringlist \a list. Should really be a QStringList feature. + */ +QStringList Uic::unique( const QStringList& list ) +{ + QStringList result; + if ( list.isEmpty() ) + return result; + QStringList l = list; + l.sort(); + result += l.first(); + for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) { + if ( *it != result.last() ) + result += *it; + } + return result; +} + + + +/*! + Creates an instance of class \a objClass, with parent \a parent and name \a objName + */ +QString Uic::createObjectInstance( const QString& objClass, const QString& parent, const QString& objName ) +{ + + if ( objClass.mid( 4 ) == "ComboBox" ) { + return objClass + ".new(false, " + parent + ", \"" + objName.mid(1) + "\")"; + } + return objClass + ".new(" + parent + ", \"" + objName.mid(1) + "\")"; +} + +bool Uic::isLayout( const QString& name ) const +{ + return layoutObjects.contains( name ); +} diff --git a/qtruby/rubylib/designer/rbuic/uic.h b/qtruby/rubylib/designer/rbuic/uic.h new file mode 100644 index 00000000..707df590 --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/uic.h @@ -0,0 +1,181 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** Copyright (c) 2001 Phil Thompson <phil@river-bank.demon.co.uk> +** +** This file is part of Qt Designer. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef UIC_H +#define UIC_H +#include <qdom.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qmap.h> +#include <qtextstream.h> +#include <qpalette.h> + + +class RubyIndent +{ +public: + RubyIndent() : tabStop(4), current(0) {calc();} + + void setTabStop(uint n) {tabStop = n; calc();} + void operator++() {++current; calc();} + void operator--() {--current; calc();} + operator QString() {return indstr;} + +private: + uint tabStop; + uint current; + QString indstr; + + void calc(); +}; + + +class Uic : public Qt +{ +public: + Uic( const QString &fn, QTextStream& out, QDomDocument doc, bool subcl, + const QString &trm, const QString& subclname, bool omitForwardDecls, + QString &uicClass, bool useKDE ); + + static void setIndent(const RubyIndent &rubyind) {indent = rubyind;} + + void createFormImpl( const QDomElement &e ); + + void createSubImpl( const QDomElement &e, const QString& subclname ); + + void createObjectDecl( const QDomElement& e ); + void createAttrDecl( const QDomElement& e ); + void createActionDecl( const QDomElement& e ); + void createActionImpl( const QDomElement& e, const QString &parent ); + void createToolbarImpl( const QDomElement &e, const QString &parentClass, const QString &parent ); + void createMenuBarImpl( const QDomElement &e, const QString &parentClass, const QString &parent ); + void createPopupMenuImpl( const QDomElement &e, const QString &parentClass, const QString &parent ); + QString createObjectImpl( const QDomElement &e, const QString& parentClass, const QString& parent, const QString& layout = QString::null ); + QString createLayoutImpl( const QDomElement &e, const QString& parentClass, const QString& parent, const QString& layout = QString::null ); + QString createObjectInstance( const QString& objClass, const QString& parent, const QString& objName ); + QString createSpacerImpl( const QDomElement &e, const QString& parentClass, const QString& parent, const QString& layout = QString::null ); + void createExclusiveProperty( const QDomElement & e, const QString& exclusiveProp ); + QString createListBoxItemImpl( const QDomElement &e, const QString &parent, QString *value = 0 ); + QString createIconViewItemImpl( const QDomElement &e, const QString &parent ); + QString createListViewColumnImpl( const QDomElement &e, const QString &parent, QString *value = 0 ); + QString createTableRowColumnImpl( const QDomElement &e, const QString &parent, QString *value = 0 ); + QString createListViewItemImpl( const QDomElement &e, const QString &parent, + const QString &parentItem ); + void createColorGroupImpl( const QString& cg, const QDomElement& e ); + QColorGroup loadColorGroup( const QDomElement &e ); + + QDomElement getObjectProperty( const QDomElement& e, const QString& name ); + QString getPixmapLoaderFunction( const QDomElement& e ); + QString getFormClassName( const QDomElement& e ); + QString getClassName( const QDomElement& e ); + QString getObjectName( const QDomElement& e ); + QString getLayoutName( const QDomElement& e ); + QString getInclude( const QString& className ); + + QString setObjectProperty( const QString& objClass, const QString& obj, const QString &prop, const QDomElement &e, bool stdset ); + + QString registerObject( const QString& name ); + QString registeredName( const QString& name ); + bool isObjectRegistered( const QString& name ); + QStringList unique( const QStringList& ); + + QString trcall( const QString& sourceText, const QString& comment = "" ); + + static void embed( QTextStream& out, const char* project, const QStringList& images ); + + friend void getDBConnections(Uic& uic, QString& s); + static bool hasKDEwidget; + +private: + void registerLayouts ( const QDomElement& e ); + + QTextStream& out; + QTextOStream trout; + QString languageChangeBody; + QStringList objectNames; + QMap<QString,QString> objectMapper; + QStringList tags; + QStringList layouts; + QString formName; + QString lastItem; + QString trmacro; + + bool nofwd; + static RubyIndent indent; + + struct Buddy + { + Buddy( const QString& k, const QString& b ) + : key( k ), buddy( b ) {} + Buddy(){} // for valuelist + QString key; + QString buddy; + bool operator==( const Buddy& other ) const + { return (key == other.key); } + }; + struct CustomInclude + { + QString header; + QString location; + }; + QValueList<Buddy> buddies; + + QStringList layoutObjects; + bool isLayout( const QString& name ) const; + + uint item_used : 1; + uint cg_used : 1; + uint pal_used : 1; + uint stdsetdef : 1; + uint externPixmaps : 1; + + QString uiFileVersion; + QString nameOfClass; + QString pixmapLoaderFunction; + + void registerDatabases( const QDomElement& e ); + bool isWidgetInTable( const QDomElement& e, const QString& connection, const QString& table ); + bool isFrameworkCodeGenerated( const QDomElement& e ); + QString getDatabaseInfo( const QDomElement& e, const QString& tag ); + void createFormImpl( const QDomElement& e, const QString& form, const QString& connection, const QString& table ); + QStringList dbConnections; + QMap< QString, QStringList > dbCursors; + QMap< QString, QStringList > dbForms; + + static bool isMainWindow; + static QString mkBool( bool b ); + static QString mkBool( const QString& s ); + bool toBool( const QString& s ); + static QString fixString( const QString &str ); + static bool onlyAscii; + static QString mkStdSet( const QString& prop ); + static QString getComment( const QDomNode& n ); + int defSpacing, defMargin; + QString fileName; + bool writeSlotImpl; + + bool isEmptyFunction( const QString& fname ); + QMap<QString, QString> functionImpls; + + void rubySlot(QStringList::Iterator &it); +}; + +#endif diff --git a/qtruby/rubylib/designer/rbuic/widgetdatabase.cpp b/qtruby/rubylib/designer/rbuic/widgetdatabase.cpp new file mode 100644 index 00000000..c79815b1 --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/widgetdatabase.cpp @@ -0,0 +1,833 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#if !defined(UIC) +#include "../designer/pixmapchooser.h" +#endif +#include "widgetinterface.h" +#include "widgetdatabase.h" + +#include <qapplication.h> +#define NO_STATIC_COLORS +#include <globaldefs.h> +#include <qstrlist.h> +#include <qdict.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qcleanuphandler.h> + +#include <qfeatures.h> + +#include <stdlib.h> + +const int dbsize = 300; +const int dbcustom = 200; +const int dbdictsize = 211; +static WidgetDatabaseRecord* db[ dbsize ]; +static QDict<int> *className2Id = 0; +static int dbcount = 0; +static int dbcustomcount = 200; +static QStrList *wGroups; +static QStrList *invisibleGroups; +static bool whatsThisLoaded = false; +static QPluginManager<WidgetInterface> *widgetPluginManager = 0; +static bool plugins_set_up = false; +static bool was_in_setup = false; + +QCleanupHandler<QPluginManager<WidgetInterface> > cleanup_manager; + +WidgetDatabaseRecord::WidgetDatabaseRecord() +{ + isForm = false; + isContainer = false; + icon = 0; + nameCounter = 0; +} + +WidgetDatabaseRecord::~WidgetDatabaseRecord() +{ + delete icon; +} + + +/*! + \class WidgetDatabase widgetdatabase.h + \brief The WidgetDatabase class holds information about widgets + + The WidgetDatabase holds information about widgets like toolTip(), + iconSet(), ... It works Id-based, so all access functions take the + widget id as parameter. To get the id for a widget (classname), use + idFromClassName(). + + All access functions are static. Having multiple widgetdatabases in + one application doesn't make sense anyway and so you don't need more + than an instance of the widgetdatabase. + + For creating widgets, layouts, etc. see WidgetFactory. +*/ + +/*! + Creates widget database. Does nothing. +*/ + +WidgetDatabase::WidgetDatabase() +{ +} + +/*! Sets up the widget database. If the static widgetdatabase already + exists, the functions returns immediately. +*/ + +void WidgetDatabase::setupDataBase( int id ) +{ + was_in_setup = true; +#ifndef UIC + Q_UNUSED( id ) + if ( dbcount ) + return; +#else + if ( dbcount && id != -2 ) + return; + if ( dbcount && !plugins_set_up ) { + setupPlugins(); + return; + } + if ( dbcount && plugins_set_up) + return; +#endif + + wGroups = new QStrList; + invisibleGroups = new QStrList; + invisibleGroups->append( "Forms" ); + invisibleGroups->append( "Temp" ); + className2Id = new QDict<int>( dbdictsize ); + className2Id->setAutoDelete( true ); + + WidgetDatabaseRecord *r = 0; + + r = new WidgetDatabaseRecord; + r->iconSet = "pushbutton.xpm"; + r->name = "QPushButton"; + r->group = widgetGroup( "Buttons" ); + r->toolTip = "Push Button"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "toolbutton.xpm"; + r->name = "QToolButton"; + r->group = widgetGroup( "Buttons" ); + r->toolTip = "Tool Button"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "radiobutton.xpm"; + r->name = "QRadioButton"; + r->group = widgetGroup( "Buttons" ); + r->toolTip = "Radio Button"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "checkbox.xpm"; + r->name = "QCheckBox"; + r->group = widgetGroup( "Buttons" ); + r->toolTip = "Check Box"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "groupbox.xpm"; + r->name = "QGroupBox"; + r->group = widgetGroup( "Containers" ); + r->toolTip = "Group Box"; + r->isContainer = true; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "buttongroup.xpm"; + r->name = "QButtonGroup"; + r->group = widgetGroup( "Containers" ); + r->toolTip = "Button Group"; + r->isContainer = true; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "frame.xpm"; + r->name = "QFrame"; + r->group = widgetGroup( "Containers" ); + r->toolTip = "Frame"; + r->isContainer = true; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "tabwidget.xpm"; + r->name = "QTabWidget"; + r->group = widgetGroup( "Containers" ); + r->toolTip = "Tabwidget"; + r->isContainer = true; + + append( r ); + + + r = new WidgetDatabaseRecord; + r->iconSet = "listbox.xpm"; + r->name = "QListBox"; + r->group = widgetGroup( "Views" ); + r->toolTip = "List Box"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "listview.xpm"; + r->name = "QListView"; + r->group = widgetGroup( "Views" ); + r->toolTip = "List View"; + + append( r ); + +#if !defined(QT_NO_ICONVIEW) || defined(UIC) + r = new WidgetDatabaseRecord; + r->iconSet = "iconview.xpm"; + r->name = "QIconView"; + r->group = widgetGroup( "Views" ); + r->toolTip = "Icon View"; + + append( r ); +#endif + +#if !defined(QT_NO_TABLE) + r = new WidgetDatabaseRecord; + r->iconSet = "table.xpm"; + r->name = "QTable"; + r->group = widgetGroup( "Views" ); + r->toolTip = "Table"; + + append( r ); +#endif + +#if !defined(QT_NO_SQL) + r = new WidgetDatabaseRecord; + r->iconSet = "datatable.xpm"; + r->includeFile = "qdatatable.h"; + r->name = "QDataTable"; + r->group = widgetGroup( "Database" ); + r->toolTip = "Data Table"; + + append( r ); +#endif + + r = new WidgetDatabaseRecord; + r->iconSet = "lineedit.xpm"; + r->name = "QLineEdit"; + r->group = widgetGroup( "Input" ); + r->toolTip = "Line Edit"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "spinbox.xpm"; + r->name = "QSpinBox"; + r->group = widgetGroup( "Input" ); + r->toolTip = "Spin Box"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "dateedit.xpm"; + r->name = "QDateEdit"; + r->group = widgetGroup( "Input" ); + r->toolTip = "Date Edit"; + r->includeFile = "qdatetimeedit.h"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "timeedit.xpm"; + r->name = "QTimeEdit"; + r->group = widgetGroup( "Input" ); + r->toolTip = "Time Edit"; + r->includeFile = "qdatetimeedit.h"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "datetimeedit.xpm"; + r->name = "QDateTimeEdit"; + r->group = widgetGroup( "Input" ); + r->toolTip = "Date-Time Edit"; + r->includeFile = "qdatetimeedit.h"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "multilineedit.xpm"; + r->name = "QMultiLineEdit"; + r->group = widgetGroup( "Temp" ); + r->toolTip = "Multi Line Edit"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "richtextedit.xpm"; + r->name = "QTextEdit"; + r->group = widgetGroup( "Input" ); + r->toolTip = "Richtext Editor"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "combobox.xpm"; + r->name = "QComboBox"; + r->group = widgetGroup( "Input" ); + r->toolTip = "Combo Box"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "slider.xpm"; + r->name = "QSlider"; + r->group = widgetGroup( "Input" ); + r->toolTip = "Slider"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "scrollbar.xpm"; + r->name = "QScrollBar"; + r->group = widgetGroup( "Input" ); + r->toolTip = "Scrollbar"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "dial.xpm"; + r->name = "QDial"; + r->group = widgetGroup( "Input" ); + r->toolTip = "Dial"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "label.xpm"; + r->name = "QLabel"; + r->group = widgetGroup( "Temp" ); + r->toolTip = "Label"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "label.xpm"; + r->name = "TextLabel"; + r->group = widgetGroup( "Display" ); + r->toolTip = "Text Label"; + r->whatsThis = "The Text Label provides a widget to display static text."; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "pixlabel.xpm"; + r->name = "PixmapLabel"; + r->group = widgetGroup( "Display" ); + r->toolTip = "Pixmap Label"; + r->whatsThis = "The Pixmap Label provides a widget to display pixmaps."; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "lcdnumber.xpm"; + r->name = "QLCDNumber"; + r->group = widgetGroup( "Display" ); + r->toolTip = "LCD Number"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "line.xpm"; + r->name = "Line"; + r->group = widgetGroup( "Display" ); + r->toolTip = "Line"; + r->includeFile = "qframe.h"; + r->whatsThis = "The Line widget provides horizontal and vertical lines."; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "progress.xpm"; + r->name = "QProgressBar"; + r->group = widgetGroup( "Display" ); + r->toolTip = "Progress Bar"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "textview.xpm"; + r->name = "QTextView"; + r->group = widgetGroup( "Temp" ); + r->toolTip = "Text View"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "textbrowser.xpm"; + r->name = "QTextBrowser"; + r->group = widgetGroup( "Display" ); + r->toolTip = "Text Browser"; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "spacer.xpm"; + r->name = "Spacer"; + r->group = widgetGroup( "Temp" ); + r->toolTip = "Spacer"; + r->whatsThis = "The Spacer provides horizontal and vertical spacing to be able to manipulate the behaviour of layouts."; + + append( r ); + + r = new WidgetDatabaseRecord; + r->name = "QWidget"; + r->isForm = true; + r->group = widgetGroup( "Forms" ); + + append( r ); + + r = new WidgetDatabaseRecord; + r->name = "QDialog"; + r->group = widgetGroup( "Forms" ); + r->isForm = true; + + append( r ); + + r = new WidgetDatabaseRecord; + r->name = "QWizard"; + r->group = widgetGroup( "Forms" ); + r->isContainer = true; + + append( r ); + + r = new WidgetDatabaseRecord; + r->name = "QDesignerWizard"; + r->group = widgetGroup( "Forms" ); + r->isContainer = true; + + append( r ); + + r = new WidgetDatabaseRecord; + r->name = "QLayoutWidget"; + r->group = widgetGroup( "Temp" ); + r->includeFile = ""; + r->isContainer = true; + + append( r ); + + r = new WidgetDatabaseRecord; + r->name = "QSplitter"; + r->group = widgetGroup( "Temp" ); + r->includeFile = "qsplitter.h"; + r->isContainer = true; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "tabwidget.xpm"; + r->name = "QDesignerTabWidget"; + r->group = widgetGroup( "Temp" ); + r->isContainer = true; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "tabwidget.xpm"; + r->name = "QDesignerWidget"; + r->group = widgetGroup( "Temp" ); + r->isContainer = true; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = "tabwidget.xpm"; + r->name = "QDesignerDialog"; + r->group = widgetGroup( "Temp" ); + r->isContainer = true; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = ""; + r->name = "QMainWindow"; + r->includeFile = "qmainwindow.h"; + r->group = widgetGroup( "Temp" ); + r->isContainer = true; + + append( r ); + +#ifndef QT_NO_SQL + r = new WidgetDatabaseRecord; + r->iconSet = ""; + r->name = "QDataBrowser"; + r->includeFile = "qdatabrowser.h"; + r->group = widgetGroup( "Database" ); + r->toolTip = "Data Browser"; + r->iconSet = "databrowser.xpm"; + r->isContainer = true; + + append( r ); + + r = new WidgetDatabaseRecord; + r->iconSet = ""; + r->name = "QDataView"; + r->includeFile = "qdataview.h"; + r->group = widgetGroup( "Database" ); + r->toolTip = "Data View"; + r->iconSet = "dataview.xpm"; + r->isContainer = true; + + append( r ); +#endif + +#ifndef UIC + setupPlugins(); +#endif +} + +void WidgetDatabase::setupPlugins() +{ + if ( plugins_set_up ) + return; + plugins_set_up = true; + QStringList widgets = widgetManager()->featureList(); + for ( QStringList::Iterator it = widgets.begin(); it != widgets.end(); ++it ) { + if ( hasWidget( *it ) ) + continue; + WidgetDatabaseRecord *r = new WidgetDatabaseRecord; + WidgetInterface *iface = 0; + widgetManager()->queryInterface( *it, &iface ); + if ( !iface ) + continue; + +#ifndef UIC + QIconSet icon = iface->iconSet( *it ); + if ( !icon.pixmap().isNull() ) + r->icon = new QIconSet( icon ); +#endif + QString grp = iface->group( *it ); + if ( grp.isEmpty() ) + grp = "3rd party widgets"; + r->group = widgetGroup( grp ); + r->toolTip = iface->toolTip( *it ); + r->whatsThis = iface->whatsThis( *it ); + r->includeFile = iface->includeFile( *it ); + r->isContainer = iface->isContainer( *it ); + r->name = *it; + append( r ); + iface->release(); + } +} + +/*! + Returns the number of elements in the widget database. +*/ + +int WidgetDatabase::count() +{ + setupDataBase( -1 ); + return dbcount; +} + +/*! + Returns the id at which the ids of custom widgets start. +*/ + +int WidgetDatabase::startCustom() +{ + setupDataBase( -1 ); + return dbcustom; +} + +/*! + Returns the iconset which represents the class registered as \a id. +*/ + +QIconSet WidgetDatabase::iconSet( int id ) +{ + setupDataBase( id ); + WidgetDatabaseRecord *r = at( id ); + if ( !r ) + return QIconSet(); +#if !defined(UIC) && !defined(RESOURCE) + if ( !r->icon ) + r->icon = new QIconSet( PixmapChooser::loadPixmap( r->iconSet, PixmapChooser::Small ), + PixmapChooser::loadPixmap( r->iconSet, PixmapChooser::Large ) ); + return *r->icon; +#else + return QIconSet(); +#endif +} + +/*! + Returns the classname of the widget which is registered as \a id. +*/ + +QString WidgetDatabase::className( int id ) +{ + setupDataBase( id ); + WidgetDatabaseRecord *r = at( id ); + if ( !r ) + return QString::null; + return r->name; +} + +/*! + Returns the group to which the widget registered as \a id belongs. +*/ + +QString WidgetDatabase::group( int id ) +{ + setupDataBase( id ); + WidgetDatabaseRecord *r = at( id ); + if ( !r ) + return QString::null; + return r->group; +} + +/*! Returns the tooltip text of the widget which is registered as \a + id. +*/ + +QString WidgetDatabase::toolTip( int id ) +{ + setupDataBase( id ); + WidgetDatabaseRecord *r = at( id ); + if ( !r ) + return QString::null; + return r->toolTip; +} + +/*! Returns the what's this? test of the widget which is registered + as \a id. +*/ + +QString WidgetDatabase::whatsThis( int id ) +{ + setupDataBase( id ); + WidgetDatabaseRecord *r = at( id ); + if ( !r ) + return QString::null; + return r->whatsThis; +} + +/*! + Returns the include file if the widget which is registered as \a id. +*/ + +QString WidgetDatabase::includeFile( int id ) +{ + setupDataBase( id ); + WidgetDatabaseRecord *r = at( id ); + if ( !r ) + return QString::null; + if ( r->includeFile.isNull() ) + return r->name.lower() + ".h"; + return r->includeFile; +} + +/*! Returns wheather the widget registered as \a id is a form +*/ +bool WidgetDatabase::isForm( int id ) +{ + setupDataBase( id ); + WidgetDatabaseRecord *r = at( id ); + if ( !r ) + return false; + return r->isForm; +} + +/*! Returns wheather the widget registered as \a id is a container + (can have children) or not. +*/ + +bool WidgetDatabase::isContainer( int id ) +{ + setupDataBase( id ); + WidgetDatabaseRecord *r = at( id ); + if ( !r ) + return false; + return r->isContainer || r->isForm; +} + +QString WidgetDatabase::createWidgetName( int id ) +{ + setupDataBase( id ); + QString n = className( id ); + if ( n == "QLayoutWidget" ) + n = "Layout"; + if ( n[ 0 ] == 'Q' ) + n = n.mid( 1 ); + WidgetDatabaseRecord *r = at( id ); + if ( !r ) + return n; + n += QString::number( ++r->nameCounter ); + return n; +} + +/*! Returns the id for \a name or -1 if \a name is unknown. + */ +int WidgetDatabase::idFromClassName( const QString &name ) +{ + setupDataBase( -1 ); + if ( name.isEmpty() ) + return 0; + int *i = className2Id->find( name ); + if ( i ) + return *i; + if ( name == "FormWindow" ) + return idFromClassName( "QLayoutWidget" ); +#ifdef UIC +#ifndef NO_UI_PLUGINS + setupDataBase( -2 ); + i = className2Id->find( name ); + if ( i ) + return *i; +#endif +#endif + return -1; +} + +bool WidgetDatabase::hasWidget( const QString &name ) +{ + return className2Id->find( name ) != 0; +} + +WidgetDatabaseRecord *WidgetDatabase::at( int index ) +{ + if ( index < 0 ) + return 0; + if ( index >= dbcustom && index < dbcustomcount ) + return db[ index ]; + if ( index < dbcount ) + return db[ index ]; + return 0; +} + +void WidgetDatabase::insert( int index, WidgetDatabaseRecord *r ) +{ + if ( index < 0 || index >= dbsize ) + return; + db[ index ] = r; + className2Id->insert( r->name, new int( index ) ); + if ( index < dbcustom ) + dbcount = QMAX( dbcount, index ); +} + +void WidgetDatabase::append( WidgetDatabaseRecord *r ) +{ + if ( !was_in_setup ) + setupDataBase( -1 ); + insert( dbcount++, r ); +} + +QString WidgetDatabase::widgetGroup( const QString &g ) +{ + if ( wGroups->find( g ) == -1 ) + wGroups->append( g ); + return g; +} + +bool WidgetDatabase::isGroupEmpty( const QString &grp ) +{ + WidgetDatabaseRecord *r = 0; + for ( int i = 0; i < dbcount; ++i ) { + if ( !( r = db[ i ] ) ) + continue; + if ( r->group == grp ) + return false; + } + return true; +} + +QString WidgetDatabase::widgetGroup( int i ) +{ + setupDataBase( -1 ); + if ( i >= 0 && i < (int)wGroups->count() ) + return wGroups->at( i ); + return QString::null; +} + +int WidgetDatabase::numWidgetGroups() +{ + setupDataBase( -1 ); + return wGroups->count(); +} + +bool WidgetDatabase::isGroupVisible( const QString &g ) +{ + setupDataBase( -1 ); + return invisibleGroups->find( g ) == -1; +} + +int WidgetDatabase::addCustomWidget( WidgetDatabaseRecord *r ) +{ + insert( dbcustomcount++, r ); + return dbcustomcount - 1; +} + +bool WidgetDatabase::isCustomWidget( int id ) +{ + if ( id >= dbcustom && id < dbcustomcount ) + return true; + return false; +} + +bool WidgetDatabase::isWhatsThisLoaded() +{ + return whatsThisLoaded; +} + +void WidgetDatabase::loadWhatsThis( const QString &docPath ) +{ + QString whatsthisFile = docPath + "/whatsthis"; + QFile f( whatsthisFile ); + if ( !f.open( IO_ReadOnly ) ) + return; + QTextStream ts( &f ); + while ( !ts.atEnd() ) { + QString s = ts.readLine(); + QStringList l = QStringList::split( " | ", s ); + int id = idFromClassName( l[ 1 ] ); + WidgetDatabaseRecord *r = at( id ); + if ( r ) + r->whatsThis = l[ 0 ]; + } + whatsThisLoaded = true; +} + +QPluginManager<WidgetInterface> *widgetManager() +{ + if ( !widgetPluginManager ) { + widgetPluginManager = new QPluginManager<WidgetInterface>( IID_Widget, QApplication::libraryPaths(), "/designer" ); + cleanup_manager.add( &widgetPluginManager ); + } + return widgetPluginManager; +} diff --git a/qtruby/rubylib/designer/rbuic/widgetdatabase.h b/qtruby/rubylib/designer/rbuic/widgetdatabase.h new file mode 100644 index 00000000..efe585ab --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/widgetdatabase.h @@ -0,0 +1,85 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef WIDGETDATABASE_H +#define WIDGETDATABASE_H + +#include <qiconset.h> +#include <qstring.h> +#include "widgetinterface.h" // up here for GCC 2.7.* compatibility +#include <private/qpluginmanager_p.h> + + +extern QPluginManager<WidgetInterface> *widgetManager(); + +struct WidgetDatabaseRecord +{ + WidgetDatabaseRecord(); + ~WidgetDatabaseRecord(); + QString iconSet, name, group, toolTip, whatsThis, includeFile; + uint isContainer : 1; + uint isForm : 1; + QIconSet *icon; + int nameCounter; +}; + +class WidgetDatabase : public Qt +{ +public: + WidgetDatabase(); + static void setupDataBase( int id ); + static void setupPlugins(); + + static int count(); + static int startCustom(); + + static QIconSet iconSet( int id ); + static QString className( int id ); + static QString group( int id ); + static QString toolTip( int id ); + static QString whatsThis( int id ); + static QString includeFile( int id ); + static bool isForm( int id ); + static bool isContainer( int id ); + + static int idFromClassName( const QString &name ); + static QString createWidgetName( int id ); + + static WidgetDatabaseRecord *at( int index ); + static void insert( int index, WidgetDatabaseRecord *r ); + static void append( WidgetDatabaseRecord *r ); + + static QString widgetGroup( const QString &g ); + static QString widgetGroup( int i ); + static int numWidgetGroups(); + static bool isGroupVisible( const QString &g ); + static bool isGroupEmpty( const QString &grp ); + + static int addCustomWidget( WidgetDatabaseRecord *r ); + static bool isCustomWidget( int id ); + + static bool isWhatsThisLoaded(); + static void loadWhatsThis( const QString &docPath ); + + static bool hasWidget( const QString &name ); + +}; + +#endif diff --git a/qtruby/rubylib/designer/rbuic/widgetinterface.h b/qtruby/rubylib/designer/rbuic/widgetinterface.h new file mode 100644 index 00000000..bf6bcd55 --- /dev/null +++ b/qtruby/rubylib/designer/rbuic/widgetinterface.h @@ -0,0 +1,29 @@ + /********************************************************************** +** Copyright (C) 2000-2001 Trolltech AS. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef WIDGETINTERFACE_H +#define WIDGETINTERFACE_H + +#include <private/qwidgetinterface_p.h> + +#define WidgetInterface QWidgetFactoryInterface +#define IID_Widget IID_QWidgetFactory + +#endif diff --git a/qtruby/rubylib/designer/uilib/Makefile.am b/qtruby/rubylib/designer/uilib/Makefile.am new file mode 100644 index 00000000..e7f21bea --- /dev/null +++ b/qtruby/rubylib/designer/uilib/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES = -I$(top_srcdir)/smoke -I$(top_srcdir)/qtruby/rubylib/qtruby $(all_includes) -I$(RUBY_ARCHDIR) + +rubylibdir = $(RUBY_SITEARCHDIR) +rubylib_LTLIBRARIES = qui.la +qui_la_SOURCES = qui.cpp +qui_la_LDFLAGS = -module -export-dynamic $(all_libraries) -version-info 0:0:0 +qui_la_LIBADD = -lqui
\ No newline at end of file diff --git a/qtruby/rubylib/designer/uilib/extconf.rb b/qtruby/rubylib/designer/uilib/extconf.rb new file mode 100644 index 00000000..fa742b9a --- /dev/null +++ b/qtruby/rubylib/designer/uilib/extconf.rb @@ -0,0 +1,6 @@ +require 'mkmf' +dir_config('smoke') +dir_config('qt') +$CPPFLAGS += " -I../../../../smoke -I../../qtruby " +$LOCAL_LIBS += '-bundle_loader ../../qtruby/qtruby.bundle -lsmokeqt -lqui -lqt-mt -lstdc++' +create_makefile("qui") diff --git a/qtruby/rubylib/designer/uilib/qui.cpp b/qtruby/rubylib/designer/uilib/qui.cpp new file mode 100644 index 00000000..9aa7513f --- /dev/null +++ b/qtruby/rubylib/designer/uilib/qui.cpp @@ -0,0 +1,175 @@ +/*************************************************************************** + qui.cpp - A ruby wrapper for the QWidgetFactory class + ------------------- + begin : Wed Mar 14 2004 + copyright : (C) 2004 by Richard Dale + email : Richard_Dale@tipitina.demon.co.uk + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include <qwidgetfactory.h> +#include <qwidget.h> + +#include "smoke.h" + +#undef DEBUG +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#ifndef __USE_POSIX +#define __USE_POSIX +#endif +#ifndef __USE_XOPEN +#define __USE_XOPEN +#endif +#include <ruby.h> + +#include "qtruby.h" +#include "smokeruby.h" + +extern Smoke *qt_Smoke; +extern bool isDerivedFrom(Smoke *smoke, Smoke::Index classId, Smoke::Index baseId); + +extern "C" { +extern VALUE set_obj_info(const char * className, smokeruby_object * o); + +static VALUE qui_module; +static VALUE qwidget_factory_class; + +static VALUE +create(int argc, VALUE * argv, VALUE /*klass*/) +{ + QWidget * topLevelWidget = 0; + VALUE result = Qnil; + + if (argc == 0 || argc > 4) { + rb_raise(rb_eArgError, "wrong number of arguments (%d)\n", argc); + } + + QObject * connector = 0; + if (argc >= 2) { + if (TYPE(argv[1]) == T_DATA) { + smokeruby_object *o = value_obj_info(argv[1]); + if (o != 0) { + connector = (QObject *) o->ptr; + } + } else { + rb_raise(rb_eArgError, "invalid argument type\n"); + } + } + + QWidget * parent = 0; + if (argc >= 3) { + if (TYPE(argv[2]) == T_DATA) { + smokeruby_object *o = value_obj_info(argv[2]); + if (o != 0) { + parent = (QWidget *) o->ptr; + } + } else { + rb_raise(rb_eArgError, "invalid argument type\n"); + } + } + + const char * name = 0; + if (argc >= 4) { + if (TYPE(argv[3]) == T_STRING) { + name = StringValuePtr(argv[3]); + } else { + rb_raise(rb_eArgError, "invalid argument type\n"); + } + } + + if (TYPE(argv[0]) == T_STRING) { + topLevelWidget = QWidgetFactory::create(QString(StringValuePtr(argv[0])), connector, parent, name); + } else if (TYPE(argv[0]) == T_DATA) { + QIODevice * dev = 0; + smokeruby_object *o = value_obj_info(argv[0]); + + if (o != 0 && o->ptr != 0 && o->classId == qt_Smoke->idClass("QIODevice")) { + dev = (QIODevice *) o->ptr; + } else { + rb_raise(rb_eArgError, "invalid argument type\n"); + } + + topLevelWidget = QWidgetFactory::create(dev, connector, parent, name); + } else { + rb_raise(rb_eArgError, "invalid argument type\n"); + } + + if (topLevelWidget != 0) { + smokeruby_object * o = (smokeruby_object *) malloc(sizeof(smokeruby_object)); + o->smoke = qt_Smoke; + o->classId = qt_Smoke->idClass(topLevelWidget->className()); + o->ptr = topLevelWidget; + o->allocated = false; + + const char * className = qt_Smoke->binding->className(o->classId); + result = set_obj_info(className, o); + } + + return result; +} + +static VALUE +load_images(VALUE klass, VALUE dir) +{ + QWidgetFactory::loadImages(QString(StringValuePtr(dir))); + return klass; +} + +static VALUE +widgets(VALUE /*self*/) +{ + VALUE result = rb_ary_new(); + QStringList widgetList = QWidgetFactory::widgets(); + + for (QStringList::Iterator it = widgetList.begin(); it != widgetList.end(); ++it) { + QString widgetName = *it; + if (widgetName.startsWith("Q")) { + widgetName.replace(0, 1, QString("Qt::")); + } else if (widgetName.startsWith("K")) { + widgetName.replace(0, 1, QString("KDE::")); + } + rb_ary_push(result, rb_str_new2(widgetName.latin1())); + } + + return result; +} + +static VALUE +supports_widget(VALUE /*self*/, VALUE widget) +{ + QString widgetName(StringValuePtr(widget)); + + if (widgetName.startsWith("Qt::")) { + widgetName.replace(0, 4, QString("Q")); + } else if (widgetName.startsWith("KDE::")) { + widgetName.replace(0, 5, QString("K")); + } + + return QWidgetFactory::supportsWidget(widgetName) ? Qtrue : Qfalse; +} + +void +Init_qui() +{ + qui_module = rb_define_module("QUI"); + qwidget_factory_class = rb_define_class_under(qui_module, "WidgetFactory", rb_cObject); + + rb_define_singleton_method(qwidget_factory_class, "create", (VALUE (*) (...)) create, -1); + rb_define_singleton_method(qwidget_factory_class, "loadImages", (VALUE (*) (...)) load_images, 1); + rb_define_singleton_method(qwidget_factory_class, "load_images", (VALUE (*) (...)) load_images, 1); + rb_define_singleton_method(qwidget_factory_class, "widgets", (VALUE (*) (...)) widgets, 0); + rb_define_singleton_method(qwidget_factory_class, "supportsWidget", (VALUE (*) (...)) supports_widget, 1); + rb_define_singleton_method(qwidget_factory_class, "supports_widget", (VALUE (*) (...)) supports_widget, 1); +} + +}; diff --git a/qtruby/rubylib/designer/uilib/test/test.rb b/qtruby/rubylib/designer/uilib/test/test.rb new file mode 100644 index 00000000..628f1270 --- /dev/null +++ b/qtruby/rubylib/designer/uilib/test/test.rb @@ -0,0 +1,20 @@ +require 'Qt'
+require 'qui'
+
+a = Qt::Application.new(ARGV)
+if ARGV.length == 0
+ puts "Usage: test.rb <image dir> <ui file>"
+ exit
+end
+
+if ARGV.length == 2
+ QUI::WidgetFactory.loadImages( ARGV[ 0 ] )
+ w = QUI::WidgetFactory.create( ARGV[ 1 ] )
+ if w.nil?
+ puts "Failed to create top level widget"
+ exit
+ end
+ w.show()
+ a.connect( a, SIGNAL('lastWindowClosed()'), a, SLOT('quit()') )
+ a.exec()
+end
diff --git a/qtruby/rubylib/examples/base/kicons.rb b/qtruby/rubylib/examples/base/kicons.rb new file mode 100644 index 00000000..0d0a2c01 --- /dev/null +++ b/qtruby/rubylib/examples/base/kicons.rb @@ -0,0 +1,54 @@ +class KIconCollection + IconInfo = Struct.new(:collection, :id, :filetype) + def initialize(icon_collections) + @icon_info = {} + icon_collections.each_pair { + |collection_name, collection| + collection.each_pair { + |key, value| + info = IconInfo.new(collection_name, value, "png") + @icon_info[key] = info + } + } + end + def dims + "32x32" + end + def kdedir + ENV["KDEDIR"] + end + def get_icon_path(icon_type) + info = @icon_info[icon_type] + "#{kdedir}/share/icons/default.kde/#{dims}/#{info.collection}/#{info.id}.#{info.filetype}" + end + def get_icon_set(icon_type) + path = get_icon_path(icon_type) + pixmap = Qt::Pixmap.new(path) + icon_set = Qt::IconSet.new + icon_set.setPixmap(pixmap, Qt::IconSet.Small) + icon_set + end + def make_qt_action(parent, text_with_accel, icon_type) + act = Qt::Action.new(parent) + act.setIconSet(get_icon_set(icon_type)) + act.setMenuText(text_with_accel) + act + end +end + +module Icons + FILE_NEW, FILE_OPEN, FILE_CLOSE, FILE_SAVE, FILE_SAVE_AS, EXIT = 1,2,3,4,5,6 +end + +icon_collections = { + "actions" => { + Icons::FILE_NEW => "filenew", + Icons::FILE_OPEN => "fileopen", + Icons::FILE_CLOSE => "fileclose", + Icons::FILE_SAVE => "filesave", + Icons::FILE_SAVE_AS => "filesaveas", + Icons::EXIT => "exit" + } +} +$kIcons = KIconCollection.new(icon_collections) +print "Using KDEDIR == ", $kIcons.kdedir, "\n" diff --git a/qtruby/rubylib/examples/base/rui.rb b/qtruby/rubylib/examples/base/rui.rb new file mode 100644 index 00000000..ad14bc11 --- /dev/null +++ b/qtruby/rubylib/examples/base/rui.rb @@ -0,0 +1,21 @@ +require '../base/kicons.rb' + +RAction = Struct.new(:text_with_accel, :icon_type, :rec, :slot, :included_in, :action) +RSeperator = Struct.new(:included_in, :id) + +def build_actions(actions) + actions.each { |a| + if a.is_a? RSeperator + a.included_in.each { + |to| a.id = to.insertSeparator() + } + else + qt_action = $kIcons.make_qt_action(self, a.text_with_accel, a.icon_type) + connect(qt_action, SIGNAL('activated()'), a.rec, a.slot) + a.included_in.each { + |to| qt_action.addTo(to) + } + a.action = qt_action + end + } +end diff --git a/qtruby/rubylib/examples/canvastest/canvastest.rb b/qtruby/rubylib/examples/canvastest/canvastest.rb new file mode 100644 index 00000000..4187d91d --- /dev/null +++ b/qtruby/rubylib/examples/canvastest/canvastest.rb @@ -0,0 +1,75 @@ +#!/usr/bin/env ruby -w + +require 'Qt' +require 'rexml/document' + +require '../base/kicons.rb' +require '../base/rui.rb' + +class MyCanvasView < Qt::CanvasView + def initialize(canvas, parent) + @canvas = canvas + super(canvas, parent) + end + def contentsMousePressEvent(e) + super + list = canvas.collisions(e.pos) + return if list.empty? + c = list.first + return if c.rtti != Qt::CanvasItem::Rtti_Rectangle + c.hide + @canvas.update + end +end + +class MyWidget < Qt::MainWindow + slots 'new()', 'open()', 'save_as()' + def make_rect + rect = Qt::CanvasRectangle.new(rand(@canvas.width()), rand(@canvas.height()), + @canvas.width / 5, @canvas.width / 5, @canvas) + z = rand(256) + color = Qt::Color.new(z,z,z) + rect.setBrush(Qt::Brush.new(color)) + color = Qt::Color.new(rand(32)*8, rand(32)*8, rand(32)*8) + rect.setPen(Qt::Pen.new(color, 6)) + rect.setZ(z) + rect.show + @rects << rect + end + def initialize() + super + + fileTools = Qt::ToolBar.new(self, "file operations") + fileMenu = Qt::PopupMenu.new(self) + + actions = [ + RAction.new("&New", Icons::FILE_NEW, self, SLOT('new()'), [fileTools, fileMenu]), + RAction.new("&Open...", Icons::FILE_OPEN, self, SLOT('open()'), [fileTools, fileMenu]), + @save = RAction.new("Save &As...", Icons::FILE_SAVE_AS, self, SLOT('save_as()'), [fileTools, fileMenu]), + RSeperator.new([fileMenu]), + RAction.new("E&xit", Icons::EXIT, $qApp, SLOT('quit()'), [fileMenu]) + ] + build_actions(actions) + + menubar = Qt::MenuBar.new(self) + menubar.insertItem("&File", fileMenu) + + @canvas = Qt::Canvas.new(640, 480) + + @rects = [] + 5.times { make_rect } + + @canvas_view = MyCanvasView.new(@canvas, self) + self.setCentralWidget(@canvas_view) + @canvas.update + end +end + +a = Qt::Application.new(ARGV) + +w = MyWidget.new +w.show + +a.setMainWidget(w) +a.exec() +exit diff --git a/qtruby/rubylib/examples/killerfilter/killerfilter.rb b/qtruby/rubylib/examples/killerfilter/killerfilter.rb new file mode 100644 index 00000000..647dc079 --- /dev/null +++ b/qtruby/rubylib/examples/killerfilter/killerfilter.rb @@ -0,0 +1,56 @@ +#!/usr/bin/env ruby -w + +# This is the EventFilter example from Chapter 16 of 'Programming with Qt' + +require 'Qt' + +class KillerFilter < Qt::Object + + def eventFilter( object, event ) + if event.type() == Qt::Event::MouseButtonPress + if event.button() == RightButton + object.close(false) + return true + else + return false + end + else + return false + end + end + +end + +a = Qt::Application.new(ARGV) + +toplevel = Qt::Widget.new +toplevel.resize(230, 130) + +killerfilter = KillerFilter.new + +pb = Qt::PushButton.new(toplevel) +pb.setGeometry( 10, 10, 100, 50 ) +pb.text = "pushbutton" +pb.installEventFilter(killerfilter) + +le = Qt::LineEdit.new(toplevel) +le.setGeometry( 10, 70, 100, 50 ) +le.text = "Line edit" +le.installEventFilter(killerfilter) + +cb = Qt::CheckBox.new(toplevel) +cb.setGeometry( 120, 10, 100, 50 ) +cb.text = "Check-box" +cb.installEventFilter(killerfilter) + +rb = Qt::RadioButton.new(toplevel) +rb.setGeometry( 120, 70, 100, 50 ) +rb.text = "Radio button" +rb.installEventFilter(killerfilter) + +a.mainWidget = toplevel +toplevel.show +a.exec + + +
\ No newline at end of file diff --git a/qtruby/rubylib/examples/network/clientserver/client/client.rb b/qtruby/rubylib/examples/network/clientserver/client/client.rb new file mode 100644 index 00000000..1f16b0ca --- /dev/null +++ b/qtruby/rubylib/examples/network/clientserver/client/client.rb @@ -0,0 +1,88 @@ +require 'Qt' + +class Client < Qt::VBox + + def initialize( host, port ) + super() + # GUI layout + @infoText = Qt::TextView.new( self ) + hb = Qt::HBox.new( self ) + @inputText = Qt::LineEdit.new( hb ) + send = Qt::PushButton.new( tr("Send") , hb ) + close = Qt::PushButton.new( tr("Close connection") , self ) + quit = Qt::PushButton.new( tr("Quit") , self ) + + connect( send, SIGNAL('clicked()'), SLOT('sendToServer()') ) + connect( close, SIGNAL('clicked()'), SLOT('closeConnection()') ) + connect( quit, SIGNAL('clicked()'), $qApp, SLOT('quit()') ) + + # create the socket and connect various of its signals + @socket = Qt::Socket.new( self ) + connect( @socket, SIGNAL('connected()'), + SLOT('socketConnected()') ) + connect( @socket, SIGNAL('connectionClosed()'), + SLOT('socketConnectionClosed()') ) + connect( @socket, SIGNAL('readyRead()'), + SLOT('socketReadyRead()') ) + connect( @socket, SIGNAL('error(int)'), + SLOT('socketError(int)') ) + + # connect to the server + @infoText.append( tr("Trying to connect to the server\n") ) + @socket.connectToHost( host, port ) + end + + slots 'closeConnection()', 'sendToServer()', + 'socketReadyRead()', 'socketConnected()', + 'socketConnectionClosed()', 'socketClosed()', + 'socketError(int)' + + def closeConnection() + @socket.close() + if @socket.state() == Qt::Socket::Closing + # We have a delayed close. + connect( @socket, SIGNAL('delayedCloseFinished()'), + SLOT('socketClosed()') ) + else + # The socket is closed. + socketClosed() + end + end + + def sendToServer() + # write to the server + os = Qt::TextStream.new(@socket) + os << @inputText.text() << "\n" + @inputText.setText( "" ) + os.dispose() + end + + def socketReadyRead() + # read from the server + while @socket.canReadLine() do + @infoText.append( @socket.readLine() ) + end + end + + def socketConnected() + @infoText.append( tr("Connected to server\n") ) + end + + def socketConnectionClosed() + @infoText.append( tr("Connection closed by the server\n") ) + end + + def socketClosed() + @infoText.append( tr("Connection closed\n") ) + end + + def socketError( e ) + @infoText.append( tr("Error number %d occurred\n" % e) ) + end +end + +app = Qt::Application.new( ARGV ) +client = Client.new( ARGV.length < 1 ? "localhost" : ARGV[0], 4242 ) +app.mainWidget = client +client.show +app.exec diff --git a/qtruby/rubylib/examples/network/clientserver/server/server.rb b/qtruby/rubylib/examples/network/clientserver/server/server.rb new file mode 100644 index 00000000..d8a937f4 --- /dev/null +++ b/qtruby/rubylib/examples/network/clientserver/server/server.rb @@ -0,0 +1,115 @@ +require 'Qt' + +=begin + The ClientSocket class provides a socket that is connected with a client. + For every client that connects to the server, the server creates a new + instance of this class. +=end +class ClientSocket < Qt::Socket + def initialize(sock, parent=nil, name=nil) + super( parent, name ) + @line = 0 + connect( self, SIGNAL('readyRead()'), + SLOT('readClient()') ) + connect( self, SIGNAL('connectionClosed()'), + SLOT('deleteLater()') ) + setSocket( sock ) + end + + signals 'logText(const QString&)' + + slots 'readClient()' + + def readClient() + ts = Qt::TextStream.new( self ) + while canReadLine() do + str = ts.readLine() + emit logText( tr("Read: '%s'\n" % str) ) + + ts << @line << ": " << str + # 'endl' needs to be called like this in ruby + endl(ts) + emit logText( tr("Wrote: '%d: %s'\n" % [@line, str]) ) + + @line += 1 + end + ts.dispose() + end +end + + +=begin + The SimpleServer class handles new connections to the server. For every + client that connects, it creates a new ClientSocket -- that instance is now + responsible for the communication with that client. +=end +class SimpleServer < Qt::ServerSocket + def initialize( parent=nil ) + super( 4242, 1, parent ) + if !ok() + qWarning("Failed to bind to port 4242") + exit(1) + end + end + + def newConnection( socket ) + s = ClientSocket.new( socket, self ) + emit newConnect( s ) + end + + # The type of the argument is 'QSocket*', not + # 'ClientSocket*' as only types in the Smoke + # library can be used for types in Signals + signals 'newConnect(QSocket*)' +end + + +=begin + The ServerInfo class provides a small GUI for the server. It also creates the + SimpleServer and as a result the server. +=end +class ServerInfo < Qt::VBox + def initialize() + super + @server = SimpleServer.new( self ) + + itext = tr( + "This is a small server example.\n" + + "Connect with the client now." + ) + lb = Qt::Label.new( itext, self ) + lb.setAlignment( AlignHCenter ) + @infoText = Qt::TextView.new( self ) + quit = Qt::PushButton.new( tr("Quit") , self ) + + # See the comment above about why the 'ClientSocket*' + # type cannot be used + connect( @server, SIGNAL('newConnect(QSocket*)'), + SLOT('newConnect(QSocket*)') ) + connect( quit, SIGNAL('clicked()'), $qApp, + SLOT('quit()') ) + end + + slots 'newConnect(QSocket*)', 'connectionClosed()' + + def newConnect( s ) + @infoText.append( tr("New connection\n") ) + connect( s, SIGNAL('logText(const QString&)'), + @infoText, SLOT('append(const QString&)') ) + connect( s, SIGNAL('connectionClosed()'), + SLOT('connectionClosed()') ) + end + + def connectionClosed() + @infoText.append( tr("Client closed connection\n") ) + end +end + + +app = Qt::Application.new( ARGV ) +info = ServerInfo.new +app.mainWidget = info +info.show +app.exec + + diff --git a/qtruby/rubylib/examples/passivepopup/passivepopup.rb b/qtruby/rubylib/examples/passivepopup/passivepopup.rb new file mode 100644 index 00000000..37f60c1f --- /dev/null +++ b/qtruby/rubylib/examples/passivepopup/passivepopup.rb @@ -0,0 +1,39 @@ +#!/usr/bin/env ruby + +require 'Qt' + +class PassiveWindow < Qt::Frame + MARGIN = 20 + + def initialize(message) + super(nil, "passivedlg", + Qt::WStyle_Customize | Qt::WX11BypassWM | Qt::WStyle_StaysOnTop | + Qt::WStyle_Tool | Qt::WStyle_NoBorder) + + setFrameStyle(Qt::Frame::Box| Qt::Frame::Plain) + setLineWidth(2) + + setMinimumWidth(100) + layout=Qt::VBoxLayout.new(self, 6, 11) + layout.setAutoAdd(true) + Qt::Label.new(message, self) + + quit=Qt::PushButton.new(tr("Close"), self) + connect(quit, SIGNAL("clicked()"), SLOT("close()")) + end + + def show + super + move(Qt::Application.desktop().width() - width() - MARGIN, + Qt::Application.desktop().height() - height() - MARGIN) + end +end + +if (Process.fork != nil) + exit +end +app = Qt::Application.new(ARGV) +win = PassiveWindow.new(ARGV[0]) +app.mainWidget = win +win.show +app.exec diff --git a/qtruby/rubylib/examples/qt-examples/aclock/aclock.rb b/qtruby/rubylib/examples/qt-examples/aclock/aclock.rb new file mode 100644 index 00000000..a2cdc378 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/aclock/aclock.rb @@ -0,0 +1,113 @@ +#!/usr/bin/env ruby -w + +require 'Qt' + +# an analog clock widget using an internal QTimer +class AnalogClock < Qt::Widget + slots 'setTime(const QTime&)', 'drawClock(QPainter*)', 'timeout()' + + def initialize(*k) + super(*k) + + @time = Qt::Time::currentTime + @internalTimer = Qt::Timer.new(self) + connect(@internalTimer, SIGNAL('timeout()'), self, SLOT('timeout()')) + @internalTimer.start(5000) + end + + def mousePressEvent(e) + if isTopLevel + topLeft = geometry.topLeft - frameGeometry.topLeft + @clickPos = e.pos + topLeft + end + end + + def mouseMoveEvent(e) + if isTopLevel + move(e.globalPos - @clickPos) unless @clickPos.nil? + end + end + + def setTime(t) + # erm. huh? + timeout() + end + + # The QTimer::timeout() signal is received by this slot. + def timeout + new_time = Qt::Time::currentTime + @time = @time.addSecs 5 + unless new_time.minute == @time.minute + if autoMask + updateMask + else + update + end + end + end + + def paintEvent(blah) + unless autoMask + paint = Qt::Painter.new(self) + paint.setBrush(colorGroup.foreground) + drawClock(paint) + paint.end + end + end + + # If clock is transparent, we use updateMask() instead of paintEvent() + def updateMask + bm = Qt::Bitmap.new(size) + bm.fill(color0) # transparent + + paint = Qt::Painter.new + paint.begin(bm, self) + paint.setBrush(color1) # use non-transparent color + paint.setPen(color1) + + drawClock(paint) + + paint.end + setMask(bm) + end + + # The clock is painted using a 1000x1000 square coordinate system, in + # the centered square, as big as possible. The painter's pen and + # brush colors are used. + def drawClock(paint) + paint.save + + paint.setWindow(-500,-500, 1000,1000) + + v = paint.viewport + d = [v.width, v.height].min + vpx = (v.left + (v.width-d)) / 2 + vpy = (v.top - (v.height-d)) / 2 + paint.setViewport(vpx, vpy, d, d) + + paint.save + paint.rotate(30*(@time.hour%12-3) + @time.minute/2) + pts = Qt::PointArray.new(4, [-20,0, 0,-20, 300,0, 0,20]) + paint.drawConvexPolygon(pts) + paint.restore + + paint.save + paint.rotate((@time.minute-15)*6) + pts = Qt::PointArray.new(4, [-10,0, 0,-10, 400,0, 0,10]) + paint.drawConvexPolygon(pts) + paint.restore; + + 12.times { + paint.drawLine(440,0, 460,0) + paint.rotate(30) + } + + paint.restore + end + + def setAutoMask(background) + setBackgroundMode(background ? PaletteForeground : PaletteBackground) + Qt::Widget::setAutoMask(background) + end + +end diff --git a/qtruby/rubylib/examples/qt-examples/aclock/main.rb b/qtruby/rubylib/examples/qt-examples/aclock/main.rb new file mode 100755 index 00000000..dadbee15 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/aclock/main.rb @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +require 'Qt' +require 'aclock' + +a = Qt::Application.new(ARGV) +clock = AnalogClock.new +ARGV.each {|arg| + clock.setAutoMask(true) if arg == '-transparent' +} +clock.resize(100, 100) +a.setMainWidget(clock) +clock.setCaption('QtRuby example - Analog Clock') +clock.show +a.exec diff --git a/qtruby/rubylib/examples/qt-examples/chart/README b/qtruby/rubylib/examples/qt-examples/chart/README new file mode 100644 index 00000000..921437c5 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/README @@ -0,0 +1,8 @@ + +A Complete Canvas Application + +This is a complete example program with a main window, menus and +toolbars. The main widget is a Qt::Canvas, and this example +demonstrates basic canvas usage. + +This example is the subject of Qt Tutorial #2 diff --git a/qtruby/rubylib/examples/qt-examples/chart/canvastext.rb b/qtruby/rubylib/examples/qt-examples/chart/canvastext.rb new file mode 100644 index 00000000..8a298faa --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/canvastext.rb @@ -0,0 +1,16 @@ + +class CanvasText < Qt::CanvasText + + CANVAS_TEXT = 1100 + attr :index + + def initialize(index, *k) + super(*k) + @index = index + end + + def rtti() return CANVAS_TEXT end + +end + + diff --git a/qtruby/rubylib/examples/qt-examples/chart/canvasview.rb b/qtruby/rubylib/examples/qt-examples/chart/canvasview.rb new file mode 100644 index 00000000..416b0de7 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/canvasview.rb @@ -0,0 +1,53 @@ +class CanvasView < Qt::CanvasView + + def initialize(canvas, elements, parent = nil, name = "canvas view", f = 0) + super(canvas, parent, name, f) + @elements = elements + @movingItem = nil + end + + def contentsContextMenuEvent( e ) + parent().optionsMenu.exec( Qt::Cursor.pos() ) + end + + + def viewportResizeEvent( e ) + canvas().resize( e.size().width(), e.size().height() ) + parent().drawElements() + end + + + def contentsMousePressEvent( e ) + list = canvas().collisions( e.pos() ) + list.each do |it| + if it.rtti() == CanvasText::CANVAS_TEXT + @movingItem = it + @pos = e.pos() + return + end + end + @movingItem = nil + end + + + def contentsMouseMoveEvent( e ) + if @movingItem + offset = e.pos() - @pos + @movingItem.moveBy( offset.x(), offset.y() ) + @pos = e.pos() + form = parent() + form.changed = true + chartType = form.chartType() + item = @movingItem + i = item.index() + + @elements[i].setProX( chartType, item.x() / canvas().width() ) + @elements[i].setProY( chartType, item.y() / canvas().height() ) + + canvas().update() + end + end + +end + + diff --git a/qtruby/rubylib/examples/qt-examples/chart/chartform.rb b/qtruby/rubylib/examples/qt-examples/chart/chartform.rb new file mode 100644 index 00000000..a649ce12 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/chartform.rb @@ -0,0 +1,488 @@ +class ChartForm < Qt::MainWindow + + slots 'fileNew()', + 'fileOpen()', + 'fileOpenRecent( int )', + 'fileSave()', + 'fileSaveAs()', + 'fileSaveAsPixmap()', + 'filePrint()', + 'fileQuit()', + 'optionsSetData()', + 'updateChartType( QAction * )', + 'optionsSetFont()', + 'optionsSetOptions()', + 'helpHelp()', + 'helpAbout()', + 'helpAboutQt()', + 'saveOptions()' + + attr_accessor :changed + attr_reader :chartType, :optionsMenu + + MAX_ELEMENTS = 100 + MAX_RECENTFILES = 9 # Must not exceed 9 + + PIE = 0 + VERTICAL_BAR = 1 + HORIZONTAL_BAR = 2 + + NO = 0 + YES = 1 + AS_PERCENTAGE = 2 + + WINDOWS_REGISTRY = "/Trolltech/QtExamples" + APP_KEY = "/Chart/" + + def initialize( filename ) + super( nil, nil, WDestructiveClose ) + @filename = filename + setIcon( Qt::Pixmap.new( "images/options_piechart.xpm" ) ) + + fileNewAction = Qt::Action.new( + "New Chart", Qt::IconSet.new(Qt::Pixmap.new( "images/file_new.xpm" )), + "&New", Qt::KeySequence.new(CTRL+Key_N), self, "new" ) + connect( fileNewAction, SIGNAL( 'activated()' ), self, SLOT( 'fileNew()' ) ) + + fileOpenAction = Qt::Action.new( + "Open Chart", Qt::IconSet.new(Qt::Pixmap.new( "images/file_open.xpm" )), + "&Open...", Qt::KeySequence.new(CTRL+Key_O), self, "open" ) + connect( fileOpenAction, SIGNAL( 'activated()' ), self, SLOT( 'fileOpen()' ) ) + + fileSaveAction = Qt::Action.new( + "Save Chart", Qt::IconSet.new(Qt::Pixmap.new( "images/file_save.xpm" )), + "&Save", Qt::KeySequence.new(CTRL+Key_S), self, "save" ) + connect( fileSaveAction, SIGNAL( 'activated()' ), self, SLOT( 'fileSave()' ) ) + + fileSaveAsAction = Qt::Action.new( + "Save Chart As", Qt::IconSet.new(Qt::Pixmap.new( "images/file_save.xpm" )), + "Save &As...", Qt::KeySequence.new(0), self, "save as" ) + connect( fileSaveAsAction, SIGNAL( 'activated()' ), + self, SLOT( 'fileSaveAs()' ) ) + + fileSaveAsPixmapAction = Qt::Action.new( + "Save Chart As Bitmap", Qt::IconSet.new(Qt::Pixmap.new( "images/file_save.xpm" )), + "Save As &Bitmap...", Qt::KeySequence.new(CTRL+Key_B), self, "save as bitmap" ) + connect( fileSaveAsPixmapAction, SIGNAL( 'activated()' ), + self, SLOT( 'fileSaveAsPixmap()' ) ) + + filePrintAction = Qt::Action.new( + "Print Chart", Qt::IconSet.new(Qt::Pixmap.new( "images/file_print.xpm" )), + "&Print Chart...", Qt::KeySequence.new(CTRL+Key_P), self, "print chart" ) + connect( filePrintAction, SIGNAL( 'activated()' ), + self, SLOT( 'filePrint()' ) ) + + optionsSetDataAction = Qt::Action.new( + "Set Data", Qt::IconSet.new(Qt::Pixmap.new( "images/options_setdata.xpm" )), + "Set &Data...", Qt::KeySequence.new(CTRL+Key_D), self, "set data" ) + connect( optionsSetDataAction, SIGNAL( 'activated()' ), + self, SLOT( 'optionsSetData()' ) ) + + + chartGroup = Qt::ActionGroup.new( self ) # Connected later + chartGroup.setExclusive( true ) + + @optionsPieChartAction = Qt::Action.new( + "Pie Chart", Qt::IconSet.new(Qt::Pixmap.new( "images/options_piechart.xpm" )), + "&Pie Chart", Qt::KeySequence.new(CTRL+Key_I), chartGroup, "pie chart" ) + @optionsPieChartAction.setToggleAction( true ) + + @optionsHorizontalBarChartAction = Qt::Action.new( + "Horizontal Bar Chart", Qt::IconSet.new(Qt::Pixmap.new( "images/options_horizontalbarchart.xpm" )), + "&Horizontal Bar Chart", Qt::KeySequence.new(CTRL+Key_H), chartGroup, + "horizontal bar chart" ) + @optionsHorizontalBarChartAction.setToggleAction( true ) + + @optionsVerticalBarChartAction = Qt::Action.new( + "Vertical Bar Chart", Qt::IconSet.new(Qt::Pixmap.new( "images/options_verticalbarchart.xpm" )), + "&Vertical Bar Chart", Qt::KeySequence.new(CTRL+Key_V), chartGroup, "Vertical bar chart" ) + @optionsVerticalBarChartAction.setToggleAction( true ) + + + optionsSetFontAction = Qt::Action.new( + "Set Font", Qt::IconSet.new(Qt::Pixmap.new( "images/options_setfont.xpm" )), + "Set &Font...", Qt::KeySequence.new(CTRL+Key_F), self, "set font" ) + connect( optionsSetFontAction, SIGNAL( 'activated()' ), + self, SLOT( 'optionsSetFont()' ) ) + + optionsSetOptionsAction = Qt::Action.new( + "Set Options", Qt::IconSet.new(Qt::Pixmap.new( "images/options_setoptions.xpm" )), + "Set &Options...", Qt::KeySequence.new(0), self, "set options" ) + connect( optionsSetOptionsAction, SIGNAL( 'activated()' ), + self, SLOT( 'optionsSetOptions()' ) ) + + fileQuitAction = Qt::Action.new( "Quit", "&Quit", Qt::KeySequence.new(CTRL+Key_Q), self, "quit" ) + connect( fileQuitAction, SIGNAL( 'activated()' ), self, SLOT( 'fileQuit()' ) ) + + + fileTools = Qt::ToolBar.new( self, "file operations" ) + fileTools.setLabel( "File Operations" ) + fileNewAction.addTo( fileTools ) + fileOpenAction.addTo( fileTools ) + fileSaveAction.addTo( fileTools ) + fileTools.addSeparator() + filePrintAction.addTo( fileTools ) + + optionsTools = Qt::ToolBar.new( self, "options operations" ) + optionsTools.setLabel( "Options Operations" ) + optionsSetDataAction.addTo( optionsTools ) + optionsTools.addSeparator() + @optionsPieChartAction.addTo( optionsTools ) + @optionsHorizontalBarChartAction.addTo( optionsTools ) + @optionsVerticalBarChartAction.addTo( optionsTools ) + optionsTools.addSeparator() + optionsSetFontAction.addTo( optionsTools ) + optionsTools.addSeparator() + optionsSetOptionsAction.addTo( optionsTools ) + + @fileMenu = Qt::PopupMenu.new( self ) + menuBar().insertItem( "&File", @fileMenu ) + fileNewAction.addTo( @fileMenu ) + fileOpenAction.addTo( @fileMenu ) + fileSaveAction.addTo( @fileMenu ) + fileSaveAsAction.addTo( @fileMenu ) + @fileMenu.insertSeparator() + fileSaveAsPixmapAction.addTo( @fileMenu ) + @fileMenu.insertSeparator() + filePrintAction.addTo( @fileMenu ) + @fileMenu.insertSeparator() + fileQuitAction.addTo( @fileMenu ) + + @optionsMenu = Qt::PopupMenu.new( self ) + menuBar().insertItem( "&Options", @optionsMenu ) + optionsSetDataAction.addTo( @optionsMenu ) + @optionsMenu.insertSeparator() + @optionsPieChartAction.addTo( @optionsMenu ) + @optionsHorizontalBarChartAction.addTo( @optionsMenu ) + @optionsVerticalBarChartAction.addTo( @optionsMenu ) + @optionsMenu.insertSeparator() + optionsSetFontAction.addTo( @optionsMenu ) + @optionsMenu.insertSeparator() + optionsSetOptionsAction.addTo( @optionsMenu ) + + menuBar().insertSeparator() + + helpMenu = Qt::PopupMenu.new( self ) + menuBar().insertItem( "&Help", helpMenu ) + helpMenu.insertItem( "&Help", self, SLOT('helpHelp()'), Qt::KeySequence.new(Key_F1) ) + helpMenu.insertItem( "&About", self, SLOT('helpAbout()') ) + helpMenu.insertItem( "About &Qt", self, SLOT('helpAboutQt()') ) + + + @printer = nil + @elements = Array.new(MAX_ELEMENTS) + + settings = Qt::Settings.new + settings.insertSearchPath( Qt::Settings::Windows, WINDOWS_REGISTRY ) + windowWidth = settings.readNumEntry( APP_KEY + "WindowWidth", 460 ) + windowHeight = settings.readNumEntry( APP_KEY + "WindowHeight", 530 ) + windowX = settings.readNumEntry( APP_KEY + "WindowX", -1 ) + windowY = settings.readNumEntry( APP_KEY + "WindowY", -1 ) + setChartType( settings.readNumEntry( APP_KEY + "ChartType", PIE ) ) + @addValues = settings.readNumEntry( APP_KEY + "AddValues", NO ) + @decimalPlaces = settings.readNumEntry( APP_KEY + "Decimals", 2 ) + @font = Qt::Font.new( "Helvetica", 18, Qt::Font::Bold ) + @font.fromString( + settings.readEntry( APP_KEY + "Font", @font.toString() ) ) + @recentFiles = [] + for i in 0...MAX_RECENTFILES + filename = settings.readEntry( APP_KEY + "File" + ( i + 1 ).to_s ) + if !filename.nil? + @recentFiles.push( filename ) + end + end + if @recentFiles.length() > 0 + updateRecentFilesMenu() + end + + + # Connect *after* we've set the chart type on so we don't call + # drawElements() prematurely. + connect( chartGroup, SIGNAL( 'selected(QAction*)' ), + self, SLOT( 'updateChartType(QAction*)' ) ) + + resize( windowWidth, windowHeight ) + if windowX != -1 || windowY != -1 + move( windowX, windowY ) + end + + @canvas = Qt::Canvas.new( self ) + @canvas.resize( width(), height() ) + @canvasView = CanvasView.new( @canvas, @elements, self ) + setCentralWidget( @canvasView ) + @canvasView.show() + + if ! @filename.nil? + load( @filename ) + else + init() + @elements[0].set( 20, red, 14, "Red" ) + @elements[1].set( 70, cyan, 2, "Cyan", darkGreen ) + @elements[2].set( 35, blue, 11, "Blue" ) + @elements[3].set( 55, yellow, 1, "Yellow", darkBlue ) + @elements[4].set( 80, magenta, 1, "Magenta" ) + drawElements() + end + + statusBar().message( "Ready", 2000 ) + end + + + + def init() + setCaption( "Chart" ) + @filename = nil + @changed = false + + @elements[0] = Element.new( Element::INVALID, red ) + @elements[1] = Element.new( Element::INVALID, cyan ) + @elements[2] = Element.new( Element::INVALID, blue ) + @elements[3] = Element.new( Element::INVALID, yellow ) + @elements[4] = Element.new( Element::INVALID, green ) + @elements[5] = Element.new( Element::INVALID, magenta ) + @elements[6] = Element.new( Element::INVALID, darkYellow ) + @elements[7] = Element.new( Element::INVALID, darkRed ) + @elements[8] = Element.new( Element::INVALID, darkCyan ) + @elements[9] = Element.new( Element::INVALID, darkGreen ) + @elements[10] = Element.new( Element::INVALID, darkMagenta ) + @elements[11] = Element.new( Element::INVALID, darkBlue ) + for i in 12...MAX_ELEMENTS + x = (i.to_f / MAX_ELEMENTS) * 360 + y = ((x * 256) % 105) + 151 + z = ((i * 17) % 105) + 151; + @elements[i] = Element.new( Element::INVALID, Qt::Color.new( x, y, z, Qt::Color::Hsv ) ) + end + end + + def closeEvent( e ) + fileQuit() + end + + + def fileNew() + if okToClear() + init() + drawElements() + end + end + + + def fileOpen() + if !okToClear() + return + end + + filename = Qt::FileDialog.getOpenFileName( + nil, "Charts (*.cht)", self, + "file open", "Chart -- File Open" ) + if !filename.nil? + load( filename ) + else + statusBar().message( "File Open abandoned", 2000 ) + end + end + + + def fileSaveAs() + filename = Qt::FileDialog.getSaveFileName( + nil, "Charts (*.cht)", self, + "file save as", "Chart -- File Save As" ) + if !filename.nil? + answer = 0 + if Qt::File.exists( filename ) + answer = Qt::MessageBox.warning( + self, "Chart -- Overwrite File", + "Overwrite\n\'#{filename}\'?", + "&Yes", "&No", nil, 1, 1 ) + end + if answer == 0 + @filename = filename + updateRecentFiles( filename ) + fileSave() + return + end + end + statusBar().message( "Saving abandoned", 2000 ) + end + + + def fileOpenRecent( index ) + if !okToClear() + return + end + + load( @recentFiles[index] ) + end + + + def updateRecentFiles( filename ) + if @recentFiles.include?( filename ) + return + end + + @recentFiles.push( filename ) + if @recentFiles.length() > MAX_RECENTFILES + @recentFiles.shift() + end + + updateRecentFilesMenu() + end + + + def updateRecentFilesMenu() + for i in 0...MAX_RECENTFILES + if @fileMenu.findItem( i ) + @fileMenu.removeItem( i ) + end + if i < @recentFiles.length() + @fileMenu.insertItem( "&%d %s" % [i + 1, @recentFiles[i]], + self, SLOT( 'fileOpenRecent(int)' ), + Qt::KeySequence.new(0), i ) + end + end + end + + + def fileQuit() + if okToClear() + saveOptions() + $qApp.exit( 0 ) + end + end + + + def okToClear() + if @changed + if @filename.nil? + msg = "Unnamed chart " + else + msg = "Chart '#{@filename}'\n" + end + msg += "has been changed." + + x = Qt::MessageBox.information( self, "Chart -- Unsaved Changes", + msg, "&Save", "Cancel", "&Abandon", + 0, 1 ) + case x + when 0 # Save + fileSave() + when 1 # Cancel + when 2 # Abandon + else + return false + end + end + return true + end + + + def saveOptions() + settings = Qt::Settings.new + settings.insertSearchPath( Qt::Settings::Windows, WINDOWS_REGISTRY ) + settings.writeEntry( APP_KEY + "WindowWidth", width() ) + settings.writeEntry( APP_KEY + "WindowHeight", height() ) + settings.writeEntry( APP_KEY + "WindowX", x() ) + settings.writeEntry( APP_KEY + "WindowY", y() ) + settings.writeEntry( APP_KEY + "ChartType", @chartType ) + settings.writeEntry( APP_KEY + "AddValues", @addValues ) + settings.writeEntry( APP_KEY + "Decimals", @decimalPlaces ) + settings.writeEntry( APP_KEY + "Font", @font.toString() ) + for i in 0...@recentFiles.length + settings.writeEntry( APP_KEY + "File" + ( i + 1 ).to_s, + @recentFiles[i] ) + end + end + + + def optionsSetData() + setDataForm = SetDataForm.new( @elements, @decimalPlaces, self ) + if setDataForm.exec() + @changed = true + drawElements() + end + end + + + def setChartType( chartType ) + @chartType = chartType; + case @chartType + when PIE + @optionsPieChartAction.setOn( true ) + when VERTICAL_BAR: + @optionsVerticalBarChartAction.setOn( true ) + when HORIZONTAL_BAR: + @optionsHorizontalBarChartAction.setOn( true ) + end + end + + + def updateChartType( action ) + if action == @optionsPieChartAction + @chartType = PIE + elsif action == @optionsHorizontalBarChartAction + @chartType = HORIZONTAL_BAR + elsif action == @optionsVerticalBarChartAction + @chartType = VERTICAL_BAR + end + + drawElements() + end + + + def optionsSetFont() + ok = Qt::Boolean.new + font = Qt::FontDialog.getFont( ok, @font, self ) + if !ok.nil? + @font = font + drawElements() + end + end + + + def optionsSetOptions() + optionsForm = OptionsForm.new( self ) + optionsForm.chartTypeComboBox.setCurrentItem( @chartType ) + optionsForm.font = @font + case @addValues + when NO + optionsForm.noRadioButton.setChecked( true ) + when YES + optionsForm.yesRadioButton.setChecked( true ) + when AS_PERCENTAGE + optionsForm.asPercentageRadioButton.setChecked( true ) + end + optionsForm.decimalPlacesSpinBox.setValue( @decimalPlaces ) + if optionsForm.exec() + setChartType( optionsForm.chartTypeComboBox.currentItem() ) + @font = optionsForm.font + if optionsForm.noRadioButton.isChecked() + @addValues = NO + elsif optionsForm.yesRadioButton.isChecked() + @addValues = YES + elsif optionsForm.asPercentageRadioButton.isChecked() + @addValues = AS_PERCENTAGE + end + @decimalPlaces = optionsForm.decimalPlacesSpinBox.value() + drawElements() + end + end + + + def helpHelp() + statusBar().message( "Help is not implemented yet", 2000 ) + end + + + def helpAbout() + Qt::MessageBox.about( self, "Chart -- About", + "<center><h1><font color=blue>Chart<font></h1></center>" + + "<p>Chart your data with <i>chart</i>.</p>" ) + end + + + def helpAboutQt() + Qt::MessageBox.aboutQt( self, "Chart -- About Qt" ) + end + +end + diff --git a/qtruby/rubylib/examples/qt-examples/chart/chartform_canvas.rb b/qtruby/rubylib/examples/qt-examples/chart/chartform_canvas.rb new file mode 100644 index 00000000..86c66f76 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/chartform_canvas.rb @@ -0,0 +1,212 @@ +class ChartForm + + def drawElements() + list = @canvas.allItems() + list.each do |it| + it.dispose + end + + # 360 * 16 for pies Qt works with 16ths of degrees + scaleFactor = @chartType == PIE ? 5760 : + @chartType == VERTICAL_BAR ? @canvas.height() : + @canvas.width() + biggest = 0.0 + count = 0 + total = 0.0 + scales = Array.new(MAX_ELEMENTS) + + for i in 0...MAX_ELEMENTS + if @elements[i].isValid() + value = @elements[i].value() + count += 1 + total += value + if value > biggest + biggest = value + end + scales[i] = @elements[i].value() * scaleFactor + end + end + + if count > 0 + # 2nd loop because of total and biggest + for i in 0...MAX_ELEMENTS + if @elements[i].isValid() + if @chartType == PIE + scales[i] = (@elements[i].value() * scaleFactor) / total + else + scales[i] = (@elements[i].value() * scaleFactor) / biggest + end + end + end + + case @chartType + when PIE + drawPieChart( scales, total, count ) + when VERTICAL_BAR: + drawVerticalBarChart( scales, total, count ) + when HORIZONTAL_BAR: + drawHorizontalBarChart( scales, total, count ) + end + end + + @canvas.update() + end + + + def drawPieChart( scales, total, i ) + width = @canvas.width().to_f + height = @canvas.height().to_f + size = width > height ? height : width + x = width / 2 + y = height / 2 + angle = 0 + + for i in 0...MAX_ELEMENTS + if @elements[i].isValid() + extent = scales[i] + arc = Qt::CanvasEllipse.new( size, size, angle, extent, @canvas ) + arc.setX( x ) + arc.setY( y ) + arc.setZ( 0 ) + arc.setBrush( Qt::Brush.new( @elements[i].valueColor(), + @elements[i].valuePattern() ) ) + arc.show() + angle += extent + label = @elements[i].label() + if !label.empty? || @addValues != NO + label = valueLabel( label, @elements[i].value(), total ) + text = CanvasText.new( i, label, @font, @canvas ) + proX = @elements[i].proX( PIE ).to_f + proY = @elements[i].proY( PIE ).to_f + if proX < 0 || proY < 0 + # Find the centre of the pie segment + rect = arc.boundingRect() + proX = ( rect.width() / 2 ) + rect.x() + proY = ( rect.height() / 2 ) + rect.y() + # Centre text over the centre of the pie segment + rect = text.boundingRect() + proX -= ( rect.width() / 2 ) + proY -= ( rect.height() / 2 ) + # Make proportional + proX /= width + proY /= height + end + text.setColor( @elements[i].labelColor() ) + text.setX( proX * width ) + text.setY( proY * height ) + text.setZ( 1 ) + text.show() + @elements[i].setProX( PIE, proX ) + @elements[i].setProY( PIE, proY ) + end + end + end + end + + + def drawVerticalBarChart(scales, total, count ) + width = @canvas.width().to_f + height = @canvas.height().to_f + prowidth = width / count + x = 0 + pen = Qt::Pen.new + pen.style = NoPen + + for i in 0...MAX_ELEMENTS + if @elements[i].isValid() + extent = scales[i] + y = height - extent + rect = Qt::CanvasRectangle.new(x, y, prowidth, extent, @canvas ) + rect.setBrush( Qt::Brush.new( @elements[i].valueColor(), + @elements[i].valuePattern() ) ) + rect.setPen( pen ) + rect.setZ( 0 ) + rect.show() + label = @elements[i].label() + if !label.empty? || @addValues != NO + proX = @elements[i].proX( VERTICAL_BAR ).to_f + proY = @elements[i].proY( VERTICAL_BAR ).to_f + if proX < 0 || proY < 0 + proX = x / width + proY = y / height + end + label = valueLabel( label, @elements[i].value(), total ) + text = CanvasText.new( i, label, @font, @canvas ) + text.setColor( @elements[i].labelColor() ) + text.setX( proX * width ) + text.setY( proY * height ) + text.setZ( 1 ) + text.show() + @elements[i].setProX( VERTICAL_BAR, proX ) + @elements[i].setProY( VERTICAL_BAR, proY ) + end + x += prowidth + end + end + end + + + def drawHorizontalBarChart(scales, total, count ) + width = @canvas.width().to_f + height = @canvas.height().to_f + proheight = height / count + y = 0 + pen = Qt::Pen.new + pen.style = NoPen + + for i in 0...MAX_ELEMENTS + if @elements[i].isValid() + extent = scales[i] + rect = Qt::CanvasRectangle.new(0, y, extent, proheight, @canvas ) + rect.setBrush( Qt::Brush.new( @elements[i].valueColor(), + @elements[i].valuePattern() ) ) + rect.setPen( pen ) + rect.setZ( 0 ) + rect.show() + label = @elements[i].label() + if !label.empty? || @addValues != NO + proX = @elements[i].proX( HORIZONTAL_BAR ).to_f + proY = @elements[i].proY( HORIZONTAL_BAR ).to_f + if proX < 0 || proY < 0 + proX = 0 + proY = y / height + end + label = valueLabel( label, @elements[i].value(), total ) + text = CanvasText.new( i, label, @font, @canvas ) + text.setColor( @elements[i].labelColor() ) + text.setX( proX * width ) + text.setY( proY * height ) + text.setZ( 1 ) + text.show() + @elements[i].setProX( HORIZONTAL_BAR, proX ) + @elements[i].setProY( HORIZONTAL_BAR, proY ) + end + y += proheight + end + end + end + + + def valueLabel(label, value, total ) + if @addValues == NO + return label + end + + newLabel = label + if !label.empty? + if @chartType == VERTICAL_BAR + newLabel += "\n" + else + newLabel += ' ' + end + end + if @addValues == YES + newLabel += "%.#{@decimalPlaces}f" % value + elsif @addValues == AS_PERCENTAGE + newLabel += "%.#{@decimalPlaces}f%s" % [(value / total) * 100, '%'] + end + return newLabel + end + +end + diff --git a/qtruby/rubylib/examples/qt-examples/chart/chartform_files.rb b/qtruby/rubylib/examples/qt-examples/chart/chartform_files.rb new file mode 100644 index 00000000..648aba62 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/chartform_files.rb @@ -0,0 +1,102 @@ +class ChartForm + + def load( filename ) + file = Qt::File.new( filename ) + if !file.open( Qt::IO_ReadOnly ) + statusBar().message( "Failed to load \'%s\'" % filename, 2000 ) + return + end + + init() # Make sure we have colours + @filename = filename + ts = Qt::TextStream.new( file ) + errors = 0 + i = 0 + while !ts.eof() + element = Element.new + ts >> element + if element.isValid() + @elements[i] = element + i += 1 + else + errors += 1 + end + if i == MAX_ELEMENTS + statusBar().message("Read maximum number of elements (%d) discarding others" % i, 2000 ) + break + end + end + + file.close() + + bad = "" + if errors > 0 + bad = " skipped %d bad record" % errors + if errors > 1 + bad += "s" + end + end + statusBar().message( "Read %d values from \'%s\'" % [i, filename], 3000 ) + + setCaption( "Chart -- %s" % filename ) + updateRecentFiles( filename ) + + drawElements() + @changed = false + end + + + def fileSave() + if @filename.nil? + fileSaveAs() + return + end + + file = Qt::File.new( @filename ) + if !file.open( Qt::IO_WriteOnly ) + statusBar().message( "Failed to save \'%s\'" % @filename, 2000 ) + return + end + ts = Qt::TextStream.new( file ) + for i in 0...MAX_ELEMENTS + if @elements[i].isValid() + ts << @elements[i] + end + end + + file.close() + + setCaption( "Chart -- %s" % @filename ) + statusBar().message( "Saved \'%s\'" % @filename, 2000 ) + @changed = false + end + + + def fileSaveAsPixmap() + filename = Qt::FileDialog.getSaveFileName(nil, "Images (*.png *.xpm *.jpg)", + self, "file save as bitmap", + "Chart -- File Save As Bitmap" ) + if Qt::Pixmap.grabWidget( @canvasView ).save( filename, + filename.sub(/.*\.([^.]*)$/, '\1').upcase() ) + statusBar().message( "Wrote \'%s\'" % filename, 2000 ) + else + statusBar().message( "Failed to write \'%s\'" % filename, 2000 ) + end + end + + def filePrint() + if !@printer + @printer = Qt::Printer.new + end + if @printer.setup() + painter = Qt::Painter.new( @printer ) + @canvas.drawArea( Qt::Rect.new( 0, 0, @canvas.width(), @canvas.height() ), + painter, false ) + if !@printer.outputFileName().empty? + statusBar().message( "Printed \'%s\'" % @printer.outputFileName(), 2000 ) + end + end + end + +end + diff --git a/qtruby/rubylib/examples/qt-examples/chart/element.rb b/qtruby/rubylib/examples/qt-examples/chart/element.rb new file mode 100644 index 00000000..ba135632 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/element.rb @@ -0,0 +1,161 @@ +class Element + + FIELD_SEP = ':' + PROPOINT_SEP = ';' + XY_SEP = ',' + + EPSILON = 0.0000001 + + INVALID = -1 + NO_PROPORTION = -1 + MAX_PROPOINTS = 3 # One proportional point per chart type + + attr_accessor :value, :valueColor, :valuePattern, :label, :labelColor + + def initialize( value = INVALID, valueColor = Qt::gray, + valuePattern = Qt::SolidPattern, + label = nil, + labelColor = Qt::black ) + init( value, valueColor, valuePattern, label, labelColor ) + @propoints = [] + for i in 0...MAX_PROPOINTS * 2 + @propoints[i] = NO_PROPORTION + end + end + + def isValid() return @value > EPSILON end + + + def init( value, valueColor, valuePattern, + label, labelColor ) + @value = value + @valueColor = valueColor + if Qt::SolidPattern >= valuePattern || Qt::DiagCrossPattern <= valuePattern + valuePattern = Qt::SolidPattern + end + @valuePattern = valuePattern + @label = label + @labelColor = labelColor + end + + def set( value = INVALID, valueColor = Qt::gray, + valuePattern = Qt::SolidPattern, + label = nil, + labelColor = Qt::black ) + init( value, valueColor, valuePattern, label, labelColor ) + end + + def setValuePattern( valuePattern ) + if valuePattern < Qt::SolidPattern.to_i || valuePattern > Qt::DiagCrossPattern.to_i + valuePattern = Qt::SolidPattern + end + @valuePattern = valuePattern + end + + + def proX( index ) + return @propoints[2 * index] + end + + + def proY( index ) + return @propoints[(2 * index) + 1] + end + + + def setProX( index, value ) + @propoints[2 * index] = value + end + + + def setProY( index, value ) + @propoints[(2 * index) + 1] = value + end + +end + +class Qt::TextStream + + alias op_write << + + def <<( item ) + if !item.kind_of? Element + return op_write(item) + end + element = item + self << element.value() << Element::FIELD_SEP << + element.valueColor().name() << Element::FIELD_SEP << + element.valuePattern().to_i << Element::FIELD_SEP << + element.labelColor().name() << Element::FIELD_SEP + + for i in 0...Element::MAX_PROPOINTS + self << element.proX( i ) << Element::XY_SEP << element.proY( i ) + self << ( i == Element::MAX_PROPOINTS - 1 ? Element::FIELD_SEP : Element::PROPOINT_SEP ) + end + + self << element.label() << "\n" + + return self + end + + alias op_read >> + + def >>( item ) + if !item.kind_of? Element + return op_read(item) + end + + element = item + data = readLine() + element.value = Element::INVALID + + errors = 0 + + fields = data.split( Element::FIELD_SEP ) + if fields.length() >= 4 + value = fields[0].to_f + if value.nil? + errors += 1 + end + valueColor = Qt::Color.new( fields[1] ) + if !valueColor.isValid() + errors += 1 + end + valuePattern = fields[2].to_i + if valuePattern.nil? + errors += 1 + end + labelColor = Qt::Color.new( fields[3] ) + if !labelColor.isValid() + errors += 1 + end + propoints = fields[4].split( Element::PROPOINT_SEP ) + label = data.split(Element::FIELD_SEP)[5] + if errors == 0 + element.set( value, valueColor, valuePattern, label, labelColor ) + i = 0 + propoints.each do |point| + errors = 0 + xy = point.split( Element::XY_SEP ) + x = xy[0].to_f + if x.nil? || x <= 0.0 || x >= 1.0 + errors += 1 + end + y = xy[1].to_f + if y.nil? || y <= 0.0 || y >= 1.0 + errors += 1 + end + if errors > 0 + x = y = Element::NO_PROPORTION + end + element.setProX( i, x ) + element.setProY( i, y ) + i += 1 + end + end + end + + return self + end + +end diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/chart-forms.sk b/qtruby/rubylib/examples/qt-examples/chart/images/chart-forms.sk new file mode 100644 index 00000000..d9087b48 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/chart-forms.sk @@ -0,0 +1,256 @@ +##Sketch 1 2 +document() +layout('A4',0) +layer('Layer 1',1,1,0,0,(0,0,0)) +fp((0.9,0.9,0.9)) +le() +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +r(188.034,0,0,-149.201,526.688,-521.707) +fp((0.9,0.9,0.9)) +le() +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +r(128.762,0,0,-92.995,341.407,-572.49) +fp((0.9,0.9,0.9)) +le() +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +r(128.762,0,0,-92.995,768.68,-572.934) +fp((0.9,0.9,0.9)) +le() +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +e(31.6796,0,0,31.6796,635.564,-722.4) +fp((0.8,0.8,0.8)) +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +r(188.034,0,0,-149.201,518.884,-513.603) +fp((1,1,1)) +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +r(158.398,0,0,-106.28,533.702,-545.185) +fp((0,0,0)) +le() +lw(1) +Fn('Helvetica-Narrow-Bold') +Fs(18) +txt('ChartForm',(575.182,-535.064)) +fp((0.8,0.8,0.8)) +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +r(128.762,0,0,-92.995,335.96,-566.743) +fp((0,0,0)) +le() +lw(1) +Fn('Helvetica-Narrow-Bold') +Fs(18) +txt('OptionsForm',(354.009,-589.226)) +fp((0.8,0.8,0.8)) +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +r(128.762,0,0,-92.995,763.221,-566.743) +fp((0,0,0)) +le() +lw(1) +Fn('Helvetica-Narrow-Bold') +Fs(18) +txt('SetDataForm',(781.675,-587.279)) +fp((1,0,1)) +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +e(31.6796,0,0,31.6796,631.296,-719.01) +fp((0,0,0)) +le() +lw(1) +Fn('Helvetica-Narrow-BoldOblique') +Fs(18) +txt('chart',(613.251,-723.609)) +G() +fp((0,0,0)) +le() +lw(1) +Fn('Helvetica-Narrow-Bold') +Fs(18) +txt('CanvasView',(569.827,-575.941)) +fp((0,0,0)) +le() +lw(1) +Fn('Helvetica-Narrow-Bold') +Fs(18) +txt('depicting a',(573.94,-595.357)) +fp((0,0,0)) +le() +lw(1) +Fn('Helvetica-Narrow-Bold') +Fs(18) +txt('QCanvas',(580.906,-614.774)) +G_() +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +b() +bs(662.975,-716.966,0) +bc(669.107,-712.686,671.463,-709.765,672.485,-707.625,2) +bc(673.507,-705.485,677.438,-697.225,677.438,-696.155,2) +bc(677.438,-695.085,679.326,-682.725,679.326,-682.725,2) +bc(679.326,-682.725,679.326,-670.955,679.326,-670.955,2) +bc(679.326,-670.955,679.326,-665.605,679.326,-664.535,2) +lw(1.41732) +la1(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +b() +bs(710.729,-618.861,0) +bs(759.036,-618.861,0) +lw(1.41732) +la1(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +b() +bs(515.603,-617.742,0) +bs(467.885,-617.742,0) +G() +fp((0,0.392,0)) +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +e(29.7517,0,0,-7.65884,468.929,-768.389) +fp((0,0.392,0)) +le() +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +r(58.9143,0,0,-44.1857,439.032,-724.349) +fp((0,0.392,0)) +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +e(29.7517,0,0,-7.65884,468.929,-722.581) +G_() +lw(1.41732) +la1(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +b() +bs(499.125,-739.077,0) +bc(507.548,-735.049,508.671,-732.747,512.04,-728.144,2) +bc(515.409,-723.54,519.901,-717.21,520.463,-716.059,2) +bc(521.024,-714.909,531.132,-689.589,531.132,-689.589,2) +bc(531.132,-689.589,533.378,-679.231,533.378,-679.231,2) +bc(533.378,-679.231,535.062,-671.175,535.062,-671.175,2) +bc(535.062,-671.175,535.062,-664.845,535.062,-664.845,2) +fp((1,1,1)) +le() +lw(1) +Fn('Helvetica-Narrow-Bold') +Fs(18) +txt('disk',(453.761,-753.806)) +fp((0,0,0)) +le() +lw(1) +Fn('Helvetica-Narrow') +Fs(18) +txt('run',(681.17,-700.783)) +fp((0,0,0)) +le() +lw(1) +Fn('Helvetica-Narrow') +Fs(18) +txt('save',(524.007,-725.891)) +fp((0,0,0)) +le() +lw(1) +Fn('Helvetica-Narrow') +Fs(18) +txt('load',(494.295,-706.489)) +fp((0.9,0.9,0.9)) +le() +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +r(143.737,0,0,-67.586,525.422,-405.581) +fp((0.596,0.984,0.596)) +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +r(143.737,0,0,-67.586,519.327,-401.081) +fp((0,0,0)) +le() +lw(1) +Fn('Helvetica-Narrow') +Fs(18) +txt('getOpenFileName()',(526.396,-420.297)) +fp((0.9,0.9,0.9)) +le() +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +r(143.737,0,0,-67.586,704.655,-405.581) +fp((0.596,0.984,0.596)) +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +r(143.737,0,0,-67.586,698.561,-401.081) +fp((0,0,0)) +le() +lw(1) +Fn('Helvetica-Narrow') +Fs(18) +txt('getSaveFileName()',(706.863,-420.39)) +fp((0.9,0.9,0.9)) +le() +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +r(100.503,0,0,-67.586,375.286,-429.722) +fp((0.529,0.808,0.98)) +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +r(100.503,0,0,-67.586,371.024,-425.223) +fp((0,0,0)) +le() +lw(1) +Fn('Helvetica-Narrow') +Fs(18) +txt('getFont()',(391.333,-444.571)) +fp((0.9,0.9,0.9)) +le() +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +r(100.503,0,0,-67.586,935.176,-580.856) +fp((1,0.753,0.796)) +lw(1.41732) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +r(100.503,0,0,-67.586,930.915,-576.357) +fp((0,0,0)) +le() +lw(1) +Fn('Helvetica-Narrow') +Fs(18) +txt('getColor()',(948.361,-598.303)) +lw(1.41732) +ld((1, 1)) +la1(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +b() +bs(591.836,-511.466,0) +bs(591.836,-471.702,0) +lw(1.41732) +ld((1, 1)) +la1(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +b() +bs(674.96,-513.083,0) +bs(749.29,-470.169,0) +lw(1.41732) +ld((1, 1)) +la1(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +b() +bs(538.877,-513.083,0) +bs(472.859,-474.968,0) +lw(1.41732) +ld((1, 1)) +la1(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +b() +bs(410.746,-562.437,0) +bs(410.746,-494.504,0) +lw(1.41732) +ld((1, 1)) +la1(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +la2(([(-4.0, 3.0), (2.0, 0.0), (-4.0, -3.0), (-4.0, 3.0)], 1)) +b() +bs(895.423,-617.315,0) +bs(928.721,-617.315,0) +guidelayer('Guide Lines',1,0,0,1,(0,0,1)) +grid((0,0,20,20),0,(0,0,1),'Grid') diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/file_new.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/file_new.xpm new file mode 100644 index 00000000..8537176c --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/file_new.xpm @@ -0,0 +1,36 @@ +/* XPM */ +static const char *file_new[] = { +"20 20 12 1", +" c white", +"! c None", +"# c #000", +"$ c #2e2e2e2e2e2e", +"% c #ffffffffffff", +"& c #5c5c5c5c5c5c", +"( c #878787878787", +") c #c2c2c2c2c2c2", +"* c black", +"+ c #00C900", +", c #ffff00", +"- c red", +"!!!!!!!!!!!!!!!!!!!!", +"!!##########$!!!!!!!", +"!!#%%%%%%%%#&$!!!!!!", +"!!#%%%%%%%%#(&$!!!!!", +"!!#%%%%%%%%#)(&$!!!!", +"!!#%%%%%%%%#%)(&$!!!", +"!!#%%%%*****#####!!!", +"!!#%%%*+++++*%%%#!!!", +"!!#%%*,++++++*%%#!!!", +"!!#%*,,,++++++*%#!!!", +"!!#%*,,,,+++++*%#!!!", +"!!#%*,,,,-++++*%#!!!", +"!!#%*,,,---+++*%#!!!", +"!!#%*,,-----++*%#!!!", +"!!#%%*-------*%%#!!!", +"!!#%%%*-----*%%%#!!!", +"!!#%%%%*****%%%%#!!!", +"!!#%%%%%%%%%%%%%#!!!", +"!!###############!!!", +"!!!!!!!!!!!!!!!!!!!!" +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/file_open.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/file_open.xpm new file mode 100644 index 00000000..7a7b681d --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/file_open.xpm @@ -0,0 +1,33 @@ +/* XPM */ +static const char *file_open[] = { +"20 20 9 1", +" c white", +"! c None", +"# c #002EFF", +"$ c #000", +"% c #ffffffff0", +"& c #ffffffffffff", +"( c #00C900", +") c #ffff00", +"* c #A500FF", +"!!!!!!!!!!####!!!!#!", +"!!!!!!!!!#!!!!##!##!", +"!!!!!!!!!!!!!!!!###!", +"!!!!!!!!!!!!!!!####!", +"!!!!!!!!!!!!!!#####!", +"!$$$$!!!!!!!!!!!!!!!", +"$%&%&$$$$$$$$!!!!!!!", +"$&%&%&%&%&%&$!!!!!!!", +"$%&&&$$$$$&&$!!!!!!!", +"$&&&$((((($&$!!!!!!!", +"$%&$)(($$$$$$$$$$$$$", +"$&$)))$***********$$", +"$%$))$***********$$!", +"$&$)$***********$$!!", +"$%$$***********$$!!!", +"$&$***********$$!!!!", +"$$***********$$!!!!!", +"$$***********$!!!!!!", +"$$$$$$$$$$$$$!!!!!!!", +"!!!!!!!!!!!!!!!!!!!!" +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/file_print.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/file_print.xpm new file mode 100644 index 00000000..915f65ba --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/file_print.xpm @@ -0,0 +1,115 @@ +/* XPM */ +static const char *file_print[] = { +/* columns rows colors chars-per-pixel */ +"20 20 89 1", +" c Gray0", +". c #101008081010", +"X c #101010101010", +"o c #101010101818", +"O c #181810101818", +"+ c #181818181818", +"@ c #181818182121", +"# c #212118182121", +"$ c Gray13", +"% c #212121212929", +"& c #292921212929", +"* c Gray16", +"= c #292929293131", +"- c #313129293131", +"; c #313131313131", +": c #313131313939", +"> c #393931313939", +", c #393939393939", +"< c #393939394242", +"1 c #424239394242", +"2 c Gray26", +"3 c #4a4a4a4a5252", +"4 c #5a5a52525a5a", +"5 c #5a5a5a5a6363", +"6 c #6b6b63636b6b", +"7 c Gray42", +"8 c #6b6b6b6b7373", +"9 c #73736b6b7373", +"0 c #7b7b73737b7b", +"q c #7b7b73738484", +"w c #0808ffff0808", +"e c #2929ffff2929", +"r c #3131ffff3131", +"t c #5a5acece5a5a", +"y c #6b6bffff6363", +"u c #7b7bffff7b7b", +"i c #84847b7b8484", +"p c #84847b7b8c8c", +"a c #8c8c7b7b9494", +"s c #848484848c8c", +"d c #8c8c84848c8c", +"f c Gray55", +"g c #8c8c84849494", +"h c #8c8c8c8c9494", +"j c #94948c8c9494", +"k c #94948c8c9c9c", +"l c Gray58", +"z c #949494949c9c", +"x c #9c9c94949c9c", +"c c Gray61", +"v c #9c9c9494a5a5", +"b c #9c9c9c9ca5a5", +"n c #a5a59c9ca5a5", +"m c #a5a59c9cadad", +"M c #adad9c9cadad", +"N c #a5a5a5a5a5a5", +"B c #a5a5a5a5adad", +"V c #adada5a5adad", +"C c Gray68", +"Z c #adadadadb5b5", +"A c #b5b5adadb5b5", +"S c Gray71", +"D c Gray74", +"F c #9494c6c69494", +"G c #9c9ccecea5a5", +"H c #bdbdd6d6bdbd", +"J c #c0c0c0c0c0c0", +"K c #c6c6c6c6c6c6", +"L c #cecec6c6cece", +"P c #cececececece", +"I c #cecececed6d6", +"U c #d6d6ceced6d6", +"Y c #d6d6cecedede", +"T c Gray84", +"R c #d6d6d6d6dede", +"E c #deded6d6dede", +"W c Gray87", +"Q c #deded6d6e7e7", +"! c #dedededee7e7", +"~ c #d6d6ffffd6d6", +"^ c #e7e7dedee7e7", +"/ c #e7e7e7e7e7e7", +"( c #e7e7e7e7efef", +") c #efefe7e7efef", +"_ c #efefefefefef", +"` c #e7e7ffffe7e7", +"' c Gray97", +"] c Gray100", +"[ c None", +/* pixels */ +"[[[[[[SDPPKKDDCD[[[[", +"[[[[[[D_///___WD[[[[", +"[[[[[[DKKPKKKKDK[[[[", +"[[[[[[SDDSDDSSCD[[[[", +"[[[[[KCKDKDDDKS[[[[[", +"[[[[[KDDDDDDDDS[[[[[", +"[[[[[CP/WWWWTWNNZ[[[", +"[[[Dc9STPTPTWWj427S[", +"[[Dziq00000pag8<%@2N", +"[DcE(!ERRUYGtFn2##O<", +"Db)]]]]]]]~ewePa;@X#", +"V']]]]]]]]`yru]Q0@ #", +"BRILITRRWE!RHUILhO @", +"jAZVBmBnmmnmMvzh6o #", +"jZZmBnnnbbbbvxxg6o +", +"lmmnbnbbbvxxxvjs6O 3", +"jBnnvcvxvxvxzjhd8o+C", +"lsdgfgdhghjhjkhg6+l[", +"S9%@$%&&&-::>>:-:l[[", +"[[C511,:;**%++.2c[[[" +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/file_save.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/file_save.xpm new file mode 100644 index 00000000..61638992 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/file_save.xpm @@ -0,0 +1,33 @@ +/* XPM */ +static const char *file_save[] = { +"20 20 9 1", +" c white", +"! c #000", +"# c #002EFF", +"$ c #999999999999", +"% c None", +"& c #00C900", +"( c #ffff00", +") c red1", +"* c black", +"!!!!!!!!!!!!!!!!!!!!", +"!##!$$$!!!!!!$$$!%%!", +"!##!$$!&&&&&&!$$!%%!", +"!##!$!(&&&&&&&!$!!!!", +"!##!!(((&&&&&&&!!##!", +"!##!!((((&&&&&&!!##!", +"!##!!((((()&&&&!!##!", +"!##!!((())))&&&!!##!", +"!##!!(())))))&&!!##!", +"!##!$!))))))))!$!##!", +"!###!$!))))))!$*###!", +"!####***!!!!!***###!", +"!##################!", +"!###!!!!!!!!!!!!!##!", +"!###!!!!!!!!!$$$!##!", +"!###!!!!!!!!!$$$!##!", +"!###!!!!!!!!!$$$!##!", +"!###!!!!!!!!!$$$!##!", +"!###!!!!!!!!!$$$!##!", +"%!!!!!!!!!!!!!!!!!!%" +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/file_saveaspostscript.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/file_saveaspostscript.xpm new file mode 100644 index 00000000..7dd75fcf --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/file_saveaspostscript.xpm @@ -0,0 +1,34 @@ +/* XPM */ +static const char *file_saveaspostscript[] = { +"20 20 10 1", +" c white", +"! c #000", +"# c #002EFF", +"$ c #999999999999", +"% c None", +"& c #00C900", +"( c #ffff00", +") c red1", +"* c black", +"+ c grey100", +"!!!!!!!!!!!!!!!!!!!!", +"!##!$$$!!!!!!$$$!%%!", +"!##!$$!&&&&&&!$$!%%!", +"!##!$!(&&&&&&&!$!!!!", +"!##!!(((&&&&&&&!!##!", +"!##!!((((&&&&&&!!##!", +"!##!!((((()&&&&!!##!", +"!##!!((())))&&&!!##!", +"!##!!(())))))&&!!##!", +"!##!$!))))))))!$!##!", +"!###!$!))))))!$*###!", +"!####***!!!!!***###!", +"!##################!", +"!###!!!!!!!!!!!!!##!", +"!###!+++!+++!$$$!##!", +"!###!+!+!+!!!$$$!##!", +"!###!+++!+++!$$$!##!", +"!###!+!!!!!+!$$$!##!", +"!###!+!!!+++!$$$!##!", +"%!!!!!!!!!!!!!!!!!!%" +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/options_horizontalbarchart.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/options_horizontalbarchart.xpm new file mode 100644 index 00000000..afb06ffa --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/options_horizontalbarchart.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static const char *options_horizontalbarchart[] = { +"20 20 7 1", +"( c #A500FF", +" c white", +"! c red1", +"# c None", +"$ c #E9FF00", +"% c #00C900", +"& c #002EFF", +"!!!!!!!!!!!!!!######", +"!!!!!!!!!!!!!!######", +"!!!!!!!!!!!!!!######", +"!!!!!!!!!!!!!!######", +"$$$$$$$$$$$$$$$$$###", +"$$$$$$$$$$$$$$$$$###", +"$$$$$$$$$$$$$$$$$###", +"$$$$$$$$$$$$$$$$$###", +"%%%%%%%%%%%%%%%%%%%%", +"%%%%%%%%%%%%%%%%%%%%", +"%%%%%%%%%%%%%%%%%%%%", +"%%%%%%%%%%%%%%%%%%%%", +"&&&&&&&&&&&&&&######", +"&&&&&&&&&&&&&&######", +"&&&&&&&&&&&&&&######", +"&&&&&&&&&&&&&&######", +"(((((((((((#########", +"(((((((((((#########", +"(((((((((((#########", +"(((((((((((#########" +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/options_piechart.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/options_piechart.xpm new file mode 100644 index 00000000..221c78de --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/options_piechart.xpm @@ -0,0 +1,30 @@ +/* XPM */ +static const char *options_piechart[] = { +"20 20 6 1", +" c white", +"! c None", +"# c black", +"$ c #00C900", +"% c #E9FF00", +"& c red1", +"!!!!!!#######!!!!!!!", +"!!!!##$$$$$$$##!!!!!", +"!!!#%$$$$$$$$$$#!!!!", +"!!#%%%$$$$$$$$$$#!!!", +"!#%%%%%$$$$$$$$$$#!!", +"!#%%%%%$$$$$$$$$$#!!", +"#%%%%%%%$$$$$$$$$$#!", +"#%%%%%%%%$$$$$$$$$#!", +"#%%%%%%%%$$$$$$$$$#!", +"#%%%%%%%%%$$$$$$$$#!", +"#%%%%%%%%&&$$$$$$$#!", +"#%%%%%%%&&&&$$$$$$#!", +"#%%%%%%&&&&&&&$$$$#!", +"#%%%%%&&&&&&&&&$$$#!", +"!#%%%&&&&&&&&&&&&#!!", +"!#%%&&&&&&&&&&&&&#!!", +"!!#&&&&&&&&&&&&&#!!!", +"!!!#&&&&&&&&&&&#!!!!", +"!!!!##&&&&&&&##!!!!!", +"!!!!!!#######!!!!!!!" +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/options_setdata.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/options_setdata.xpm new file mode 100644 index 00000000..4ff70a54 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/options_setdata.xpm @@ -0,0 +1,34 @@ +/* XPM */ +static const char *options_setdata[] = { +"20 20 10 1", +" c white", +"! c None", +"# c grey40", +"$ c #002EFF", +"% c black", +"& c red1", +"( c #00C900", +") c #A500FF", +"* c #E9FF00", +"+ c cyan1", +"!!!!!!!!!!!!!!!!!!!!", +"!#####!$$!!#####!%%!", +"!#####!$$!!#####!%%!", +"!!!!!!!!!!!!!!!!!!!!", +"!#####!&&!!#####!%%!", +"!#####!&&!!#####!%%!", +"!!!!!!!!!!!!!!!!!!!!", +"!#####!((!!#####!%%!", +"!#####!((!!#####!%%!", +"!!!!!!!!!!!!!!!!!!!!", +"!#####!))!!#####!%%!", +"!#####!))!!#####!%%!", +"!!!!!!!!!!!!!!!!!!!!", +"!#####!**!!#####!%%!", +"!#####!**!!#####!%%!", +"!!!!!!!!!!!!!!!!!!!!", +"!#####!++!!#####!%%!", +"!#####!++!!#####!%%!", +"!!!!!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!!!!!" +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/options_setfont.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/options_setfont.xpm new file mode 100644 index 00000000..ab552248 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/options_setfont.xpm @@ -0,0 +1,27 @@ +/* XPM */ +static const char *options_setfont[] = { +"20 20 3 1", +" c white", +"! c None", +"# c #002EFF", +"!!!!!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!#####!!!", +"!!!!!!!!!!!#######!!", +"!!!!!!!!!!!##!!!!##!", +"!!!!!!!!!!##!!!!###!", +"!!!!!!!!!!##!!!!###!", +"!!!!!!!!!###!!!!!!!!", +"!!!!!!!!!##!!!!!!!!!", +"!!!!!#############!!", +"!!!!###!!########!!!", +"!!!##!!!!##!!!!!!!!!", +"!!!##!!!!#!!!!!!!!!!", +"!!!!!!!!##!!!!!!!!!!", +"!!!!!!!!##!!!!!!!!!!", +"!!!!!!!###!!!!!!!!!!", +"!!!!!!!##!!!!!!!!!!!", +"!!!!!!##!!!!!!!!!!!!", +"!#!!!###!!!!!!!!!!!!", +"!######!!!!!!!!!!!!!", +"!!####!!!!!!!!!!!!!!" +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/options_setoptions.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/options_setoptions.xpm new file mode 100644 index 00000000..029cf47d --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/options_setoptions.xpm @@ -0,0 +1,32 @@ +/* XPM */ +static const char *options_setoptions[] = { +"20 20 8 1", +"( c #A500FF", +" c white", +") c grey30", +"! c None", +"# c #000", +"$ c grey60", +"% c grey100", +"& c black", +"!!!!!!!!!!!!!!!!!!!!", +"!#############$$$$$!", +"!#%%%%%%%%%%%%&$$$$!", +"!#%%%%%%%%%%%%%&$$$!", +"!#%%%%%%%%%%%%%%&$$!", +"!#%%%%%%%%%%%%%%%&$!", +"!#%%((%%%%%%%%%%%%#!", +"!#%%((%%%%%%%%%%%%#!", +"!#%%((%%%%%%%%%%%%#!", +"!#(((((((%%%%%%%%%#!", +"!#(((((((%%%%%%%%%#!", +"!#%%((%%%%%)%))))%#!", +"!#%%((%%%%%%%%%%%%#!", +"!#%%((%)))%)))%))%#!", +"!#%%%%%%%%%%%%%%%%#!", +"!#%%))%)%))))%))%%#!", +"!#%%%%%%%%%%%%%%%%#!", +"!#%%%%%%%%%%%%%%%%#!", +"!##################!", +"!!!!!!!!!!!!!!!!!!!!" +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/options_verticalbarchart.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/options_verticalbarchart.xpm new file mode 100644 index 00000000..e812f0f9 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/options_verticalbarchart.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static const char *options_verticalbarchart[] = { +"20 20 7 1", +"( c #A500FF", +" c white", +"! c None", +"# c #00C900", +"$ c #E9FF00", +"% c red1", +"& c #002EFF", +"!!!!!!!!####!!!!!!!!", +"!!!!!!!!####!!!!!!!!", +"!!!!!!!!####!!!!!!!!", +"!!!!$$$$####!!!!!!!!", +"!!!!$$$$####!!!!!!!!", +"!!!!$$$$####!!!!!!!!", +"%%%%$$$$####&&&&!!!!", +"%%%%$$$$####&&&&!!!!", +"%%%%$$$$####&&&&!!!!", +"%%%%$$$$####&&&&((((", +"%%%%$$$$####&&&&((((", +"%%%%$$$$####&&&&((((", +"%%%%$$$$####&&&&((((", +"%%%%$$$$####&&&&((((", +"%%%%$$$$####&&&&((((", +"%%%%$$$$####&&&&((((", +"%%%%$$$$####&&&&((((", +"%%%%$$$$####&&&&((((", +"%%%%$$$$####&&&&((((", +"%%%%$$$$####&&&&((((" +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/pattern01.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/pattern01.xpm new file mode 100644 index 00000000..26a70cbd --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/pattern01.xpm @@ -0,0 +1,27 @@ +/* XPM */ +static const char *pattern01[] = { +/* columns rows colors chars-per-pixel */ +"40 20 1 1", +" c black", +/* pixels */ +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" " +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/pattern02.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/pattern02.xpm new file mode 100644 index 00000000..cc5b7948 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/pattern02.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static const char *pattern02[] = { +/* columns rows colors chars-per-pixel */ +"40 20 2 1", +" c black", +". c white", +/* pixels */ +". . . . . . . . . . ", +" ", +" ", +" ", +". . . . . . . . . . ", +" ", +" ", +" ", +". . . . . . . . . . ", +" ", +" ", +" ", +". . . . . . . . . . ", +" ", +" ", +" ", +". . . . . . . . . . ", +" ", +" ", +" " +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/pattern03.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/pattern03.xpm new file mode 100644 index 00000000..d9fc57a9 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/pattern03.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static const char *pattern03[] = { +/* columns rows colors chars-per-pixel */ +"40 20 2 1", +" c black", +". c white", +/* pixels */ +" . . . . . . . . . . ", +" ", +". . . . . . . . . . ", +" ", +" . . . . . . . . . . ", +" ", +". . . . . . . . . . ", +" ", +" . . . . . . . . . . ", +" ", +". . . . . . . . . . ", +" ", +" . . . . . . . . . . ", +" ", +". . . . . . . . . . ", +" ", +" . . . . . . . . . . ", +" ", +". . . . . . . . . . ", +" " +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/pattern04.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/pattern04.xpm new file mode 100644 index 00000000..85b9223b --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/pattern04.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static const char *pattern04[] = { +/* columns rows colors chars-per-pixel */ +"40 20 2 1", +" c black", +". c white", +/* pixels */ +". . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +" . . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +". . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +" . . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +". . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +" . . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +". . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +" . . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +". . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +" . . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . ." +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/pattern05.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/pattern05.xpm new file mode 100644 index 00000000..cc7beee9 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/pattern05.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static const char *pattern05[] = { +/* columns rows colors chars-per-pixel */ +"40 20 2 1", +" c black", +". c white", +/* pixels */ +" . . . . . . . . . . . . . . . . . . . .", +". . . . . . . . . . . . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +". . . . . . . . . . . . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +". . . . . . . . . . . . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +". . . . . . . . . . . . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +". . . . . . . . . . . . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +". . . . . . . . . . . . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +". . . . . . . . . . . . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +". . . . . . . . . . . . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +". . . . . . . . . . . . . . . . . . . . ", +" . . . . . . . . . . . . . . . . . . . .", +". . . . . . . . . . . . . . . . . . . . " +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/pattern06.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/pattern06.xpm new file mode 100644 index 00000000..ad8b0554 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/pattern06.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static const char *pattern06[] = { +/* columns rows colors chars-per-pixel */ +"40 20 2 1", +" c black", +". c white", +/* pixels */ +".. ... ... ... ... ... ... ... ... ... .", +". . . . . . . . . . . . . . . . . . . . ", +" ... ... ... ... ... ... ... ... ... ...", +". . . . . . . . . . . . . . . . . . . . ", +".. ... ... ... ... ... ... ... ... ... .", +". . . . . . . . . . . . . . . . . . . . ", +" ... ... ... ... ... ... ... ... ... ...", +". . . . . . . . . . . . . . . . . . . . ", +".. ... ... ... ... ... ... ... ... ... .", +". . . . . . . . . . . . . . . . . . . . ", +" ... ... ... ... ... ... ... ... ... ...", +". . . . . . . . . . . . . . . . . . . . ", +".. ... ... ... ... ... ... ... ... ... .", +". . . . . . . . . . . . . . . . . . . . ", +" ... ... ... ... ... ... ... ... ... ...", +". . . . . . . . . . . . . . . . . . . . ", +".. ... ... ... ... ... ... ... ... ... .", +". . . . . . . . . . . . . . . . . . . . ", +" ... ... ... ... ... ... ... ... ... ...", +". . . . . . . . . . . . . . . . . . . . " +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/pattern07.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/pattern07.xpm new file mode 100644 index 00000000..d01c55f9 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/pattern07.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static const char *pattern07[] = { +/* columns rows colors chars-per-pixel */ +"40 20 2 1", +" c black", +". c white", +/* pixels */ +" ... ... ... ... ... ... ... ... ... ...", +"........................................", +".. ... ... ... ... ... ... ... ... ... .", +"........................................", +" ... ... ... ... ... ... ... ... ... ...", +"........................................", +".. ... ... ... ... ... ... ... ... ... .", +"........................................", +" ... ... ... ... ... ... ... ... ... ...", +"........................................", +".. ... ... ... ... ... ... ... ... ... .", +"........................................", +" ... ... ... ... ... ... ... ... ... ...", +"........................................", +".. ... ... ... ... ... ... ... ... ... .", +"........................................", +" ... ... ... ... ... ... ... ... ... ...", +"........................................", +".. ... ... ... ... ... ... ... ... ... .", +"........................................" +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/pattern08.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/pattern08.xpm new file mode 100644 index 00000000..b0ce09fe --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/pattern08.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static const char *pattern08[] = { +/* columns rows colors chars-per-pixel */ +"40 20 2 1", +" c black", +". c white", +/* pixels */ +"........................................", +"........................................", +"... ... ... ... ... ... ... ... ... ... ", +"........................................", +"........................................", +"........................................", +"... ... ... ... ... ... ... ... ... ... ", +"........................................", +"........................................", +"........................................", +"... ... ... ... ... ... ... ... ... ... ", +"........................................", +"........................................", +"........................................", +"... ... ... ... ... ... ... ... ... ... ", +"........................................", +"........................................", +"........................................", +"... ... ... ... ... ... ... ... ... ... ", +"........................................" +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/pattern09.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/pattern09.xpm new file mode 100644 index 00000000..7d34bc42 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/pattern09.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static const char *pattern09[] = { +/* columns rows colors chars-per-pixel */ +"40 20 2 1", +" c black", +". c white", +/* pixels */ +"........................................", +" ", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +" ", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +" ", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +" " +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/pattern10.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/pattern10.xpm new file mode 100644 index 00000000..c908016d --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/pattern10.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static const char *pattern10[] = { +/* columns rows colors chars-per-pixel */ +"40 20 2 1", +" c black", +". c white", +/* pixels */ +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... .", +".. ..... ..... ..... ..... ..... ..... ." +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/pattern11.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/pattern11.xpm new file mode 100644 index 00000000..8feda9a4 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/pattern11.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static const char *pattern11[] = { +/* columns rows colors chars-per-pixel */ +"40 20 2 1", +" c black", +". c white", +/* pixels */ +". ..... ..... ..... ..... ..... ..... ..", +". ..... ..... ..... ..... ..... ..... ..", +" ", +". ..... ..... ..... ..... ..... ..... ..", +". ..... ..... ..... ..... ..... ..... ..", +". ..... ..... ..... ..... ..... ..... ..", +". ..... ..... ..... ..... ..... ..... ..", +". ..... ..... ..... ..... ..... ..... ..", +" ", +". ..... ..... ..... ..... ..... ..... ..", +". ..... ..... ..... ..... ..... ..... ..", +". ..... ..... ..... ..... ..... ..... ..", +". ..... ..... ..... ..... ..... ..... ..", +". ..... ..... ..... ..... ..... ..... ..", +" ", +". ..... ..... ..... ..... ..... ..... ..", +". ..... ..... ..... ..... ..... ..... ..", +". ..... ..... ..... ..... ..... ..... ..", +". ..... ..... ..... ..... ..... ..... ..", +". ..... ..... ..... ..... ..... ..... .." +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/pattern12.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/pattern12.xpm new file mode 100644 index 00000000..a57233f2 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/pattern12.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static const char *pattern12[] = { +/* columns rows colors chars-per-pixel */ +"40 20 2 1", +" c black", +". c white", +/* pixels */ +"..... ....... ....... ....... ....... ..", +".... ....... ....... ....... ....... ...", +"... ....... ....... ....... ....... ....", +".. ....... ....... ....... ....... .....", +". ....... ....... ....... ....... ......", +" ....... ....... ....... ....... .......", +"....... ....... ....... ....... ....... ", +"...... ....... ....... ....... ....... .", +"..... ....... ....... ....... ....... ..", +".... ....... ....... ....... ....... ...", +"... ....... ....... ....... ....... ....", +".. ....... ....... ....... ....... .....", +". ....... ....... ....... ....... ......", +" ....... ....... ....... ....... .......", +"....... ....... ....... ....... ....... ", +"...... ....... ....... ....... ....... .", +"..... ....... ....... ....... ....... ..", +".... ....... ....... ....... ....... ...", +"... ....... ....... ....... ....... ....", +".. ....... ....... ....... ....... ....." +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/pattern13.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/pattern13.xpm new file mode 100644 index 00000000..97f874fe --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/pattern13.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static const char *pattern13[] = { +/* columns rows colors chars-per-pixel */ +"40 20 2 1", +" c black", +". c white", +/* pixels */ +" ....... ....... ....... ....... .......", +". ....... ....... ....... ....... ......", +".. ....... ....... ....... ....... .....", +"... ....... ....... ....... ....... ....", +".... ....... ....... ....... ....... ...", +"..... ....... ....... ....... ....... ..", +"...... ....... ....... ....... ....... .", +"....... ....... ....... ....... ....... ", +" ....... ....... ....... ....... .......", +". ....... ....... ....... ....... ......", +".. ....... ....... ....... ....... .....", +"... ....... ....... ....... ....... ....", +".... ....... ....... ....... ....... ...", +"..... ....... ....... ....... ....... ..", +"...... ....... ....... ....... ....... .", +"....... ....... ....... ....... ....... ", +" ....... ....... ....... ....... .......", +". ....... ....... ....... ....... ......", +".. ....... ....... ....... ....... .....", +"... ....... ....... ....... ....... ...." +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/images/pattern14.xpm b/qtruby/rubylib/examples/qt-examples/chart/images/pattern14.xpm new file mode 100644 index 00000000..e9e68845 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/images/pattern14.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static const char *pattern14[] = { +/* columns rows colors chars-per-pixel */ +"40 20 2 1", +" c black", +". c white", +/* pixels */ +"... . ..... . ..... . ..... . ..... . ..", +".. ... ... ... ... ... ... ... ... ... .", +". ..... . ..... . ..... . ..... . ..... ", +" ....... ....... ....... ....... .......", +". ..... . ..... . ..... . ..... . ..... ", +".. ... ... ... ... ... ... ... ... ... .", +"... . ..... . ..... . ..... . ..... . ..", +".... ....... ....... ....... ....... ...", +"... . ..... . ..... . ..... . ..... . ..", +".. ... ... ... ... ... ... ... ... ... .", +". ..... . ..... . ..... . ..... . ..... ", +" ....... ....... ....... ....... .......", +". ..... . ..... . ..... . ..... . ..... ", +".. ... ... ... ... ... ... ... ... ... .", +"... . ..... . ..... . ..... . ..... . ..", +".... ....... ....... ....... ....... ...", +"... . ..... . ..... . ..... . ..... . ..", +".. ... ... ... ... ... ... ... ... ... .", +". ..... . ..... . ..... . ..... . ..... ", +" ....... ....... ....... ....... ......." +}; diff --git a/qtruby/rubylib/examples/qt-examples/chart/main.rb b/qtruby/rubylib/examples/qt-examples/chart/main.rb new file mode 100644 index 00000000..db395a3e --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/main.rb @@ -0,0 +1,26 @@ +require 'Qt' + +require 'canvasview.rb' +require 'canvastext.rb' +require 'element.rb' +require 'chartform.rb' +require 'chartform_canvas.rb' +require 'chartform_files.rb' +require 'optionsform.rb' +require 'setdataform.rb' + +app = Qt::Application.new( ARGV ) + +if app.ARGV.length > 0 + filename = app.ARGV[0] + if filename.rindex( /.cht$/ ).nil? + filename = nil + end +end + +cf = ChartForm.new( filename ) +app.mainWidget = cf +cf.show + +app.exec + diff --git a/qtruby/rubylib/examples/qt-examples/chart/optionsform.rb b/qtruby/rubylib/examples/qt-examples/chart/optionsform.rb new file mode 100644 index 00000000..6b2eeac4 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/optionsform.rb @@ -0,0 +1,127 @@ +class OptionsForm < Qt::Dialog + slots 'chooseFont()' + + attr_reader :chartTypeComboBox, + :noRadioButton, + :yesRadioButton, + :asPercentageRadioButton, + :decimalPlacesSpinBox, + :font + + def initialize( parent = nil, name = "options form", + modal = false, f = 0 ) + super( parent, name, modal, f ) + setCaption( "Chart -- Options" ) + resize( 320, 290 ) + + @optionsFormLayout = Qt::VBoxLayout.new( self, 11, 6 ) + + @chartTypeLayout = Qt::HBoxLayout.new( nil, 0, 6 ) + + @chartTypeTextLabel = Qt::Label.new( "&Chart Type", self ) + @chartTypeLayout.addWidget( @chartTypeTextLabel ) + + @chartTypeComboBox = Qt::ComboBox.new( false, self ) + @chartTypeComboBox.insertItem( Qt::Pixmap.new( "images/options_piechart.xpm" ), "Pie Chart" ) + @chartTypeComboBox.insertItem( Qt::Pixmap.new( "images/options_verticalbarchart.xpm" ), + "Vertical Bar Chart" ) + @chartTypeComboBox.insertItem( Qt::Pixmap.new( "images/options_horizontalbarchart.xpm" ), + "Horizontal Bar Chart" ) + @chartTypeLayout.addWidget( @chartTypeComboBox ) + @optionsFormLayout.addLayout( @chartTypeLayout ) + + @fontLayout = Qt::HBoxLayout.new( nil, 0, 6 ) + + @fontPushButton = Qt::PushButton.new( "&Font...", self ) + @fontLayout.addWidget( @fontPushButton ) + @spacer = Qt::SpacerItem.new( 0, 0, Qt::SizePolicy::Expanding, + Qt::SizePolicy::Minimum ) + @fontLayout.addItem( @spacer ) + + @fontTextLabel = Qt::Label.new( self ) # Must be set by caller via setFont() + @fontLayout.addWidget( @fontTextLabel ) + @optionsFormLayout.addLayout( @fontLayout ) + + @addValuesFrame = Qt::Frame.new( self ) + @addValuesFrame.setFrameShape( Qt::Frame::StyledPanel ) + @addValuesFrame.setFrameShadow( Qt::Frame::Sunken ) + @addValuesFrameLayout = Qt::VBoxLayout.new( @addValuesFrame, 11, 6 ) + + @addValuesButtonGroup = Qt::ButtonGroup.new( "Show Values", @addValuesFrame ) + @addValuesButtonGroup.setColumnLayout(0, Qt::Vertical ) + @addValuesButtonGroup.layout().setSpacing( 6 ) + @addValuesButtonGroup.layout().setMargin( 11 ) + @addValuesButtonGroupLayout = Qt::VBoxLayout.new( + @addValuesButtonGroup.layout() ) + @addValuesButtonGroupLayout.setAlignment( Qt::AlignTop ) + + @noRadioButton = Qt::RadioButton.new( "&No", @addValuesButtonGroup ) + @noRadioButton.setChecked( true ) + @addValuesButtonGroupLayout.addWidget( @noRadioButton ) + + @yesRadioButton = Qt::RadioButton.new( "&Yes", @addValuesButtonGroup ) + @addValuesButtonGroupLayout.addWidget( @yesRadioButton ) + + @asPercentageRadioButton = Qt::RadioButton.new( "As &Percentage", + @addValuesButtonGroup ) + @addValuesButtonGroupLayout.addWidget( @asPercentageRadioButton ) + @addValuesFrameLayout.addWidget( @addValuesButtonGroup ) + + @decimalPlacesLayout = Qt::HBoxLayout.new( nil, 0, 6 ) + + @decimalPlacesTextLabel = Qt::Label.new( "&Decimal Places", @addValuesFrame ) + @decimalPlacesLayout.addWidget( @decimalPlacesTextLabel ) + + @decimalPlacesSpinBox = Qt::SpinBox.new( @addValuesFrame ) + @decimalPlacesSpinBox.setMinValue( 0 ) + @decimalPlacesSpinBox.setMaxValue( 9 ) + @decimalPlacesLayout.addWidget( @decimalPlacesSpinBox ) + + @addValuesFrameLayout.addLayout( @decimalPlacesLayout ) + + @optionsFormLayout.addWidget( @addValuesFrame ) + + @buttonsLayout = Qt::HBoxLayout.new( nil, 0, 6 ) + @spacer = Qt::SpacerItem.new( 0, 0, + Qt::SizePolicy::Expanding, Qt::SizePolicy::Minimum ) + @buttonsLayout.addItem( @spacer ) + + @okPushButton = Qt::PushButton.new( "OK", self ) + @okPushButton.setDefault( true ) + @buttonsLayout.addWidget( @okPushButton ) + + @cancelPushButton = Qt::PushButton.new( "Cancel", self ) + @buttonsLayout.addWidget( @cancelPushButton ) + @optionsFormLayout.addLayout( @buttonsLayout ) + + connect( @fontPushButton, SIGNAL( 'clicked()' ), self, SLOT( 'chooseFont()' ) ) + connect( @okPushButton, SIGNAL( 'clicked()' ), self, SLOT( 'accept()' ) ) + connect( @cancelPushButton, SIGNAL( 'clicked()' ), self, SLOT( 'reject()' ) ) + + @chartTypeTextLabel.setBuddy( @chartTypeComboBox ) + @decimalPlacesTextLabel.setBuddy( @decimalPlacesSpinBox ) + end + + + def chooseFont() + ok = Qt::Boolean.new + font = Qt::FontDialog.getFont( ok, @font, self ) + if !ok.nil? + setFont( font ) + end + end + + + def font=( font ) + label = font.family() + " " + font.pointSize().to_s + "pt" + if font.bold() + label += " Bold" + end + if font.italic() + label += " Italic" + end + @fontTextLabel.setText( label ) + @font = font + end + +end diff --git a/qtruby/rubylib/examples/qt-examples/chart/setdataform.rb b/qtruby/rubylib/examples/qt-examples/chart/setdataform.rb new file mode 100644 index 00000000..81a9403b --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/chart/setdataform.rb @@ -0,0 +1,184 @@ +class SetDataForm < Qt::Dialog + + slots 'setColor()', + 'setChosenColor( int, int )', + 'currentChanged( int, int )', + 'valueChanged( int, int )', + 'accept()' + + MAX_PATTERNS = 14 + + + def initialize( elements, decimalPlaces, + parent = nil, name = "set data form", + modal = true, f = 0 ) + super( parent, name, modal, f ) + + @elements = elements + @decimalPlaces = decimalPlaces + + setCaption( "Chart -- Set Data" ) + resize( 540, 440 ) + + @tableButtonBox = Qt::VBoxLayout.new( self, 11, 6, "@table button box layout" ) + + @table = Qt::Table.new( self, "data @table" ) + @table.setNumCols( 5 ) + @table.setNumRows( ChartForm::MAX_ELEMENTS ) + @table.setColumnReadOnly( 1, true ) + @table.setColumnReadOnly( 2, true ) + @table.setColumnReadOnly( 4, true ) + @table.setColumnWidth( 0, 80 ) + @table.setColumnWidth( 1, 60 ) # Columns 1 and 4 must be equal + @table.setColumnWidth( 2, 60 ) + @table.setColumnWidth( 3, 200 ) + @table.setColumnWidth( 4, 60 ) + th = @table.horizontalHeader() + th.setLabel( 0, "Value" ) + th.setLabel( 1, "Color" ) + th.setLabel( 2, "Pattern" ) + th.setLabel( 3, "Label" ) + th.setLabel( 4, "Color" ) + @tableButtonBox.addWidget( @table ) + + @buttonBox = Qt::HBoxLayout.new( nil, 0, 6, "button box layout" ) + + @colorPushButton = Qt::PushButton.new( self, "color button" ) + @colorPushButton.setText( "&Color..." ) + @colorPushButton .setEnabled( false ) + @buttonBox.addWidget( @colorPushButton ) + + spacer = Qt::SpacerItem.new( 0, 0, Qt::SizePolicy::Expanding, + Qt::SizePolicy::Minimum ) + @buttonBox.addItem( spacer ) + + okPushButton = Qt::PushButton.new( self, "ok button" ) + okPushButton.setText( "OK" ) + okPushButton.setDefault( true ) + @buttonBox.addWidget( okPushButton ) + + cancelPushButton = Qt::PushButton.new( self, "cancel button" ) + cancelPushButton.setText( "Cancel" ) + cancelPushButton.setAccel( Qt::KeySequence.new(Key_Escape) ) + @buttonBox.addWidget( cancelPushButton ) + + @tableButtonBox.addLayout( @buttonBox ) + + connect( @table, SIGNAL( 'clicked(int,int,int,const QPoint&)' ), + self, SLOT( 'setChosenColor(int,int)' ) ) + connect( @table, SIGNAL( 'currentChanged(int,int)' ), + self, SLOT( 'currentChanged(int,int)' ) ) + connect( @table, SIGNAL( 'valueChanged(int,int)' ), + self, SLOT( 'valueChanged(int,int)' ) ) + connect( @colorPushButton, SIGNAL( 'clicked()' ), self, SLOT( 'setColor()' ) ) + connect( okPushButton, SIGNAL( 'clicked()' ), self, SLOT( 'accept()' ) ) + connect( cancelPushButton, SIGNAL( 'clicked()' ), self, SLOT( 'reject()' ) ) + + patterns = Array.new(MAX_PATTERNS) + patterns[0] = Qt::Pixmap.new( "images/pattern01.xpm" ) + patterns[1] = Qt::Pixmap.new( "images/pattern02.xpm" ) + patterns[2] = Qt::Pixmap.new( "images/pattern03.xpm" ) + patterns[3] = Qt::Pixmap.new( "images/pattern04.xpm" ) + patterns[4] = Qt::Pixmap.new( "images/pattern05.xpm" ) + patterns[5] = Qt::Pixmap.new( "images/pattern06.xpm" ) + patterns[6] = Qt::Pixmap.new( "images/pattern07.xpm" ) + patterns[7] = Qt::Pixmap.new( "images/pattern08.xpm" ) + patterns[8] = Qt::Pixmap.new( "images/pattern09.xpm" ) + patterns[9] = Qt::Pixmap.new( "images/pattern10.xpm" ) + patterns[10] = Qt::Pixmap.new( "images/pattern11.xpm" ) + patterns[11] = Qt::Pixmap.new( "images/pattern12.xpm" ) + patterns[12] = Qt::Pixmap.new( "images/pattern13.xpm" ) + patterns[13] = Qt::Pixmap.new( "images/pattern14.xpm" ) + + rect = @table.cellRect( 0, 1 ) + pix = Qt::Pixmap.new( rect.width(), rect.height() ) + + for i in 0...ChartForm::MAX_ELEMENTS + element = @elements[i] + + if element.isValid() + @table.setText(i, 0, "%.#{@decimalPlaces}f" % element.value() ) + end + + color = element.valueColor() + pix.fill( color ) + @table.setPixmap( i, 1, pix ) + @table.setText( i, 1, color.name() ) + + combobox = Qt::ComboBox.new + for j in 0...MAX_PATTERNS + combobox.insertItem( patterns[j] ) + end + combobox.setCurrentItem( element.valuePattern() - 1 ) + @table.setCellWidget( i, 2, combobox ) + + @table.setText( i, 3, element.label() ) + + color = element.labelColor() + pix.fill( color ) + @table.setPixmap( i, 4, pix ) + @table.setText( i, 4, color.name() ) + end + + end + + + def currentChanged( i, col ) + @colorPushButton.setEnabled( col == 1 || col == 4 ) + end + + + def valueChanged( row, col ) + if col == 0 + d = @table.text( row, col ).to_f + if d && d > EPSILON + @table.setText( row, col, "%.#{@decimalPlaces}f" % d ) + elsif ! @table.text( row, col ).empty? + @table.setText( row, col, @table.text( row, col ) + "?" ) + end + end + end + + + def setColor() + setChosenColor( @table.currentRow(), @table.currentColumn() ) + @table.setFocus() + end + + + def setChosenColor( row, col ) + if !( col == 1 || col == 4 ) + return + end + + color = Qt::ColorDialog.getColor( + Qt::Color.new( @table.text( row, col ) ), + self, "color dialog" ) + if color.isValid() + pix = @table.pixmap( row, col ) + pix.fill( color ) + @table.setPixmap( row, col, pix ) + @table.setText( row, col, color.name() ) + end + end + + + def accept() + for i in 0...ChartForm::MAX_ELEMENTS + element = @elements[i] + d = @table.text( i, 0 ).to_f + if d + element.value = d + else + element.value = Element::INVALID + end + element.valueColor = Qt::Color.new( @table.text( i, 1 ) ) + element.valuePattern = (@table.cellWidget( i, 2 )).currentItem() + 1 + element.label = @table.text( i, 3 ) + element.labelColor = Qt::Color.new( @table.text( i, 4 ) ) + end + + super + end + +end diff --git a/qtruby/rubylib/examples/qt-examples/checklists/checklists.rb b/qtruby/rubylib/examples/qt-examples/checklists/checklists.rb new file mode 100644 index 00000000..8f67d9aa --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/checklists/checklists.rb @@ -0,0 +1,147 @@ +require 'Qt' + +class CheckLists < Qt::Widget + slots 'copy1to2()', 'copy2to3()' + + # Constructor + # + # Create all child widgets of the CheckList Widget + def initialize + super() + + lay = Qt::HBoxLayout.new(self) + lay.setMargin(5) + + # create a widget which layouts its childs in a column + vbox1 = Qt::VBoxLayout.new(lay) + vbox1.setMargin(5) + + # First child: a Label + vbox1.addWidget(Qt::Label.new('Check some items!', self)) + + # Second child: the ListView + @lv1 = Qt::ListView.new(self) + vbox1.addWidget(@lv1) + @lv1.addColumn('Items') + @lv1.setRootIsDecorated(true) + + # create a list with 4 ListViewItems which will be parent items of other ListViewItems + parentList = Array.new + + + parentList.push(Qt::ListViewItem.new(@lv1, 'Parent Item 1')) + parentList.push(Qt::ListViewItem.new(@lv1, 'Parent Item 2')) + parentList.push(Qt::ListViewItem.new(@lv1, 'Parent Item 3')) + parentList.push(Qt::ListViewItem.new(@lv1, 'Parent Item 4')) + + item = 0 + num = 1 + # go through the list of parent items... + parentList.each {|item| + item.setOpen(true) + # ...and create 5 checkable child ListViewItems for each parent item + for i in 1..5 + str = sprintf('%s. Child of Parent %s', i, num) + Qt::CheckListItem.new(item, str, Qt::CheckListItem.CheckBox) + end + num = num + 1 + } + + # Create another widget for layouting + tmp = Qt::VBoxLayout.new(lay) + tmp.setMargin(5) + + # create a pushbutton + copy1 = Qt::PushButton.new(' -> ', self) + tmp.addWidget(copy1) + copy1.setMaximumWidth(copy1.sizeHint.width) + # connect the SIGNAL clicked() of the pushbutton with the SLOT copy1to2() + connect(copy1, SIGNAL('clicked()'), self, SLOT('copy1to2()')) + + # another widget for layouting + vbox2 = Qt::VBoxLayout.new(lay) + vbox2.setMargin(5) + + # and another label + vbox2.addWidget(Qt::Label.new('Check one item!', self)) + + # create the second listview + @lv2 = Qt::ListView.new(self) + vbox2.addWidget(@lv2) + @lv2.addColumn('Items') + @lv2.setRootIsDecorated(true) + + # another widget needed for layouting only + tmp = Qt::VBoxLayout.new(lay) + tmp.setMargin(5) + + # create another pushbutton... + copy2 = Qt::PushButton.new(' -> ', self) + lay.addWidget( copy2 ) + copy2.setMaximumWidth(copy2.sizeHint.width) + # ...and connect its clicked() SIGNAL to the copy2to3() SLOT + connect(copy2, SIGNAL('clicked()'), self, SLOT('copy2to3()')) + + tmp = Qt::VBoxLayout.new(lay) + tmp.setMargin(5) + + # and create a label which will be at the right of the window + @label = Qt::Label.new('No Item yet...', self) + tmp.addWidget(@label) + end + + # SLOT copy1to2() + # + # Copies all checked ListViewItems from the first ListView to + # the second one, and inserts them as Radio-ListViewItem. + def copy1to2 + @lv2.clear + it = Qt::ListViewItemIterator.new(@lv1) + # Insert first a controller Item into the second ListView. Always if Radio-ListViewItems + # are inserted into a Listview, the parent item of these MUST be a controller Item! + item = Qt::CheckListItem.new(@lv2, 'Controller', Qt::CheckListItem::Controller ); + item.setOpen(true); + + # iterate through the first ListView... + while (it.current) + # ...check state of childs, and... + if ( it.current.parent ) + # ...if the item is checked... + if (it.current.isOn) + # ...insert a Radio-ListViewItem with the same text into the second ListView + Qt::CheckListItem.new(item, it.current.text(0), Qt::CheckListItem::RadioButton) + end + end + it += 1 + end + + if (item.firstChild) + item.firstChild.setOn(true) + end + end + + + # SLOT copy2to3() + # + # Copies the checked item of the second ListView into the + # Label at the right. + def copy2to3 + # create an iterator which operates on the second ListView + it = Qt::ListViewItemIterator.new(@lv2) + + @label.setText('No Item checked') + + # iterate through the second ListView... + while (it.current) + # ...check state of childs, and... + if ( it.current.parent) + # ...if the item is checked... + if (it.current.isOn) + # ...set the text of the item to the label + @label.setText(it.current.text(0)) + end + end + it += 1 + end + end +end diff --git a/qtruby/rubylib/examples/qt-examples/checklists/main.rb b/qtruby/rubylib/examples/qt-examples/checklists/main.rb new file mode 100755 index 00000000..0c0e755c --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/checklists/main.rb @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +require 'Qt' + +require 'checklists' + +a = Qt::Application.new(ARGV) + +checklists = CheckLists.new +checklists.resize(650, 350) +checklists.setCaption('QtRuby Example - CheckLists') +a.setMainWidget(checklists) +checklists.show + +a.exec() diff --git a/qtruby/rubylib/examples/qt-examples/dclock/dclock.rb b/qtruby/rubylib/examples/qt-examples/dclock/dclock.rb new file mode 100644 index 00000000..6ac52c4c --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/dclock/dclock.rb @@ -0,0 +1,67 @@ +require 'Qt' + +class DigitalClock < Qt::LCDNumber + + slots 'stopDate()', 'showTime()' + + # Constructs a DigitalClock widget + def initialize + super + + @showingColon = false + setFrameStyle(Qt::Frame.Panel | Qt::Frame.Raised) + setLineWidth(2) # set frame line width + showTime # display the current time + @normalTimer = startTimer(500) # 1/2 second timer events + @showDateTimer = -1 # not showingdate + end + + # Handles timer events for the digital clock widget. + # There are two different timers; one timer for updating the clock + # and another one for switching back from date mode to time mode. + def timerEvent (e) + if (e.timerId == @showDateTimer) # stop showing date + stopDate + else # normal timer + if (@showDateTimer == -1) # not showing date + showTime() + end + end + end + + # Enters date mode when the left mouse button is pressed. + def mousePressEvent (e) + if (e.button == Qt::MouseEvent.LeftButton) # left button pressed + showDate + end + end + + def stopDate + killTimer(@showDateTimer) + @showDateTimer = -1 + showTime + end + + def showTime + @showingColon = !@showingColon # toggle/blink colon + s = Qt::Time.currentTime.toString[0..4] + if (!@showingColon) + s[2] = ' ' + end + if (s[0] == '0') + s[0] = ' ' + end + display(s) # set LCD number/text + end + + def showDate + if (@showDateTimer != -1) # already showing date + return + end + date = Qt::Date.currentDate + s = sprintf('%2d %2d', date.month, date.day) + display(s) # sets the LCD number/text + @showDateTimer = startTimer(2000) # keep this state for 2 secs + end + +end diff --git a/qtruby/rubylib/examples/qt-examples/dclock/main.rb b/qtruby/rubylib/examples/qt-examples/dclock/main.rb new file mode 100755 index 00000000..c76ae55d --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/dclock/main.rb @@ -0,0 +1,12 @@ +#!/usr/bin/env ruby + +require 'Qt' +require 'dclock' + +a = Qt::Application.new(ARGV) +clock = DigitalClock.new +clock.resize(170,80) +a.setMainWidget(clock) +clock.setCaption('QtRuby Example - Digital Clock') +clock.show +a.exec diff --git a/qtruby/rubylib/examples/qt-examples/fonts/simple-qfont-demo/main.rb b/qtruby/rubylib/examples/qt-examples/fonts/simple-qfont-demo/main.rb new file mode 100755 index 00000000..9559a921 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/fonts/simple-qfont-demo/main.rb @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby + +require 'Qt' +require 'viewer' +$KCODE='u' + + +a = Qt::Application.new(ARGV) + +textViewer = Viewer.new +textViewer.setCaption('QtRuby Example - Simple QFont Demo') +a.setMainWidget(textViewer) +textViewer.show +a.exec() diff --git a/qtruby/rubylib/examples/qt-examples/fonts/simple-qfont-demo/viewer.rb b/qtruby/rubylib/examples/qt-examples/fonts/simple-qfont-demo/viewer.rb new file mode 100644 index 00000000..d9c16a62 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/fonts/simple-qfont-demo/viewer.rb @@ -0,0 +1,140 @@ +class Viewer < Qt::Widget + slots 'setDefault()', 'setSansSerif()', 'setItalics()' + + def initialize + super + + setFontSubstitutions + + codec = Qt::TextCodec::codecForName("utf8") + + # Shouldn't 'pack("U*")' for UTF-8 work here? - Richard + # The 'U' option packs each element into two bytes and doesn't work + # The 'c' option packs them into a single byte and does work + greeting_heb = codec.toUnicode([0327, 0251, 0327, 0234, 0327, 0225, 0327, 0235].pack("C*")) + greeting_ru = codec.toUnicode([0320, 0227, 0320, 0264, 0321, 0200, 0320, 0260, 0320, 0262, 0321, 0201, 0321, 0202, 0320, 0262, 0321, 0203, 0320, 0271, 0321, 0202, 0320, 0265].pack("C*")) + greeting_en = 'Hello' + + @greetings = Qt::TextView.new(self, 'textview') + @greetings.setText( + greeting_en + "\n" + + greeting_ru + "\n" + + greeting_heb) + + @fontInfo = Qt::TextView.new(self, 'fontinfo') + + setDefault + + @defaultButton = Qt::PushButton.new('Default', self, 'pushbutton1') + @defaultButton.setFont(Qt::Font.new('times')) + connect(@defaultButton, SIGNAL('clicked()'), self, SLOT('setDefault()')) + + + @sansSerifButton = Qt::PushButton.new('Sans Serif', self, 'pushbutton2') + @sansSerifButton.setFont(Qt::Font.new('Helvetica', 12)) + connect(@sansSerifButton, SIGNAL('clicked()'), self, SLOT('setSansSerif()')) + + @italicsButton = Qt::PushButton.new('Italics', self, 'pushbutton1') + @italicsButton.setFont(Qt::Font.new('lucida', 12, Qt::Font.Bold, true)) + connect(@italicsButton, SIGNAL('clicked()'), self, SLOT('setItalics()')) + + layout + end + + def setDefault + font = Qt::Font.new('Bavaria') + font.setPointSize(24) + font.setWeight(Qt::Font.Bold) + font.setUnderline(true) + + @greetings.setFont(font) + showFontInfo(font) + end + + def setSansSerif + font = Qt::Font.new('Newyork', 18) + font.setStyleHint(Qt::Font.SansSerif) + + @greetings.setFont(font) + showFontInfo(font) + end + + def setItalics + font = Qt::Font.new('Tokyo') + font.setPointSize(32) + font.setWeight(Qt::Font.Bold) + font.setItalic(true) + + @greetings.setFont(font) + showFontInfo(font) + end + + def setFontSubstitutions + substitutes = Array.new + + substitutes.push('Times') + substitutes.push('Mincho') + substitutes.push('Arabic Newspaper') + substitutes.push('crox') + + Qt::Font.insertSubstitutions('Bavaria', substitutes) + Qt::Font.insertSubstitution('Tokyo', 'Lucida') + end + + def layout + textViewContainer = Qt::HBoxLayout.new + textViewContainer.addWidget(@greetings) + textViewContainer.addWidget(@fontInfo) + + buttonContainer = Qt::HBoxLayout.new + buttonContainer.addWidget(@defaultButton) + buttonContainer.addWidget(@sansSerifButton) + buttonContainer.addWidget(@italicsButton) + + maxButtonHeight = @defaultButton.height + + if (@sansSerifButton.height > maxButtonHeight) + maxButtonHeight = @sansSerifButton.height + end + + if (@italicsButton.height > maxButtonHeight) + maxButtonHeight = @italicsButton.height + end + + @defaultButton.setFixedHeight(maxButtonHeight) + @sansSerifButton.setFixedHeight(maxButtonHeight) + @italicsButton.setFixedHeight(maxButtonHeight) + + container = Qt::VBoxLayout.new(self) + container.addLayout(textViewContainer) + container.addLayout(buttonContainer) + + resize(700, 250) + end + + def showFontInfo (font) + info = Qt::FontInfo.new(font) + messageText = + 'Font requested: "' + + font.family + '" ' + + font.pointSize.to_s + 'pt<BR>' + + 'Font used: "' + + info.family.to_s + '" ' + + info.pointSize.to_s + 'pt<P>' + + substitutions = Qt::Font.substitutes(font.family) + + unless substitutions.size == 0 + messageText = messageText + 'The following substitutions exist for ' + + font.family + ':<UL>' + substitutions.each {|x| + messageText = messageText + '<LI>"' + x + '"' + } + messageText = messageText + '</UL>' + else + messageText = messageText + 'No substitutions exist for ' + font.family + '.' + end + + @fontInfo.setText(messageText) + end +end diff --git a/qtruby/rubylib/examples/qt-examples/forever/forever.rb b/qtruby/rubylib/examples/qt-examples/forever/forever.rb new file mode 100755 index 00000000..46849784 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/forever/forever.rb @@ -0,0 +1,84 @@ +#!/usr/bin/env ruby -w + +require 'Qt' + +# +# Forever - a widget that draws rectangles forever. +# + +class Forever < Qt::Widget + + NUM_COLORS = 120 + # + # Constructs a Forever widget. + # + + slots 'updateCaption()' + + def initialize(*k) + super(nil) + @colors = [] + 0.upto(NUM_COLORS-1) do |a| + @colors[a] = Qt::Color.new( rand(255), + rand(255), + rand(255) ) + end + @rectangles = 0 + startTimer( 0 ) # run continuous timer + counter = Qt::Timer.new( self ) + connect( counter, SIGNAL("timeout()"), + self, SLOT("updateCaption()") ) + counter.start( 1000 ) + end + + + def updateCaption() + s = "Qt Example - Forever - " + @rectangles.to_s + " rectangles/second" + @rectangles = 0 + self.caption = s + end + + + # + # Handles paint events for the Forever widget. + # + + def paintEvent( e ) + paint = Qt::Painter.new( self ) # painter object + w = width() + h = height() + if w <= 0 || h <= 0 then + return + end + paint.setPen( NoPen ) # do not draw outline + paint.setBrush( @colors[rand(NUM_COLORS)]) # set random brush color + + p1 = Qt::Point.new( rand(w), rand(h)) # p1 = top left + p2 = Qt::Point.new( rand(w), rand(h)) # p2 = bottom right + + r = Qt::Rect.new( p1, p2 ) + paint.drawRect( r ) # draw filled rectangle + paint.end() + end + + # + # Handles timer events for the Forever widget. + # + + def timerEvent( e ) + 0.upto(99) do |i| + repaint( false ) # repaint, don't erase + end + @rectangles += 100 + end + + +end + +a = Qt::Application.new(ARGV) +always = Forever.new +always.resize( 400, 250 ) # start up with size 400x250 +a.mainWidget = always # set as main widget +always.caption = "QtRuby Example - Forever" +always.show +a.exec diff --git a/qtruby/rubylib/examples/qt-examples/hello/hello.rb b/qtruby/rubylib/examples/qt-examples/hello/hello.rb new file mode 100644 index 00000000..ce957c75 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/hello/hello.rb @@ -0,0 +1,78 @@ +require 'Qt' + +class Hello < Qt::Widget + + signals 'clicked()' + slots 'animate()' + + # Constructs a Hello widget. Starts a 40 ms animation timer + def initialize (text) + super() + + @b = 0 + @text = text + @sin_tbl = [0, 38, 71, 92, 100, 92, 71, 38, 0, -38, -71, -92, -100, -92, -71, -38] + timer = Qt::Timer.new(self); + connect(timer, SIGNAL('timeout()'), SLOT('animate()')) + timer.start(40); + + resize(260, 130) + end + + # This slot is called each time the timer fires. + def animate + @b = (@b + 1) & 15 + repaint(false) + end + + # Handles mouse button release events for the Hello widget. + # + # We emit the clicked() signal when the mouse is released inside + # the widget. + def mouseReleaseEvent(e) + if (rect.contains(e.pos)) + emit clicked + end + end + + # Handles paint events for the Hello widget. + # + # Flicker-free update. The text is first drawn in the pixmap and the + # pixmap is then blt'ed to the screen. + def paintEvent(e) + if @text.empty? + return + end + + # 1: Compute some sizes, positions etc. + fm = fontMetrics + + w = fm.width(@text) + 20 + h = fm.height * 2 + pmx = width/2 - w/2 + pmy = height/2 - h/2 + + # 2: Create the pixmap and fill it with the widget's background + pm = Qt::Pixmap.new(w, h) + pm.fill(self, pmx, pmy) + + # 3: Paint the pixmap. Cool wave effect + p = Qt::Painter.new; + x = 10 + y = h/2 + fm.descent + i = 0 + p.begin(pm) + p.setFont(font) + + for i in 0..@text.size-1 + j = (@b+i) & 15 + p.setPen(Qt::Color.new((15-j)*16,255,255,Qt::Color.Hsv) ) + p.drawText( x, y-@sin_tbl[j]*h/800, @text[i,1], 1 ) + x += fm.width(@text[i,1]) + end + p.end + + #4: Copy the pixmap to the Hello widget + bitBlt(self, pmx, pmy, pm) + end +end diff --git a/qtruby/rubylib/examples/qt-examples/hello/main.rb b/qtruby/rubylib/examples/qt-examples/hello/main.rb new file mode 100755 index 00000000..a6d3447f --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/hello/main.rb @@ -0,0 +1,23 @@ +#!/usr/bin/env ruby + +require 'Qt' +require 'hello' + +a = Qt::Application.new(ARGV) +s = '' + +s = ARGV[0..ARGV.size-1].join(' ') if ARGV.length + +if (s.empty?) + s = 'Hello, World' +end + +h = Hello.new(s) +h.setCaption('QtRuby says hello') +h.connect(h, SIGNAL('clicked()'), a, SLOT('quit()')) +h.setFont(Qt::Font.new('times', 32, Qt::Font.Bold)) # default font +h.setBackgroundColor(Qt::white) # default bg color +a.setMainWidget(h) +h.show + +a.exec diff --git a/qtruby/rubylib/examples/qt-examples/progress/progress.rb b/qtruby/rubylib/examples/qt-examples/progress/progress.rb new file mode 100644 index 00000000..02116958 --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/progress/progress.rb @@ -0,0 +1,275 @@ +#!/usr/bin/env ruby -w + +require 'Qt' + +class AnimatedThingy < Qt::Label + attr_accessor :label, :step + attr_accessor :ox0, :oy0, :ox1, :oy1 + attr_accessor :x0, :y0, :x1, :y1 + attr_accessor :dx0, :dx1, :dy0, :dy1 + NQIX = 10 + + def initialize(*k) + super(*k) + @label = k[1] + "\n... and wasting CPU\nwith this animation!\n" + @step = 0 + @ox0, @oy0, @ox1, @oy1 = *Array.new(4) { Array.new(10, 0) } + @x0 = @y0 = @x1 = @y1 = 0 + @dx0 = rand(8)+2 + @dy0 = rand(8)+2 + @dx1 = rand(8)+2 + @dy1 = rand(8)+2 + end + + def show + startTimer(100) unless isVisible + super + end + + def hide + super + killTimers() + end + + def sizeHint + Qt::Size.new(120,100) + end + + def inc(x, dx, b) + x += dx + if x < 0 + x = 0 + dx = rand(8) + 2 + elsif x >= b + x = b-1 + dx = -(rand(8)+2) + end + yield x, dx + end + + def timerEvent(e) + p = Qt::Painter.new(self) + pn = p.pen + pn.setWidth(2) + pn.setColor(backgroundColor) + p.setPen(pn) + + @step = (@step + 1) % NQIX + + p.drawLine(@ox0[@step], @oy0[@step], @ox1[@step], @oy1[@step]) + + inc(@x0, @dx0, width) { |x,dx| @x0, @dx0 = x, dx } + inc(@y0, @dy0, height) { |y,dy| @y0, @dy0 = y, dy } + inc(@x1, @dx1, width) { |x,dx| @x1, @dx1 = x, dx } + inc(@y1, @dy1, height) { |y,dy| @y1, @dy1 = y, dy } + @ox0[@step] = @x0 + @oy0[@step] = @y0 + @ox1[@step] = @x1 + @oy1[@step] = @y1 + + c = Qt::Color.new + c.setHsv( (@step*255)/NQIX, 255, 255 ) # rainbow effect + pn.setColor(c) + pn.setWidth(2) + p.setPen(pn) + p.drawLine(@ox0[@step], @oy0[@step], @ox1[@step], @oy1[@step]) + p.setPen(colorGroup().text()) + p.drawText(rect(), AlignCenter, @label) + p.end() + end + + def paintEvent(event) + p = Qt::Painter.new(self) + pn = p.pen() + pn.setWidth(2) + p.setPen(pn) + p.setClipRect(event.rect()) + 0.upto(NQIX-1) do |i| + c = Qt::Color.new() + c.setHsv( (i*255)/NQIX, 255, 255 ) # rainbow effect + pn.setColor(c) + p.setPen(pn) + p.drawLine(@ox0[i], @oy0[i], @ox1[i], @oy1[i]) + end + p.setPen(colorGroup().text()) + p.drawText(rect(), AlignCenter, @label) + p.end + end +end + +class CPUWaster < Qt::Widget + attr_accessor :menubar, :file, :options, :rects, :pb + attr_accessor :td_id , :ld_id, :dl_id, :cl_id, :md_id + attr_accessor :got_stop, :timer_driven, :default_label + slots 'drawItemRects(int)', 'doMenuItem(int)', 'stopDrawing()', 'timerDriven()' + slots 'loopDriven()', 'defaultLabel()', 'customLabel()', 'toggleMinimumDuration()' + + FIRST_DRAW_ITEM = 1000 + LAST_DRAW_ITEM = 1006 + + def initialize(*k) + super(*k) + + @menubar = Qt::MenuBar.new(self, "menu") + @pb = nil + + @file = Qt::PopupMenu.new + @menubar.insertItem( "&File", file ) + FIRST_DRAW_ITEM.upto(LAST_DRAW_ITEM) { + |i| file.insertItem( "#{drawItemRects(i)} Rectangles", i) + } + connect( menubar, SIGNAL('activated(int)'), self, SLOT('doMenuItem(int)') ) + @file.insertSeparator + @file.insertItem("Quit", $qApp, SLOT('quit()')) + @options = Qt::PopupMenu.new + @menubar.insertItem("&Options", options) + @td_id = options.insertItem("Timer driven", self, SLOT('timerDriven()')) + @ld_id = options.insertItem("Loop driven", self, SLOT('loopDriven()')) + @options.insertSeparator + @dl_id = options.insertItem("Default label", self, SLOT('defaultLabel()')) + @cl_id = options.insertItem("Custom label", self, SLOT('customLabel()')) + @options.insertSeparator + @md_id = options.insertItem("No minimum duration", self, SLOT('toggleMinimumDuration()')) + @options.setCheckable true + + loopDriven + defaultLabel + + setFixedSize(400, 300) + setBackgroundColor(black) + end + + def drawItemRects(id) + n = id - FIRST_DRAW_ITEM - 1 + r = 100 + n.downto(0) { |n| + r *= (n%3 != 0) ? 5 : 4 + } + r + end + + def doMenuItem(id) + draw drawItemRects(id) if id >= FIRST_DRAW_ITEM && id <= LAST_DRAW_ITEM + end + + def stopDrawing + @got_stop = true + end + + def timerDriven + @timer_driven = true + @options.setItemChecked(@td_id, true) + @options.setItemChecked(@ld_id, false) + end + + def loopDriven + @timer_driven = false + @options.setItemChecked(@td_id, false) + @options.setItemChecked(@ld_id, true) + end + + def defaultLabel + @default_label = true + @options.setItemChecked(@dl_id, true) + @options.setItemChecked(@cl_id, false) + end + + def customLabel + @default_label = false + @options.setItemChecked(@dl_id, false) + @options.setItemChecked(@cl_id, true) + end + + def toggleMinimumDuration + checked = @options.isItemChecked(@md_id) + @options.setItemChecked(@md_id, !checked) + end + + def timerEvent(e) + @pb.setProgress( @pb.totalSteps - @rects ) if @rects % 100 == 0 + @rects -= 1 + + painter = Qt::Painter.new(self) + + ww = width + wh = height + + if ww > 8 and wh > 8 + c = Qt::Color.new(rand(255), rand(255), rand(255)) + x = rand(ww - 8) + y = rand(wh - 8) + w = rand(ww - x) + h = rand(wh - y) + painter.fillRect(x, y, w, h, Qt::Brush.new(c)) + end + + painter.end() + + if @rects == 0 || @got_stop + @pb.setProgress(@pb.totalSteps) + painter = Qt::Painter.new(self) + painter.fillRect(0, 0, width(), height(), Qt::Brush.new(backgroundColor)) + painter.end() + enableDrawingItems(true) + killTimers() + @pb = nil + end + end + + def newProgressDialog(label, steps, modal) + d = Qt::ProgressDialog.new(label, "Cancel", steps, self, "progress", modal) + d.setMinimumDuration(0) if @options.isItemChecked(@md_id) + d.setLabel( AnimatedThingy.new(d, label) ) unless @default_label + d.show + d + end + + def enableDrawingItems(yes) + FIRST_DRAW_ITEM.upto(LAST_DRAW_ITEM) { + |i| menubar.setItemEnabled(i, yes) + } + end + + def draw(n) + if timer_driven + unless @pb.nil? + warn("This cannot happen!") + return + end + @rects = n + @pb = newProgressDialog("Drawing rectangles.\nUsing timer event.", n, false) + @pb.setCaption("Please Wait") + connect(@pb, SIGNAL('cancelled()'), self, SLOT('stopDrawing()')) + enableDrawingItems(false) + startTimer(0) + @got_stop = false + else + lpb = newProgressDialog("Drawing rectangles.\nUsing loop.", n, true) + lpb.setCaption("Please Wait") + + painter = Qt::Painter.new(self) + 0.upto(n) { |i| + if (i % 100) == 0 + lpb.setProgress(i) + break if lpb.wasCancelled + end + cw, ch = width, height + c = Qt::Color.new(rand(255), rand(255), rand(255)) + x = rand(cw - 8) + y = rand(cw - 8) + w = rand(cw - x) + h = rand(cw - y) + painter.fillRect(x, y, w, h, Qt::Brush.new(c)) + } + lpb.cancel + painter.fillRect(0, 0, width, height, Qt::Brush.new(backgroundColor)) + painter.end() + end + end +end + +a = Qt::Application.new(ARGV) +w = CPUWaster.new +w.show +a.setMainWidget(w) +a.exec diff --git a/qtruby/rubylib/examples/qt-examples/tictac/main.rb b/qtruby/rubylib/examples/qt-examples/tictac/main.rb new file mode 100755 index 00000000..024ae70c --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/tictac/main.rb @@ -0,0 +1,13 @@ +#!/usr/bin/env ruby -w + +require 'Qt' +require 'tictac' + +a = Qt::Application.new(ARGV) +n = 3 # get board size n + +ttt = TicTacToe.new(n) +a.setMainWidget(ttt) +ttt.setCaption('QtRuby Example - TicTac') +ttt.show() +a.exec() diff --git a/qtruby/rubylib/examples/qt-examples/tictac/tictac.rb b/qtruby/rubylib/examples/qt-examples/tictac/tictac.rb new file mode 100644 index 00000000..04ab8b9d --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/tictac/tictac.rb @@ -0,0 +1,311 @@ +require 'Qt' + +class TicTacButton < Qt::PushButton + + attr_accessor :btype + + Blank, Circle, Cross = 0, 1, 2 + + def initialize(p) + super(p) + @btype = Blank + end + + def drawButtonLabel(p) + r = rect() + p.setPen( Qt::Pen.new( Qt::white,2 ) ) # set fat pen + if (@btype == Circle) + p.drawEllipse( r.left()+4, r.top()+4, r.width()-8, r.height()-8 ) + elsif (@btype == Cross) # draw cross + p.drawLine( r.topLeft() +Qt::Point.new(4,4), r.bottomRight()-Qt::Point.new(4,4)) + p.drawLine( r.bottomLeft()+Qt::Point.new(4,-4),r.topRight() -Qt::Point.new(4,-4)) + end + super(p) + end +end + +class TicTacGameBoard < Qt::Widget + signals 'finished()' + slots 'buttonClicked()' + + Init, HumansTurn, HumanWon, ComputerWon, NobodyWon = 0, 1, 2, 3, 4 + + attr_accessor :state, :computer_starts + + def initialize (n, parent) + super(parent) + @state = Init + @nBoard = n + n = n*n + @computer_starts = false + @buttons = Array.new(n) + @btArray = Array.new(n) + + grid = Qt::GridLayout.new(self, n, n, 4) + p = Qt::Palette.new(Qt::blue) + + for i in (0..n-1) + ttb = TicTacButton.new(self) + ttb.setPalette(p) + ttb.setEnabled(false) + connect(ttb, SIGNAL('clicked()'), self, SLOT('buttonClicked()')) + grid.addWidget(ttb, i % @nBoard, i / @nBoard) + @buttons[i] = ttb + @btArray[i] = TicTacButton::Blank + end + end + + def newGame + @state = HumansTurn + for i in 0..(@nBoard*@nBoard)-1 + @btArray[i] = TicTacButton::Blank + end + if @computer_starts == true + computerMove + else + updateButtons + end + end + + def updateButtons + for i in 0..(@nBoard*@nBoard)-1 + if @buttons[i].btype != @btArray[i] + @buttons[i].btype = @btArray[i] + end + if @buttons[i].btype == TicTacButton::Blank + @buttons[i].setEnabled(true) + else + @buttons[i].setEnabled(false) + end + @buttons[i].repaint + end + end + + def checkBoard + t = 0 + row = 0 + col = 0 + won = false + + # check horizontal + for row in 0..@nBoard-1 + if won == true + break + end + t = @btArray[row*@nBoard] + if (t == TicTacButton::Blank) + next + end + col = 1 + while ( (col < @nBoard) && (@btArray[row*@nBoard+col] == t) ) + col += 1 + end + if (col == @nBoard) + won = true + end + end + + # check vertical + for col in 0..@nBoard-1 + if won == true + break + end + t = @btArray[col] + if (t == TicTacButton::Blank) + next + end + row = 1 + while ( (row < @nBoard) && (@btArray[row*@nBoard+col] == t) ) + row += 1 + end + if (row == @nBoard) + won = true + end + end + + # check diagonal top left to bottom right + if (won == false) + t = @btArray[0] + if (t != TicTacButton::Blank) + i = 1; + while (i<@nBoard && (@btArray[i*@nBoard+i] == t)) + i += 1 + end + if (i == @nBoard) + won = true + end + end + end + + # check diagonal bottom left to top right + if (won == false) + j = @nBoard-1 + i = 0; + t = @btArray[i+j*@nBoard]; + if (t != TicTacButton::Blank) + i += 1 + j -= 1 + while ( (i<@nBoard) && (@btArray[i+j*@nBoard] == t) ) + i += 1 + j -= 1 + end + if (i == @nBoard) + won = true + end + end + end + + if (won == false) + # no winner + t = 0 + end + + t + end + + def computerMove + numButtons = @nBoard*@nBoard + altv = Array.new + stopHuman = -1 + i = 0 + + for i in 0..numButtons-1 # try all positions + if @btArray[i] != TicTacButton::Blank # already a piece there + next + end + + @btArray[i] = TicTacButton::Cross # test if computer wins + if (checkBoard == @btArray[i]) # computer will win + @state = ComputerWon + stopHuman = -1 + break + end + + @btArray[i] = TicTacButton::Circle # test if human wins + if (checkBoard == @btArray[i]) # oops... + stopHuman = i # remember position + @btArray[i] = TicTacButton::Blank # restore button + next # computer still might win + end + @btArray[i] = TicTacButton::Blank; # restore button + altv.push(i) # remember alternative + end + + if (stopHuman >= 0) # must stop human from winning + @btArray[stopHuman] = TicTacButton::Cross + elsif (i == numButtons-1) # tried all alternatives + if (altv.size > 0) # set random piece + @btArray[altv[rand(altv.size)]] = TicTacButton::Cross + end + if ((altv.size-1) == 0) # no more blanks + @state = NobodyWon + emit finished() + end + end + updateButtons # update buttons + end + + def buttonClicked + unless @state == HumansTurn + return + end + + at = nil + for i in 0..@buttons.size + if @buttons[i].object_id == sender.object_id + at = i + break + end + end + if @btArray[at] == TicTacButton::Blank + @btArray[at] = TicTacButton::Circle + updateButtons + + if (checkBoard == 0) + computerMove + end + s = checkBoard + if (s != 0) + if (s == TicTacButton::Circle) + @state = HumanWon + else + @state = ComputerWon + end + emit finished() + end + end + end + +end + +class TicTacToe < Qt::Widget + slots 'newGameClicked()', 'gameOver()' + + def initialize (boardSize) + super() + + l = Qt::VBoxLayout.new(self, 6) + + @state_msg = [ + 'Click Play to start', + 'Make your move', + 'You won!', + 'Computer won!', + 'It\'s a draw'] + + # Create a message label + @message = Qt::Label.new(self) + @message.setFrameStyle((Qt::Frame.WinPanel|Qt::Frame.Sunken)) + @message.setAlignment(Qt::AlignCenter) + l.addWidget(@message) + + # Create the game board and connect the signal finished() + # to this/self gameOver() slot + @board = TicTacGameBoard.new(boardSize, self) + connect(@board, SIGNAL('finished()'), self, SLOT('gameOver()')); + l.addWidget(@board) + + # Create a horizontal frame line + line = Qt::Frame.new(self) + line.setFrameStyle(Qt::Frame.HLine|Qt::Frame.Sunken) + l.addWidget(line) + + # Create the combo box for deciding who should start + # and connect its clicked() signals to the buttonClicked() slot + @whoStarts = Qt::ComboBox.new(self) + @whoStarts.insertItem('Computer starts') + @whoStarts.insertItem('Human starts') + l.addWidget(@whoStarts); + + # Create the push buttons and connect their signals to the right slots + @newGame = Qt::PushButton.new('Play!', self) + connect(@newGame, SIGNAL('clicked()'), self, SLOT('newGameClicked()')) + @quit = Qt::PushButton.new('Quit', self) + connect(@quit, SIGNAL('clicked()'), $qApp, SLOT('quit()')) + b = Qt::HBoxLayout.new + l.addLayout(b) + b.addWidget(@newGame) + b.addWidget(@quit) + + newState() + end + + def newState + @message.setText(@state_msg[@board.state]) + end + + def newGameClicked + if @whoStarts.currentItem == 0 + @board.computer_starts = true + else + @board.computer_starts = false + end + @board.newGame() + newState() + end + + def gameOver + # Update text box + newState() + end +end diff --git a/qtruby/rubylib/examples/qt-examples/tooltip/main.rb b/qtruby/rubylib/examples/qt-examples/tooltip/main.rb new file mode 100755 index 00000000..f1f9ccda --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/tooltip/main.rb @@ -0,0 +1,12 @@ +#!/usr/bin/env ruby + +require 'Qt' +require 'tooltip' + +a = Qt::Application.new(ARGV) + +mw = TellMe.new +mw.setCaption('QtRuby Example - Dynamic Tool Tips') +a.setMainWidget(mw) +mw.show +a.exec diff --git a/qtruby/rubylib/examples/qt-examples/tooltip/tooltip.rb b/qtruby/rubylib/examples/qt-examples/tooltip/tooltip.rb new file mode 100644 index 00000000..181816da --- /dev/null +++ b/qtruby/rubylib/examples/qt-examples/tooltip/tooltip.rb @@ -0,0 +1,95 @@ +require 'Qt' + +class DynamicTip < Qt::ToolTip + def initialize(p) + super(p) + end + + def maybeTip(p) + if !parentWidget.inherits('TellMe') + return + end + + r = parentWidget.tip(p) + if !r.isValid + return + end + + s = 'position: ' + r.center.x.to_s + ', ' + r.center.y.to_s + tip(r,s) + end +end + +class TellMe < Qt::Widget + + def initialize + super + + setMinimumSize(30, 30) + + @r1 = randomRect + @r2 = randomRect + @r3 = randomRect + + @t = DynamicTip.new(self) + + Qt::ToolTip.add(self, @r3, 'this color is called red') #TT says this is helpful, I'm not so sure + end + + def tip(point) + if (@r1.contains(point)) + @r1 + elsif (@r2.contains(point)) + @r2 + else + Qt::Rect.new(0,0, -1, -1) + end + end + + def paintEvent(e) + p = Qt::Painter.new(self) + + if (e.rect.intersects(@r1)) + p.setBrush(Qt::blue) + p.drawRect(@r1) + end + + if (e.rect.intersects(@r2)) + p.setBrush(Qt::blue) + p.drawRect(@r2) + end + + if (e.rect.intersects(@r3)) + p.setBrush(Qt::red) + p.drawRect(@r3) + end + + p.end + end + + def mousePressEvent (e) + if (@r1.contains(e.pos)) + @r1 = randomRect + end + + if (@r2.contains(e.pos)) + @r2 = randomRect + end + + repaint + end + + def resizeEvent(e) + unless rect.contains(@r1) + @r1 = randomRect + end + + unless rect.contains(@r2) + @r2 = randomRect + end + end + + def randomRect + Qt::Rect.new(rand(width - 20), rand(height - 20), 20, 20) + end +end diff --git a/qtruby/rubylib/examples/qtscribble/scribble.rb b/qtruby/rubylib/examples/qtscribble/scribble.rb new file mode 100644 index 00000000..7c6e1ca5 --- /dev/null +++ b/qtruby/rubylib/examples/qtscribble/scribble.rb @@ -0,0 +1,274 @@ +#!/usr/bin/env ruby -w + + # + # A class that lets the user draw with the mouse. The + # window knows how to redraw itself. + # + +require 'Qt' + + class ScribbleArea < Qt::Widget + + slots "setColor(QColor)", "slotLoad(const QString&)", "slotSave(const QString&)", "slotClearArea()" + + # + # The constructor. Initializes the member variables. + # + def initialize() + super + # initialize member variables + @_buffer = Qt::Pixmap.new() + @_last = Qt::Point.new() + @_currentcolor = black + + # don't blank the window before repainting + setBackgroundMode( NoBackground ) + + # create a pop-up menu + @_popupmenu = Qt::PopupMenu.new() + @_popupmenu.insertItem( "&Clear", self, SLOT( "slotClearArea()" ) ) + end + + # + # This slot sets the curren color for the scribble area. It will be + # connected with the colorChanged( Qt::Color ) signal from the + # ScribbleWindow. + # + def setColor( new_color ) + @_currentcolor = new_color + end + + # + # This slot clears the drawing area by filling the off-screen buffer with + # white and copying it over to the window. + # + def slotClearArea() + # fill the off screen buffer with plain white + @_buffer.fill( white ) + + # and copy it over to the window + bitBlt( self, 0, 0, @_buffer ) + end + + + # + # This method does the actual loading. It relies on Qt::Pixmap (and the + # underlying I/O machinery) to determine the filetype. + # + def slotLoad( filename ) + if !@_buffer.load( filename ) + Qt::MessageBox.warning( nil, "Load error", "Could not load file" ) + end + + repaint() # refresh the window + end + + + # + # This method does the actual saving. We hard-code the file type as + # BMP. Unix users might want to replace this with something like XPM. + # + def slotSave( filename ) + if !@_buffer.save( filename, "BMP" ) + Qt::MessageBox.warning( nil, "Save error", "Could not save file" ) + end + end + + + # + # This method is called whenever the user presses the + # mouse over the window. It just records the position of the mouse + # at the time of the click. + # + def mousePressEvent(event) + if event.button() == RightButton + @_popupmenu.exec( Qt::Cursor.pos() ) + else + @_last = event.pos() # retrieve the coordinates from the event + end + end + + + # + # The method is called whenever the usr moves the mouse + # while the mouse button is pressed. If we had called + # setMouseTracking(true) before, the method would also be called + # when the mouse was moved with any button pressed. We know that + # we haven't, and thus don't have to check whether any buttons are + # pressed. + # + def mouseMoveEvent(event) + # create a Qt::Painter object for drawing onto the window + windowpainter = Qt::Painter.new() + # and another Qt::Painter object for drawing int an off-screen pixmap + bufferpainter = Qt::Painter.new() + + # start painting + windowpainter.begin( self ) # This painter paints onto the window + bufferpainter.begin( @_buffer ) # and this one paints in the buffer + + # set a standard pen with the currently selected color + windowpainter.setPen( @_currentcolor ) + bufferpainter.setPen( @_currentcolor ) + + # draw a line in both the window and the buffer + windowpainter.drawLine( @_last, event.pos() ) + bufferpainter.drawLine( @_last, event.pos() ) + + # done with painting + windowpainter.end() + bufferpainter.end() + + # remember the current mouse position + @_last = event.pos() + end + + # + # This method is called whenever the widget needs + # painting, for example when it has been obscured and then revealed again. + # + def paintEvent(event) + bitBlt(self, 0, 0, @_buffer) + end + + # + # This method get called whenever the widget needs + # painting, for example, when it has been obscured and then revealed again. + # + def resizeEvent(event) + save = Qt::Pixmap.new( @_buffer ) + @_buffer.resize( event.size() ) + @_buffer.fill( white ) + bitBlt( @_buffer, 0, 0, save ) + end + end + +class ScribbleWindow < Qt::Widget + + slots "slotAbout()", "slotAboutQt()", "slotColorMenu(int)", "slotLoad()", "slotSave()" + signals "colorChanged(QColor)", "load(const QString&)", "save(const QString&)" + + COLOR_MENU_ID_BLACK = 0 + COLOR_MENU_ID_RED = 1 + COLOR_MENU_ID_BLUE = 2 + COLOR_MENU_ID_GREEN = 3 + COLOR_MENU_ID_YELLOW = 4 + + def initialize() + super + # The next lines build the menu bar. We first create the menus + # one by one, then add them to the menu bar. # + @_filemenu = Qt::PopupMenu.new() # create a file menu + @_filemenu.insertItem( "&Load", self, SLOT( "slotLoad()" ) ) + @_filemenu.insertItem( "&Save", self, SLOT( "slotSave()" ) ) + @_filemenu.insertSeparator() + @_filemenu.insertItem( "&Quit", $qApp, SLOT( "quit()" ) ) + + @_colormenu = Qt::PopupMenu.new() # create a color menu + @_colormenu.insertItem( "B&lack", COLOR_MENU_ID_BLACK) + @_colormenu.insertItem( "&Red", COLOR_MENU_ID_RED) + @_colormenu.insertItem( "&Blue", COLOR_MENU_ID_BLUE) + @_colormenu.insertItem( "&Green", COLOR_MENU_ID_GREEN) + @_colormenu.insertItem( "&Yellow", COLOR_MENU_ID_YELLOW) + Qt::Object.connect( @_colormenu, SIGNAL( "activated( int )" ), + self, SLOT( "slotColorMenu( int )" ) ) + + @_helpmenu = Qt::PopupMenu.new() # create a help menu + @_helpmenu.insertItem( "&About QtScribble", self, SLOT( "slotAbout()" ) ) + @_helpmenu.insertItem( "&About Qt", self, SLOT( "slotAboutQt()" ) ) + + @_menubar = Qt::MenuBar.new( self, "" ) # create a menu bar + @_menubar.insertItem( "&File", @_filemenu ) + @_menubar.insertItem( "&Color", @_colormenu ) + @_menubar.insertItem( "&Help", @_helpmenu ) + + # We create a Qt::ScrollView and a ScribbleArea. The ScribbleArea will + # be managed by the scroll view.# + @_scrollview = Qt::ScrollView.new( self ) + @_scrollview.setGeometry( 0, @_menubar.height(), + width(), height() - @_menubar.height() ) + @_scribblearea = ScribbleArea.new() + @_scribblearea.setGeometry( 0, 0, 1000, 1000 ) + @_scrollview.addChild( @_scribblearea ) + Qt::Object.connect( self, SIGNAL( "colorChanged(QColor)" ), + @_scribblearea, SLOT( "setColor(QColor)" ) ) + Qt::Object.connect( self, SIGNAL( "save(const QString&)" ), + @_scribblearea, SLOT( "slotSave(const QString&)" ) ) + Qt::Object.connect( self, SIGNAL( "load(const QString&)" ), + @_scribblearea, SLOT( "slotLoad(const QString&)" ) ) + end + + def resizeEvent( event ) + # When the whole window is resized, we have to rearrange the geometry + # in the ScribbleWindow as well. Note that the ScribbleArea does not need + # to be changed. + @_scrollview.setGeometry( 0, @_menubar.height(), + width(), height() - @_menubar.height() ) + end + + + + def slotAbout() + Qt::MessageBox.information( self, "About QtScribble 5", + "This is the Scribble 5 application\n" + + "Copyright 1998 by Mathias Kalle Dalheimer\n") + end + + def slotAboutQt() + Qt::MessageBox.aboutQt( self, "About Qt" ) + end + + def slotColorMenu( item ) + case item + when COLOR_MENU_ID_BLACK + emit colorChanged( black ) + when COLOR_MENU_ID_RED + emit colorChanged( darkRed ) + when COLOR_MENU_ID_BLUE + emit colorChanged( darkBlue ) + when COLOR_MENU_ID_GREEN + emit colorChanged( darkGreen ) + when COLOR_MENU_ID_YELLOW + emit colorChanged( yellow ) + end + end + + + # + # This is the slot for the menu item File/Load. It opens a + # Qt::FileDialog to ask the user for a filename, then emits a save() + # signal with the filename as parameter. + # + def slotLoad() + # Open a file dialog for loading. The default directory is the + # current directory, the filter *.bmp. + # + filename = Qt::FileDialog.getOpenFileName( ".", "*.bmp", self ) + if !filename.nil? + emit load( filename ) + end + end + + # + # This is the slot for the menu item File/Load. It opens a + # Qt::FileDialog to ask the user for a filename, then emits a save() + # signal with the filename as parameter. + # + def slotSave() + # Open a file dialog for saving. The default directory is the + # current directory, the filter *.bmp. + # + filename = Qt::FileDialog.getSaveFileName( ".", "*.bmp", self ) + if !filename.nil? + emit save( filename ) + end + end +end + +myapp = Qt::Application.new(ARGV) +mywidget = ScribbleWindow.new() +mywidget.setGeometry(50, 500, 400, 400) + +myapp.setMainWidget(mywidget) +mywidget.show() +myapp.exec() diff --git a/qtruby/rubylib/examples/ruboids/Manifest b/qtruby/rubylib/examples/ruboids/Manifest new file mode 100644 index 00000000..4fe3f97a --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/Manifest @@ -0,0 +1,26 @@ +Manifest +README +TODO +boids.properties +generateManifest.rb +index.html +release.rb +ruboids/ + Boid.rb + BoidView.rb + Camera.rb + CameraDialog.rb + Canvas.rb + Cloud.rb + CloudView.rb + Flock.rb + Graphics.rb + Params.rb + Point.rb + Thing.rb + Triangle.rb + View.rb + World.rb + WorldWindow.rb + info.rb + ruboids.rb diff --git a/qtruby/rubylib/examples/ruboids/README b/qtruby/rubylib/examples/ruboids/README new file mode 100644 index 00000000..5417037a --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/README @@ -0,0 +1,53 @@ +INTRODUCTION +============ + +RuBoids is a Boids simulation written in Ruby and using OpenGL and Qt. For +information on Boids, see http://www.red3d.com/cwr/boids/. + +Ruby is an object-oriented scripting language by Yukihiro Matsumoto. The +official Ruby Web site (http://www.ruby-lang.org/) contains information and +pointers to resources for this wonderful language. + +RuBoids is developed and maintained by Jim Menard (<jimm@io.com>). The +latest version of RuBoids can be found on the Ruby Application Archive or +on the official RuBoids Web page +(http://www.io.com/~jimm/downloads/ruboids/). + +RUNNING +======= + + cd ruboids + ruboids.rb + +RuBoids looks for an optional properties file. If none is specified on the +command line, it looks for a file named 'boids.properties' in the current +directory. For example, to load the example properties file in this +directory: + + cd ruboids + ruboids.rb ../boids.properties + +DEPENDENCIES +============ + +RuBoids requires the OpenGL and Qt packages, which can be found on the Ruby +Application Archive. + +COPYING +======= + +RuBoids is copyrighted free software by Jim Menard and is released under the +same license as Ruby. See the Ruby license +(http://www.ruby-lang.org/en/LICENSE.txt). + +RuBoids may be freely copied in its entirety providing this notice, all +source code, all documentation, and all other files are included. + +RuBoids is copyright (c) 2001 by Jim Menard. + +WARRANTY +======== + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. diff --git a/qtruby/rubylib/examples/ruboids/TODO b/qtruby/rubylib/examples/ruboids/TODO new file mode 100644 index 00000000..25fe501d --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/TODO @@ -0,0 +1,29 @@ +BUGS +==== + +* Boid rotation. + +* Frustum. + +TO DO +===== + +* Comment the code. + +* More documentation. + +* Save/restore params (e.g. camera position). + +POSSIBLE ADDITIONS +================== + +* Make sphere output more efficient by using strips or fans. + +* Boids-eye view: camera follows position of a boid. This is not hard, but + I have to fix boid rotation first. + +* Velocity and destination influence proportional to distance from other + boids. Boids that are farther away have less influence on your velocity + and destination. + +* Boids that are behind you don't influence you because you can't see them. diff --git a/qtruby/rubylib/examples/ruboids/boids.properties b/qtruby/rubylib/examples/ruboids/boids.properties new file mode 100644 index 00000000..20bb5001 --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/boids.properties @@ -0,0 +1,33 @@ +# This is an example configuration file for RuBoids. It sets all of the +# possible properties. The values here are the default values set in +# Params.rb. + +world.sleep_millis = 75 +world.width = 400 +world.height = 400 +world.depth = 400 +window.width = 500 +window.height = 500 +flock.boids = 10 +boid.max_speed = 30 +boid.bounds_limit_pull = 5 +boid.bounds_limit_above_ground_level = 5 +boid.wing_length = 10 +boid.personal_space_dist = 12 +boid.square_of_personal_space_dist = 144 +boid.max_perching_turns = 150 +boid.perch_wing_flap_percent = 30 +cloud.count = 10 +cloud.min_speed = 2 +cloud.max_speed = 50 +cloud.min_bubbles = 3 +cloud.max_bubbles = 10 +cloud.max_bubble_radius = 10 +cloud.min_altitude = 250 +camera.x = 0 +camera.y = 0 +camera.z = 60 +camera.rot_x = 50 +camera.rot_y = 10 +camera.rot_z = 0 +camera.zoom = 1 diff --git a/qtruby/rubylib/examples/ruboids/generateManifest.rb b/qtruby/rubylib/examples/ruboids/generateManifest.rb new file mode 100755 index 00000000..f877b0b3 --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/generateManifest.rb @@ -0,0 +1,42 @@ +#! /usr/bin/env ruby +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# +# This script builds the Manifest file. It can be run stand-alone, but +# is normally used from within release.rb. +# + +def recurseDirectory(io, dirName, indentLevel) + Dir.entries(dirName).sort.each { | f | + next if f =~ /^\.\.?/ + fileName = "#{dirName}/#{f}" + fileName.sub!(/^\.\//, '') + if File.directory?(fileName) + io.puts "\t" * indentLevel + fileName + '/' + recurseDirectory(io, fileName, indentLevel + 1) + else + io.puts "\t" * indentLevel + f + end + } +end + +def generateManifest + io = nil + begin + io = File.open('Manifest', 'w') + recurseDirectory(io, '.', 0) + ensure + io.close() if io + end +end + +if $0 == __FILE__ + generateManifest() +end + + + + diff --git a/qtruby/rubylib/examples/ruboids/index.html b/qtruby/rubylib/examples/ruboids/index.html new file mode 100644 index 00000000..9b320f8e --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/index.html @@ -0,0 +1,147 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"> +<html> +<head> +<title>RuBoids</title> +<link rel="stylesheet" href="../../style.css" type="text/css"> +</head> +<body background="../../images/gradient_bg.gif" bgcolor="white"> +<!--#exec cgi="../../cgi-bin/log_visitor.cgi" --> + +<table width="100%"> + <tr> + <td valign="top"> + <table width="100" border="0" cellpadding="0" cellspacing="0"> + <tr> + <td valign="top"><font size="-1"> +<script language="JavaScript"><!-- +PREFIX = "../../"; // --> +</script> +<!-- #include virtual="/~jimm/menu.js"--> +<A href="../../index.html"> +<IMG src="../../images/KeyMaster.gif" +width="32" height="32" alt="Home" border="0"></A><BR> +<A href="../../index.html">Home</A><BR> +<A href="../nqxml/index.html">NQXML</A><BR> +RuBoids<BR> +<A href="../../computers.html">Computers</A><BR> +<A href="../../java.html">Java</A><BR> +<A href="../../beos.html">BeOS</A><BR> +<A href="../../ftp_sites.html">FTP sites</A><BR> +<A href="../../music.html">Music</A><BR> +<A href="../../midi_ref.html">MIDI Reference</A><BR> +<A href="../../keymaster.html">KeyMaster</A><BR> +<A href="../../MIDI_Through.html">MIDI Through</A><BR> +<A href="../../jimm.html">Narcissism</A><BR> +<A href="../../resume.html">Resume</A><BR> +<A href="../../urls.html">Links</A><BR> +<A href="../../map.html">Site Map</A><BR> + + + </font></td> + </tr> + </table> + </td> + <td valign="top"> + <table border="0" cellpadding="0" cellspacing="0"> + <tr valign="top"> + <td> + +<div align="right"> +<font size="+3"><b>RuBoids</b></font> +<img src="../../images/computer.gif" width="32" height="32" + alt="[home]" border="0"> +</div><br> +<hr> + +<h1>Introduction</h1> + +<p> +RuBoids is a Boids simulation written in Ruby and using OpenGL and Qt. For +information on Boids, see <a +href="http://www.red3d.com/cwr/boids/">http://www.red3d.com/cwr/boids/</a>. +</p> + +<p> +Ruby is an object-oriented scripting language by Yukihiro Matsumoto. Visit +the official <a href="http://www.ruby-lang.org/">Ruby Web site</a> for more +information. +</p> + +<p> +</p> + +<p> +RuBoids is developed and maintained by Jim Menard, <a +href="mailto:jimm@io.com">jimm@io.com</a>. The official +Web page of RuBoids is <a +href="http://www.io.com/~jimm/downloads/RuBoids/">http://www.io.com/~jimm/downloads/RuBoids/</a>, +where the latest release may be found. +</p> + + +<h1>Dependencies</h1> + +<p> +RuBoids requires the OpenGL and Qt packages, which can be found on the <a +href="html://www.ruby-lang.org/en/raa.html">Ruby Application Archive</a>. +</p> + + +<h1>Download</h1> + +<p> +Download the latest version, <a href="ruboids-0.0.1.tar.gz">RuBoids version +0.0.1</a>. +</p> + +<!-- +<p> +Earlier versions: +</p> + +<ul> +<li><a href="RuBoids-0.0.1.tar.gz">Version 0.0.1</a></li> +</ul> +--> + +<h1>Bugs</h1> + +<p> +Boids don't rotate properly. In other words, they point in the wrong +direction. +</p> + + +<h1>Copying</h1> + +<p> +RuBoids is copyrighted free software by Jim Menard and is released under +the same license as Ruby. +</p> + +<h1>Warranty</h1> + +<p> +THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +</p> + +<hr> +Back to my <a href="../../index.html">home page</a>, or the +<a href="../../map.html">Site Map</a>.<br> +<font size="-1"> +<i>Page last modified on +<!--#config timefmt="%B %d, %Y" --> +<!--#echo var="LAST_MODIFIED" --> +by <a href="mailto:jimm@io.com">me</a>.</i> +<br>Contents © 2001 by Jim Menard. All rights reserved. +</font> +</td> +</tr> +</table> +</td> +</tr> +</table> +</body> +</html> diff --git a/qtruby/rubylib/examples/ruboids/release.rb b/qtruby/rubylib/examples/ruboids/release.rb new file mode 100755 index 00000000..d82ba154 --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/release.rb @@ -0,0 +1,152 @@ +#! /usr/bin/env ruby +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# +# This script gathers everything needed to release RuBoids into one directory +# and, if requested, publishes the contents to the RuBoids Web site. +# +# usage: +# +# release.rb [--publish, -p] +# +# Specifying --publish or -p causes the resulting files to be published +# to the Web site. +# + +require 'net/ftp' +require 'ftools' # For makedirs and install +require 'generateManifest' # For--you guessed it--generating the Manifest + +# Start looking for RUBOIDS classes in this directory. +# This forces us to use the local copy of RUBOIDS, even if there is +# a previously installed version out there somewhere. +$LOAD_PATH[0, 0] = '.' + +require 'ruboids/info' # For Version string + +FILE_PERMISSION = 0644 +DIR_PERMISSION = 0755 + +PUBLISH_FLAG = '-p' +RUBOIDS_DIR = 'ruboids' +DOCS_DIR = '.' +#DOCS_HTML_DIR = "#{DOCS_DIR}/README" + +RUBOIDS_DIR_WITH_VERSION = "#{RUBOIDS_DIR}-#{Version}" +RELEASE_DIR = "/tmp/#{RUBOIDS_DIR_WITH_VERSION}_release" +#RELEASE_HTML_DIR = "#{RELEASE_DIR}/README" + +DOWNLOAD_FILE = "#{DOCS_DIR}/index.html" +#DOCBOOK_FILE = "#{DOCS_DIR}/README.sgml" + +WEB_SITE = 'io.com' +WEB_DIR = 'public-web/downloads/ruboids' + +# Copies all files from `fromDir' into the release directory. Sets the +# permissions of all files to 0644. +def copyFiles(fromDir, toDir, match=nil) + Dir.foreach(fromDir) { | f | + next if f =~ /^\.\.?/ || (!match.nil? && !(f =~ match)) + File.install("#{fromDir}/#{f}", toDir, FILE_PERMISSION) + } +end + +# Recursively removes the contents of a directory. +def rmDirectory(dirName) + return unless File.exists?(dirName) + Dir.foreach(dirName) { | f | + next if f =~ /^\.\.?/ + path = "#{dirName}/#{f}" + rmDirectory(path) if File.directory?(path) + File.delete(path) if !File.directory?(path) + } + +end + +# Recursively sends files and directories. +def sendToWebSite(ftp, releaseDir, webDir) + ftp.chdir(webDir) + Dir.foreach(releaseDir) { | f | + next if f =~ /^\.\.?/ + path = "#{releaseDir}/#{f}" + if File.directory?(path) + begin + ftp.mkdir(f) + rescue Net::FTPPermError + # ignore; it's OK if the directory already exists + end + sendToWebSite(ftp, path, f) + ftp.chdir('..') + else + ftp.putbinaryfile(path, f) + end + } +end + +def ensureVersionInFile(fileName, regex) + lines = File.open(fileName).grep(regex) + found = lines.detect { | line | line =~ /#{Version}/o } + if !found + $stderr.puts "Warning: it looks like the #{fileName} version number" + + " is incorrect" + end +end + +# ================================================================ +# main +# ================================================================ + +# Make sure the docs mention the correct version number. +#ensureVersionInFile(DOWNLOAD_FILE, /Download the latest/) +#ensureVersionInFile(DOCBOOK_FILE, /releaseinfo/) + +# Empty release dir if it already exists. +rmDirectory(RELEASE_DIR) + +# (Re)create release dir. This makes RELEASE_HTML_DIR, whose parent +# is RELEASE_DIR. Therefore, RELEASE_DIR is created as well. +#File.makedirs(RELEASE_HTML_DIR) +File.makedirs(RELEASE_DIR) + +# Recreate the full documentation (creating README and docs/README) and copy +# the HTML files to the release directory. Finally, clean up the docs +# directory. + +#system("cd #{DOCS_DIR} && make") +#copyFiles(DOCS_DIR, RELEASE_DIR, /\.html$/) +#copyFiles(DOCS_HTML_DIR, RELEASE_HTML_DIR, /\.html$/) +copyFiles(DOCS_DIR, RELEASE_DIR, 'index.html') + +# Generate the Manifest file. +generateManifest() + +# Create .tar.gz file. We temporarily rename the RUBOIDS folder to +# "ruboids-X.Y.Z", tar and gzip that directory, then restore its original +# name. +Dir.chdir('..') +File.rename(RUBOIDS_DIR, RUBOIDS_DIR_WITH_VERSION) +system("tar -czf #{RELEASE_DIR}/#{RUBOIDS_DIR_WITH_VERSION}.tar.gz " + + RUBOIDS_DIR_WITH_VERSION) +File.chmod(FILE_PERMISSION, "#{RELEASE_DIR}/#{RUBOIDS_DIR_WITH_VERSION}.tar.gz") +File.rename(RUBOIDS_DIR_WITH_VERSION, RUBOIDS_DIR) + +# ftp files if requested +if !ARGV.empty? && ARGV[0] == PUBLISH_FLAG + require 'net/ftp' + + # Ask for ftp username and password + guess = ENV['LOGNAME'] || ENV['USER'] + print "username [#{guess}]: " + username = $stdin.gets().chomp() + username = guess if username.empty? + print "password: " + password = $stdin.gets().chomp() + + # ftp files to web site + ftp = Net::FTP.open(WEB_SITE, username, password) + sendToWebSite(ftp, RELEASE_DIR, WEB_DIR) + ftp.close() +end diff --git a/qtruby/rubylib/examples/ruboids/ruboids/Boid.rb b/qtruby/rubylib/examples/ruboids/ruboids/Boid.rb new file mode 100644 index 00000000..38ac7bcc --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/Boid.rb @@ -0,0 +1,141 @@ +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +require 'BoidView' +require 'Flock' +require 'Point' +require 'Params' + +class Boid < Thing + + attr_accessor :maxSpeed, :maxSpeedSquared, :perchingTurnsLeft, + :wingFlapPos, :almostGroundLevel, :flock + + def initialize(pos = nil) + super(pos, nil) + init + end + + def init + @maxSpeed = $PARAMS['boid_max_speed'] + @maxSpeedSquared = @maxSpeed * @maxSpeed + @flock = nil # set by flock when flock adds to self + @wingFlapPos = rand(7) + @perchingTurnsLeft = 0 + @almostGroundLevel = 5.0 + + @view = BoidView.new(self) + end + + def move + # Flap wings. Only flap occasionally if not perching. + if (@perchingTurnsLeft == 0 || + rand(100) < $PARAMS['boid_perch_wing_flap_percent']) + @wingFlapPos = (@wingFlapPos + 1) & 7 + end + + if @perchingTurnsLeft > 0 + # Only take off when wing flap position == 2. + if --@perchingTurnsLeft == 0 && @wingFlapPos != 2 + @perchingTurnsLeft = (8 + 2 - @wingFlapPos) & 7 + return + end + end + + moveTowardsFlockCenter() + avoidOthers() + matchOthersVelocities() + boundPosition() + limitSpeed() + + super() # Add velocity vector to position. + + # Boids at ground level perch for a while. + if @position.y < @almostGroundLevel + @position.y = @almostGroundLevel + @vector.x = @vector.y = @vector.z = 0 + @perchingTurnsLeft = + rand($PARAMS['boid_max_perching_turns']) + end + end + + def moveTowardsFlockCenter() + flockCenter = @flock.centerExcluding(self) + flockCenter.subtractPoint(@position) + # Move 1% of the way towards the center + flockCenter.divideBy(100.0) + + @vector.addPoint(flockCenter) + end + + def avoidOthers() + c = Point.new() + @flock.members.each { | b | + if b != self + otherPos = b.position + if @position.squareOfDistanceTo(otherPos) < + $PARAMS['boid_square_of_personal_space_dist'] + c.addPoint(@position) + c.subtractPoint(otherPos) + end + end + } + @vector.addPoint(c) + end + + def matchOthersVelocities() + vel = Point.new() + flock.members.each { | b | + if b != self + vel.addPoint(b.vector) + end + } + vel.divideBy(flock.members.length - 1) + vel.subtractPoint(@vector) + vel.divideBy(8) + + @vector.addPoint(vel) + end + + def boundPosition() + v = Point.new + + halfWidth = $PARAMS['world_width'] / 2 + halfHeight = $PARAMS['world_height'] / 2 + halfDepth = $PARAMS['world_depth'] / 2 + + if position.x < -halfWidth + v.x = $PARAMS['boid_bounds_limit_pull'] + elsif position.x > halfWidth + v.x = -$PARAMS['boid_bounds_limit_pull'] + end + + if position.y < -halfHeight + almostGroundLevel + + $PARAMS['boid_bounds_limit_above_ground_level'] + v.y = $PARAMS['boid_bounds_limit_pull'] + elsif position.y > halfHeight + v.y = -$PARAMS['boid_bounds_limit_pull'] + end + + if position.z < -halfDepth + v.z = $PARAMS['boid_bounds_limit_pull'] + elsif position.z > halfDepth + v.z = -$PARAMS['boid_bounds_limit_pull'] + end + + @vector.addPoint(v) + end + + def limitSpeed() + speedSquared = Point::ORIGIN.squareOfDistanceTo(@vector) + if speedSquared > @maxSpeedSquared + f = Math.sqrt(speedSquared) * @maxSpeed + @vector.divideBy(f) + end + end +end + diff --git a/qtruby/rubylib/examples/ruboids/ruboids/BoidView.rb b/qtruby/rubylib/examples/ruboids/ruboids/BoidView.rb new file mode 100644 index 00000000..f2fc1288 --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/BoidView.rb @@ -0,0 +1,159 @@ +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +require 'View' + +class BoidView < View + + BODY_COLOR = [0, 0, 0] + BEAK_COLOR = [0.75, 0.5, 0.0] + SHADOW_COLOR = [0.25, 0.55, 0.25] + + HALF_WING_BASE = 3 + HALF_LENGTH = 5 + HALF_THICKNESS = 1 + NOSE_LENGTH = 3 + + @@object = nil + @@shadow = nil + @@wings = nil + @@wingsShadows = nil + + def initialize(model) + super(model, [0, 0, 0]) + @wings = nil + @wingsShadows = nil + end + + def makeObject + @@object = BoidView.makeObject() unless @@object + @object = @@object + @wings = @@wings + end + + def makeShadow + BoidView.makeShadow() unless @@shadow + @shadow = @@shadow + @wingsShadows = @@wingsShadows + end + + def drawObject + super() + + angle = 0 + case model.wingFlapPos + when 0 + angle = 60 + when 1, 7 + angle = 30 + when 2, 6 + angle = 0 + when 3, 5 + angle = -30 + when 4 + angle = -60 + end + + PushMatrix() + Rotate(angle, 0, 0, 1) + CallList(@wings[0]) + Rotate(angle * -2, 0, 0, 1) + CallList(@wings[1]) + PopMatrix() + end + + def BoidView.makeObject + makeWings() + + object = GenLists(1) + NewList(object, COMPILE) + + makeBody() + makeNose() + + EndList() + + return object + end + + def BoidView.makeShadow + @@shadow = GenLists(1) + NewList(@@shadow, COMPILE) + + p0 = Point::ORIGIN.dup() + p1 = Point::ORIGIN.dup() + dims = Point.new(HALF_THICKNESS, HALF_THICKNESS, HALF_LENGTH) + p0.subtractPoint(dims) + p1.addPoint(dims) + + groundLevel = -($PARAMS['world_height'] / 2) + 1 + + Color(SHADOW_COLOR) + Begin(QUADS) + Vertex(p1.x, groundLevel, p0.z) + Vertex(p0.x, groundLevel, p0.z) + Vertex(p0.x, groundLevel, p1.z) + Vertex(p1.x, groundLevel, p1.z) + End() +# Begin(TRIANGLES) +# Vertex(p1.x, groundLevel, p1.z) +# Vertex(0, groundLevel, p0.z) +# Vertex(p0.x, groundLevel, p1.z) +# End() + + EndList() + end + + def BoidView.makeBody + p0 = Point::ORIGIN.dup() + p1 = Point::ORIGIN.dup() + dims = Point.new(HALF_THICKNESS, HALF_THICKNESS, HALF_LENGTH) + p0.subtractPoint(dims) + p1.addPoint(dims) + + Color(BODY_COLOR) + Graphics.boxFromCorners(p0, p1) + end + + def BoidView.makeWings + @@wings = [] + len = -$PARAMS['boid_wing_length'] + @@wings << makeOneWing(len) + @@wings << makeOneWing(-len) + end + + def BoidView.makeOneWing(len) + wing = GenLists(1) + NewList(wing, COMPILE) + + Color(BODY_COLOR) + Begin(TRIANGLES) + + Vertex(0, 0, -HALF_WING_BASE) + Vertex(len, 0, 0) + Vertex(0, 0, HALF_WING_BASE) + + End() + EndList() + return wing + end + + def BoidView.makeNose() + Color(BEAK_COLOR) + Begin(TRIANGLE_FAN) + + Vertex(0, 0, HALF_LENGTH + NOSE_LENGTH) + Vertex( HALF_THICKNESS, HALF_THICKNESS, HALF_LENGTH) + Vertex(-HALF_THICKNESS, HALF_THICKNESS, HALF_LENGTH) + Vertex(-HALF_THICKNESS, -HALF_THICKNESS, HALF_LENGTH) + Vertex( HALF_THICKNESS, -HALF_THICKNESS, HALF_LENGTH) + + End() + end + +end + diff --git a/qtruby/rubylib/examples/ruboids/ruboids/Camera.rb b/qtruby/rubylib/examples/ruboids/ruboids/Camera.rb new file mode 100644 index 00000000..787fc4af --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/Camera.rb @@ -0,0 +1,24 @@ +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +require 'Params' + +class Camera + + attr_accessor :position, :rotation, :zoom + + def initialize + @position = Point.new($PARAMS['camera_x'], + $PARAMS['camera_y'], + $PARAMS['camera_z']) + @rotation = Point.new($PARAMS['camera_rot_x'], + $PARAMS['camera_rot_y'], + $PARAMS['camera_rot_z']) + @zoom = $PARAMS['camera_zoom'] + end +end + diff --git a/qtruby/rubylib/examples/ruboids/ruboids/CameraDialog.rb b/qtruby/rubylib/examples/ruboids/ruboids/CameraDialog.rb new file mode 100644 index 00000000..6e01db15 --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/CameraDialog.rb @@ -0,0 +1,213 @@ +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +require 'Qt' +require 'World' +require 'Camera' + +class Adjustor + attr_accessor :slider, :num, :origValue + def initialize(slider, num, origValue = 0) + @slider = slider + @num = num + @origValue = origValue + end + def setSlider(val); @slider.setValue(val); end + def setNum(val); @num.setNum(val); end + def set(val) + setSlider(val) + setNum(val) + end + def reset + set(@origValue) + return @origValue + end +end + +class CameraDialog < Qt::Dialog + slots 'slotReset()', 'slotLocXChanged(int)', + 'slotLocYChanged(int)', 'slotLocZChanged(int)', + 'slotRotationXChanged(int)', 'slotRotationYChanged(int)', + 'slotRotationZChanged(int)', 'slotZoomChanged(int)' + + def initialize(parent) + super + @locAdjustors = [] + @rotationAdjustors = [] + @otherAdjustors = [] + @avoidUpdates = false + + @camera = World.instance.camera + + # Remember values for reset + @origCamera = @camera.dup() + + # Group and layout widgets + vLayout = Qt::VBoxLayout.new(self, 5) + + locBox = Qt::GroupBox.new('Location', self, 'locBox') + rotationBox = Qt::GroupBox.new('Rotation', self, 'rotationBox') + otherBox = Qt::GroupBox.new('Other', self, 'otherBox') + + locLayout = Qt::GridLayout.new(locBox, 5, 3, 20) + rotationLayout = Qt::GridLayout.new(rotationBox, 5, 3, 20) + otherLayout = Qt::GridLayout.new(otherBox, 3, 3, 20) + buttonLayout = Qt::HBoxLayout.new() + + vLayout.addWidget(locBox) + vLayout.addWidget(rotationBox) + vLayout.addWidget(otherBox) + vLayout.addSpacing(10) + vLayout.addLayout(buttonLayout) + + # Add extra space at the top of each layout so the group box title + # doesn't get squished. + locLayout.addRowSpacing(0, 15) + rotationLayout.addRowSpacing(0, 15) + otherLayout.addRowSpacing(0, 15) + + # Contents of camera location box + @locAdjustors << addSlider(1, locBox, locLayout, 'X', -1000, 1000, 1, + 'slotLocXChanged(int)', @camera.position.x) + @locAdjustors << addSlider(2, locBox, locLayout, 'Y', -1000, 1000, 1, + 'slotLocYChanged(int)', @camera.position.y) + @locAdjustors << addSlider(3, locBox, locLayout, 'Z', -1000, 1000, 1, + 'slotLocZChanged(int)', @camera.position.z) + + # Contents of camera rotation box + @rotationAdjustors << addSlider(1, rotationBox, rotationLayout, 'X', + 0, 360, 1, 'slotRotationXChanged(int)', + @camera.rotation.x) + @rotationAdjustors << addSlider(2, rotationBox, rotationLayout, 'Y', + 0, 360, 1, 'slotRotationYChanged(int)', + @camera.rotation.y) + @rotationAdjustors << addSlider(3, rotationBox, rotationLayout, 'Z', + 0, 360, 1, 'slotRotationZChanged(int)', + @camera.rotation.z) + + @otherAdjustors << addSlider(1, otherBox, otherLayout, 'Zoom', + 1, 100, 1, 'slotZoomChanged(int)', + @camera.zoom * 10.0) + @otherAdjustors[0].origValue = @camera.zoom + + # The Close button + button = Qt::PushButton.new('Close', self, 'Dialog Close') + connect(button, SIGNAL('clicked()'), self, SLOT('close()')) + button.setDefault(true) + button.setFixedSize(button.sizeHint()) + buttonLayout.addWidget(button) + + # The Close button + button = Qt::PushButton.new('Reset', self, 'Dialog Reset') + connect(button, SIGNAL('clicked()'), self, SLOT('slotReset()')) + button.setFixedSize(button.sizeHint()) + buttonLayout.addWidget(button) + + # 15 layout management + locLayout.activate() + rotationLayout.activate() + otherLayout.activate() + vLayout.activate() + + resize(0, 0) + + setCaption('Camera Settings') + end + + def addSlider(row, box, layout, label, min, max, pageStep, slot, + initialValue) + # Label + text = Qt::Label.new(label, box) + text.setMinimumSize(text.sizeHint()) + layout.addWidget(text, row, 0) + + # Slider + slider = Qt::Slider.new(min, max, pageStep, initialValue, + Qt::Slider::Horizontal, box) + slider.setMinimumSize(slider.sizeHint()) + slider.setMinimumWidth(180) + layout.addWidget(slider, row, 1) + + # Connection from slider signal to our slot + connect(slider, SIGNAL('valueChanged(int)'), self, SLOT(slot)) + + # Number display + num = Qt::Label.new('XXXXX', box) + num.setMinimumSize(num.sizeHint()) + num.setFrameStyle(Qt::Frame::Panel | Qt::Frame::Sunken) + num.setAlignment(AlignRight | AlignVCenter) + num.setNum(initialValue) + + layout.addWidget(num, row, 2) + + return Adjustor.new(slider, num, initialValue) + end + + def cameraChanged + World.instance.setupTranslation() unless @avoidUpdates + end + + def slotLocXChanged(val) + @locAdjustors[0].setNum(val) + @camera.position.x = val + cameraChanged() + end + + def slotLocYChanged(val) + @locAdjustors[1].setNum(val) + @camera.position.y = val + cameraChanged() + end + + def slotLocZChanged(val) + @locAdjustors[2].setNum(val) + @camera.position.z = val + cameraChanged() + end + + def slotRotationXChanged(val) + @rotationAdjustors[0].setNum(val) + @camera.rotation.x = val + cameraChanged() + end + + def slotRotationYChanged(val) + @rotationAdjustors[1].setNum(val) + @camera.rotation.y = val + cameraChanged() + end + + def slotRotationZChanged(val) + @rotationAdjustors[2].setNum(val) + @camera.rotation.z = val + cameraChanged() + end + + def slotZoomChanged(val) + @otherAdjustors[0].setNum(val) + @camera.zoom = val / 10.0 + cameraChanged() + end + + def slotReset + @avoidUpdates = true + + @camera.position.x = @locAdjustors[0].reset() + @camera.position.y = @locAdjustors[1].reset() + @camera.position.z = @locAdjustors[2].reset() + + @camera.rotation.x = @rotationAdjustors[0].reset() + @camera.rotation.y = @rotationAdjustors[1].reset() + @camera.rotation.z = @rotationAdjustors[2].reset() + + @camera.zoom = @otherAdjustors[0].reset() + + @avoidUpdates = false + cameraChanged() + end + +end diff --git a/qtruby/rubylib/examples/ruboids/ruboids/Canvas.rb b/qtruby/rubylib/examples/ruboids/ruboids/Canvas.rb new file mode 100644 index 00000000..91ed934b --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/Canvas.rb @@ -0,0 +1,144 @@ +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +require 'Qt' +require 'opengl' +require 'World' +require 'Cloud' +require 'Flock' +require 'Params' +require 'Camera' + +include GL + +class Canvas < Qt::GLWidget + + GRASS_COLOR = [0, 0.75, 0] + MDA_ROTATE = :MDA_ROTATE + MDA_ZOOM = :MDA_ZOOM + MDA_CHANGE_FOCUS = :MDA_CHANGE_FOCUS + + def initialize(parent = nil, name = '') + super + @grassObject = nil +# catchEvent + end + + def update + updateGL() + end + + def initializeGL() + ClearColor(0.4, 0.4, 1.0, 0.0) # Let OpenGL clear to light blue + @grassObject = makeGrassObject() + ShadeModel(FLAT) + end + + def paintGL() + Enable(DEPTH_TEST) + Clear(COLOR_BUFFER_BIT | DEPTH_BUFFER_BIT) + + MatrixMode(MODELVIEW) + + camera = World.instance.camera + + LoadIdentity() + Rotate(camera.rotation.x, 1, 0, 0) + Rotate(camera.rotation.y, 0, 1, 0) + Rotate(camera.rotation.z, 0, 0, 1.0) + Translate(-camera.position.x, -camera.position.y, -camera.position.z) + Scale(camera.zoom, camera.zoom, camera.zoom) + + CallList(@grassObject) + + World.instance.clouds.each { | cloud | cloud.draw() } + World.instance.flock.draw() + end + + # Set up the OpenGL view port, matrix mode, etc. + def resizeGL(w, h) + Viewport(0, 0, w, h) + MatrixMode(PROJECTION) + LoadIdentity() + +# # left, right, bottom, top, front, back (focal_length) + halfXSize = $PARAMS['world_width'] / 2 * 1.25 + halfYSize = $PARAMS['world_height'] / 2 * 1.25 + halfZSize = $PARAMS['world_depth'] / 2 * 1.25 + +# Frustum(-halfXSize, halfXSize, -halfYSize, halfYSize, +# 5, halfZSize * 2) + + Ortho(-halfXSize, halfXSize, -halfYSize, halfYSize, + -halfZSize, halfZSize) + + MatrixMode(MODELVIEW) + end + + def makeGrassObject + halfXSize = $PARAMS['world_width'] + halfYSize = $PARAMS['world_depth'] / 2 + halfZSize = $PARAMS['world_height'] + + list = GenLists(1) + NewList(list, COMPILE) + LineWidth(2.0) + Begin(QUADS) + + Color(GRASS_COLOR) + # Counter-clockwise + Vertex( halfXSize, -halfYSize, halfZSize) + Vertex(-halfXSize, -halfYSize, halfZSize) + Vertex(-halfXSize, -halfYSize, -halfZSize) + Vertex( halfXSize, -halfYSize, -halfZSize) + + End() + EndList() + return list + end + + def mousePressEvent(e) + @mouseLoc = e.pos() + case e.button() + when Qt::LeftButton + @mouseDragAction = MDA_ZOOM + when Qt::RightButton + @mouseDragAction = MDA_ROTATE + when Qt::MidButton + @mouseDragAction = MDA_CHANGE_FOCUS + end + end + + # Rotate around sphere with right (#2) button. Zoom with left button. + # Change focus with left button. + def mouseMoveEvent(e) + return if @mouseLoc.nil? + + dx = dy = 0 + if e.x() != @mouseLoc.x() + dx = e.x() - @mouseLoc.x() # move right increases dx + @mouseLoc.setX(e.x()) + end + if e.y() != @mouseLoc.y() + dy = @mouseLoc.y() - e.y() # move up increases dy + @mouseLoc.setY(e.y()) + end + + return if dx == 0 && dy == 0 + + case @mouseDragAction + when MDA_ZOOM + return if (dy == 0) + World.instance.camera.zoom += 0.1 * -dy + when MDA_ROTATE + break + when MDA_CHANGE_FOCUS + break + end + World.instance.setupTranslation() + end +end diff --git a/qtruby/rubylib/examples/ruboids/ruboids/Cloud.rb b/qtruby/rubylib/examples/ruboids/ruboids/Cloud.rb new file mode 100644 index 00000000..5d30222a --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/Cloud.rb @@ -0,0 +1,61 @@ +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +require 'Thing' +require 'CloudView' +require 'Params' + +class Bubble + + attr_reader :loc, :radius, :color + + def initialize + @radius = rand($PARAMS['cloud_max_bubble_radius']) + 1 + @loc = Point.new(0, rand(8) - 4, rand(8) - 4) + c = 0.85 + rand() * 0.15 + @color = [c, c, c] + end + +end + + +class Cloud < Thing + + attr_reader :speed, :bubbles, :width + + def initialize + minSpeed = $PARAMS['cloud_min_speed'] + minBubbles = $PARAMS['cloud_min_bubbles'] + @speed = rand($PARAMS['cloud_max_speed'] - minSpeed) + minSpeed + numBubbles = rand($PARAMS['cloud_max_bubbles'] - minBubbles) + + minBubbles + @bubbles = [] + prevBubble = nil + (0 ... numBubbles).each { | i | + bubble = Bubble.new() + if !prevBubble.nil? + bubble.loc.x = prevBubble.loc.x + + rand((prevBubble.radius + bubble.radius) * 0.66) + end + + @bubbles[i] = prevBubble = bubble + } + + @width = bubbles.last.loc.x + + @bubbles.first.radius + @bubbles.last.radius + + @view = CloudView.new(self) + end + + def move + @position.x += pixelsPerSecToPixelsPerMove(speed) + halfWorldWidth = $PARAMS['world_width'] + if (@position.x >= halfWorldWidth / 2) + @position.x = -(halfWorldWidth + @width) + end + end +end diff --git a/qtruby/rubylib/examples/ruboids/ruboids/CloudView.rb b/qtruby/rubylib/examples/ruboids/ruboids/CloudView.rb new file mode 100644 index 00000000..75c62177 --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/CloudView.rb @@ -0,0 +1,54 @@ +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +require 'Qt' +require 'View' +require 'Cloud' +require 'Params' +require 'World' +require 'Graphics' + +class CloudView < View + + def initialize(cloud) + super(cloud) + end + + def makeObject + @object = GenLists(1) + NewList(@object, COMPILE) + + @model.bubbles.each { | bubble | + Color(bubble.color) + PushMatrix() + Translate(bubble.loc.x, bubble.loc.y, bubble.loc.z) + Scale(bubble.radius, bubble.radius, bubble.radius) + Graphics.sphere() + PopMatrix() + } + + EndList() + end + + def makeShadow + @shadow = GenLists(1) + NewList(@shadow, COMPILE) + + groundLevel = -($PARAMS['world_height'] / 2) + 1 + @model.bubbles.each { | bubble | + Color(shadowColorForHeight(model.position.y + bubble.loc.y)) + PushMatrix() + Translate(bubble.loc.x, groundLevel, bubble.loc.z) + Scale(bubble.radius, 1.0, bubble.radius) + Graphics.circle(2) + PopMatrix() + } + + EndList() + end + +end diff --git a/qtruby/rubylib/examples/ruboids/ruboids/Flock.rb b/qtruby/rubylib/examples/ruboids/ruboids/Flock.rb new file mode 100644 index 00000000..4d476a2b --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/Flock.rb @@ -0,0 +1,47 @@ +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +require 'Flock' +require 'Boid' +require 'Params' + +class Flock + attr_reader :members + + def initialize + @members = [] + end + + def add(boid) + @members << boid + boid.flock = self + end + + def draw + @members.each { | boid | boid.draw() } + end + + def move + @members.each { | boid | boid.move() } + end + + # Return distance between two boid's positions. + def distBetween(b1, b2) + return b1.position.distanceTo(b2.position) + end + + # Center of mass + def centerExcluding(b) + p = Point.new() + @members.each { | boid | + p.addPoint(boid.position) unless boid == b + } + p.divideBy(@members.length - 1) + return p + end +end + diff --git a/qtruby/rubylib/examples/ruboids/ruboids/Graphics.rb b/qtruby/rubylib/examples/ruboids/ruboids/Graphics.rb new file mode 100644 index 00000000..5e982208 --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/Graphics.rb @@ -0,0 +1,278 @@ +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +require 'Triangle' + +class Graphics + + DEFAULT_SPHERE_ITERATIONS = 3 + + XPLUS = Point.new(1, 0, 0) # X + XMINUS = Point.new(-1, 0, 0)# -X + YPLUS = Point.new(0, 1, 0) # Y + YMINUS = Point.new(0, -1, 0)# -Y + ZPLUS = Point.new(0, 0, 1) # Z + ZMINUS = Point.new(0, 0, -1)# -Z + + # defined w/counter-clockwise triangles + OCTAHEDRON = [ + Triangle.new(YPLUS, ZPLUS, XPLUS), + Triangle.new(XMINUS, ZPLUS, YPLUS), + Triangle.new(YMINUS, ZPLUS, XMINUS), + Triangle.new(XPLUS, ZPLUS, YMINUS), + Triangle.new(ZMINUS, YPLUS, XPLUS), + Triangle.new(ZMINUS, XMINUS , YPLUS), + Triangle.new(ZMINUS, YMINUS , XMINUS), + Triangle.new(ZMINUS, XPLUS, YMINUS) + ] + # Defines counter-clockwise points used in OpenGL TRIANGLE_STRIP to + # create a circle on the X/Z plane. Don't include center point here; + # It is added when outputting the circle. + SQUARE = [ + XPLUS, ZMINUS, XMINUS, ZPLUS, XPLUS + ] + + @@spheres = Hash.new() + @@circles = Hash.new() + + def Graphics.radiansToDegrees(rad) + return rad * 180.0 / Math::PI + end + + def Graphics.degreesToRadians(deg) + return deg * Math::PI / 180.0 + end + + # Given a vector, return a point containing x, y, z rotation angles. + # + # atan2(x, y) = the angle formed with the x axis by the ray from the + # origin to the point {x,y} + def Graphics.rotations(v) + return Point::ORIGIN.dup() if v.nil? + return v if v == Point::ORIGIN + + x = Math.atan2(v.y, v.z) + y = Math.atan2(v.z, v.x) + z = Math.atan2(v.y, v.x) + + rot = Point.new(z, x, y) + rot.add(Math::PI).multiplyBy(180.0).divideBy(Math::PI) + + rot.x = rot.x.to_i + rot.y = rot.y.to_i + rot.z = rot.z.to_i + + return rot + end + + # Build box from corners. All faces are counter-clockwise. + def Graphics.boxFromCorners(p0, p1) + pa = p0.dup() + pb = p1.dup() + + # Make sure all coords of pa are < all coords of pb + if pa.x > pb.x + tmp = pa.x; pa.x = pb.x; pb.x = tmp + end + if pa.y > pb.y + tmp = pa.y; pa.y = pb.y; pb.y = tmp + end + if pa.z > pb.z + tmp = pa.z; pa.z = pb.z; pb.z = tmp + end + + Begin(QUAD_STRIP) + + # top + Vertex(pb.x, pb.y, pa.z) + Vertex(pa.x, pb.y, pa.z) + # top/front + Vertex(pb.x, pb.y, pb.z) + Vertex(pa.x, pb.y, pb.z) + # front/bottom + Vertex(pb.x, pa.y, pb.z) + Vertex(pa.x, pa.y, pb.z) + # bottom/back + Vertex(pb.x, pa.y, pa.z) + Vertex(pa.x, pa.y, pa.z) + # back/top + Vertex(pb.x, pb.y, pa.z) + Vertex(pa.x, pb.y, pa.z) + + End() + + Begin(QUADS) + + # left + Vertex(pa.x, pa.y, pb.z) + Vertex(pa.x, pa.y, pa.z) + Vertex(pa.x, pb.y, pa.z) + Vertex(pa.x, pb.y, pb.z) + + # right + Vertex(pb.x, pa.y, pb.z) + Vertex(pb.x, pa.y, pa.z) + Vertex(pb.x, pb.y, pa.z) + Vertex(pb.x, pb.y, pb.z) + + End() + end + + # sphere() (and buildSphere()) - generate a triangle mesh approximating + # a sphere by recursive subdivision. First approximation is an + # octahedron; each level of refinement increases the number of + # triangles by a factor of 4. + # + # Level 3 (128 triangles) is a good tradeoff if gouraud shading is used + # to render the database. + # + # Usage: sphere [level] [counterClockwise] + # + # The value level is an integer >= 1 setting the recursion level + # (default = DEFAULT_SPHERE_ITERATIONS). + # The boolean counterClockwise causes triangles to be generated + # with vertices in counterclockwise order as viewed from + # the outside in a RHS coordinate system. The default is + # counter-clockwise. + # + # @author Jon Leech (leech@cs.unc.edu) 3/24/89 (C version) + # Ruby version by Jim Menard (jimm@io.com), May 2001. + def Graphics.sphere(iterations = DEFAULT_SPHERE_ITERATIONS, + counterClockwise = true) + if @@spheres[iterations].nil? + @@spheres[iterations] = buildSphere(iterations, OCTAHEDRON) + end + sphere = @@spheres[iterations] + + Begin(TRIANGLES) + sphere.each { | triangle | + triangle.points.each { | p | + Vertex(p.x, p.y, p.z) if counterClockwise + Vertex(p.z, p.y, p.x) if !counterClockwise + } + } + End() + end + + # + # Subdivide each triangle in the oldObj approximation and normalize + # the new points thus generated to lie on the surface of the unit + # sphere. + # Each input triangle with vertices labelled [0,1,2] as shown + # below will be turned into four new triangles: + # + # Make new points + # a = (0+2)/2 + # b = (0+1)/2 + # c = (1+2)/2 + # 1 + # /\ Normalize a, b, c + # / \ + # b/____\ c Construct new counter-clockwise triangles + # /\ /\ [a,b,0] + # / \ / \ [c,1,b] + # /____\/____\ [c,b,a] + # 0 a 2 [2,c,a] + # + # + # The normalize step (which makes each point a, b, c unit distance + # from the origin) is where we can modify the sphere's shape. + # + def Graphics.buildSphere(iterations, sphere) + oldObj = sphere + # Subdivide each starting triangle (maxlevel - 1) times + iterations -= 1 + iterations.times { + # Create a new object. Allocate 4 * the number of points in the + # the current approximation. + newObj = Array.new(oldObj.length * 4) + + j = 0 + oldObj.each { | oldt | + # New midpoints + a = Point.midpoint(oldt.points[0], oldt.points[2]) + a.normalize!() + b = Point.midpoint(oldt.points[0], oldt.points[1]) + b.normalize!() + c = Point.midpoint(oldt.points[1], oldt.points[2]) + c.normalize!() + + # New triangeles. Their vertices are counter-clockwise. + newObj[j] = Triangle.new(a, b, oldt.points[0]) + j += 1 + newObj[j] = Triangle.new(c, oldt.points[1], b) + j += 1 + newObj[j] = Triangle.new(c, b, a) + j += 1 + newObj[j] = Triangle.new(oldt.points[2], c, a) + j += 1 + } + + # Continue subdividing new triangles + oldObj = newObj + } + return oldObj + end + + # Creates a circle in the X/Z plane. To have the circle's normal + # point down (-Y), specify clockwise instead of counter-clockwise. + # To create the circle in another plane, call OpenGL's Rotate() method + # before calling this. + def Graphics.circle(iterations = DEFAULT_SPHERE_ITERATIONS, + counterClockwise = true) + if @@circles[iterations].nil? + @@circles[iterations] = buildCircle(iterations, SQUARE) + end + circle = @@circles[iterations] + + Begin(TRIANGLE_FAN) + Vertex(0, 0, 0) + if counterClockwise + circle.each { | p | Vertex(p.x, 0, p.z) } + else + circle.reverse.each { | p | Vertex(p.x, 0, p.z) } + end + End() + end + + # Different than buildSphere because we are creating triangles to + # be used in an OpenGL TRIANGLE_FAN operation. Thus the first point + # (the center) is always inviolate. We create new points between + # the remaining points. + def Graphics.buildCircle(iterations, circle) + oldObj = circle + # Subdivide each starting line segment (maxlevel - 1) times + iterations -= 1 + iterations.times { + # Create a new object. Allocate 2 * the number of points in the + # the current approximation. Subtract one because the last point + # (same as the first point) is simply copied. + newObj = Array.new(oldObj.length * 2 - 1) + + prevP = nil + j = 0 + oldObj.each { | p | + if !prevP.nil? + newObj[j] = prevP + j += 1 + + # New midpoint + a = Point.midpoint(prevP, p) + a.normalize!() + newObj[j] = a + j += 1 + end + prevP = p + } + newObj[j] = prevP # Copy last point + + # Continue subdividing new triangles + oldObj = newObj + } + return oldObj + end +end diff --git a/qtruby/rubylib/examples/ruboids/ruboids/Params.rb b/qtruby/rubylib/examples/ruboids/ruboids/Params.rb new file mode 100644 index 00000000..9ff57851 --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/Params.rb @@ -0,0 +1,87 @@ +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +require 'singleton' + +$PARAMS = { + 'world_sleep_millis' => 75, + 'world_width' => 400, + 'world_height' => 400, + 'world_depth' => 400, + 'window_width' => 500, + 'window_height' => 500, + 'flock_boids' => 10, + 'boid_max_speed' => 30, + 'boid_bounds_limit_pull' => 5, + 'boid_bounds_limit_above_ground_level' => 5, + 'boid_wing_length' => 10, + 'boid_personal_space_dist' => 12, + 'boid_square_of_personal_space_dist' => 144, + 'boid_max_perching_turns' => 150, + 'boid_perch_wing_flap_percent' => 30, + 'cloud_count' => 10, + 'cloud_min_speed' => 2, + 'cloud_max_speed' => 50, + 'cloud_min_bubbles' => 3, + 'cloud_max_bubbles' => 10, + 'cloud_max_bubble_radius' => 10, + 'cloud_min_altitude' => 250, + 'camera_x' => 0, + 'camera_y' => 0, + 'camera_z' => 60, + 'camera_rot_x' => 50, + 'camera_rot_y' => 10, + 'camera_rot_z' => 0, + 'camera_zoom' => 1 +} + +class Params + + @@reals = %w( +world_width +world_height +world_depth +boid_max_speed +boid_bounds_limit_pull +boid_bounds_limit_above_ground_level +boid_wing_length +boid_personal_space_dist +boid_square_of_personal_space_dist +cloud_min_speed +cloud_max_speed +cloud_max_bubble_radius +cloud_min_altitude +camera_x +camera_y +camera_z +camera_rot_x +camera_rot_y +camera_rot_z +camera_zoom +) + + def Params.readParamsFromFile(paramFileName) + File.open(paramFileName).each { | line | + line.chomp! + next if line.empty? || line =~ /^#/ + + key, value = line.split(/\s*=\s*/) + next unless value + key.downcase!() + key.gsub!(/\./, '_') + + isReal = @@reals.include?(key) + value = value.to_f if isReal + value = value.to_i if !isReal + $PARAMS[key] = value + } + $PARAMS['boid_square_of_personal_space_dist'] = + $PARAMS['boid_personal_space_dist'] * + $PARAMS['boid_personal_space_dist'] + end + +end diff --git a/qtruby/rubylib/examples/ruboids/ruboids/Point.rb b/qtruby/rubylib/examples/ruboids/ruboids/Point.rb new file mode 100644 index 00000000..0331f795 --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/Point.rb @@ -0,0 +1,153 @@ +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +class Point + + attr_accessor :x, :y, :z + + # Return a new Point that is the midpoint on the line between two + # points. + def Point.midpoint(a, b) + return Point.new((a.x + b.x) * 0.5, (a.y + b.y) * 0.5, + (a.z + b.z) * 0.5) + end + + def initialize(x = 0, y = 0, z = 0) + if x.kind_of?(Point) + @x = x.x + @y = x.y + @z = x.z + else + @x = x + @y = y + @z = z + end + end + + ORIGIN = Point.new(0, 0, 0) + + def ==(point) + return point.kind_of?(Point) && + @x == point.x && @y == point.y && @z == point.z + end + + # Normalize this point. + def normalize! + mag = @x * @x + @y * @y + @z * @z + if mag != 1.0 + mag = 1.0 / Math.sqrt(mag) + @x *= mag + @y *= mag + @z *= mag + end + return self + end + + # Return a new point that is a normalized version of this point. + def normalize + return self.dup().normalize!() + end + + # Return a new point that is the cross product of this point and another. + # The cross product of two unit vectors is another vector that's at + # right angles to the first two (for example, a surface normal). + def crossProduct(p) + return Point.new(@y * p.z - @z * p.y, @z * p.x - @x * p.z, + @x * p.y - @y * p.x) + end + + # Return the (scalar) dot product of this vector and another. + # The dot product of two vectors produces the cosine of the angle + # between them, multiplied by the lengths of those vectors. (The dot + # product of two normalized vectors equals cosine of the angle.) + def dotProduct(p) + return @x * p.x + @y * p.y + @z * p.z + end + + # Return square of distance between this point and another. + def squareOfDistanceTo(p) + dx = p.x - @x + dy = p.y - @y + dz = p.z - @z + return dx * dx + dy * dy + dz * dz + end + + # Return distance between this point and another. + def distanceTo(p) + dx = p.x - @x + dy = p.y - @y + dz = p.z - @z + return Math.sqrt(dx * dx + dy * dy + dz * dz) + end + + def add(d) + @x += d + @y += d + @z += d + return self + end + + def addPoint(p) + @x += p.x + @y += p.y + @z += p.z + return self + end + + + def subtract(d) + @x -= d + @y -= d + @z -= d + return self + end + + def subtractPoint(p) + @x -= p.x + @y -= p.y + @z -= p.z + return self + end + + + def multiplyBy(d) + @x *= d + @y *= d + @z *= d + return self + end + + def multiplyByPoint(p) + @x *= p.x + @y *= p.y + @z *= p.z + return self + end + + def divideBy(d) + @x = @x / d + @y = @y / d + @z = @z / d + return self + end + + def divideByPoint(p) + @x = @x / p.x + @y = @y / p.y + @z = @z / p.z + return self + end + + def to_a + return [@x, @y, @z] + end + + def to_s + return "Point<#{@x}, #{@y}, #{@z}>" + end + +end diff --git a/qtruby/rubylib/examples/ruboids/ruboids/Thing.rb b/qtruby/rubylib/examples/ruboids/ruboids/Thing.rb new file mode 100644 index 00000000..9b6bfe5b --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/Thing.rb @@ -0,0 +1,34 @@ +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +require 'Point' + +class Thing + + attr_accessor :position, :vector, :view + + def initialize(pos = nil, vec = nil) + @position = pos ? pos : Point.new + @vector = vec ? vec : Point.new + end + + def move + position.x += vector.x + position.y += vector.y + position.z += vector.z + end + + def draw + view.draw() if view + end + + def pixelsPerSecToPixelsPerMove(pixelsPerSecond) + pps = (pixelsPerSecond.to_f / (1000.0 / 75.0)).to_i + pps = 1 if pps == 0 + return pps + end +end diff --git a/qtruby/rubylib/examples/ruboids/ruboids/Triangle.rb b/qtruby/rubylib/examples/ruboids/ruboids/Triangle.rb new file mode 100644 index 00000000..eedf69f9 --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/Triangle.rb @@ -0,0 +1,21 @@ +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +require 'Point' + +class Triangle + attr_accessor :points + + def initialize(p0 = Point::ORIGIN, + p1 = Point::ORIGIN, + p2 = Point::ORIGIN) + @points = [] + @points << p0 ? p0 : Point::ORIGIN.dup() + @points << p1 ? p1 : Point::ORIGIN.dup() + @points << p2 ? p2 : Point::ORIGIN.dup() + end +end diff --git a/qtruby/rubylib/examples/ruboids/ruboids/View.rb b/qtruby/rubylib/examples/ruboids/ruboids/View.rb new file mode 100644 index 00000000..a5323629 --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/View.rb @@ -0,0 +1,88 @@ +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +# A lightweight view +class View + + SHADOW_COLOR = [ 0.25, 0.25, 0.25 ] + + attr_accessor :model, :color, :object, :shadow + + def initialize(model, color = nil) + super() + @model = model + @color = color + @object = nil + @shadow = nil + end + + def makeObject + raise "subclass should implement" + end + + def makeShadow + # Don't raise error; some models may not have a shadow + end + + def drawObject + CallList(@object) + end + + def drawShadow + CallList(@shadow) if @shadow + end + + def draw + # We don't always have enough information to make the 3D objects + # at initialize() time. + makeObject() unless @object + makeShadow() unless @shadow + + rot = Graphics.rotations(model.vector) + + PushMatrix() + + # Translate and rotate shadow. Rotation around y axis only. + Translate(model.position.x, 0, model.position.z) + Rotate(rot.y, 0, 1, 0) if rot.y.nonzero? + + # Draw shadow. + drawShadow() unless @shadow.nil? + + # Translate and rotate object. Rotate object around x and z axes (y + # axis already done for shadow). + Translate(0, model.position.y, 0) + Rotate(rot.x, 1, 0, 0) if rot.x.nonzero? + Rotate(rot.z, 0, 0, 1) if rot.z.nonzero? + + # Draw object. + drawObject() + + PopMatrix() + end + + # Given the height of an object, return a shadow color. The shadow color + # gets lighter as heigt increases. + def shadowColorForHeight(height) + wh = $PARAMS['world_height'] + ratio = (height + wh / 2.0) / wh + + shadowColor = [] + SHADOW_COLOR.each_with_index { | c0, i | + min = c0 + max = Canvas::GRASS_COLOR[i] + if min > max + tmp = min + min = max + max = tmp + end + shadowColor << min + ratio * (max - min) + } + return shadowColor + end + +end diff --git a/qtruby/rubylib/examples/ruboids/ruboids/World.rb b/qtruby/rubylib/examples/ruboids/ruboids/World.rb new file mode 100644 index 00000000..17608bca --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/World.rb @@ -0,0 +1,82 @@ +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +require 'singleton' +require 'Qt' +require 'Params' +require 'Cloud' +require 'Flock' +require 'Boid' +require 'Camera' +require 'Canvas' + +class World < Qt::Object + slots 'slotMove()' + + include Singleton + + attr_accessor :canvas + attr_reader :width, :height, :depth, :camera, :clouds, :flock + + def initialize + super + @width = $PARAMS['world_width'] + @height = $PARAMS['world_height'] + @depth = $PARAMS['world_depth'] + + @clouds = [] + minAltitude = $PARAMS['cloud_min_altitude'] + $PARAMS['cloud_count'].times { + c = Cloud.new + c.position = + Point.new(rand(@width) - @width / 2, + rand(@height) - @height / 2, + rand(@depth - minAltitude) - @depth / 2 + minAltitude) + @clouds << c + } + # Sort clouds by height so lower/darker shadows are drawn last + @clouds.sort { |a, b| a.position.y <=> b.position.y } + + @flock = Flock.new + $PARAMS['flock_boids'].times { + b = Boid.new + b.position = Point.new(rand(@width) - @width / 2, + rand(@height) - @height / 2, + rand(@depth) - @depth / 2) + @flock.add(b) # flock will delete boid + } + + @clock = Qt::Timer.new() + connect(@clock, SIGNAL('timeout()'), self, SLOT('slotMove()')) + + @camera = Camera.new # Reads values from params + setupTranslation() + end + + # Should be called whenever camera or screen changes. + def setupTranslation + @canvas.update() if @canvas + end + + def start + @clock.start($PARAMS['world_sleep_millis']) + end + + def slotMove + @clouds.each { | c | c.move() } + @flock.move() + @canvas.update() if @canvas + + # Camera follow boid. +# b = @flock.members.first +# @camera.position = b.position +# @camera.rotation = Graphics.rotations(b.vector) +# @camera.zoom = 1.0 + + end +end + diff --git a/qtruby/rubylib/examples/ruboids/ruboids/WorldWindow.rb b/qtruby/rubylib/examples/ruboids/ruboids/WorldWindow.rb new file mode 100644 index 00000000..56650ece --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/WorldWindow.rb @@ -0,0 +1,54 @@ +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +require 'Qt' +require 'Canvas' +require 'CameraDialog' + +class WorldWindow < Qt::MainWindow + slots 'slotMenuActivated(int)' + + MENU_CAMERA_DIALOG = 1 + + attr_accessor :canvas + + def initialize + super + setCaption("Boids") + setupMenubar() + + @canvas = Canvas.new(self, "TheDamnCanvas") + setCentralWidget(@canvas) + setGeometry(0, 0, $PARAMS['window_width'], + $PARAMS['window_height']) + end + + def setupMenubar + + # Create and populate file menu + menu = Qt::PopupMenu.new(self) + menu.insertItem("Exit", $qApp, SLOT("quit()"), Qt::KeySequence.new(CTRL+Key_Q)) + + # Add file menu to menu bar + menuBar.insertItem("&File", menu) + + # Create and populate options menu + menu = Qt::PopupMenu.new(self) + menu.insertItem("&Camera...", MENU_CAMERA_DIALOG, -1) + + # Add options menu to menu bar and link it to method below + menuBar.insertItem("&Options", menu) + connect(menu, SIGNAL("activated(int)"), self, SLOT('slotMenuActivated(int)')) + + end + + def slotMenuActivated(id) + if id == MENU_CAMERA_DIALOG + CameraDialog.new(nil).exec() + end + end +end diff --git a/qtruby/rubylib/examples/ruboids/ruboids/info.rb b/qtruby/rubylib/examples/ruboids/ruboids/info.rb new file mode 100644 index 00000000..fcfc50f6 --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/info.rb @@ -0,0 +1,12 @@ +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +VERSION_MAJOR = 0 +VERSION_MINOR = 0 +VERSION_TWEAK = 1 +Version = "#{VERSION_MAJOR}.#{VERSION_MINOR}.#{VERSION_TWEAK}" +Copyright = 'Copyright (c) 2001 by Jim Menard <jimm@io.com>' diff --git a/qtruby/rubylib/examples/ruboids/ruboids/ruboids.rb b/qtruby/rubylib/examples/ruboids/ruboids/ruboids.rb new file mode 100755 index 00000000..b9bdecba --- /dev/null +++ b/qtruby/rubylib/examples/ruboids/ruboids/ruboids.rb @@ -0,0 +1,29 @@ +#! /usr/bin/env ruby +# +# Copyright (c) 2001 by Jim Menard <jimm@io.com> +# +# Released under the same license as Ruby. See +# http://www.ruby-lang.org/en/LICENSE.txt. +# + +require 'Qt' +require 'World' +require 'WorldWindow' +require 'Canvas' +require 'Params' + +app = Qt::Application.new(ARGV) +if (!Qt::GLFormat::hasOpenGL()) + warning("This system has no OpenGL support. Exiting.") + exit -1 +end + +Params.readParamsFromFile(ARGV[0] || 'boids.properties') +world = World.instance +win = WorldWindow.new +app.mainWidget = win + +World.instance.canvas = win.canvas +win.show +World.instance.start +app.exec diff --git a/qtruby/rubylib/examples/testcases/bugs.rb b/qtruby/rubylib/examples/testcases/bugs.rb new file mode 100644 index 00000000..6b5e3153 --- /dev/null +++ b/qtruby/rubylib/examples/testcases/bugs.rb @@ -0,0 +1,57 @@ +require 'Qt' + + +#### TODO ### +# dup of qobject crash +def bug1 + p1 = Qt::Point.new(5,5) + p1.setX 5 + p p1 + p3 = p1.dup + p3.setX 5 + p p3 +end +#bug1 + + +#### FIXED ### +def bug3 + a = Qt::Application.new(ARGV) + @file = Qt::PopupMenu.new + @file.insertSeparator + Qt::debug_level = Qt::DebugLevel::High + p $qApp + @file.insertItem("Quit", $qApp, SLOT('quit()')) + @file.exec +end +#bug3 + + +class CPUWaster < Qt::Widget + def initialize(*k) + super(*k) + end + def draw + painter = Qt::Painter.new(self) + 0.upto(1000) { |i| + cw, ch = width, height + c = Qt::Color.new(rand(255), rand(255), rand(255)) + x = rand(cw - 8) + y = rand(cw - 8) + w = rand(cw - x) + h = rand(cw - y) + brush = Qt::Brush.new(c) + brush.setStyle(Qt::Dense6Pattern) + Qt::debug_level = Qt::DebugLevel::High + painter.fillRect(Qt::Rect.new(x, y, w, h), brush) + Qt::debug_level = Qt::DebugLevel::Off + } + end +end +def bug4 + Qt::Application.new(ARGV) + w = CPUWaster.new + w.show + w.draw +end +bug4 diff --git a/qtruby/rubylib/examples/testcases/error_reporting.rb b/qtruby/rubylib/examples/testcases/error_reporting.rb new file mode 100644 index 00000000..e2012447 --- /dev/null +++ b/qtruby/rubylib/examples/testcases/error_reporting.rb @@ -0,0 +1,85 @@ +require 'Qt' + +#### CRASH ### +# param mismatch? +class Bug1 < Qt::PushButton + def initialize(*k) + super(*k) + end + def Bug1.test + a = Qt::Application.new(ARGV) + w = Qt::VBox.new + hello = Bug1.new(a) + hello.resize(100, 30) + a.setMainWidget(w) + hello.show() + a.exec() + end +end +#Bug1.test + + +#### MORE DEBUG INFO NEEDED ### +# missing method +class Bug2 < Qt::VBox + def initialize(*k) + super(*k) + end + def Bug2.test + a = Qt::Application.new(ARGV) + w = Bug2.new + a.setMainWidget(w) + w.show2() + a.exec() + end +end +#Bug2.test + + +#### MORE DEBUG INFO NEEDED ### +# missing prototype +class Bug2a < Qt::VBox + def initialize(*k) + super(*k) + end + def Bug2a.test + a = Qt::Application.new(ARGV) + w = Bug2a.new + a.setMainWidget(w) + w.show(p) + a.exec() + end +end +Bug2a.test + + +#### FIXED ### +# no such constructor for PushButton +class Bug3 < Qt::PushButton + def initialize + super + end + def Bug3.test + a = Qt::Application.new(ARGV) + hello = Bug3.new + hello.resize(100, 30) + a.setMainWidget(hello) + hello.show() + a.exec() + end +end +#Bug3.test + + +#### FIXED ### +# no *class* variable/method resize in PushButton +class Bug4 < Qt::PushButton + def initialize + super + end + def Bug4.test + hello = Bug4 + hello.resize(100, 30) + end +end +#Bug4.test diff --git a/qtruby/rubylib/examples/testcases/opoverloading.rb b/qtruby/rubylib/examples/testcases/opoverloading.rb new file mode 100644 index 00000000..1798a995 --- /dev/null +++ b/qtruby/rubylib/examples/testcases/opoverloading.rb @@ -0,0 +1,46 @@ +require 'Qt' + +class Qt::Point + def to_s + "(#{x}, #{y})" + end +end + +$t = binding +def test(str) + puts "#{str.ljust 25} => #{eval(str, $t)}" +end + +test("p1 = Qt::Point.new(5,5)") +test("p2 = Qt::Point.new(20,20)") +test("p1 + p2") +test("p1 - p2") +test("-p1 + p2") +test("p2 += p1") +test("p2 -= p1") +test("p2 * 3") + +class Qt::Region + def to_s + "(#{isNull})" + end +end + +test("r1 = Qt::Region.new()") +test("r2 = Qt::Region.new( 100,100,200,80, Qt::Region::Ellipse )") +test("r1 + r2") + +class Qt::WMatrix + def to_s + "(#{m11}, #{m12}, #{m21}, #{m22}, #{dx}, #{dy})" + end +end + +test("a = Math::PI/180 * 25") # convert 25 to radians +test("sina = Math.sin(a)") +test("cosa = Math.cos(a)") +test("m1 = Qt::WMatrix.new(1, 0, 0, 1, 10, -20)") # translation matrix +test("m2 = Qt::WMatrix.new( cosa, sina, -sina, cosa, 0, 0 )") +test("m3 = Qt::WMatrix.new(1.2, 0, 0, 0.7, 0, 0)") # scaling matrix +test("m = Qt::WMatrix.new") +test("m = m3 * m2 * m1") # combine all transformations diff --git a/qtruby/rubylib/examples/textedit/textedit.rb b/qtruby/rubylib/examples/textedit/textedit.rb new file mode 100644 index 00000000..db905b4f --- /dev/null +++ b/qtruby/rubylib/examples/textedit/textedit.rb @@ -0,0 +1,150 @@ +#!/usr/bin/ruby -w + +require 'Qt' +require 'rexml/document' + +require '../base/rui.rb' + +class MyTextEditor < Qt::TextEdit + signals 'saved()' + slots 'insert_icon()', 'new()', 'open()', 'save_as()' + def initialize(w = nil) + @images = {} + @@next_image_id = 0 + super(w) + self.setTextFormat(Qt::RichText) + end + def insert_richtext(richtext) + # todo, use a rand string + unique_string = '000___xxx123456789xxx___xxx123456789xxx___000' + insert(unique_string) + txt = self.text().gsub(unique_string, richtext) + self.setText(txt) + end + def next_image_id + @@next_image_id += 1 + end + def load_image(fname, image_id) + pixmap = Qt::Pixmap.new(fname) + msfactory = Qt::MimeSourceFactory.defaultFactory + msfactory.setPixmap(image_id, pixmap) + @images[image_id] = fname + image_id + end + def insert_icon + fname = Qt::FileDialog.getOpenFileName + return if fname.nil? + image_id = "image_#{next_image_id}" + load_image(fname, image_id) + insert_richtext('<qt><img source="'+image_id+'"></qt>') + end + def createPopupMenu(pos) # virtual + pm = Qt::PopupMenu.new + pm.insertItem("Insert Image!", self, SLOT('insert_icon()')) + pm + end + def has_metadata + !@images.empty? + end + def metadata_fname(fname) + "#{fname}.metadata.xml" + end + def attempt_metadata_load(fname) + return unless File.exists?(metadata_fname(fname)) + file = File.open(metadata_fname(fname)) + @xmldoc = REXML::Document.new file + @xmldoc.root.elements.each("image") { + |image| + image_id = image.attributes["ident"] + img_fname = image.attributes["filename"] + load_image(img_fname, image_id) + } + end + def metadata_save_if_has(fname) + return if not has_metadata + metadata_doc = REXML::Document.new '<metadata/>' + @images.each { + |id, img_fname| + metadata_doc.root.add_element("image", {"filename"=>img_fname, "ident"=>id}) + } + file = File.new(metadata_fname(fname), "w") + file.puts(metadata_doc) + file.close + end + def metadata_clear + @images = {} + end + def new(txt = "") + metadata_clear + self.setText(txt) + end + def open + fname = Qt::FileDialog.getOpenFileName + return if fname.nil? + unless File.exists?(fname) + Qt::MessageBox.critical(self, "File Does Not Exist", "Sorry, unable to find the requested file!") + return + end + return if fname.nil? + txt = File.open(fname).gets(nil) + metadata_clear + attempt_metadata_load(fname) + self.setText(txt) + end + def save_as + fname = Qt::FileDialog.getSaveFileName + return if fname.nil? + if File.exists?(fname) + Qt::MessageBox.critical(self, "File Already Exists", "Sorry, file already exists. Please choose a non-existing filename!") + return save_as + end + file = File.new(fname, "w") + file.puts(text()) + file.close + metadata_save_if_has(fname) + emit saved() + end +end + +class MyWidget < Qt::MainWindow + slots 'text_changed()', 'saved()' + def initialize() + super + @editor = MyTextEditor.new(self) + connect(@editor, SIGNAL('textChanged()'), self, SLOT('text_changed()')) + connect(@editor, SIGNAL('saved()'), self, SLOT('saved()')) + + fileTools = Qt::ToolBar.new(self, "file operations") + fileMenu = Qt::PopupMenu.new(self) + + actions = [ + RAction.new("&New", Icons::FILE_NEW, @editor, SLOT('new()'), [fileTools, fileMenu]), + RAction.new("&Open...", Icons::FILE_OPEN, @editor, SLOT('open()'), [fileTools, fileMenu]), + @save = RAction.new("Save &As...", Icons::FILE_SAVE_AS, @editor, SLOT('save_as()'), [fileTools, fileMenu]), + RSeperator.new([fileMenu]), + RAction.new("E&xit", Icons::EXIT, $qApp, SLOT('quit()'), [fileMenu]) + ] + + build_actions(actions) + + menubar = Qt::MenuBar.new(self) + menubar.insertItem("&File", fileMenu) + + self.setCentralWidget(@editor) + end + def saved + @save.action.setEnabled(false) + end + def text_changed + @save.action.setEnabled(true) + end +end + +a = Qt::Application.new(ARGV) + +w = MyWidget.new +w.show + +a.setMainWidget(w) +a.exec() +exit diff --git a/qtruby/rubylib/qtruby/Makefile.am b/qtruby/rubylib/qtruby/Makefile.am new file mode 100644 index 00000000..1df273bf --- /dev/null +++ b/qtruby/rubylib/qtruby/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES = -I$(top_srcdir)/smoke $(all_includes) -I$(RUBY_ARCHDIR) + +noinst_HEADERS = qtruby.h marshall.h smokeruby.h extconf.rb + +noinst_LTLIBRARIES = libqtrubyinternal.la +libqtrubyinternal_la_SOURCES = Qt.cpp handlers.cpp +libqtrubyinternal_la_METASOURCES = AUTO + +rubylibdir = $(RUBY_SITEARCHDIR) +rubylib_LTLIBRARIES = qtruby.la +qtruby_la_SOURCES = +qtruby_la_LDFLAGS = -module $(all_libraries) -version-info 0:0:0 +qtruby_la_LIBADD = libqtrubyinternal.la $(LIB_QT) $(top_builddir)/smoke/qt/libsmokeqt.la + +SUBDIRS = lib diff --git a/qtruby/rubylib/qtruby/Qt.cpp b/qtruby/rubylib/qtruby/Qt.cpp new file mode 100644 index 00000000..a8415512 --- /dev/null +++ b/qtruby/rubylib/qtruby/Qt.cpp @@ -0,0 +1,2967 @@ +/*************************************************************************** + Qt.cpp - description + ------------------- + begin : Fri Jul 4 2003 + copyright : (C) 2003-2004 by Richard Dale + email : Richard_Dale@tipitina.demon.co.uk + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include <stdio.h> +#include <stdarg.h> + +#include <qglobal.h> +#include <qregexp.h> +#include <qstring.h> +#include <qptrdict.h> +#include <qintdict.h> +#include <qapplication.h> +#include <qmetaobject.h> +#include <private/qucomextra_p.h> +#include <qvariant.h> +#include <qcursor.h> +#include <qobjectlist.h> +#include <qsignalslotimp.h> +#include <qcstring.h> + +#undef DEBUG +#ifndef __USE_POSIX +#define __USE_POSIX +#endif +#ifndef __USE_XOPEN +#define __USE_XOPEN +#endif +#ifdef _BOOL +#define HAS_BOOL +#endif + +#include <ruby.h> + +#ifndef QT_VERSION_STR +#define QT_VERSION_STR "Unknown" +#endif + +#undef free +#undef malloc + +#include "marshall.h" +#include "qtruby.h" +#include "smokeruby.h" +#include "smoke.h" + +// #define DEBUG + +#define QTRUBY_VERSION "1.0.13" + +extern Smoke *qt_Smoke; +extern void init_qt_Smoke(); +extern void smokeruby_mark(void * ptr); +extern void smokeruby_free(void * ptr); +extern VALUE qchar_to_s(VALUE self); + +#ifdef DEBUG +int do_debug = qtdb_gc; +#else +int do_debug = qtdb_none; +#endif + +QPtrDict<VALUE> pointer_map(2179); +int object_count = 0; + +QAsciiDict<Smoke::Index> methcache(2179); +QAsciiDict<Smoke::Index> classcache(2179); +// Maps from a classname in the form Qt::Widget to an int id +QIntDict<char> classname(2179); + +extern "C" { +VALUE qt_module = Qnil; +VALUE qext_scintilla_module = Qnil; +VALUE kde_module = Qnil; +VALUE kparts_module = Qnil; +VALUE kio_module = Qnil; +VALUE kns_module = Qnil; +VALUE dom_module = Qnil; +VALUE kontact_module = Qnil; +VALUE kate_module = Qnil; +VALUE ktexteditor_module = Qnil; +VALUE koffice_module = Qnil; +VALUE qt_internal_module = Qnil; +VALUE qt_base_class = Qnil; +VALUE qmetaobject_class = Qnil; +VALUE qvariant_class = Qnil; +VALUE kconfigskeleton_class = Qnil; +VALUE kconfigskeleton_itemenum_class = Qnil; +VALUE kconfigskeleton_itemenum_choice_class = Qnil; +VALUE kio_udsatom_class = Qnil; +VALUE kwin_class = Qnil; +VALUE konsole_part_class = Qnil; +bool application_terminated = false; +}; + +#define logger logger_backend +void rb_str_catf(VALUE self, const char *format, ...) __attribute__ ((format (printf, 2, 3))); + +static VALUE (*_new_kde)(int, VALUE *, VALUE) = 0; +static VALUE (*_kconfigskeletonitem_immutable)(VALUE) = 0; + +Smoke::Index _current_method = 0; + +extern TypeHandler Qt_handlers[]; +void install_handlers(TypeHandler *); + +smokeruby_object *value_obj_info(VALUE ruby_value) { // ptr on success, null on fail + if (TYPE(ruby_value) != T_DATA) { + return 0; + } + + smokeruby_object * o = 0; + Data_Get_Struct(ruby_value, smokeruby_object, o); + return o; +} + +void *value_to_ptr(VALUE ruby_value) { // ptr on success, null on fail + smokeruby_object *o = value_obj_info(ruby_value); + return o; +} + +VALUE getPointerObject(void *ptr); + +bool isQObject(Smoke *smoke, Smoke::Index classId) { + if(qstrcmp(smoke->classes[classId].className, "QObject") == 0) + return true; + for(Smoke::Index *p = smoke->inheritanceList + smoke->classes[classId].parents; + *p; + p++) { + if(isQObject(smoke, *p)) + return true; + } + return false; +} + +bool isDerivedFrom(Smoke *smoke, Smoke::Index classId, Smoke::Index baseId) { + if(classId == 0 && baseId == 0) + return false; + if(classId == baseId) + return true; + for(Smoke::Index *p = smoke->inheritanceList + smoke->classes[classId].parents; + *p; + p++) { + if(isDerivedFrom(smoke, *p, baseId)) + return true; + } + return false; +} + +bool isDerivedFromByName(Smoke *smoke, const char *className, const char *baseClassName) { + if(!smoke || !className || !baseClassName) + return false; + Smoke::Index idClass = smoke->idClass(className); + Smoke::Index idBase = smoke->idClass(baseClassName); + return isDerivedFrom(smoke, idClass, idBase); +} + +VALUE getPointerObject(void *ptr) { + if (pointer_map[ptr] == 0) { + return Qnil; + } else { + return *(pointer_map[ptr]); + } +} + +void unmapPointer(smokeruby_object *o, Smoke::Index classId, void *lastptr) { + void *ptr = o->smoke->cast(o->ptr, o->classId, classId); + if(ptr != lastptr) { + lastptr = ptr; + if (pointer_map[ptr] != 0) { + VALUE * obj_ptr = pointer_map[ptr]; + + if (do_debug & qtdb_gc) { + const char *className = o->smoke->classes[o->classId].className; + qWarning("unmapPointer (%s*)%p -> %p", className, ptr, obj_ptr); + } + + pointer_map.remove(ptr); + free((void*) obj_ptr); + } + } + for(Smoke::Index *i = o->smoke->inheritanceList + o->smoke->classes[classId].parents; + *i; + i++) { + unmapPointer(o, *i, lastptr); + } +} + +// Store pointer in pointer_map hash : "pointer_to_Qt_object" => weak ref to associated Ruby object +// Recurse to store it also as casted to its parent classes. + +void mapPointer(VALUE obj, smokeruby_object *o, Smoke::Index classId, void *lastptr) { + void *ptr = o->smoke->cast(o->ptr, o->classId, classId); + + if (ptr != lastptr) { + lastptr = ptr; + VALUE * obj_ptr = (VALUE *) malloc(sizeof(VALUE)); + memcpy(obj_ptr, &obj, sizeof(VALUE)); + + if (do_debug & qtdb_gc) { + const char *className = o->smoke->classes[o->classId].className; + qWarning("mapPointer (%s*)%p -> %p", className, ptr, (void*)obj); + } + + pointer_map.insert(ptr, obj_ptr); + } + + for(Smoke::Index *i = o->smoke->inheritanceList + o->smoke->classes[classId].parents; + *i; + i++) { + mapPointer(obj, o, *i, lastptr); + } + + return; +} + +Marshall::HandlerFn getMarshallFn(const SmokeType &type); + +class VirtualMethodReturnValue : public Marshall { + Smoke *_smoke; + Smoke::Index _method; + Smoke::Stack _stack; + SmokeType _st; + VALUE _retval; +public: + const Smoke::Method &method() { return _smoke->methods[_method]; } + SmokeType type() { return _st; } + Marshall::Action action() { return Marshall::FromVALUE; } + Smoke::StackItem &item() { return _stack[0]; } + VALUE * var() { return &_retval; } + + void unsupported() { + rb_raise(rb_eArgError, "Cannot handle '%s' as return-type of virtual method %s::%s", + type().name(), + _smoke->className(method().classId), + _smoke->methodNames[method().name]); + } + + Smoke *smoke() { return _smoke; } + void next() {} + bool cleanup() { return false; } + + VirtualMethodReturnValue(Smoke *smoke, Smoke::Index meth, Smoke::Stack stack, VALUE retval) : + _smoke(smoke), _method(meth), _stack(stack), _retval(retval) { + _st.set(_smoke, method().ret); + Marshall::HandlerFn fn = getMarshallFn(type()); + (*fn)(this); + } +}; + +class VirtualMethodCall : public Marshall { + Smoke *_smoke; + Smoke::Index _method; + Smoke::Stack _stack; + VALUE _obj; + int _cur; + Smoke::Index *_args; + VALUE *_sp; + bool _called; + +public: + SmokeType type() { return SmokeType(_smoke, _args[_cur]); } + Marshall::Action action() { return Marshall::ToVALUE; } + Smoke::StackItem &item() { return _stack[_cur + 1]; } + VALUE * var() { return _sp + _cur; } + const Smoke::Method &method() { return _smoke->methods[_method]; } + void unsupported() { + rb_raise(rb_eArgError, "Cannot handle '%s' as argument of virtual method %s::%s", + type().name(), + _smoke->className(method().classId), + _smoke->methodNames[method().name]); + } + Smoke *smoke() { return _smoke; } + void callMethod() { + if(_called) return; + _called = true; + + VALUE _retval = rb_funcall2(_obj, + rb_intern(_smoke->methodNames[method().name]), + method().numArgs, + _sp ); + VirtualMethodReturnValue r(_smoke, _method, _stack, _retval); + } + + void next() { + int oldcur = _cur; + _cur++; + while(!_called && _cur < method().numArgs) { + Marshall::HandlerFn fn = getMarshallFn(type()); + (*fn)(this); + _cur++; + } + callMethod(); + _cur = oldcur; + } + + bool cleanup() { return false; } // is this right? + + VirtualMethodCall(Smoke *smoke, Smoke::Index meth, Smoke::Stack stack, VALUE obj) : + _smoke(smoke), _method(meth), _stack(stack), _obj(obj), _cur(-1), _sp(0), _called(false) { + _sp = (VALUE *) calloc(method().numArgs, sizeof(VALUE)); + + _args = _smoke->argumentList + method().args; + } + + ~VirtualMethodCall() { + free(_sp); + } +}; + +class MethodReturnValue : public Marshall { + Smoke *_smoke; + Smoke::Index _method; + VALUE * _retval; + Smoke::Stack _stack; +public: + MethodReturnValue(Smoke *smoke, Smoke::Index method, Smoke::Stack stack, VALUE * retval) : + _smoke(smoke), _method(method), _retval(retval), _stack(stack) { + Marshall::HandlerFn fn = getMarshallFn(type()); + (*fn)(this); + } + + const Smoke::Method &method() { return _smoke->methods[_method]; } + SmokeType type() { return SmokeType(_smoke, method().ret); } + Marshall::Action action() { return Marshall::ToVALUE; } + Smoke::StackItem &item() { return _stack[0]; } + VALUE * var() { + return _retval; + } + void unsupported() { + rb_raise(rb_eArgError, "Cannot handle '%s' as return-type of %s::%s", + type().name(), + qstrcmp(_smoke->className(method().classId), "QGlobalSpace") == 0 ? "" : _smoke->className(method().classId), + _smoke->methodNames[method().name]); + } + Smoke *smoke() { return _smoke; } + void next() {} + bool cleanup() { return false; } +}; + +class MethodCall : public Marshall { + int _cur; + Smoke *_smoke; + Smoke::Stack _stack; + Smoke::Index _method; + Smoke::Index *_args; + VALUE _target; + void *_current_object; + Smoke::Index _current_object_class; + VALUE *_sp; + int _items; + VALUE _retval; + bool _called; +public: + MethodCall(Smoke *smoke, Smoke::Index method, VALUE target, VALUE *sp, int items) : + _cur(-1), _smoke(smoke), _method(method), _target(target), _current_object(0), _sp(sp), _items(items), _called(false) + { + + if (_target != Qnil) { + smokeruby_object *o = value_obj_info(_target); + if (o && o->ptr) { + _current_object = o->ptr; + _current_object_class = o->classId; + } + } + + _args = _smoke->argumentList + _smoke->methods[_method].args; + _items = _smoke->methods[_method].numArgs; + _stack = new Smoke::StackItem[items + 1]; + _retval = Qnil; + } + + ~MethodCall() { + delete[] _stack; + } + + SmokeType type() { + return SmokeType(_smoke, _args[_cur]); + } + + Marshall::Action action() { + return Marshall::FromVALUE; + } + Smoke::StackItem &item() { + return _stack[_cur + 1]; + } + + VALUE * var() { + if(_cur < 0) return &_retval; + return _sp + _cur; + } + + inline const Smoke::Method &method() { + return _smoke->methods[_method]; + } + + void unsupported() { + if (qstrcmp(_smoke->className(method().classId), "QGlobalSpace") == 0) { + rb_raise(rb_eArgError, "Cannot handle '%s' as argument to %s", + type().name(), + _smoke->methodNames[method().name]); + } else { + rb_raise(rb_eArgError, "Cannot handle '%s' as argument to %s::%s", + type().name(), + _smoke->className(method().classId), + _smoke->methodNames[method().name]); + } + } + + Smoke *smoke() { + return _smoke; + } + + inline void callMethod() { + if(_called) return; + _called = true; + + QString className(_smoke->className(method().classId)); + + if ( ! className.endsWith(_smoke->methodNames[method().name]) + && TYPE(_target) != T_DATA + && _target != Qnil + && !(method().flags & Smoke::mf_static) ) + { + rb_raise(rb_eArgError, "Instance is not initialized, cannot call %s", + _smoke->methodNames[method().name]); + } + + if (_target == Qnil && !(method().flags & Smoke::mf_static)) { + rb_raise(rb_eArgError, "%s is not a class method\n", _smoke->methodNames[method().name]); + } + + Smoke::ClassFn fn = _smoke->classes[method().classId].classFn; + void *ptr = _smoke->cast(_current_object, _current_object_class, method().classId); + _items = -1; + (*fn)(method().method, ptr, _stack); + MethodReturnValue r(_smoke, _method, _stack, &_retval); + } + + void next() { + int oldcur = _cur; + _cur++; + + while(!_called && _cur < _items) { + Marshall::HandlerFn fn = getMarshallFn(type()); + (*fn)(this); + _cur++; + } + callMethod(); + _cur = oldcur; + } + + bool cleanup() { + return true; + } +}; + +class UnencapsulatedQObject : public QObject { +public: + QConnectionList *public_receivers(int signal) const { return receivers(signal); } + void public_activate_signal(QConnectionList *clist, QUObject *o) { activate_signal(clist, o); } +}; + +class EmitSignal : public Marshall { + UnencapsulatedQObject *_qobj; + int _id; + MocArgument *_args; + VALUE *_sp; + int _items; + int _cur; + Smoke::Stack _stack; + bool _called; +public: + EmitSignal(QObject *qobj, int id, int items, VALUE args, VALUE *sp) : + _qobj((UnencapsulatedQObject*)qobj), _id(id), _sp(sp), _items(items), + _cur(-1), _called(false) + { + _items = NUM2INT(rb_ary_entry(args, 0)); + Data_Get_Struct(rb_ary_entry(args, 1), MocArgument, _args); + _stack = new Smoke::StackItem[_items]; + } + ~EmitSignal() { + delete[] _stack; + } + const MocArgument &arg() { return _args[_cur]; } + SmokeType type() { return arg().st; } + Marshall::Action action() { return Marshall::FromVALUE; } + Smoke::StackItem &item() { return _stack[_cur]; } + VALUE * var() { return _sp + _cur; } + void unsupported() { + rb_raise(rb_eArgError, "Cannot handle '%s' as signal argument", type().name()); + } + Smoke *smoke() { return type().smoke(); } + void emitSignal() { + if(_called) return; + _called = true; + + QConnectionList *clist = _qobj->public_receivers(_id); + if(!clist) return; + + QUObject *o = new QUObject[_items + 1]; + for(int i = 0; i < _items; i++) { + QUObject *po = o + i + 1; + Smoke::StackItem *si = _stack + i; + switch(_args[i].argType) { + case xmoc_bool: + static_QUType_bool.set(po, si->s_bool); + break; + case xmoc_int: + static_QUType_int.set(po, si->s_int); + break; + case xmoc_double: + static_QUType_double.set(po, si->s_double); + break; + case xmoc_charstar: + static_QUType_charstar.set(po, (char*)si->s_voidp); + break; + case xmoc_QString: + static_QUType_QString.set(po, *(QString*)si->s_voidp); + break; + default: + { + const SmokeType &t = _args[i].st; + void *p; + switch(t.elem()) { + case Smoke::t_bool: + p = &si->s_bool; + break; + case Smoke::t_char: + p = &si->s_char; + break; + case Smoke::t_uchar: + p = &si->s_uchar; + break; + case Smoke::t_short: + p = &si->s_short; + break; + case Smoke::t_ushort: + p = &si->s_ushort; + break; + case Smoke::t_int: + p = &si->s_int; + break; + case Smoke::t_uint: + p = &si->s_uint; + break; + case Smoke::t_long: + p = &si->s_long; + break; + case Smoke::t_ulong: + p = &si->s_ulong; + break; + case Smoke::t_float: + p = &si->s_float; + break; + case Smoke::t_double: + p = &si->s_double; + break; + case Smoke::t_enum: + { + // allocate a new enum value + Smoke::EnumFn fn = SmokeClass(t).enumFn(); + if(!fn) { + rb_warning("Unknown enumeration %s\n", t.name()); + p = new int((int)si->s_enum); + break; + } + Smoke::Index id = t.typeId(); + (*fn)(Smoke::EnumNew, id, p, si->s_enum); + (*fn)(Smoke::EnumFromLong, id, p, si->s_enum); + // FIXME: MEMORY LEAK + } + break; + case Smoke::t_class: + case Smoke::t_voidp: + p = si->s_voidp; + break; + default: + p = 0; + break; + } + static_QUType_ptr.set(po, p); + } + } + } + + _qobj->public_activate_signal(clist, o); + delete[] o; + } + void next() { + int oldcur = _cur; + _cur++; + + while(!_called && _cur < _items) { + Marshall::HandlerFn fn = getMarshallFn(type()); + (*fn)(this); + _cur++; + } + + emitSignal(); + _cur = oldcur; + } + bool cleanup() { return true; } +}; + +class InvokeSlot : public Marshall { + VALUE _obj; + ID _slotname; + int _items; + MocArgument *_args; + QUObject *_o; + int _cur; + bool _called; + VALUE *_sp; + Smoke::Stack _stack; +public: + const MocArgument &arg() { return _args[_cur]; } + SmokeType type() { return arg().st; } + Marshall::Action action() { return Marshall::ToVALUE; } + Smoke::StackItem &item() { return _stack[_cur]; } + VALUE * var() { return _sp + _cur; } + Smoke *smoke() { return type().smoke(); } + bool cleanup() { return false; } + void unsupported() { + rb_raise(rb_eArgError, "Cannot handle '%s' as slot argument\n", type().name()); + } + void copyArguments() { + for(int i = 0; i < _items; i++) { + QUObject *o = _o + i + 1; + switch(_args[i].argType) { + case xmoc_bool: + _stack[i].s_bool = static_QUType_bool.get(o); + break; + case xmoc_int: + _stack[i].s_int = static_QUType_int.get(o); + break; + case xmoc_double: + _stack[i].s_double = static_QUType_double.get(o); + break; + case xmoc_charstar: + _stack[i].s_voidp = static_QUType_charstar.get(o); + break; + case xmoc_QString: + _stack[i].s_voidp = &static_QUType_QString.get(o); + break; + default: // case xmoc_ptr: + { + const SmokeType &t = _args[i].st; + void *p = static_QUType_ptr.get(o); + switch(t.elem()) { + case Smoke::t_bool: + _stack[i].s_bool = *(bool*)p; + break; + case Smoke::t_char: + _stack[i].s_char = *(char*)p; + break; + case Smoke::t_uchar: + _stack[i].s_uchar = *(unsigned char*)p; + break; + case Smoke::t_short: + _stack[i].s_short = *(short*)p; + break; + case Smoke::t_ushort: + _stack[i].s_ushort = *(unsigned short*)p; + break; + case Smoke::t_int: + _stack[i].s_int = *(int*)p; + break; + case Smoke::t_uint: + _stack[i].s_uint = *(unsigned int*)p; + break; + case Smoke::t_long: + _stack[i].s_long = *(long*)p; + break; + case Smoke::t_ulong: + _stack[i].s_ulong = *(unsigned long*)p; + break; + case Smoke::t_float: + _stack[i].s_float = *(float*)p; + break; + case Smoke::t_double: + _stack[i].s_double = *(double*)p; + break; + case Smoke::t_enum: + { + Smoke::EnumFn fn = SmokeClass(t).enumFn(); + if(!fn) { + rb_warning("Unknown enumeration %s\n", t.name()); + _stack[i].s_enum = *(int*)p; + break; + } + Smoke::Index id = t.typeId(); + (*fn)(Smoke::EnumToLong, id, p, _stack[i].s_enum); + } + break; + case Smoke::t_class: + case Smoke::t_voidp: + _stack[i].s_voidp = p; + break; + } + } + } + } + } + void invokeSlot() { + if(_called) return; + _called = true; + + (void) rb_funcall2(_obj, _slotname, _items, _sp); + } + + void next() { + int oldcur = _cur; + _cur++; + + while(!_called && _cur < _items) { + Marshall::HandlerFn fn = getMarshallFn(type()); + (*fn)(this); + _cur++; + } + + invokeSlot(); + _cur = oldcur; + } + + InvokeSlot(VALUE obj, ID slotname, VALUE args, QUObject *o) : + _obj(obj), _slotname(slotname), _o(o), _cur(-1), _called(false) + { + _items = NUM2INT(rb_ary_entry(args, 0)); + Data_Get_Struct(rb_ary_entry(args, 1), MocArgument, _args); + _sp = (VALUE *) calloc(_items, sizeof(VALUE)); + _stack = new Smoke::StackItem[_items]; + copyArguments(); + } + + ~InvokeSlot() { + delete[] _stack; + free(_sp); + } +}; + +class QtRubySmokeBinding : public SmokeBinding { +public: + QtRubySmokeBinding(Smoke *s) : SmokeBinding(s) {} + + void deleted(Smoke::Index classId, void *ptr) { + VALUE obj = getPointerObject(ptr); + smokeruby_object *o = value_obj_info(obj); + if(do_debug & qtdb_gc) { + qWarning("%p->~%s()", ptr, smoke->className(classId)); + } + if(!o || !o->ptr) { + return; + } + unmapPointer(o, o->classId, 0); + o->ptr = 0; + } + + bool callMethod(Smoke::Index method, void *ptr, Smoke::Stack args, bool /*isAbstract*/) { + VALUE obj = getPointerObject(ptr); + smokeruby_object *o = value_obj_info(obj); + + if (do_debug & qtdb_virtual) { + Smoke::Method & meth = smoke->methods[method]; + QCString signature(smoke->methodNames[meth.name]); + signature += "("; + + for (int i = 0; i < meth.numArgs; i++) { + if (i != 0) signature += ", "; + signature += smoke->types[smoke->argumentList[meth.args + i]].name; + } + + signature += ")"; + if (meth.flags & Smoke::mf_const) { + signature += " const"; + } + + qWarning( "virtual %p->%s::%s called", + ptr, + smoke->classes[smoke->methods[method].classId].className, + (const char *) signature ); + } + + if(!o) { + if( do_debug & qtdb_virtual ) // if not in global destruction + qWarning("Cannot find object for virtual method %p -> %p", ptr, &obj); + return false; + } + + const char *methodName = smoke->methodNames[smoke->methods[method].name]; + + // If the virtual method hasn't been overriden, just call the C++ one. + if (rb_respond_to(obj, rb_intern(methodName)) == 0) { + return false; + } + + VirtualMethodCall c(smoke, method, args, obj); + c.next(); + return true; + } + + char *className(Smoke::Index classId) { + return classname.find((int) classId); + } +}; + +void rb_str_catf(VALUE self, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + char *p = 0; + int len; + if (len = vasprintf(&p, format, ap), len != -1) { + rb_str_cat(self, p, len); + free(p); + } + va_end(ap); +} + +extern "C" { + +static VALUE +qdebug(VALUE klass, VALUE msg) +{ + qDebug("%s", StringValuePtr(msg)); + return klass; +} + +static VALUE +qfatal(VALUE klass, VALUE msg) +{ + qFatal("%s", StringValuePtr(msg)); + return klass; +} + +static VALUE +qwarning(VALUE klass, VALUE msg) +{ + qWarning("%s", StringValuePtr(msg)); + return klass; +} + +// ---------------- Helpers ------------------- + +//---------- All functions except fully qualified statics & enums --------- + +static VALUE qobject_metaobject(VALUE self); +static VALUE kde_package_to_class(const char * package, VALUE base_class); + +VALUE +set_obj_info(const char * className, smokeruby_object * o) +{ + VALUE klass = rb_funcall(qt_internal_module, + rb_intern("find_class"), + 1, + rb_str_new2(className) ); + + Smoke::Index *r = classcache.find(className); + if (r != 0) { + o->classId = (int)*r; + } + + // If the instance is a subclass of QObject, then check to see if the + // className from its QMetaObject is in the Smoke library. If not then + // create a Ruby class for it dynamically. Remove the first letter from + // any class names beginning with 'Q' or 'K' and put them under the Qt:: + // or KDE:: modules respectively. + if (isDerivedFrom(o->smoke, o->classId, o->smoke->idClass("QObject"))) { + QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject")); + QMetaObject * meta = qobject->metaObject(); + int classId = o->smoke->idClass(meta->className()); + // The class isn't in the Smoke lib.. + if (classId == 0) { + VALUE new_klass = Qnil; + QString className(meta->className()); + // The konsolePart class is in kdebase, and so it can't be in the Smoke library. + // This hack instantiates a Ruby KDE::KonsolePart instance + if (className == "konsolePart") { + new_klass = konsole_part_class; + } else if (className.startsWith("Q")) { + className.replace("Q", ""); + className = className.mid(0, 1).upper() + className.mid(1); + new_klass = rb_define_class_under(qt_module, className.latin1(), klass); + } else if (kde_module == Qnil) { + new_klass = rb_define_class(className.latin1(), klass); + } else { + new_klass = kde_package_to_class(className.latin1(), klass); + } + + if (new_klass != Qnil) { + klass = new_klass; + } + + // Add a Qt::Object.metaObject method which will do dynamic despatch on the + // metaObject() virtual method so that the true QMetaObject of the class + // is returned, rather than for the one for the parent class that is in + // the Smoke library. + rb_define_method(klass, "metaObject", (VALUE (*) (...)) qobject_metaobject, 0); + } + } + + VALUE obj = Data_Wrap_Struct(klass, smokeruby_mark, smokeruby_free, (void *) o); + return obj; +} + +static VALUE mapObject(VALUE self, VALUE obj); + +VALUE +cast_object_to(VALUE /*self*/, VALUE object, VALUE new_klass) +{ + smokeruby_object *o = value_obj_info(object); + + VALUE new_klassname = rb_funcall(new_klass, rb_intern("name"), 0); + + Smoke::Index * cast_to_id = classcache.find(StringValuePtr(new_klassname)); + if (cast_to_id == 0) { + rb_raise(rb_eArgError, "unable to find class \"%s\" to cast to\n", StringValuePtr(new_klassname)); + } + + smokeruby_object *o_cast = (smokeruby_object *) malloc(sizeof(smokeruby_object)); + memcpy(o_cast, o, sizeof(smokeruby_object)); + + o_cast->allocated = o->allocated; + o->allocated = false; + + o_cast->classId = (int) *cast_to_id; + o_cast->ptr = o->smoke->cast(o->ptr, o->classId, o_cast->classId); + + VALUE obj = Data_Wrap_Struct(new_klass, smokeruby_mark, smokeruby_free, (void *) o_cast); + mapPointer(obj, o_cast, o_cast->classId, 0); + return obj; +} + +const char * +get_VALUEtype(VALUE ruby_value) +{ + char * classname = rb_obj_classname(ruby_value); + const char *r = ""; + if(ruby_value == Qnil) + r = "u"; + else if(TYPE(ruby_value) == T_FIXNUM || TYPE(ruby_value) == T_BIGNUM || qstrcmp(classname, "Qt::Integer") == 0) + r = "i"; + else if(TYPE(ruby_value) == T_FLOAT) + r = "n"; + else if(TYPE(ruby_value) == T_STRING) + r = "s"; + else if(ruby_value == Qtrue || ruby_value == Qfalse || qstrcmp(classname, "Qt::Boolean") == 0) + r = "B"; + else if(qstrcmp(classname, "Qt::Enum") == 0) { + VALUE temp = rb_funcall(qt_internal_module, rb_intern("get_qenum_type"), 1, ruby_value); + r = StringValuePtr(temp); + } else if(TYPE(ruby_value) == T_DATA) { + smokeruby_object *o = value_obj_info(ruby_value); + if(!o) { + r = "a"; + } else { + r = o->smoke->classes[o->classId].className; + } + } + else { + r = "U"; + } + + return r; +} + +VALUE prettyPrintMethod(Smoke::Index id) +{ + VALUE r = rb_str_new2(""); + Smoke::Method &meth = qt_Smoke->methods[id]; + const char *tname = qt_Smoke->types[meth.ret].name; + if(meth.flags & Smoke::mf_static) rb_str_catf(r, "static "); + rb_str_catf(r, "%s ", (tname ? tname:"void")); + rb_str_catf(r, "%s::%s(", qt_Smoke->classes[meth.classId].className, qt_Smoke->methodNames[meth.name]); + for(int i = 0; i < meth.numArgs; i++) { + if(i) rb_str_catf(r, ", "); + tname = qt_Smoke->types[qt_Smoke->argumentList[meth.args+i]].name; + rb_str_catf(r, "%s", (tname ? tname:"void")); + } + rb_str_catf(r, ")"); + if(meth.flags & Smoke::mf_const) rb_str_catf(r, " const"); + return r; +} + +//---------- Ruby methods (for all functions except fully qualified statics & enums) --------- + +// Used to display debugging info about the signals a Qt::Object has connected. +// Returns a Hash with keys of the signals names, and values of Arrays of +// Qt::Connections for the target slots +static VALUE +receivers_qobject(VALUE self) +{ + if (TYPE(self) != T_DATA) { + return Qnil; + } + + smokeruby_object * o = 0; + Data_Get_Struct(self, smokeruby_object, o); + UnencapsulatedQObject * qobject = (UnencapsulatedQObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject")); + VALUE result = rb_hash_new(); + QStrList signalNames = qobject->metaObject()->signalNames(true); + + for (int sig = 0; sig < qobject->metaObject()->numSignals(true); sig++) { + QConnectionList * clist = qobject->public_receivers(sig); + if (clist != 0) { + VALUE name = rb_str_new2(signalNames.at(sig)); + VALUE members = rb_ary_new(); + + for ( QConnection * connection = clist->first(); + connection != 0; + connection = clist->next() ) + { + VALUE obj = getPointerObject(connection); + if (obj == Qnil) { + smokeruby_object * c = ALLOC(smokeruby_object); + c->classId = o->smoke->idClass("QConnection"); + c->smoke = o->smoke; + c->ptr = connection; + c->allocated = false; + obj = set_obj_info("Qt::Connection", c); + } + + rb_ary_push(members, obj); + } + + rb_hash_aset(result, name, members); + } + } + + return result; +} + +// Takes a variable name and a QProperty with QVariant value, and returns a ' +// variable=value' pair with the value in ruby inspect style +static QCString +inspectProperty(Smoke * smoke, const QMetaProperty * property, const char * name, QVariant & value) +{ + if (property->isEnumType()) { + QMetaObject * metaObject = *(property->meta); + return QCString().sprintf( " %s=%s::%s", + name, + smoke->binding->className(smoke->idClass(metaObject->className())), + property->valueToKey(value.toInt()) ); + } + + switch (value.type()) { + case QVariant::String: + case QVariant::CString: + { + if (value.toString().isNull()) { + return QCString().sprintf(" %s=nil", name); + } else { + return QCString().sprintf( " %s=\"%s\"", + name, + value.toString().latin1() ); + } + } + + case QVariant::Bool: + { + QString rubyName; + QRegExp name_re("^(is|has)(.)(.*)"); + + if (name_re.search(name) != -1) { + rubyName = name_re.cap(2).lower() + name_re.cap(3) + "?"; + } else { + rubyName = name; + } + + return QCString().sprintf(" %s=%s", rubyName.latin1(), value.toString().latin1()); + } + + case QVariant::Color: + { + QColor c = value.toColor(); + return QCString().sprintf(" %s=#<Qt::Color:0x0 %s>", name, c.name().latin1()); + } + + case QVariant::Cursor: + { + QCursor c = value.toCursor(); + return QCString().sprintf(" %s=#<Qt::Cursor:0x0 shape=%d>", name, c.shape()); + } + + case QVariant::Double: + { + return QCString().sprintf(" %s=%.4f", name, value.toDouble()); + } + + case QVariant::Font: + { + QFont f = value.toFont(); + return QCString().sprintf( " %s=#<Qt::Font:0x0 family=%s, pointSize=%d, weight=%d, italic=%s, bold=%s, underline=%s, strikeOut=%s>", + name, + f.family().latin1(), f.pointSize(), f.weight(), + f.italic() ? "true" : "false", f.bold() ? "true" : "false", + f.underline() ? "true" : "false", f.strikeOut() ? "true" : "false" ); + } + + case QVariant::Point: + { + QPoint p = value.toPoint(); + return QCString().sprintf( " %s=#<Qt::Point:0x0 x=%d, y=%d>", + name, + p.x(), p.y() ); + } + + case QVariant::Rect: + { + QRect r = value.toRect(); + return QCString().sprintf( " %s=#<Qt::Rect:0x0 left=%d, right=%d, top=%d, bottom=%d>", + name, + r.left(), r.right(), r.top(), r.bottom() ); + } + + case QVariant::Size: + { + QSize s = value.toSize(); + return QCString().sprintf( " %s=#<Qt::Size:0x0 width=%d, height=%d>", + name, + s.width(), s.height() ); + } + + case QVariant::SizePolicy: + { + QSizePolicy s = value.toSizePolicy(); + return QCString().sprintf( " %s=#<Qt::SizePolicy:0x0 horData=%d, verData=%d>", + name, + s.horData(), s.verData() ); + } + + case QVariant::Brush: + case QVariant::ColorGroup: + case QVariant::Image: + case QVariant::Palette: + case QVariant::Pixmap: + case QVariant::Region: + { + return QCString().sprintf(" %s=#<Qt::%s:0x0>", name, value.typeName() + 1); + } + + default: + return QCString().sprintf( " %s=%s", + name, + (value.isNull() || value.toString().isNull()) ? "nil" : value.toString().latin1() ); + } +} + +// Retrieves the properties for a QObject and returns them as 'name=value' pairs +// in a ruby inspect string. For example: +// +// #<Qt::HBoxLayout:0x30139030 name=unnamed, margin=0, spacing=0, resizeMode=3> +// +static VALUE +inspect_qobject(VALUE self) +{ + if (TYPE(self) != T_DATA) { + return Qnil; + } + + // Start with #<Qt::HBoxLayout:0x30139030> from the original inspect() call + // Drop the closing '>' + VALUE inspect_str = rb_call_super(0, 0); + rb_str_resize(inspect_str, RSTRING(inspect_str)->len - 1); + + smokeruby_object * o = 0; + Data_Get_Struct(self, smokeruby_object, o); + QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject")); + + QCString value_list; + value_list.append(QCString().sprintf(" name=\"%s\"", qobject->name())); + + if (qobject->isWidgetType()) { + QWidget * w = (QWidget *) qobject; + value_list.append(QCString().sprintf( ", x=%d, y=%d, width=%d, height=%d", + w->x(), + w->y(), + w->width(), + w->height() ) ); + } + + value_list.append(">"); + rb_str_cat(inspect_str, value_list.data(), strlen(value_list.data())); + + return inspect_str; +} + +// Retrieves the properties for a QObject and pretty_prints them as 'name=value' pairs +// For example: +// +// #<Qt::HBoxLayout:0x30139030 +// name=unnamed, +// margin=0, +// spacing=0, +// resizeMode=3> +// +static VALUE +pretty_print_qobject(VALUE self, VALUE pp) +{ + if (TYPE(self) != T_DATA) { + return Qnil; + } + + // Start with #<Qt::HBoxLayout:0x30139030> + // Drop the closing '>' + VALUE inspect_str = rb_funcall(self, rb_intern("to_s"), 0, 0); + rb_str_resize(inspect_str, RSTRING(inspect_str)->len - 1); + rb_funcall(pp, rb_intern("text"), 1, inspect_str); + rb_funcall(pp, rb_intern("breakable"), 0); + + smokeruby_object * o = 0; + Data_Get_Struct(self, smokeruby_object, o); + UnencapsulatedQObject * qobject = (UnencapsulatedQObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject")); + QStrList names = qobject->metaObject()->propertyNames(true); + + QCString value_list; + + if (qobject->parent() != 0) { + QCString parentInspectString; + VALUE obj = getPointerObject(qobject->parent()); + if (obj != Qnil) { + VALUE parent_inspect_str = rb_funcall(obj, rb_intern("to_s"), 0, 0); + rb_str_resize(parent_inspect_str, RSTRING(parent_inspect_str)->len - 1); + parentInspectString = StringValuePtr(parent_inspect_str); + } else { + parentInspectString.sprintf("#<%s:0x0", qobject->parent()->className()); + } + + if (qobject->parent()->isWidgetType()) { + QWidget * w = (QWidget *) qobject->parent(); + value_list = QCString().sprintf( " parent=%s name=\"%s\", x=%d, y=%d, width=%d, height=%d>,\n", + parentInspectString.data(), + w->name(), + w->x(), + w->y(), + w->width(), + w->height() ); + } else { + value_list = QCString().sprintf( " parent=%s name=\"%s\">,\n", + parentInspectString.data(), + qobject->parent()->name() ); + } + + rb_funcall(pp, rb_intern("text"), 1, rb_str_new2(value_list.data())); + } + + if (qobject->children() != 0) { + value_list = QCString().sprintf(" children=Array (%d element(s)),\n", qobject->children()->count()); + rb_funcall(pp, rb_intern("text"), 1, rb_str_new2(value_list.data())); + } + + value_list = QCString(" metaObject=#<Qt::MetaObject:0x0"); + value_list.append(QCString().sprintf(" className=%s", qobject->metaObject()->className())); + + if (qobject->metaObject()->superClass() != 0) { + value_list.append(QCString().sprintf(", superClass=#<Qt::MetaObject:0x0>", qobject->metaObject()->superClass())); + } + + if (qobject->metaObject()->numSignals() > 0) { + value_list.append(QCString().sprintf(", signalNames=Array (%d element(s))", qobject->metaObject()->numSignals())); + } + + if (qobject->metaObject()->numSlots() > 0) { + value_list.append(QCString().sprintf(", slotNames=Array (%d element(s))", qobject->metaObject()->numSlots())); + } + + value_list.append(">,\n"); + rb_funcall(pp, rb_intern("text"), 1, rb_str_new2(value_list.data())); + + int signalCount = 0; + for (int sig = 0; sig < qobject->metaObject()->numSignals(true); sig++) { + QConnectionList * clist = qobject->public_receivers(sig); + if (clist != 0) { + signalCount++; + } + } + + if (signalCount > 0) { + value_list = QCString().sprintf(" receivers=Hash (%d element(s)),\n", signalCount); + rb_funcall(pp, rb_intern("text"), 1, rb_str_new2(value_list.data())); + } + + int index = 0; + const char * name = names.first(); + + if (name != 0) { + QVariant value = qobject->property(name); + const QMetaProperty * property = qobject->metaObject()->property(index, true); + value_list = " " + inspectProperty(o->smoke, property, name, value); + rb_funcall(pp, rb_intern("text"), 1, rb_str_new2(value_list.data())); + index++; + + for ( name = names.next(); + name != 0; + name = names.next(), index++ ) + { + rb_funcall(pp, rb_intern("text"), 1, rb_str_new2(",\n")); + + value = qobject->property(name); + property = qobject->metaObject()->property(index, true); + value_list = " " + inspectProperty(o->smoke, property, name, value); + rb_funcall(pp, rb_intern("text"), 1, rb_str_new2(value_list.data())); + } + } + + rb_funcall(pp, rb_intern("text"), 1, rb_str_new2(">")); + + return self; +} + +static VALUE +metaObject(VALUE self) +{ + VALUE metaObject = rb_funcall(qt_internal_module, rb_intern("getMetaObject"), 1, self); + return metaObject; +} + +static VALUE +qobject_metaobject(VALUE self) +{ + smokeruby_object * o = value_obj_info(self); + QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject")); + QMetaObject * meta = qobject->metaObject(); + VALUE obj = getPointerObject(meta); + if (obj != Qnil) { + return obj; + } + + smokeruby_object * m = (smokeruby_object *) malloc(sizeof(smokeruby_object)); + m->smoke = o->smoke; + m->classId = m->smoke->idClass("QMetaObject"); + m->ptr = meta; + m->allocated = false; + obj = set_obj_info("Qt::MetaObject", m); + return obj; +} + +static VALUE +new_qvariant(int argc, VALUE * argv, VALUE self) +{ +static Smoke::Index new_qvariant_qlist = 0; +static Smoke::Index new_qvariant_qmap = 0; + + if (new_qvariant_qlist == 0) { + Smoke::Index nameId = qt_Smoke->idMethodName("QVariant?"); + Smoke::Index meth = qt_Smoke->findMethod(qt_Smoke->idClass("QVariant"), nameId); + Smoke::Index i = qt_Smoke->methodMaps[meth].method; + i = -i; // turn into ambiguousMethodList index + while (qt_Smoke->ambiguousMethodList[i] != 0) { + const char * argType = qt_Smoke->types[qt_Smoke->argumentList[qt_Smoke->methods[qt_Smoke->ambiguousMethodList[i]].args]].name; + + if (qstrcmp(argType, "const QValueList<QVariant>&" ) == 0) { + new_qvariant_qlist = qt_Smoke->ambiguousMethodList[i]; + } else if (qstrcmp(argType, "const QMap<QString,QVariant>&" ) == 0) { + new_qvariant_qmap = qt_Smoke->ambiguousMethodList[i]; + } + + i++; + } + } + + if (argc == 1 && TYPE(argv[0]) == T_HASH) { + _current_method = new_qvariant_qmap; + MethodCall c(qt_Smoke, _current_method, self, argv, argc-1); + c.next(); + return *(c.var()); + } else if ( argc == 1 + && TYPE(argv[0]) == T_ARRAY + && RARRAY(argv[0])->len > 0 + && TYPE(rb_ary_entry(argv[0], 0)) != T_STRING ) + { + _current_method = new_qvariant_qlist; + MethodCall c(qt_Smoke, _current_method, self, argv, argc-1); + c.next(); + return *(c.var()); + } + + return rb_call_super(argc, argv); +} + +static QCString * +find_cached_selector(int argc, VALUE * argv, VALUE klass, char * methodName) +{ + // Look in the cache +static QCString * mcid = 0; + if (mcid == 0) { + mcid = new QCString(); + } + *mcid = rb_class2name(klass); + *mcid += ';'; + *mcid += methodName; + for(int i=3; i<argc ; i++) + { + *mcid += ';'; + *mcid += get_VALUEtype(argv[i]); + } + + Smoke::Index *rcid = methcache.find((const char *)*mcid); +#ifdef DEBUG + if (do_debug & qtdb_calls) qWarning("method_missing mcid: %s", (const char *) *mcid); +#endif + + if (rcid) { + // Got a hit +#ifdef DEBUG + if (do_debug & qtdb_calls) qWarning("method_missing cache hit, mcid: %s", (const char *) *mcid); +#endif + _current_method = *rcid; + } else { + _current_method = -1; + } + + return mcid; +} + +static VALUE +method_missing(int argc, VALUE * argv, VALUE self) +{ + char * methodName = rb_id2name(SYM2ID(argv[0])); + VALUE klass = rb_funcall(self, rb_intern("class"), 0); + + // Look for 'thing?' methods, and try to match isThing() or hasThing() in the Smoke runtime +static QString * pred = 0; + if (pred == 0) { + pred = new QString(); + } + + *pred = methodName; + if (pred->endsWith("?")) { + smokeruby_object *o = value_obj_info(self); + if(!o || !o->ptr) { + rb_call_super(argc, argv); + } + + // Drop the trailing '?' + pred->replace(pred->length() - 1, 1, ""); + + pred->replace(0, 1, pred->at(0).upper()); + pred->replace(0, 0, QString("is")); + Smoke::Index meth = o->smoke->findMethod(o->smoke->classes[o->classId].className, pred->latin1()); + + if (meth == 0) { + pred->replace(0, 2, QString("has")); + meth = o->smoke->findMethod(o->smoke->classes[o->classId].className, pred->latin1()); + } + + if (meth > 0) { + methodName = (char *) pred->latin1(); + } + } + + VALUE * temp_stack = (VALUE *) calloc(argc+3, sizeof(VALUE)); + temp_stack[0] = rb_str_new2("Qt"); + temp_stack[1] = rb_str_new2(methodName); + temp_stack[2] = klass; + temp_stack[3] = self; + for (int count = 1; count < argc; count++) { + temp_stack[count+3] = argv[count]; + } + + { + QCString * mcid = find_cached_selector(argc+3, temp_stack, klass, methodName); + + if (_current_method == -1) { + // Find the C++ method to call. Do that from Ruby for now + + VALUE retval = rb_funcall2(qt_internal_module, rb_intern("do_method_missing"), argc+3, temp_stack); + if (_current_method == -1) { + char * op = rb_id2name(SYM2ID(argv[0])); + if ( qstrcmp(op, "-") == 0 + || qstrcmp(op, "+") == 0 + || qstrcmp(op, "/") == 0 + || qstrcmp(op, "%") == 0 + || qstrcmp(op, "|") == 0 ) + { + // Look for operator methods of the form 'operator+=', 'operator-=' and so on.. + char op1[3]; + op1[0] = op[0]; + op1[1] = '='; + op1[2] = '\0'; + temp_stack[1] = rb_str_new2(op1); + retval = rb_funcall2(qt_internal_module, rb_intern("do_method_missing"), argc+3, temp_stack); + } + + if (_current_method == -1) { + free(temp_stack); + + // Check for property getter/setter calls + smokeruby_object *o = value_obj_info(self); + if ( o != 0 + && o->ptr != 0 + && isDerivedFrom(o->smoke, o->classId, o->smoke->idClass("QObject")) ) + { + QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject")); +static QString * prop = 0; + if (prop == 0) { + prop = new QString(); + } + + *prop = rb_id2name(SYM2ID(argv[0])); + QMetaObject * meta = qobject->metaObject(); + if (argc == 1) { + if (prop->endsWith("?")) { + prop->replace(0, 1, pred->at(0).upper()); + prop->replace(0, 0, QString("is")); + if (meta->findProperty(prop->latin1(), true) == -1) { + prop->replace(0, 2, QString("has")); + } + } + + if (meta->findProperty(prop->latin1(), true) != -1) { + VALUE qvariant = rb_funcall(self, rb_intern("property"), 1, rb_str_new2(prop->latin1())); + return rb_funcall(qvariant, rb_intern("to_ruby"), 0); + } + } else if (argc == 2 && prop->endsWith("=")) { + prop->replace("=", ""); + if (meta->findProperty(prop->latin1(), true) != -1) { + VALUE qvariant = rb_funcall(qvariant_class, rb_intern("new"), 1, argv[1]); + return rb_funcall(self, rb_intern("setProperty"), 2, rb_str_new2(prop->latin1()), qvariant); + } + } + } + + rb_call_super(argc, argv); + } + } + // Success. Cache result. + methcache.insert((const char *)*mcid, new Smoke::Index(_current_method)); + } + } + + MethodCall c(qt_Smoke, _current_method, self, temp_stack+4, argc-1); + c.next(); + VALUE result = *(c.var()); + free(temp_stack); + + return result; +} + +static VALUE +class_method_missing(int argc, VALUE * argv, VALUE klass) +{ + VALUE result = Qnil; + char * methodName = rb_id2name(SYM2ID(argv[0])); + VALUE * temp_stack = (VALUE *) calloc(argc+3, sizeof(VALUE)); + temp_stack[0] = rb_str_new2("Qt"); + temp_stack[1] = rb_str_new2(methodName); + temp_stack[2] = klass; + temp_stack[3] = Qnil; + for (int count = 1; count < argc; count++) { + temp_stack[count+3] = argv[count]; + } + + { + QCString * mcid = find_cached_selector(argc+3, temp_stack, klass, methodName); + + if (_current_method == -1) { + VALUE retval = rb_funcall2(qt_internal_module, rb_intern("do_method_missing"), argc+3, temp_stack); + Q_UNUSED(retval); + if (_current_method != -1) { + // Success. Cache result. + methcache.insert((const char *)*mcid, new Smoke::Index(_current_method)); + } + } + } + + if (_current_method == -1) { +static QRegExp * rx = 0; + if (rx == 0) { + rx = new QRegExp("[a-zA-Z]+"); + } + + if (rx->search(methodName) == -1) { + // If an operator method hasn't been found as an instance method, + // then look for a class method - after 'op(self,a)' try 'self.op(a)' + VALUE * method_stack = (VALUE *) calloc(argc - 1, sizeof(VALUE)); + method_stack[0] = argv[0]; + for (int count = 1; count < argc - 1; count++) { + method_stack[count] = argv[count+1]; + } + result = method_missing(argc-1, method_stack, argv[1]); + free(method_stack); + free(temp_stack); + return result; + } else { + rb_call_super(argc, argv); + } + } + + MethodCall c(qt_Smoke, _current_method, Qnil, temp_stack+4, argc-1); + c.next(); + result = *(c.var()); + free(temp_stack); + return result; +} + +static VALUE module_method_missing(int argc, VALUE * argv, VALUE /*klass*/) +{ + return class_method_missing(argc, argv, qt_module); +} + +static VALUE kde_module_method_missing(int argc, VALUE * argv, VALUE klass) +{ + return class_method_missing(argc, argv, klass); +} + +/* + +class LCDRange < Qt::Widget + + def initialize(s, parent, name) + super(parent, name) + init() + ... + +For a case such as the above, the QWidget can't be instantiated until +the initializer has been run up to the point where 'super(parent, name)' +is called. Only then, can the number and type of arguments passed to the +constructor be known. However, the rest of the intializer +can't be run until 'self' is a proper T_DATA object with a wrapped C++ +instance. + +The solution is to run the initialize code twice. First, only up to the +'super(parent, name)' call, where the QWidget would get instantiated in +initialize_qt(). And then rb_throw() jumps out of the +initializer returning the wrapped object as a result. + +The second time round 'self' will be the wrapped instance of type T_DATA, +so initialize() can be allowed to proceed to the end. +*/ +static VALUE +initialize_qt(int argc, VALUE * argv, VALUE self) +{ + VALUE retval; + VALUE temp_obj; + + if (TYPE(self) == T_DATA) { + // If a ruby block was passed then run that now + if (rb_block_given_p()) { + rb_funcall(qt_internal_module, rb_intern("run_initializer_block"), 2, self, rb_block_proc()); + } + + return self; + } + + VALUE klass = rb_funcall(self, rb_intern("class"), 0); + VALUE constructor_name = rb_str_new2("new"); + + VALUE * temp_stack = (VALUE *) calloc(argc+4, sizeof(VALUE)); + temp_stack[0] = rb_str_new2("Qt"); + temp_stack[1] = constructor_name; + temp_stack[2] = klass; + temp_stack[3] = self; + for (int count = 0; count < argc; count++) { + temp_stack[count+4] = argv[count]; + } + + { + // Put this in a C block so that the mcid will be de-allocated at the end of the block, + // rather than on f'n exit, to avoid the longjmp problem described below + QCString * mcid = find_cached_selector(argc+4, temp_stack, klass, rb_class2name(klass)); + + if (_current_method == -1) { + retval = rb_funcall2(qt_internal_module, rb_intern("do_method_missing"), argc+4, temp_stack); + if (_current_method != -1) { + // Success. Cache result. + methcache.insert((const char *)*mcid, new Smoke::Index(_current_method)); + } + } + } + + if (_current_method == -1) { + free(temp_stack); + // Another longjmp here.. + rb_raise(rb_eArgError, "unresolved constructor call %s\n", rb_class2name(klass)); + } + + { + // Allocate the MethodCall within a C block. Otherwise, because the continue_new_instance() + // call below will longjmp out, it wouldn't give C++ an opportunity to clean up + MethodCall c(qt_Smoke, _current_method, self, temp_stack+4, argc); + c.next(); + temp_obj = *(c.var()); + } + + smokeruby_object * p = 0; + Data_Get_Struct(temp_obj, smokeruby_object, p); + smokeruby_object * o = (smokeruby_object *) malloc(sizeof(smokeruby_object)); + memcpy(o, p, sizeof(smokeruby_object)); + p->ptr = 0; + p->allocated = false; + o->allocated = true; + VALUE result = Data_Wrap_Struct(klass, smokeruby_mark, smokeruby_free, o); + mapObject(result, result); + free(temp_stack); + // Off with a longjmp, never to return.. + rb_throw("newqt", result); + /*NOTREACHED*/ + return self; +} + +VALUE +new_qt(int argc, VALUE * argv, VALUE klass) +{ + VALUE * temp_stack = (VALUE *) calloc(argc + 1, sizeof(VALUE)); + temp_stack[0] = rb_obj_alloc(klass); + for (int count = 0; count < argc; count++) { + temp_stack[count+1] = argv[count]; + } + + VALUE result = rb_funcall2(qt_internal_module, rb_intern("try_initialize"), argc+1, temp_stack); + rb_obj_call_init(result, argc, argv); + + free(temp_stack); + return result; +} + +static VALUE +new_qapplication(int argc, VALUE * argv, VALUE klass) +{ + VALUE result = Qnil; + + if (argc == 1 && TYPE(argv[0]) == T_ARRAY) { + // Convert '(ARGV)' to '(NUM, [$0]+ARGV)' + VALUE * local_argv = (VALUE *) calloc(argc + 1, sizeof(VALUE)); + VALUE temp = rb_ary_dup(argv[0]); + rb_ary_unshift(temp, rb_gv_get("$0")); + local_argv[0] = INT2NUM(RARRAY(temp)->len); + local_argv[1] = temp; + result = new_qt(2, local_argv, klass); + free(local_argv); + } else { + result = new_qt(argc, argv, klass); + } + + rb_gv_set("$qApp", result); + return result; +} + +// Returns $qApp.ARGV() - the original ARGV array with Qt command line options removed +static VALUE +qapplication_argv(VALUE /*self*/) +{ + VALUE result = rb_ary_new(); + // Drop argv[0], as it isn't included in the ruby global ARGV + for (int index = 1; index < qApp->argc(); index++) { + rb_ary_push(result, rb_str_new2(qApp->argv()[index])); + } + + return result; +} + +//----------------- Sig/Slot ------------------ + + +VALUE +getmetainfo(VALUE self, int &offset, int &index) +{ + char * signalname = rb_id2name(rb_frame_last_func()); + VALUE metaObject_value = rb_funcall(qt_internal_module, rb_intern("getMetaObject"), 1, self); + + smokeruby_object *ometa = value_obj_info(metaObject_value); + if(!ometa) return 0; + QMetaObject *metaobject = (QMetaObject*)ometa->ptr; + + offset = metaobject->signalOffset(); + + VALUE signalInfo = rb_funcall(qt_internal_module, rb_intern("signalInfo"), 2, self, rb_str_new2(signalname)); + VALUE member = rb_ary_entry(signalInfo, 0); + index = NUM2INT(rb_ary_entry(signalInfo, 1)); + return rb_funcall(qt_internal_module, rb_intern("getMocArguments"), 1, member); +} + +VALUE +getslotinfo(VALUE self, int id, char *&slotname, int &index, bool isSignal = false) +{ + VALUE member; + + VALUE metaObject_value = rb_funcall(qt_internal_module, rb_intern("getMetaObject"), 1, self); + smokeruby_object *ometa = value_obj_info(metaObject_value); + if(!ometa) return Qnil; + + QMetaObject *metaobject = (QMetaObject*)ometa->ptr; + + int offset = isSignal ? metaobject->signalOffset() : metaobject->slotOffset(); + + index = id - offset; // where we at + if(index < 0) return Qnil; + + if (isSignal) { + member = rb_funcall(qt_internal_module, rb_intern("signalAt"), 2, self, INT2NUM(index)); + } else { + member = rb_funcall(qt_internal_module, rb_intern("slotAt"), 2, self, INT2NUM(index)); + } + + VALUE mocArgs = rb_funcall(qt_internal_module, rb_intern("getMocArguments"), 1, member); + slotname = StringValuePtr(member); + + return mocArgs; +} + +static VALUE +qt_signal(int argc, VALUE * argv, VALUE self) +{ + smokeruby_object *o = value_obj_info(self); + QObject *qobj = (QObject*)o->smoke->cast( + o->ptr, + o->classId, + o->smoke->idClass("QObject") + ); + if(qobj->signalsBlocked()) return Qfalse; + + int offset; + int index; + + VALUE args = getmetainfo(self, offset, index); + + if(args == Qnil) return Qfalse; + + // Okay, we have the signal info. *whew* + EmitSignal signal(qobj, offset + index, argc, args, argv); + signal.next(); + + return Qtrue; +} + +static VALUE +qt_invoke(int /*argc*/, VALUE * argv, VALUE self) +{ + // Arguments: int id, QUObject *o + int id = NUM2INT(argv[0]); + QUObject *_o = 0; + + Data_Get_Struct(rb_ary_entry(argv[1], 0), QUObject, _o); + if(_o == 0) { + rb_raise(rb_eRuntimeError, "Cannot create QUObject\n"); + } + + smokeruby_object *o = value_obj_info(self); + (void) (QObject*)o->smoke->cast( + o->ptr, + o->classId, + o->smoke->idClass("QObject") + ); + + // Now, I need to find out if this means me + int index; + char *slotname; + bool isSignal = qstrcmp(rb_id2name(rb_frame_last_func()), "qt_emit") == 0; + VALUE mocArgs = getslotinfo(self, id, slotname, index, isSignal); + if(mocArgs == Qnil) { + // No ruby slot/signal found, assume the target is a C++ one + Smoke::Index nameId = o->smoke->idMethodName(isSignal ? "qt_emit$?" : "qt_invoke$?"); + Smoke::Index meth = o->smoke->findMethod(o->classId, nameId); + if(meth > 0) { + Smoke::Method &m = o->smoke->methods[o->smoke->methodMaps[meth].method]; + Smoke::ClassFn fn = o->smoke->classes[m.classId].classFn; + Smoke::StackItem i[3]; + i[1].s_int = id; + i[2].s_class = _o; + (*fn)(m.method, o->ptr, i); + return i[0].s_bool == 1 ? Qtrue : Qfalse; + } + + // Should never happen.. + rb_raise(rb_eRuntimeError, "Cannot find %s::qt_invoke() method\n", + o->smoke->classes[o->classId].className ); + } + + QString name(slotname); +static QRegExp * rx = 0; + if (rx == 0) { + rx = new QRegExp("\\(.*"); + } + name.replace(*rx, ""); + + InvokeSlot slot(self, rb_intern(name.latin1()), mocArgs, _o); + slot.next(); + + return Qtrue; +} + +static VALUE +qobject_connect(int argc, VALUE * argv, VALUE self) +{ + if (rb_block_given_p()) { + if (argc == 1) { + return rb_funcall(qt_internal_module, rb_intern("signal_connect"), 3, self, argv[0], rb_block_proc()); + } else if (argc == 2) { + return rb_funcall(qt_internal_module, rb_intern("connect"), 4, argv[0], argv[1], self, rb_block_proc()); + } else if (argc == 3) { + return rb_funcall(qt_internal_module, rb_intern("connect"), 4, argv[0], argv[1], argv[2], rb_block_proc()); + } else { + rb_raise(rb_eArgError, "Invalid argument list"); + } + } else { + return rb_call_super(argc, argv); + } +} + +// --------------- Ruby C functions for Qt::_internal.* helpers ---------------- + + +static VALUE +getMethStat(VALUE /*self*/) +{ + VALUE result_list = rb_ary_new(); + rb_ary_push(result_list, INT2NUM((int)methcache.size())); + rb_ary_push(result_list, INT2NUM((int)methcache.count())); + return result_list; +} + +static VALUE +getClassStat(VALUE /*self*/) +{ + VALUE result_list = rb_ary_new(); + rb_ary_push(result_list, INT2NUM((int)classcache.size())); + rb_ary_push(result_list, INT2NUM((int)classcache.count())); + return result_list; +} + +static VALUE +getIsa(VALUE /*self*/, VALUE classId) +{ + VALUE parents_list = rb_ary_new(); + + Smoke::Index *parents = + qt_Smoke->inheritanceList + + qt_Smoke->classes[NUM2INT(classId)].parents; + + while(*parents) { + //qWarning("\tparent: %s", qt_Smoke->classes[*parents].className); + rb_ary_push(parents_list, rb_str_new2(qt_Smoke->classes[*parents++].className)); + } + return parents_list; +} + +// Return the class name of a QObject. Note that the name will be in the +// form of Qt::Widget rather than QWidget. Is this a bug or a feature? +static VALUE +class_name(VALUE self) +{ + VALUE klass = rb_funcall(self, rb_intern("class"), 0); + return rb_funcall(klass, rb_intern("name"), 0); +} + +// Allow classnames in both 'Qt::Widget' and 'QWidget' formats to be +// used as an argument to Qt::Object.inherits() +static VALUE +inherits_qobject(int argc, VALUE * argv, VALUE /*self*/) +{ + if (argc != 1) { + return rb_call_super(argc, argv); + } + + Smoke::Index * classId = classcache.find(StringValuePtr(argv[0])); + + if (classId == 0) { + return rb_call_super(argc, argv); + } else { + VALUE super_class = rb_str_new2(qt_Smoke->classes[*classId].className); + return rb_call_super(argc, &super_class); + } +} + +static VALUE +qbytearray_data(VALUE self) +{ + smokeruby_object *o = value_obj_info(self); + if (o == 0 || o->ptr == 0) { + return Qnil; + } + QByteArray * dataArray = (QByteArray*) o->ptr; + return rb_str_new(dataArray->data(), (long) dataArray->size()); +} + +static VALUE +qbytearray_size(VALUE self) +{ + smokeruby_object *o = value_obj_info(self); + if (o == 0 || o->ptr == 0) { + return Qnil; + } + QByteArray * dataArray = (QByteArray*) o->ptr; + return UINT2NUM(dataArray->size()); +} + +static VALUE +qbytearray_setRawData(VALUE self, VALUE data) +{ + smokeruby_object *o = value_obj_info(self); + if (o == 0 || o->ptr == 0) { + return Qnil; + } + QByteArray * dataArray = (QByteArray*) o->ptr; + dataArray->setRawData(StringValuePtr(data), RSTRING(data)->len); + return self; +} + +static void +mocargs_free(void * ptr) +{ + MocArgument * mocArgs = (MocArgument *) ptr; + delete[] mocArgs; + return; +} + +static VALUE +allocateMocArguments(VALUE /*self*/, VALUE count_value) +{ + int count = NUM2INT(count_value); + MocArgument * ptr = new MocArgument[count + 1]; + return Data_Wrap_Struct(rb_cObject, 0, mocargs_free, ptr); +} + +static VALUE +setMocType(VALUE /*self*/, VALUE ptr, VALUE idx_value, VALUE name_value, VALUE static_type_value) +{ + int idx = NUM2INT(idx_value); + char *name = StringValuePtr(name_value); + char *static_type = StringValuePtr(static_type_value); + Smoke::Index typeId = qt_Smoke->idType(name); + if(!typeId) return Qfalse; + MocArgument *arg = 0; + Data_Get_Struct(ptr, MocArgument, arg); + arg[idx].st.set(qt_Smoke, typeId); + if(qstrcmp(static_type, "ptr") == 0) + arg[idx].argType = xmoc_ptr; + else if(qstrcmp(static_type, "bool") == 0) + arg[idx].argType = xmoc_bool; + else if(qstrcmp(static_type, "int") == 0) + arg[idx].argType = xmoc_int; + else if(qstrcmp(static_type, "double") == 0) + arg[idx].argType = xmoc_double; + else if(qstrcmp(static_type, "char*") == 0) + arg[idx].argType = xmoc_charstar; + else if(qstrcmp(static_type, "QString") == 0) + arg[idx].argType = xmoc_QString; + return Qtrue; +} + +static VALUE +setDebug(VALUE self, VALUE on_value) +{ + int on = NUM2INT(on_value); + do_debug = on; + return self; +} + +static VALUE +debugging(VALUE /*self*/) +{ + return INT2NUM(do_debug); +} + +static VALUE +getTypeNameOfArg(VALUE /*self*/, VALUE method_value, VALUE idx_value) +{ + int method = NUM2INT(method_value); + int idx = NUM2INT(idx_value); + Smoke::Method &m = qt_Smoke->methods[method]; + Smoke::Index *args = qt_Smoke->argumentList + m.args; + return rb_str_new2((char*)qt_Smoke->types[args[idx]].name); +} + +static VALUE +classIsa(VALUE /*self*/, VALUE className_value, VALUE base_value) +{ + char *className = StringValuePtr(className_value); + char *base = StringValuePtr(base_value); + return isDerivedFromByName(qt_Smoke, className, base) ? Qtrue : Qfalse; +} + +static VALUE +isEnum(VALUE /*self*/, VALUE enumName_value) +{ + char *enumName = StringValuePtr(enumName_value); + Smoke::Index typeId = qt_Smoke->idType(enumName); + return typeId > 0 + && ( (qt_Smoke->types[typeId].flags & Smoke::tf_elem) == Smoke::t_enum + || (qt_Smoke->types[typeId].flags & Smoke::tf_elem) == Smoke::t_ulong + || (qt_Smoke->types[typeId].flags & Smoke::tf_elem) == Smoke::t_long + || (qt_Smoke->types[typeId].flags & Smoke::tf_elem) == Smoke::t_uint + || (qt_Smoke->types[typeId].flags & Smoke::tf_elem) == Smoke::t_int ) ? Qtrue : Qfalse; +} + +static VALUE +insert_pclassid(VALUE self, VALUE p_value, VALUE ix_value) +{ + char *p = StringValuePtr(p_value); + int ix = NUM2INT(ix_value); + classcache.insert(p, new Smoke::Index((Smoke::Index)ix)); + classname.insert(ix, strdup(p)); + return self; +} + +static VALUE +find_pclassid(VALUE /*self*/, VALUE p_value) +{ + char *p = StringValuePtr(p_value); + Smoke::Index *r = classcache.find(p); + if(r) + return INT2NUM((int)*r); + else + return INT2NUM(0); +} + +static VALUE +insert_mcid(VALUE self, VALUE mcid_value, VALUE ix_value) +{ + char *mcid = StringValuePtr(mcid_value); + int ix = NUM2INT(ix_value); + methcache.insert(mcid, new Smoke::Index((Smoke::Index)ix)); + return self; +} + +static VALUE +find_mcid(VALUE /*self*/, VALUE mcid_value) +{ + char *mcid = StringValuePtr(mcid_value); + Smoke::Index *r = methcache.find(mcid); + if(r) + return INT2NUM((int)*r); + else + return INT2NUM(0); +} + +static VALUE +getVALUEtype(VALUE /*self*/, VALUE ruby_value) +{ + return rb_str_new2(get_VALUEtype(ruby_value)); +} + +static VALUE +make_QUParameter(VALUE /*self*/, VALUE name_value, VALUE type_value, VALUE /*extra*/, VALUE inout_value) +{ + char *name = StringValuePtr(name_value); + char *type = StringValuePtr(type_value); + int inout = NUM2INT(inout_value); + QUParameter *p = new QUParameter; + p->name = new char[strlen(name) + 1]; + strcpy((char*)p->name, name); + if(qstrcmp(type, "bool") == 0) + p->type = &static_QUType_bool; + else if(qstrcmp(type, "int") == 0) + p->type = &static_QUType_int; + else if(qstrcmp(type, "double") == 0) + p->type = &static_QUType_double; + else if(qstrcmp(type, "char*") == 0 || qstrcmp(type, "const char*") == 0) + p->type = &static_QUType_charstar; + else if(qstrcmp(type, "QString") == 0 || qstrcmp(type, "QString&") == 0 || + qstrcmp(type, "const QString") == 0 || qstrcmp(type, "const QString&") == 0) + p->type = &static_QUType_QString; + else + p->type = &static_QUType_ptr; + // Lacking support for several types. Evil. + p->inOut = inout; + p->typeExtra = 0; + return Data_Wrap_Struct(rb_cObject, 0, 0, p); +} + +static VALUE +make_QMetaData(VALUE /*self*/, VALUE name_value, VALUE method) +{ + char *name = StringValuePtr(name_value); + QMetaData *m = new QMetaData; // will be deleted + m->name = new char[strlen(name) + 1]; + strcpy((char*)m->name, name); + Data_Get_Struct(method, QUMethod, m->method); + m->access = QMetaData::Public; + return Data_Wrap_Struct(rb_cObject, 0, 0, m); +} + +static VALUE +make_QUMethod(VALUE /*self*/, VALUE name_value, VALUE params) +{ + char *name = StringValuePtr(name_value); + QUMethod *m = new QUMethod; // permanent memory allocation + m->name = new char[strlen(name) + 1]; // this too + strcpy((char*)m->name, name); + m->parameters = 0; + m->count = RARRAY(params)->len; + + if (m->count > 0) { + m->parameters = new QUParameter[m->count]; + for (long i = 0; i < m->count; i++) { + VALUE param = rb_ary_entry(params, i); + QUParameter *p = 0; + Data_Get_Struct(param, QUParameter, p); + ((QUParameter *) m->parameters)[i] = *p; + delete p; + } + } + return Data_Wrap_Struct(rb_cObject, 0, 0, m); +} + +static VALUE +make_QMetaData_tbl(VALUE /*self*/, VALUE list) +{ + long count = RARRAY(list)->len; + QMetaData *m = new QMetaData[count]; + + for (long i = 0; i < count; i++) { + VALUE item = rb_ary_entry(list, i); + + QMetaData *old = 0; + Data_Get_Struct(item, QMetaData, old); + m[i] = *old; + delete old; + } + + return Data_Wrap_Struct(rb_cObject, 0, 0, m); +} + +static VALUE +make_metaObject(VALUE /*self*/, VALUE className_value, VALUE parent, VALUE slot_tbl_value, VALUE slot_count_value, VALUE signal_tbl_value, VALUE signal_count_value) +{ + char *className = strdup(StringValuePtr(className_value)); + + QMetaData * slot_tbl = 0; + int slot_count = 0; + if (slot_tbl_value != Qnil) { + Data_Get_Struct(slot_tbl_value, QMetaData, slot_tbl); + slot_count = NUM2INT(slot_count_value); + } + + QMetaData * signal_tbl = 0; + int signal_count = 0; + if (signal_tbl_value != Qnil) { + Data_Get_Struct(signal_tbl_value, QMetaData, signal_tbl); + signal_count = NUM2INT(signal_count_value); + } + + smokeruby_object *po = value_obj_info(parent); + if(!po || !po->ptr) { + rb_raise(rb_eRuntimeError, "Cannot create metaObject\n"); + } + + QMetaObject *meta = QMetaObject::new_metaobject( + className, (QMetaObject*)po->ptr, + (const QMetaData*)slot_tbl, slot_count, // slots + (const QMetaData*)signal_tbl, signal_count, // signals + 0, 0, // properties + 0, 0, // enums + 0, 0); + + smokeruby_object * o = (smokeruby_object *) malloc(sizeof(smokeruby_object)); + o->smoke = qt_Smoke; + o->classId = qt_Smoke->idClass("QMetaObject"); + o->ptr = meta; + o->allocated = true; + + return Data_Wrap_Struct(qmetaobject_class, smokeruby_mark, smokeruby_free, o); +} + +static VALUE +add_metaobject_methods(VALUE self, VALUE klass) +{ + rb_define_method(klass, "qt_invoke", (VALUE (*) (...)) qt_invoke, -1); + rb_define_method(klass, "qt_emit", (VALUE (*) (...)) qt_invoke, -1); + rb_define_method(klass, "metaObject", (VALUE (*) (...)) metaObject, 0); + return self; +} + +static VALUE +add_signal_methods(VALUE self, VALUE klass, VALUE signalNames) +{ + for (long index = 0; index < RARRAY(signalNames)->len; index++) { + VALUE signal = rb_ary_entry(signalNames, index); + rb_define_method(klass, StringValuePtr(signal), (VALUE (*) (...)) qt_signal, -1); + } + return self; +} + +static VALUE +dispose(VALUE self) +{ + smokeruby_object *o = value_obj_info(self); + if(!o || !o->ptr) { return Qnil; } + + const char *className = o->smoke->classes[o->classId].className; + if(do_debug & qtdb_gc) printf("Deleting (%s*)%p\n", className, o->ptr); + + unmapPointer(o, o->classId, 0); + object_count--; + + char *methodName = new char[strlen(className) + 2]; + methodName[0] = '~'; + strcpy(methodName + 1, className); + Smoke::Index nameId = o->smoke->idMethodName(methodName); + Smoke::Index meth = o->smoke->findMethod(o->classId, nameId); + if(meth > 0) { + Smoke::Method &m = o->smoke->methods[o->smoke->methodMaps[meth].method]; + Smoke::ClassFn fn = o->smoke->classes[m.classId].classFn; + Smoke::StackItem i[1]; + (*fn)(m.method, o->ptr, i); + } + delete[] methodName; + o->ptr = 0; + o->allocated = false; + + return self; +} + +static VALUE +is_disposed(VALUE self) +{ + smokeruby_object *o = value_obj_info(self); + if(!o || !o->ptr) { return Qtrue; } + return Qfalse; +} + +static VALUE +mapObject(VALUE self, VALUE obj) +{ + smokeruby_object *o = value_obj_info(obj); + if(!o) + return Qnil; + mapPointer(obj, o, o->classId, 0); + return self; +} + +static VALUE +isaQObject(VALUE /*self*/, VALUE classid) +{ + int classid_value = NUM2INT(classid); + return isQObject(qt_Smoke, classid_value) ? Qtrue : Qfalse; +} + +// Returns the Smoke classId of a ruby instance +static VALUE +idInstance(VALUE /*self*/, VALUE instance) +{ + smokeruby_object *o = value_obj_info(instance); + if(!o) + return Qnil; + + return INT2NUM(o->classId); +} + +static VALUE +idClass(VALUE /*self*/, VALUE name_value) +{ + char *name = StringValuePtr(name_value); + return INT2NUM(qt_Smoke->idClass(name)); +} + +static VALUE +idMethodName(VALUE /*self*/, VALUE name_value) +{ + char *name = StringValuePtr(name_value); + return INT2NUM(qt_Smoke->idMethodName(name)); +} + +static VALUE +idMethod(VALUE /*self*/, VALUE idclass_value, VALUE idmethodname_value) +{ + int idclass = NUM2INT(idclass_value); + int idmethodname = NUM2INT(idmethodname_value); + return INT2NUM(qt_Smoke->idMethod(idclass, idmethodname)); +} + +static VALUE +findMethod(VALUE /*self*/, VALUE c_value, VALUE name_value) +{ + char *c = StringValuePtr(c_value); + char *name = StringValuePtr(name_value); + VALUE result = rb_ary_new(); + Smoke::Index meth = qt_Smoke->findMethod(c, name); +#ifdef DEBUG + if (do_debug & qtdb_calls) qWarning("DAMNIT on %s::%s => %d", c, name, meth); +#endif + if(!meth) { + meth = qt_Smoke->findMethod("QGlobalSpace", name); +#ifdef DEBUG + if (do_debug & qtdb_calls) qWarning("DAMNIT on QGlobalSpace::%s => %d", name, meth); +#endif + } + + if(!meth) { + return result; + // empty list + } else if(meth > 0) { + Smoke::Index i = qt_Smoke->methodMaps[meth].method; + if(!i) { // shouldn't happen + rb_raise(rb_eArgError, "Corrupt method %s::%s", c, name); + } else if(i > 0) { // single match + Smoke::Method &methodRef = qt_Smoke->methods[i]; + if ((methodRef.flags & Smoke::mf_internal) == 0) { + rb_ary_push(result, INT2NUM(i)); + } + } else { // multiple match + i = -i; // turn into ambiguousMethodList index + while(qt_Smoke->ambiguousMethodList[i]) { + Smoke::Method &methodRef = qt_Smoke->methods[qt_Smoke->ambiguousMethodList[i]]; + if ((methodRef.flags & Smoke::mf_internal) == 0) { + rb_ary_push(result, INT2NUM(qt_Smoke->ambiguousMethodList[i])); +#ifdef DEBUG + if (do_debug & qtdb_calls) qWarning("Ambiguous Method %s::%s => %d", c, name, qt_Smoke->ambiguousMethodList[i]); +#endif + + } + i++; + } + } + } + return result; +} + +// findAllMethods(classid [, startingWith]) : returns { "mungedName" => [index in methods, ...], ... } + +static VALUE +findAllMethods(int argc, VALUE * argv, VALUE /*self*/) +{ + VALUE classid = argv[0]; + VALUE result = rb_hash_new(); + if(classid != Qnil) { + Smoke::Index c = (Smoke::Index) NUM2INT(classid); + if (c > qt_Smoke->numClasses) { + return Qnil; + } + char * pat = 0L; + if(argc > 1 && TYPE(argv[1]) == T_STRING) + pat = StringValuePtr(argv[1]); +#ifdef DEBUG + if (do_debug & qtdb_calls) qWarning("findAllMethods called with classid = %d, pat == %s", c, pat); +#endif + Smoke::Index imax = qt_Smoke->numMethodMaps; + Smoke::Index imin = 0, icur = -1, methmin, methmax; + methmin = -1; methmax = -1; // kill warnings + int icmp = -1; + while(imax >= imin) { + icur = (imin + imax) / 2; + icmp = qt_Smoke->leg(qt_Smoke->methodMaps[icur].classId, c); + if(!icmp) { + Smoke::Index pos = icur; + while(icur && qt_Smoke->methodMaps[icur-1].classId == c) + icur --; + methmin = icur; + icur = pos; + while(icur < imax && qt_Smoke->methodMaps[icur+1].classId == c) + icur ++; + methmax = icur; + break; + } + if (icmp > 0) + imax = icur - 1; + else + imin = icur + 1; + } + if(!icmp) { + for(Smoke::Index i=methmin ; i <= methmax ; i++) { + Smoke::Index m = qt_Smoke->methodMaps[i].name; + if(!pat || !qstrncmp(qt_Smoke->methodNames[m], pat, strlen(pat))) { + Smoke::Index ix= qt_Smoke->methodMaps[i].method; + VALUE meths = rb_ary_new(); + if(ix >= 0) { // single match + Smoke::Method &methodRef = qt_Smoke->methods[ix]; + if ((methodRef.flags & Smoke::mf_internal) == 0) { + rb_ary_push(meths, INT2NUM((int)ix)); + } + } else { // multiple match + ix = -ix; // turn into ambiguousMethodList index + while(qt_Smoke->ambiguousMethodList[ix]) { + Smoke::Method &methodRef = qt_Smoke->methods[qt_Smoke->ambiguousMethodList[ix]]; + if ((methodRef.flags & Smoke::mf_internal) == 0) { + rb_ary_push(meths, INT2NUM((int)qt_Smoke->ambiguousMethodList[ix])); + } + ix++; + } + } + rb_hash_aset(result, rb_str_new2(qt_Smoke->methodNames[m]), meths); + } + } + } + } + return result; +} + +/* + Flags values + 0 All methods, except enum values and protected non-static methods + mf_static Static methods only + mf_enum Enums only + mf_protected Protected non-static methods only +*/ + +#define PUSH_QTRUBY_METHOD \ + if ( (methodRef.flags & (Smoke::mf_internal|Smoke::mf_ctor|Smoke::mf_dtor)) == 0 \ + && qstrcmp(qt_Smoke->methodNames[methodRef.name], "operator=") != 0 \ + && qstrcmp(qt_Smoke->methodNames[methodRef.name], "operator!=") != 0 \ + && qstrcmp(qt_Smoke->methodNames[methodRef.name], "operator--") != 0 \ + && qstrcmp(qt_Smoke->methodNames[methodRef.name], "operator++") != 0 \ + && qstrncmp(qt_Smoke->methodNames[methodRef.name], "operator ", strlen("operator ")) != 0 \ + && ( (flags == 0 && (methodRef.flags & (Smoke::mf_static|Smoke::mf_enum|Smoke::mf_protected)) == 0) \ + || ( flags == Smoke::mf_static \ + && (methodRef.flags & Smoke::mf_enum) == 0 \ + && (methodRef.flags & Smoke::mf_static) == Smoke::mf_static ) \ + || (flags == Smoke::mf_enum && (methodRef.flags & Smoke::mf_enum) == Smoke::mf_enum) \ + || ( flags == Smoke::mf_protected \ + && (methodRef.flags & Smoke::mf_static) == 0 \ + && (methodRef.flags & Smoke::mf_protected) == Smoke::mf_protected ) ) ) { \ + if (qstrncmp(qt_Smoke->methodNames[methodRef.name], "operator", strlen("operator")) == 0) { \ + if (op_re.search(qt_Smoke->methodNames[methodRef.name]) != -1) { \ + rb_ary_push(result, rb_str_new2(op_re.cap(1) + op_re.cap(2))); \ + } else { \ + rb_ary_push(result, rb_str_new2(qt_Smoke->methodNames[methodRef.name] + strlen("operator"))); \ + } \ + } else if (predicate_re.search(qt_Smoke->methodNames[methodRef.name]) != -1 && methodRef.numArgs == 0) { \ + rb_ary_push(result, rb_str_new2(predicate_re.cap(2).lower() + predicate_re.cap(3) + "?")); \ + } else if (set_re.search(qt_Smoke->methodNames[methodRef.name]) != -1 && methodRef.numArgs == 1) { \ + rb_ary_push(result, rb_str_new2(set_re.cap(2).lower() + set_re.cap(3) + "=")); \ + } else { \ + rb_ary_push(result, rb_str_new2(qt_Smoke->methodNames[methodRef.name])); \ + } \ + } + +static VALUE +findAllMethodNames(VALUE /*self*/, VALUE result, VALUE classid, VALUE flags_value) +{ + QRegExp predicate_re("^(is|has)(.)(.*)"); + QRegExp set_re("^(set)([A-Z])(.*)"); + QRegExp op_re("operator(.*)(([-%~/+|&*])|(>>)|(<<)|(&&)|(\\|\\|)|(\\*\\*))=$"); + + unsigned short flags = (unsigned short) NUM2UINT(flags_value); + if (classid != Qnil) { + Smoke::Index c = (Smoke::Index) NUM2INT(classid); + if (c > qt_Smoke->numClasses) { + return Qnil; + } +#ifdef DEBUG + if (do_debug & qtdb_calls) qWarning("findAllMethodNames called with classid = %d", c); +#endif + Smoke::Index imax = qt_Smoke->numMethodMaps; + Smoke::Index imin = 0, icur = -1, methmin, methmax; + methmin = -1; methmax = -1; // kill warnings + int icmp = -1; + + while (imax >= imin) { + icur = (imin + imax) / 2; + icmp = qt_Smoke->leg(qt_Smoke->methodMaps[icur].classId, c); + if (icmp == 0) { + Smoke::Index pos = icur; + while(icur && qt_Smoke->methodMaps[icur-1].classId == c) + icur --; + methmin = icur; + icur = pos; + while(icur < imax && qt_Smoke->methodMaps[icur+1].classId == c) + icur ++; + methmax = icur; + break; + } + if (icmp > 0) + imax = icur - 1; + else + imin = icur + 1; + } + + if (icmp == 0) { + for (Smoke::Index i=methmin ; i <= methmax ; i++) { + Smoke::Index ix= qt_Smoke->methodMaps[i].method; + if (ix >= 0) { // single match + Smoke::Method &methodRef = qt_Smoke->methods[ix]; + PUSH_QTRUBY_METHOD + } else { // multiple match + ix = -ix; // turn into ambiguousMethodList index + while (qt_Smoke->ambiguousMethodList[ix]) { + Smoke::Method &methodRef = qt_Smoke->methods[qt_Smoke->ambiguousMethodList[ix]]; + PUSH_QTRUBY_METHOD + ix++; + } + } + } + } + } + return result; +} + +static VALUE +dumpCandidates(VALUE /*self*/, VALUE rmeths) +{ + VALUE errmsg = rb_str_new2(""); + if(rmeths != Qnil) { + int count = RARRAY(rmeths)->len; + for(int i = 0; i < count; i++) { + rb_str_catf(errmsg, "\t"); + int id = NUM2INT(rb_ary_entry(rmeths, i)); + Smoke::Method &meth = qt_Smoke->methods[id]; + const char *tname = qt_Smoke->types[meth.ret].name; + if(meth.flags & Smoke::mf_enum) { + rb_str_catf(errmsg, "enum "); + rb_str_catf(errmsg, "%s::%s", qt_Smoke->classes[meth.classId].className, qt_Smoke->methodNames[meth.name]); + rb_str_catf(errmsg, "\n"); + } else { + if(meth.flags & Smoke::mf_static) rb_str_catf(errmsg, "static "); + rb_str_catf(errmsg, "%s ", (tname ? tname:"void")); + rb_str_catf(errmsg, "%s::%s(", qt_Smoke->classes[meth.classId].className, qt_Smoke->methodNames[meth.name]); + for(int i = 0; i < meth.numArgs; i++) { + if(i) rb_str_catf(errmsg, ", "); + tname = qt_Smoke->types[qt_Smoke->argumentList[meth.args+i]].name; + rb_str_catf(errmsg, "%s", (tname ? tname:"void")); + } + rb_str_catf(errmsg, ")"); + if(meth.flags & Smoke::mf_const) rb_str_catf(errmsg, " const"); + rb_str_catf(errmsg, "\n"); + } + } + } + return errmsg; +} + +static VALUE +isObject(VALUE /*self*/, VALUE obj) +{ + void * ptr = 0; + ptr = value_to_ptr(obj); + return (ptr > 0 ? Qtrue : Qfalse); +} + +static VALUE +setCurrentMethod(VALUE self, VALUE meth_value) +{ + int meth = NUM2INT(meth_value); + // FIXME: damn, this is lame, and it doesn't handle ambiguous methods + _current_method = meth; //qt_Smoke->methodMaps[meth].method; + return self; +} + +static VALUE +getClassList(VALUE /*self*/) +{ + VALUE class_list = rb_ary_new(); + + for(int i = 1; i <= qt_Smoke->numClasses; i++) { + rb_ary_push(class_list, rb_str_new2(qt_Smoke->classes[i].className)); + } + + return class_list; +} + +static VALUE +kde_package_to_class(const char * package, VALUE base_class) +{ + VALUE klass = Qnil; + QString packageName(package); +static QRegExp * scope_op = 0; + if (scope_op == 0) { + scope_op = new QRegExp("^([^:]+)::([^:]+)$"); + } + + if (packageName.startsWith("KDE::ConfigSkeleton::ItemEnum::")) { + klass = rb_define_class_under(kconfigskeleton_itemenum_class, package+strlen("KDE::ConfigSkeleton::EnumItem::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + kconfigskeleton_itemenum_choice_class = klass; + } else if (packageName.startsWith("KDE::ConfigSkeleton::")) { + klass = rb_define_class_under(kconfigskeleton_class, package+strlen("KDE::ConfigSkeleton::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + rb_define_method(klass, "immutable?", (VALUE (*) (...)) _kconfigskeletonitem_immutable, 0); + rb_define_method(klass, "isImmutable", (VALUE (*) (...)) _kconfigskeletonitem_immutable, 0); + } else if (packageName.startsWith("KDE::Win::")) { + klass = rb_define_class_under(kwin_class, package+strlen("KDE::Win::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (packageName.startsWith("KDE::")) { + klass = rb_define_class_under(kde_module, package+strlen("KDE::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (packageName.startsWith("KParts::")) { + klass = rb_define_class_under(kparts_module, package+strlen("KParts::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + if (packageName == "KParts::ReadOnlyPart") { + konsole_part_class = rb_define_class_under(kde_module, "KonsolePart", klass); + } + } else if (packageName.startsWith("KNS::")) { + klass = rb_define_class_under(kns_module, package+strlen("KNS::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (packageName.startsWith("KIO::")) { + klass = rb_define_class_under(kio_module, package+strlen("KIO::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + if (packageName == "KIO::UDSAtom") { + kio_udsatom_class = klass; + } + } else if (packageName.startsWith("DOM::")) { + klass = rb_define_class_under(dom_module, package+strlen("DOM::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (packageName.startsWith("Kontact::")) { + klass = rb_define_class_under(kontact_module, package+strlen("Kontact::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (packageName.startsWith("Ko") && scope_op->search(packageName) == -1) { + klass = rb_define_class_under(koffice_module, package+strlen("Ko"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (packageName.startsWith("Kate::")) { + klass = rb_define_class_under(kate_module, package+strlen("Kate::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (packageName.startsWith("Kate")) { + klass = rb_define_class_under(kate_module, package+strlen("Kate"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (packageName.startsWith("KTextEditor::")) { + klass = rb_define_class_under(ktexteditor_module, package+strlen("KTextEditor::"), base_class); + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) _new_kde, -1); + } else if (scope_op->search(packageName) != -1) { + // If an unrecognised classname of the form 'XXXXXX::YYYYYY' is found, + // then create a module XXXXXX to put the class YYYYYY under + VALUE module = rb_define_module(scope_op->cap(1).latin1()); + klass = rb_define_class_under(module, scope_op->cap(2).latin1(), base_class); + } else if ( packageName.startsWith("K") + && packageName.mid(1, 1).contains(QRegExp("[A-Z]")) == 1 ) + { + klass = rb_define_class_under(kde_module, package+strlen("K"), base_class); + } else { + packageName = packageName.mid(0, 1).upper() + packageName.mid(1); + klass = rb_define_class_under(kde_module, packageName.latin1(), base_class); + } + + return klass; +} + +static VALUE +create_qobject_class(VALUE /*self*/, VALUE package_value) +{ + const char *package = StringValuePtr(package_value); + VALUE klass; + + if (QString(package).startsWith("Qt::")) { + klass = rb_define_class_under(qt_module, package+strlen("Qt::"), qt_base_class); + if (qstrcmp(package, "Qt::Application") == 0) { + rb_define_singleton_method(klass, "new", (VALUE (*) (...)) new_qapplication, -1); + rb_define_method(klass, "ARGV", (VALUE (*) (...)) qapplication_argv, 0); + } + } else if (QString(package).startsWith("Qext::")) { + if (qext_scintilla_module == Qnil) { + qext_scintilla_module = rb_define_module("Qext"); + } + klass = rb_define_class_under(qext_scintilla_module, package+strlen("Qext::"), qt_base_class); + } else { + klass = kde_package_to_class(package, qt_base_class); + } + + rb_define_method(klass, "inspect", (VALUE (*) (...)) inspect_qobject, 0); + rb_define_method(klass, "pretty_print", (VALUE (*) (...)) pretty_print_qobject, 1); + rb_define_method(klass, "receivers", (VALUE (*) (...)) receivers_qobject, 0); + rb_define_method(klass, "className", (VALUE (*) (...)) class_name, 0); + rb_define_method(klass, "inherits", (VALUE (*) (...)) inherits_qobject, -1); + rb_define_method(klass, "connect", (VALUE (*) (...)) qobject_connect, -1); + rb_define_singleton_method(klass, "connect", (VALUE (*) (...)) qobject_connect, -1); + + return klass; +} + +static VALUE +create_qt_class(VALUE /*self*/, VALUE package_value) +{ + const char *package = StringValuePtr(package_value); + VALUE klass; + + if (QString(package).startsWith("Qt::")) { + klass = rb_define_class_under(qt_module, package+strlen("Qt::"), qt_base_class); + } else if (QString(package).startsWith("Qext::")) { + if (qext_scintilla_module == Qnil) { + qext_scintilla_module = rb_define_module("Qext"); + } + klass = rb_define_class_under(qext_scintilla_module, package+strlen("Qext::"), qt_base_class); + } else { + klass = kde_package_to_class(package, qt_base_class); + } + + if (qstrcmp(package, "Qt::MetaObject") == 0) { + qmetaobject_class = klass; + } else if (qstrcmp(package, "Qt::Variant") == 0) { + qvariant_class = klass; + rb_define_singleton_method(qvariant_class, "new", (VALUE (*) (...)) new_qvariant, -1); + } else if (qstrcmp(package, "Qt::ByteArray") == 0) { + rb_define_method(klass, "data", (VALUE (*) (...)) qbytearray_data, 0); + rb_define_method(klass, "size", (VALUE (*) (...)) qbytearray_size, 0); + rb_define_method(klass, "setRawData", (VALUE (*) (...)) qbytearray_setRawData, 1); + } else if (qstrcmp(package, "Qt::Char") == 0) { + rb_define_method(klass, "to_s", (VALUE (*) (...)) qchar_to_s, 0); + } + + return klass; +} + +static VALUE +version(VALUE /*self*/) +{ + return rb_str_new2(QT_VERSION_STR); +} + +static VALUE +qtruby_version(VALUE /*self*/) +{ + return rb_str_new2(QTRUBY_VERSION); +} + +void +set_new_kde(VALUE (*new_kde) (int, VALUE *, VALUE)) +{ + _new_kde = new_kde; + + if (qt_module == Qnil) { + qt_module = rb_define_module("Qt"); + qt_internal_module = rb_define_module_under(qt_module, "Internal"); + qt_base_class = rb_define_class_under(qt_module, "Base", rb_cObject); + } + + kde_module = rb_define_module("KDE"); + rb_define_singleton_method(kde_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(kde_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + + kparts_module = rb_define_module("KParts"); + rb_define_singleton_method(kparts_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(kparts_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + + kns_module = rb_define_module("KNS"); + rb_define_singleton_method(kns_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(kns_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + + kio_module = rb_define_module("KIO"); + rb_define_singleton_method(kio_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(kio_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + + dom_module = rb_define_module("DOM"); + rb_define_singleton_method(dom_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(dom_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + + kontact_module = rb_define_module("Kontact"); + rb_define_singleton_method(kontact_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(kontact_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + + ktexteditor_module = rb_define_module("KTextEditor"); + rb_define_singleton_method(ktexteditor_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(ktexteditor_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + + kwin_class = rb_define_class_under(kde_module, "Win", qt_base_class); + + kate_module = rb_define_module("Kate"); + rb_define_singleton_method(kate_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(kate_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + + koffice_module = rb_define_module("Ko"); + rb_define_singleton_method(koffice_module, "method_missing", (VALUE (*) (...)) kde_module_method_missing, -1); + rb_define_singleton_method(koffice_module, "const_missing", (VALUE (*) (...)) kde_module_method_missing, -1); +} + +void +set_kconfigskeletonitem_immutable(VALUE (*kconfigskeletonitem_immutable) (VALUE)) +{ + _kconfigskeletonitem_immutable = kconfigskeletonitem_immutable; + + kconfigskeleton_class = rb_define_class_under(kde_module, "ConfigSkeleton", qt_base_class); + kconfigskeleton_itemenum_class = rb_define_class_under(kconfigskeleton_class, "ItemEnum", qt_base_class); +} + +static VALUE +set_application_terminated(VALUE /*self*/, VALUE yn) +{ + application_terminated = (yn == Qtrue ? true : false); + return Qnil; +} + +void +Init_qtruby() +{ + if (qt_Smoke != 0L) { + // This function must have been called twice because both + // 'require Qt' and 'require Korundum' statements have + // been included in a ruby program + rb_fatal("require 'Qt' must not follow require 'Korundum'\n"); + return; + } + + init_qt_Smoke(); + qt_Smoke->binding = new QtRubySmokeBinding(qt_Smoke); + install_handlers(Qt_handlers); + + methcache.setAutoDelete(true); + classcache.setAutoDelete(true); + + if (qt_module == Qnil) { + qt_module = rb_define_module("Qt"); + qt_internal_module = rb_define_module_under(qt_module, "Internal"); + qt_base_class = rb_define_class_under(qt_module, "Base", rb_cObject); + } + + rb_define_singleton_method(qt_base_class, "new", (VALUE (*) (...)) new_qt, -1); + rb_define_method(qt_base_class, "initialize", (VALUE (*) (...)) initialize_qt, -1); + rb_define_singleton_method(qt_base_class, "method_missing", (VALUE (*) (...)) class_method_missing, -1); + rb_define_singleton_method(qt_module, "method_missing", (VALUE (*) (...)) module_method_missing, -1); + rb_define_method(qt_base_class, "method_missing", (VALUE (*) (...)) method_missing, -1); + + rb_define_singleton_method(qt_base_class, "const_missing", (VALUE (*) (...)) class_method_missing, -1); + rb_define_singleton_method(qt_module, "const_missing", (VALUE (*) (...)) module_method_missing, -1); + rb_define_method(qt_base_class, "const_missing", (VALUE (*) (...)) method_missing, -1); + + rb_define_method(qt_base_class, "dispose", (VALUE (*) (...)) dispose, 0); + rb_define_method(qt_base_class, "isDisposed", (VALUE (*) (...)) is_disposed, 0); + rb_define_method(qt_base_class, "disposed?", (VALUE (*) (...)) is_disposed, 0); + + rb_define_method(rb_cObject, "qDebug", (VALUE (*) (...)) qdebug, 1); + rb_define_method(rb_cObject, "qFatal", (VALUE (*) (...)) qfatal, 1); + rb_define_method(rb_cObject, "qWarning", (VALUE (*) (...)) qwarning, 1); + + rb_define_module_function(qt_internal_module, "getMethStat", (VALUE (*) (...)) getMethStat, 0); + rb_define_module_function(qt_internal_module, "getClassStat", (VALUE (*) (...)) getClassStat, 0); + rb_define_module_function(qt_internal_module, "getIsa", (VALUE (*) (...)) getIsa, 1); + rb_define_module_function(qt_internal_module, "allocateMocArguments", (VALUE (*) (...)) allocateMocArguments, 1); + rb_define_module_function(qt_internal_module, "setMocType", (VALUE (*) (...)) setMocType, 4); + rb_define_module_function(qt_internal_module, "setDebug", (VALUE (*) (...)) setDebug, 1); + rb_define_module_function(qt_internal_module, "debug", (VALUE (*) (...)) debugging, 0); + rb_define_module_function(qt_internal_module, "getTypeNameOfArg", (VALUE (*) (...)) getTypeNameOfArg, 2); + rb_define_module_function(qt_internal_module, "classIsa", (VALUE (*) (...)) classIsa, 2); + rb_define_module_function(qt_internal_module, "isEnum", (VALUE (*) (...)) isEnum, 1); + rb_define_module_function(qt_internal_module, "insert_pclassid", (VALUE (*) (...)) insert_pclassid, 2); + rb_define_module_function(qt_internal_module, "find_pclassid", (VALUE (*) (...)) find_pclassid, 1); + rb_define_module_function(qt_internal_module, "insert_mcid", (VALUE (*) (...)) insert_mcid, 2); + rb_define_module_function(qt_internal_module, "find_mcid", (VALUE (*) (...)) find_mcid, 1); + rb_define_module_function(qt_internal_module, "getVALUEtype", (VALUE (*) (...)) getVALUEtype, 1); + rb_define_module_function(qt_internal_module, "make_QUParameter", (VALUE (*) (...)) make_QUParameter, 4); + rb_define_module_function(qt_internal_module, "make_QMetaData", (VALUE (*) (...)) make_QMetaData, 2); + rb_define_module_function(qt_internal_module, "make_QUMethod", (VALUE (*) (...)) make_QUMethod, 2); + rb_define_module_function(qt_internal_module, "make_QMetaData_tbl", (VALUE (*) (...)) make_QMetaData_tbl, 1); + rb_define_module_function(qt_internal_module, "make_metaObject", (VALUE (*) (...)) make_metaObject, 6); + rb_define_module_function(qt_internal_module, "addMetaObjectMethods", (VALUE (*) (...)) add_metaobject_methods, 1); + rb_define_module_function(qt_internal_module, "addSignalMethods", (VALUE (*) (...)) add_signal_methods, 2); + rb_define_module_function(qt_internal_module, "mapObject", (VALUE (*) (...)) mapObject, 1); + // isQOjbect => isaQObject + rb_define_module_function(qt_internal_module, "isQObject", (VALUE (*) (...)) isaQObject, 1); + rb_define_module_function(qt_internal_module, "idInstance", (VALUE (*) (...)) idInstance, 1); + rb_define_module_function(qt_internal_module, "idClass", (VALUE (*) (...)) idClass, 1); + rb_define_module_function(qt_internal_module, "idMethodName", (VALUE (*) (...)) idMethodName, 1); + rb_define_module_function(qt_internal_module, "idMethod", (VALUE (*) (...)) idMethod, 2); + rb_define_module_function(qt_internal_module, "findMethod", (VALUE (*) (...)) findMethod, 2); + rb_define_module_function(qt_internal_module, "findAllMethods", (VALUE (*) (...)) findAllMethods, -1); + rb_define_module_function(qt_internal_module, "findAllMethodNames", (VALUE (*) (...)) findAllMethodNames, 3); + rb_define_module_function(qt_internal_module, "dumpCandidates", (VALUE (*) (...)) dumpCandidates, 1); + rb_define_module_function(qt_internal_module, "isObject", (VALUE (*) (...)) isObject, 1); + rb_define_module_function(qt_internal_module, "setCurrentMethod", (VALUE (*) (...)) setCurrentMethod, 1); + rb_define_module_function(qt_internal_module, "getClassList", (VALUE (*) (...)) getClassList, 0); + rb_define_module_function(qt_internal_module, "create_qt_class", (VALUE (*) (...)) create_qt_class, 1); + rb_define_module_function(qt_internal_module, "create_qobject_class", (VALUE (*) (...)) create_qobject_class, 1); + rb_define_module_function(qt_internal_module, "cast_object_to", (VALUE (*) (...)) cast_object_to, 2); + rb_define_module_function(qt_internal_module, "application_terminated=", (VALUE (*) (...)) set_application_terminated, 1); + + rb_define_module_function(qt_module, "version", (VALUE (*) (...)) version, 0); + rb_define_module_function(qt_module, "qtruby_version", (VALUE (*) (...)) qtruby_version, 0); + + rb_require("Qt/qtruby.rb"); + + // Do package initialization + rb_funcall(qt_internal_module, rb_intern("init_all_classes"), 0); +} + +}; diff --git a/qtruby/rubylib/qtruby/configure.in.in b/qtruby/rubylib/qtruby/configure.in.in new file mode 100644 index 00000000..41654f5e --- /dev/null +++ b/qtruby/rubylib/qtruby/configure.in.in @@ -0,0 +1,19 @@ +AC_CHECK_PROG(RUBY, ruby, ruby) + +if test -z "$RUBY"; then + DO_NOT_COMPILE="$DO_NOT_COMPILE qtruby" +else + AC_MSG_CHECKING(for ruby dirs) + RUBY_ARCHDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"archdir"@:>@)'` + RUBY_SITEARCHDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"sitearchdir"@:>@)'` + RUBY_SITEDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"sitelibdir"@:>@)'` + RUBY_LIBDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"libdir"@:>@)'` + RUBY_LIBRUBYARG=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"LIBRUBYARG_SHARED"@:>@)'` + AC_MSG_RESULT([archdir $RUBY_ARCHDIR, sitearchdir $RUBY_SITEARCHDIR, sitedir $RUBY_SITEDIR, libdir $RUBY_LIBDIR, librubyarg $RUBY_LIBRUBYARG]) + AC_SUBST(RUBY_ARCHDIR) + AC_SUBST(RUBY_SITEARCHDIR) + AC_SUBST(RUBY_SITEDIR) + AC_SUBST(RUBY_LIBDIR) + AC_SUBST(RUBY_LIBRUBYARG) +fi + diff --git a/qtruby/rubylib/qtruby/extconf.rb b/qtruby/rubylib/qtruby/extconf.rb new file mode 100644 index 00000000..0ffd2382 --- /dev/null +++ b/qtruby/rubylib/qtruby/extconf.rb @@ -0,0 +1,5 @@ +require 'mkmf' +dir_config('smoke') +dir_config('qt') +$LOCAL_LIBS += '-lsmokeqt -lqt-mt -lstdc++' +create_makefile("qtruby") diff --git a/qtruby/rubylib/qtruby/handlers.cpp b/qtruby/rubylib/qtruby/handlers.cpp new file mode 100644 index 00000000..6bddf50c --- /dev/null +++ b/qtruby/rubylib/qtruby/handlers.cpp @@ -0,0 +1,1978 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the 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 <qstring.h> +#include <qregexp.h> +#include <qapplication.h> +#include <qcanvas.h> +#include <qlistview.h> +#include <qiconview.h> +#include <qtable.h> +#include <qpopupmenu.h> +#include <qlayout.h> +#include <qmetaobject.h> +#include <qvaluelist.h> +#include <qobjectlist.h> +#include <qtextcodec.h> +#include <qhostaddress.h> +#include <qpair.h> + +#include <private/qucomextra_p.h> + +#include "smoke.h" + +#undef DEBUG +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#ifndef __USE_POSIX +#define __USE_POSIX +#endif +#ifndef __USE_XOPEN +#define __USE_XOPEN +#endif +#include <ruby.h> + +#include "marshall.h" +#include "qtruby.h" +#include "smokeruby.h" + +#ifndef HINT_BYTES +#define HINT_BYTES HINT_BYTE +#endif + +extern "C" { +extern VALUE set_obj_info(const char * className, smokeruby_object * o); +extern VALUE qt_internal_module; +extern VALUE qvariant_class; +extern bool application_terminated; +}; + +extern bool isDerivedFromByName(Smoke *smoke, const char *className, const char *baseClassName); +extern void mapPointer(VALUE obj, smokeruby_object *o, Smoke::Index classId, void *lastptr); + +static const char * (*_kde_resolve_classname)(Smoke*, int, void*) = 0; + +extern "C" { + +void +set_kde_resolve_classname(const char * (*kde_resolve_classname) (Smoke*, int, void *)) +{ + _kde_resolve_classname = kde_resolve_classname; +} + +}; + +void +mark_qobject_children(QObject * qobject) +{ + VALUE obj; + + const QObjectList *l = qobject->children(); + if (l == 0) { + return; + } + QObjectListIt it( *l ); // iterate over the children + QObject *child; + + while ( (child = it.current()) != 0 ) { + ++it; + obj = getPointerObject(child); + if (obj != Qnil) { + if(do_debug & qtdb_gc) qWarning("Marking (%s*)%p -> %p\n", child->className(), child, (void*)obj); + rb_gc_mark(obj); + } + + mark_qobject_children(child); + } +} + +void +smokeruby_mark(void * p) +{ + VALUE obj; + smokeruby_object * o = (smokeruby_object *) p; + const char *className = o->smoke->classes[o->classId].className; + + if(do_debug & qtdb_gc) qWarning("Checking for mark (%s*)%p\n", className, o->ptr); + + if(o->ptr && o->allocated) { + if (isDerivedFromByName(o->smoke, className, "QListView")) { + QListView * listview = (QListView *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QListView")); + QListViewItemIterator it(listview); + QListViewItem *item; + + while ( (item = it.current()) != 0 ) { + ++it; + obj = getPointerObject(item); + if (obj != Qnil) { + if(do_debug & qtdb_gc) qWarning("Marking (%s*)%p -> %p\n", className, item, (void*)obj); + rb_gc_mark(obj); + } + } + return; + } + + if (isDerivedFromByName(o->smoke, className, "QTable")) { + QTable * table = (QTable *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QTable")); + QTableItem *item; + + for ( int row = 0; row < table->numRows(); row++ ) { + for ( int col = 0; col < table->numCols(); col++ ) { + item = table->item(row, col); + obj = getPointerObject(item); + if (obj != Qnil) { + if(do_debug & qtdb_gc) qWarning("Marking (%s*)%p -> %p\n", className, item, (void*)obj); + rb_gc_mark(obj); + } + } + } + return; + } + + if (isDerivedFromByName(o->smoke, className, "QCanvas")) { + QCanvas * canvas = (QCanvas *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QCanvas")); + QCanvasItemList list = canvas->allItems(); + for ( QCanvasItemList::iterator it = list.begin(); it != list.end(); ++it ) { + obj = getPointerObject(*it); + if (obj != Qnil) { + if(do_debug & qtdb_gc) qWarning("Marking (%s*)%p -> %p\n", className, *it, (void*)obj); + rb_gc_mark(obj); + } + } + return; + } + + if (isDerivedFromByName(o->smoke, className, "QCanvasItem")) { + QCanvasItem * item = (QCanvasItem *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QCanvasItem")); + QCanvas * canvas = item->canvas(); + obj = getPointerObject(canvas); + if (obj != Qnil) { + if(do_debug & qtdb_gc) qWarning("Marking (%s*)%p -> %p\n", "QCanvas", canvas, (void*)obj); + rb_gc_mark(obj); + } + return; + } + + if (isDerivedFromByName(o->smoke, className, "QObject")) { + QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject")); + mark_qobject_children(qobject); + return; + } + } +} + +void +smokeruby_free(void * p) +{ + smokeruby_object *o = (smokeruby_object*)p; + const char *className = o->smoke->classes[o->classId].className; + + if(do_debug & qtdb_gc) qWarning("Checking for delete (%s*)%p allocated: %s\n", className, o->ptr, o->allocated ? "true" : "false"); + + if(application_terminated || !o->allocated || o->ptr == 0) { + free(o); + return; + } + + unmapPointer(o, o->classId, 0); + object_count --; + + if ( qstrcmp(className, "QObject") == 0 + || qstrcmp(className, "QListBoxItem") == 0 + || qstrcmp(className, "QStyleSheetItem") == 0 + || qstrcmp(className, "KCommand") == 0 + || qstrcmp(className, "KNamedCommand") == 0 + || qstrcmp(className, "KMacroCommand") == 0 + || qstrcmp(className, "KAboutData") == 0 + || qstrcmp(className, "KCmdLineArgs") == 0 + || qstrcmp(className, "QSqlCursor") == 0 ) + { + // Don't delete instances of these classes for now + free(o); + return; + } else if (isDerivedFromByName(o->smoke, className, "QLayoutItem")) { + QLayoutItem * item = (QLayoutItem *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QLayoutItem")); + if (item->layout() != 0 || item->widget() != 0 || item->spacerItem() != 0) { + free(o); + return; + } + } else if (qstrcmp(className, "QIconViewItem") == 0) { + QIconViewItem * item = (QIconViewItem *) o->ptr; + if (item->iconView() != 0) { + free(o); + return; + } + } else if (qstrcmp(className, "QCheckListItem") == 0) { + QCheckListItem * item = (QCheckListItem *) o->ptr; + if (item->parent() != 0 || item->listView() != 0) { + free(o); + return; + } + } else if (qstrcmp(className, "QListViewItem") == 0) { + QListViewItem * item = (QListViewItem *) o->ptr; + if (item->parent() != 0 || item->listView() != 0) { + free(o); + return; + } + } else if (isDerivedFromByName(o->smoke, className, "QTableItem")) { + QTableItem * item = (QTableItem *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QTableItem")); + if (item->table() != 0) { + free(o); + return; + } + } else if (qstrcmp(className, "QPopupMenu") == 0) { + QPopupMenu * item = (QPopupMenu *) o->ptr; + if (item->parentWidget(false) != 0) { + free(o); + return; + } + } else if (isDerivedFromByName(o->smoke, className, "QWidget")) { + QWidget * qwidget = (QWidget *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QWidget")); + if (qwidget->parentWidget(true) != 0) { + free(o); + return; + } + } else if (isDerivedFromByName(o->smoke, className, "QObject")) { + QObject * qobject = (QObject *) o->smoke->cast(o->ptr, o->classId, o->smoke->idClass("QObject")); + if (qobject->parent() != 0) { + free(o); + return; + } + } + + if(do_debug & qtdb_gc) qWarning("Deleting (%s*)%p\n", className, o->ptr); + + char *methodName = new char[strlen(className) + 2]; + methodName[0] = '~'; + strcpy(methodName + 1, className); + Smoke::Index nameId = o->smoke->idMethodName(methodName); + Smoke::Index meth = o->smoke->findMethod(o->classId, nameId); + if(meth > 0) { + Smoke::Method &m = o->smoke->methods[o->smoke->methodMaps[meth].method]; + Smoke::ClassFn fn = o->smoke->classes[m.classId].classFn; + Smoke::StackItem i[1]; + (*fn)(m.method, o->ptr, i); + } + delete[] methodName; + free(o); + + return; +} + +/* + * Given an approximate classname and a qt instance, try to improve the resolution of the name + * by using the various Qt rtti mechanisms for QObjects, QEvents and QCanvasItems + */ +static const char * +resolve_classname(Smoke* smoke, int classId, void * ptr) +{ + if (isDerivedFromByName(smoke, smoke->classes[classId].className, "QEvent")) { + QEvent * qevent = (QEvent *) smoke->cast(ptr, classId, smoke->idClass("QEvent")); + switch (qevent->type()) { + case QEvent::ChildInserted: + case QEvent::ChildRemoved: + return "Qt::ChildEvent"; + case QEvent::Close: + return "Qt::CloseEvent"; + case QEvent::ContextMenu: + return "Qt::ContextMenuEvent"; +// case QEvent::User: +// return "Qt::CustomEvent"; + case QEvent::DragEnter: + return "Qt::DragEnterEvent"; + case QEvent::DragLeave: + return "Qt::DragLeaveEvent"; + case QEvent::DragMove: + return "Qt::DragMoveEvent"; + case QEvent::DragResponse: + return "Qt::DragResponseEvent"; + case QEvent::Drop: + return "Qt::DropEvent"; + case QEvent::FocusIn: + case QEvent::FocusOut: + return "Qt::FocusEvent"; + case QEvent::Hide: + return "Qt::HideEvent"; + case QEvent::KeyPress: + case QEvent::KeyRelease: + return "Qt::KeyEvent"; + case QEvent::IMStart: + case QEvent::IMCompose: + case QEvent::IMEnd: + return "Qt::IMEvent"; + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + return "Qt::MouseEvent"; + case QEvent::Move: + return "Qt::MoveEvent"; + case QEvent::Paint: + return "Qt::PaintEvent"; + case QEvent::Resize: + return "Qt::ResizeEvent"; + case QEvent::Show: + return "Qt::ShowEvent"; + // case QEvent::Tablet: + // return "Qt::TabletEvent"; + case QEvent::Timer: + return "Qt::TimerEvent"; + case QEvent::Wheel: + return "Qt::WheelEvent"; + default: + break; + } + } else if (isDerivedFromByName(smoke, smoke->classes[classId].className, "QObject")) { + QObject * qobject = (QObject *) smoke->cast(ptr, classId, smoke->idClass("QObject")); + QMetaObject * meta = qobject->metaObject(); + + while (meta != 0) { + Smoke::Index classId = smoke->idClass(meta->className()); + if (classId != 0) { + return smoke->binding->className(classId); + } + + meta = meta->superClass(); + } + } else if (isDerivedFromByName(smoke, smoke->classes[classId].className, "QCanvasItem")) { + QCanvasItem * qcanvasitem = (QCanvasItem *) smoke->cast(ptr, classId, smoke->idClass("QCanvasItem")); + switch (qcanvasitem->rtti()) { + case QCanvasItem::Rtti_Sprite: + return "Qt::CanvasSprite"; + case QCanvasItem::Rtti_PolygonalItem: + return "Qt::CanvasPolygonalItem"; + case QCanvasItem::Rtti_Text: + return "Qt::CanvasText"; + case QCanvasItem::Rtti_Polygon: + return "Qt::CanvasPolygon"; + case QCanvasItem::Rtti_Rectangle: + return "Qt::CanvasRectangle"; + case QCanvasItem::Rtti_Ellipse: + return "Qt::CanvasEllipse"; + case QCanvasItem::Rtti_Line: + return "Qt::CanvasLine"; + case QCanvasItem::Rtti_Spline: + return "Qt::CanvasSpline"; + default: + break; + } + } else if (isDerivedFromByName(smoke, smoke->classes[classId].className, "QListViewItem")) { + QListViewItem * item = (QListViewItem *) smoke->cast(ptr, classId, smoke->idClass("QListViewItem")); + switch (item->rtti()) { + case 0: + return "Qt::ListViewItem"; + case 1: + return "Qt::CheckListItem"; + default: + return "Qt::ListViewItem"; + break; + } + } else if (isDerivedFromByName(smoke, smoke->classes[classId].className, "QTableItem")) { + QTableItem * item = (QTableItem *) smoke->cast(ptr, classId, smoke->idClass("QTableItem")); + switch (item->rtti()) { + case 0: + return "Qt::TableItem"; + case 1: + return "Qt::ComboTableItem"; + case 2: + return "Qt::CheckTableItem"; + default: + return "Qt::TableItem"; + break; + } + } + + if (_kde_resolve_classname != 0) { + return (*_kde_resolve_classname)(smoke, classId, ptr); + } + + return smoke->binding->className(classId); +} + +bool +matches_arg(Smoke *smoke, Smoke::Index meth, Smoke::Index argidx, const char *argtype) +{ + Smoke::Index *arg = smoke->argumentList + smoke->methods[meth].args + argidx; + SmokeType type = SmokeType(smoke, *arg); + return type.name() && qstrcmp(type.name(), argtype) == 0; +} + +void * +construct_copy(smokeruby_object *o) +{ + const char *className = o->smoke->className(o->classId); + int classNameLen = strlen(className); + char *ccSig = new char[classNameLen + 2]; // copy constructor signature + strcpy(ccSig, className); + strcat(ccSig, "#"); + Smoke::Index ccId = o->smoke->idMethodName(ccSig); + delete[] ccSig; + + char *ccArg = new char[classNameLen + 8]; + sprintf(ccArg, "const %s&", className); + + Smoke::Index ccMeth = o->smoke->findMethod(o->classId, ccId); + + if(!ccMeth) { + delete[] ccArg; + return 0; + } + Smoke::Index method = o->smoke->methodMaps[ccMeth].method; + if(method > 0) { + // Make sure it's a copy constructor + if(!matches_arg(o->smoke, method, 0, ccArg)) { + delete[] ccArg; + return 0; + } + delete[] ccArg; + ccMeth = method; + } else { + // ambiguous method, pick the copy constructor + Smoke::Index i = -method; + while(o->smoke->ambiguousMethodList[i]) { + if(matches_arg(o->smoke, o->smoke->ambiguousMethodList[i], 0, ccArg)) + break; + i++; + } + delete[] ccArg; + ccMeth = o->smoke->ambiguousMethodList[i]; + if(!ccMeth) + return 0; + } + + // Okay, ccMeth is the copy constructor. Time to call it. + Smoke::StackItem args[2]; + args[0].s_voidp = 0; + args[1].s_voidp = o->ptr; + Smoke::ClassFn fn = o->smoke->classes[o->classId].classFn; + (*fn)(o->smoke->methods[ccMeth].method, 0, args); + return args[0].s_voidp; +} + +void +marshall_basetype(Marshall *m) +{ + switch(m->type().elem()) { + case Smoke::t_bool: + switch(m->action()) { + case Marshall::FromVALUE: + if (TYPE(*(m->var())) == T_OBJECT) { + // A Qt::Boolean has been passed as a value + VALUE temp = rb_funcall(qt_internal_module, rb_intern("get_qboolean"), 1, *(m->var())); + m->item().s_bool = (temp == Qtrue ? true : false); + } else { + m->item().s_bool = (*(m->var()) == Qtrue ? true : false); + } + break; + case Marshall::ToVALUE: + *(m->var()) = m->item().s_bool ? Qtrue : Qfalse; + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_char: + switch(m->action()) { + case Marshall::FromVALUE: + m->item().s_char = NUM2CHR(*(m->var())); + break; + case Marshall::ToVALUE: + *(m->var()) = CHR2FIX(m->item().s_char); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_uchar: + switch(m->action()) { + case Marshall::FromVALUE: + m->item().s_uchar = NUM2CHR(*(m->var())); + break; + case Marshall::ToVALUE: + *(m->var()) = CHR2FIX(m->item().s_uchar); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_short: + switch(m->action()) { + case Marshall::FromVALUE: + m->item().s_short = (short) NUM2INT(*(m->var())); + break; + case Marshall::ToVALUE: + *(m->var()) = INT2NUM(m->item().s_short); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_ushort: + switch(m->action()) { + case Marshall::FromVALUE: + m->item().s_ushort = (unsigned short) NUM2UINT(*(m->var())); + break; + case Marshall::ToVALUE: + *(m->var()) = UINT2NUM(m->item().s_ushort); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_int: + switch(m->action()) { + case Marshall::FromVALUE: + if (TYPE(*(m->var())) == T_OBJECT) { + m->item().s_int = (int) NUM2INT(rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, *(m->var()))); + } else { + m->item().s_int = (int) NUM2INT(*(m->var())); + } + break; + case Marshall::ToVALUE: + *(m->var()) = INT2NUM(m->item().s_int); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_uint: + switch(m->action()) { + case Marshall::FromVALUE: + if (TYPE(*(m->var())) == T_OBJECT) { + m->item().s_int = (unsigned int) NUM2UINT(rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, *(m->var()))); + } else { + m->item().s_uint = (unsigned int) NUM2UINT(*(m->var())); + } + break; + case Marshall::ToVALUE: + *(m->var()) = UINT2NUM(m->item().s_uint); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_long: + switch(m->action()) { + case Marshall::FromVALUE: + if (TYPE(*(m->var())) == T_OBJECT) { + m->item().s_int = (long) NUM2LONG(rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, *(m->var()))); + } else { + m->item().s_long = (long) NUM2LONG(*(m->var())); + } + break; + case Marshall::ToVALUE: + *(m->var()) = INT2NUM(m->item().s_long); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_ulong: + switch(m->action()) { + case Marshall::FromVALUE: + if (TYPE(*(m->var())) == T_OBJECT) { + m->item().s_int = (unsigned long) NUM2ULONG(rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, *(m->var()))); + } else { + m->item().s_ulong = (unsigned long) NUM2ULONG(*(m->var())); + } + break; + case Marshall::ToVALUE: + *(m->var()) = INT2NUM(m->item().s_ulong); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_float: + switch(m->action()) { + case Marshall::FromVALUE: + m->item().s_float = (float) NUM2DBL(*(m->var())); + break; + case Marshall::ToVALUE: + *(m->var()) = rb_float_new((double) m->item().s_float); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_double: + switch(m->action()) { + case Marshall::FromVALUE: + m->item().s_double = (double) NUM2DBL(*(m->var())); + break; + case Marshall::ToVALUE: + *(m->var()) = rb_float_new(m->item().s_double); + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_enum: + switch(m->action()) { + case Marshall::FromVALUE: + { + if (TYPE(*(m->var())) == T_OBJECT) { + // A Qt::Enum is a subclass of Qt::Integer, so 'get_qinteger()' can be called ok + VALUE temp = rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, *(m->var())); + m->item().s_enum = (long) NUM2LONG(temp); + } else { + m->item().s_enum = (long) NUM2LONG(*(m->var())); + } + } + break; + case Marshall::ToVALUE: + *(m->var()) = rb_funcall( qt_internal_module, + rb_intern("create_qenum"), + 2, INT2NUM(m->item().s_enum), rb_str_new2(m->type().name()) ); + + break; + default: + m->unsupported(); + break; + } + break; + case Smoke::t_class: + switch(m->action()) { + case Marshall::FromVALUE: + { + if(*(m->var()) == Qnil) { + m->item().s_class = 0; + break; + } + if(TYPE(*(m->var())) != T_DATA) { + rb_raise(rb_eArgError, "Invalid type, expecting %s\n", m->type().name()); + break; + } + + smokeruby_object *o = value_obj_info(*(m->var())); + if(!o || !o->ptr) { + if(m->type().isRef()) { + rb_warning("References can't be nil\n"); + m->unsupported(); + } + m->item().s_class = 0; + break; + } + void *ptr = o->ptr; + if(!m->cleanup() && m->type().isStack()) { + ptr = construct_copy(o); + } + const Smoke::Class &c = m->smoke()->classes[m->type().classId()]; + ptr = o->smoke->cast( + ptr, // pointer + o->classId, // from + o->smoke->idClass(c.className) // to + ); + m->item().s_class = ptr; + break; + } + break; + case Marshall::ToVALUE: + { + if(m->item().s_voidp == 0) { + *(m->var()) = Qnil; + break; + } + + void *p = m->item().s_voidp; + VALUE obj = getPointerObject(p); + if(obj != Qnil) { + *(m->var()) = obj; + break; + } + + smokeruby_object * o = (smokeruby_object *) malloc(sizeof(smokeruby_object)); + o->smoke = m->smoke(); + o->classId = m->type().classId(); + o->ptr = p; + o->allocated = false; + + const char * classname = resolve_classname(o->smoke, o->classId, o->ptr); + + if(m->type().isConst() && m->type().isRef()) { + p = construct_copy( o ); + if(p) { + o->ptr = p; + o->allocated = true; + } + } + + obj = set_obj_info(classname, o); + if (do_debug & qtdb_calls) { + qWarning("allocating %s %p -> %p\n", classname, o->ptr, (void*)obj); + } + + if(m->type().isStack()) { + o->allocated = true; + // Keep a mapping of the pointer so that it is only wrapped once as a ruby VALUE + mapPointer(obj, o, o->classId, 0); + } + + *(m->var()) = obj; + } + break; + default: + m->unsupported(); + break; + } + break; + default: + m->unsupported(); + break; + } +} + +static void marshall_void(Marshall * /*m*/) {} +static void marshall_unknown(Marshall *m) { + m->unsupported(); +} + +static void marshall_charP(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE rv = *(m->var()); + if (rv == Qnil) { + m->item().s_voidp = 0; + break; + } + + int len = RSTRING(rv)->len; + char* mem = (char*) malloc(len+1); + memcpy(mem, StringValuePtr(rv), len); + mem[len] ='\0'; + m->item().s_voidp = mem; + } + break; + case Marshall::ToVALUE: + { + char *p = (char*)m->item().s_voidp; + if(p) + *(m->var()) = rb_str_new2(p); + else + *(m->var()) = Qnil; + if(m->cleanup()) + delete[] p; + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_ucharP(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE rv = *(m->var()); + if (rv == Qnil) { + m->item().s_voidp = 0; + break; + } + int len = RSTRING(rv)->len; + char* mem = (char*) malloc(len+1); + memcpy(mem, StringValuePtr(rv), len); + mem[len] ='\0'; + m->item().s_voidp = mem; + } + break; + case Marshall::ToVALUE: + default: + m->unsupported(); + break; + } +} + +static const char * KCODE = 0; +static QTextCodec *codec = 0; + +static void +init_codec() { + VALUE temp = rb_gv_get("$KCODE"); + KCODE = StringValuePtr(temp); + if (qstrcmp(KCODE, "EUC") == 0) { + codec = QTextCodec::codecForName("eucJP"); + } else if (qstrcmp(KCODE, "SJIS") == 0) { + codec = QTextCodec::codecForName("Shift-JIS"); + } +} + +QString* +qstringFromRString(VALUE rstring) { + if (KCODE == 0) { + init_codec(); + } + + QString * s; + if (qstrcmp(KCODE, "UTF8") == 0) + s = new QString(QString::fromUtf8(StringValuePtr(rstring), RSTRING(rstring)->len)); + else if (qstrcmp(KCODE, "EUC") == 0) + s = new QString(codec->toUnicode(StringValuePtr(rstring))); + else if (qstrcmp(KCODE, "SJIS") == 0) + s = new QString(codec->toUnicode(StringValuePtr(rstring))); + else if(qstrcmp(KCODE, "NONE") == 0) + s = new QString(QString::fromLatin1(StringValuePtr(rstring))); + else + s = new QString(QString::fromLocal8Bit(StringValuePtr(rstring), RSTRING(rstring)->len)); + return s; +} + +VALUE +rstringFromQString(QString * s) { + if (KCODE == 0) { + init_codec(); + } + + if (qstrcmp(KCODE, "UTF8") == 0) + return rb_str_new2(s->utf8()); + else if (qstrcmp(KCODE, "EUC") == 0) + return rb_str_new2(codec->fromUnicode(*s)); + else if (qstrcmp(KCODE, "SJIS") == 0) + return rb_str_new2(codec->fromUnicode(*s)); + else if (qstrcmp(KCODE, "NONE") == 0) + return rb_str_new2(s->latin1()); + else + return rb_str_new2(s->local8Bit()); +} + +static void marshall_QString(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + QString* s = 0; + if ( *(m->var()) != Qnil) { + s = qstringFromRString(*(m->var())); + } else { + s = new QString(QString::null); + } + + m->item().s_voidp = s; + m->next(); + + if (!m->type().isConst() && *(m->var()) != Qnil && s != 0 && !s->isNull()) { + rb_str_resize(*(m->var()), 0); + VALUE temp = rstringFromQString(s); + rb_str_cat2(*(m->var()), StringValuePtr(temp)); + } + + if (s != 0 && m->cleanup()) { + delete s; + } + } + break; + case Marshall::ToVALUE: + { + QString *s = (QString*)m->item().s_voidp; + if (s != 0) { + if (s->isNull()) { + *(m->var()) = Qnil; + } else { + *(m->var()) = rstringFromQString(s); + } + if (m->cleanup() || m->type().isStack()) { + delete s; + } + } else { + *(m->var()) = Qnil; + } + } + break; + default: + m->unsupported(); + break; + } +} + +// The only way to convert a QChar to a QString is to +// pass a QChar to a QString constructor. However, +// QStrings aren't in the QtRuby api, so add this +// convenience method 'Qt::Char.to_s' to get a ruby +// string from a Qt::Char. +VALUE +qchar_to_s(VALUE self) +{ + smokeruby_object *o = value_obj_info(self); + if (o == 0 || o->ptr == 0) { + return Qnil; + } + + QChar * qchar = (QChar*) o->ptr; + QString s(*qchar); + return rstringFromQString(&s); +} + +#if 0 +static const char *not_ascii(const char *s, uint &len) +{ + bool r = false; + for(; *s ; s++, len--) + if((uint)*s > 0x7F) + { + r = true; + break; + } + return r ? s : 0L; +} +#endif + +static void marshall_QCString(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + QCString *s = 0; + VALUE rv = *(m->var()); + if (rv == Qnil) { + s = new QCString(); + } else { + // Add 1 to the ruby string length to allow for a QCString '\0' terminator + s = new QCString(StringValuePtr(*(m->var())), RSTRING(*(m->var()))->len + 1); + } + m->item().s_voidp = s; + + m->next(); + + if (!m->type().isConst() && rv != Qnil && s != 0) { + rb_str_resize(rv, 0); + rb_str_cat2(rv, (const char *)*s); + } + if(s && m->cleanup()) + delete s; + } + break; + case Marshall::ToVALUE: + { + QCString *s = (QCString*)m->item().s_voidp; + if(s && (const char *) *s != 0) { + *(m->var()) = rb_str_new2((const char *)*s); +// const char * p = (const char *)*s; +// uint len = s->length(); +// if(not_ascii(p,len)) +// { +// #if PERL_VERSION == 6 && PERL_SUBVERSION == 0 +// QTextCodec* c = QTextCodec::codecForMib(106); // utf8 +// if(c->heuristicContentMatch(p,len) >= 0) +// #else +// if(is_utf8_string((U8 *)p,len)) +// #endif +// SvUTF8_on(*(m->var())); +// } + } else { + if (m->type().isConst()) { + *(m->var()) = Qnil; + } else { + *(m->var()) = rb_str_new2(""); + } + } + m->next(); + + if (!m->type().isConst() && s != 0) { + *s = (const char *) StringValuePtr(*(m->var())); + } + + if(s && m->cleanup()) + delete s; + } + break; + default: + m->unsupported(); + break; + } +} + +static void marshall_QCOORD_array(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE av = *(m->var()); + if (TYPE(av) != T_ARRAY) { + m->item().s_voidp = 0; + break; + } + int count = RARRAY(av)->len; + QCOORD *coord = new QCOORD[count + 2]; + for(long i = 0; i < count; i++) { + VALUE svp = rb_ary_entry(av, i); + coord[i] = NUM2INT(svp); + } + m->item().s_voidp = coord; + m->next(); + } + break; + default: + m->unsupported(); + } +} + +static void marshall_longlong(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + m->item().s_voidp = new long long; + *(long long *)m->item().s_voidp = rb_num2ll(*(m->var())); + + m->next(); + + if(m->cleanup() && m->type().isConst()) { + delete (long long *) m->item().s_voidp; + } + } + break; + case Marshall::ToVALUE: + { + *(m->var()) = rb_ll2inum(*(long long *) m->item().s_voidp); + } + break; + default: + m->unsupported(); + break; + } +} + +static void marshall_ulonglong(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + m->item().s_voidp = new unsigned long long; + *(long long *)m->item().s_voidp = rb_num2ull(*(m->var())); + + m->next(); + + if(m->cleanup() && m->type().isConst()) { + delete (unsigned long long *) m->item().s_voidp; + } + } + break; + case Marshall::ToVALUE: + { + *(m->var()) = rb_ull2inum(*(unsigned long long *) m->item().s_voidp); + } + break; + default: + m->unsupported(); + break; + } +} + +static void marshall_intR(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE rv = *(m->var()); + int * i = new int; + if (TYPE(rv) == T_OBJECT) { + // A Qt::Integer has been passed as an integer value + VALUE temp = rb_funcall(qt_internal_module, rb_intern("get_qinteger"), 1, rv); + *i = NUM2INT(temp); + m->item().s_voidp = i; + m->next(); + rb_funcall(qt_internal_module, rb_intern("set_qinteger"), 2, rv, INT2NUM(*i)); + rv = temp; + } else { + *i = NUM2INT(rv); + m->item().s_voidp = i; + m->next(); + } + if(m->cleanup() && m->type().isConst()) { + delete i; + } else { + m->item().s_voidp = new int((int)NUM2INT(rv)); + } + } + break; + case Marshall::ToVALUE: + { + int *ip = (int*)m->item().s_voidp; + VALUE rv = *(m->var()); + if(!ip) { + rv = Qnil; + break; + } + *(m->var()) = INT2NUM(*ip); + m->next(); + if(!m->type().isConst()) + *ip = NUM2INT(*(m->var())); + } + break; + default: + m->unsupported(); + break; + } +} + +static void marshall_boolR(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE rv = *(m->var()); + bool * b = new bool; + if (TYPE(rv) == T_OBJECT) { + // A Qt::Boolean has been passed as a value + VALUE temp = rb_funcall(qt_internal_module, rb_intern("get_qboolean"), 1, rv); + *b = (temp == Qtrue ? true : false); + m->item().s_voidp = b; + m->next(); + rb_funcall(qt_internal_module, rb_intern("set_qboolean"), 2, rv, (*b ? Qtrue : Qfalse)); + } else { + *b = (rv == Qtrue ? true : false); + m->item().s_voidp = b; + m->next(); + } + if(m->cleanup() && m->type().isConst()) { + delete b; + } + } + break; + case Marshall::ToVALUE: + { + bool *ip = (bool*)m->item().s_voidp; + if(!ip) { + *(m->var()) = Qnil; + break; + } + *(m->var()) = (*ip?Qtrue:Qfalse); + m->next(); + if(!m->type().isConst()) + *ip = *(m->var()) == Qtrue ? true : false; + } + break; + default: + m->unsupported(); + break; + } +} + +static void marshall_charP_array(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE arglist = *(m->var()); + if (arglist == Qnil + || TYPE(arglist) != T_ARRAY + || RARRAY(arglist)->len == 0 ) + { + m->item().s_voidp = 0; + break; + } + + char **argv = new char *[RARRAY(arglist)->len + 1]; + long i; + for(i = 0; i < RARRAY(arglist)->len; i++) { + VALUE item = rb_ary_entry(arglist, i); + char *s = StringValuePtr(item); + argv[i] = new char[strlen(s) + 1]; + strcpy(argv[i], s); + } + argv[i] = 0; + m->item().s_voidp = argv; + m->next(); + if(m->cleanup()) { + rb_ary_clear(arglist); + for(i = 0; argv[i]; i++) + rb_ary_push(arglist, rb_str_new2(argv[i])); + } + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_QStringList(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE list = *(m->var()); + if (TYPE(list) != T_ARRAY) { + m->item().s_voidp = 0; + break; + } + + int count = RARRAY(list)->len; + QStringList *stringlist = new QStringList; + + for(long i = 0; i < count; i++) { + VALUE item = rb_ary_entry(list, i); + if(TYPE(item) != T_STRING) { + stringlist->append(QString()); + continue; + } + stringlist->append(*(qstringFromRString(item))); + } + + m->item().s_voidp = stringlist; + m->next(); + + + if (stringlist != 0 && !m->type().isConst()) { + rb_ary_clear(list); + for(QStringList::Iterator it = stringlist->begin(); it != stringlist->end(); ++it) + rb_ary_push(list, rstringFromQString(&(*it))); + } + + if (m->cleanup()) + delete stringlist; + break; + } + case Marshall::ToVALUE: + { + QStringList *stringlist = static_cast<QStringList *>(m->item().s_voidp); + if(!stringlist) { + *(m->var()) = Qnil; + break; + } + + VALUE av = rb_ary_new(); + for(QStringList::Iterator it = stringlist->begin(); it != stringlist->end(); ++it) { + VALUE rv = rstringFromQString(&(*it)); + rb_ary_push(av, rv); + } + + if(m->cleanup()) + delete stringlist; + + *(m->var()) = av; + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_QStrList(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE list = *(m->var()); + if (TYPE(list) != T_ARRAY) { + m->item().s_voidp = 0; + break; + } + + int count = RARRAY(list)->len; + QStrList *stringlist = new QStrList; + + for(long i = 0; i < count; i++) { + VALUE item = rb_ary_entry(list, i); + if(TYPE(item) != T_STRING) { + stringlist->append(QString()); + continue; + } + stringlist->append(QString::fromUtf8(StringValuePtr(item), RSTRING(item)->len)); + } + + m->item().s_voidp = stringlist; + m->next(); + + if (!m->type().isConst()) { + rb_ary_clear(list); + for(const char * it = stringlist->first(); it != 0; it = stringlist->next()) + rb_ary_push(list, rb_str_new2(it)); + } + + if (m->cleanup()) { + delete stringlist; + } + break; + } + case Marshall::ToVALUE: + { + QStrList *stringlist = static_cast<QStrList *>(m->item().s_voidp); + if(!stringlist) { + *(m->var()) = Qnil; + break; + } + + VALUE av = rb_ary_new(); + for(const char * it = stringlist->first(); it != 0; it = stringlist->next()) { + VALUE rv = rb_str_new2(it); + rb_ary_push(av, rv); + } + + if(m->cleanup()) + delete stringlist; + + *(m->var()) = av; + } + break; + default: + m->unsupported(); + break; + } +} + +template <class Item, class ItemList, class ItemListIterator, const char *ItemSTR > +void marshall_ItemList(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE list = *(m->var()); + if (TYPE(list) != T_ARRAY) { + m->item().s_voidp = 0; + break; + } + int count = RARRAY(list)->len; + ItemList *cpplist = new ItemList; + long i; + for(i = 0; i < count; i++) { + VALUE item = rb_ary_entry(list, i); + // TODO do type checking! + smokeruby_object *o = value_obj_info(item); + if(!o || !o->ptr) + continue; + void *ptr = o->ptr; + ptr = o->smoke->cast( + ptr, // pointer + o->classId, // from + o->smoke->idClass(ItemSTR) // to + ); + cpplist->append((Item*)ptr); + } + + m->item().s_voidp = cpplist; + m->next(); + + if (!m->type().isConst()) { + rb_ary_clear(list); + for(ItemListIterator it = cpplist->begin(); + it != cpplist->end(); + ++it ) + { + VALUE obj = getPointerObject((void*)(*it)); + rb_ary_push(list, obj); + } + } + + if (m->cleanup()) { + delete cpplist; + } + } + break; + case Marshall::ToVALUE: + { + ItemList *valuelist = (ItemList*)m->item().s_voidp; + if(!valuelist) { + *(m->var()) = Qnil; + break; + } + + VALUE av = rb_ary_new(); + + for(ItemListIterator it = valuelist->begin(); + it != valuelist->end(); + ++it) { + void *p = *it; + + if(m->item().s_voidp == 0) { + *(m->var()) = Qnil; + break; + } + + VALUE obj = getPointerObject(p); + if(obj == Qnil) { + smokeruby_object * o = ALLOC(smokeruby_object); + o->smoke = m->smoke(); + o->classId = m->smoke()->idClass(ItemSTR); + o->ptr = p; + o->allocated = false; + obj = set_obj_info(resolve_classname(o->smoke, o->classId, o->ptr), o); + } + rb_ary_push(av, obj); + } + + if(m->cleanup()) + delete valuelist; + else + *(m->var()) = av; + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_QValueListInt(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE list = *(m->var()); + if (TYPE(list) != T_ARRAY) { + m->item().s_voidp = 0; + break; + } + int count = RARRAY(list)->len; + QValueList<int> *valuelist = new QValueList<int>; + long i; + for(i = 0; i < count; i++) { + VALUE item = rb_ary_entry(list, i); + if(TYPE(item) != T_FIXNUM && TYPE(item) != T_BIGNUM) { + valuelist->append(0); + continue; + } + valuelist->append(NUM2INT(item)); + } + + m->item().s_voidp = valuelist; + m->next(); + + if (!m->type().isConst()) { + rb_ary_clear(list); + for(QValueListIterator<int> it = valuelist->begin(); + it != valuelist->end(); + ++it) + rb_ary_push(list, INT2NUM((int)*it)); + } + + if (m->cleanup()) { + delete valuelist; + } + } + break; + case Marshall::ToVALUE: + { + QValueList<int> *valuelist = (QValueList<int>*)m->item().s_voidp; + if(!valuelist) { + *(m->var()) = Qnil; + break; + } + + VALUE av = rb_ary_new(); + + for(QValueListIterator<int> it = valuelist->begin(); + it != valuelist->end(); + ++it) + rb_ary_push(av, INT2NUM(*it)); + + *(m->var()) = av; + + if(m->cleanup()) + delete valuelist; + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_voidP(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE rv = *(m->var()); + if (rv != Qnil) + m->item().s_voidp = (void*)NUM2INT(*(m->var())); + else + m->item().s_voidp = 0; + } + break; + case Marshall::ToVALUE: + { + *(m->var()) = Data_Wrap_Struct(rb_cObject, 0, 0, m->item().s_voidp); + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_QMapQStringQString(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE hash = *(m->var()); + if (TYPE(hash) != T_HASH) { + m->item().s_voidp = 0; + break; + } + + QMap<QString,QString> * map = new QMap<QString,QString>; + + // Convert the ruby hash to an array of key/value arrays + VALUE temp = rb_funcall(hash, rb_intern("to_a"), 0); + + for (long i = 0; i < RARRAY(temp)->len; i++) { + VALUE key = rb_ary_entry(rb_ary_entry(temp, i), 0); + VALUE value = rb_ary_entry(rb_ary_entry(temp, i), 1); + (*map)[QString(StringValuePtr(key))] = QString(StringValuePtr(value)); + } + + m->item().s_voidp = map; + m->next(); + + if(m->cleanup()) + delete map; + } + break; + case Marshall::ToVALUE: + { + QMap<QString,QString> *map = (QMap<QString,QString>*)m->item().s_voidp; + if(!map) { + *(m->var()) = Qnil; + break; + } + + VALUE hv = rb_hash_new(); + + QMap<QString,QString>::Iterator it; + for (it = map->begin(); it != map->end(); ++it) { + rb_hash_aset(hv, rstringFromQString((QString*)&(it.key())), rstringFromQString((QString*) &(it.data()))); + } + + *(m->var()) = hv; + m->next(); + + if(m->cleanup()) + delete map; + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_QMapQStringQVariant(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE hash = *(m->var()); + if (TYPE(hash) != T_HASH) { + m->item().s_voidp = 0; + break; + } + + QMap<QString,QVariant> * map = new QMap<QString,QVariant>; + + // Convert the ruby hash to an array of key/value arrays + VALUE temp = rb_funcall(hash, rb_intern("to_a"), 0); + + for (long i = 0; i < RARRAY(temp)->len; i++) { + VALUE key = rb_ary_entry(rb_ary_entry(temp, i), 0); + VALUE value = rb_ary_entry(rb_ary_entry(temp, i), 1); + + smokeruby_object *o = value_obj_info(value); + if (!o || !o->ptr || o->classId != o->smoke->idClass("QVariant")) { + // If the value isn't a Qt::Variant, then try and construct + // a Qt::Variant from it + value = rb_funcall(qvariant_class, rb_intern("new"), 1, value); + if (value == Qnil) { + continue; + } + o = value_obj_info(value); + } + + void * ptr = o->ptr; + ptr = o->smoke->cast(ptr, o->classId, o->smoke->idClass("QVariant")); + + (*map)[QString(StringValuePtr(key))] = (QVariant)*(QVariant*)ptr; + } + + m->item().s_voidp = map; + m->next(); + + if(m->cleanup()) + delete map; + } + break; + case Marshall::ToVALUE: + { + QMap<QString,QVariant> *map = (QMap<QString,QVariant>*)m->item().s_voidp; + if(!map) { + *(m->var()) = Qnil; + break; + } + + VALUE hv = rb_hash_new(); + + QMap<QString,QVariant>::Iterator it; + for (it = map->begin(); it != map->end(); ++it) { + void *p = new QVariant(it.data()); + VALUE obj = getPointerObject(p); + + if (obj == Qnil) { + smokeruby_object * o = ALLOC(smokeruby_object); + o->classId = m->smoke()->idClass("QVariant"); + o->smoke = m->smoke(); + o->ptr = p; + o->allocated = true; + obj = set_obj_info("Qt::Variant", o); + } + + rb_hash_aset(hv, rstringFromQString((QString*)&(it.key())), obj); + } + + *(m->var()) = hv; + m->next(); + + if(m->cleanup()) + delete map; + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_QUObject(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE array = *(m->var()); + if (array != Qnil && TYPE(array) == T_ARRAY) { + VALUE rv = rb_ary_entry(array, 0); + Data_Get_Struct(rv, QUObject, m->item().s_voidp); + } else { + m->item().s_voidp = 0; + } + } + break; + case Marshall::ToVALUE: + { + VALUE rv = Data_Wrap_Struct(rb_cObject, 0, 0, m->item().s_voidp); + VALUE array = rb_ary_new2(1); + rb_ary_push(array, rv); + *(m->var()) = array; + } + break; + default: + m->unsupported(); + break; + } +} + +void marshall_QRgb_array(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE list = *(m->var()); + if (TYPE(list) != T_ARRAY) { + m->item().s_voidp = 0; + break; + } + int count = RARRAY(list)->len; + QRgb *rgb = new QRgb[count + 2]; + long i; + for(i = 0; i < count; i++) { + VALUE item = rb_ary_entry(list, i); + if(TYPE(item) != T_FIXNUM && TYPE(item) != T_BIGNUM) { + rgb[i] = 0; + continue; + } + + rgb[i] = NUM2UINT(item); + } + m->item().s_voidp = rgb; + m->next(); + } + break; + case Marshall::ToVALUE: + // Implement this with a tied array or something + default: + m->unsupported(); + break; + } +} + +void marshall_QPairintint(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE list = *(m->var()); + if (TYPE(list) != T_ARRAY || RARRAY(list)->len != 2) { + m->item().s_voidp = 0; + break; + } + int int0; + int int1; + VALUE item = rb_ary_entry(list, 0); + if(TYPE(item) != T_FIXNUM && TYPE(item) != T_BIGNUM) { + int0 = 0; + } else { + int0 = NUM2INT(item); + } + + item = rb_ary_entry(list, 1); + if(TYPE(item) != T_FIXNUM && TYPE(item) != T_BIGNUM) { + int1 = 0; + } else { + int1 = NUM2INT(item); + } + + QPair<int,int> * qpair = new QPair<int,int>(int0,int1); + m->item().s_voidp = qpair; + m->next(); + if(m->cleanup()) + delete qpair; + } + break; + case Marshall::ToVALUE: + default: + m->unsupported(); + break; + } +} + +#define DEF_LIST_MARSHALLER(ListIdent,ItemList,Item,Itr) namespace { char ListIdent##STR[] = #Item; }; \ + Marshall::HandlerFn marshall_##ListIdent = marshall_ItemList<Item,ItemList,Itr,ListIdent##STR>; + +#include <qcanvas.h> +#include <qdir.h> +#include <qobjectlist.h> +#include <qwidgetlist.h> +#include <qdockwindow.h> +#include <qnetworkprotocol.h> +#include <qtoolbar.h> +#include <qtabbar.h> + +#if QT_VERSION >= 0x030200 +DEF_LIST_MARSHALLER( QPtrListQNetworkOperation, QPtrList<QNetworkOperation>, QNetworkOperation, QPtrListStdIterator<QNetworkOperation> ) +DEF_LIST_MARSHALLER( QPtrListQToolBar, QPtrList<QToolBar>, QToolBar, QPtrListStdIterator<QToolBar> ) +DEF_LIST_MARSHALLER( QPtrListQTab, QPtrList<QTab>, QTab, QPtrListStdIterator<QTab> ) +DEF_LIST_MARSHALLER( QPtrListQDockWindow, QPtrList<QDockWindow>, QDockWindow, QPtrListStdIterator<QDockWindow> ) +DEF_LIST_MARSHALLER( QFileInfoList, QFileInfoList, QFileInfo, QFileInfoList::Iterator ) +DEF_LIST_MARSHALLER( QObjectList, QObjectList, QObject, QPtrListStdIterator<QObject> ) +DEF_LIST_MARSHALLER( QWidgetList, QWidgetList, QWidget, QPtrListStdIterator<QWidget> ) +#endif + +DEF_LIST_MARSHALLER( QCanvasItemList, QCanvasItemList, QCanvasItem, QValueListIterator<QCanvasItem*> ) + +template <class Item, class ItemList, class ItemListIterator, const char *ItemSTR > +void marshall_ValueItemList(Marshall *m) { + switch(m->action()) { + case Marshall::FromVALUE: + { + VALUE list = *(m->var()); + if (TYPE(list) != T_ARRAY) { + m->item().s_voidp = 0; + break; + } + int count = RARRAY(list)->len; + ItemList *cpplist = new ItemList; + long i; + for(i = 0; i < count; i++) { + VALUE item = rb_ary_entry(list, i); + // TODO do type checking! + smokeruby_object *o = value_obj_info(item); + + // Special case for the QValueList<QVariant> type + if ( qstrcmp(ItemSTR, "QVariant") == 0 + && (!o || !o->ptr || o->classId != o->smoke->idClass("QVariant")) ) + { + // If the value isn't a Qt::Variant, then try and construct + // a Qt::Variant from it + item = rb_funcall(qvariant_class, rb_intern("new"), 1, item); + if (item == Qnil) { + continue; + } + o = value_obj_info(item); + } + + if(!o || !o->ptr) + continue; + void *ptr = o->ptr; + ptr = o->smoke->cast( + ptr, // pointer + o->classId, // from + o->smoke->idClass(ItemSTR) // to + ); + cpplist->append(*(Item*)ptr); + } + + m->item().s_voidp = cpplist; + m->next(); + + if (!m->type().isConst()) { + rb_ary_clear(list); + for(ItemListIterator it = cpplist->begin(); + it != cpplist->end(); + ++it) + { + VALUE obj = getPointerObject((void*)&(*it)); + rb_ary_push(list, obj); + } + } + + if (m->cleanup()) { + delete cpplist; + } + } + break; + case Marshall::ToVALUE: + { + ItemList *valuelist = (ItemList*)m->item().s_voidp; + if(!valuelist) { + *(m->var()) = Qnil; + break; + } + + VALUE av = rb_ary_new(); + + int ix = m->smoke()->idClass(ItemSTR); + const char * className = m->smoke()->binding->className(ix); + + for(ItemListIterator it = valuelist->begin(); + it != valuelist->end(); + ++it) { + void *p = &(*it); + + if(m->item().s_voidp == 0) { + *(m->var()) = Qnil; + break; + } + + VALUE obj = getPointerObject(p); + if(obj == Qnil) { + smokeruby_object * o = ALLOC(smokeruby_object); + o->smoke = m->smoke(); + o->classId = o->smoke->idClass(ItemSTR); + o->ptr = p; + o->allocated = false; + obj = set_obj_info(className, o); + } + rb_ary_push(av, obj); + } + + if(m->cleanup()) + delete valuelist; + else + *(m->var()) = av; + } + break; + default: + m->unsupported(); + break; + } +} + +#define DEF_VALUELIST_MARSHALLER(ListIdent,ItemList,Item,Itr) namespace dummy { char ListIdent##STR[] = #Item; }; \ + Marshall::HandlerFn marshall_##ListIdent = marshall_ValueItemList<Item,ItemList,Itr,dummy::ListIdent##STR>; + +DEF_VALUELIST_MARSHALLER( QVariantList, QValueList<QVariant>, QVariant, QValueList<QVariant>::Iterator ) +DEF_VALUELIST_MARSHALLER( QPixmapList, QValueList<QPixmap>, QPixmap, QValueList<QPixmap>::Iterator ) +DEF_VALUELIST_MARSHALLER( QIconDragItemList, QValueList<QIconDragItem>, QIconDragItem, QValueList<QIconDragItem>::Iterator ) +DEF_VALUELIST_MARSHALLER( QImageTextKeyLangList, QValueList<QImageTextKeyLang>, QImageTextKeyLang, QValueList<QImageTextKeyLang>::Iterator ) +DEF_VALUELIST_MARSHALLER( QUrlInfoList, QValueList<QUrlInfo>, QUrlInfo, QValueList<QUrlInfo>::Iterator ) +DEF_VALUELIST_MARSHALLER( QTranslatorMessageList, QValueList<QTranslatorMessage>, QTranslatorMessage, QValueList<QTranslatorMessage>::Iterator ) +DEF_VALUELIST_MARSHALLER( QHostAddressList, QValueList<QHostAddress>, QHostAddress, QValueList<QHostAddress>::Iterator ) + +TypeHandler Qt_handlers[] = { + { "QString", marshall_QString }, + { "QString&", marshall_QString }, + { "QString*", marshall_QString }, + { "QCString", marshall_QCString }, + { "QCString&", marshall_QCString }, + { "QCString*", marshall_QCString }, + { "QStringList", marshall_QStringList }, + { "QStringList&", marshall_QStringList }, + { "QStringList*", marshall_QStringList }, + { "QStrList", marshall_QStrList }, + { "QStrList&", marshall_QStrList }, + { "QStrList*", marshall_QStrList }, + { "long long int", marshall_longlong }, + { "long long int&", marshall_longlong }, + { "Q_INT64", marshall_longlong }, + { "Q_INT64&", marshall_longlong }, + { "Q_LLONG", marshall_longlong }, + { "Q_LLONG&", marshall_longlong }, + { "KIO::filesize_t", marshall_longlong }, + { "DOM::DOMTimeStamp", marshall_ulonglong }, + { "unsigned long long int", marshall_ulonglong }, + { "unsigned long long int&", marshall_ulonglong }, + { "Q_UINT64", marshall_ulonglong }, + { "Q_UINT64&", marshall_ulonglong }, + { "Q_ULLONG", marshall_ulonglong }, + { "Q_ULLONG&", marshall_ulonglong }, + { "signed int&", marshall_intR }, + { "int&", marshall_intR }, + { "int*", marshall_intR }, + { "bool&", marshall_boolR }, + { "bool*", marshall_boolR }, + { "char*", marshall_charP }, + { "char**", marshall_charP_array }, + { "uchar*", marshall_ucharP }, + { "QRgb*", marshall_QRgb_array }, + { "QPair<int,int>&", marshall_QPairintint }, + { "QUObject*", marshall_QUObject }, + { "const QCOORD*", marshall_QCOORD_array }, + { "void", marshall_void }, + { "QValueList<int>", marshall_QValueListInt }, + { "QValueList<int>&", marshall_QValueListInt }, + { "QValueList<QVariant>", marshall_QVariantList }, + { "QValueList<QVariant>&", marshall_QVariantList }, + { "QValueList<QPixmap>", marshall_QPixmapList }, + { "QValueList<QIconDragItem>&", marshall_QIconDragItemList }, + { "QValueList<QImageTextKeyLang>", marshall_QImageTextKeyLangList }, + { "QValueList<QUrlInfo>&", marshall_QUrlInfoList }, + { "QValueList<QTranslatorMessage>", marshall_QTranslatorMessageList }, + { "QValueList<QHostAddress>", marshall_QHostAddressList }, + { "QCanvasItemList", marshall_QCanvasItemList }, + { "QMap<QString,QString>", marshall_QMapQStringQString }, + { "QMap<QString,QString>&", marshall_QMapQStringQString }, + { "QMap<QString,QVariant>", marshall_QMapQStringQVariant }, + { "QMap<QString,QVariant>&", marshall_QMapQStringQVariant }, +#if QT_VERSION >= 0x030200 + { "QWidgetList", marshall_QWidgetList }, + { "QWidgetList*", marshall_QWidgetList }, + { "QWidgetList&", marshall_QWidgetList }, + { "QObjectList*", marshall_QObjectList }, + { "QObjectList&", marshall_QObjectList }, + { "QFileInfoList*", marshall_QFileInfoList }, + { "QPtrList<QToolBar>", marshall_QPtrListQToolBar }, + { "QPtrList<QTab>*", marshall_QPtrListQTab }, + { "QPtrList<QDockWindow>", marshall_QPtrListQDockWindow }, + { "QPtrList<QDockWindow>*", marshall_QPtrListQDockWindow }, + { "QPtrList<QNetworkOperation>", marshall_QPtrListQNetworkOperation }, + { "QPtrList<QNetworkOperation>&", marshall_QPtrListQNetworkOperation }, +#endif + { 0, 0 } +}; + +QAsciiDict<TypeHandler> type_handlers(199); + +void install_handlers(TypeHandler *h) { + while(h->name) { + type_handlers.insert(h->name, h); + h++; + } +} + +Marshall::HandlerFn getMarshallFn(const SmokeType &type) { + if(type.elem()) + return marshall_basetype; + if(!type.name()) + return marshall_void; + TypeHandler *h = type_handlers[type.name()]; + if(h == 0 && type.isConst() && strlen(type.name()) > strlen("const ")) { + h = type_handlers[type.name() + strlen("const ")]; + } + + if(h != 0) { + return h->fn; + } + + return marshall_unknown; +} diff --git a/qtruby/rubylib/qtruby/lib/Makefile.am b/qtruby/rubylib/qtruby/lib/Makefile.am new file mode 100644 index 00000000..e8f6457a --- /dev/null +++ b/qtruby/rubylib/qtruby/lib/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = Qt + +rubylibdir = $(RUBY_SITEDIR) +rubylib_DATA = Qt.rb diff --git a/qtruby/rubylib/qtruby/lib/Qt.rb b/qtruby/rubylib/qtruby/lib/Qt.rb new file mode 100644 index 00000000..70e9c776 --- /dev/null +++ b/qtruby/rubylib/qtruby/lib/Qt.rb @@ -0,0 +1 @@ +require 'qtruby' diff --git a/qtruby/rubylib/qtruby/lib/Qt/Makefile.am b/qtruby/rubylib/qtruby/lib/Qt/Makefile.am new file mode 100644 index 00000000..267582ef --- /dev/null +++ b/qtruby/rubylib/qtruby/lib/Qt/Makefile.am @@ -0,0 +1,2 @@ +qtrubylibdir = $(RUBY_SITEDIR)/Qt +qtrubylib_DATA = qtruby.rb diff --git a/qtruby/rubylib/qtruby/lib/Qt/qtruby.rb b/qtruby/rubylib/qtruby/lib/Qt/qtruby.rb new file mode 100644 index 00000000..50f2eaef --- /dev/null +++ b/qtruby/rubylib/qtruby/lib/Qt/qtruby.rb @@ -0,0 +1,1978 @@ +=begin +/*************************************************************************** + qtruby.rb - description + ------------------- + begin : Fri Jul 4 2003 + copyright : (C) 2003 by Richard Dale + email : Richard_Dale@tipitina.demon.co.uk + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +=end + +module Qt + module DebugLevel + Off, Minimal, High, Extensive = 0, 1, 2, 3 + end + + module QtDebugChannel + QTDB_NONE = 0x00 + QTDB_AMBIGUOUS = 0x01 + QTDB_METHOD_MISSING = 0x02 + QTDB_CALLS = 0x04 + QTDB_GC = 0x08 + QTDB_VIRTUAL = 0x10 + QTDB_VERBOSE = 0x20 + QTDB_ALL = QTDB_VERBOSE | QTDB_VIRTUAL | QTDB_GC | QTDB_CALLS | QTDB_METHOD_MISSING | QTDB_AMBIGUOUS + end + + @@debug_level = DebugLevel::Off + def Qt.debug_level=(level) + @@debug_level = level + Internal::setDebug Qt::QtDebugChannel::QTDB_ALL if level >= DebugLevel::Extensive + end + + def Qt.debug_level + @@debug_level + end + + class Base + def self.signals(*signal_list) + meta = Qt::Meta[self.name] || Qt::MetaInfo.new(self) + meta.add_signals(signal_list) + meta.changed = true + end + + def self.slots(*slot_list) + meta = Qt::Meta[self.name] || Qt::MetaInfo.new(self) + meta.add_slots(slot_list) + meta.changed = true + end + + def **(a) + return Qt::**(self, a) + end + def +(a) + return Qt::+(self, a) + end + def ~(a) + return Qt::~(self, a) + end + def -@() + return Qt::-(self) + end + def -(a) + return Qt::-(self, a) + end + def *(a) + return Qt::*(self, a) + end + def /(a) + return Qt::/(self, a) + end + def %(a) + return Qt::%(self, a) + end + def >>(a) + return Qt::>>(self, a) + end + def <<(a) + return Qt::<<(self, a) + end + def &(a) + return Qt::&(self, a) + end + def ^(a) + return Qt::^(self, a) + end + def |(a) + return Qt::|(self, a) + end + +# Module has '<', '<=', '>' and '>=' operator instance methods, so pretend they +# don't exist by calling method_missing() explicitely + def <(a) + begin + Qt::method_missing(:<, self, a) + rescue + super(a) + end + end + + def <=(a) + begin + Qt::method_missing(:<=, self, a) + rescue + super(a) + end + end + + def >(a) + begin + Qt::method_missing(:>, self, a) + rescue + super(a) + end + end + + def >=(a) + begin + Qt::method_missing(:>=, self, a) + rescue + super(a) + end + end + +# Object has a '==' operator instance method, so pretend it +# don't exist by calling method_missing() explicitely + def ==(a) + begin + Qt::method_missing(:==, self, a) + rescue + super(a) + end + end + + def methods(regular=true) + if !regular + return singleton_methods + end + + qt_methods(super, 0x0) + end + + def protected_methods + # From smoke.h, Smoke::mf_protected 0x80 + qt_methods(super, 0x80) + end + + def public_methods + methods + end + + def singleton_methods + # From smoke.h, Smoke::mf_static 0x01 + qt_methods(super, 0x01) + end + + private + def qt_methods(meths, flags) + ids = [] + # These methods are all defined in Qt::Base, even if they aren't supported by a particular + # subclass, so remove them to avoid confusion + meths -= ["%", "&", "*", "**", "+", "-", "-@", "/", "<", "<<", "<=", ">", ">=", ">>", "|", "~", "^"] + classid = Qt::Internal::idInstance(self) + Qt::Internal::getAllParents(classid, ids) + ids << classid + ids.each { |c| Qt::Internal::findAllMethodNames(meths, c, flags) } + return meths.uniq + end + end # Qt::Base + + # Delete the underlying C++ instance after exec returns + # Otherwise, rb_gc_call_finalizer_at_exit() can delete + # stuff that Qt::Application still needs for its cleanup. + class Application < Qt::Base + def exec + method_missing(:exec) + self.dispose + Qt::Internal.application_terminated = true + end + + def exit(*args) + method_missing(:exit, *args) + end + + def type(*args) + method_missing(:type, *args) + end + end + + class BoxLayout < Qt::Base + include Enumerable + + def each + it = iterator() + while it.current + yield it.current + it += 1 + end + end + end + + class Buffer < Qt::Base + def open(*args) + method_missing(:open, *args) + end + end + + class ButtonGroup < Qt::Base + def id(*args) + method_missing(:id, *args) + end + end + + class ByteArray < Qt::Base + def to_s + return data() + end + + def length + return size() + end + + def data=(data) + setRawData(data) + end + end + + class CheckListItem < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class ChildEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class ClassInfo < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class CloseEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Color < Qt::Base + def inspect + str = super + str.sub(/>$/, " %s>" % name) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, " %s>" % name) + end + + def name(*args) + method_missing(:name, *args) + end + end + + class Connection < Qt::Base + def inspect + str = super + str.sub(/>$/, " memberName=%s, memberType=%s, object=%s>" % + [memberName.inspect, memberType == 1 ? "SLOT" : "SIGNAL", object.inspect] ) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, "\n memberName=%s,\n memberType=%s,\n object=%s>" % + [memberName.inspect, memberType == 1 ? "SLOT" : "SIGNAL", object.inspect] ) + end + end + + class ContextMenuEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Cursor < Qt::Base + def inspect + str = super + str.sub(/>$/, " shape=%d>" % shape) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, " shape=%d>" % shape) + end + end + + class CustomEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Date < Qt::Base + def inspect + str = super + str.sub(/>$/, " %s>" % toString) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, " %s>" % toString) + end + end + + class DateTime < Qt::Base + def inspect + str = super + str.sub(/>$/, " %s>" % toString) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, " %s>" % toString) + end + end + + class Dialog < Qt::Base + def exec(*args) + method_missing(:exec, *args) + end + end + + class DomAttr < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class DomDocumentType < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class DragLeaveEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class DropEvent < Qt::Base + def format(*args) + method_missing(:format, *args) + end + + def type(*args) + method_missing(:type, *args) + end + end + + class EucJpCodec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class EucKrCodec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class Event < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class EventLoop < Qt::Base + def exec(*args) + method_missing(:exec, *args) + end + + def exit(*args) + method_missing(:exit, *args) + end + end + + class File < Qt::Base + def name(*args) + method_missing(:name, *args) + end + + def open(*args) + method_missing(:open, *args) + end + end + + class FocusEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Font < Qt::Base + def inspect + str = super + str.sub(/>$/, " family=%s, pointSize=%d, weight=%d, italic=%s, bold=%s, underline=%s, strikeOut=%s>" % + [family.inspect, pointSize, weight, italic, bold, underline, strikeOut]) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, "\n family=%s,\n pointSize=%d,\n weight=%d,\n italic=%s,\n bold=%s,\n underline=%s,\n strikeOut=%s>" % + [family.inspect, pointSize, weight, italic, bold, underline, strikeOut]) + end + end + + class Ftp < Qt::Base + def abort(*args) + method_missing(:abort, *args) + end + end + + class GLContext < Qt::Base + def format(*args) + method_missing(:format, *args) + end + end + + class GLWidget < Qt::Base + def format(*args) + method_missing(:format, *args) + end + end + + class Gb18030Codec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class Gb2312Codec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class GbkCodec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class GridLayout < Qt::Base + include Enumerable + + def each + it = iterator() + while it.current + yield it.current + it += 1 + end + end + end + + class HBoxLayout < Qt::Base + include Enumerable + + def each + it = iterator() + while it.current + yield it.current + it += 1 + end + end + end + + class HebrewCodec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class HideEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Http < Qt::Base + def abort(*args) + method_missing(:abort, *args) + end + end + + class HttpRequestHeader < Qt::Base + def method(*args) + method_missing(:method, *args) + end + end + + class IconDrag < Qt::Base + def format(*args) + method_missing(:format, *args) + end + + def type(*args) + method_missing(:type, *args) + end + end + + class Image < Qt::Base + def load(*args) + method_missing(:load, *args) + end + end + + class ImageDecoder < Qt::Base + def format(*args) + method_missing(:format, *args) + end + end + + class ImageDrag < Qt::Base + def format(*args) + method_missing(:format, *args) + end + end + + class ImageIO < Qt::Base + def format(*args) + method_missing(:format, *args) + end + end + + class IMEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class JisCodec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class KeyEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class LCDNumber < Qt::Base + def display(item) + method_missing(:display, item) + end + end + + class Layout < Qt::Base + def freeze(*args) + method_missing(:freeze, *args) + end + end + + class LayoutIterator < Qt::Base + def +(a) + for i in 1..a + send("operator++".to_sym) + end + return self + end + end + + class Library < Qt::Base + def load(*args) + method_missing(:load, *args) + end + end + + class ListView < Qt::Base + include Enumerable + + def each + it = Qt::ListViewItemIterator.new(self) + while it.current + yield it.current + it += 1 + end + end + + def sort(*args) + method_missing(:sort, *args) + end + end + + class ListViewItem < Qt::Base + include Enumerable + + def each + it = Qt::ListViewItemIterator.new(self) + while it.current + yield it.current + it += 1 + end + end + + def sort(*args) + method_missing(:sort, *args) + end + + def inspect + str = super + str.sub!(/>$/, "") + for i in 0..(listView.columns - 1) + str << " text%d=%s," % [i, self.text(i)] + end + str.sub!(/,?$/, ">") + end + + def pretty_print(pp) + str = to_s + str.sub!(/>$/, "") + for i in 0..(listView.columns - 1) + str << " text%d=%s," % [i, self.text(i)] + end + str.sub!(/,?$/, ">") + pp.text str + end + end + + class Locale < Qt::Base + def name(*args) + method_missing(:name, *args) + end + def system(*args) + method_missing(:system, *args) + end + end + + class MenuItem < Qt::Base + def id(*args) + method_missing(:id, *args) + end + end + + class MetaData < Qt::Base + def method(*args) + method_missing(:method, *args) + end + + def name(*args) + method_missing(:name, *args) + end + end + + class MetaEnum < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class MetaProperty < Qt::Base + def id(*args) + method_missing(:id, *args) + end + + def name(*args) + method_missing(:name, *args) + end + + def type(*args) + method_missing(:type, *args) + end + end + + class MouseEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class MoveEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Object < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class PaintEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Picture < Qt::Base + def load(*args) + method_missing(:load, *args) + end + end + + class Pixmap < Qt::Base + def load(*args) + method_missing(:load, *args) + end + end + + class Point < Qt::Base + def inspect + str = super + str.sub(/>$/, " x=%d, y=%d>" % [x, y]) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, "\n x=%d,\n y=%d>" % [x, y]) + end + end + + class PolygonScanner < Qt::Base + def scan(*args) + method_missing(:scan, *args) + end + end + + class PopupMenu < Qt::Base + def exec(*args) + method_missing(:exec, *args) + end + end + + class Printer < Qt::Base + def abort(*args) + method_missing(:abort, *args) + end + end + + class MetaObject < Qt::Base + def inspect + str = super + str.sub!(/>$/, "") + str << " className=%s," % className + str << " propertyNames=Array (%d element(s))," % numProperties unless numProperties == 0 + str << " signalNames=Array (%d element(s))," % numSignals unless numSignals == 0 + str << " slotNames=Array (%d element(s))," % numSlots unless numSlots == 0 + str << " superClass=%s," % superClass.inspect unless superClass == nil + str.chop! + str << ">" + end + + def pretty_print(pp) + str = to_s + str.sub!(/>$/, "") + str << "\n className=%s," % className + str << "\n propertyNames=Array (%d element(s))," % numProperties unless numProperties == 0 + str << "\n signalNames=Array (%d element(s))," % numSignals unless numSignals == 0 + str << "\n slotNames=Array (%d element(s))," % numSlots unless numSlots == 0 + str << "\n superClass=%s," % superClass.inspect unless superClass == nil + str.chop! + str << ">" + pp.text str + end + end + + class Rect < Qt::Base + def inspect + str = super + str.sub(/>$/, " left=%d, right=%d, top=%d, bottom=%d>" % [left, right, top, bottom]) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, "\n left=%d,\n right=%d,\n top=%d,\n bottom=%d>" % [left, right, top, bottom]) + end + end + + class ResizeEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class ShowEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Size < Qt::Base + def inspect + str = super + str.sub(/>$/, " width=%d, height=%d>" % [width, height]) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, "\n width=%d,\n height=%d>" % [width, height]) + end + end + + class SizePolicy < Qt::Base + def inspect + str = super + str.sub(/>$/, " horData=%d, verData=%d>" % [horData, verData]) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, "\n horData=%d,\n verData=%d>" % [horData, verData]) + end + end + + class SjisCodec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class Socket < Qt::Base + def open(*args) + method_missing(:open, *args) + end + end + + class SocketDevice < Qt::Base + def open(*args) + method_missing(:open, *args) + end + + def type(*args) + method_missing(:type, *args) + end + end + + class SocketNotifier < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class SqlCursor < Qt::Base + def exec(*args) + method_missing(:exec, *args) + end + + def name(*args) + method_missing(:name, *args) + end + + def select(*args) + method_missing(:select, *args) + end + end + + class SqlDatabase < Qt::Base + def exec(*args) + method_missing(:exec, *args) + end + def open(*args) + method_missing(:open, *args) + end + end + + class SqlDriver < Qt::Base + def open(*args) + method_missing(:open, *args) + end + end + + class SqlError < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class SqlField < Qt::Base + def name(*args) + method_missing(:name, *args) + end + + def type(*args) + method_missing(:type, *args) + end + end + + class SqlFieldInfo < Qt::Base + def name(*args) + method_missing(:name, *args) + end + + def type(*args) + method_missing(:type, *args) + end + end + + class SqlIndex < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class SqlQuery < Qt::Base + def exec(*args) + method_missing(:exec, *args) + end + end + + class SqlSelectCursor < Qt::Base + def exec(*args) + method_missing(:exec, *args) + end + + def name(*args) + method_missing(:name, *args) + end + + def select(*args) + method_missing(:select, *args) + end + end + + class StoredDrag < Qt::Base + def format(*args) + method_missing(:format, *args) + end + end + + class StyleSheetItem < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class TextDrag < Qt::Base + def format(*args) + method_missing(:format, *args) + end + end + + class TabletEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Time < Qt::Base + def inspect + str = super + str.sub(/>$/, " %s>" % toString) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, " %s>" % toString) + end + end + + class TimeEdit < Qt::Base + def display + method_missing(:display) + end + end + + class TimerEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Translator < Qt::Base + def load(*args) + method_missing(:load, *args) + end + end + + class TranslatorMessage < Qt::Base + def hash(*args) + method_missing(:hash, *args) + end + end + + class TsciiCodec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class UrlInfo < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class Utf16Codec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class Utf8Codec < Qt::Base + def name(*args) + method_missing(:name, *args) + end + end + + class Variant < Qt::Base + String = 3 + Date = 26 + Time = 27 + DateTime = 28 + + def initialize(*args) + # In C++, the boolean constructor needs an ugly dummy int argument, + # so special case that here to avoid needing it in Ruby + if args[0] == true || args[0] == false + super(args[0], 0) + else + super + end + end + + def to_a + return toStringList() + end + + def to_f + return toDouble() + end + + def to_i + return toInt() + end + + def to_int + return toInt() + end + + def to_ruby + case type() + when Qt::Variant::Bitmap + return toBitmap + when Qt::Variant::Bool + return toBool + when Qt::Variant::Brush + return toBrush + when Qt::Variant::ByteArray + return toByteArray + when Qt::Variant::Color + return toColor + when Qt::Variant::ColorGroup + return toColorGroup + when Qt::Variant::CString + return toCString + when Qt::Variant::Cursor + return toCursor + when Qt::Variant::Date + return toDate + when Qt::Variant::DateTime + return toDateTime + when Qt::Variant::Double + return toDouble + when Qt::Variant::Font + return toFont + when Qt::Variant::IconSet + return toIconSet + when Qt::Variant::Image + return toImage + when Qt::Variant::Int + return toInt + when Qt::Variant::KeySequence + return toKeySequence + when Qt::Variant::List + return toList + when Qt::Variant::LongLong + return toLongLong + when Qt::Variant::Map + return toMap + when Qt::Variant::Palette + return toPalette + when Qt::Variant::Pen + return toPen + when Qt::Variant::Pixmap + return toPixmap + when Qt::Variant::Point + return toPoint + when Qt::Variant::PointArray + return toPointArray + when Qt::Variant::Rect + return toRect + when Qt::Variant::Region + return toRegion + when Qt::Variant::Size + return toSize + when Qt::Variant::SizePolicy + return toSizePolicy + when Qt::Variant::String + return toString + when Qt::Variant::StringList + return toStringList + when Qt::Variant::Time + return toTime + when Qt::Variant::UInt + return toUint + when Qt::Variant::ULongLong + return toULongLong + end + end + + def inspect + str = super + str.sub(/>$/, " typeName=%s>" % typeName) + end + + def pretty_print(pp) + str = to_s + pp.text str.sub(/>$/, " typeName=%s>" % typeName) + end + + def load(*args) + method_missing(:load, *args) + end + + def type(*args) + method_missing(:type, *args) + end + end + + class VBoxLayout < Qt::Base + include Enumerable + + def each + it = iterator() + while it.current + yield it.current + it += 1 + end + end + end + + class WhatsThis < Qt::Base + def WhatsThis.display(*k) + method_missing(:display, *k) + end + end + + class WheelEvent < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + class Widget < Qt::Base + def raise(*args) + method_missing(:raise, *args) + end + end + + class WidgetStack < Qt::Base + def id(*args) + method_missing(:id, *args) + end + end + + class XmlAttributes < Qt::Base + def type(*args) + method_missing(:type, *args) + end + end + + # Provides a mutable numeric class for passing to methods with + # C++ 'int*' or 'int&' arg types + class Integer + attr_accessor :value + def initialize(n=0) @value = n end + + def +(n) + return Integer.new(@value + n.to_i) + end + def -(n) + return Integer.new(@value - n.to_i) + end + def *(n) + return Integer.new(@value * n.to_i) + end + def /(n) + return Integer.new(@value / n.to_i) + end + def %(n) + return Integer.new(@value % n.to_i) + end + def **(n) + return Integer.new(@value ** n.to_i) + end + + def |(n) + return Integer.new(@value | n.to_i) + end + def &(n) + return Integer.new(@value & n.to_i) + end + def ^(n) + return Integer.new(@value ^ n.to_i) + end + def <<(n) + return Integer.new(@value << n.to_i) + end + def >>(n) + return Integer.new(@value >> n.to_i) + end + + def >(n) + return @value > n.to_i + end + def >=(n) + return @value >= n.to_i + end + def <(n) + return @value < n.to_i + end + def <=(n) + return @value <= n.to_i + end + + def <=>(n) + if @value < n + return -1 + elsif @value > n + return 1 + else + return 0 + end + end + + def to_f() return @value.to_f end + def to_i() return @value.to_i end + def to_s() return @value.to_s end + + def coerce(n) + [n, @value] + end + end + + # If a C++ enum was converted to an ordinary ruby Integer, the + # name of the type is lost. The enum type name is needed for overloaded + # method resolution when two methods differ only by an enum type. + class Enum < Qt::Integer + attr_accessor :type + def initialize(n, type) + super(n) + @value = n + @type = type + end + + def |(n) + return Enum.new(@value | n.to_i, @type) + end + def &(n) + return Enum.new(@value & n.to_i, @type) + end + def ^(n) + return Enum.new(@value ^ n.to_i, @type) + end + def <(n) + return @value < n.to_i + end + def <=(n) + return @value <= n.to_i + end + def >(n) + return @value > n.to_i + end + def >=(n) + return @value >= n.to_i + end + def <<(n) + return Enum.new(@value << n.to_i, @type) + end + def >>(n) + return Enum.new(@value >> n.to_i, @type) + end + + def ==(n) return @value == n.to_i end + def to_i() return @value end + + def inspect + to_s + end + + def pretty_print(pp) + pp.text "#<%s:0x%8.8x @type=%s, @value=%d>" % [self.class.name, object_id, type, value] + end + end + + # Provides a mutable boolean class for passing to methods with + # C++ 'bool*' or 'bool&' arg types + class Boolean + attr_accessor :value + def initialize(b=false) @value = b end + def nil? + return !@value + end + end + + class SignalBlockInvocation < Qt::Object + def initialize(parent, block, args) + super(parent) + self.class.slots "invoke(#{args})" + @block = block + end + + def invoke(*args) + @block.call(*args) + end + end + + class BlockInvocation < Qt::Object + def initialize(target, block, args) + super(target) + self.class.slots "invoke(#{args})" + @target = target + @block = block + end + + def invoke(*args) + @target.instance_exec(*args, &@block) + end + end + + module Internal + @@classes = {} + @@cpp_names = {} + @@idclass = [] + + def Internal.normalize_classname(classname) + if classname =~ /^Qext/ + now = classname.sub(/^Qext(?=[A-Z])/,'Qext::') + elsif classname =~ /^Q/ + now = classname.sub(/^Q(?=[A-Z])/,'Qt::') + elsif classname =~ /^(KConfigSkeleton|KWin)::/ + now = classname.sub(/^K?(?=[A-Z])/,'KDE::') + elsif classname !~ /::/ + now = classname.sub(/^K?(?=[A-Z])/,'KDE::') + else + now = classname + end + # puts "normalize_classname = was::#{classname}, now::#{now}" + now + end + + def Internal.init_class(c) + classname = Qt::Internal::normalize_classname(c) + classId = Qt::Internal.idClass(c) + insert_pclassid(classname, classId) + @@idclass[classId] = classname + @@cpp_names[classname] = c + klass = isQObject(classId) ? create_qobject_class(classname) \ + : create_qt_class(classname) + @@classes[classname] = klass unless klass.nil? + end + + def Internal.debug_level + Qt.debug_level + end + + def Internal.checkarg(argtype, typename) + puts " #{typename} (#{argtype})" if debug_level >= DebugLevel::High + if argtype == 'i' + if typename =~ /^int&?$|^signed int&?$|^signed$|^Q_INT32&?$/ + return 1 + elsif typename =~ /^(?:short|ushort|unsigned short int|uchar|uint|long|ulong|unsigned long int|unsigned|float|double|Q_UINT32|Q_UINT16|Q_INT16)$/ + return 0 + else + t = typename.sub(/^const\s+/, '') + t.sub!(/[&*]$/, '') + if isEnum(t) + return 0 + end + end + elsif argtype == 'n' + if typename =~ /^double$/ + return 2 + elsif typename =~ /^float$/ + return 1 + elsif typename =~ /^int&?$/ + return 0 + elsif typename =~ /^(?:short|ushort|uint|long|ulong|signed|unsigned|float|double)$/ + return 0 + else + t = typename.sub(/^const\s+/, '') + t.sub!(/[&*]$/, '') + if isEnum(t) + return 0 + end + end + elsif argtype == 'B' + if typename =~ /^(?:bool)[*&]?$/ + return 0 + end + elsif argtype == 's' + if typename =~ /^(const )?((QChar)[*&]?)$/ + return 1 + elsif typename =~ /^(?:u?char\*|const u?char\*|(?:const )?(Q(C?)String)[*&]?)$/ + qstring = !$1.nil? + c = ("C" == $2) + return c ? 2 : (qstring ? 3 : 0) + end + elsif argtype == 'a' + # FIXME: shouldn't be hardcoded. Installed handlers should tell what ruby type they expect. + if typename =~ /^(?: + const\ QCOORD\*| + (?:const\ )? + (?: + QStringList[\*&]?| + QValueList<int>[\*&]?| + QRgb\*| + char\*\* + ) + )$/x + return 0 + end + elsif argtype == 'u' + # Give nil matched against string types a higher score than anything else + if typename =~ /^(?:u?char\*|const u?char\*|(?:const )?((Q(C?)String))[*&]?)$/ + return 1 + # Numerics will give a runtime conversion error, so they fail the match + elsif typename =~ /^(?:short|ushort|uint|long|ulong|signed|unsigned|int)$/ + return -99 + else + return 0 + end + elsif argtype == 'U' + if typename =~ /QStringList/ + return 1 + else + return 0 + end + else + t = typename.sub(/^const\s+/, '') + t.sub!(/[&*]$/, '') + if argtype == t + return 1 + elsif classIsa(argtype, t) + return 0 + elsif isEnum(argtype) and + (t =~ /int|Q_INT32|uint|Q_UINT32|long|ulong/ or isEnum(t)) + return 0 + end + end + return -99 + end + + def Internal.find_class(classname) + # puts @@classes.keys.sort.join "\n" + @@classes[classname] + end + + # Runs the initializer as far as allocating the Qt C++ instance. + # Then use a throw to jump back to here with the C++ instance + # wrapped in a new ruby variable of type T_DATA + def Internal.try_initialize(instance, *args) + initializer = instance.method(:initialize) + catch "newqt" do + initializer.call(*args) + end + end + + # If a block was passed to the constructor, then + # run that now. Either run the context of the new instance + # if no args were passed to the block. Or otherwise, + # run the block in the context of the arg. + def Internal.run_initializer_block(instance, block) + if block.arity == -1 + instance.instance_eval(&block) + elsif block.arity == 1 + block.call(instance) + else + raise ArgumentError, "Wrong number of arguments to block(#{block.arity} for 1)" + end + end + + def Internal.do_method_missing(package, method, klass, this, *args) + if klass.class == Module + classname = klass.name + else + classname = @@cpp_names[klass.name] + if classname.nil? + if klass != Object and klass != Qt + return do_method_missing(package, method, klass.superclass, this, *args) + else + return nil + end + end + end + + if method == "new" + method = classname.dup + method.gsub!(/^(KParts|KIO|KNS|DOM|Kontact|Kate|KTextEditor|KConfigSkeleton::ItemEnum|KConfigSkeleton|KWin)::/,"") + end + method = "operator" + method.sub("@","") if method !~ /[a-zA-Z]+/ + # Change foobar= to setFoobar() + method = 'set' + method[0,1].upcase + method[1,method.length].sub("=", "") if method =~ /.*[^-+%\/|=]=$/ + + methods = [] + methods << method.dup + args.each do |arg| + if arg.nil? + # For each nil arg encountered, triple the number of munged method + # templates, in order to cover all possible types that can match nil + temp = [] + methods.collect! do |meth| + temp << meth + '?' + temp << meth + '#' + meth << '$' + end + methods.concat(temp) + elsif isObject(arg) + methods.collect! { |meth| meth << '#' } + elsif arg.kind_of? Array or arg.kind_of? Hash + methods.collect! { |meth| meth << '?' } + else + methods.collect! { |meth| meth << '$' } + end + end + + methodIds = [] + methods.collect { |meth| methodIds.concat( findMethod(classname, meth) ) } + + if method =~ /_/ && methodIds.length == 0 + # If the method name contains underscores, convert to camel case + # form and try again + method.gsub!(/(.)_(.)/) {$1 + $2.upcase} + return do_method_missing(package, method, klass, this, *args) + end + + if debug_level >= DebugLevel::High + puts "classname == #{classname}" + puts ":: method == #{method}" + puts "-> methodIds == #{methodIds.inspect}" + puts "candidate list:" + prototypes = dumpCandidates(methodIds).split("\n") + line_len = (prototypes.collect { |p| p.length }).max + prototypes.zip(methodIds) { + |prototype,id| puts "#{prototype.ljust line_len} (#{id})" + } + end + + chosen = nil + if methodIds.length > 0 + best_match = -1 + methodIds.each do + |id| + puts "matching => #{id}" if debug_level >= DebugLevel::High + current_match = 0 + (0...args.length).each do + |i| + current_match += checkarg( getVALUEtype(args[i]), getTypeNameOfArg(id, i) ) + end + + # Note that if current_match > best_match, then chosen must be nil + if current_match > best_match + best_match = current_match + chosen = id + # Multiple matches are an error; the equality test below _cannot_ be commented out. + # If ambiguous matches occur the problem must be fixed be adjusting the relative + # ranking of the arg types involved in checkarg(). + elsif current_match == best_match + chosen = nil + end + puts "match => #{id} score: #{current_match}" if debug_level >= DebugLevel::High + end + + puts "Resolved to id: #{chosen}" if !chosen.nil? && debug_level >= DebugLevel::High + end + + if debug_level >= DebugLevel::Minimal && chosen.nil? && method !~ /^operator/ + id = find_pclassid(normalize_classname(klass.name)) + hash = findAllMethods(id) + constructor_names = nil + if method == classname + puts "No matching constructor found, possibles:\n" + constructor_names = hash.keys.grep(/^#{classname}/) + else + puts "Possible prototypes:" + constructor_names = hash.keys + end + method_ids = hash.values_at(*constructor_names).flatten + puts dumpCandidates(method_ids) + end + + puts "setCurrentMethod(#{chosen})" if debug_level >= DebugLevel::High + setCurrentMethod(chosen) if chosen + return nil + end + + def Internal.init_all_classes() + Qt::Internal::getClassList().each do |c| + if c == "Qt" + # Don't change Qt to Qt::t, just leave as is + @@cpp_names["Qt"] = c + elsif c != "QInternal" + Qt::Internal::init_class(c) + end + end + + @@classes['Qt::Integer'] = Qt::Integer + @@classes['Qt::Boolean'] = Qt::Boolean + @@classes['Qt::Enum'] = Qt::Enum + end + + def Internal.get_qinteger(num) + return num.value + end + + def Internal.set_qinteger(num, val) + return num.value = val + end + + def Internal.create_qenum(num, type) + return Qt::Enum.new(num, type) + end + + def Internal.get_qenum_type(e) + return e.type + end + + def Internal.get_qboolean(b) + return b.value + end + + def Internal.set_qboolean(b, val) + return b.value = val + end + + def Internal.getAllParents(class_id, res) + getIsa(class_id).each do |s| + c = idClass(s) + res << c + getAllParents(c, res) + end + end + + def Internal.getSignalNames(klass) + meta = Meta[klass.name] || MetaInfo.new(klass) + signal_names = [] + meta.get_signals.each do |signal| + signal_names.push signal.name + end + return signal_names + end + + def Internal.signalInfo(qobject, signal_name) + signals = Meta[qobject.class.name].get_signals + signals.each_with_index do |signal, i| + if signal.name == signal_name + return [signal.full_name, i] + end + end + end + + def Internal.signalAt(qobject, index) + klass = qobject.class + begin + meta = Meta[klass.name] + klass = klass.superclass + end while meta.nil? and klass != Object + meta.get_signals[index].full_name + end + + def Internal.slotAt(qobject, index) + klass = qobject.class + begin + meta = Meta[klass.name] + klass = klass.superclass + end while meta.nil? and klass != Object + meta.get_slots[index].full_name + end + + def Internal.getMocArguments(member) + argStr = member.sub(/.*\(/, '').sub(/\)$/, '') + args = argStr.scan(/([^,]*<[^>]+>)|([^,]+)/) + mocargs = allocateMocArguments(args.length) + args.each_with_index do |arg, i| + arg = arg.to_s + a = arg.sub(/^const\s+/, '') + a = (a =~ /^(bool|int|double|char\*|QString)&?$/) ? $1 : 'ptr' + valid = setMocType(mocargs, i, arg, a) + end + result = [] + result << args.length << mocargs + result + end + + def Internal.makeMetaData(data) + return nil if data.nil? + tbl = [] + data.each do |entry| + name = entry.name + argStr = entry.arg_types + params = [] + args = argStr.scan(/[^,]+/) + args.each do |arg| + name = '' # umm.. is this the aim?, well. it works. soo... ;-) + param = make_QUParameter(name, arg, 0, 1) + params << param + end + method = make_QUMethod(name, params) + tbl << make_QMetaData(entry.full_name, method) + end + make_QMetaData_tbl(tbl) + end + + def Internal.getMetaObject(qobject) + klass = qobject.class + begin + meta = Meta[klass.name] + klass = klass.superclass + end while meta.nil? and klass != Object + + return nil if meta.nil? + + if meta.metaobject.nil? or meta.changed + slots = meta.get_slots + slotTable = makeMetaData(slots) + signals = meta.get_signals + signalTable = makeMetaData(signals) + meta.metaobject = make_metaObject(qobject.class.name, + qobject.staticMetaObject(), + slotTable, + slots.length, + signalTable, + signals.length) + addSignalMethods(qobject.class, getSignalNames(qobject.class)) + meta.changed = false + end + + meta.metaobject + end + + def Internal.connect(src, signal, target, block) + signature = (signal =~ /\((.*)\)/) ? $1 : "" + return Qt::Object.connect( src, + signal, + Qt::BlockInvocation.new(target, block, signature), + SLOT("invoke(#{signature})") ) + end + + def Internal.signal_connect(src, signal, block) + signature = (signal =~ /\((.*)\)/) ? $1 : "" + return Qt::Object.connect( src, + signal, + Qt::SignalBlockInvocation.new(src, block, signature), + SLOT("invoke(#{signature})") ) + end + end # Qt::Internal + + Meta = {} + + # An entry for each signal or slot + # Example + # foobar(QString,bool) + # :name is 'foobar' + # :full_name is 'foobar(QString,bool)' + # :arg_types is 'QString,bool' + QObjectMember = Struct.new :name, :full_name, :arg_types + + class MetaInfo + attr_accessor :signals, :slots, :metaobject, :mocargs, :changed + def initialize(klass) + Meta[klass.name] = self + @klass = klass + @metaobject = nil + @signals = [] + @slots = [] + @changed = false + Internal.addMetaObjectMethods(klass) + end + + def add_signals(signal_list) + signal_list.each do |signal| + if signal.kind_of? Symbol + signal = signal.to_s + "()" + end + signal = Qt::Object.normalizeSignalSlot(signal) + if signal =~ /([^\s]*)\((.*)\)/ + @signals.push QObjectMember.new($1, signal, $2) + else + qWarning( "#{@klass.name}: Invalid signal format: '#{signal}'" ) + end + end + end + + # Return a list of signals, including inherited ones + def get_signals + all_signals = [] + current = @klass + while current != Qt::Base + meta = Meta[current.name] + if !meta.nil? + all_signals.concat meta.signals + end + current = current.superclass + end + return all_signals + end + + def add_slots(slot_list) + slot_list.each do |slot| + if slot.kind_of? Symbol + slot = slot.to_s + "()" + end + slot = Qt::Object.normalizeSignalSlot(slot) + if slot =~ /([^\s]*)\((.*)\)/ + @slots.push QObjectMember.new($1, slot, $2) + else + qWarning( "#{@klass.name}: Invalid slot format: '#{slot}'" ) + end + end + end + + # Return a list of slots, including inherited ones + def get_slots + all_slots = [] + current = @klass + while current != Qt::Base + meta = Meta[current.name] + if !meta.nil? + all_slots.concat meta.slots + end + current = current.superclass + end + return all_slots + end + end # Qt::MetaInfo + + IO_Direct = 0x0100 + IO_Sequential = 0x0200 + IO_Combined = 0x0300 + IO_TypeMask = 0x0f00 + IO_Raw = 0x0040 + IO_Async = 0x0080 + IO_ReadOnly = 0x0001 + IO_WriteOnly = 0x0002 + IO_ReadWrite = 0x0003 + IO_Append = 0x0004 + IO_Truncate = 0x0008 + IO_Translate = 0x0010 + IO_ModeMask = 0x00ff + IO_Open = 0x1000 + IO_StateMask = 0xf000 + IO_Ok = 0 + IO_ReadError = 1 + IO_WriteError = 2 + IO_FatalError = 3 + IO_ResourceError = 4 + IO_OpenError = 5 + IO_ConnectError = 5 + IO_AbortError = 6 + IO_TimeOutError = 7 + IO_UnspecifiedError= 8 + +end # Qt + +class Object + def SIGNAL(signal) + if signal.kind_of? Symbol + return "2" + signal.to_s + "()" + else + return "2" + signal + end + end + + def SLOT(slot) + if slot.kind_of? Symbol + return "1" + slot.to_s + "()" + else + return "1" + slot + end + end + + def emit(signal) + return signal + end + + # See the discussion here: http://eigenclass.org/hiki.rb?instance_exec + # about implementations of the ruby 1.9 method instance_exec(). This + # version is the one from Rails. It isn't thread safe, but that doesn't + # matter for the intended use in invoking blocks as Qt slots. + def instance_exec(*arguments, &block) + block.bind(self)[*arguments] + end +end + +class Proc + # Part of the Rails Object#instance_exec implementation + def bind(object) + block, time = self, Time.now + (class << object; self end).class_eval do + method_name = "__bind_#{time.to_i}_#{time.usec}" + define_method(method_name, &block) + method = instance_method(method_name) + remove_method(method_name) + method + end.bind(object) + end +end + +class Module + alias_method :_constants, :constants + alias_method :_instance_methods, :instance_methods + alias_method :_protected_instance_methods, :protected_instance_methods + alias_method :_public_instance_methods, :public_instance_methods + + private :_constants, :_instance_methods + private :_protected_instance_methods, :_public_instance_methods + + def constants + qt_methods(_constants, 0x10, true) + end + + def instance_methods(inc_super=true) + qt_methods(_instance_methods(inc_super), 0x0, inc_super) + end + + def protected_instance_methods(inc_super=true) + qt_methods(_protected_instance_methods(inc_super), 0x80, inc_super) + end + + def public_instance_methods(inc_super=true) + qt_methods(_public_instance_methods(inc_super), 0x0, inc_super) + end + + private + def qt_methods(meths, flags, inc_super=true) + if !self.kind_of? Class + return meths + end + + klass = self + classid = 0 + loop do + classid = Qt::Internal::find_pclassid(klass.name) + break if classid > 0 + + klass = klass.superclass + if klass.nil? + return meths + end + end + + # These methods are all defined in Qt::Base, even if they aren't supported by a particular + # subclass, so remove them to avoid confusion + meths -= ["%", "&", "*", "**", "+", "-", "-@", "/", "<", "<<", "<=", ">", ">=", ">>", "|", "~", "^"] + ids = [] + if inc_super + Qt::Internal::getAllParents(classid, ids) + end + ids << classid + ids.each { |c| Qt::Internal::findAllMethodNames(meths, c, flags) } + return meths.uniq + end +end diff --git a/qtruby/rubylib/qtruby/marshall.h b/qtruby/rubylib/qtruby/marshall.h new file mode 100644 index 00000000..cf20b86d --- /dev/null +++ b/qtruby/rubylib/qtruby/marshall.h @@ -0,0 +1,46 @@ +#ifndef MARSHALL_H +#define MARSHALL_H + +#include <smoke.h> +#include <ruby.h> + +class SmokeType; + +class Marshall { +public: + /** + * FromVALUE is used for virtual function return values and regular + * method arguments. + * + * ToVALUE is used for method return-values and virtual function + * arguments. + */ + typedef void (*HandlerFn)(Marshall *); + enum Action { FromVALUE, ToVALUE }; + virtual SmokeType type() = 0; + virtual Action action() = 0; + virtual Smoke::StackItem &item() = 0; + virtual VALUE * var() = 0; + virtual void unsupported() = 0; + virtual Smoke *smoke() = 0; + /** + * For return-values, next() does nothing. + * For FromRV, next() calls the method and returns. + * For ToRV, next() calls the virtual function and returns. + * + * Required to reset Marshall object to the state it was + * before being called when it returns. + */ + virtual void next() = 0; + /** + * For FromSV, cleanup() returns false when the handler should free + * any allocated memory after next(). + * + * For ToSV, cleanup() returns true when the handler should delete + * the pointer passed to it. + */ + virtual bool cleanup() = 0; + + virtual ~Marshall() {} +}; +#endif diff --git a/qtruby/rubylib/qtruby/qtruby.h b/qtruby/rubylib/qtruby/qtruby.h new file mode 100644 index 00000000..66f9fedc --- /dev/null +++ b/qtruby/rubylib/qtruby/qtruby.h @@ -0,0 +1,55 @@ +/*************************************************************************** + qtruby.h - description + ------------------- + begin : Fri Jul 4 2003 + copyright : (C) 2003 by Richard Dale + email : Richard_Dale@tipitina.demon.co.uk + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QTRUBY_H +#define QTRUBY_H + +#include "marshall.h" + +struct smokeruby_object { + bool allocated; + Smoke *smoke; + int classId; + void *ptr; +}; + +struct TypeHandler { + const char *name; + Marshall::HandlerFn fn; +}; + +extern int do_debug; // evil +extern VALUE rv_qapp; +extern int object_count; + +// keep this enum in sync with lib/Qt/debug.pm + +enum QtDebugChannel { + qtdb_none = 0x00, + qtdb_ambiguous = 0x01, + qtdb_method_missing = 0x02, + qtdb_calls = 0x04, + qtdb_gc = 0x08, + qtdb_virtual = 0x10, + qtdb_verbose = 0x20 +}; + +void unmapPointer(smokeruby_object *, Smoke::Index, void*); +smokeruby_object *value_obj_info(VALUE value); +VALUE getPointerObject(void *ptr); + +#endif diff --git a/qtruby/rubylib/qtruby/smokeruby.h b/qtruby/rubylib/qtruby/smokeruby.h new file mode 100644 index 00000000..8f152c10 --- /dev/null +++ b/qtruby/rubylib/qtruby/smokeruby.h @@ -0,0 +1,321 @@ +#ifndef SMOKERUBY_H +#define SMOKERUBY_H + +#include "smoke.h" + +#undef DEBUG +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#ifndef __USE_POSIX +#define __USE_POSIX +#endif +#ifndef __USE_XOPEN +#define __USE_XOPEN +#endif +#include "ruby.h" + +#include <qcstring.h> + +#include "qtruby.h" +#include "marshall.h" + + +class SmokeRuby; + +class SmokeType { + Smoke::Type *_t; // derived from _smoke and _id, but cached + + Smoke *_smoke; + Smoke::Index _id; +public: + SmokeType() : _t(0), _smoke(0), _id(0) {} + SmokeType(Smoke *s, Smoke::Index i) : _smoke(s), _id(i) { + if(_id < 0 || _id > _smoke->numTypes) _id = 0; + _t = _smoke->types + _id; + } + // default copy constructors are fine, this is a constant structure + + // mutators + void set(Smoke *s, Smoke::Index i) { + _smoke = s; + _id = i; + _t = _smoke->types + _id; + } + + // accessors + Smoke *smoke() const { return _smoke; } + Smoke::Index typeId() const { return _id; } + const Smoke::Type &type() const { return *_t; } + unsigned short flags() const { return _t->flags; } + unsigned short elem() const { return _t->flags & Smoke::tf_elem; } + const char *name() const { return _t->name; } + Smoke::Index classId() const { return _t->classId; } + + // tests + bool isStack() const { return ((flags() & Smoke::tf_ref) == Smoke::tf_stack); } + bool isPtr() const { return ((flags() & Smoke::tf_ref) == Smoke::tf_ptr); } + bool isRef() const { return ((flags() & Smoke::tf_ref) == Smoke::tf_ref); } + bool isConst() const { return (flags() & Smoke::tf_const); } + bool isClass() const { + if(elem() == Smoke::t_class) + return classId() ? true : false; + return false; + } + + bool operator ==(const SmokeType &b) const { + const SmokeType &a = *this; + if(a.name() == b.name()) return true; + if(a.name() && b.name() && qstrcmp(a.name(), b.name()) == 0) + return true; + return false; + } + bool operator !=(const SmokeType &b) const { + const SmokeType &a = *this; + return !(a == b); + } + +}; + +class SmokeClass { + Smoke::Class *_c; + Smoke *_smoke; + Smoke::Index _id; +public: + SmokeClass(const SmokeType &t) { + _smoke = t.smoke(); + _id = t.classId(); + _c = _smoke->classes + _id; + } + SmokeClass(Smoke *smoke, Smoke::Index id) : _smoke(smoke), _id(id) { + _c = _smoke->classes + _id; + } + + Smoke *smoke() const { return _smoke; } + const Smoke::Class &c() const { return *_c; } + Smoke::Index classId() const { return _id; } + const char *className() const { return _c->className; } + Smoke::ClassFn classFn() const { return _c->classFn; } + Smoke::EnumFn enumFn() const { return _c->enumFn; } + bool operator ==(const SmokeClass &b) const { + const SmokeClass &a = *this; + if(a.className() == b.className()) return true; + if(a.className() && b.className() && qstrcmp(a.className(), b.className()) == 0) + return true; + return false; + } + bool operator !=(const SmokeClass &b) const { + const SmokeClass &a = *this; + return !(a == b); + } + bool isa(const SmokeClass &sc) const { + // This is a sick function, if I do say so myself + if(*this == sc) return true; + Smoke::Index *parents = _smoke->inheritanceList + _c->parents; + for(int i = 0; parents[i]; i++) { + if(SmokeClass(_smoke, parents[i]).isa(sc)) return true; + } + return false; + } + + unsigned short flags() const { return _c->flags; } + bool hasConstructor() const { return flags() & Smoke::cf_constructor; } + bool hasCopy() const { return flags() & Smoke::cf_deepcopy; } + bool hasVirtual() const { return flags() & Smoke::cf_virtual; } + bool hasFire() const { return !(flags() & Smoke::cf_undefined); } +}; + +class SmokeMethod { + Smoke::Method *_m; + Smoke *_smoke; + Smoke::Index _id; +public: + SmokeMethod(Smoke *smoke, Smoke::Index id) : _smoke(smoke), _id(id) { + _m = _smoke->methods + _id; + } + + Smoke *smoke() const { return _smoke; } + const Smoke::Method &m() const { return *_m; } + SmokeClass c() const { return SmokeClass(_smoke, _m->classId); } + const char *name() const { return _smoke->methodNames[_m->name]; } + int numArgs() const { return _m->numArgs; } + unsigned short flags() const { return _m->flags; } + SmokeType arg(int i) const { + if(i >= numArgs()) return SmokeType(); + return SmokeType(_smoke, _smoke->argumentList[_m->args + i]); + } + SmokeType ret() const { return SmokeType(_smoke, _m->ret); } + Smoke::Index methodId() const { return _id; } + Smoke::Index method() const { return _m->method; } + + bool isStatic() const { return flags() & Smoke::mf_static; } + bool isConst() const { return flags() & Smoke::mf_const; } + + void call(Smoke::Stack args, void *ptr = 0) const { + Smoke::ClassFn fn = c().classFn(); + (*fn)(method(), ptr, args); + } +}; + +class Smoke_MAGIC { // to be rewritten + SmokeClass _c; + void *_ptr; + bool _isAllocated; +public: + Smoke_MAGIC(void *p, const SmokeClass &c) : + _c(c), _ptr(p), _isAllocated(false) {} + const SmokeClass &c() const { return _c; } + void *ptr() const { return _ptr; } + bool isAllocated() const { return _isAllocated; } + void setAllocated(bool isAllocated) { _isAllocated = isAllocated; } +}; + + +/** + * SmokeObject is a thin wrapper around VALUE objects. Each SmokeObject instance + * increments the refcount of its VALUE for the duration of its existance. + * + * SmokeObject instances are only returned from SmokeRuby, since the method + * of binding data to the scalar must be consistent across all modules. + */ +class SmokeObject { + VALUE rv; + Smoke_MAGIC *m; + +public: + SmokeObject(VALUE obj, Smoke_MAGIC *mag) : rv(obj), m(mag) { + rb_gc_register_address(&rv); + } + + ~SmokeObject() { + rb_gc_unregister_address(&rv); + } + + SmokeObject(const SmokeObject &other) { + rv = other.rv; + m = other.m; + rb_gc_register_address(&rv); + } + + SmokeObject &operator =(const SmokeObject &other) { + rv = other.rv; + m = other.m; + rb_gc_register_address(&rv); + return *this; + } + + const SmokeClass &c() { return m->c(); } + Smoke *smoke() { return c().smoke(); } + VALUE * var() { return &rv; } + void *ptr() { return m->ptr(); } + Smoke::Index classId() { return c().classId(); } + void *cast(const SmokeClass &toc) { + return smoke()->cast( + ptr(), + classId(), + smoke()->idClass(toc.className()) + ); + } + const char *className() { return c().className(); } + + bool isValid() const { return !NIL_P(rv); } + bool isAllocated() const { return m->isAllocated(); } + void setAllocated(bool i) { m->setAllocated(i); } +}; + + +/** + * Since it's not easy to share functions between Ruby modules, the common + * interface between all Smoked libraries and Ruby will be defined in this + * class. There will be only one SmokeRuby instance loaded for an entire Ruby + * process. It has no data members here -- this is only an abstract interface. + */ + +class SmokeRuby { + void *future_extension; +public: + SmokeRuby() : future_extension(0) {} + + // don't need this, we're only defining an interface + virtual ~SmokeRuby() = 0; + + /** + * Registers a Smoke object + */ + virtual void registerSmoke(const char *name, Smoke *smoke) = 0; + + /** + * Gets a smoke object from its name + */ + virtual Smoke *getSmoke(const char *name) = 0; + + /** + * Determines if the named smoke is registered. + */ + bool isSmokeRegistered(const char *name) { return getSmoke(name) ? true : false; } + + virtual void registerHandlers(TypeHandler *handlers) = 0; + + /** + * Returns a new blessed SV referring to the pointer passed. + * Use sv_2mortal() before passing it around. + * + * @param p pointer to the C++ object. The pointer isn't automatically deleted by SmokePerl. + * @param c class of the pointer + * @see #getObject + * @see #deleteObject + */ + virtual SmokeObject newObject(void *p, const SmokeClass &c) = 0; + + /** + * Same as newObject(), except it doesn't treat p as owned by Perl + */ + virtual SmokeObject wrapObject(void *p, const SmokeClass &c) = 0; + + /** + * Any SV* created with newObject() on a class with virtual methods can be + * retrieved again. + */ + virtual SmokeObject getObject(void *p) = 0; + + /** + * Create a SmokeObject from the given VALUE + */ + virtual SmokeObject getObject(VALUE value) = 0; +}; + +/* + * Type handling by moc is simple. + * + * If the type name matches /^(?:const\s+)?\Q$types\E&?$/, use the + * static_QUType, where $types is join('|', qw(bool int double char* QString); + * + * Everything else is passed as a pointer! There are types which aren't + * Smoke::tf_ptr but will have to be passed as a pointer. Make sure to keep + * track of what's what. + */ + +/* + * Simply using typeids isn't enough for signals/slots. It will be possible + * to declare signals and slots which use arguments which can't all be + * found in a single smoke object. Instead, we need to store smoke => typeid + * pairs. We also need additional informatation, such as whether we're passing + * a pointer to the union element. + */ + +enum MocArgumentType { + xmoc_ptr, + xmoc_bool, + xmoc_int, + xmoc_double, + xmoc_charstar, + xmoc_QString +}; + +struct MocArgument { + // smoke object and associated typeid + SmokeType st; + MocArgumentType argType; +}; + +#endif // SMOKERUBY_H diff --git a/qtruby/rubylib/tutorial/t1/t1.rb b/qtruby/rubylib/tutorial/t1/t1.rb new file mode 100755 index 00000000..19d8a029 --- /dev/null +++ b/qtruby/rubylib/tutorial/t1/t1.rb @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby +$VERBOSE = true; $:.unshift File.dirname($0) + +require 'Qt' + +a = Qt::Application.new(ARGV) +hello = Qt::PushButton.new('Hello World!', nil) +hello.resize(100, 30) +a.setMainWidget(hello) +hello.show() +a.exec() diff --git a/qtruby/rubylib/tutorial/t10/cannon.rb b/qtruby/rubylib/tutorial/t10/cannon.rb new file mode 100644 index 00000000..3c02f8ff --- /dev/null +++ b/qtruby/rubylib/tutorial/t10/cannon.rb @@ -0,0 +1,71 @@ +require 'Qt' + +class CannonField < Qt::Widget + signals 'angleChanged(int)', 'forceChanged(int)' + slots 'setAngle(int)', 'setForce(int)' + + def initialize(parent, name) + super + @ang = 45 + @f = 0 + setPalette( Qt::Palette.new( Qt::Color.new( 250, 250, 200) ) ) + end + + def setAngle( degrees ) + if degrees < 5 + degrees = 5 + elsif degrees > 70 + degrees = 70 + end + if @ang == degrees + return + end + @ang = degrees + repaint() + emit angleChanged( @ang ) + end + + def setForce( newton ) + if newton < 0 + newton = 0 + end + if @f == newton + return + end + @f = newton + emit forceChanged( @f ) + end + + def paintEvent( e ) + if !e.rect().intersects( cannonRect() ) + return + end + + cr = cannonRect() + pix = Qt::Pixmap.new( cr.size() ) + pix.fill( self, cr.topLeft() ) + + p = Qt::Painter.new( pix ) + p.setBrush( blue ) + p.setPen( Qt::NoPen ) + p.translate( 0, pix.height() - 1 ) + p.drawPie( Qt::Rect.new(-35, -35, 70, 70), 0, 90*16 ) + p.rotate( - @ang ) + p.drawRect( Qt::Rect.new(33, -4, 15, 8) ) + p.end() + + p.begin(self) + p.drawPixmap(cr.topLeft(), pix ) + p.end() + end + + def cannonRect() + r = Qt::Rect.new( 0, 0, 50, 50) + r.moveBottomLeft( rect().bottomLeft() ) + return r + end + + def sizePolicy() + return Qt::SizePolicy.new( Qt::SizePolicy::Expanding, Qt::SizePolicy::Expanding ) + end +end diff --git a/qtruby/rubylib/tutorial/t10/lcdrange.rb b/qtruby/rubylib/tutorial/t10/lcdrange.rb new file mode 100644 index 00000000..a0adc842 --- /dev/null +++ b/qtruby/rubylib/tutorial/t10/lcdrange.rb @@ -0,0 +1,35 @@ +require 'Qt' + +class LCDRange < Qt::VBox + signals 'valueChanged(int)' + slots 'setValue(int)', 'setRange(int, int)' + + def initialize(parent, name) + super + lcd = Qt::LCDNumber.new(2, self, 'lcd') + @slider = Qt::Slider.new(Qt::VBox::Horizontal, self, 'slider') + @slider.setRange(0, 99) + @slider.setValue(0) + connect(@slider, SIGNAL('valueChanged(int)'), lcd, SLOT('display(int)')) + connect(@slider, SIGNAL('valueChanged(int)'), SIGNAL('valueChanged(int)')) + setFocusProxy(@slider) + end + + def value() + @slider.value() + end + + def setValue( value ) + @slider.setValue( value ) + end + + def setRange( minVal, maxVal ) + if minVal < 0 || maxVal > 99 || minVal > maxVal + qWarning( "LCDRange::setRange(#{minVal},#{maxVal})\n" + + "\tRange must be 0..99\n" + + "\tand minVal must not be greater than maxVal" ) + return + end + @slider.setRange( minVal, maxVal ) + end +end diff --git a/qtruby/rubylib/tutorial/t10/t10.rb b/qtruby/rubylib/tutorial/t10/t10.rb new file mode 100755 index 00000000..4168d507 --- /dev/null +++ b/qtruby/rubylib/tutorial/t10/t10.rb @@ -0,0 +1,57 @@ +#!/usr/bin/env ruby +$VERBOSE = true; $:.unshift File.dirname($0) + +require 'Qt' +require 'lcdrange.rb' +require 'cannon.rb' + +class MyWidget < Qt::Widget + def initialize() + super + quit = Qt::PushButton.new('Quit', self, 'quit') + quit.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold)) + + connect(quit, SIGNAL('clicked()'), $qApp, SLOT('quit()')) + + angle = LCDRange.new( self, 'angle' ) + angle.setRange( 5, 70 ) + + force = LCDRange.new( self, 'force' ) + force.setRange( 10, 50 ) + + cannonField = CannonField.new( self, 'cannonField' ) + + connect( angle, SIGNAL('valueChanged(int)'), + cannonField, SLOT('setAngle(int)') ) + connect( cannonField, SIGNAL('angleChanged(int)'), + angle, SLOT('setValue(int)') ) + + connect( force, SIGNAL('valueChanged(int)'), + cannonField, SLOT('setForce(int)') ) + connect( cannonField, SIGNAL('forceChanged(int)'), + force, SLOT('setValue(int)') ) + + grid = Qt::GridLayout.new( self, 2, 2, 10 ) + grid.addWidget( quit, 0, 0 ) + grid.addWidget( cannonField, 1, 1 ) + grid.setColStretch( 1, 10 ) + + leftBox = Qt::VBoxLayout.new() + grid.addLayout( leftBox, 1, 0 ) + leftBox.addWidget( angle ) + leftBox.addWidget( force ) + + angle.setValue( 60 ) + force.setValue( 25 ) + angle.setFocus() + end +end + +Qt::Application.setColorSpec( Qt::Application::CustomColor ) +a = Qt::Application.new(ARGV) + +w = MyWidget.new +w.setGeometry( 100, 100, 500, 355 ) +a.setMainWidget(w) +w.show +a.exec diff --git a/qtruby/rubylib/tutorial/t11/cannon.rb b/qtruby/rubylib/tutorial/t11/cannon.rb new file mode 100644 index 00000000..e9446b02 --- /dev/null +++ b/qtruby/rubylib/tutorial/t11/cannon.rb @@ -0,0 +1,137 @@ +include Math +require 'Qt' + +class CannonField < Qt::Widget + + signals 'angleChanged(int)', 'forceChanged(int)' + slots 'setAngle(int)', 'setForce(int)', 'shoot()', 'moveShot()' + + def initialize(parent, name) + super + @ang = 45 + @f = 0 + @timerCount = 0; + @autoShootTimer = Qt::Timer.new( self, 'movement handler' ) + connect( @autoShootTimer, SIGNAL('timeout()'), + self, SLOT('moveShot()') ); + @shoot_ang = 0 + @shoot_f = 0 + setPalette( Qt::Palette.new( Qt::Color.new( 250, 250, 200) ) ) + @barrelRect = Qt::Rect.new(33, -4, 15, 8) + end + + def setAngle( degrees ) + if degrees < 5 + degrees = 5 + elsif degrees > 70 + degrees = 70 + end + if @ang == degrees + return + end + @ang = degrees + repaint( cannonRect(), false ) + emit angleChanged( @ang ) + end + + def setForce( newton ) + if newton < 0 + newton = 0 + end + if @f == newton + return + end + @f = newton + emit forceChanged( @f ) + end + + def shoot() + if @autoShootTimer.isActive() + return + end; + @timerCount = 0 + @shoot_ang = @ang + @shoot_f = @f + @autoShootTimer.start( 50 ) + end + + def moveShot() + r = Qt::Region.new( shotRect() ) + @timerCount += 1 + + shotR = shotRect() + + if shotR.x() > width() || shotR.y() > height() + @autoShootTimer.stop() + else + r = r.unite( Qt::Region.new( shotR ) ) + end + repaint( r ) + end + + def paintEvent( e ) + updateR = e.rect() + p = Qt::Painter.new( self ) + + if updateR.intersects( cannonRect() ) + paintCannon( p ) + end + if @autoShootTimer.isActive() && + updateR.intersects( shotRect() ) + paintShot( p ) + end + p.end() + end + + def paintShot( p ) + p.setBrush( black ) + p.setPen( Qt::NoPen ) + p.drawRect( shotRect() ) + end + + def paintCannon(p) + cr = cannonRect() + pix = Qt::Pixmap.new( cr.size() ) + pix.fill( self, cr.topLeft() ) + + tmp = Qt::Painter.new( pix ) + tmp.setBrush( blue ) + tmp.setPen( Qt::NoPen ) + tmp.translate( 0, pix.height() - 1 ) + tmp.drawPie( Qt::Rect.new(-35, -35, 70, 70), 0, 90*16 ) + tmp.rotate( - @ang ) + tmp.drawRect( @barrelRect ) + tmp.end() + + p.drawPixmap(cr.topLeft(), pix ) + end + + def cannonRect() + r = Qt::Rect.new( 0, 0, 50, 50) + r.moveBottomLeft( rect().bottomLeft() ) + return r + end + + def shotRect() + gravity = 4.0 + + time = @timerCount / 4.0 + velocity = @shoot_f + radians = @shoot_ang*3.14159265/180.0 + + velx = velocity*cos( radians ) + vely = velocity*sin( radians ) + x0 = ( @barrelRect.right() + 5.0 )*cos(radians) + y0 = ( @barrelRect.right() + 5.0 )*sin(radians) + x = x0 + velx*time + y = y0 + vely*time - 0.5*gravity*time*time + + r = Qt::Rect.new( 0, 0, 6, 6 ); + r.moveCenter( Qt::Point.new( x.round, height() - 1 - y.round ) ) + return r + end + + def sizePolicy() + return Qt::SizePolicy.new( Qt::SizePolicy::Expanding, Qt::SizePolicy::Expanding ) + end +end diff --git a/qtruby/rubylib/tutorial/t11/lcdrange.rb b/qtruby/rubylib/tutorial/t11/lcdrange.rb new file mode 100644 index 00000000..a0adc842 --- /dev/null +++ b/qtruby/rubylib/tutorial/t11/lcdrange.rb @@ -0,0 +1,35 @@ +require 'Qt' + +class LCDRange < Qt::VBox + signals 'valueChanged(int)' + slots 'setValue(int)', 'setRange(int, int)' + + def initialize(parent, name) + super + lcd = Qt::LCDNumber.new(2, self, 'lcd') + @slider = Qt::Slider.new(Qt::VBox::Horizontal, self, 'slider') + @slider.setRange(0, 99) + @slider.setValue(0) + connect(@slider, SIGNAL('valueChanged(int)'), lcd, SLOT('display(int)')) + connect(@slider, SIGNAL('valueChanged(int)'), SIGNAL('valueChanged(int)')) + setFocusProxy(@slider) + end + + def value() + @slider.value() + end + + def setValue( value ) + @slider.setValue( value ) + end + + def setRange( minVal, maxVal ) + if minVal < 0 || maxVal > 99 || minVal > maxVal + qWarning( "LCDRange::setRange(#{minVal},#{maxVal})\n" + + "\tRange must be 0..99\n" + + "\tand minVal must not be greater than maxVal" ) + return + end + @slider.setRange( minVal, maxVal ) + end +end diff --git a/qtruby/rubylib/tutorial/t11/t11.rb b/qtruby/rubylib/tutorial/t11/t11.rb new file mode 100755 index 00000000..8656f6f5 --- /dev/null +++ b/qtruby/rubylib/tutorial/t11/t11.rb @@ -0,0 +1,67 @@ +#!/usr/bin/env ruby +$VERBOSE = true; $:.unshift File.dirname($0) + +require 'Qt' +require 'lcdrange.rb' +require 'cannon.rb' + +class MyWidget < Qt::Widget + def initialize() + super + quit = Qt::PushButton.new('Quit', self, 'quit') + quit.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold)) + + connect(quit, SIGNAL('clicked()'), $qApp, SLOT('quit()')) + + angle = LCDRange.new( self, 'angle' ) + angle.setRange( 5, 70 ) + + force = LCDRange.new( self, 'force' ) + force.setRange( 10, 50 ) + + cannonField = CannonField.new( self, 'cannonField' ) + + connect( angle, SIGNAL('valueChanged(int)'), + cannonField, SLOT('setAngle(int)') ) + connect( cannonField, SIGNAL('angleChanged(int)'), + angle, SLOT('setValue(int)') ) + + connect( force, SIGNAL('valueChanged(int)'), + cannonField, SLOT('setForce(int)') ) + connect( cannonField, SIGNAL('forceChanged(int)'), + force, SLOT('setValue(int)') ) + + shoot = Qt::PushButton.new( '&Shoot', self, 'shoot' ) + shoot.setFont( Qt::Font.new( 'Times', 18, Qt::Font::Bold ) ) + + connect( shoot, SIGNAL('clicked()'), cannonField, SLOT('shoot()') ) + + grid = Qt::GridLayout.new( self, 2, 2, 10 ) + grid.addWidget( quit, 0, 0 ) + grid.addWidget( cannonField, 1, 1 ) + grid.setColStretch( 1, 10 ) + + leftBox = Qt::VBoxLayout.new() + grid.addLayout( leftBox, 1, 0 ) + leftBox.addWidget( angle ) + leftBox.addWidget( force ) + + topBox = Qt::HBoxLayout.new() + grid.addLayout( topBox, 0, 1 ) + topBox.addWidget( shoot ) + topBox.addStretch( 1 ) + + angle.setValue( 60 ) + force.setValue( 25 ) + angle.setFocus() + end +end + +Qt::Application.setColorSpec( Qt::Application::CustomColor ) +a = Qt::Application.new(ARGV) + +w = MyWidget.new +w.setGeometry( 100, 100, 500, 355 ) +a.setMainWidget(w) +w.show +a.exec diff --git a/qtruby/rubylib/tutorial/t12/cannon.rb b/qtruby/rubylib/tutorial/t12/cannon.rb new file mode 100644 index 00000000..1b72cbc0 --- /dev/null +++ b/qtruby/rubylib/tutorial/t12/cannon.rb @@ -0,0 +1,173 @@ +include Math +require 'Qt' + +class CannonField < Qt::Widget + + signals 'hit()', 'missed()', 'angleChanged(int)', 'forceChanged(int)' + slots 'setAngle(int)', 'setForce(int)', 'shoot()', 'moveShot()', 'newTarget()' + + def initialize(parent, name) + super + @ang = 45 + @f = 0 + @timerCount = 0; + @autoShootTimer = Qt::Timer.new( self, 'movement handler' ) + connect( @autoShootTimer, SIGNAL('timeout()'), + self, SLOT('moveShot()') ); + @shoot_ang = 0 + @shoot_f = 0 + @target = Qt::Point.new(0, 0) + setPalette( Qt::Palette.new( Qt::Color.new( 250, 250, 200) ) ) + newTarget() + @barrelRect = Qt::Rect.new(33, -4, 15, 8) + end + + def setAngle( degrees ) + if degrees < 5 + degrees = 5 + elsif degrees > 70 + degrees = 70 + end + if @ang == degrees + return + end + @ang = degrees + repaint( cannonRect(), false ) + emit angleChanged( @ang ) + end + + def setForce( newton ) + if newton < 0 + newton = 0 + end + if @f == newton + return + end + @f = newton + emit forceChanged( @f ) + end + + def shoot() + if @autoShootTimer.isActive() + return + end; + @timerCount = 0 + @shoot_ang = @ang + @shoot_f = @f + @autoShootTimer.start( 50 ) + end + + @@first_time = true + + def newTarget() + if @@first_time + @@first_time = false + midnight = Qt::Time.new( 0, 0, 0 ) + srand( midnight.secsTo(Qt::Time.currentTime()) ) + end + r = Qt::Region.new( targetRect() ) + @target = Qt::Point.new( 200 + rand(190), + 10 + rand(255) ) + repaint( r.unite( Qt::Region.new(targetRect()) ) ) + end + + def moveShot() + r = Qt::Region.new( shotRect() ) + @timerCount += 1 + + shotR = shotRect() + + if shotR.intersects( targetRect() ) + @autoShootTimer.stop() + emit hit() + elsif shotR.x() > width() || shotR.y() > height() + @autoShootTimer.stop() + emit missed() + else + r = r.unite( Qt::Region.new( shotR ) ) + end + + repaint( r ) + end + + def paintEvent( e ) + updateR = e.rect() + p = Qt::Painter.new( self ) + + if updateR.intersects( cannonRect() ) + paintCannon( p ) + end + if @autoShootTimer.isActive() && + updateR.intersects( shotRect() ) + paintShot( p ) + end + if updateR.intersects( targetRect() ) + paintTarget( p ) + end + p.end() + end + + def paintShot( p ) + p.setBrush( black ) + p.setPen( Qt::NoPen ) + p.drawRect( shotRect() ) + end + + def paintTarget( p ) + p.setBrush( red ) + p.setPen( black ) + p.drawRect( targetRect() ) + end + + def paintCannon(p) + cr = cannonRect() + pix = Qt::Pixmap.new( cr.size() ) + pix.fill( self, cr.topLeft() ) + + tmp = Qt::Painter.new( pix ) + tmp.setBrush( blue ) + tmp.setPen( Qt::NoPen ) + tmp.translate( 0, pix.height() - 1 ) + tmp.drawPie( Qt::Rect.new(-35, -35, 70, 70), 0, 90*16 ) + tmp.rotate( - @ang ) + tmp.drawRect( @barrelRect ) + tmp.end() + + p.drawPixmap(cr.topLeft(), pix ) + end + + def cannonRect() + r = Qt::Rect.new( 0, 0, 50, 50) + r.moveBottomLeft( rect().bottomLeft() ) + return r + end + + def shotRect() + gravity = 4.0 + + time = @timerCount / 4.0 + velocity = @shoot_f + radians = @shoot_ang*3.14159265/180.0 + + velx = velocity*cos( radians ) + vely = velocity*sin( radians ) + x0 = ( @barrelRect.right() + 5.0 )*cos(radians) + y0 = ( @barrelRect.right() + 5.0 )*sin(radians) + x = x0 + velx*time + y = y0 + vely*time - 0.5*gravity*time*time + + r = Qt::Rect.new( 0, 0, 6, 6 ); + r.moveCenter( Qt::Point.new( x.round, height() - 1 - y.round ) ) + return r + end + + def targetRect() + r = Qt::Rect.new( 0, 0, 20, 10 ) + r.moveCenter( Qt::Point.new(@target.x(),height() - 1 - @target.y()) ); + return r + end + + def sizePolicy() + return Qt::SizePolicy.new( Qt::SizePolicy::Expanding, Qt::SizePolicy::Expanding ) + end +end diff --git a/qtruby/rubylib/tutorial/t12/lcdrange.rb b/qtruby/rubylib/tutorial/t12/lcdrange.rb new file mode 100644 index 00000000..ef5c849d --- /dev/null +++ b/qtruby/rubylib/tutorial/t12/lcdrange.rb @@ -0,0 +1,47 @@ +require 'Qt' + +class LCDRange < Qt::VBox + signals 'valueChanged(int)' + slots 'setValue(int)', 'setRange(int, int)', 'setText(const char *)' + + def initialize(s, parent, name) + super(parent, name) + init() + setText(s) + end + + def init() + lcd = Qt::LCDNumber.new(2, self, 'lcd') + @slider = Qt::Slider.new(Qt::VBox::Horizontal, self, 'slider') + @slider.setRange(0, 99) + @slider.setValue(0) + @label = Qt::Label.new( ' ', self, 'label' ) + @label.setAlignment( Qt::AlignCenter ) + connect(@slider, SIGNAL('valueChanged(int)'), lcd, SLOT('display(int)')) + connect(@slider, SIGNAL('valueChanged(int)'), SIGNAL('valueChanged(int)')) + setFocusProxy(@slider) + end + + def value() + @slider.value() + end + + def setValue( value ) + @slider.setValue( value ) + end + + def setRange( minVal, maxVal ) + if minVal < 0 || maxVal > 99 || minVal > maxVal + qWarning( "LCDRange::setRange(#{minVal},#{maxVal})\n" + + "\tRange must be 0..99\n" + + "\tand minVal must not be greater than maxVal" ) + return + end + @slider.setRange( minVal, maxVal ) + end + + def setText( s ) + @label.setText( s ) + end + +end diff --git a/qtruby/rubylib/tutorial/t12/t12.rb b/qtruby/rubylib/tutorial/t12/t12.rb new file mode 100755 index 00000000..a02c3a8e --- /dev/null +++ b/qtruby/rubylib/tutorial/t12/t12.rb @@ -0,0 +1,68 @@ +#!/usr/bin/env ruby +$VERBOSE = true; $:.unshift File.dirname($0) + +require 'Qt' +require 'lcdrange.rb' +require 'cannon.rb' + +class MyWidget < Qt::Widget + + def initialize() + super + quit = Qt::PushButton.new('&Quit', self, 'quit') + quit.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold)) + + connect(quit, SIGNAL('clicked()'), $qApp, SLOT('quit()')) + + angle = LCDRange.new( 'ANGLE', self, 'angle' ) + angle.setRange( 5, 70 ) + + force = LCDRange.new( 'FORCE', self, 'force' ) + force.setRange( 10, 50 ) + + cannonField = CannonField.new( self, 'cannonField' ) + + connect( angle, SIGNAL('valueChanged(int)'), + cannonField, SLOT('setAngle(int)') ) + connect( cannonField, SIGNAL('angleChanged(int)'), + angle, SLOT('setValue(int)') ) + + connect( force, SIGNAL('valueChanged(int)'), + cannonField, SLOT('setForce(int)') ) + connect( cannonField, SIGNAL('forceChanged(int)'), + force, SLOT('setValue(int)') ) + + shoot = Qt::PushButton.new( '&Shoot', self, 'shoot' ) + shoot.setFont( Qt::Font.new( 'Times', 18, Qt::Font::Bold ) ) + + connect( shoot, SIGNAL('clicked()'), cannonField, SLOT('shoot()') ) + + grid = Qt::GridLayout.new( self, 2, 2, 10 ) + grid.addWidget( quit, 0, 0 ) + grid.addWidget( cannonField, 1, 1 ) + grid.setColStretch( 1, 10 ) + + leftBox = Qt::VBoxLayout.new() + grid.addLayout( leftBox, 1, 0 ) + leftBox.addWidget( angle ) + leftBox.addWidget( force ) + + topBox = Qt::HBoxLayout.new() + grid.addLayout( topBox, 0, 1 ) + topBox.addWidget( shoot ) + topBox.addStretch( 1 ) + + angle.setValue( 60 ) + force.setValue( 25 ) + angle.setFocus() + end +end + +Qt::Application.setColorSpec( Qt::Application::CustomColor ) +a = Qt::Application.new(ARGV) + +w = MyWidget.new +w.setGeometry( 100, 100, 500, 355 ) +a.setMainWidget(w) +w.show +a.exec diff --git a/qtruby/rubylib/tutorial/t13/cannon.rb b/qtruby/rubylib/tutorial/t13/cannon.rb new file mode 100644 index 00000000..d99f9a09 --- /dev/null +++ b/qtruby/rubylib/tutorial/t13/cannon.rb @@ -0,0 +1,219 @@ +require 'Qt' +include Math + +class CannonField < Qt::Widget + + signals 'hit()', 'missed()', 'angleChanged(int)', 'forceChanged(int)', + 'canShoot(bool)' + + slots 'setAngle(int)', 'setForce(int)', 'shoot()', 'moveShot()', + 'newTarget()', 'setGameOver()', 'restartGame' + + + def initialize(parent, name) + super + @ang = 45 + @f = 0 + @timerCount = 0; + @autoShootTimer = Qt::Timer.new( self, "movement handler" ) + connect( @autoShootTimer, SIGNAL('timeout()'), + self, SLOT('moveShot()') ); + @shoot_ang = 0 + @shoot_f = 0 + @target = Qt::Point.new(0, 0) + @gameEnded = false + setPalette( Qt::Palette.new( Qt::Color.new( 250, 250, 200) ) ) + newTarget() + @barrelRect = Qt::Rect.new(33, -4, 15, 8) + end + + def angle() + return @ang + end + def force() + return @f + end + def gameOver() + return @gameEnded + end + + def setAngle( degrees ) + if degrees < 5 + degrees = 5 + elsif degrees > 70 + degrees = 70 + end + if @ang == degrees + return + end + @ang = degrees + repaint( cannonRect(), false ) + emit angleChanged( @ang ) + end + + def setForce( newton ) + if newton < 0 + newton = 0 + end + if @f == newton + return + end + @f = newton + emit forceChanged( @f ) + end + + def shoot() + if isShooting() + return + end + @timerCount = 0 + @shoot_ang = @ang + @shoot_f = @f + @autoShootTimer.start( 50 ) + emit canShoot( false ) + end + + @@first_time = true + + def newTarget() + if @@first_time + @@first_time = false + midnight = Qt::Time.new( 0, 0, 0 ) + srand( midnight.secsTo(Qt::Time.currentTime()) ) + end + r = Qt::Region.new( targetRect() ) + @target = Qt::Point.new( 200 + rand(190), + 10 + rand(255) ) + repaint( r.unite( Qt::Region.new(targetRect()) ) ) + end + + def setGameOver() + if @gameEnded + return + end + if isShooting() + @autoShootTimer.stop() + end + @gameEnded = true + repaint() + end + + def restartGame() + if isShooting() + @autoShootTimer.stop() + end + @gameEnded = false + repaint() + emit canShoot( true ) + end + + def moveShot() + r = Qt::Region.new( shotRect() ) + @timerCount += 1 + + shotR = shotRect() + + if shotR.intersects( targetRect() ) + @autoShootTimer.stop() + emit hit() + emit canShoot(true) + elsif shotR.x() > width() || shotR.y() > height() + @autoShootTimer.stop() + emit missed() + emit canShoot(true) + else + r = r.unite( Qt::Region.new( shotR ) ) + end + + repaint( r ) + end + + def paintEvent( e ) + updateR = e.rect() + p = Qt::Painter.new( self ) + + if @gameEnded + p.setPen( black ) + p.setFont( Qt::Font.new( "Courier", 48, QFont::Bold ) ) + p.drawText( rect(), AlignCenter, "Game Over" ) + end + if updateR.intersects( cannonRect() ) + paintCannon( p ) + end + if isShooting() && updateR.intersects( shotRect() ) + paintShot( p ) + end + if !@gameEnded && updateR.intersects( targetRect() ) + paintTarget( p ) + end + p.end() + end + + def paintShot( p ) + p.setBrush( black ) + p.setPen( Qt::NoPen ) + p.drawRect( shotRect() ) + end + + def paintTarget( p ) + p.setBrush( red ) + p.setPen( black ) + p.drawRect( targetRect() ) + end + + def paintCannon(p) + cr = cannonRect() + pix = Qt::Pixmap.new( cr.size() ) + pix.fill( self, cr.topLeft() ) + + tmp = Qt::Painter.new( pix ) + tmp.setBrush( blue ) + tmp.setPen( Qt::NoPen ) + tmp.translate( 0, pix.height() - 1 ) + tmp.drawPie( Qt::Rect.new(-35, -35, 70, 70), 0, 90*16 ) + tmp.rotate( - @ang ) + tmp.drawRect( @barrelRect ) + tmp.end() + + p.drawPixmap(cr.topLeft(), pix ) + end + + def cannonRect() + r = Qt::Rect.new( 0, 0, 50, 50) + r.moveBottomLeft( rect().bottomLeft() ) + return r + end + + def shotRect() + gravity = 4.0 + + time = @timerCount / 4.0 + velocity = @shoot_f + radians = @shoot_ang*3.14159265/180.0 + + velx = velocity*cos( radians ) + vely = velocity*sin( radians ) + x0 = ( @barrelRect.right() + 5.0 )*cos(radians) + y0 = ( @barrelRect.right() + 5.0 )*sin(radians) + x = x0 + velx*time + y = y0 + vely*time - 0.5*gravity*time*time + + r = Qt::Rect.new( 0, 0, 6, 6 ); + r.moveCenter( Qt::Point.new( x.round, height() - 1 - y.round ) ) + return r + end + + def targetRect() + r = Qt::Rect.new( 0, 0, 20, 10 ) + r.moveCenter( Qt::Point.new(@target.x(),height() - 1 - @target.y()) ); + return r + end + + def isShooting() + return @autoShootTimer.isActive() + end + + def sizePolicy() + return Qt::SizePolicy.new( Qt::SizePolicy::Expanding, Qt::SizePolicy::Expanding ) + end +end diff --git a/qtruby/rubylib/tutorial/t13/gamebrd.rb b/qtruby/rubylib/tutorial/t13/gamebrd.rb new file mode 100644 index 00000000..dc993927 --- /dev/null +++ b/qtruby/rubylib/tutorial/t13/gamebrd.rb @@ -0,0 +1,112 @@ +require 'Qt' +require 'lcdrange.rb' +require 'cannon.rb' + +class GameBoard < Qt::Widget + + slots 'fire()', 'hit()', 'missed()', 'newGame()' + + def initialize() + super + quit = Qt::PushButton.new('&Quit', self, 'quit') + quit.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold)) + + connect(quit, SIGNAL('clicked()'), $qApp, SLOT('quit()')) + + angle = LCDRange.new( 'ANGLE', self, 'angle' ) + angle.setRange( 5, 70 ) + + force = LCDRange.new( 'FORCE', self, 'force' ) + force.setRange( 10, 50 ) + + @cannonField = CannonField.new( self, 'cannonField' ) + + connect( angle, SIGNAL('valueChanged(int)'), + @cannonField, SLOT('setAngle(int)') ) + connect( @cannonField, SIGNAL('angleChanged(int)'), + angle, SLOT('setValue(int)') ) + + connect( force, SIGNAL('valueChanged(int)'), + @cannonField, SLOT('setForce(int)') ) + connect( @cannonField, SIGNAL('forceChanged(int)'), + force, SLOT('setValue(int)') ) + + connect( @cannonField, SIGNAL('hit()'), + self, SLOT('hit()') ) + connect( @cannonField, SIGNAL('missed()'), + self, SLOT('missed()') ) + + shoot = Qt::PushButton.new( '&Shoot', self, 'shoot' ) + shoot.setFont( Qt::Font.new( 'Times', 18, Qt::Font::Bold ) ) + + connect( shoot, SIGNAL('clicked()'), SLOT('fire()') ) + connect( @cannonField, SIGNAL('canShoot(bool)'), + shoot, SLOT('setEnabled(bool)') ) + + restart = Qt::PushButton.new( '&New Game', self, 'newgame' ) + restart.setFont( Qt::Font.new( 'Times', 18, Qt::Font::Bold ) ) + + connect( restart, SIGNAL('clicked()'), self, SLOT('newGame()') ) + + @hits = Qt::LCDNumber.new( 2, self, 'hits' ) + @shotsLeft = Qt::LCDNumber.new( 2, self, 'shotsleft' ) + hitsL = Qt::Label.new( 'HITS', self, 'hitsLabel' ) + shotsLeftL = Qt::Label.new( 'SHOTS LEFT', self, 'shotsleftLabel' ) + + grid = Qt::GridLayout.new( self, 2, 2, 10 ) + grid.addWidget( quit, 0, 0 ) + grid.addWidget( @cannonField, 1, 1 ) + grid.setColStretch( 1, 10 ) + + leftBox = Qt::VBoxLayout.new() + grid.addLayout( leftBox, 1, 0 ) + leftBox.addWidget( angle ) + leftBox.addWidget( force ) + + topBox = Qt::HBoxLayout.new() + grid.addLayout( topBox, 0, 1 ) + topBox.addWidget( shoot ) + topBox.addWidget( @hits ) + topBox.addWidget( hitsL ) + topBox.addWidget( @shotsLeft ) + topBox.addWidget( shotsLeftL ) + topBox.addStretch( 1 ) + topBox.addWidget( restart ) + + angle.setValue( 60 ) + force.setValue( 25 ) + angle.setFocus() + + newGame() + end + + def fire() + if @cannonField.gameOver() || @cannonField.isShooting() + return + end + @shotsLeft.display( @shotsLeft.intValue() - 1 ) + @cannonField.shoot() + end + + def hit() + @hits.display( @hits.intValue() + 1 ) + if @shotsLeft.intValue() == 0 + @cannonField.setGameOver() + else + @cannonField.newTarget() + end + end + + def missed() + if @shotsLeft.intValue() == 0 + @cannonField.setGameOver() + end + end + + def newGame() + @shotsLeft.display( 15.0 ) + @hits.display( 0 ) + @cannonField.restartGame() + @cannonField.newTarget() + end +end diff --git a/qtruby/rubylib/tutorial/t13/lcdrange.rb b/qtruby/rubylib/tutorial/t13/lcdrange.rb new file mode 100644 index 00000000..03edb89c --- /dev/null +++ b/qtruby/rubylib/tutorial/t13/lcdrange.rb @@ -0,0 +1,55 @@ +require 'Qt' + +class LCDRange < Qt::Widget + signals 'valueChanged(int)' + slots 'setValue(int)', 'setRange(int, int)', 'setText(const char*)' + + def initialize(s, parent, name) + super(parent, name) + init() + setText(s) + end + + def init() + @lcd = Qt::LCDNumber.new(2, self, 'lcd') + @slider = Qt::Slider.new(Qt::VBox::Horizontal, self, 'slider') + @slider.setRange(0, 99) + @slider.setValue(0) + + @label = Qt::Label.new( ' ', self, 'label' ) + @label.setAlignment( Qt::AlignCenter ) + + connect(@slider, SIGNAL('valueChanged(int)'), @lcd, SLOT('display(int)')) + connect(@slider, SIGNAL('valueChanged(int)'), SIGNAL('valueChanged(int)')) + + setFocusProxy(@slider) + + l = Qt::VBoxLayout.new( self ) + l.addWidget( @lcd, 1 ) + l.addWidget( @slider ) + l.addWidget( @label ) + end + + def value() + @slider.value() + end + + def setValue( value ) + @slider.setValue( value ) + end + + def setRange( minVal, maxVal ) + if minVal < 0 || maxVal > 99 || minVal > maxVal + qWarning( "LCDRange::setRange(#{minVal},#{maxVal})\n" + + "\tRange must be 0..99\n" + + "\tand minVal must not be greater than maxVal" ) + return + end + @slider.setRange( minVal, maxVal ) + end + + def setText( s ) + @label.setText( s ) + end + +end diff --git a/qtruby/rubylib/tutorial/t13/t13.rb b/qtruby/rubylib/tutorial/t13/t13.rb new file mode 100755 index 00000000..817dfe70 --- /dev/null +++ b/qtruby/rubylib/tutorial/t13/t13.rb @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby +$VERBOSE = true; $:.unshift File.dirname($0) + +require 'Qt' +require 'gamebrd.rb' + +Qt::Application.setColorSpec( Qt::Application::CustomColor ) +a = Qt::Application.new(ARGV) + +gb = GameBoard.new +gb.setGeometry( 100, 100, 500, 355 ) +a.setMainWidget(gb) +gb.show +a.exec diff --git a/qtruby/rubylib/tutorial/t14/cannon.rb b/qtruby/rubylib/tutorial/t14/cannon.rb new file mode 100644 index 00000000..b0f77d37 --- /dev/null +++ b/qtruby/rubylib/tutorial/t14/cannon.rb @@ -0,0 +1,279 @@ +require 'Qt' +include Math + +class CannonField < Qt::Widget + + signals 'hit()', 'missed()', 'angleChanged(int)', 'forceChanged(int)', + 'canShoot(bool)' + + slots 'setAngle(int)', 'setForce(int)', 'shoot()', 'moveShot()', + 'newTarget()', 'setGameOver()', 'restartGame()' + + def initialize(parent, name) + super + @ang = 45 + @f = 0 + @timerCount = 0; + @autoShootTimer = Qt::Timer.new( self, 'movement handler' ) + connect( @autoShootTimer, SIGNAL('timeout()'), + self, SLOT('moveShot()') ) + @shoot_ang = 0 + @shoot_f = 0 + @target = Qt::Point.new(0, 0) + @gameEnded = false + @barrelPressed = false + setPalette( Qt::Palette.new( Qt::Color.new( 250, 250, 200) ) ) + newTarget() + @barrelRect = Qt::Rect.new(33, -4, 15, 8) + end + + def angle() + return @ang + end + + def force() + return @f + end + + def gameOver() + return @gameEnded + end + + def setAngle( degrees ) + if degrees < 5 + degrees = 5 + elsif degrees > 70 + degrees = 70 + end + if @ang == degrees + return + end + @ang = degrees + repaint( cannonRect(), false ) + emit angleChanged( @ang ) + end + + def setForce( newton ) + if newton < 0 + newton = 0 + end + if @f == newton + return + end + @f = newton + emit forceChanged( @f ) + end + + def shoot() + if isShooting() + return + end + @timerCount = 0 + @shoot_ang = @ang + @shoot_f = @f + @autoShootTimer.start( 50 ) + emit canShoot( false ) + end + + @@first_time = true + + def newTarget() + if @@first_time + @@first_time = false + midnight = Qt::Time.new( 0, 0, 0 ) + srand( midnight.secsTo(Qt::Time.currentTime()) ) + end + r = Qt::Region.new( targetRect() ) + @target = Qt::Point.new( 200 + rand(190), + 10 + rand(255) ) + repaint( r.unite( Qt::Region.new(targetRect()) ) ) + end + + def setGameOver() + if @gameEnded + return + end + if isShooting() + @autoShootTimer.stop() + end + @gameEnded = true + repaint() + end + + def restartGame() + if isShooting() + @autoShootTimer.stop() + end + @gameEnded = false + repaint() + emit canShoot( true ) + end + + def moveShot() + r = Qt::Region.new( shotRect() ) + @timerCount += 1 + + shotR = shotRect() + + if shotR.intersects( targetRect() ) + @autoShootTimer.stop() + emit hit() + emit canShoot(true) + elsif shotR.x() > width() || shotR.y() > height() || + shotR.intersects(barrierRect()) + @autoShootTimer.stop() + emit missed() + emit canShoot(true) + else + r = r.unite( Qt::Region.new( shotR ) ) + end + + repaint( r ) + end + private :moveShot + + def mousePressEvent( e ) + if e.button() != Qt::LeftButton + return + end + if barrelHit( e.pos() ) + @barrelPressed = true + end + end + + def mouseMoveEvent( e ) + if !@barrelPressed + return + end + pnt = e.pos(); + if pnt.x() <= 0 + pnt.setX( 1 ) + end + if pnt.y() >= height() + pnt.setY( height() - 1 ) + end + rad = atan2((rect().bottom()-pnt.y()), pnt.x()) + setAngle( ( rad*180/3.14159265 ).round ) + end + + def mouseReleaseEvent( e ) + if e.button() == Qt::LeftButton + @barrelPressed = false + end + end + + def paintEvent( e ) + updateR = e.rect() + p = Qt::Painter.new( self ) + + if @gameEnded + p.setPen( black ) + p.setFont( Qt::Font.new( 'Courier', 48, Qt::Font::Bold ) ) + p.drawText( rect(), Qt::AlignCenter, 'Game Over' ) + end + if updateR.intersects( cannonRect() ) + paintCannon( p ) + end + if updateR.intersects( barrierRect() ) + paintBarrier( p ) + end + if isShooting() && updateR.intersects( shotRect() ) + paintShot( p ) + end + if !@gameEnded && updateR.intersects( targetRect() ) + paintTarget( p ) + end + + p.end() + end + + def paintShot( p ) + p.setBrush( black ) + p.setPen( Qt::NoPen ) + p.drawRect( shotRect() ) + end + + def paintTarget( p ) + p.setBrush( red ) + p.setPen( black ) + p.drawRect( targetRect() ) + end + + def paintBarrier( p ) + p.setBrush( yellow ) + p.setPen( black ) + p.drawRect( barrierRect() ) + end + + def paintCannon(p) + cr = cannonRect() + pix = Qt::Pixmap.new( cr.size() ) + pix.fill( self, cr.topLeft() ) + + tmp = Qt::Painter.new( pix ) + tmp.setBrush( blue ) + tmp.setPen( Qt::NoPen ) + + tmp.translate( 0, pix.height() - 1 ) + tmp.drawPie( Qt::Rect.new(-35, -35, 70, 70), 0, 90*16 ) + tmp.rotate( - @ang ) + tmp.drawRect( @barrelRect ) + tmp.end() + + p.drawPixmap(cr.topLeft(), pix ) + end + private :paintShot, :paintTarget, :paintBarrier, :paintCannon + + def cannonRect() + r = Qt::Rect.new( 0, 0, 50, 50) + r.moveBottomLeft( rect().bottomLeft() ) + return r + end + + def shotRect() + gravity = 4.0 + + time = @timerCount / 4.0 + velocity = @shoot_f + radians = @shoot_ang*3.14159265/180.0 + + velx = velocity*cos( radians ) + vely = velocity*sin( radians ) + x0 = ( @barrelRect.right() + 5.0 )*cos(radians) + y0 = ( @barrelRect.right() + 5.0 )*sin(radians) + x = x0 + velx*time + y = y0 + vely*time - 0.5*gravity*time*time + + r = Qt::Rect.new( 0, 0, 6, 6 ); + r.moveCenter( Qt::Point.new( x.round, height() - 1 - y.round ) ) + return r + end + + def targetRect() + r = Qt::Rect.new( 0, 0, 20, 10 ) + r.moveCenter( Qt::Point.new(@target.x(),height() - 1 - @target.y()) ) + return r + end + + def barrierRect() + return Qt::Rect.new( 145, height() - 100, 15, 100 ) + end + + def barrelHit( p ) + mtx = Qt::WMatrix.new + mtx.translate( 0, height() - 1 ) + mtx.rotate( - @ang ) + mtx = mtx.invert() + return @barrelRect.contains( mtx.map(p) ) + end + + private :cannonRect, :shotRect, :targetRect, :barrierRect, :barrelHit + + def isShooting() + return @autoShootTimer.isActive() + end + + def sizePolicy() + return Qt::SizePolicy.new( Qt::SizePolicy::Expanding, Qt::SizePolicy::Expanding ) + end +end diff --git a/qtruby/rubylib/tutorial/t14/gamebrd.rb b/qtruby/rubylib/tutorial/t14/gamebrd.rb new file mode 100644 index 00000000..b72d58c1 --- /dev/null +++ b/qtruby/rubylib/tutorial/t14/gamebrd.rb @@ -0,0 +1,121 @@ +require 'lcdrange.rb' +require 'cannon.rb' + +class GameBoard < Qt::Widget + + slots 'fire()', 'hit()', 'missed()', 'newGame()' + + def initialize() + super + quit = Qt::PushButton.new('&Quit', self, 'quit') + quit.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold)) + + connect(quit, SIGNAL('clicked()'), $qApp, SLOT('quit()')) + + angle = LCDRange.new( 'ANGLE', self, 'angle' ) + angle.setRange( 5, 70 ) + + force = LCDRange.new( 'FORCE', self, 'force' ) + force.setRange( 10, 50 ) + + box = Qt::VBox.new( self, 'cannonFrame' ) + box.setFrameStyle( Qt::Frame::WinPanel | Qt::Frame::Sunken ) + @cannonField = CannonField.new( box, 'cannonField' ) + + connect( angle, SIGNAL('valueChanged(int)'), + @cannonField, SLOT('setAngle(int)') ) + connect( @cannonField, SIGNAL('angleChanged(int)'), + angle, SLOT('setValue(int)') ) + + connect( force, SIGNAL('valueChanged(int)'), + @cannonField, SLOT('setForce(int)') ) + connect( @cannonField, SIGNAL('forceChanged(int)'), + force, SLOT('setValue(int)') ) + + connect( @cannonField, SIGNAL('hit()'), + self, SLOT('hit()') ) + connect( @cannonField, SIGNAL('missed()'), + self, SLOT('missed()') ) + + shoot = Qt::PushButton.new( '&Shoot', self, 'shoot' ) + shoot.setFont( Qt::Font.new( 'Times', 18, Qt::Font::Bold ) ) + + connect( shoot, SIGNAL('clicked()'), SLOT('fire()') ) + connect( @cannonField, SIGNAL('canShoot(bool)'), + shoot, SLOT('setEnabled(bool)') ) + + restart = Qt::PushButton.new( '&New Game', self, 'newgame' ) + restart.setFont( Qt::Font.new( 'Times', 18, Qt::Font::Bold ) ) + + connect( restart, SIGNAL('clicked()'), self, SLOT('newGame()') ) + + @hits = Qt::LCDNumber.new( 2, self, 'hits' ) + @shotsLeft = Qt::LCDNumber.new( 2, self, 'shotsleft' ) + hitsL = Qt::Label.new( 'HITS', self, 'hitsLabel' ) + shotsLeftL = Qt::Label.new( 'SHOTS LEFT', self, 'shotsleftLabel' ) + + accel = Qt::Accel.new( self ) + accel.connectItem( accel.insertItem( Qt::KeySequence.new(Key_Enter) ), + self, SLOT('fire()') ) + accel.connectItem( accel.insertItem( Qt::KeySequence.new(Key_Return) ), + self, SLOT('fire()') ) + accel.connectItem( accel.insertItem( Qt::KeySequence.new(CTRL+Key_Q) ), + $qApp, SLOT('quit()') ) + + grid = Qt::GridLayout.new( self, 2, 2, 10 ) + grid.addWidget( quit, 0, 0 ) + grid.addWidget( box, 1, 1) + grid.setColStretch( 1, 10 ) + + leftBox = Qt::VBoxLayout.new() + grid.addLayout( leftBox, 1, 0 ) + leftBox.addWidget( angle ) + leftBox.addWidget( force ) + + topBox = Qt::HBoxLayout.new() + grid.addLayout( topBox, 0, 1 ) + topBox.addWidget( shoot ) + topBox.addWidget( @hits ) + topBox.addWidget( hitsL ) + topBox.addWidget( @shotsLeft ) + topBox.addWidget( shotsLeftL ) + topBox.addStretch( 1 ) + topBox.addWidget( restart ) + + angle.setValue( 60 ) + force.setValue( 25 ) + angle.setFocus() + + newGame() + end + + def fire() + if @cannonField.gameOver() || @cannonField.isShooting() + return + end + @shotsLeft.display( @shotsLeft.intValue() - 1 ) + @cannonField.shoot() + end + + def hit() + @hits.display( @hits.intValue() + 1 ) + if @shotsLeft.intValue() == 0 + @cannonField.setGameOver() + else + @cannonField.newTarget() + end + end + + def missed() + if @shotsLeft.intValue() == 0 + @cannonField.setGameOver() + end + end + + def newGame() + @shotsLeft.display( 15.0 ) + @hits.display( 0 ) + @cannonField.restartGame() + @cannonField.newTarget() + end +end diff --git a/qtruby/rubylib/tutorial/t14/lcdrange.rb b/qtruby/rubylib/tutorial/t14/lcdrange.rb new file mode 100644 index 00000000..492d93b1 --- /dev/null +++ b/qtruby/rubylib/tutorial/t14/lcdrange.rb @@ -0,0 +1,55 @@ +require 'Qt' + +class LCDRange < Qt::Widget + signals 'valueChanged(int)' + slots 'setValue(int)', 'setRange(int, int)', 'setText(const char*)' + + def initialize(s, parent, name) + super(parent, name) + init() + setText(s) + end + + def init() + @lcd = Qt::LCDNumber.new(2, self, 'lcd') + @slider = Qt::Slider.new(Qt::VBox::Horizontal, self, 'slider') + @slider.setRange(0, 99) + @slider.setValue(0) + + @label = Qt::Label.new( ' ', self, 'label' ) + @label.setAlignment( Qt::AlignCenter ) + + connect(@slider, SIGNAL('valueChanged(int)'), @lcd, SLOT('display(int)')) + connect(@slider, SIGNAL('valueChanged(int)'), SIGNAL('valueChanged(int)')) + + setFocusProxy(@slider) + + @l = Qt::VBoxLayout.new( self ) + @l.addWidget( @lcd, 1 ) + @l.addWidget( @slider ) + @l.addWidget( @label ) + end + + def value() + @slider.value() + end + + def setValue( value ) + @slider.setValue( value ) + end + + def setRange( minVal, maxVal ) + if minVal < 0 || maxVal > 99 || minVal > maxVal + qWarning( "LCDRange::setRange(#{minVal},#{maxVal})\n" + + "\tRange must be 0..99\n" + + "\tand minVal must not be greater than maxVal" ) + return + end + @slider.setRange( minVal, maxVal ) + end + + def setText( s ) + @label.setText( s ) + end + +end diff --git a/qtruby/rubylib/tutorial/t14/t14.rb b/qtruby/rubylib/tutorial/t14/t14.rb new file mode 100755 index 00000000..817dfe70 --- /dev/null +++ b/qtruby/rubylib/tutorial/t14/t14.rb @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby +$VERBOSE = true; $:.unshift File.dirname($0) + +require 'Qt' +require 'gamebrd.rb' + +Qt::Application.setColorSpec( Qt::Application::CustomColor ) +a = Qt::Application.new(ARGV) + +gb = GameBoard.new +gb.setGeometry( 100, 100, 500, 355 ) +a.setMainWidget(gb) +gb.show +a.exec diff --git a/qtruby/rubylib/tutorial/t2/t2.rb b/qtruby/rubylib/tutorial/t2/t2.rb new file mode 100755 index 00000000..8139e98c --- /dev/null +++ b/qtruby/rubylib/tutorial/t2/t2.rb @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby +$VERBOSE = true; $:.unshift File.dirname($0) + +require 'Qt'; + +a = Qt::Application.new(ARGV) + +quit = Qt::PushButton.new('Quit', nil) +quit.resize(75, 30) +quit.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold)) + +Qt::Object.connect(quit, SIGNAL('clicked()'), a, SLOT('quit()')) + +a.setMainWidget(quit) +quit.show +a.exec +exit diff --git a/qtruby/rubylib/tutorial/t3/t3.rb b/qtruby/rubylib/tutorial/t3/t3.rb new file mode 100755 index 00000000..305afbc4 --- /dev/null +++ b/qtruby/rubylib/tutorial/t3/t3.rb @@ -0,0 +1,20 @@ +#!/usr/bin/env ruby +$VERBOSE = true; $:.unshift File.dirname($0) + +require 'Qt' + +a = Qt::Application.new(ARGV) + +box = Qt::VBox.new() +box.resize(200, 120) + +quit = Qt::PushButton.new('Quit', box) +quit.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold)) + +a.connect(quit, SIGNAL('clicked()'), SLOT('quit()')) + +a.setMainWidget(box) +box.show + +a.exec +exit diff --git a/qtruby/rubylib/tutorial/t4/t4.rb b/qtruby/rubylib/tutorial/t4/t4.rb new file mode 100755 index 00000000..ae48b7ac --- /dev/null +++ b/qtruby/rubylib/tutorial/t4/t4.rb @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +$VERBOSE = true; $:.unshift File.dirname($0) + +require 'Qt' + +class MyWidget < Qt::Widget + +def initialize(parent = nil, name = nil) + super + setMinimumSize(200, 120) + setMaximumSize(200, 120) + + quit = Qt::PushButton.new('Quit', self, 'quit') + quit.setGeometry(62, 40, 75, 30) + quit.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold)) + connect(quit, SIGNAL('clicked()'), $qApp, SLOT('quit()')) +end + +end + +a = Qt::Application.new(ARGV) + +w = MyWidget.new +w.setGeometry(100, 100, 200, 120) +a.setMainWidget(w) +w.show +a.exec diff --git a/qtruby/rubylib/tutorial/t5/t5.rb b/qtruby/rubylib/tutorial/t5/t5.rb new file mode 100755 index 00000000..6eb7f808 --- /dev/null +++ b/qtruby/rubylib/tutorial/t5/t5.rb @@ -0,0 +1,31 @@ +#!/usr/bin/env ruby +$VERBOSE = true; $:.unshift File.dirname($0) + +require 'Qt' + +class MyWidget < Qt::VBox + +def initialize() + super + quit = Qt::PushButton.new('Quit', self, 'quit') + quit.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold)) + + connect(quit, SIGNAL('clicked()'), $qApp, SLOT('quit()')) + + lcd = Qt::LCDNumber.new(2, self, 'lcd') + + slider = Qt::Slider.new(Horizontal, self, 'slider') + slider.setRange(0, 99) + slider.setValue(0) + + connect(slider, SIGNAL('valueChanged(int)'), lcd, SLOT('display(int)')) +end + +end + +a = Qt::Application.new(ARGV) + +w = MyWidget.new +a.setMainWidget(w) +w.show +a.exec diff --git a/qtruby/rubylib/tutorial/t6/t6.rb b/qtruby/rubylib/tutorial/t6/t6.rb new file mode 100755 index 00000000..d89203d0 --- /dev/null +++ b/qtruby/rubylib/tutorial/t6/t6.rb @@ -0,0 +1,45 @@ +#!/usr/bin/env ruby +$VERBOSE = true; $:.unshift File.dirname($0) + +require 'Qt' + +class LCDRange < Qt::VBox + +def initialize(grid) + super + lcd = Qt::LCDNumber.new(2, self, 'lcd') + + slider = Qt::Slider.new(Qt::VBox::Horizontal, self, 'slider') + slider.setRange(0, 99) + slider.setValue(0) + + lcd.connect(slider, SIGNAL('valueChanged(int)'), SLOT('display(int)')) +end + +end + +class MyWidget < Qt::VBox + +def initialize() + super + quit = Qt::PushButton.new('Quit', self, 'quit') + quit.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold)) + + connect(quit, SIGNAL('clicked()'), $qApp, SLOT('quit()')) + grid = Qt::Grid.new( 4, self ) + + for c in 0..3 + for r in 0..3 + LCDRange.new(grid) + end + end +end + +end + +a = Qt::Application.new(ARGV) + +w = MyWidget.new +a.setMainWidget(w) +w.show +a.exec diff --git a/qtruby/rubylib/tutorial/t7/lcdrange.rb b/qtruby/rubylib/tutorial/t7/lcdrange.rb new file mode 100644 index 00000000..3df3c961 --- /dev/null +++ b/qtruby/rubylib/tutorial/t7/lcdrange.rb @@ -0,0 +1,25 @@ +#!/usr/bin/ruby -w +require 'Qt' + +class LCDRange < Qt::VBox + signals 'valueChanged(int)' + slots 'setValue(int)' + + def initialize(grid) + super + lcd = Qt::LCDNumber.new(2, self, 'lcd') + @slider = Qt::Slider.new(Qt::VBox::Horizontal, self, 'slider') + @slider.setRange(0, 99) + @slider.setValue(0) + connect(@slider, SIGNAL('valueChanged(int)'), lcd, SLOT('display(int)')) + connect(@slider, SIGNAL('valueChanged(int)'), SIGNAL('valueChanged(int)')) + end + + def value() + @slider.value() + end + + def setValue( value ) + @slider.setValue( value ) + end +end diff --git a/qtruby/rubylib/tutorial/t7/t7.rb b/qtruby/rubylib/tutorial/t7/t7.rb new file mode 100755 index 00000000..396953de --- /dev/null +++ b/qtruby/rubylib/tutorial/t7/t7.rb @@ -0,0 +1,37 @@ +#!/usr/bin/env ruby +$VERBOSE = true; $:.unshift File.dirname($0) + +require 'Qt' +require 'lcdrange.rb' + +class MyWidget < Qt::VBox + +def initialize() + super + quit = Qt::PushButton.new('Quit', self, 'quit') + quit.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold)) + + connect(quit, SIGNAL('clicked()'), $qApp, SLOT('quit()')) + grid = Qt::Grid.new( 4, self ) + + previous = nil + for c in 0..3 + for r in 0..3 + lr = LCDRange.new(grid) + if previous != nil + connect( lr, SIGNAL('valueChanged(int)'), + previous, SLOT('setValue(int)') ) + end + previous = lr + end + end +end + +end + +a = Qt::Application.new(ARGV) + +w = MyWidget.new +a.setMainWidget(w) +w.show +a.exec diff --git a/qtruby/rubylib/tutorial/t8/cannon.rb b/qtruby/rubylib/tutorial/t8/cannon.rb new file mode 100644 index 00000000..b3202a93 --- /dev/null +++ b/qtruby/rubylib/tutorial/t8/cannon.rb @@ -0,0 +1,38 @@ +require 'Qt' + +class CannonField < Qt::Widget + signals 'angleChanged(int)' + slots 'setAngle(int)' + + def initialize(parent, name) + super + @ang = 45 + setPalette( Qt::Palette.new( Qt::Color.new( 250, 250, 200) ) ) + end + + def setAngle( degrees ) + if degrees < 5 + degrees = 5 + elsif degrees > 70 + degrees = 70 + end + if @ang == degrees + return + end + @ang = degrees + repaint() + emit angleChanged( @ang ) + end + + def paintEvent( event ) + s = "Angle = #{@ang}" + p = Qt::Painter.new( self ) + p.drawText( 200, 200, s ) + p.end() + end + + + def sizePolicy() + return Qt::SizePolicy.new( Qt::SizePolicy::Expanding, Qt::SizePolicy::Expanding ) + end +end diff --git a/qtruby/rubylib/tutorial/t8/lcdrange.rb b/qtruby/rubylib/tutorial/t8/lcdrange.rb new file mode 100644 index 00000000..011196fd --- /dev/null +++ b/qtruby/rubylib/tutorial/t8/lcdrange.rb @@ -0,0 +1,35 @@ +require 'Qt' + +class LCDRange < Qt::VBox + signals 'valueChanged(int)' + slots 'setValue(int)', 'setRange(int, int)' + + def initialize(parent, name) + super + lcd = Qt::LCDNumber.new(2, self, 'lcd') + @slider = Qt::Slider.new(Qt::VBox::Horizontal, self, 'slider') + @slider.setRange(0, 99) + @slider.setValue(0) + connect(@slider, SIGNAL('valueChanged(int)'), lcd, SLOT('display(int)')) + connect(@slider, SIGNAL('valueChanged(int)'), SIGNAL('valueChanged(int)')) + setFocusProxy(@slider) + end + + def value() + @slider.value() + end + + def setValue( value ) + @slider.setValue( value ) + end + + def setRange( minVal, maxVal ) + if minVal < 0 || maxVal > 99 || minVal > maxVal + qWarning( "LCDRange::setRange(#{minVal},#{maxVal})\n" + + "\tRange must be 0..99\n" + + "\tand minVal must not be greater than maxVal" ) + return + end + @slider.setRange( minVal, maxVal ) + end +end diff --git a/qtruby/rubylib/tutorial/t8/t8.rb b/qtruby/rubylib/tutorial/t8/t8.rb new file mode 100755 index 00000000..881d15e7 --- /dev/null +++ b/qtruby/rubylib/tutorial/t8/t8.rb @@ -0,0 +1,44 @@ +#!/usr/bin/env ruby +$VERBOSE = true; $:.unshift File.dirname($0) + +require 'Qt' +require 'lcdrange.rb' +require 'cannon.rb' + +class MyWidget < Qt::Widget + def initialize() + super + quit = Qt::PushButton.new('Quit', self, 'quit') + quit.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold)) + + connect(quit, SIGNAL('clicked()'), $qApp, SLOT('quit()')) + + angle = LCDRange.new( self, 'angle' ) + angle.setRange( 5, 70 ) + + cannonField = CannonField.new( self, 'cannonField' ) + + connect( angle, SIGNAL('valueChanged(int)'), + cannonField, SLOT('setAngle(int)') ) + connect( cannonField, SIGNAL('angleChanged(int)'), + angle, SLOT('setValue(int)') ) + grid = Qt::GridLayout.new( self, 2, 2, 10 ) + # 2x2, 10 pixel border + + grid.addWidget( quit, 0, 0 ) + grid.addWidget( angle, 1, 0, Qt.AlignTop ) + grid.addWidget( cannonField, 1, 1 ) + grid.setColStretch( 1, 10 ) + + angle.setValue( 60 ) + angle.setFocus() + end +end + +a = Qt::Application.new(ARGV) + +w = MyWidget.new +w.setGeometry( 100, 100, 500, 355 ) +a.setMainWidget(w) +w.show +a.exec diff --git a/qtruby/rubylib/tutorial/t9/cannon.rb b/qtruby/rubylib/tutorial/t9/cannon.rb new file mode 100644 index 00000000..14bcc70f --- /dev/null +++ b/qtruby/rubylib/tutorial/t9/cannon.rb @@ -0,0 +1,43 @@ +require 'Qt' + +class CannonField < Qt::Widget + signals 'angleChanged(int)' + slots 'setAngle(int)' + + def initialize(parent, name) + super + @ang = 45 + setPalette( Qt::Palette.new( Qt::Color.new( 250, 250, 200) ) ) + end + + def setAngle( degrees ) + if degrees < 5 + degrees = 5 + elsif degrees > 70 + degrees = 70 + end + if @ang == degrees + return + end + @ang = degrees + repaint() + emit angleChanged( @ang ) + end + + def paintEvent( event ) + p = Qt::Painter.new( self ) + + p.setBrush( blue ) + p.setPen( Qt::NoPen ) + p.translate( 0, rect().bottom() ) + p.drawPie( Qt::Rect.new(-35, -35, 70, 70), 0, 90*16 ) + p.rotate( - @ang ) + p.drawRect( Qt::Rect.new(33, -4, 15, 8) ) + p.end() + end + + + def sizePolicy() + return Qt::SizePolicy.new( Qt::SizePolicy::Expanding, Qt::SizePolicy::Expanding ) + end +end diff --git a/qtruby/rubylib/tutorial/t9/lcdrange.rb b/qtruby/rubylib/tutorial/t9/lcdrange.rb new file mode 100644 index 00000000..6eb2f732 --- /dev/null +++ b/qtruby/rubylib/tutorial/t9/lcdrange.rb @@ -0,0 +1,36 @@ +require 'Qt' + +class LCDRange < Qt::VBox + signals 'valueChanged(int)' + slots 'setValue(int)', 'setRange(int, int)' + + def initialize(parent, name) + super + lcd = Qt::LCDNumber.new(2, self, 'lcd') + @slider = Qt::Slider.new(Qt::VBox::Horizontal, self, 'slider') + @slider.setRange(0, 99) + @slider.setValue(0) + connect(@slider, SIGNAL('valueChanged(int)'), lcd, SLOT('display(int)')) + connect(@slider, SIGNAL('valueChanged(int)'), SIGNAL('valueChanged(int)')) + setFocusProxy(@slider) + end + + def value() + @slider.value() + end + + def setValue( value ) + @slider.setValue( value ) + end + + def setRange( minVal, maxVal ) + if minVal < 0 || maxVal > 99 || minVal > maxVal + printf( "LCDRange::setRange(%d,%d)\n" + + "\tRange must be 0..99\n" + + "\tand minVal must not be greater than maxVal", + minVal, maxVal ) + return + end + @slider.setRange( minVal, maxVal ) + end +end diff --git a/qtruby/rubylib/tutorial/t9/t9.rb b/qtruby/rubylib/tutorial/t9/t9.rb new file mode 100755 index 00000000..4185b972 --- /dev/null +++ b/qtruby/rubylib/tutorial/t9/t9.rb @@ -0,0 +1,44 @@ +#!/usr/bin/env ruby +$VERBOSE = true; $:.unshift File.dirname($0) + +require 'Qt' +require 'lcdrange.rb' +require 'cannon.rb' + +class MyWidget < Qt::Widget + def initialize() + super + quit = Qt::PushButton.new('Quit', self, 'quit') + quit.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold)) + + connect(quit, SIGNAL('clicked()'), $qApp, SLOT('quit()')) + + angle = LCDRange.new( self, 'angle' ) + angle.setRange( 5, 70 ) + + cannonField = CannonField.new( self, 'cannonField' ) + + connect( angle, SIGNAL('valueChanged(int)'), + cannonField, SLOT('setAngle(int)') ) + connect( cannonField, SIGNAL('angleChanged(int)'), + angle, SLOT('setValue(int)') ) + grid = Qt::GridLayout.new( self, 2, 2, 10 ) + # 2x2, 10 pixel border + + grid.addWidget( quit, 0, 0 ) + grid.addWidget( angle, 1, 0, Qt::AlignTop ) + grid.addWidget( cannonField, 1, 1 ) + grid.setColStretch( 1, 10 ) + + angle.setValue( 60 ) + angle.setFocus() + end +end + +a = Qt::Application.new(ARGV) + +w = MyWidget.new +w.setGeometry( 100, 100, 500, 355 ) +a.setMainWidget(w) +w.show +a.exec |