From bad14bdfddf00d46f9aca8f6b88d426f80c5bccb Mon Sep 17 00:00:00 2001 From: Jesse van den Kieboom Date: Sun, 9 Oct 2005 08:53:36 +0000 Subject: [PATCH] Initial import --- .tm_project.cache | 0 AUTHORS | 2 + COPYING | 340 ++++++ ChangeLog | 479 ++++++++ INSTALL | 229 ++++ Makefile.am | 30 + NEWS | 0 README | 0 aclocal.m4 | 1682 +++++++++++++++++++++++++++ autogen.sh | 159 +++ configure.in | 152 +++ gnoemoe-logo.svg | 717 ++++++++++++ gnoemoe.desktop | 9 + gnoemoe.xml | 8 + moo.lang | 217 ++++ pixmaps/Makefile.am | 14 + pixmaps/editor.xpm | 74 ++ pixmaps/editor_text.xpm | 80 ++ pixmaps/editor_verb.xpm | 83 ++ pixmaps/gnoemoe_logo.svg | 592 ++++++++++ pixmaps/ice-userlist/Makefile.am | 11 + pixmaps/ice-userlist/avail+idle.svg | 186 +++ pixmaps/ice-userlist/away+idle.svg | 314 +++++ pixmaps/ice-userlist/away.svg | 301 +++++ pixmaps/ice-userlist/busy+idle.svg | 314 +++++ pixmaps/ice-userlist/busy.svg | 301 +++++ pixmaps/ice-userlist/inhabitant.svg | 110 ++ pixmaps/ice-userlist/programmer.svg | 114 ++ pixmaps/ice-userlist/wizard.svg | 383 ++++++ pixmaps/saveclose.xpm | 243 ++++ pixmaps/terminal.png | Bin 0 -> 772 bytes pixmaps/tray/Makefile.am | 7 + pixmaps/tray/activate.svg | 210 ++++ pixmaps/tray/active.svg | 198 ++++ pixmaps/tray/default.svg | 168 +++ pixmaps/tray/notify.svg | 126 ++ pixmaps/userlist/Makefile.am | 14 + pixmaps/userlist/all.svg | 1568 +++++++++++++++++++++++++ pixmaps/userlist/away.svg | 301 +++++ pixmaps/userlist/friend.svg | 432 +++++++ pixmaps/userlist/idle.svg | 186 +++ pixmaps/userlist/idleaway.svg | 314 +++++ pixmaps/userlist/inhabitant.svg | 114 ++ pixmaps/userlist/inhabitantplus.svg | 108 ++ pixmaps/userlist/key.svg | 415 +++++++ pixmaps/userlist/newbie.svg | 110 ++ pixmaps/userlist/schooled.svg | 201 ++++ pixmaps/userlist/star.svg | 91 ++ pixmaps/userlist/wizard.svg | 383 ++++++ pixmaps/world.svg | 168 +++ pixmaps/world_activity.svg | 126 ++ po/ChangeLog | 7 + po/Makefile.in.in | 256 ++++ po/POTFILES.in | 21 + po/nl.gmo | Bin 0 -> 12210 bytes po/nl.po | 973 ++++++++++++++++ scripts/Makefile.am | 7 + scripts/editing.rb | 53 + scripts/misc.rb | 39 + scripts/music.rb | 171 +++ scripts/run.rb | 65 ++ src/.swp | Bin 0 -> 12288 bytes src/Makefile.am | 47 + src/ansi.h | 124 ++ src/debug.c | 39 + src/debug.h | 6 + src/gengtkclass.rb | 58 + src/gm-app-view.c | 1417 ++++++++++++++++++++++ src/gm-app-view.h | 77 ++ src/gm-app.c | 587 ++++++++++ src/gm-app.h | 79 ++ src/gm-bogus.h | 8 + src/gm-color-table.c | 281 +++++ src/gm-color-table.h | 67 ++ src/gm-editor.c | 128 ++ src/gm-editor.h | 68 ++ src/gm-marshal.list | 4 + src/gm-net.c | 537 +++++++++ src/gm-net.h | 81 ++ src/gm-options.c | 207 ++++ src/gm-options.h | 69 ++ src/gm-pixbuf.c | 182 +++ src/gm-pixbuf.h | 24 + src/gm-script.c.old | 663 +++++++++++ src/gm-script.h.old | 44 + src/gm-scripts-dialog.c | 844 ++++++++++++++ src/gm-scripts-dialog.h | 18 + src/gm-scripts.c | 867 ++++++++++++++ src/gm-scripts.h | 109 ++ src/gm-string.c | 203 ++++ src/gm-string.h | 36 + src/gm-support.c | 565 +++++++++ src/gm-support.h | 148 +++ src/gm-tray.c | 55 + src/gm-tray.h | 50 + src/gm-triggers-dialog.c | 991 ++++++++++++++++ src/gm-triggers-dialog.h | 10 + src/gm-triggers.c | 440 +++++++ src/gm-triggers.h | 118 ++ src/gm-ui.h | 77 ++ src/gm-world-info-dialog.c | 111 ++ src/gm-world-info-dialog.h | 6 + src/gm-world-input-view.c | 383 ++++++ src/gm-world-input-view.h | 57 + src/gm-world-logs-dialog.c | 116 ++ src/gm-world-logs-dialog.h | 10 + src/gm-world-properties-dialog.c | 674 +++++++++++ src/gm-world-properties-dialog.h | 16 + src/gm-world-tab.c | 196 ++++ src/gm-world-tab.h | 50 + src/gm-world-text-view.c | 954 +++++++++++++++ src/gm-world-text-view.h | 59 + src/gm-world-view.c | 459 ++++++++ src/gm-world-view.h | 75 ++ src/gm-world.c | 876 ++++++++++++++ src/gm-world.h | 136 +++ src/gm-worlds-list-dialog.c | 466 ++++++++ src/gm-worlds-list-dialog.h | 6 + src/gtktemplate.c | 57 + src/gtktemplate.h | 50 + stamp-h.in | 1 + ui/Makefile.am | 13 + ui/fixglade.rb | 17 + ui/gm-editor.glade | 80 ++ ui/gm-main.glade | 707 +++++++++++ ui/gm-preferences.glade | 1539 ++++++++++++++++++++++++ ui/gm-scripts.glade | 357 ++++++ ui/gm-triggers.glade | 459 ++++++++ ui/gm-ui.xml | 40 + ui/gm-world-info.glade | 559 +++++++++ ui/gm-world-properties.glade | 864 ++++++++++++++ ui/gm-world.glade | 242 ++++ ui/gm-worlds-list.glade | 310 +++++ 133 files changed, 33249 insertions(+) create mode 100644 .tm_project.cache create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 aclocal.m4 create mode 100755 autogen.sh create mode 100644 configure.in create mode 100644 gnoemoe-logo.svg create mode 100644 gnoemoe.desktop create mode 100644 gnoemoe.xml create mode 100644 moo.lang create mode 100644 pixmaps/Makefile.am create mode 100644 pixmaps/editor.xpm create mode 100644 pixmaps/editor_text.xpm create mode 100644 pixmaps/editor_verb.xpm create mode 100644 pixmaps/gnoemoe_logo.svg create mode 100644 pixmaps/ice-userlist/Makefile.am create mode 100644 pixmaps/ice-userlist/avail+idle.svg create mode 100644 pixmaps/ice-userlist/away+idle.svg create mode 100644 pixmaps/ice-userlist/away.svg create mode 100644 pixmaps/ice-userlist/busy+idle.svg create mode 100644 pixmaps/ice-userlist/busy.svg create mode 100644 pixmaps/ice-userlist/inhabitant.svg create mode 100644 pixmaps/ice-userlist/programmer.svg create mode 100644 pixmaps/ice-userlist/wizard.svg create mode 100644 pixmaps/saveclose.xpm create mode 100644 pixmaps/terminal.png create mode 100644 pixmaps/tray/Makefile.am create mode 100644 pixmaps/tray/activate.svg create mode 100644 pixmaps/tray/active.svg create mode 100644 pixmaps/tray/default.svg create mode 100644 pixmaps/tray/notify.svg create mode 100644 pixmaps/userlist/Makefile.am create mode 100644 pixmaps/userlist/all.svg create mode 100644 pixmaps/userlist/away.svg create mode 100644 pixmaps/userlist/friend.svg create mode 100644 pixmaps/userlist/idle.svg create mode 100644 pixmaps/userlist/idleaway.svg create mode 100644 pixmaps/userlist/inhabitant.svg create mode 100644 pixmaps/userlist/inhabitantplus.svg create mode 100644 pixmaps/userlist/key.svg create mode 100644 pixmaps/userlist/newbie.svg create mode 100644 pixmaps/userlist/schooled.svg create mode 100644 pixmaps/userlist/star.svg create mode 100644 pixmaps/userlist/wizard.svg create mode 100644 pixmaps/world.svg create mode 100644 pixmaps/world_activity.svg create mode 100644 po/ChangeLog create mode 100644 po/Makefile.in.in create mode 100644 po/POTFILES.in create mode 100644 po/nl.gmo create mode 100644 po/nl.po create mode 100644 scripts/Makefile.am create mode 100644 scripts/editing.rb create mode 100644 scripts/misc.rb create mode 100644 scripts/music.rb create mode 100644 scripts/run.rb create mode 100644 src/.swp create mode 100644 src/Makefile.am create mode 100644 src/ansi.h create mode 100644 src/debug.c create mode 100644 src/debug.h create mode 100755 src/gengtkclass.rb create mode 100644 src/gm-app-view.c create mode 100644 src/gm-app-view.h create mode 100644 src/gm-app.c create mode 100644 src/gm-app.h create mode 100644 src/gm-bogus.h create mode 100644 src/gm-color-table.c create mode 100644 src/gm-color-table.h create mode 100644 src/gm-editor.c create mode 100644 src/gm-editor.h create mode 100644 src/gm-marshal.list create mode 100644 src/gm-net.c create mode 100644 src/gm-net.h create mode 100644 src/gm-options.c create mode 100644 src/gm-options.h create mode 100644 src/gm-pixbuf.c create mode 100644 src/gm-pixbuf.h create mode 100644 src/gm-script.c.old create mode 100644 src/gm-script.h.old create mode 100644 src/gm-scripts-dialog.c create mode 100644 src/gm-scripts-dialog.h create mode 100644 src/gm-scripts.c create mode 100644 src/gm-scripts.h create mode 100644 src/gm-string.c create mode 100644 src/gm-string.h create mode 100644 src/gm-support.c create mode 100644 src/gm-support.h create mode 100644 src/gm-tray.c create mode 100644 src/gm-tray.h create mode 100644 src/gm-triggers-dialog.c create mode 100644 src/gm-triggers-dialog.h create mode 100644 src/gm-triggers.c create mode 100644 src/gm-triggers.h create mode 100644 src/gm-ui.h create mode 100644 src/gm-world-info-dialog.c create mode 100644 src/gm-world-info-dialog.h create mode 100644 src/gm-world-input-view.c create mode 100644 src/gm-world-input-view.h create mode 100644 src/gm-world-logs-dialog.c create mode 100644 src/gm-world-logs-dialog.h create mode 100644 src/gm-world-properties-dialog.c create mode 100644 src/gm-world-properties-dialog.h create mode 100644 src/gm-world-tab.c create mode 100644 src/gm-world-tab.h create mode 100644 src/gm-world-text-view.c create mode 100644 src/gm-world-text-view.h create mode 100644 src/gm-world-view.c create mode 100644 src/gm-world-view.h create mode 100644 src/gm-world.c create mode 100644 src/gm-world.h create mode 100644 src/gm-worlds-list-dialog.c create mode 100644 src/gm-worlds-list-dialog.h create mode 100644 src/gtktemplate.c create mode 100644 src/gtktemplate.h create mode 100644 stamp-h.in create mode 100644 ui/Makefile.am create mode 100755 ui/fixglade.rb create mode 100644 ui/gm-editor.glade create mode 100644 ui/gm-main.glade create mode 100644 ui/gm-preferences.glade create mode 100644 ui/gm-scripts.glade create mode 100644 ui/gm-triggers.glade create mode 100644 ui/gm-ui.xml create mode 100644 ui/gm-world-info.glade create mode 100644 ui/gm-world-properties.glade create mode 100644 ui/gm-world.glade create mode 100644 ui/gm-worlds-list.glade diff --git a/.tm_project.cache b/.tm_project.cache new file mode 100644 index 0000000..e69de29 diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..997656f --- /dev/null +++ b/AUTHORS @@ -0,0 +1,2 @@ +Jesse van den Kieboom +Sjoerd Simons (Debian package maintainer) diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..89fb9a5 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,479 @@ +2005-15-06 Jesse van den Kieboom + + * VERSION CHANGED TO 0.9.1 + * if_scripts: Fixed write perms checking + * if_main: beautified about dialog + +2005-14-06 Jesse van den Kieboom + + * VERSION CHANGED TO 0.9.0 + +2005-14-06 Jesse van den Kieboom + + * mcp/mcp_userlist: added userlist events for triggering + * mcp/mcp_ice_userlist mcp/mcp_ice_serverinfo: added loading of + image alternatives (following specs), fixed invalid uri detection + * if_triggers: added entrycompletion for scripts instead of dropdown + * if_main: searching now happens while-you-type finding the first + match and coloring the background red (like ff) if none found. + added drag-and-drop tabs (live moving) + * world: added inverse ansi, added invisible ansi + * if_world: improved keeping text window end scrolled + * if_world_listing: list shows world info (server and player) and + uses a custom icon when provided by dns-nl-icecrew-serverinfo. + load on startup is moved to if_world_properties + * if_world_properties: load on startup is now provided by this dialog + rather than by if_world_listing + +2005-07-06 Jesse van den Kieboom + + * if_scripts: fixed crashing when one time opened + * scripts/music: added copyright for Simon Gijsen + * if_main: added basic searching firefox style. + searches can take place in worlds, editors and + logs + +2005-07-06 Jesse van den Kieboom + + * ui/: seperated interfaces over several single glade + files. + * mcp/mcp_ice_serverinfo: added serverinfo mcp package + (see http://www.icecrew.nl/software/mcp for specs) + * triggers if_triggers world if_world_properties: added trigger support. + gnoemoe now handles two types of triggers for all sorts + of events. Check it out. + * world: removed stale functions, removed transparent background + fixed default charset loading, fixed/improved activity updates, + added reconnect foolproof (fixes problems with multiple + autoreconnects), added basic searching + * debug: loglevel 0 now goes to stdout, > 0 goes to stderr + +2005-06-06 Jesse van den Kieboom + + * net: added connect timeout + * if_main world: added log opening support + * if_mcpconsole: fixed crashing, mcp console window is now + reintroduced + * if_main: added serverinfo window, improved notification area + stuff. It's now just a notification icon, showing when there + is activity and interactive in combination with triggers. + added log opening dialog + * if_ editor: fixed some memory leaks (no severe ones) + +2005-01-06 Jesse van den Kieboom + + * VERSION CHANGED TO 0.8.2 + * Fixed dutch translations + * net.c: Fixed connecting weirdness + * if_scripts.c: Fixed ruby mime type + +2005-10-05 Jesse van den Kieboom + + * if_world.c: Added sending of empty lines + * editor.c gnoe_pixbuf.c world.c: Fixed possible + GError problems + +2005-27-03 Jesse van den Kieboom + + * scripts/music.rb: Added xmms function (by Simon Gijsen) + +2005-18-01 Jesse van den Kieboom + + * if_world_properties.c if_worlds_listing.c script.c + support.c world.c: Changed all declarations to be + at the top of a function (patch for FreeBSD + 4.x by Pav Lucistnik) + * if_world.c world.c: Changed int to gsize + (patch for OSX by Christian Luijten) + * if_world_properties.c: Moved encodings declaration + from header to source file (patch for OSX by + Christian Luijten) + +2004-29-12 Jesse van den Kieboom + + * world.c: added sane default charset (ISO-8859-15) + * support.c: Added trailing / in url regexp + (e.g. http://www.icecrew.nl/) + +2004-14-12 Jesse van den Kieboom + + * VERSION CHANGED TO 0.8.1 + * Makefile.am: fixed intltool problems + * gnoemoe.desktop: changed icon to svg (which it already should have + been, the png version doesn't get installed!) + +2004-13-12 Jesse van den Kieboom + + * VERSION CHANGED TO 0.8.0 + * po: Added/Fixed Dutch (nl) translations + * if_main.c: changed GnoeMoe GTK+ MOO Client to GnoeMoe Gnome MOO Client + * if_world_properties.c: fixed bacause typo + +2004-13-12 Jesse van den Kieboom + + * support.c: added a remote file fetcher function which uses gnomevfs + to async fetch files from a remote system + * mcp_ice_userlist.c: added ranks and states messages which handle + remote rank and state icons + * world.c: fixed creation of tags with bold enabled while bold-colors + was set + * if_scripts.c: fixed segmentation fault when loading a second script + in the editor (curEdit wasn't reset to NULL, free didn't like it) + * gnoe_pixbuf.c: added test for loading icons with absolute paths + +2004-21-10 Jesse van den Kieboom + * mcp_ice_userlist.c: the P_NAME property now also has a signal connected + so name changes will be detected by the userlist + +2004-21-10 Jesse van den Kieboom + + * world.c: fixed color problems (adding of to much tags and bold problems) + * configure.in: fixed regexp include problem when ruby enabled + +2004-13-10 Jesse van den Kieboom + + * Added url matching, underlining and action-taking in the world + * Added notification tray icon (when GnoeMoe is hidden and new text + is added to a world the icon will blink showing there is activity in + one of the worlds, the tooltip will indicate which worlds and how + many lines). Blinking is only done when GnoeMoe is hidden which can + be achieved by clicking on the icon or by activating the Hide GnoeMoe + menu item (in the popup menu of the notification icon). Same goes + for re-showing GnoeMoe + * Added application icon state. The application icon will now show there + is activity in worlds by changing its icon. This only happens when + GnoeMoe is not the top-level activated window + +2004-10-10 Jesse van den Kieboom + + * VERSION CHANGED TO 0.7.1 + * Fixed compile error + +2004-09-10 Jesse van den Kieboom + + * VERSION CHANGED TO 0.7.0 + * No more Run menu + * Added NL translation (dutch) + * if_worlds_listing.c: fixed SEGV on duplicating world + +2004-09-10 Jesse van den Kieboom + + * main.c: Added --load option for loading worlds from the commandline + Improved gnoe_pixbuf initializing and finalizing + Fixed scripts initializing to happen before world auto loading + * world.c: Moved userlist API to the world to abstract it from the + mcp modules + * widgets/mcp_ping: Removed the lagmeter + * gnoe_pixbuf: Added initializing/finalizing, + removed external librsvg + * src/Makefile.am: Mcp packages now reside in mcp/ + +2004-08-04 Jesse van den Kieboom + + * script.c: scripts directories are now monitored via GnomeVFS to + automagically update changes in scripts + * if_main.c: window title now shows active world + +2004-08-04 Jesse van den Kieboom + + * VERSION CHANGED TO 0.6.4 + * world.c: fixed auto-reconnect + fixed on_disconnect script function call which could crash on + unloading a world + * net.c: fixed auto-reconnect + * if_mcpconsole.c: added world loaded check for if_mcpconsole_add + * editor.c: fixed crash on editor_fini + * if_main.c: added editor switch accelerators (Ctrl-) + +2004-07-28 Jesse van den Kieboom + + * VERSION CHANGED TO 0.6.3 + * logo: newly very improved logo, veeeery nice one, by Simon Gijsen + * pixmaps/userlist/default.map: fixed inhabitant/inhabitant+ switch + +2004-07-28 Jesse van den Kieboom + + * userlist.c: improved sorting + * gnoe_pixbuf.c: database of pixbufs, normal files as well as svg's + * script.c: added host and port functions to world class + improved connect function of world class + * net.c: fixed setting of net status flag on disconnect before calling + world_disconnected + +2004-07-27 Jesse van den Kieboom + + * VERSION CHANGED TO 0.6.2 + * userlist.c: now using svg image mapping for icons + * pixmaps/userlist: contains new svg image containing all the userlist + icons and 1 map file for mapping the svg to nameble icons + * script.c: added script function description support + WARNING: ALL SCRIPTS MUST!! HAVE A DESCRIPTION NOW, USERS NEED + TO CHANGE THIS IN THEIR OWN SCRIPTS + * world.c: fixed wrong month in logging + * script.c: added auto-creation of USER scripts directory + +2004-07-19 Jesse van den Kieboom + + * VERSION CHANGED TO 0.6.1 + * pixmaps/icons: improved new icons from Simon Gijsen + * gnoemoe.glade: fixed width resize on long input + +2004-07-19 Jesse van den Kieboom + + * editor.c: fixed font unref stupid + * gnoemoe.glade: fixed window width stretching + +2004-07-19 Jesse van den Kieboom + + * VERSION CHANGED TO 0.6.0 + * scripts/misc.rb: added sendall script + * scripts/editing.rb: added editing scripts (/e, /de, /me, /ne, /he) + * gnoemoe.glade: added mcpconsole interface, added scripts interface + * configure.in: added ruby checking and compile status + +2004-07-19 Jesse van den Kieboom + + * userlist.c: improved scroll handler timeout to be removed when + the userlist is destroyed + * packages.c/mcp.c: added callbacks to log messages to the mcpconsole + * if_world_properties.c: fixed renaming problem (the dir wouldn't + get renamed and thus the world got duplicated) + * editor.c: added unref of font description + +2004-07-17 Jesse van den Kieboom + + * if_scripts.c: added, interface for manipulating and viewing + scripts (editor/console/overview/flush&reload) + * world.c: improved userlist width saving/restoring + * world.c: added on_connect, on_login, on_disconnect script calls + * world.c: added mcp text buffer + * if_mcpconsole.c: added, interface for viewing mcp messages + +2004-07-16 Jesse van den Kieboom + + * script.c: added input method for world class (this gives the + opportunity for recursion) + * script.c: added connect method for world class + * script.c: added disconnect method for world class + * script.c: added quit method for world class + * script.c: improved function run + * script.c: added flush&reload function + * script.c: added script function aliassing + +2004-07-14 Jesse van den Kieboom + + * VERSION CHANGED TO 0.5.0 + * script.c: added open method for client class + * script.c: added connected? method for world class + * script.c: added shared scripts dir + * scripts/*: added some scripts + * configure.in/Makefile.am: added installation of scripts + +2004-07-14 Jesse van den Kieboom + + * world.c: added some documentation + * world.c: added main_process_input function + * script.c/script.h: added, GnoeMoe now has ruby scripting support + * main.c: added main_get_app_path function + +2004-07-12 Jesse van den Kieboom + + * VERSION CHANGED TO 0.4.3 + * if_world.c: added world settings saving on destroy + needed for userlist width saving + +2004-07-09 Jesse van den Kieboom + + * pixmap.c: added function to set alpha channel of a pixbuf + * world.c/if_world.c/if_preferences.c/main.c/gnoemoe/glade: + added background transparancy + +2004-07-07 Jesse van den Kieboom + + * VERSION CHANGED TO 0.4.2 + * if_main.c: changed Ctrl- to Alt- (used for tab switching) to + match other gnome apps + * net.c: disconnect on sendline fail + +2004-07-06 Jesse van den Kieboom + + * net.c: improved connecting (its now non-blocking) + +2004-07-03 Jesse van den Kieboom + + * VERSION CHANGED TO 0.4.1 + * packages.c: fixed (added) decimal point conversion in negotiate + package + +2004-07-02 Jesse van den Kieboom + + * VERSION CHANGED TO 0.4 + * world.c/world.h: added update_character_metrics function, calculates + character width (changes when font changes, is used to calculate + linelen etc.) + * packages.c: improved linelen calculation, its now more accurate + * support.h/support.c: added function for length calculation of char ** + * main.c/if_main.c: added window metrics saving/restoring + * if_preferences.c/editor.c: added builtin editor colors/styles + configuration + +2004-07-01 Jesse van den Kieboom + + * support.h: added documentation + * support.c: added functions for decimal point conversion (used mainly + by mcp version number conversions) + * mcp.c/packages.c: added decimal point conversion for package versions + and mcp versions (needed cause some locales use . and some use ,) + +2004-06-29 Jesse van den Kieboom + + * world.h/world.c: added documentation for all functions/enums/structs + * world.c: removed functions world_get_by_index, world_paste + added high color support for bold text + * ansi.h: added bold color definitions, added documentation + * editor.c: added editor_init and editor_fini functions to setup the + editor environment and to finish the editor environment + all editors will now use the same GtkSourceLanguage (which is stored + from within editors_init) + * if_world.c: fixed Pageup/Pagedown key bindings to be ignored when + Ctrl or Shift is pressed + * gnoemoe.glade: added editor color configuration in dlgPreferences + +2004-06-24 Jesse van den Kieboom + + * VERSION CHANGED TO 0.3.1 + * gnoemoe.glade: removed Properties/Close/Quit/Preferences button + (Dis)Connect label changed to Connect + fixed half sized toolbar + +2004-06-24 Jesse van den Kieboom + + * VERSION CHANGED TO 0.3.0 + + * gnoemoe.glade: added buttons for easy access to world functions + * if_world.c/if_main.c: added functions to handle new buttons ( + world - connect/disconnect/properties/close + main - preferences/quit + * if_world.c/if_world.h: added world_connected function which is now + called from net_do_connect. Autologin now resides in this new function + * if_main.c: added accelerators for cycling through pages (F6/Shift F6) + removed F4 accelerator for connect/disconnect for now + set appropriate sensitivity of world tool buttons (depends whether + a world or a terminal is active) + * if_world_properties.c/if_world_properties.h: added option for + charset selection + * world.c/world.h/editor.c/if_world.c: added better scroll to bottom + functions in world.c/world.h + editor.c and if_world.c make use of these functions to keep scrolling + sticky + * main.c: changed colors to be more natural to the eye + +2004-06-24 Jesse van den Kieboom + + * if_world.c: added pageup/pagedown for world text view + +2004-06-21 Jesse van den Kieboom + + * all.c/all.h: reformatted + * all.c/all.h: removed profiles, color/font can now be configured + in preferences + +2004-06-21 Jesse van den Kieboom + + * world.c: fixed focussing of input widget on world + activation (added doEvents()) + * world.c/support.c: replaced \e by \x1B + * if_world.c: removed dos newlines + * if_main.c: added enable/disable of cut/copy/paste + menu items (disabled for terminal tabs, enabled for + world tabs) + +2004-06-21 Jesse van den Kieboom + + * if_main.c: implemented cut/copy/paste menu items + * if_world.c/world.c: better implementation for saving + userlist width (now saving hpane position) + * if_world.c: changed active state base color for + userlist and textview (they are unfocus-able but + its nicer to have the same selection colors as if they + can actually focus) + * userlist.c/pixmaps/icons: changed icons from xpm to + png + +2004-06-21 Jesse van den Kieboom + + * gnoemoe.glade: replaced input entry widget by textview + widget (for multiline input purposes) + reorganised world view layout + * if_world.c/world.c: replaced input entry widget by + textview widget (for multiline input purposes) + * editor.c: fixed charset conversion for saving editor file + (from current locale to UTF-8) + * world.c: fixed userlist width saving shrinks userlist + by 3 pixels + + +2004-06-20 Jesse van den Kieboom + + * VERSION CHANGED TO 0.2.4 + + +2004-06-17 Jesse van den Kieboom + + * net.c: fixed 100% CPU problem, removal of channel watch + +2004-06-17 Jesse van den Kieboom + + * editor.c/support.c/world.c: function to let the main loop + process GUI events is now in support.c + * userlist.c: fixed scrolling problem (scroll to top on + user add if it was already scrolled to the top) + +2004-06-17 Jesse van den Kieboom + + * editor.c: corrected charset conversion, + now converting to current locale for external editors + (which use files), and to UTF-8 for the internal editor, + fixed signal connecting. signals for save/save&close/close + buttons were also tried for VTE editors, wrong! + * pixmaps: cleaned up unused pixmaps, + removed different logo size pixmaps + * gnoemoe.glade: use 48x48 for icon + +2004-06-17 Jesse van den Kieboom + + * Makefile.am: added install of moo.lang file, + needed to enable code highlighting in gtksourceview + * moo.lang: added + * world.c: the input entry now will use the same font + as the main text window will do + +2004-06-17 Jesse van den Kieboom + + * Makefile.am: Removed $(DESTDIR) + * pixmaps/Makefile.am: Removed $(DESTDIR) + * pixmaps/icons/Makefile.am: Removed $(DESTDIR) + + +2004-06-16 Jesse van den Kieboom + + * Logo: added gnoemoe logo + * Makefile.am: distributed makefile for pixmaps and + pixmaps/icons to seperate Makefile.am's, + pixmaps are now installed in pimaps subdir, + added gnoemoe-logo.png as logo, + * gnoemoe.glade: removed main status bar, + fixed all pixmaps to point to pixmaps/.... + * interface: added warning/info/error dialogs to inform + user for faulty actions (or not yet implemented actions) + * all: added gnome support, gnoemoe now accepts session + saving and it uses the gnome command parsing + * net.c: world_send_line now blocks if it can't send + all the data at once + * if_world.c: input widget now has monospace font + * world.c: added logging + * main.c: removed save_size option + * if_world_properties_listing: fixed SEGV problem when + adding a new world + * pixmaps: added gnoemoe_logo_about.png for the aboutbox + * Makefile.am: Added gnoemoe.glade (after removing it) diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..54caf7c --- /dev/null +++ b/INSTALL @@ -0,0 +1,229 @@ +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software +Foundation, Inc. + + This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the `--target=TYPE' option to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +will cause the specified gcc to be used as the C compiler (unless it is +overridden in the site shell script). + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..22de50e --- /dev/null +++ b/Makefile.am @@ -0,0 +1,30 @@ +## Process this file with automake to produce Makefile.in + +SUBDIRS = src po pixmaps scripts ui + +DISTCLEANFILES = \ + intltool-extract \ + intltool-merge \ + intltool-update + +EXTRA_DIST = \ + gnoemoe.desktop \ + gnoemoe-logo.svg \ + moo.lang \ + gnoemoe.xml \ + autogen.sh \ + intltool-extract.in \ + intltool-merge.in \ + intltool-update.in + +Applicationsdir = $(datadir)/applications +Applications_DATA = gnoemoe.desktop + +icondir = $(datadir)/pixmaps +icon_DATA = gnoemoe-logo.svg + +langdir = $(datadir)/gtksourceview-1.0/language-specs +lang_DATA = moo.lang + +mimedir = $(datadir)/mime/packages +mime_DATA = gnoemoe.xml diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..e69de29 diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..4f281b5 --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,1682 @@ +# generated automatically by aclocal 1.7.9 -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# Do all the work for Automake. -*- Autoconf -*- + +# This macro actually does too much some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 +# Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 10 + +AC_PREREQ([2.54]) + +# Autoconf 2.50 wants to disallow AM_ names. We explicitly allow +# the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl + AC_REQUIRE([AC_PROG_INSTALL])dnl +# test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) + AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) +AM_MISSING_PROG(AUTOCONF, autoconf) +AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) +AM_MISSING_PROG(AUTOHEADER, autoheader) +AM_MISSING_PROG(MAKEINFO, makeinfo) +AM_MISSING_PROG(AMTAR, tar) +AM_PROG_INSTALL_SH +AM_PROG_INSTALL_STRIP +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl + +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_CC], + defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_CXX], + defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl +]) +]) + + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $1 | $1:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count]) + +# Copyright 2002 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +AC_DEFUN([AM_AUTOMAKE_VERSION],[am__api_version="1.7"]) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION so it can be traced. +# This function is AC_REQUIREd by AC_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], + [AM_AUTOMAKE_VERSION([1.7.9])]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright 2001, 2002 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 2 + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# ------------------------------ +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), 1)]) + +# _AM_SET_OPTIONS(OPTIONS) +# ---------------------------------- +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# +# Check to make sure that the build environment is sane. +# + +# Copyright 1996, 1997, 2000, 2001 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 3 + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT(yes)]) + +# -*- Autoconf -*- + + +# Copyright 1997, 1999, 2000, 2001 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 3 + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it supports --run. +# If it does, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + AC_MSG_WARN([`missing' script is too old or missing]) +fi +]) + +# AM_AUX_DIR_EXPAND + +# Copyright 2001 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to +# `$srcdir', `$srcdir/..', or `$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is `.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +# Rely on autoconf to set up CDPATH properly. +AC_PREREQ([2.50]) + +AC_DEFUN([AM_AUX_DIR_EXPAND], [ +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` +]) + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. + +# Copyright 2001 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +install_sh=${install_sh-"$am_aux_dir/install-sh"} +AC_SUBST(install_sh)]) + +# AM_PROG_INSTALL_STRIP + +# Copyright 2001 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# One issue with vendor `install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in `make install-strip', and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be `maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# -*- Autoconf -*- +# Copyright (C) 2003 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 1 + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# serial 5 -*- Autoconf -*- + +# Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "GCJ", or "OBJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +ifelse([$1], CC, [depcc="$CC" am_compiler_list=], + [$1], CXX, [depcc="$CXX" am_compiler_list=], + [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + : > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # (even with -Werror). So we grep stderr for any message + # that says an option was ignored. + if grep 'ignoring option' conftest.err >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE(dependency-tracking, +[ --disable-dependency-tracking Speeds up one-time builds + --enable-dependency-tracking Do not reject slow dependency extractors]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH]) +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +#serial 2 + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[for mf in $CONFIG_FILES; do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # So let's grep whole file. + if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + grep '^DEP_FILES *= *[[^ @%:@]]' < "$mf" > /dev/null || continue + # Extract the definition of DEP_FILES from the Makefile without + # running `make'. + DEPDIR=`sed -n -e '/^DEPDIR = / s///p' < "$mf"` + test -z "$DEPDIR" && continue + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n -e '/^U = / s///p' < "$mf"` + test -d "$dirpart/$DEPDIR" || mkdir "$dirpart/$DEPDIR" + # We invoke sed twice because it is the simplest approach to + # changing $(DEPDIR) to its actual value in the expansion. + for file in `sed -n -e ' + /^DEP_FILES = .*\\\\$/ { + s/^DEP_FILES = // + :loop + s/\\\\$// + p + n + /\\\\$/ b loop + p + } + /^DEP_FILES = / s/^DEP_FILES = //p' < "$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each `.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 2 + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo done +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright 1997, 2000, 2001 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 5 + +AC_PREREQ(2.52) + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE]) +AC_SUBST([$1_FALSE]) +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]) +fi])]) + +# Like AC_CONFIG_HEADER, but automatically create stamp file. -*- Autoconf -*- + +# Copyright 1996, 1997, 2000, 2001 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +AC_PREREQ([2.52]) + +# serial 6 + +# AM_CONFIG_HEADER is obsolete. It has been replaced by AC_CONFIG_HEADERS. +AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)]) + +# Add --enable-maintainer-mode option to configure. +# From Jim Meyering + +# Copyright 1996, 1998, 2000, 2001, 2002 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 2 + +AC_DEFUN([AM_MAINTAINER_MODE], +[AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) + dnl maintainer-mode is disabled by default + AC_ARG_ENABLE(maintainer-mode, +[ --enable-maintainer-mode enable make rules and dependencies not useful + (and sometimes confusing) to the casual installer], + USE_MAINTAINER_MODE=$enableval, + USE_MAINTAINER_MODE=no) + AC_MSG_RESULT([$USE_MAINTAINER_MODE]) + AM_CONDITIONAL(MAINTAINER_MODE, [test $USE_MAINTAINER_MODE = yes]) + MAINT=$MAINTAINER_MODE_TRUE + AC_SUBST(MAINT)dnl +] +) + +AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE]) + +# isc-posix.m4 serial 2 (gettext-0.11.2) +dnl Copyright (C) 1995-2002 Free Software Foundation, Inc. +dnl This file is free software, distributed under the terms of the GNU +dnl General Public License. As a special exception to the GNU General +dnl Public License, this file may be distributed as part of a program +dnl that contains a configuration script generated by Autoconf, under +dnl the same distribution terms as the rest of that program. + +# This file is not needed with autoconf-2.53 and newer. Remove it in 2005. + +# This test replaces the one in autoconf. +# Currently this macro should have the same name as the autoconf macro +# because gettext's gettext.m4 (distributed in the automake package) +# still uses it. Otherwise, the use in gettext.m4 makes autoheader +# give these diagnostics: +# configure.in:556: AC_TRY_COMPILE was called before AC_ISC_POSIX +# configure.in:556: AC_TRY_RUN was called before AC_ISC_POSIX + +undefine([AC_ISC_POSIX]) + +AC_DEFUN([AC_ISC_POSIX], + [ + dnl This test replaces the obsolescent AC_ISC_POSIX kludge. + AC_CHECK_LIB(cposix, strerror, [LIBS="$LIBS -lcposix"]) + ] +) + + +# Copyright 1996, 1997, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 2 + +# @defmac AC_PROG_CC_STDC +# @maindex PROG_CC_STDC +# @ovindex CC +# If the C compiler in not in ANSI C mode by default, try to add an option +# to output variable @code{CC} to make it so. This macro tries various +# options that select ANSI C on some system or another. It considers the +# compiler to be in ANSI C mode if it handles function prototypes correctly. +# +# If you use this macro, you should check after calling it whether the C +# compiler has been set to accept ANSI C; if not, the shell variable +# @code{am_cv_prog_cc_stdc} is set to @samp{no}. If you wrote your source +# code in ANSI C, you can make an un-ANSIfied copy of it by using the +# program @code{ansi2knr}, which comes with Ghostscript. +# @end defmac + +AC_DEFUN([AM_PROG_CC_STDC], +[AC_REQUIRE([AC_PROG_CC]) +AC_BEFORE([$0], [AC_C_INLINE]) +AC_BEFORE([$0], [AC_C_CONST]) +dnl Force this before AC_PROG_CPP. Some cpp's, eg on HPUX, require +dnl a magic option to avoid problems with ANSI preprocessor commands +dnl like #elif. +dnl FIXME: can't do this because then AC_AIX won't work due to a +dnl circular dependency. +dnl AC_BEFORE([$0], [AC_PROG_CPP]) +AC_MSG_CHECKING([for ${CC-cc} option to accept ANSI C]) +AC_CACHE_VAL(am_cv_prog_cc_stdc, +[am_cv_prog_cc_stdc=no +ac_save_CC="$CC" +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + AC_TRY_COMPILE( +[#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +], [ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; +], +[am_cv_prog_cc_stdc="$ac_arg"; break]) +done +CC="$ac_save_CC" +]) +if test -z "$am_cv_prog_cc_stdc"; then + AC_MSG_RESULT([none needed]) +else + AC_MSG_RESULT([$am_cv_prog_cc_stdc]) +fi +case "x$am_cv_prog_cc_stdc" in + x|xno) ;; + *) CC="$CC $am_cv_prog_cc_stdc" ;; +esac +]) + +AU_DEFUN([fp_PROG_CC_STDC], [AM_PROG_CC_STDC]) + + +dnl IT_PROG_INTLTOOL([MINIMUM-VERSION], [no-xml]) +# serial 2 IT_PROG_INTLTOOL +AC_DEFUN([IT_PROG_INTLTOOL], +[ + +if test -n "$1"; then + AC_MSG_CHECKING(for intltool >= $1) + + INTLTOOL_REQUIRED_VERSION_AS_INT=`echo $1 | awk -F. '{ printf "%d", $[1] * 100 + $[2]; }'` + INTLTOOL_APPLIED_VERSION=`awk -F\" '/\\$VERSION / { printf $[2]; }' < ${ac_aux_dir}/intltool-update.in` + changequote({{,}}) + INTLTOOL_APPLIED_VERSION_AS_INT=`awk -F\" '/\\$VERSION / { split(${{2}}, VERSION, "."); printf "%d\n", VERSION[1] * 100 + VERSION[2];}' < ${ac_aux_dir}/intltool-update.in` + changequote([,]) + + if test "$INTLTOOL_APPLIED_VERSION_AS_INT" -ge "$INTLTOOL_REQUIRED_VERSION_AS_INT"; then + AC_MSG_RESULT([$INTLTOOL_APPLIED_VERSION found]) + else + AC_MSG_RESULT([$INTLTOOL_APPLIED_VERSION found. Your intltool is too old. You need intltool $1 or later.]) + exit 1 + fi +fi + + INTLTOOL_DESKTOP_RULE='%.desktop: %.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' +INTLTOOL_DIRECTORY_RULE='%.directory: %.directory.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_KEYS_RULE='%.keys: %.keys.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -k -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_PROP_RULE='%.prop: %.prop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_OAF_RULE='%.oaf: %.oaf.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -o -p $(top_srcdir)/po $< [$]@' + INTLTOOL_PONG_RULE='%.pong: %.pong.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_SERVER_RULE='%.server: %.server.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -o -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_SHEET_RULE='%.sheet: %.sheet.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' +INTLTOOL_SOUNDLIST_RULE='%.soundlist: %.soundlist.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_UI_RULE='%.ui: %.ui.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_XML_RULE='%.xml: %.xml.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_XML_NOMERGE_RULE='%.xml: %.xml.in $(INTLTOOL_MERGE) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u /tmp $< [$]@' + INTLTOOL_XAM_RULE='%.xam: %.xml.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_KBD_RULE='%.kbd: %.kbd.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -m -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_CAVES_RULE='%.caves: %.caves.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_SCHEMAS_RULE='%.schemas: %.schemas.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -s -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_THEME_RULE='%.theme: %.theme.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + +AC_SUBST(INTLTOOL_DESKTOP_RULE) +AC_SUBST(INTLTOOL_DIRECTORY_RULE) +AC_SUBST(INTLTOOL_KEYS_RULE) +AC_SUBST(INTLTOOL_PROP_RULE) +AC_SUBST(INTLTOOL_OAF_RULE) +AC_SUBST(INTLTOOL_PONG_RULE) +AC_SUBST(INTLTOOL_SERVER_RULE) +AC_SUBST(INTLTOOL_SHEET_RULE) +AC_SUBST(INTLTOOL_SOUNDLIST_RULE) +AC_SUBST(INTLTOOL_UI_RULE) +AC_SUBST(INTLTOOL_XAM_RULE) +AC_SUBST(INTLTOOL_KBD_RULE) +AC_SUBST(INTLTOOL_XML_RULE) +AC_SUBST(INTLTOOL_XML_NOMERGE_RULE) +AC_SUBST(INTLTOOL_CAVES_RULE) +AC_SUBST(INTLTOOL_SCHEMAS_RULE) +AC_SUBST(INTLTOOL_THEME_RULE) + +# Use the tools built into the package, not the ones that are installed. + +INTLTOOL_EXTRACT='$(top_builddir)/intltool-extract' +INTLTOOL_MERGE='$(top_builddir)/intltool-merge' +INTLTOOL_UPDATE='$(top_builddir)/intltool-update' + +AC_SUBST(INTLTOOL_EXTRACT) +AC_SUBST(INTLTOOL_MERGE) +AC_SUBST(INTLTOOL_UPDATE) + +AC_PATH_PROG(INTLTOOL_PERL, perl) +if test -z "$INTLTOOL_PERL"; then + AC_MSG_ERROR([perl not found; required for intltool]) +fi +if test -z "`$INTLTOOL_PERL -v | fgrep '5.' 2> /dev/null`"; then + AC_MSG_ERROR([perl 5.x required for intltool]) +fi +if test "x$2" != "xno-xml"; then + AC_MSG_CHECKING([for XML::Parser]) + if `$INTLTOOL_PERL -e "require XML::Parser" 2>/dev/null`; then + AC_MSG_RESULT([ok]) + else + AC_MSG_ERROR([XML::Parser perl module is required for intltool]) + fi +fi + +AC_PATH_PROG(INTLTOOL_ICONV, iconv, iconv) +AC_PATH_PROG(INTLTOOL_MSGFMT, msgfmt, msgfmt) +AC_PATH_PROG(INTLTOOL_MSGMERGE, msgmerge, msgmerge) +AC_PATH_PROG(INTLTOOL_XGETTEXT, xgettext, xgettext) + +# Remove file type tags (using []) from po/POTFILES. + +ifdef([AC_DIVERSION_ICMDS],[ + AC_DIVERT_PUSH(AC_DIVERSION_ICMDS) + changequote(,) + mv -f po/POTFILES po/POTFILES.tmp + sed -e '/\[encoding.*\]/d' -e 's/\[.*\] *//' < po/POTFILES.tmp > po/POTFILES + rm -f po/POTFILES.tmp + changequote([,]) + AC_DIVERT_POP() +],[ + ifdef([AC_CONFIG_COMMANDS_PRE],[ + AC_CONFIG_COMMANDS_PRE([ + changequote(,) + mv -f po/POTFILES po/POTFILES.tmp + sed -e '/\[encoding.*\]/d' -e 's/\[.*\] *//' < po/POTFILES.tmp > po/POTFILES + rm -f po/POTFILES.tmp + changequote([,]) + ]) + ]) + +if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then + # Keeping the `.' argument allows $(mkdir_p) to be used without + # argument. Indeed, we sometimes output rules like + # $(mkdir_p) $(somedir) + # where $(somedir) is conditionally defined. + # (`test -n '$(somedir)' && $(mkdir_p) $(somedir)' is a more + # expensive solution, as it forces Make to start a sub-shell.) + mkdir_p='mkdir -p -- .' +else + # On NextStep and OpenStep, the `mkdir' command does not + # recognize any option. It will interpret all options as + # directories to create, and then abort because `.' already + # exists. + for d in ./-p ./--version; + do + test -d $d && rmdir $d + done + # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists. + if test -f "$ac_aux_dir/mkinstalldirs"; then + mkdir_p='$(mkinstalldirs)' + else + mkdir_p='$(install_sh) -d' + fi +fi +AC_SUBST([mkdir_p]) +]) + +# Manually sed perl in so people don't have to put the intltool scripts in AC_OUTPUT. + +AC_CONFIG_COMMANDS([intltool], [ + +intltool_edit="-e 's#@INTLTOOL_EXTRACT@#`pwd`/intltool-extract#g' \ + -e 's#@INTLTOOL_ICONV@#${INTLTOOL_ICONV}#g' \ + -e 's#@INTLTOOL_MSGFMT@#${INTLTOOL_MSGFMT}#g' \ + -e 's#@INTLTOOL_MSGMERGE@#${INTLTOOL_MSGMERGE}#g' \ + -e 's#@INTLTOOL_XGETTEXT@#${INTLTOOL_XGETTEXT}#g' \ + -e 's#@INTLTOOL_PERL@#${INTLTOOL_PERL}#g'" + +eval sed ${intltool_edit} < ${ac_aux_dir}/intltool-extract.in \ + > intltool-extract.out +if cmp -s intltool-extract intltool-extract.out 2>/dev/null; then + rm -f intltool-extract.out +else + mv -f intltool-extract.out intltool-extract +fi +chmod ugo+x intltool-extract +chmod u+w intltool-extract + +eval sed ${intltool_edit} < ${ac_aux_dir}/intltool-merge.in \ + > intltool-merge.out +if cmp -s intltool-merge intltool-merge.out 2>/dev/null; then + rm -f intltool-merge.out +else + mv -f intltool-merge.out intltool-merge +fi +chmod ugo+x intltool-merge +chmod u+w intltool-merge + +eval sed ${intltool_edit} < ${ac_aux_dir}/intltool-update.in \ + > intltool-update.out +if cmp -s intltool-update intltool-update.out 2>/dev/null; then + rm -f intltool-update.out +else + mv -f intltool-update.out intltool-update +fi +chmod ugo+x intltool-update +chmod u+w intltool-update + +], INTLTOOL_PERL='${INTLTOOL_PERL}' ac_aux_dir=${ac_aux_dir} +INTLTOOL_EXTRACT='${INTLTOOL_EXTRACT}' ICONV='${INTLTOOL_ICONV}' +MSGFMT='${INTLTOOL_MSGFMT}' MSGMERGE='${INTLTOOL_MSGMERGE}' +XGETTEXT='${INTLTOOL_XGETTEXT}') + +]) + +# deprecated macros +AC_DEFUN([AC_PROG_INTLTOOL], [IT_PROG_INTLTOOL($@)]) + + + +dnl PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, action-not) +dnl defines GSTUFF_LIBS, GSTUFF_CFLAGS, see pkg-config man page +dnl also defines GSTUFF_PKG_ERRORS on error +AC_DEFUN(PKG_CHECK_MODULES, [ + succeeded=no + + if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + fi + + if test "$PKG_CONFIG" = "no" ; then + echo "*** The pkg-config script could not be found. Make sure it is" + echo "*** in your path, or set the PKG_CONFIG environment variable" + echo "*** to the full path to pkg-config." + echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config." + else + PKG_CONFIG_MIN_VERSION=0.9.0 + if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then + AC_MSG_CHECKING(for $2) + + if $PKG_CONFIG --exists "$2" ; then + AC_MSG_RESULT(yes) + succeeded=yes + + AC_MSG_CHECKING($1_CFLAGS) + $1_CFLAGS=`$PKG_CONFIG --cflags "$2"` + AC_MSG_RESULT($$1_CFLAGS) + + AC_MSG_CHECKING($1_LIBS) + $1_LIBS=`$PKG_CONFIG --libs "$2"` + AC_MSG_RESULT($$1_LIBS) + else + $1_CFLAGS="" + $1_LIBS="" + ## If we have a custom action on failure, don't print errors, but + ## do set a variable so people can do so. + $1_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` + ifelse([$4], ,echo $$1_PKG_ERRORS,) + fi + + AC_SUBST($1_CFLAGS) + AC_SUBST($1_LIBS) + else + echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer." + echo "*** See http://www.freedesktop.org/software/pkgconfig" + fi + fi + + if test $succeeded = yes; then + ifelse([$3], , :, [$3]) + else + ifelse([$4], , AC_MSG_ERROR([Library requirements ($2) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them.]), [$4]) + fi +]) + + + +# Copyright (C) 1995-2002 Free Software Foundation, Inc. +# Copyright (C) 2001-2003,2004 Red Hat, Inc. +# +# This file is free software, distributed under the terms of the GNU +# General Public License. As a special exception to the GNU General +# Public License, this file may be distributed as part of a program +# that contains a configuration script generated by Autoconf, under +# the same distribution terms as the rest of that program. +# +# This file can be copied and used freely without restrictions. It can +# be used in projects which are not available under the GNU Public License +# but which still want to provide support for the GNU gettext functionality. +# +# Macro to add for using GNU gettext. +# Ulrich Drepper , 1995, 1996 +# +# Modified to never use included libintl. +# Owen Taylor , 12/15/1998 +# +# Major rework to remove unused code +# Owen Taylor , 12/11/2002 +# +# Added better handling of ALL_LINGUAS from GNU gettext version +# written by Bruno Haible, Owen Taylor 5/30/3002 +# +# Modified to require ngettext +# Matthias Clasen 08/06/2004 +# +# We need this here as well, since someone might use autoconf-2.5x +# to configure GLib then an older version to configure a package +# using AM_GLIB_GNU_GETTEXT +AC_PREREQ(2.53) + +dnl +dnl We go to great lengths to make sure that aclocal won't +dnl try to pull in the installed version of these macros +dnl when running aclocal in the glib directory. +dnl +m4_copy([AC_DEFUN],[glib_DEFUN]) +m4_copy([AC_REQUIRE],[glib_REQUIRE]) +dnl +dnl At the end, if we're not within glib, we'll define the public +dnl definitions in terms of our private definitions. +dnl + +# GLIB_LC_MESSAGES +#-------------------- +glib_DEFUN([GLIB_LC_MESSAGES], + [AC_CHECK_HEADERS([locale.h]) + if test $ac_cv_header_locale_h = yes; then + AC_CACHE_CHECK([for LC_MESSAGES], am_cv_val_LC_MESSAGES, + [AC_TRY_LINK([#include ], [return LC_MESSAGES], + am_cv_val_LC_MESSAGES=yes, am_cv_val_LC_MESSAGES=no)]) + if test $am_cv_val_LC_MESSAGES = yes; then + AC_DEFINE(HAVE_LC_MESSAGES, 1, + [Define if your file defines LC_MESSAGES.]) + fi + fi]) + +# GLIB_PATH_PROG_WITH_TEST +#---------------------------- +dnl GLIB_PATH_PROG_WITH_TEST(VARIABLE, PROG-TO-CHECK-FOR, +dnl TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]]) +glib_DEFUN([GLIB_PATH_PROG_WITH_TEST], +[# Extract the first word of "$2", so it can be a program name with args. +set dummy $2; ac_word=[$]2 +AC_MSG_CHECKING([for $ac_word]) +AC_CACHE_VAL(ac_cv_path_$1, +[case "[$]$1" in + /*) + ac_cv_path_$1="[$]$1" # Let the user override the test with a path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in ifelse([$5], , $PATH, [$5]); do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if [$3]; then + ac_cv_path_$1="$ac_dir/$ac_word" + break + fi + fi + done + IFS="$ac_save_ifs" +dnl If no 4th arg is given, leave the cache variable unset, +dnl so AC_PATH_PROGS will keep looking. +ifelse([$4], , , [ test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4" +])dnl + ;; +esac])dnl +$1="$ac_cv_path_$1" +if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then + AC_MSG_RESULT([$]$1) +else + AC_MSG_RESULT(no) +fi +AC_SUBST($1)dnl +]) + +# GLIB_WITH_NLS +#----------------- +glib_DEFUN([GLIB_WITH_NLS], + dnl NLS is obligatory + [USE_NLS=yes + AC_SUBST(USE_NLS) + + gt_cv_have_gettext=no + + CATOBJEXT=NONE + XGETTEXT=: + INTLLIBS= + + AC_CHECK_HEADER(libintl.h, + [gt_cv_func_dgettext_libintl="no" + libintl_extra_libs="" + + # + # First check in libc + # + AC_CACHE_CHECK([for ngettext in libc], gt_cv_func_ngettext_libc, + [AC_TRY_LINK([ +#include +], + [return !ngettext ("","", 1)], + gt_cv_func_ngettext_libc=yes, + gt_cv_func_ngettext_libc=no) + ]) + + if test "$gt_cv_func_ngettext_libc" = "yes" ; then + AC_CACHE_CHECK([for dgettext in libc], gt_cv_func_dgettext_libc, + [AC_TRY_LINK([ +#include +], + [return !dgettext ("","")], + gt_cv_func_dgettext_libc=yes, + gt_cv_func_dgettext_libc=no) + ]) + fi + + if test "$gt_cv_func_ngettext_libc" = "yes" ; then + AC_CHECK_FUNCS(bind_textdomain_codeset) + fi + + # + # If we don't have everything we want, check in libintl + # + if test "$gt_cv_func_dgettext_libc" != "yes" \ + || test "$gt_cv_func_ngettext_libc" != "yes" \ + || test "$ac_cv_func_bind_textdomain_codeset" != "yes" ; then + + AC_CHECK_LIB(intl, bindtextdomain, + [AC_CHECK_LIB(intl, ngettext, + [AC_CHECK_LIB(intl, dgettext, + gt_cv_func_dgettext_libintl=yes)])]) + + if test "$gt_cv_func_dgettext_libintl" != "yes" ; then + AC_MSG_CHECKING([if -liconv is needed to use gettext]) + AC_MSG_RESULT([]) + AC_CHECK_LIB(intl, ngettext, + [AC_CHECK_LIB(intl, dcgettext, + [gt_cv_func_dgettext_libintl=yes + libintl_extra_libs=-liconv], + :,-liconv)], + :,-liconv) + fi + + # + # If we found libintl, then check in it for bind_textdomain_codeset(); + # we'll prefer libc if neither have bind_textdomain_codeset(), + # and both have dgettext and ngettext + # + if test "$gt_cv_func_dgettext_libintl" = "yes" ; then + glib_save_LIBS="$LIBS" + LIBS="$LIBS -lintl $libintl_extra_libs" + unset ac_cv_func_bind_textdomain_codeset + AC_CHECK_FUNCS(bind_textdomain_codeset) + LIBS="$glib_save_LIBS" + + if test "$ac_cv_func_bind_textdomain_codeset" = "yes" ; then + gt_cv_func_dgettext_libc=no + else + if test "$gt_cv_func_dgettext_libc" = "yes" \ + && test "$gt_cv_func_ngettext_libc" = "yes"; then + gt_cv_func_dgettext_libintl=no + fi + fi + fi + fi + + if test "$gt_cv_func_dgettext_libc" = "yes" \ + || test "$gt_cv_func_dgettext_libintl" = "yes"; then + gt_cv_have_gettext=yes + fi + + if test "$gt_cv_func_dgettext_libintl" = "yes"; then + INTLLIBS="-lintl $libintl_extra_libs" + fi + + if test "$gt_cv_have_gettext" = "yes"; then + AC_DEFINE(HAVE_GETTEXT,1, + [Define if the GNU gettext() function is already present or preinstalled.]) + GLIB_PATH_PROG_WITH_TEST(MSGFMT, msgfmt, + [test -z "`$ac_dir/$ac_word -h 2>&1 | grep 'dv '`"], no)dnl + if test "$MSGFMT" != "no"; then + glib_save_LIBS="$LIBS" + LIBS="$LIBS $INTLLIBS" + AC_CHECK_FUNCS(dcgettext) + AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT) + GLIB_PATH_PROG_WITH_TEST(XGETTEXT, xgettext, + [test -z "`$ac_dir/$ac_word -h 2>&1 | grep '(HELP)'`"], :) + AC_TRY_LINK(, [extern int _nl_msg_cat_cntr; + return _nl_msg_cat_cntr], + [CATOBJEXT=.gmo + DATADIRNAME=share], + [case $host in + *-*-solaris*) + dnl On Solaris, if bind_textdomain_codeset is in libc, + dnl GNU format message catalog is always supported, + dnl since both are added to the libc all together. + dnl Hence, we'd like to go with DATADIRNAME=share and + dnl and CATOBJEXT=.gmo in this case. + AC_CHECK_FUNC(bind_textdomain_codeset, + [CATOBJEXT=.gmo + DATADIRNAME=share], + [CATOBJEXT=.mo + DATADIRNAME=lib]) + ;; + *) + CATOBJEXT=.mo + DATADIRNAME=lib + ;; + esac]) + LIBS="$glib_save_LIBS" + INSTOBJEXT=.mo + else + gt_cv_have_gettext=no + fi + fi + ]) + + if test "$gt_cv_have_gettext" = "yes" ; then + AC_DEFINE(ENABLE_NLS, 1, + [always defined to indicate that i18n is enabled]) + fi + + dnl Test whether we really found GNU xgettext. + if test "$XGETTEXT" != ":"; then + dnl If it is not GNU xgettext we define it as : so that the + dnl Makefiles still can work. + if $XGETTEXT --omit-header /dev/null 2> /dev/null; then + : ; + else + AC_MSG_RESULT( + [found xgettext program is not GNU xgettext; ignore it]) + XGETTEXT=":" + fi + fi + + # We need to process the po/ directory. + POSUB=po + + AC_OUTPUT_COMMANDS( + [case "$CONFIG_FILES" in *po/Makefile.in*) + sed -e "/POTFILES =/r po/POTFILES" po/Makefile.in > po/Makefile + esac]) + + dnl These rules are solely for the distribution goal. While doing this + dnl we only have to keep exactly one list of the available catalogs + dnl in configure.in. + for lang in $ALL_LINGUAS; do + GMOFILES="$GMOFILES $lang.gmo" + POFILES="$POFILES $lang.po" + done + + dnl Make all variables we use known to autoconf. + AC_SUBST(CATALOGS) + AC_SUBST(CATOBJEXT) + AC_SUBST(DATADIRNAME) + AC_SUBST(GMOFILES) + AC_SUBST(INSTOBJEXT) + AC_SUBST(INTLLIBS) + AC_SUBST(PO_IN_DATADIR_TRUE) + AC_SUBST(PO_IN_DATADIR_FALSE) + AC_SUBST(POFILES) + AC_SUBST(POSUB) + ]) + +# AM_GLIB_GNU_GETTEXT +# ------------------- +# Do checks necessary for use of gettext. If a suitable implementation +# of gettext is found in either in libintl or in the C library, +# it will set INTLLIBS to the libraries needed for use of gettext +# and AC_DEFINE() HAVE_GETTEXT and ENABLE_NLS. (The shell variable +# gt_cv_have_gettext will be set to "yes".) It will also call AC_SUBST() +# on various variables needed by the Makefile.in.in installed by +# glib-gettextize. +dnl +glib_DEFUN([GLIB_GNU_GETTEXT], + [AC_REQUIRE([AC_PROG_CC])dnl + AC_REQUIRE([AC_HEADER_STDC])dnl + + GLIB_LC_MESSAGES + GLIB_WITH_NLS + + if test "$gt_cv_have_gettext" = "yes"; then + if test "x$ALL_LINGUAS" = "x"; then + LINGUAS= + else + AC_MSG_CHECKING(for catalogs to be installed) + NEW_LINGUAS= + for presentlang in $ALL_LINGUAS; do + useit=no + if test "%UNSET%" != "${LINGUAS-%UNSET%}"; then + desiredlanguages="$LINGUAS" + else + desiredlanguages="$ALL_LINGUAS" + fi + for desiredlang in $desiredlanguages; do + # Use the presentlang catalog if desiredlang is + # a. equal to presentlang, or + # b. a variant of presentlang (because in this case, + # presentlang can be used as a fallback for messages + # which are not translated in the desiredlang catalog). + case "$desiredlang" in + "$presentlang"*) useit=yes;; + esac + done + if test $useit = yes; then + NEW_LINGUAS="$NEW_LINGUAS $presentlang" + fi + done + LINGUAS=$NEW_LINGUAS + AC_MSG_RESULT($LINGUAS) + fi + + dnl Construct list of names of catalog files to be constructed. + if test -n "$LINGUAS"; then + for lang in $LINGUAS; do CATALOGS="$CATALOGS $lang$CATOBJEXT"; done + fi + fi + + dnl If the AC_CONFIG_AUX_DIR macro for autoconf is used we possibly + dnl find the mkinstalldirs script in another subdir but ($top_srcdir). + dnl Try to locate is. + MKINSTALLDIRS= + if test -n "$ac_aux_dir"; then + MKINSTALLDIRS="$ac_aux_dir/mkinstalldirs" + fi + if test -z "$MKINSTALLDIRS"; then + MKINSTALLDIRS="\$(top_srcdir)/mkinstalldirs" + fi + AC_SUBST(MKINSTALLDIRS) + + dnl Generate list of files to be processed by xgettext which will + dnl be included in po/Makefile. + test -d po || mkdir po + if test "x$srcdir" != "x."; then + if test "x`echo $srcdir | sed 's@/.*@@'`" = "x"; then + posrcprefix="$srcdir/" + else + posrcprefix="../$srcdir/" + fi + else + posrcprefix="../" + fi + rm -f po/POTFILES + sed -e "/^#/d" -e "/^\$/d" -e "s,.*, $posrcprefix& \\\\," -e "\$s/\(.*\) \\\\/\1/" \ + < $srcdir/po/POTFILES.in > po/POTFILES + ]) + +# AM_GLIB_DEFINE_LOCALEDIR(VARIABLE) +# ------------------------------- +# Define VARIABLE to the location where catalog files will +# be installed by po/Makefile. +glib_DEFUN([GLIB_DEFINE_LOCALEDIR], +[glib_REQUIRE([GLIB_GNU_GETTEXT])dnl +glib_save_prefix="$prefix" +glib_save_exec_prefix="$exec_prefix" +test "x$prefix" = xNONE && prefix=$ac_default_prefix +test "x$exec_prefix" = xNONE && exec_prefix=$prefix +if test "x$CATOBJEXT" = "x.mo" ; then + localedir=`eval echo "${libdir}/locale"` +else + localedir=`eval echo "${datadir}/locale"` +fi +prefix="$glib_save_prefix" +exec_prefix="$glib_save_exec_prefix" +AC_DEFINE_UNQUOTED($1, "$localedir", + [Define the location where the catalogs will be installed]) +]) + +dnl +dnl Now the definitions that aclocal will find +dnl +ifdef(glib_configure_in,[],[ +AC_DEFUN([AM_GLIB_GNU_GETTEXT],[GLIB_GNU_GETTEXT($@)]) +AC_DEFUN([AM_GLIB_DEFINE_LOCALEDIR],[GLIB_DEFINE_LOCALEDIR($@)]) +])dnl + diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..8fe1de8 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,159 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +DIE=0 + +if [ -n "$GNOME2_DIR" ]; then + ACLOCAL_FLAGS="-I $GNOME2_DIR/share/aclocal $ACLOCAL_FLAGS" + LD_LIBRARY_PATH="$GNOME2_DIR/lib:$LD_LIBRARY_PATH" + PATH="$GNOME2_DIR/bin:$PATH" + export PATH + export LD_LIBRARY_PATH +fi + +(test -f $srcdir/configure.in) || { + echo -n "**Error**: Directory "\`$srcdir\'" does not look like the" + echo " top-level package directory" + exit 1 +} + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`autoconf' installed." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +(grep "^AC_PROG_INTLTOOL" $srcdir/configure.in >/dev/null) && { + (intltoolize --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`intltool' installed." + echo "You can get it from:" + echo " ftp://ftp.gnome.org/pub/GNOME/" + DIE=1 + } +} + +(grep "^AM_PROG_XML_I18N_TOOLS" $srcdir/configure.in >/dev/null) && { + (xml-i18n-toolize --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`xml-i18n-toolize' installed." + echo "You can get it from:" + echo " ftp://ftp.gnome.org/pub/GNOME/" + DIE=1 + } +} + +(grep "^AM_PROG_LIBTOOL" $srcdir/configure.in >/dev/null) && { + (libtool --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`libtool' installed." + echo "You can get it from: ftp://ftp.gnu.org/pub/gnu/" + DIE=1 + } +} + +(grep "^AM_GLIB_GNU_GETTEXT" $srcdir/configure.in >/dev/null) && { + (grep "sed.*POTFILES" $srcdir/configure.in) > /dev/null || \ + (glib-gettextize --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`glib' installed." + echo "You can get it from: ftp://ftp.gtk.org/pub/gtk" + DIE=1 + } +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`automake' installed." + echo "You can get it from: ftp://ftp.gnu.org/pub/gnu/" + DIE=1 + NO_AUTOMAKE=yes +} + + +# if no automake, don't bother testing for aclocal +test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: Missing \`aclocal'. The version of \`automake'" + echo "installed doesn't appear recent enough." + echo "You can get automake from ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +if test -z "$*"; then + echo "**Warning**: I am going to run \`configure' with no arguments." + echo "If you wish to pass any to it, please specify them on the" + echo \`$0\'" command line." + echo +fi + +case $CC in +xlc ) + am_opt=--include-deps;; +esac + +for coin in `find $srcdir -path $srcdir/CVS -prune -o -name configure.in -print` +do + dr=`dirname $coin` + if test -f $dr/NO-AUTO-GEN; then + echo skipping $dr -- flagged as no auto-gen + else + echo processing $dr + ( cd $dr + + aclocalinclude="$ACLOCAL_FLAGS" + + if grep "^AM_GLIB_GNU_GETTEXT" configure.in >/dev/null; then + echo "Creating $dr/aclocal.m4 ..." + test -r $dr/aclocal.m4 || touch $dr/aclocal.m4 + echo "Running glib-gettextize... Ignore non-fatal messages." + echo "no" | glib-gettextize --force --copy + echo "Making $dr/aclocal.m4 writable ..." + test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4 + fi + if grep "^AC_PROG_INTLTOOL" configure.in >/dev/null; then + echo "Running intltoolize..." + intltoolize --copy --force --automake + fi + if grep "^AM_PROG_XML_I18N_TOOLS" configure.in >/dev/null; then + echo "Running xml-i18n-toolize..." + xml-i18n-toolize --copy --force --automake + fi + if grep "^AM_PROG_LIBTOOL" configure.in >/dev/null; then + if test -z "$NO_LIBTOOLIZE" ; then + echo "Running libtoolize..." + libtoolize --force --copy + fi + fi + echo "Running aclocal $aclocalinclude ..." + aclocal $aclocalinclude + if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then + echo "Running autoheader..." + autoheader + fi + echo "Running automake --gnu $am_opt ..." + automake --add-missing --gnu $am_opt + echo "Running autoconf ..." + autoconf + ) + fi +done + +conf_flags="--enable-maintainer-mode" + +if test x$NOCONFIGURE = x; then + echo Running $srcdir/configure $conf_flags "$@" ... + $srcdir/configure $conf_flags "$@" \ + && echo Now type \`make\' to compile. || exit 1 +else + echo Skipping configure process. +fi diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..26fbbc1 --- /dev/null +++ b/configure.in @@ -0,0 +1,152 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(configure.in) +AM_INIT_AUTOMAKE(gnoemoe, 0.9.1) +AC_DEFINE_UNQUOTED(IVERSION, "0.9.1", "this is used for internal representation of the version") +AM_CONFIG_HEADER(config.h) +AM_MAINTAINER_MODE + +AC_ISC_POSIX +AC_STDC_HEADERS +AC_PROG_CC +AM_PROG_CC_STDC +AC_HEADER_STDC + +AC_PROG_INTLTOOL + +pkg_modules="gtk+-2.0 >= 2.0.0 gdk-pixbuf-2.0 libgnome-2.0 libglade-2.0 gtksourceview-1.0 gnome-vfs-2.0 libgnomeui-2.0 vte libxml-2.0 cairo" +PKG_CHECK_MODULES(PACKAGE, [$pkg_modules]) + +AC_SUBST(PACKAGE_CFLAGS) +AC_SUBST(PACKAGE_LIBS) + +AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal) + +AC_ARG_ENABLE(rubyen, +[ +GnoeMoe specific options: + --disable-ruby disable use of the ruby scripting support], + rubyen=$enableval, rubyen=yes) + +#---------------------------------------------------------------- +# Look for Ruby +#---------------------------------------------------------------- + +if test "_$rubyen" = "_yes"; then + RUBYBIN= + + AC_ARG_WITH(ruby,[ --with-ruby=path Set location of Ruby executable],[ RUBYBIN="$withval"], [RUBYBIN=]) + + # First figure out what the name of Ruby is + + if test -z "$RUBYBIN"; then + AC_CHECK_PROGS(RUBY, ruby) + else + RUBY="$RUBYBIN" + fi + + AC_MSG_CHECKING(for Ruby header files) + if test -n "$RUBY"; then + RUBYDIR=`($RUBY -rmkmf -e 'print Config::CONFIG[["archdir"]] || $archdir') 2>/dev/null` + if test "$RUBYDIR" != ""; then + dirs="$RUBYDIR" + RUBYINCLUDE=none + for i in $dirs; do + if test -r $i/ruby.h; then + AC_MSG_RESULT($i) + RUBYINCLUDE="-idirafter $i" + break; + fi + done + if test "$RUBYINCLUDE" = none; then + AC_MSG_RESULT(could not locate ruby.h, disabling ruby support) + rubymsg="(no headers found, install ruby1.8-dev)" + rubyen="no" + fi + + # Find library and path for linking. + AC_MSG_CHECKING(for Ruby library) + RUBYLIB="" + rb_libdir=`($RUBY -rrbconfig -e 'print Config::CONFIG[["libdir"]]') 2>/dev/null` + rb_bindir=`($RUBY -rrbconfig -e 'print Config::CONFIG[["bindir"]]') 2>/dev/null` + dirs="$dirs $rb_libdir $rb_bindir" + + rb_libruby=`($RUBY -rrbconfig -e 'print Config::CONFIG[["LIBRUBY_A"]]') 2>/dev/null` + RUBYLINK=`($RUBY -rrbconfig -e ' + c = Config::CONFIG + if c.has_key? "LIBRUBYARG_STATIC" # 1.8.x + if c[["LIBRUBY"]] == c[["LIBRUBY_A"]] + link = c[["LIBRUBYARG_STATIC"]] + else + link = c[["LIBRUBYARG_SHARED"]] + end + else # 1.6.x + link = "-l" + c[["RUBY_INSTALL_NAME"]] + end + puts link') 2>/dev/null` + + if test "$rb_libruby" != ""; then + for i in $dirs; do + if (test -r $i/$rb_libruby;) then + RUBYLIB="$i" + break; + fi + done + fi + if test "$RUBYLIB" = ""; then + AC_MSG_RESULT(not found... disabling ruby support) + rubymsg="(libs not found)" + rubyen="no" + else + AC_MSG_RESULT($RUBYLINK in $RUBYLIB) + fi + else + AC_MSG_RESULT(unable to determine ruby configuration, disabling ruby support) + rubymsg="(unable to determine configuration)" + rubyen="no" + fi + else + AC_MSG_RESULT(not found.. disabling ruby support) + rubymsg="(no headers found, install ruby1.8-dev)" + rubyen="no" + fi +fi + +if test "_$rubyen" == "_no"; then + RUBYINCLUDE="" + RUBYLINK="" + AM_CONDITIONAL(HAVE_SCRIPT, false) +else + rubymsg="enjoy scripting :)" + AC_DEFINE([HAVE_RUBY], [], [Ruby scripting is enabled]) + AM_CONDITIONAL(HAVE_SCRIPT, true) +fi + +AC_SUBST(RUBYINCLUDE) +AC_SUBST(RUBYLINK) + +GETTEXT_PACKAGE=gnoemoe +AC_SUBST(GETTEXT_PACKAGE) +AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", "gettext package") + +dnl Add the languages which your application supports here. +ALL_LINGUAS="nl" +AM_GLIB_GNU_GETTEXT + +AC_OUTPUT([ +Makefile +pixmaps/Makefile +pixmaps/userlist/Makefile +pixmaps/ice-userlist/Makefile +pixmaps/tray/Makefile +src/Makefile +src/test/Makefile +po/Makefile.in +scripts/Makefile +ui/Makefile +]) + +echo +echo Building with ruby ............. : $rubyen $rubymsg +echo Install prefix ................. : $prefix +echo Install share .................. : $datadir diff --git a/gnoemoe-logo.svg b/gnoemoe-logo.svg new file mode 100644 index 0000000..79bc44e --- /dev/null +++ b/gnoemoe-logo.svg @@ -0,0 +1,717 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gnoemoe.desktop b/gnoemoe.desktop new file mode 100644 index 0000000..77abf2c --- /dev/null +++ b/gnoemoe.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=GnoeMoe +GenericName=MOO Client +Comment=GnoeMoe Gnome MOO Client +Exec=gnoemoe +Icon=gnoemoe-logo.svg +Terminal=0 +Type=Application +Categories=GNOME;GTK;Application;Network diff --git a/gnoemoe.xml b/gnoemoe.xml new file mode 100644 index 0000000..68d8302 --- /dev/null +++ b/gnoemoe.xml @@ -0,0 +1,8 @@ + + + + MOO Code + + + + diff --git a/moo.lang b/moo.lang new file mode 100644 index 0000000..f7df7ee --- /dev/null +++ b/moo.lang @@ -0,0 +1,217 @@ + + + + + \ + + + ^[ ]*" + " + + + + " + " + + + + if + else + elseif + endif + for + endfor + break + continue + while + endwhile + in + return + try + except + endtry + finally + fork + endfork + + + + E_NONE + E_TYPE + E_DIV + E_PERM + E_PROPNF + E_VERBNF + E_VARNF + E_INVIND + E_RECMOVE + E_MAXREC + E_RANGE + E_ARGS + E_NACC + E_INVARG + E_QUOTA + E_FLOAT + + + + abs + acos + add_property + asin + atan + binary_hash + boot_player + buffered_output_length + call_function + caller_perms + callers + ceil + children + chparent + clear_property + connected_players + connected_seconds + connection_name + connection_option + connection_options + cos + cosh + create + crypt + ctime + db_disk_size + decode_binary + delete_property + delete_verb + disassemble + dump_database + encode_binary + equal + eval + exp + floatstr + floor + flush_input + force_input + function_info + idle_seconds + index + is_clear_property + is_member + is_player + kill_task + length + listappend + listdelete + listen + listeners + listinsert + listset + log + log10 + match + max + max_object + memory_usage + min + move + notify + object_bytes + open_network_connection + output_delimiters + parent + pass + players + properties + property_info + queue_info + queued_tasks + raise + random + read + recycle + renumber + reset_max_object + resume + rindex + rmatch + seconds_left + server_log + server_version + set_connection_option + set_player_flag + set_property_info + set_task_perms + set_verb_args + set_verb_code + set_verb_info + setadd + setremove + shutdown + sin + sinh + sqrt + strcmp + string_hash + strsub + substitute + suspend + tan + tanh + task_id + task_stack + ticks_left + time + tofloat + toint + toliteral + tonum + toobj + tostr + trunc + typeof + unlisten + valid + value_bytes + value_hash + verb_args + verb_code + verb_info + verbs + + + + player + this + caller + verb + argstr + args + dobjstr + dobj + prepstr + iobjstr + iobj + + + + ERR + LIST + STR + OBJ + INT + FLOAT + ANY + + + + \$[A-Za-z0-9_]+ + + + + #[-]?[0-9]+ + + + + diff --git a/pixmaps/Makefile.am b/pixmaps/Makefile.am new file mode 100644 index 0000000..f4fff8b --- /dev/null +++ b/pixmaps/Makefile.am @@ -0,0 +1,14 @@ +SUBDIRS = userlist ice-userlist tray + +pixmapsdir = $(pkgdatadir)/pixmaps +pixmaps_DATA = close.xpm \ + editor.xpm \ + editor_text.xpm \ + editor_verb.xpm \ + gnoemoe_logo.svg \ + saveclose.xpm \ + terminal.png \ + world.svg \ + world_activity.svg + +EXTRA_DIST = $(pixmaps_DATA) diff --git a/pixmaps/editor.xpm b/pixmaps/editor.xpm new file mode 100644 index 0000000..c04bd47 --- /dev/null +++ b/pixmaps/editor.xpm @@ -0,0 +1,74 @@ +/* XPM */ +static char * editor_xpm[] = { +"16 16 55 1", +" c None", +". c #000000", +"+ c #FEFEFE", +"@ c #FDFDFD", +"# c #E0E0E0", +"$ c #C1C1C1", +"% c #F1F1F1", +"& c #C3C3C3", +"* c #FBFBFB", +"= c #A8A8A8", +"- c #BEBEBE", +"; c #B5B5B5", +"> c #CACACA", +", c #ADADAD", +"' c #767676", +") c #5D5D5D", +"! c #404040", +"~ c #F0F0F0", +"{ c #E2E2E2", +"] c #858585", +"^ c #4B4B49", +"/ c #161616", +"( c #B3B3B3", +"_ c #C8C8C8", +": c #BDBDBD", +"< c #EFEFEF", +"[ c #EEEEEE", +"} c #C5C5C5", +"| c #ECECEC", +"1 c #C4C4C4", +"2 c #D3D3D3", +"3 c #BCBCBC", +"4 c #C7C7C7", +"5 c #EBEBEB", +"6 c #EDEDED", +"7 c #EAEAEA", +"8 c #C2C2C2", +"9 c #E8E8E8", +"0 c #686E83", +"a c #C0C0C0", +"b c #E9E9E9", +"c c #878BA5", +"d c #7C84A3", +"e c #E7E7E7", +"f c #BFBFBF", +"g c #E6E6E6", +"h c #A7A2A6", +"i c #9795A1", +"j c #E5E5E5", +"k c #686D83", +"l c #676D82", +"m c #E4E4E4", +"n c #E3E3E3", +"o c #BBBBBB", +"p c #A0A0A0", +" ......... ", +" .++++++@#$. ", +" .+%%%%%%&*=. ", +" .+%-;->%,')!. ", +" .+%%%%%~{]^/. ", +" .+%(_:(#<[[}. ", +" .+<#<[[[[[|1. ", +" .@[(2(33445&. ", +" .@6|||55777$. ", +" .@588900009a. ", +" .@77bb0cd0ef. ", +" .@9$$g0hi0j-. ", +" .@eegg0kklm:. ", +" .@gjjjmmnnno. ", +" .8:::33oooop. ", +" ........... "}; diff --git a/pixmaps/editor_text.xpm b/pixmaps/editor_text.xpm new file mode 100644 index 0000000..f45146d --- /dev/null +++ b/pixmaps/editor_text.xpm @@ -0,0 +1,80 @@ +/* XPM */ +static char * editor_text_xpm[] = { +"16 16 61 1", +" c None", +". c #000000", +"+ c #FEFEFE", +"@ c #FDFDFD", +"# c #E0E0E0", +"$ c #C1C1C1", +"% c #F1F1F1", +"& c #C3C3C3", +"* c #FBFBFB", +"= c #A8A8A8", +"- c #CCCCCC", +"; c #ADADAD", +"> c #767676", +", c #5D5D5D", +"' c #404040", +") c #F0F0F0", +"! c #E2E2E2", +"~ c #858585", +"{ c #4B4B49", +"] c #FBE73B", +"^ c #F2B64D", +"/ c #CACACA", +"( c #EFEFEF", +"_ c #C9C9C9", +": c #FCEB3D", +"< c #F7B544", +"[ c #675A36", +"} c #EEEEEE", +"| c #FCE93B", +"1 c #F7B545", +"2 c #6C5F34", +"3 c #F9DF39", +"4 c #F4B244", +"5 c #665D3E", +"6 c #EDEDED", +"7 c #ECECEC", +"8 c #EBEBEB", +"9 c #F6D236", +"0 c #EFB44D", +"a c #5C4F2B", +"b c #C4C4C4", +"c c #E8E8E8", +"d c #D7AE74", +"e c #655930", +"f c #C0C0C0", +"g c #EAEAEA", +"h c #E9E9E9", +"i c #4F4115", +"j c #E7E7E7", +"k c #BFBFBF", +"l c #C2C2C2", +"m c #E6E6E6", +"n c #E5E5E5", +"o c #BEBEBE", +"p c #E4E4E4", +"q c #BDBDBD", +"r c #E3E3E3", +"s c #BBBBBB", +"t c #BCBCBC", +"u c #A0A0A0", +"v c #2A2A2A", +" ......... ", +".++++++@#$. ", +".+%%%%%%&*=. ", +".+%---%%;>,'.. ", +".+%%%%%)!~{.]^. ", +".+%////(/_.:<[. ", +".+(((}}}}.|12. ", +".@}__}__.345. ", +".@67778.90a.. ", +".@8bbbc.de.f. ", +".@gghh.i..jk. ", +".@clml..mmno. ", +".@jjmmmnnppq. ", +".@mnnnpprrrs. ", +".lqqqttssssuv ", +" ........... "}; diff --git a/pixmaps/editor_verb.xpm b/pixmaps/editor_verb.xpm new file mode 100644 index 0000000..557d351 --- /dev/null +++ b/pixmaps/editor_verb.xpm @@ -0,0 +1,83 @@ +/* XPM */ +static char * editor_verb_xpm[] = { +"16 16 64 1", +" c None", +". c #000000", +"+ c #FEFEFE", +"@ c #FDFDFD", +"# c #E0E0E0", +"$ c #C1C1C1", +"% c #F1F1F1", +"& c #C3C3C3", +"* c #FBFBFB", +"= c #A8A8A8", +"- c #0000FF", +"; c #00FF00", +"> c #ADADAD", +", c #767676", +"' c #5D5D5D", +") c #404040", +"! c #F0F0F0", +"~ c #E2E2E2", +"{ c #858585", +"] c #4B4B49", +"^ c #FBE73B", +"/ c #F2B64D", +"( c #FF0000", +"_ c #EFEFEF", +": c #CACACA", +"< c #C9C9C9", +"[ c #FCEB3D", +"} c #F7B544", +"| c #675A36", +"1 c #EEEEEE", +"2 c #FCE93B", +"3 c #F7B545", +"4 c #6C5F34", +"5 c #000080", +"6 c #F9DF39", +"7 c #F4B244", +"8 c #665D3E", +"9 c #EDEDED", +"0 c #ECECEC", +"a c #EBEBEB", +"b c #F6D236", +"c c #EFB44D", +"d c #5C4F2B", +"e c #E8E8E8", +"f c #D7AE74", +"g c #655930", +"h c #C0C0C0", +"i c #EAEAEA", +"j c #E9E9E9", +"k c #4F4115", +"l c #E7E7E7", +"m c #BFBFBF", +"n c #FFFF00", +"o c #E6E6E6", +"p c #E5E5E5", +"q c #BEBEBE", +"r c #E4E4E4", +"s c #BDBDBD", +"t c #E3E3E3", +"u c #BBBBBB", +"v c #C2C2C2", +"w c #BCBCBC", +"x c #A0A0A0", +"y c #2A2A2A", +" ......... ", +".++++++@#$. ", +".+%%%%%%&*=. ", +".+%---;;>,').. ", +".+%%%%%!~{].^/. ", +".+%;;((_:<.[}|. ", +".+___1111.234. ", +".@155;--.678. ", +".@9000a.bcd.. ", +".@a-((e.fg.h. ", +".@iijj.k..lm. ", +".@en;;..oopq. ", +".@llooopprrs. ", +".@o((--((;tu. ", +".vssswwuuuuxy ", +" ........... "}; diff --git a/pixmaps/gnoemoe_logo.svg b/pixmaps/gnoemoe_logo.svg new file mode 100644 index 0000000..00c780d --- /dev/null +++ b/pixmaps/gnoemoe_logo.svg @@ -0,0 +1,592 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pixmaps/ice-userlist/Makefile.am b/pixmaps/ice-userlist/Makefile.am new file mode 100644 index 0000000..e1ae620 --- /dev/null +++ b/pixmaps/ice-userlist/Makefile.am @@ -0,0 +1,11 @@ +iconsdir = $(pkgdatadir)/pixmaps/ice-userlist +icons_DATA = inhabitant.svg \ + programmer.svg \ + wizard.svg \ + avail+idle.svg \ + busy.svg \ + busy+idle.svg \ + away.svg \ + away+idle.svg + +EXTRA_DIST = $(icons_DATA) diff --git a/pixmaps/ice-userlist/avail+idle.svg b/pixmaps/ice-userlist/avail+idle.svg new file mode 100644 index 0000000..c0435b3 --- /dev/null +++ b/pixmaps/ice-userlist/avail+idle.svg @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/pixmaps/ice-userlist/away+idle.svg b/pixmaps/ice-userlist/away+idle.svg new file mode 100644 index 0000000..0f0c3e3 --- /dev/null +++ b/pixmaps/ice-userlist/away+idle.svg @@ -0,0 +1,314 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pixmaps/ice-userlist/away.svg b/pixmaps/ice-userlist/away.svg new file mode 100644 index 0000000..4e43f51 --- /dev/null +++ b/pixmaps/ice-userlist/away.svg @@ -0,0 +1,301 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pixmaps/ice-userlist/busy+idle.svg b/pixmaps/ice-userlist/busy+idle.svg new file mode 100644 index 0000000..0f0c3e3 --- /dev/null +++ b/pixmaps/ice-userlist/busy+idle.svg @@ -0,0 +1,314 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pixmaps/ice-userlist/busy.svg b/pixmaps/ice-userlist/busy.svg new file mode 100644 index 0000000..4e43f51 --- /dev/null +++ b/pixmaps/ice-userlist/busy.svg @@ -0,0 +1,301 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pixmaps/ice-userlist/inhabitant.svg b/pixmaps/ice-userlist/inhabitant.svg new file mode 100644 index 0000000..7d23f73 --- /dev/null +++ b/pixmaps/ice-userlist/inhabitant.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/pixmaps/ice-userlist/programmer.svg b/pixmaps/ice-userlist/programmer.svg new file mode 100644 index 0000000..5144aee --- /dev/null +++ b/pixmaps/ice-userlist/programmer.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + image/svg+xml + + + + + + + diff --git a/pixmaps/ice-userlist/wizard.svg b/pixmaps/ice-userlist/wizard.svg new file mode 100644 index 0000000..123a77d --- /dev/null +++ b/pixmaps/ice-userlist/wizard.svg @@ -0,0 +1,383 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/pixmaps/saveclose.xpm b/pixmaps/saveclose.xpm new file mode 100644 index 0000000..9147cb6 --- /dev/null +++ b/pixmaps/saveclose.xpm @@ -0,0 +1,243 @@ +/* XPM */ +static char * saveclose_xpm[] = { +"24 24 216 2", +" c None", +". c #000000", +"+ c #C3D7F4", +"@ c #A9CDE5", +"# c #75757A", +"$ c #EFC5BB", +"% c #F1C8BE", +"& c #F0C6BC", +"* c #EEBCB2", +"= c #EEBEB5", +"- c #EEC1B8", +"; c #EDBFB6", +"> c #E9B7AD", +", c #755D58", +"' c #C49B93", +") c #E9B9B1", +"! c #E5BFBA", +"~ c #737277", +"{ c #BBD6E8", +"] c #8AAAC5", +"^ c #605F68", +"/ c #E08D7E", +"( c #E0826E", +"_ c #E0806E", +": c #DC7A68", +"< c #DC8171", +"[ c #DA7868", +"} c #D48173", +"| c #D47D6E", +"1 c #AD6055", +"2 c #C4675B", +"3 c #C36558", +"4 c #BAD5E9", +"5 c #88A7C3", +"6 c #686670", +"7 c #C8817B", +"8 c #CB7C74", +"9 c #CB7A73", +"0 c #CB7B73", +"a c #CC7C72", +"b c #CB7D73", +"c c #BF6B64", +"d c #CC7A70", +"e c #C16A62", +"f c #7A4A45", +"g c #6F403F", +"h c #B6D3E7", +"i c #87ABC1", +"j c #737373", +"k c #FFFFFF", +"l c #3A4C5A", +"m c #B9D3E7", +"n c #85A4BF", +"o c #999999", +"p c #6E869B", +"q c #4F697C", +"r c #B9D3E6", +"s c #84A3BF", +"t c #CECECE", +"u c #CDCDCD", +"v c #919191", +"w c #232323", +"x c #3C5263", +"y c #B7D2E7", +"z c #82A0BB", +"A c #636363", +"B c #D7D7D7", +"C c #4E4E4E", +"D c #B5CAE5", +"E c #7FA2B9", +"F c #5C5C5C", +"G c #AECCE5", +"H c #7DA0B6", +"I c #494949", +"J c #ACACAC", +"K c #C5C5C5", +"L c #515151", +"M c #B0D1E4", +"N c #83A1B6", +"O c #735B5B", +"P c #4E606E", +"Q c #AACEE3", +"R c #7B9BB2", +"S c #7A8E9A", +"T c #7A7A7A", +"U c #6B6F72", +"V c #6F6F6F", +"W c #696969", +"X c #6F777E", +"Y c #86A2B9", +"Z c #3A515D", +"` c #A9C9E2", +" . c #7494AF", +".. c #819EB6", +"+. c #83A0B8", +"@. c #829FB7", +"#. c #7F9DB6", +"$. c #7E9CB5", +"%. c #7998B2", +"&. c #85A1B8", +"*. c #87A3BA", +"=. c #8CA7BD", +"-. c #8AA5BB", +";. c #364A59", +">. c #ABC4E2", +",. c #7294AD", +"'. c #6F90AC", +"). c #7192AE", +"!. c #414A4E", +"~. c #424A51", +"{. c #525B63", +"]. c #626F79", +"^. c #5F6C76", +"/. c #5C6971", +"(. c #5A666F", +"_. c #58636B", +":. c #57636A", +"<. c #3B5360", +"[. c #39424B", +"}. c #7897B3", +"|. c #A4B9CB", +"1. c #364853", +"2. c #AAC9E2", +"3. c #7091AA", +"4. c #6F8FA7", +"5. c #4A5359", +"6. c #97938C", +"7. c #DFDDDA", +"8. c #E3E1DE", +"9. c #EBEAE8", +"0. c #EAE9E7", +"a. c #CFCEC9", +"b. c #C9C6C0", +"c. c #9B968E", +"d. c #566168", +"e. c #4B657A", +"f. c #54738C", +"g. c #AAC6DD", +"h. c #34464E", +"i. c #AAC9E1", +"j. c #6C8EA6", +"k. c #6C8CA4", +"l. c #40474D", +"m. c #DAD8D3", +"n. c #E7E6E2", +"o. c #67655E", +"p. c #524F47", +"q. c #D9D7D4", +"r. c #C7C5BF", +"s. c #C0BCB5", +"t. c #B8B3AB", +"u. c #434C54", +"v. c #4D697F", +"w. c #4F6F84", +"x. c #B3CADC", +"y. c #313E49", +"z. c #A8C8E1", +"A. c #6B8DA6", +"B. c #728FA4", +"C. c #E2E1DD", +"D. c #F0EFEC", +"E. c #CDCAC6", +"F. c #C2BFB9", +"G. c #CAC6C0", +"H. c #DCDAD7", +"I. c #4B555D", +"J. c #4E697F", +"K. c #BACCDC", +"L. c #A4C4DE", +"M. c #698BA3", +"N. c #708AA1", +"O. c #383E43", +"P. c #E0DEDA", +"Q. c #514E46", +"R. c #4F4C44", +"S. c #C7C4BE", +"T. c #CBC8C2", +"U. c #E1E0DC", +"V. c #E9E8E6", +"W. c #475158", +"X. c #4E6879", +"Y. c #4D6C80", +"Z. c #A3C3DB", +"`. c #383F43", +" + c #778999", +".+ c #6E899E", +"++ c #65859C", +"@+ c #33383C", +"#+ c #D7D4D0", +"$+ c #D6D4D0", +"%+ c #4E4A43", +"&+ c #4D4942", +"*+ c #D1CEC9", +"=+ c #E6E5E2", +"-+ c #EDECEA", +";+ c #454F55", +">+ c #486173", +",+ c #4D6678", +"'+ c #A1C1DA", +")+ c #373C40", +"!+ c #0C0D0F", +"~+ c #4E5E6A", +"{+ c #5B6E7C", +"]+ c #4F5B62", +"^+ c #A4A099", +"/+ c #CCC9C3", +"(+ c #D7D5D1", +"_+ c #E4E2E0", +":+ c #DDDBD7", +"<+ c #B8B5B0", +"[+ c #3E474D", +"}+ c #4A6176", +"|+ c #4A6070", +"1+ c #9BC3D8", +"2+ c #363C41", +"3+ c #28323E", +" ", +" . . . . . . . . . . . . . . . . . . . . ", +" . + @ # $ % & * = - ; > , . ' ) ! ~ . . . ", +" . { ] ^ / ( _ : < [ } | 1 . . 2 3 . . . . ", +" . 4 5 6 7 8 9 0 a b c d e f . . g . . . . ", +" . h i j k k k k k k k k k k . . . . . l . ", +" . m n j k k k k k k k k k k o . . . p q . ", +" . r s j t u u u u u u u u v . . w . . x . ", +" . y z A k k k k k k k k B . . . C . . . . ", +" . D E j k k k k k k k k F . . B B . . . . . ", +" . G H j t u u u u u u I . . J u K L . . . . ", +" . M N O k k k k k k C F . B k k k j P . . ", +" . Q R S T j j j j j U V j j j j W X Y Z . ", +" . ` ...+.Y @.#.$.@.%.Y &.&.*.@.=.+.-.;.. ", +" . >.,.'.).!.~.{.].^./.(._.:.{.<.[.}.|.1.. ", +" . 2.3.4.5.6.7.8.9.9.0.7.a.b.c.d.e.f.g.h.. ", +" . i.j.k.l.m.n.o.p.p.q.r.s.t.a.u.v.w.x.y.. ", +" . z.A.B.l.C.D.o.p.p.E.F.s.G.H.I.J.w.K.y.. ", +" . L.M.N.O.P.9.Q.R.R.S.s.T.U.V.W.X.Y.Z.`.. ", +" . +.+++@+#+$+%+&+&+s.*+=+-+=+;+>+,+'+)+. ", +" !+~+{+]+^+s.t./+/+(+_+0.:+<+[+}+|+1+2+. ", +" . . . . . . . . . . . . . . . . 3+. ", +" ", +" "}; diff --git a/pixmaps/terminal.png b/pixmaps/terminal.png new file mode 100644 index 0000000000000000000000000000000000000000..299a635ab48b0fca65162e280b64ba74abde04ff GIT binary patch literal 772 zcmV+f1N;1mP)5-;KExnuwU0+C5X zK~#9!J(Im}TvZUoe{Ztv>H z_wI3Valzegk7*i=7*q=>XJQD%5JrID=(-LohV|KFZf^g*Z=nM4{NiU6!*n)7wcx#{ z>wEgH!+A%Fi5Meo+j26Su|9iDP6aC>q!`gsI6GgnT&?K(J==E6>(_5++ZOLVF-H2n zC*_RPN(g~c3Kk&7h_x2e2stNGPKXHe`3$5I!W8d4BCxxgP*TEsk7^wOymwS8H@CM; zCKC`ajbXl+W39y>d5kfH5U|!#V#Ile>VbiC4lT;Q+cUU<*(O3s#H~KRYDA-1QB6y12IOHixWP3^axeu z=Jtlc4e#?%w4nM93_=Lhsu+O2>j=@a`e4QBho=CPQdpiWnNFs7=jpl*@BPS13o!=D zI|*I4r|SotdC`9pHaR0JxNl8G^rQX=O}$(fuJ zMJtG~@B8tIP19WMb~_p)934&HH!oG8HU?AxK`84-UqCmypSiG&E|al@QYWk z{<~jQF$O>=rHmEMg;X-Rj4o(Zf{!c~i|c)te)%6j_UH=!xSf;$0000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pixmaps/tray/active.svg b/pixmaps/tray/active.svg new file mode 100644 index 0000000..adad20d --- /dev/null +++ b/pixmaps/tray/active.svg @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pixmaps/tray/default.svg b/pixmaps/tray/default.svg new file mode 100644 index 0000000..53b1cbd --- /dev/null +++ b/pixmaps/tray/default.svg @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pixmaps/tray/notify.svg b/pixmaps/tray/notify.svg new file mode 100644 index 0000000..057806c --- /dev/null +++ b/pixmaps/tray/notify.svg @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pixmaps/userlist/Makefile.am b/pixmaps/userlist/Makefile.am new file mode 100644 index 0000000..e1d1a21 --- /dev/null +++ b/pixmaps/userlist/Makefile.am @@ -0,0 +1,14 @@ +iconsdir = $(pkgdatadir)/pixmaps/userlist +icons_DATA = idleaway.svg \ + idle.svg \ + away.svg \ + friend.svg \ + inhabitant.svg \ + inhabitantplus.svg \ + key.svg \ + newbie.svg \ + schooled.svg \ + star.svg \ + wizard.svg + +EXTRA_DIST = $(icons_DATA) diff --git a/pixmaps/userlist/all.svg b/pixmaps/userlist/all.svg new file mode 100644 index 0000000..12b60f2 --- /dev/null +++ b/pixmaps/userlist/all.svg @@ -0,0 +1,1568 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pixmaps/userlist/away.svg b/pixmaps/userlist/away.svg new file mode 100644 index 0000000..4e43f51 --- /dev/null +++ b/pixmaps/userlist/away.svg @@ -0,0 +1,301 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pixmaps/userlist/friend.svg b/pixmaps/userlist/friend.svg new file mode 100644 index 0000000..d36eeeb --- /dev/null +++ b/pixmaps/userlist/friend.svg @@ -0,0 +1,432 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/pixmaps/userlist/idle.svg b/pixmaps/userlist/idle.svg new file mode 100644 index 0000000..c0435b3 --- /dev/null +++ b/pixmaps/userlist/idle.svg @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/pixmaps/userlist/idleaway.svg b/pixmaps/userlist/idleaway.svg new file mode 100644 index 0000000..0f0c3e3 --- /dev/null +++ b/pixmaps/userlist/idleaway.svg @@ -0,0 +1,314 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pixmaps/userlist/inhabitant.svg b/pixmaps/userlist/inhabitant.svg new file mode 100644 index 0000000..5144aee --- /dev/null +++ b/pixmaps/userlist/inhabitant.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + image/svg+xml + + + + + + + diff --git a/pixmaps/userlist/inhabitantplus.svg b/pixmaps/userlist/inhabitantplus.svg new file mode 100644 index 0000000..4b3b262 --- /dev/null +++ b/pixmaps/userlist/inhabitantplus.svg @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/pixmaps/userlist/key.svg b/pixmaps/userlist/key.svg new file mode 100644 index 0000000..f4a250f --- /dev/null +++ b/pixmaps/userlist/key.svg @@ -0,0 +1,415 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/pixmaps/userlist/newbie.svg b/pixmaps/userlist/newbie.svg new file mode 100644 index 0000000..7d23f73 --- /dev/null +++ b/pixmaps/userlist/newbie.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/pixmaps/userlist/schooled.svg b/pixmaps/userlist/schooled.svg new file mode 100644 index 0000000..a7c75fd --- /dev/null +++ b/pixmaps/userlist/schooled.svg @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/pixmaps/userlist/star.svg b/pixmaps/userlist/star.svg new file mode 100644 index 0000000..622f808 --- /dev/null +++ b/pixmaps/userlist/star.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/pixmaps/userlist/wizard.svg b/pixmaps/userlist/wizard.svg new file mode 100644 index 0000000..123a77d --- /dev/null +++ b/pixmaps/userlist/wizard.svg @@ -0,0 +1,383 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/pixmaps/world.svg b/pixmaps/world.svg new file mode 100644 index 0000000..53b1cbd --- /dev/null +++ b/pixmaps/world.svg @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pixmaps/world_activity.svg b/pixmaps/world_activity.svg new file mode 100644 index 0000000..057806c --- /dev/null +++ b/pixmaps/world_activity.svg @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/po/ChangeLog b/po/ChangeLog new file mode 100644 index 0000000..28bb8f6 --- /dev/null +++ b/po/ChangeLog @@ -0,0 +1,7 @@ +2004-13-12 Jesse van den Kieboom + + * Added/Fixed Dutch (nl) translations + +2004-09-10 Jesse van den Kieboom + + * Added Dutch (nl) translations diff --git a/po/Makefile.in.in b/po/Makefile.in.in new file mode 100644 index 0000000..22cadc6 --- /dev/null +++ b/po/Makefile.in.in @@ -0,0 +1,256 @@ +# Makefile for program source directory in GNU NLS utilities package. +# Copyright (C) 1995, 1996, 1997 by Ulrich Drepper +# +# This file file be copied and used freely without restrictions. It can +# be used in projects which are not available under the GNU Public License +# but which still want to provide support for the GNU gettext functionality. +# Please note that the actual code is *not* freely available. +# +# - Modified by Owen Taylor to use GETTEXT_PACKAGE +# instead of PACKAGE and to look for po2tbl in ./ not in intl/ +# +# - Modified by jacob berkman to install +# Makefile.in.in and po2tbl.sed.in for use with glib-gettextize + +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +PACKAGE = @PACKAGE@ +VERSION = @VERSION@ + +SHELL = /bin/sh +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +top_builddir = .. +VPATH = @srcdir@ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datadir = @datadir@ +libdir = @libdir@ +localedir = $(libdir)/locale +gnulocaledir = $(datadir)/locale +gettextsrcdir = $(datadir)/glib-2.0/gettext/po +subdir = po +install_sh = @install_sh@ +mkdir_p = @mkdir_p@ +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs + +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ + +CC = @CC@ +GENCAT = @GENCAT@ +GMSGFMT = @GMSGFMT@ +MSGFMT = @MSGFMT@ +XGETTEXT = @XGETTEXT@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +MSGMERGE = INTLTOOL_EXTRACT=$(INTLTOOL_EXTRACT) srcdir=$(srcdir) $(INTLTOOL_UPDATE) --gettext-package $(GETTEXT_PACKAGE) --dist +GENPOT = INTLTOOL_EXTRACT=$(INTLTOOL_EXTRACT) srcdir=$(srcdir) $(INTLTOOL_UPDATE) --gettext-package $(GETTEXT_PACKAGE) --pot + +DEFS = @DEFS@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ + +INCLUDES = -I.. -I$(top_srcdir)/intl + +COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $(XCFLAGS) + +SOURCES = +POFILES = @POFILES@ +GMOFILES = @GMOFILES@ +DISTFILES = ChangeLog Makefile.in.in POTFILES.in \ +$(POFILES) $(GMOFILES) $(SOURCES) + +POTFILES = \ + +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +INSTOBJEXT = @INSTOBJEXT@ + +.SUFFIXES: +.SUFFIXES: .c .o .po .pox .gmo .mo .msg .cat + +.c.o: + $(COMPILE) $< + +.po.pox: + $(MAKE) $(GETTEXT_PACKAGE).pot + $(MSGMERGE) $< $(top_builddir)/po/$(GETTEXT_PACKAGE).pot -o $*pox + +.po.mo: + $(MSGFMT) -o $@ $< + +.po.gmo: + file=`echo $* | sed 's,.*/,,'`.gmo \ + && rm -f $$file && $(GMSGFMT) -o $$file $< + +.po.cat: + sed -f ../intl/po2msg.sed < $< > $*.msg \ + && rm -f $@ && $(GENCAT) $@ $*.msg + + +all: all-@USE_NLS@ + +all-yes: $(CATALOGS) +all-no: + +$(GETTEXT_PACKAGE).pot: $(POTFILES) + $(GENPOT) + +install: install-exec install-data +install-exec: +install-data: install-data-@USE_NLS@ +install-data-no: all +install-data-yes: all + if test -n "$(MKINSTALLDIRS)"; then \ + $(MKINSTALLDIRS) $(DESTDIR)$(datadir); \ + else \ + $(SHELL) $(top_srcdir)/mkinstalldirs $(DESTDIR)$(datadir); \ + fi + @catalogs='$(CATALOGS)'; \ + for cat in $$catalogs; do \ + cat=`basename $$cat`; \ + case "$$cat" in \ + *.gmo) destdir=$(gnulocaledir);; \ + *) destdir=$(localedir);; \ + esac; \ + lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \ + dir=$(DESTDIR)$$destdir/$$lang/LC_MESSAGES; \ + if test -n "$(MKINSTALLDIRS)"; then \ + $(MKINSTALLDIRS) $$dir; \ + else \ + $(SHELL) $(top_srcdir)/mkinstalldirs $$dir; \ + fi; \ + if test -r $$cat; then \ + $(INSTALL_DATA) $$cat $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \ + echo "installing $$cat as $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT)"; \ + else \ + $(INSTALL_DATA) $(srcdir)/$$cat $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \ + echo "installing $(srcdir)/$$cat as" \ + "$$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT)"; \ + fi; \ + if test -r $$cat.m; then \ + $(INSTALL_DATA) $$cat.m $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \ + echo "installing $$cat.m as $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m"; \ + else \ + if test -r $(srcdir)/$$cat.m ; then \ + $(INSTALL_DATA) $(srcdir)/$$cat.m \ + $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \ + echo "installing $(srcdir)/$$cat as" \ + "$$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m"; \ + else \ + true; \ + fi; \ + fi; \ + done + if test "$(PACKAGE)" = "glib"; then \ + if test -n "$(MKINSTALLDIRS)"; then \ + $(MKINSTALLDIRS) $(DESTDIR)$(gettextsrcdir); \ + else \ + $(SHELL) $(top_srcdir)/mkinstalldirs $(DESTDIR)$(gettextsrcdir); \ + fi; \ + $(INSTALL_DATA) $(srcdir)/Makefile.in.in \ + $(DESTDIR)$(gettextsrcdir)/Makefile.in.in; \ + else \ + : ; \ + fi + +# Define this as empty until I found a useful application. +installcheck: + +uninstall: + catalogs='$(CATALOGS)'; \ + for cat in $$catalogs; do \ + cat=`basename $$cat`; \ + lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \ + rm -f $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \ + rm -f $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \ + rm -f $(DESTDIR)$(gnulocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \ + rm -f $(DESTDIR)$(gnulocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \ + done + if test "$(PACKAGE)" = "glib"; then \ + rm -f $(DESTDIR)$(gettextsrcdir)/Makefile.in.in; \ + fi + +check: all + +dvi info tags TAGS ID: + +mostlyclean: + rm -f core core.* *.pox $(GETTEXT_PACKAGE).pot *.old.po cat-id-tbl.tmp + rm -fr *.o + rm -f .intltool-merge-cache + +clean: mostlyclean + +distclean: clean + rm -f Makefile Makefile.in POTFILES *.mo *.msg *.cat *.cat.m + +maintainer-clean: distclean + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + rm -f $(GMOFILES) + +distdir = ../$(GETTEXT_PACKAGE)-$(VERSION)/$(subdir) +dist distdir: $(DISTFILES) $(GETTEXT_PACKAGE).pot + dists="$(DISTFILES)"; \ + for file in $$dists; do \ + ln $(srcdir)/$$file $(distdir) 2> /dev/null \ + || cp -p $(srcdir)/$$file $(distdir); \ + done + +update-po: Makefile + $(MAKE) $(GETTEXT_PACKAGE).pot + tmpdir=`pwd`; \ + catalogs='$(CATALOGS)'; \ + for cat in $$catalogs; do \ + cat=`basename $$cat`; \ + lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \ + echo "$$lang:"; \ + result="`$(MSGMERGE) -o $$tmpdir/$$lang.new.po $$lang`"; \ + if $$result; then \ + if cmp $(srcdir)/$$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ + rm -f $$tmpdir/$$lang.new.po; \ + else \ + if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \ + :; \ + else \ + echo "msgmerge for $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \ + rm -f $$tmpdir/$$lang.new.po; \ + exit 1; \ + fi; \ + fi; \ + else \ + echo "msgmerge for $$cat failed!"; \ + rm -f $$tmpdir/$$lang.new.po; \ + fi; \ + done + +# POTFILES is created from POTFILES.in by stripping comments, empty lines +# and Intltool tags (enclosed in square brackets), and appending a full +# relative path to them +POTFILES: POTFILES.in + ( if test 'x$(srcdir)' != 'x.'; then \ + posrcprefix='$(top_srcdir)/'; \ + else \ + posrcprefix="../"; \ + fi; \ + rm -f $@-t $@ \ + && (sed -e '/^#/d' \ + -e "s/^\[.*\] +//" \ + -e '/^[ ]*$$/d' \ + -e "s@.*@ $$posrcprefix& \\\\@" < $(srcdir)/$@.in \ + | sed -e '$$s/\\$$//') > $@-t \ + && chmod a-w $@-t \ + && mv $@-t $@ ) + +Makefile: Makefile.in.in ../config.status POTFILES + cd .. \ + && CONFIG_FILES=$(subdir)/$@.in CONFIG_HEADERS= \ + $(SHELL) ./config.status + +# Tell versions [3.59,3.63) of GNU make not to export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..06b7991 --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,21 @@ +# List of source files containing translatable strings. +src/gm-app.c +src/gm-app-view.c +src/gm-net.c +src/gm-scripts.c +src/gm-scripts-dialog.c +src/gm-triggers-dialog.c +src/gm-world.c +src/gm-world-info-dialog.c +src/gm-world-logs-dialog.c +src/gm-world-properties-dialog.c +src/gm-worlds-list-dialog.c +src/gm-world-view.c +src/gm-ui.h + +ui/gm-scripts.glade +ui/gm-preferences.glade +ui/gm-worlds-list.glade +ui/gm-world-properties.glade +ui/gm-triggers.glade +ui/gm-world.glade diff --git a/po/nl.gmo b/po/nl.gmo new file mode 100644 index 0000000000000000000000000000000000000000..70435229d60e47281cc4de9ded0de2463c6eaf44 GIT binary patch literal 12210 zcmb7}3vgXWdB-;-aS|mYjR}tgD90qOZEVSQNT{PYCblF$Vp|fHEo1Xg&b@bab*!Uv zF6W#pSs}a&356O$pg==HLt0ueG!2jvUTq;fW+;Vrn39(1kYSh>m=@Y$3SkB^?eD+4 z_u<&gq<8e4-|n70`~3FXZ&%-2dCq$bzpo(AK}J^?bKY6Tq!%gGnA?BLnDgOV;RWzr z@G|%Tcp-cMUID)VFNgmOFNQyZm%y_w_4zJ^Pb2_w&8dN)80BNFmVTcdI0pcT2?Hh;Zz?X*fS3tFM4(huqynl5_e?2^x^tVHm^RDpz zZm9A;6yhI)`u+iU1U?kvE9n$nHJ3xzKFBNc!VupX;uDZRQ{%A}E{6Cgq00RlR6D;3 zHICnf=fVGhYv8XTf95I%|Cw+LRDF|B^&Np%!ZgI+30D!n52}4%fefkn4%GPk2&#WS zfhy-QsC?$>zPwAJ-d_dPjv=V=c@flnY=>&^OW_9CfFFQ&Kz%nvCgt10LwYa)p9N>2 z>N^gV?+t-(gPMo;zzE(2)y~gDsp1!*>i;HGdH(`c|Bs>C_j9OnABX$k3I<8#O$8o- z`hEs#ypKWY+pD1T?{!e+o`8e!9;o`i0aeaFLVfoLR6XAd@gIixPochl46cLc(OI=? zbKrGQZ-=V?MBv>}{rwoEE9NsH{T~7!g=)u7q3ZiNR6ou|Y4qLGpyCnK z_zXeiABJkr_7L9_-cLgH=jBlIVWIRahZ?UpL6v&~J{R5vpASC+SHpjW>c>+s0;=zP zsCq7gs`oh|{YohP+6aFa-U!cz$D!)K6-MyQ@OkjVA^odR`Mw3!o^Qk7hTns-SC2!r z`-*4#`mTZ+m-SHju7|4kC{+9BAx$=Kg=+78Q0>1TGL+^)sPDcBH7`Gb8qZ%t&EEi( zs^6DD_48V&`fq@muU$~1-f8IO5vsf#s-KIHBF!7&M)*OfdcFmR;IBgb1$18XF%4DU z3{?Hc0$YKLQ03nWUwjsN0@dD!nLO41cTnT;Pf+#$CzO5qB~&|}6Z!Yo1x`ZM*Myi5 zQ$Xq2?QjyFgqqi1Lyhl6tBg4ae;c9#ri7R5o9Q z@6nL{A5h=@5^CPgq7iD>3aD|o6smvE4DnSVz6MH9H$#1YEo{SWA^s_-c7Fk?yl+8N z%6u29J->kJ_t^}R`f)kbIvIk}lUb;Ky%y^Gw?OIf9Z>B#2{*$Bq00Fo)VMqjHBZmF z%9r~*NY~6YQ1de#;tNpidk2(W-3!&&P22?*^1W`pZ4mB<*)VwW2m2(?h0pAM;;XP38`UX^a zk3hBe2T)V!>q5qf_K+yO6#E8xrFxv(DICxIo@xV#3cymtiN4X-7B5}pUY z5B2?zq00NOz+XVs`#3xYu2}2G??U(@;?IL>|1>-g#!&eZsP<)0j2&QRmG43+QA@B;X5sQ34T_(`aI z4?>O07op1kHdMYJKvd5BH;+ygN1(=~4ORcG@B(-nR6Fko@%KXM<-Kq(dB}O&P zTB!CLSjqg#Ya%bRbI0tWlC*bw)8*mt&zY(7fjzG2ZR;Y4rgKGbsQ2oCfs{Z?++V@d- z7JN7I*U0OUHzWFK9sDJ72+0urn0DvKeFff#>_zmG?U+MUw)(#n(eGj89v7l-@FU0< zkaZz#GVlX|FNLo}jw7SU7^3<9D?~r_Ww#6Y@ljCoB;9K(5`OA?jQlO~d1M~x{8kgt zUMD;L4&*SRvG^0@%g8PyMYL|0kk276M{Y%KLKcx(3&7H_!AX|`UBAwq$2z(qlh_^^$>_Pq`5C0H(0NEJQ{y6Y*xEWcG+!xY>Pazi}Z&2id*&<(vR+naAGL4sQF&vpI zqmewzY`uzR<0Q3>VZH7q+D0#&WX-6`c^$R#qB?=xU2ff2+crSs8c9X@C7Bxo+w0x1A(QV#WEX8# zx$ON3WfT`Ss*0r9w1sU%w6R^iw%77@6;*QyrLd#Jp%%`Cyti6CVtrrFhxwi0kvsB5!AnzThz7w4#6=*0y-?T^TJU z)tuRplHWh0-APjkZR;K(nx_<0+;J!UYYq>AW4 zn#T>>7>af#1!G%B6Z*5qSwkOZOvQ2(H{&E5G9%WtZiIO&;xrm-7kSIl&5^k{Ycf~e zz}9wFB^7$PO-V&*t6>5fT^mg$3yhqGw3~FWh`qYRDQUSHvNV^}=b~!4Wi=m8Rifp* z9WBKf6Oc#s!p4=2;*O?7vw1-ST_@`8qM(Vs-)1CF^P&V@l~i~(X||T3E5-#@>3-|e|WWh=`)?T}=^X+M?i!v*QQB7$ryRu4~QkgVpByXn;O&bHs zkkcx3g{lkQ=f$#HawDBCXLhO6x6so?Ta>Dgq1u(@c7M(smn_WzGtxg6EOZj2z&7fu zds$P+!>iKBa*?E{Hc39P@j$yQrOn-hL-Pe4QPSv+UAy*;O^@v}qjolKrNLN`yF8=o86!74i}^qa8n#uselD z=IuhOTHX+~QX6AFW>H~HK^)C#gEM;?sWp4Dnc3XzsbXpOO>rxhJu-V482Y&n6V^rr z%s$MbcAc_{i>lr7@$NR@7ap2a+7<&;;}uttrX79s>R^wv=aoaQgkdvoEeh6I3`CcrJ}axJ+vK#w$C;Eyqd=um=y(wa zLF-1zX^x#PqF6gnFPdm@#`B`Vu#CIW>}@=X*Sj7;hb3PfDC28n1Aq={fUOi1(wLC5wMvg&Lx?M8(2pS>sK5>?V zzu*S2^VZK9lY<7K6xdd5gIoygZ7AT3S?I3cjm#t-6O|R#*^)Xvn=JBX5igj@c7dh^ z_*L|hcCdD>kmrD{vjZ32q_hgVnB?sey}_8GUTfKi8*X!tnpw`YhZpP20gMCUOFe)S zz_?IKJ9ABs*2si7Go=tYeFph2ojk#e`S`e86c4m;MtQSeE>I*)hMDGgra-Q~j zotcX9ELdXJh0u5sT>ZfpHB+t*|0On{qtOjJi#{~Wl#L7Qa1EbkiD@o73GCrc0c7){ zyv|l@vpSWyZIVUi-2mql%*q0G-uR}-Av9BS`O+ySDQt~q>R4`zhP)5F(r7K42-YoX z(F7JfU1Im1>v=R&DAc^I=J3#Pwhqq42@lyk>X6aX?yOoFY1d{t=``CMY!~xMIcE-K z4LPl7Kb|QAz!YXUi;SD!?0horAv=8VIh1h@gU5Q4$Kpz^;n#U@l(&VMPHd$WfY~Cr zq>V?bv&@+amvD-Af&s+dogtmpm*()qfqkP>=CDoiepz_kt(RT$c#9UGbodipXM=0x z!&a)8nZt8QW&P7@sWUysJtNc~%UC6rDxBllONa&|pN8{^R--Z@DywzGrfjtVzStu; ze2v8UMcuuwGxA0+ofgnURtxR#=5bTuyu{$fRXOE9iTn*Tt+G!tZ`&ULo@Dlg9G_%) z$jpp{>7E(w6kulL5}BFZHf@<1&6k<+BZ|2>h6gyfr!hF~UA$pg3tW2}Z5rA#v}xdg z)v<1Hf7whLgFD*Ia`0e291ToP92|81BBo|=lx=D_x_Z;5YX>)P8r-xc+I-FMHJjIO zB87~D2e3>sNg>~nvF!&2_fHItZjW|P4Da8w{^-Q`*uXvv=pcT2R;DpZI~?7l?Il`d z4K*<9dlOsBIizkqrsNxwy2U|X8p_gbeCapV!Gktl7>-8UY>v^^@qIU@$q+uqwt@Y7 z_K)?tx0y~cL>0c<;6dDfoDHnO##V~Qd3{v(r->U59o#v%rI%0b!bT5{aguAGR>RSj zT2c*|3uKd5b8wz&F|(%}oD(cq9H$K`Aj6dr<6Fb<+bo3fvt-g1;(5B4#c@G4j?kx_ zp&0`fI;$l^i95$=6&4EMZLP>Dr%gG2$L%Hmbn$8OkcQ!>&QZ>=etekAd6tRm7@b&3j*)|{AFqcQ z$rl;>^k$!y7^w{=8W~xGA zuzU?E%qjNF8Ll0crp%vVM8gZ;Q08P8!=!UQ0~JpC{&26w%>_aCe6MS?MOnYT6aGy$ zn&&bJ4cAQ4Y7TbJZe(PQ+iS{@*=D#c=$%iFY%za1odBX76_Nc5dHT353!3HERT_trh!6EryBGwFKt_6hb3bs+2x^qT8d zwLNSSH7Oa}2D=i&%ZY@Kby0wWWe$e*#4!b>HRIYCqOHo2a82l<{H{Dw^ z78X=DtG_bgwu^85^#NPI%>NV0kRkG3+Jw7JaGi+Ub;2!L=H@ULeAN=o(TzgNUZ7Pv zpW1~QNpzLnv_W5-+mo5#BWZA03|?2NcAu_vHZA2qXY_&o9U#jkz3S$bec_f;Hy96p zL9*=&qLQ(I`|5# zHSe2aNcdk0x9HtH-rqFXXstXD`C87eLy@Sn|1QGvAE5(z&SeA-SE0XaU|a26M_7Nv zmxsb-gW2isDDVSVqnz(JK{71iB{vnETTr#q9O@X&&OJfbigps*9Y^Ow=T!GB?Qh7l zt}jQ&*0NYr?T8b-z7uw7%vPDU=kdEwDbE{b{+FSfiiD0fy#19{VNRKVstop-ceXgA z8SY(E^u2aV7FRR4)#hN1tI%gEwYy1Ue#14zJhu_+%u%$r=h2NaVH#y_xZM>6*Xe~f z8_o{P4+*t04bC-l{+}ifY)L8WY zp9G|nEUO4F+j-XAc?nLb*5HupIOEsaJo>)u_zVB#wwW~yEFq^hM(j$?Ga j|HGN-u6l)g5|wRcylbh@?lzYtoY2yl2Dc?{tj+%c**!BI literal 0 HcmV?d00001 diff --git a/po/nl.po b/po/nl.po new file mode 100644 index 0000000..779c9ff --- /dev/null +++ b/po/nl.po @@ -0,0 +1,973 @@ +# GnoeMoe Gnome MOO Client. +# Copyright (C) 2004 +# This file is distributed under the same license as the gnoemoe package. +# Jesse van den Kieboom, 2004. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gnoemoe 0.8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2005-10-08 14:41+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Jesse van den Kieboom \n" +"Language-Team: Dutch \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../src/gm-app.c:43 +msgid "" +"Enable debugging. Debugging can be done in different depth levels, each " +"level inherits all messages from the levels below: 0 = None, 1 = Default and " +"2 = MCP" +msgstr "" +"Activeer debugging. Je kunt op verschillende niveau's debuggen, elk niveau " +"erft alle berichten van de niveau's eronder: 0 = None, 1 = Default en 2 = MCP" + +#: ../src/gm-app.c:46 +msgid "DBGGLEVEL" +msgstr "DBGNIVEAU" + +#: ../src/gm-app.c:54 +msgid "Show application version" +msgstr "Toon applicatie versie" + +#: ../src/gm-app.c:62 +msgid "Load specified worlds, seperated by a `,'" +msgstr "Laad gespecificeerde werelden, gescheiden door een `,'" + +#: ../src/gm-app.c:63 +msgid "WORLDS" +msgstr "WERELDEN" + +#: ../src/gm-app.c:70 +msgid "Recover from previous session (used with gnome session)" +msgstr "" +"Herstel van de vorige opgeslagen sessie (wordt gebruikt door gnome sessies)" + +#: ../src/gm-app.c:419 +#, c-format +msgid "" +"GnoeMoe Application: there is no application directory, this is very bad!!!\n" +msgstr "GnoeMoe Applicatie: er is geen applicatie map, dit is heel erg!!!\n" + +#: ../src/gm-app.c:437 +#, c-format +msgid "Current version of GnoeMoe is %s\n" +msgstr "Huidige versie van GnoeMoe is %s\n" + +#: ../src/gm-app-view.c:213 +msgid "Find:" +msgstr "Zoek: " + +#: ../src/gm-app-view.c:219 +msgid "Search backwards" +msgstr "Zoek achterwaarts" + +#: ../src/gm-app-view.c:232 ../src/gm-ui.h:70 +msgid "Find next" +msgstr "Zoek volgende" + +#. File menu +#: ../src/gm-app-view.c:521 ../src/gm-ui.h:49 +msgid "Connect" +msgstr "Verbinden" + +#: ../src/gm-app-view.c:524 +msgid "Disconnect" +msgstr "Verbinding verbreken" + +#: ../src/gm-app-view.c:740 +msgid "Open world " +msgstr "Open wereld " + +#: ../src/gm-app-view.c:1210 +msgid "Jesse van den Kieboom" +msgstr "" + +#: ../src/gm-app-view.c:1211 +msgid "Sjoerd Simons (debian package)" +msgstr "Sjoerd Simons (debian pakket)" + +#: ../src/gm-app-view.c:1216 +msgid "Simon Gijsen" +msgstr "" + +#: ../src/gm-app-view.c:1223 +msgid "GnoeMoe" +msgstr "" + +#: ../src/gm-app-view.c:1225 +msgid "(C) 2004-2005 Icecrew.nl" +msgstr "" + +#: ../src/gm-app-view.c:1226 +msgid "GnoeMoe Gnome MOO Client" +msgstr "" + +#: ../src/gm-app-view.c:1385 +msgid "You didn't select a log file" +msgstr "Je hebt geen log bestand geselecteerd" + +#: ../src/gm-net.c:184 +#, c-format +msgid "Connection lost... (%s)" +msgstr "Connectie verloren... (%s)" + +#: ../src/gm-net.c:395 +msgid "No addresses available" +msgstr "Geen adressen beschikbaar" + +#: ../src/gm-net.c:439 +msgid "Not connected" +msgstr "Niet verbonden" + +#: ../src/gm-net.c:535 +msgid "Connect timeout (5)" +msgstr "Verbinden timeout (5)" + +#: ../src/gm-scripts.c:316 +#, c-format +msgid "Error in execution: %s" +msgstr "Error bij het uitvoeren van: %s" + +#: ../src/gm-scripts.c:323 +msgid "Ruby backtrace:" +msgstr "" + +#: ../src/gm-scripts.c:329 +#, c-format +msgid "\tfrom %s" +msgstr "\tuit %s" + +#: ../src/gm-scripts.c:388 +#, c-format +msgid "Run script '%s' from '%s' (%s)" +msgstr "Script '%s' uit '%s' (%s) uitvoeren" + +#: ../src/gm-scripts.c:392 +#, c-format +msgid "Run script '%s' from '%s' ()" +msgstr "Script '%s' uit '%s' () uitvoeren" + +#: ../src/gm-scripts.c:436 +#, c-format +msgid "Registering functions from '%s'" +msgstr "Functies registreren uit '%s'" + +#: ../src/gm-scripts.c:492 +#, c-format +msgid "File `%s' already loaded" +msgstr "Bestand `%s' is al toegevoegd" + +#: ../src/gm-scripts.c:502 +#, c-format +msgid "File `%s' added" +msgstr "Bestand `%s' toegevoegd" + +#: ../src/gm-scripts.c:806 +#, c-format +msgid "Register function '%s' from '%s'" +msgstr "Registreer functie '%s' uit '%s" + +#: ../src/gm-scripts.c:814 +#, c-format +msgid "Script '%s' is already defined" +msgstr "Script '%s' is al gedefinieerd" + +#: ../src/gm-scripts-dialog.c:133 +msgid "Saved " +msgstr "Opgeslagen " + +#: ../src/gm-scripts-dialog.c:146 +msgid "Saving failed: " +msgstr "Opslaan mislukt: " + +#: ../src/gm-scripts-dialog.c:190 +msgid "Loaded " +msgstr "Ingeladen " + +#: ../src/gm-scripts-dialog.c:201 ../src/gm-scripts-dialog.c:597 +msgid "New " +msgstr "Nieuw " + +#: ../src/gm-scripts-dialog.c:294 +msgid "User: " +msgstr "Gebruiker:" + +#: ../src/gm-scripts-dialog.c:296 +msgid "Share: " +msgstr "Gedeeld:" + +#: ../src/gm-scripts-dialog.c:330 ../src/gm-ui.h:38 ../ui/gm-scripts.glade.h:4 +msgid "Scripts" +msgstr "" + +#: ../src/gm-scripts-dialog.c:393 ../src/gm-scripts-dialog.c:665 +msgid "Share" +msgstr "Gedeeld" + +#: ../src/gm-scripts-dialog.c:396 +msgid "User" +msgstr "Gebruiker" + +#: ../src/gm-scripts-dialog.c:437 +msgid "Filename" +msgstr "Bestandsnaam" + +#: ../src/gm-scripts-dialog.c:729 +msgid "Changed " +msgstr "Veranderd " + +#: ../src/gm-scripts-dialog.c:731 +msgid "Changed " +msgstr "Veranderd " + +#: ../src/gm-scripts-dialog.c:758 +msgid "Save file" +msgstr "Bestand opslaan" + +#: ../src/gm-triggers-dialog.c:104 ../ui/gm-preferences.glade.h:3 +msgid "Black" +msgstr "Zwart" + +#: ../src/gm-triggers-dialog.c:105 ../ui/gm-preferences.glade.h:27 +msgid "Red" +msgstr "Rood" + +#: ../src/gm-triggers-dialog.c:106 ../ui/gm-preferences.glade.h:19 +msgid "Green" +msgstr "Groen" + +#: ../src/gm-triggers-dialog.c:107 ../ui/gm-preferences.glade.h:35 +msgid "Yellow" +msgstr "Geel" + +#: ../src/gm-triggers-dialog.c:108 ../ui/gm-preferences.glade.h:5 +msgid "Blue" +msgstr "Blauw" + +#: ../src/gm-triggers-dialog.c:109 ../ui/gm-preferences.glade.h:25 +msgid "Purple" +msgstr "Paars" + +#: ../src/gm-triggers-dialog.c:110 ../ui/gm-preferences.glade.h:9 +msgid "Cyan" +msgstr "Cyaan" + +#: ../src/gm-triggers-dialog.c:111 ../ui/gm-preferences.glade.h:33 +msgid "White" +msgstr "Wit" + +#: ../src/gm-triggers-dialog.c:116 +msgid "Contains" +msgstr "Bevat:" + +#: ../src/gm-triggers-dialog.c:117 +msgid "Not contains" +msgstr "Bevat niet" + +#: ../src/gm-triggers-dialog.c:118 +msgid "Begins with" +msgstr "Begint met" + +#: ../src/gm-triggers-dialog.c:119 +msgid "Not begins with" +msgstr "Begint niet met" + +#: ../src/gm-triggers-dialog.c:120 +msgid "Ends with" +msgstr "Eindigt met" + +#: ../src/gm-triggers-dialog.c:121 +msgid "Not ends with" +msgstr "Eindigt niet met" + +#: ../src/gm-triggers-dialog.c:122 +msgid "Matches" +msgstr "Matched" + +#: ../src/gm-triggers-dialog.c:123 +msgid "Not matches" +msgstr "Matched niet" + +#: ../src/gm-triggers-dialog.c:128 +msgid "Online" +msgstr "Verbonden" + +#: ../src/gm-triggers-dialog.c:129 +msgid "Offline" +msgstr "Niet verbonden" + +#: ../src/gm-triggers-dialog.c:130 +msgid "Idle" +msgstr "Luieren" + +#: ../src/gm-triggers-dialog.c:131 +msgid "No longer idle" +msgstr "Niet langer inactief" + +#: ../src/gm-triggers-dialog.c:132 +msgid "Away" +msgstr "Afwezig" + +#: ../src/gm-triggers-dialog.c:133 +msgid "No longer away" +msgstr "Niet langer afwezig" + +#: ../src/gm-triggers-dialog.c:138 +msgid "Highlight line" +msgstr "Markeer regel" + +#: ../src/gm-triggers-dialog.c:140 +msgid "Highlight match" +msgstr "Markeer match" + +#: ../src/gm-triggers-dialog.c:142 ../src/gm-triggers-dialog.c:156 +msgid "Beep" +msgstr "Beep" + +#: ../src/gm-triggers-dialog.c:143 ../src/gm-triggers-dialog.c:157 +msgid "Play sound" +msgstr "Geluid afspelen" + +#: ../src/gm-triggers-dialog.c:145 ../src/gm-triggers-dialog.c:159 +msgid "Notify" +msgstr "Notificeren" + +#: ../src/gm-triggers-dialog.c:147 ../src/gm-triggers-dialog.c:161 +msgid "Run script" +msgstr "Script uitvoeren" + +#: ../src/gm-triggers-dialog.c:150 ../src/gm-triggers-dialog.c:164 +msgid "Run" +msgstr "Uitvoeren" + +#: ../src/gm-triggers-dialog.c:329 +msgid "Please fill in a trigger name" +msgstr "Vul alstublieft een trigger naam in" + +#: ../src/gm-triggers-dialog.c:339 +msgid "Please specify at least one condition" +msgstr "Specificeer alstublieft ten minste één conditie" + +#: ../src/gm-triggers-dialog.c:348 +msgid "Please specify at least one action" +msgstr "Specificeer alstublieft ten minste één actie" + +#: ../src/gm-triggers-dialog.c:388 +msgid "" +"Player event\n" +"Player events are triggered on userlist activity" +msgstr "" +"Speler event\n" +"Speler events worden getriggered door\n" +"spelerlijst activiteit" + +#: ../src/gm-triggers-dialog.c:394 +msgid "" +"World event\n" +"World events are triggered on incoming lines of text" +msgstr "" +"Wereld event\n" +"Wereld events worden getriggered door\n" +"binnenkomende regels tekst" + +#: ../src/gm-triggers-dialog.c:685 +msgid "Select a event type first" +msgstr "Selecteer eerst een event type" + +#: ../src/gm-triggers-dialog.c:825 +msgid "Select file" +msgstr "Selecteer bestand" + +#: ../src/gm-triggers-dialog.c:863 +msgid "Browse" +msgstr "Bladeren" + +#: ../src/gm-world-info-dialog.c:24 ../src/gm-worlds-list-dialog.c:79 +msgid "unspecified" +msgstr "niet gespecificeerd" + +#: ../src/gm-world-logs-dialog.c:41 ../src/gm-ui.h:54 +msgid "Logs" +msgstr "" + +#: ../src/gm-world-logs-dialog.c:91 +msgid "There are no log files for this world" +msgstr "Er zijn geen log bestanden voor deze wereld" + +#: ../src/gm-world-logs-dialog.c:96 +#, c-format +msgid "Couldn't open the log directory: %s" +msgstr "Kon log directory niet openen: %s" + +#: ../src/gm-world-properties-dialog.c:36 +#: ../src/gm-world-properties-dialog.c:49 +#: ../src/gm-world-properties-dialog.c:70 +#: ../src/gm-world-properties-dialog.c:93 +msgid "Western" +msgstr "Westers" + +#: ../src/gm-world-properties-dialog.c:37 +#: ../src/gm-world-properties-dialog.c:71 +#: ../src/gm-world-properties-dialog.c:91 +msgid "Central European" +msgstr "Centraal-Europees" + +#: ../src/gm-world-properties-dialog.c:38 +msgid "South European" +msgstr "Zuid-Europees" + +#: ../src/gm-world-properties-dialog.c:39 +#: ../src/gm-world-properties-dialog.c:47 +#: ../src/gm-world-properties-dialog.c:98 +msgid "Baltic" +msgstr "Baltisch" + +#: ../src/gm-world-properties-dialog.c:40 +#: ../src/gm-world-properties-dialog.c:72 +#: ../src/gm-world-properties-dialog.c:79 +#: ../src/gm-world-properties-dialog.c:81 +#: ../src/gm-world-properties-dialog.c:82 +#: ../src/gm-world-properties-dialog.c:92 +msgid "Cyrillic" +msgstr "Cyrillisch" + +#: ../src/gm-world-properties-dialog.c:41 +#: ../src/gm-world-properties-dialog.c:75 +#: ../src/gm-world-properties-dialog.c:97 +msgid "Arabic" +msgstr "Arabisch" + +#: ../src/gm-world-properties-dialog.c:42 +#: ../src/gm-world-properties-dialog.c:94 +msgid "Greek" +msgstr "Grieks" + +#: ../src/gm-world-properties-dialog.c:43 +msgid "Hebrew Visual" +msgstr "Hebreeuws visueel" + +#: ../src/gm-world-properties-dialog.c:44 +#: ../src/gm-world-properties-dialog.c:74 +#: ../src/gm-world-properties-dialog.c:96 +msgid "Hebrew" +msgstr "Hebreeuws" + +#: ../src/gm-world-properties-dialog.c:45 +#: ../src/gm-world-properties-dialog.c:73 +#: ../src/gm-world-properties-dialog.c:95 +msgid "Turkish" +msgstr "Turks" + +#: ../src/gm-world-properties-dialog.c:46 +msgid "Nordic" +msgstr "Noors" + +#: ../src/gm-world-properties-dialog.c:48 +msgid "Celtic" +msgstr "Celtisch" + +#: ../src/gm-world-properties-dialog.c:50 +msgid "Romanian" +msgstr "Roemeens" + +#: ../src/gm-world-properties-dialog.c:51 +#: ../src/gm-world-properties-dialog.c:52 +#: ../src/gm-world-properties-dialog.c:53 +#: ../src/gm-world-properties-dialog.c:54 +#: ../src/gm-world-properties-dialog.c:55 +msgid "Unicode" +msgstr "" + +#: ../src/gm-world-properties-dialog.c:56 +msgid "Armenian" +msgstr "Armeens" + +#: ../src/gm-world-properties-dialog.c:57 +#: ../src/gm-world-properties-dialog.c:58 +#: ../src/gm-world-properties-dialog.c:63 +msgid "Chinese Traditional" +msgstr "Chinees-traditioneel" + +#: ../src/gm-world-properties-dialog.c:59 +msgid "Cyrillic/Russian" +msgstr "Cyrillisch/Russisch" + +#: ../src/gm-world-properties-dialog.c:61 +#: ../src/gm-world-properties-dialog.c:77 +#: ../src/gm-world-properties-dialog.c:85 +msgid "Japanese" +msgstr "Japans" + +#: ../src/gm-world-properties-dialog.c:62 +#: ../src/gm-world-properties-dialog.c:78 +#: ../src/gm-world-properties-dialog.c:80 +#: ../src/gm-world-properties-dialog.c:88 +msgid "Korean" +msgstr "Koreaans" + +#: ../src/gm-world-properties-dialog.c:64 +#: ../src/gm-world-properties-dialog.c:65 +#: ../src/gm-world-properties-dialog.c:66 +#: ../src/gm-world-properties-dialog.c:68 +msgid "Chinese Simplified" +msgstr "Chinees-vereenvoudigd" + +#: ../src/gm-world-properties-dialog.c:67 +msgid "Georgian" +msgstr "Georgisch" + +#: ../src/gm-world-properties-dialog.c:83 +msgid "Cyrillic/Ukrainian" +msgstr "Cyrillisch/Oekraïens" + +#: ../src/gm-world-properties-dialog.c:86 +#: ../src/gm-world-properties-dialog.c:89 +#: ../src/gm-world-properties-dialog.c:99 +msgid "Vietnamese" +msgstr "Vietnamees" + +#: ../src/gm-world-properties-dialog.c:87 +msgid "Thai" +msgstr "Thais" + +#: ../src/gm-world-properties-dialog.c:171 +#, c-format +msgid "" +"%s\n" +"Conditions: %d\n" +"Actions: %d" +msgstr "" +"%s\n" +"Condities: %d\n" +"Acties: %d" + +#: ../src/gm-world-properties-dialog.c:341 +msgid "World properties - " +msgstr "Wereld eigenschappen - " + +#: ../src/gm-world-properties-dialog.c:471 +#, c-format +msgid "Name can not be %s because a world with that name already exists." +msgstr "Naam kan niet %s zijn omdat er al een wereld bestaat met die naam." + +#: ../src/gm-world-properties-dialog.c:490 +msgid "Host can not be empty, please fill in a host." +msgstr "Host kan niet leeg zijn, vul alsjeblieft een host in." + +#: ../src/gm-world-properties-dialog.c:630 +msgid "First select a trigger to remove" +msgstr "Selecteer eerst een trigger om te verwijderen" + +#: ../src/gm-worlds-list-dialog.c:73 +msgid "" +"\n" +"Server: " +msgstr "" + +#: ../src/gm-worlds-list-dialog.c:74 +msgid "" +"\n" +"Player: " +msgstr "" +"\n" +"Speler: " + +#: ../src/gm-worlds-list-dialog.c:144 +msgid "Logo" +msgstr "" + +#: ../src/gm-worlds-list-dialog.c:150 ../src/gm-world-view.c:109 +msgid "Name" +msgstr "Naam" + +#: ../src/gm-worlds-list-dialog.c:238 +#, c-format +msgid "" +"Can't remove the world %s because it is loaded. First close the world and " +"then try again." +msgstr "" +"De wereld %s kan niet verwijderd worden omdat de wereld nog geladen is. " +"Sluit eerst de wereld en probeer dan opnieuw." + +#: ../src/gm-worlds-list-dialog.c:251 +msgid "You first need to select a world to delete." +msgstr "Je moet eerst de wereld die je wilt verwijderen selecteren." + +#: ../src/gm-worlds-list-dialog.c:274 +msgid "You first need to select a world to modify." +msgstr "Je moet eerst de wereld die je wilt wijzigen selecteren!" + +#: ../src/gm-worlds-list-dialog.c:377 +msgid "You first need to select a world to duplicate." +msgstr "Je moet eerst de wereld die je wilt dupliceren selecteren!" + +#: ../src/gm-worlds-list-dialog.c:401 +msgid "You first need to select a world to connect to." +msgstr "Je moet eerst de wereld waarmee je wilt verbinden selecteren." + +#: ../src/gm-world-view.c:103 +msgid "I" +msgstr "" + +#: ../src/gm-world-view.c:174 +msgid "Welcome to GnoeMoe, explorer of new worlds!" +msgstr "Welkom bij GnoeMoe, verkenner van nieuwe werelden!" + +#: ../src/gm-world-view.c:198 ../ui/gm-world-properties.glade.h:15 +#: ../ui/gm-world.glade.h:1 +msgid "World" +msgstr "Wereld" + +#: ../src/gm-world-view.c:357 +#, c-format +msgid "# Connect failed: %s" +msgstr "# Connectie faalde: %s" + +#: ../src/gm-world-view.c:360 +#, c-format +msgid "# Connection lost... (%s)" +msgstr "# Connectie verloren... (%s)" + +#: ../src/gm-world-view.c:363 +#, c-format +msgid "# Error: %s" +msgstr "# Fout: %s" + +#: ../src/gm-world-view.c:378 +#, c-format +msgid "# Trying %s port %s" +msgstr "# Probeer %s poort %s" + +#: ../src/gm-world-view.c:382 +#, c-format +msgid "# Connecting to %s port %s" +msgstr "# Verbinding maken naar %s poort %s" + +#: ../src/gm-world-view.c:385 +msgid "# Connected" +msgstr "# Verbonden" + +#: ../src/gm-world-view.c:388 +msgid "# Disconnected" +msgstr "# Verbinding verbroken" + +#: ../src/gm-world-view.c:391 +msgid "# Disconnecting" +msgstr "# Verbinding verbreken" + +#. Toplevel +#: ../src/gm-ui.h:17 +msgid "_World" +msgstr "_Wereld" + +#: ../src/gm-ui.h:18 +msgid "_Edit" +msgstr "_Bewerken" + +#: ../src/gm-ui.h:19 +msgid "_View" +msgstr "Beel_d" + +#: ../src/gm-ui.h:20 +msgid "_Help" +msgstr "_Hulp" + +#. World menu +#: ../src/gm-ui.h:23 +msgid "New World..." +msgstr "Nieuwe Wereld..." + +#: ../src/gm-ui.h:24 +msgid "Create a new world" +msgstr "Maak een nieuwe wereld" + +#: ../src/gm-ui.h:26 +msgid "Quit the program" +msgstr "Sluit het programma af" + +#. Edit menu +#: ../src/gm-ui.h:29 +msgid "Worlds..." +msgstr "Werelden..." + +#: ../src/gm-ui.h:30 +msgid "Edit worlds" +msgstr "Bewerk werelden" + +#: ../src/gm-ui.h:32 +msgid "Configure the application" +msgstr "Configureer de applicatie" + +#. View menu +#: ../src/gm-ui.h:36 +msgid "MCP" +msgstr "" + +#: ../src/gm-ui.h:37 +msgid "View MCP console" +msgstr "Bekijk MCP console" + +#: ../src/gm-ui.h:39 +#, fuzzy +msgid "View scripts" +msgstr "Bekijk scripts" + +#: ../src/gm-ui.h:43 +msgid "About this application" +msgstr "Over deze applicatie" + +#: ../src/gm-ui.h:50 +msgid "Connect or disconnect the current world" +msgstr "Maak of verbreek verbinding met de huidige wereld" + +#: ../src/gm-ui.h:53 +msgid "Close current world" +msgstr "Sluit de huidige wereld" + +#: ../src/gm-ui.h:55 +msgid "View current world logs" +msgstr "Bekijk logs van de huidige wereld" + +#: ../src/gm-ui.h:56 +msgid "In_fo" +msgstr "In_fo" + +#: ../src/gm-ui.h:57 +msgid "View current world info" +msgstr "Bekijk info van de huidige wereld" + +#: ../src/gm-ui.h:61 +msgid "Cut the selection" +msgstr "Knip de selectie" + +#: ../src/gm-ui.h:63 +msgid "Copy the selection" +msgstr "Kopieër de selectie" + +#: ../src/gm-ui.h:65 +msgid "Paste the clipboard" +msgstr "Plak het klembord" + +#: ../src/gm-ui.h:66 +msgid "Current world..." +msgstr "Huidige wereld..." + +#: ../src/gm-ui.h:67 +msgid "Edit the current world" +msgstr "Bewerk de huidige wereld" + +#: ../src/gm-ui.h:69 +msgid "Find text" +msgstr "Zoek naar tekst" + +#: ../src/gm-ui.h:71 +msgid "Search for the next occurence" +msgstr "Zoek verder" + +#: ../ui/gm-scripts.glade.h:1 +msgid "Console" +msgstr "" + +#: ../ui/gm-scripts.glade.h:2 ../ui/gm-preferences.glade.h:12 +msgid "Editor" +msgstr "" + +#: ../ui/gm-scripts.glade.h:3 +msgid "Overview" +msgstr "Overzicht" + +#: ../ui/gm-preferences.glade.h:1 +msgid "Background" +msgstr "Achtergrond" + +#: ../ui/gm-preferences.glade.h:2 +msgid "Background:" +msgstr "Achtergrond:" + +#: ../ui/gm-preferences.glade.h:4 +msgid "Black High" +msgstr "Zwart Fel" + +#: ../ui/gm-preferences.glade.h:6 +msgid "Blue High" +msgstr "Blauw Fel" + +#: ../ui/gm-preferences.glade.h:7 +msgid "Bold" +msgstr "Vet" + +#: ../ui/gm-preferences.glade.h:8 +msgid "Colors" +msgstr "Kleuren" + +#: ../ui/gm-preferences.glade.h:10 +msgid "Cyan High" +msgstr "Cyaan Fel" + +#: ../ui/gm-preferences.glade.h:11 +msgid "Default" +msgstr "Standaard" + +#: ../ui/gm-preferences.glade.h:13 +msgid "Embed editor into GnoeMoe (implies terminal)" +msgstr "Geëmbedde editor (impliceert terminal)" + +#: ../ui/gm-preferences.glade.h:14 +msgid "Font" +msgstr "Lettertype" + +#: ../ui/gm-preferences.glade.h:15 +msgid "Font-family:" +msgstr "Lettertype:" + +#: ../ui/gm-preferences.glade.h:16 +msgid "Foreground" +msgstr "Voorgrond" + +#: ../ui/gm-preferences.glade.h:17 +msgid "Foreground High" +msgstr "Voorgrond Fel" + +#: ../ui/gm-preferences.glade.h:18 +msgid "Foreground:" +msgstr "Voorgrond:" + +#: ../ui/gm-preferences.glade.h:20 +msgid "Green High" +msgstr "Groen Fel" + +#: ../ui/gm-preferences.glade.h:21 +msgid "Italic" +msgstr "Schuin" + +#: ../ui/gm-preferences.glade.h:22 +msgid "Needs terminal" +msgstr "Terminal nodig" + +#: ../ui/gm-preferences.glade.h:23 +msgid "Preferences" +msgstr "Voorkeuren" + +#: ../ui/gm-preferences.glade.h:24 +msgid "Program" +msgstr "Programma" + +#: ../ui/gm-preferences.glade.h:26 +msgid "Purple High" +msgstr "Paars Fel" + +#: ../ui/gm-preferences.glade.h:28 +msgid "Red High" +msgstr "Rood Fel" + +#: ../ui/gm-preferences.glade.h:29 +msgid "Strikethrough" +msgstr "Doorstreept" + +#: ../ui/gm-preferences.glade.h:30 +msgid "Underline" +msgstr "Onderstreept" + +#: ../ui/gm-preferences.glade.h:31 +msgid "Use alternative editor" +msgstr "Gebruik alternatieve editor" + +#: ../ui/gm-preferences.glade.h:32 +msgid "Use high colors for bold text" +msgstr "Gebruik felle kleuren voor vette tekst" + +#: ../ui/gm-preferences.glade.h:34 +msgid "White High" +msgstr "Wit Fel" + +#: ../ui/gm-preferences.glade.h:36 +msgid "Yellow High" +msgstr "Geel Fel" + +#: ../ui/gm-worlds-list.glade.h:1 +msgid "Worlds listing" +msgstr "Werelden lijst" + +#: ../ui/gm-worlds-list.glade.h:2 +msgid "_Connect" +msgstr "_Verbinden" + +#: ../ui/gm-worlds-list.glade.h:3 +msgid "_Duplicate" +msgstr "_Dupliceren" + +#: ../ui/gm-world-properties.glade.h:1 +msgid "Auto-login" +msgstr "Auto-inloggen" + +#: ../ui/gm-world-properties.glade.h:2 +msgid "Autologin" +msgstr "Autoinloggen" + +#: ../ui/gm-world-properties.glade.h:3 +msgid "Charset:" +msgstr "Karakterset:" + +#: ../ui/gm-world-properties.glade.h:4 +msgid "General" +msgstr "Algemeen" + +#: ../ui/gm-world-properties.glade.h:5 +msgid "Host:" +msgstr "" + +#: ../ui/gm-world-properties.glade.h:6 +msgid "Language" +msgstr "Taal" + +#: ../ui/gm-world-properties.glade.h:7 +msgid "Load on startup" +msgstr "Laden bij opstarten" + +#: ../ui/gm-world-properties.glade.h:8 ../ui/gm-triggers.glade.h:4 +msgid "Name:" +msgstr "Naam:" + +#: ../ui/gm-world-properties.glade.h:9 +msgid "Password:" +msgstr "Wachtwoord:" + +#: ../ui/gm-world-properties.glade.h:10 +msgid "Player name:" +msgstr "Speler naam:" + +#: ../ui/gm-world-properties.glade.h:11 +msgid "Port:" +msgstr "Poort:" + +#: ../ui/gm-world-properties.glade.h:12 +msgid "Server" +msgstr "" + +#: ../ui/gm-world-properties.glade.h:13 ../ui/gm-triggers.glade.h:5 +msgid "Triggers" +msgstr "" + +#: ../ui/gm-world-properties.glade.h:14 +msgid "Use autoreconnect" +msgstr "Gebruik auto-herverbinden" + +#: ../ui/gm-world-properties.glade.h:16 +msgid "World properties" +msgstr "Wereld eigenschappen" + +#: ../ui/gm-triggers.glade.h:1 +msgid "Actions:" +msgstr "Acties:" + +#: ../ui/gm-triggers.glade.h:2 +msgid "Choose which type of event you want to create a trigger for." +msgstr "Kies voor welk type event je een trigger wilt aanmaken." + +#: ../ui/gm-triggers.glade.h:3 +msgid "Conditions:" +msgstr "Condities:" diff --git a/scripts/Makefile.am b/scripts/Makefile.am new file mode 100644 index 0000000..476f0d0 --- /dev/null +++ b/scripts/Makefile.am @@ -0,0 +1,7 @@ +scriptsdir = $(pkgdatadir)/scripts +scripts_DATA = misc.rb \ + run.rb \ + editing.rb \ + music.rb + +EXTRA_DIST = $(scripts_DATA) diff --git a/scripts/editing.rb b/scripts/editing.rb new file mode 100644 index 0000000..95819d0 --- /dev/null +++ b/scripts/editing.rb @@ -0,0 +1,53 @@ +=begin + editing.rb + Copyright (c) 2004 by Jesse van den Kieboom + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Defines: + e - edit a verb + me - edit your description + he - edit your locations description + de - edit a objects description + ne - edit a property +=end + +def register_functions + $scripts.register("e", "use: /e :\nedit a verb", "edit_e") + $scripts.register("me", "use: /me\nedit your description", "edit_me") + $scripts.register("he", "use: /he\nedit your locations description", "edit_he") + $scripts.register("de", "use: /de \nedit a objects description", "edit_de") + $scripts.register("ne", "use: /ne .\nedit a property", "edit_ne") +end + +def edit_e(argstr) + $world.sendln("@edit " + argstr) +end + +def edit_me(argstr) + $world.sendln("@notedit me.description") +end + +def edit_he(argstr) + $world.sendln("@notedit here.description") +end + +def edit_ne(argstr) + $world.sendln("@notedit " + argstr) +end + +def edit_de(argstr) + $world.sendln("@notedit " + argstr + ".description") +end diff --git a/scripts/misc.rb b/scripts/misc.rb new file mode 100644 index 0000000..0b38a41 --- /dev/null +++ b/scripts/misc.rb @@ -0,0 +1,39 @@ +=begin + misc.rb + Copyright (c) 2004 by Jesse van den Kieboom + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Defines: + open - open a world + sendall - send all connected worlds the same text +=end + +def register_functions + $scripts.register("open", "use: /open \nopen a world") + $scripts.register("sendall", "use: /sendall \nsends to all currently connected worlds") +end + +def open(argstr) + $client.open(argstr) +end + +def sendall(argstr) + $client.worlds.each {|wld| + if (wld.loaded? && wld.connected?) + wld.sendln(argstr) + end + } +end diff --git a/scripts/music.rb b/scripts/music.rb new file mode 100644 index 0000000..48ba022 --- /dev/null +++ b/scripts/music.rb @@ -0,0 +1,171 @@ +=begin + music.rb + Copyright (c) 2004 by Jesse van den Kieboom + Copyright (c) 2004/07/29 by Simon Gijsen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Defines: + rb/rhythmbox - show which song rb is currenly playing, control rb + xmms/beep - show which song xmms is playing, control xmms (thanks to Simon Gijsen) +=end + +def register_functions + $scripts.register("rhythmbox", "use: /rhythmbox |next|prev|play|pause\nsends the song Rhythmbox is currently playing to the world prefixed by or performs the action specified if pause, play, next or prev is specified") + $scripts.register("rb", "alias for rhythmbox", "rhythmbox") + $scripts.register("xmms", "use: /xmms |prev|play|pause|stop|next\nsends the song XMMS is currently playing to the world prefixed by or performs the action specified if prev, play, pause, stop or next is specified") + $scripts.register("beep", "alias for xmms", "xmms") +end + +def getOgginfo(filename) + if (!File.exists?("/usr/bin/ogginfo")) + return nil + end + + info = `/usr/bin/ogginfo "#{filename}" 2> /dev/null` + artist = nil + title = nil + + if (!info.empty?) + info.split("\n").each {|line| + line = line.strip + if (line[/^artist=/i]) + artist = line[7..-1] + elsif (line[/^title=/i]) + title = line[6..-1] + end + } + + return "#{artist} - #{title}" + else + return nil + end +end + +def getID3info(filename) + if (!File.exists?("/usr/bin/id3info")) + nil + end + + info = `/usr/bin/id3info "#{filename}" 2> /dev/null`.split("\n") + artist = nil + title = nil + + if (info.length == 1 && info[0].empty? == 0) + return nil + end + + info.each {|line| + line = line.strip + if (line.index(/TPE[1|2]/) != nil) + artist = line[line.index(":") + 1..-1].strip + elsif (line.index("TIT2") != nil) + title = line[line.index(":") + 1..-1].strip + end + } + + if (title && artist) + return "#{artist} - #{title}" + else + return nil + end +end + +def getMP3info(filename) + if ((info = getID3info(filename))) + return info + end + + if (!File.exists?("/usr/bin/mp3info")) + return nil + end + + info = `/usr/bin/mp3info -p "%a - %t" "#{filename}" 2> /dev/null` + + if (!info.empty?) + return info + else + return nil + end +end + +def rhythmbox(argstr) + isopen = !(`/bin/ps -ae | /bin/grep rhythmbox`.empty?) + + if (!isopen) + $world.writeln("Script error: rhythmbox is not running!") + return + end + + if (argstr == "next") + `/usr/bin/rhythmbox --next` + elsif (argstr == "prev") + `/usr/bin/rhythmbox --previous` + elsif (argstr == "play" || argstr == "pause") + `/usr/bin/rhythmbox --play-pause` + else + path = `/usr/bin/rhythmbox --print-playing-path 2> /dev/null` + info = nil + + if (!path.empty?) + filename = path.split("\n")[0].gsub(/%20/, " ")[7..-1] + + if (filename[-3..-1] == "mp3" && (info = getMP3info(filename))) + elsif (filename[-3..-1] == "ogg" && (info = getOgginfo(filename))) + else + info = `/usr/bin/rhythmbox --print-playing 2> /dev/null` + + if (!info.empty?) + info = info.split("\n")[0] + else + info = nil + end + end + + if (info) + $world.sendln(argstr + " :luistert op dit moment naar [ #{info} ]") + else + $world.writeln("Script error: cannot retrieve rhythmbox file play") + end + + else + $world.writeln("Script error: can not execute /usr/bin/rhythmbox!") + end + end +end + +def xmms(argstr) + case argstr + when "prev", "play", "pause", "stop", "next" + io = IO.popen("/usr/bin/xmms-shell -e " + argstr + " 2>&1") + line = io.gets + io.close + else + line = `/usr/bin/xmms-shell -e current-track 2>&1` + end + + if (line[0..18] === "XMMS is not running") + $world.writeln("Script error: " + line[0..-2]) + elsif(line[0..3] === "sh: ") + $world.writeln("Script error: " + line[12..-2]) + elsif(line[0..12] === "Current song:") + index = line.index('.') + 2 + $world.sendln(argstr + " emote luistert op dit moment naar [ " + line[index..-2] + " ]") + else + line2 = `/usr/bin/xmms-shell -e current-track 2>&1` + index = line2.index('.') + 2 + $world.writeln(line[0..-2] + " [ " + line2[index..-2] + " ]") + end +end diff --git a/scripts/run.rb b/scripts/run.rb new file mode 100644 index 0000000..8a13846 --- /dev/null +++ b/scripts/run.rb @@ -0,0 +1,65 @@ +=begin + run.rb + Copyright (c) 2004 by Jesse van den Kieboom + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Defines: + run - open a world + crun - send all connected worlds the same text +=end + +def register_functions + $scripts.register("run", "use: /run \nruns in a subshell and sends output to the world") + $scripts.register("crun", "use: /crun \nsame as /run except it sends output to a channel") +end + +def run(argstr) + io = IO.popen(argstr) + + if (io) + $world.sendln(":LINUXes `" + argstr + "`") + + while ((l = io.gets)) + $world.sendln("emote | " + l[0..-2]) + end + + io.close + else + $world.writeln("Script error: can not execute #{argstr}!") + end +end + +def crun(argstr) + ar = argstr.split(" ", 2) + + if (ar.length != 2) + $world.writeln("Script error: use /crun ") + else + io = IO.popen(ar[1]) + + if (io) + $world.sendln(ar[0] + " :LINUXes `" + ar[1] + "`") + + while ((l = io.gets)) + $world.sendln(ar[0] + " :| " + l[0..-2]) + end + + io.close + else + $world.writeln("Script error: can not execute #{ar[1]}!") + end + end +end diff --git a/src/.swp b/src/.swp new file mode 100644 index 0000000000000000000000000000000000000000..626b93d36037b78cb56b0268e8027640a03e3af7 GIT binary patch literal 12288 zcmeI%y$*sf6o%o0ao6Y#pza1R@g5u;aj&t9anL|wyhks_&7VcTMpzjQ!S{rZHto=! zXM}CCS*_#cJTuiWyuS5H&Gx=1e7(>xy34^eY_o*`0__mU7g?IjrjvL)%G+@xZh-&- z2q1s}0tg_000KV~D36i7by`p9D^V2<6aok!fB*srAb08h;y%DrCiNUyYk00IagfB*srAb $@ + +gm-marshal.c: gm-marshal.list $(GLIB_GENMARSHAL) + echo "#include \"gm-marshal.h\"" > $@ && \ + $(GLIB_GENMARSHAL) $< --body --prefix=gm_marshal >> $@ + +gnoemoe_LDADD = @PACKAGE_LIBS@ $(INTLLIBS) @RUBYLINK@ + +CLEANFILES = $(BUILT_SOURCES) + +dist-hook: + cd $(distdir); rm -f $(BUILT_SOURCES) + +AM_CFLAGS = -Werror -Wall -Wsign-compare diff --git a/src/ansi.h b/src/ansi.h new file mode 100644 index 0000000..7426ad1 --- /dev/null +++ b/src/ansi.h @@ -0,0 +1,124 @@ +#ifndef ANSI_H +#define ANSI_H + +/** \defgroup ansi + * @{ + */ + +/** \brief enum indicating ansi code + * + * Enumeration which indicates the ansi code + */ +typedef enum _ansi_code { + A_DEFAULT = 0, /**< default (reset all attributes) */ + A_BOLD = 1, /**< bold text */ + A_FAINT = 2, /**< faint text */ + A_ITALIC = 3, /**< italic text */ + A_UNDERLINE = 4, /**< underlined text */ + A_INVERSE = 7, /**< inverse foreground/background colors */ + A_INVISIBLE = 8, /**< invisible text */ + A_CROSSOUT = 9, /**< crossed out text */ + A_DOUBLE_UNDERLINE = 21, /**< double underlined text */ + + A_BOLD_OFF = 22, /**< text no longer bold */ + A_ITALIC_OFF = 23, /**< text no longer italic */ + A_UNDERLINE_OFF = 24, /**< text no longer underlined */ + A_INVERSE_OFF = 27, /**< text no longer inversed */ + A_INVISIBLE_OFF = 28, /**< text no longer invisible */ + A_CROSSOUT_OFF = 29, /**< text no longer crossed out */ + + A_FG_BLACK = 30, /**< foreground color black */ + A_FG_RED = 31, /**< foreground color red */ + A_FG_GREEN = 32, /**< foreground color green */ + A_FG_YELLOW = 33, /**< foreground color yellow */ + A_FG_BLUE = 34, /**< foreground color blue */ + A_FG_PURPLE = 35, /**< foreground color purple */ + A_FG_CYAN = 36, /**< foreground color cyan */ + A_FG_WHITE = 37, /**< foreground color white */ + A_FG_DEFAULT = 39, /**< foreground color default */ + + A_BG_BLACK = 40, /**< background color black */ + A_BG_RED = 41, /**< background color red */ + A_BG_GREEN = 42, /**< background color green */ + A_BG_YELLOW = 43, /**< background color yellow */ + A_BG_BLUE = 44, /**< background color blue */ + A_BG_PURPLE = 45, /**< background color purple */ + A_BG_CYAN = 46, /**< background color cyan */ + A_BG_WHITE = 47, /**< background color white */ + A_BG_DEFAULT = 49, /**< background color default */ + + A_NOWRAP = 50, /**< do not wrap this text */ + + A_FG_BLACK_H, /**< foreground color black high */ + A_FG_RED_H, /**< foreground color red high */ + A_FG_GREEN_H, /**< foreground color green high */ + A_FG_YELLOW_H, /**< foreground color yellow high */ + A_FG_BLUE_H, /**< foreground color blue high */ + A_FG_PURPLE_H, /**< foreground color purple high */ + A_FG_CYAN_H, /**< foreground color cyan high */ + A_FG_WHITE_H, /**< foreground color white high */ + A_FG_DEFAULT_H /**< foreground color default high */ +} ansi_code; + +/** \brief struct for containing ansi-name pair + * + * Struct can be used to create a code to name to code mapping + */ +typedef struct _ansinamepair { + const ansi_code code; /**< the ansi code */ + const char *name; /**< the ansi name */ +} ansinamepair; + +/** \brief array containing color code/name mapping + * + * Array which can be used for color code/name mapping + */ +static const ansinamepair ansi_colors[] = { + {A_FG_BLACK, "fg_black"}, + {A_FG_RED, "fg_red"}, + {A_FG_GREEN, "fg_green"}, + {A_FG_YELLOW, "fg_yellow"}, + {A_FG_BLUE, "fg_blue"}, + {A_FG_PURPLE, "fg_purple"}, + {A_FG_CYAN, "fg_cyan"}, + {A_FG_WHITE, "fg_white"}, + {A_FG_DEFAULT, "fg_default"}, + {A_FG_BLACK_H, "fg_black_h"}, + {A_FG_RED_H, "fg_red_h"}, + {A_FG_GREEN_H, "fg_green_h"}, + {A_FG_YELLOW_H, "fg_yellow_h"}, + {A_FG_BLUE_H, "fg_blue_h"}, + {A_FG_PURPLE_H, "fg_purple_h"}, + {A_FG_CYAN_H, "fg_cyan_h"}, + {A_FG_WHITE_H, "fg_white_h"}, + {A_FG_DEFAULT_H, "fg_default_h"}, + {A_BG_BLACK, "bg_black"}, + {A_BG_RED, "bg_red"}, + {A_BG_GREEN, "bg_green"}, + {A_BG_YELLOW, "bg_yellow"}, + {A_BG_BLUE, "bg_blue"}, + {A_BG_PURPLE, "bg_purple"}, + {A_BG_CYAN, "bg_cyan"}, + {A_BG_WHITE, "bg_white"}, + {A_BG_DEFAULT, "bg_default"} +}; + +/** \brief array containing style code/name mapping + * + * Array which can be used for style code/name mapping + */ +static const ansinamepair ansi_styles[] = { + {A_BOLD, "bold"}, + {A_FAINT, "faint"}, + {A_BOLD_OFF, "bold-off"}, + {A_UNDERLINE, "underline"}, + {A_DOUBLE_UNDERLINE, "dblunderline"}, + {A_UNDERLINE_OFF, "underline-off"}, + {A_CROSSOUT, "crossout"}, + {A_CROSSOUT_OFF, "crossout-off"}, + {A_ITALIC, "italic"}, + {A_ITALIC_OFF, "italic-off"} +}; + +/** @} */ +#endif diff --git a/src/debug.c b/src/debug.c new file mode 100644 index 0000000..577d45b --- /dev/null +++ b/src/debug.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include + +int dbgLevel = 0; + +void +debug_msg(int level, char *line, ...) { + struct tm *timet; + time_t timer; + va_list args; + FILE *f; + + if (dbgLevel >= level) { + va_start(args, line); + timer = time(0); + timet = localtime(&timer); + + if (level == 0) { + f = stdout; + } else { + f = stderr; + } + + fprintf(f, "[%02d:%02d:%02d] # ", timet->tm_hour, timet->tm_min, + timet->tm_sec); + vfprintf(f, line, args); + printf("\n"); + va_end(args); + } +} + +void +debug_set_level(int level) { + dbgLevel = level; +} diff --git a/src/debug.h b/src/debug.h new file mode 100644 index 0000000..e59039a --- /dev/null +++ b/src/debug.h @@ -0,0 +1,6 @@ +#ifndef DEBUG_H +#define DEBUG_H 1 + +void debug_msg(int level, char *line, ...); +void debug_set_level(int level); +#endif diff --git a/src/gengtkclass.rb b/src/gengtkclass.rb new file mode 100755 index 0000000..2fceba5 --- /dev/null +++ b/src/gengtkclass.rb @@ -0,0 +1,58 @@ +#!/usr/bin/ruby + +def replaceAll(line, subst) + subst.each do |sub,rep| + line = line.gsub('{' + sub + '}', rep) + end + + return line +end + +def degenerateCaps(text) + text = text.gsub(/[A-Z]+[a-z]+/) do |s| + s.downcase + '_' + end + + return text[0..-2] +end + +subst = {} +name = ARGV[0] +parent = ARGV[1] + +if (name == nil) then + print "Class name (capitalized): " + name = gets[0..-2] +end + +subst['template_'] = degenerateCaps(name) +subst['template-'] = subst['template_'].sub('_', '-') +subst['TEMPLATE'] = subst['template_'].upcase +subst['Template'] = name + +if (parent == nil) then + print "Parent name (capitalized): " + parent = gets[0..-2] +end + +subst['parent_'] = degenerateCaps(parent) +subst['parent-'] = subst['parent_'].sub('_', '-') +subst['PARENT'] = subst['parent_'].upcase +subst['Parent'] = parent + +tc = IO.readlines("gtktemplate.c") +th = IO.readlines("gtktemplate.h") + +f = File.open('gm-' + subst['template-'] + '.c', 'w') +tc.each do |line| + f.write(replaceAll(line, subst)) +end +f.close + +f = File.open('gm-' + subst['template-'] + '.h', 'w') +th.each do |line| + f.write(replaceAll(line, subst)) +end +f.close + +print "Done ...\n" diff --git a/src/gm-app-view.c b/src/gm-app-view.c new file mode 100644 index 0000000..8ed787a --- /dev/null +++ b/src/gm-app-view.c @@ -0,0 +1,1417 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "gm-app-view.h" +#include "gm-ui.h" +#include "gm-world-view.h" +#include "gm-world.h" +#include "gm-world-info-dialog.h" +#include "gm-worlds-list-dialog.h" +#include "gm-world-logs-dialog.h" +#include "gm-world-properties-dialog.h" +#include "gm-scripts-dialog.h" +#include "gm-world-tab.h" + +//#include "if_world.h" +//#include "if_worlds_listing.h" +//#include "if_preferences.h" +//#include "if_mcpconsole.h" + +#include "gm-pixbuf.h" +#include "gm-support.h" +//#include "term.h" +//#include "world.h" +//#include "net.h" +//#include "main.h" + +void on_gm_app_view_destroy(GtkWidget * caller, gpointer user_data); +gboolean on_gm_app_view_focus_in(GtkWidget *widget, GdkEventFocus *event, + gpointer user_data); + +void on_gm_app_view_entry_find_activate(GtkEntry *entry, GmAppView *view); +void on_gm_app_view_entry_find_changed(GtkEditable *editable, + GmAppView *view); +gboolean on_gm_app_view_entry_find_key_press(GtkWidget *widget, + GdkEventKey *event, GmAppView *view); +void on_gm_app_view_button_find_close_clicked(GtkButton *button, + GmAppView *view); + +void on_gm_app_view_notebook_switch_page(GtkNotebook * notebook, + GtkNotebookPage * page, guint page_num, GmAppView *view); +gboolean on_gm_app_view_notebook_button_press(GtkNotebook *notebook, + GdkEventButton *event, GmAppView *view); +gboolean on_gm_app_view_notebook_button_release(GtkNotebook *notebook, + GdkEventButton *event, GmAppView *view); +void on_gm_app_view_check_button_search_direction_toggled( + GtkToggleButton *button, GmAppView *view); + +typedef struct _AccelInfo AccelInfo; +struct _AccelInfo { + int num; + GmAppView *view; +}; + +void on_gm_app_view_accel_switch_page(GtkAccelGroup * accelgroup, + GObject * arg1, guint arg2, GdkModifierType arg3, AccelInfo *info); +void on_gm_app_view_accel_switch_edit(GtkAccelGroup * accelgroup, + GObject * arg1, guint arg2, GdkModifierType arg3, AccelInfo *info); +void on_gm_app_view_accel_cycle_page(GtkAccelGroup * accelgroup, + GObject * arg1, guint arg2, GdkModifierType arg3, AccelInfo *info); + +void on_gm_app_view_world_added(GmApp *app, GmWorld *world, GmAppView *view); +void on_gm_app_view_world_removed(GmApp *app, GmWorld *world, GmAppView *view); +void on_gm_app_view_world_activate(GtkAction * action, GmWorld *world); +void on_gm_app_view_world_load(GmWorld *world, GmAppView *view); +void on_gm_app_view_world_unload(GmWorld *world, GmAppView *view); +void on_gm_app_view_world_activate_request(GmWorld *world, GmAppView *view); +void on_gm_app_view_world_name_changed(GmWorld *world, const gchar *name, + GmAppView *view); +void on_gm_app_view_world_state_changing(GmWorld *world, GmNetState state, + GmAppView *view); +void on_gm_app_view_world_active_changed(GmWorld *world, gboolean active, + GmAppView *view); + +#define GM_APP_VIEW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), \ + GM_TYPE_APP_VIEW, GmAppViewPrivate)) + +typedef struct _GmAppViewWorldMenuItem GmAppViewWorldMenuItem; +struct _GmAppViewWorldMenuItem { + guint merge_id; + GtkAction *action; +}; + +void gm_app_view_destroy_world_menu_item(GmAppViewWorldMenuItem *item); + +struct _GmAppViewPrivate { + GmApp *application; + GtkUIManager *manager; + GtkActionGroup *sensitive_action_group; + GtkActionGroup *action_group; + GtkActionGroup *worlds_action_group; + GHashTable *world_menu_items; + + GtkNotebook *notebook; + GmWorld *active_world; + GtkMenuBar *menu; + + GtkEntry *entry_find; + GtkVBox *vbox_find; + GtkCheckButton *check_button_search_direction; + + gboolean drag_in_progress; + gint motion_notify_handler_id; + gint x_start; + gint y_start; + GdkCursor *cursor; +}; + +/* Signals */ + +/*enum { + NUM_SIGNALS +}; + +static guint app_view_signals[NUM_SIGNALS] = {0};*/ + +G_DEFINE_TYPE(GmAppView, gm_app_view, GTK_TYPE_WINDOW) + +static void +gm_app_view_finalize(GObject *object) { + GmAppView *view = GM_APP_VIEW(object); + + gm_scripts_dialog_fini(); + + g_hash_table_destroy(view->priv->world_menu_items); + G_OBJECT_CLASS(gm_app_view_parent_class)->finalize(object); +} + + +static void +gm_app_view_class_init(GmAppViewClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gm_app_view_finalize; + + g_type_class_add_private(object_class, sizeof(GmAppViewPrivate)); +} + +GtkWidget * +gm_app_view_create_menu(GmAppView *view) { + GError *error = NULL; + GtkActionGroup *action_group; + + view->priv->manager = gtk_ui_manager_new(); + + gtk_window_add_accel_group(GTK_WINDOW(view), + gtk_ui_manager_get_accel_group(view->priv->manager)); + + gtk_ui_manager_add_ui_from_file(view->priv->manager, + PACKAGE_DATA_DIR "/" PACKAGE "/ui/gm-ui.xml", &error); + + if (error) { + debug_msg(1, "Could not merge UI file"); + g_error_free(error); + } + + action_group = gtk_action_group_new("GmAppViewSensitiveActions"); + gtk_action_group_set_translation_domain(action_group, + GETTEXT_PACKAGE); + gtk_action_group_add_actions(action_group, gm_sensitive_menu_entries, + G_N_ELEMENTS(gm_sensitive_menu_entries), view); + + gtk_ui_manager_insert_action_group(view->priv->manager, action_group, 0); + view->priv->sensitive_action_group = action_group; + + gtk_action_group_set_sensitive(action_group, TRUE); + + action_group = gtk_action_group_new("GmAppViewActions"); + gtk_action_group_set_translation_domain(action_group, + GETTEXT_PACKAGE); + gtk_action_group_add_actions(action_group, gm_menu_entries, + G_N_ELEMENTS(gm_menu_entries), view); + + gtk_ui_manager_insert_action_group(view->priv->manager, action_group, 0); + view->priv->action_group = action_group; + + gtk_action_group_set_sensitive(action_group, FALSE); + + view->priv->worlds_action_group = + gtk_action_group_new("GmAppViewWorldsActions"); + gtk_action_group_set_translation_domain(view->priv->worlds_action_group, + GETTEXT_PACKAGE); + gtk_ui_manager_insert_action_group(view->priv->manager, + view->priv->worlds_action_group, 0); + + #ifndef HAVE_RUBY + gtk_widget_set_sensitive(gtk_ui_manager_get_widget(view->priv->manager, + "/MenuBar/ViewMenu/ViewScriptsMenu"), FALSE); + #endif + + return gtk_ui_manager_get_widget(view->priv->manager, "/MenuBar"); +} + +GtkWidget * +gm_app_view_create_search_box(GmAppView *view) { + GtkWidget *vbox = gtk_vbox_new(FALSE, 3); + GtkWidget *hbox = gtk_hbox_new(FALSE, 6); + GtkWidget *lbl = gtk_label_new(_("Find:")); + GtkWidget *entry = gtk_entry_new(); + GtkWidget *button = gtk_button_new(); + GtkWidget *hbox_button = gtk_hbox_new(FALSE, 2); + GtkWidget *align = gtk_alignment_new(0.5, 0.5, 0.0, 0.0); + GtkWidget *check_button = + gtk_check_button_new_with_label(_("Search backwards")); + GtkWidget *button_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE); + GtkWidget *separator = gtk_hseparator_new(); + + gtk_container_set_border_width(GTK_CONTAINER(hbox), 3); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox), separator, FALSE, TRUE, 0); + + gtk_container_add(GTK_CONTAINER(button), align); + gtk_container_add(GTK_CONTAINER(align), hbox_button); + gtk_box_pack_start(GTK_BOX(hbox_button), gtk_image_new_from_stock( + GTK_STOCK_FIND, GTK_ICON_SIZE_BUTTON), FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_button), + gtk_label_new(_("Find next")), FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(hbox), lbl, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), check_button, FALSE, FALSE, 0); + gtk_box_pack_end(GTK_BOX(hbox), button_close, FALSE, FALSE, 0); + + view->priv->vbox_find = GTK_VBOX(vbox); + view->priv->entry_find = GTK_ENTRY(entry); + view->priv->check_button_search_direction = GTK_CHECK_BUTTON(check_button); + + gtk_widget_show_all(vbox); + gtk_widget_hide(vbox); + + g_signal_connect(entry, "activate", + G_CALLBACK(on_gm_app_view_entry_find_activate), view); + g_signal_connect(entry, "changed", + G_CALLBACK(on_gm_app_view_entry_find_changed), view); + g_signal_connect(entry, "key_press_event", + G_CALLBACK(on_gm_app_view_entry_find_key_press), view); + + g_signal_connect(button_close, "clicked", + G_CALLBACK(on_gm_app_view_button_find_close_clicked), view); + g_signal_connect(check_button, "toggled", + G_CALLBACK(on_gm_app_view_check_button_search_direction_toggled), + view); + + return vbox; +} + +void +gm_app_view_create_keybindings(GmAppView *view) { + GtkAccelGroup *grp; + GClosure *closure; + int i; + gchar num[2] = { 0, 0 }; + AccelInfo *info; + + grp = gtk_accel_group_new(); + + // Setting up Alt-1/Ctrl-1 -> Alt-9/Ctrl-9 accelerators + for (i = 0; i < 9; i++) { + info = g_new0(AccelInfo, 1); + info->num = i; + info->view = view; + + num[0] = '1' + i; + closure = g_cclosure_new(G_CALLBACK(on_gm_app_view_accel_switch_page), + info, NULL); + gtk_accel_group_connect(grp, gdk_keyval_from_name(num), GDK_MOD1_MASK, + GTK_ACCEL_VISIBLE, closure); + g_closure_unref(closure); + + closure = g_cclosure_new(G_CALLBACK(on_gm_app_view_accel_switch_edit), + info, NULL); + gtk_accel_group_connect(grp, gdk_keyval_from_name(num), + GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE, closure); + g_closure_unref(closure); + } + + info = g_new0(AccelInfo, 1); + info->num = 1; + info->view = view; + + // Next page + closure = g_cclosure_new(G_CALLBACK(on_gm_app_view_accel_cycle_page), + info, NULL); + gtk_accel_group_connect(grp, GDK_Page_Up, GDK_CONTROL_MASK, + GTK_ACCEL_VISIBLE, closure); + g_closure_unref(closure); + + info = g_new0(AccelInfo, 1); + info->num = -1; + info->view = view; + + // Previous page + closure = g_cclosure_new(G_CALLBACK(on_gm_app_view_accel_cycle_page), + info, NULL); + gtk_accel_group_connect(grp, GDK_Page_Down, GDK_CONTROL_MASK, + GTK_ACCEL_VISIBLE, closure); + g_closure_unref(closure); + + gtk_window_add_accel_group(GTK_WINDOW(view), grp); +} + + +static void +gm_app_view_init(GmAppView *view) { + GtkWidget *menu; + GtkWidget *search; + GtkWidget *vbox; + GtkWidget *note; + + view->priv = GM_APP_VIEW_GET_PRIVATE(view); + + vbox = gtk_vbox_new(FALSE, 3); + + gtk_widget_show(vbox); + gtk_container_add(GTK_CONTAINER(view), vbox); + + menu = gm_app_view_create_menu(view); + gtk_widget_show(menu); + gtk_box_pack_start(GTK_BOX(vbox), menu, FALSE, TRUE, 0); + + search = gm_app_view_create_search_box(view); + gtk_box_pack_start(GTK_BOX(vbox), search, FALSE, TRUE, 0); + + note = gtk_notebook_new(); + gtk_widget_show(note); + gtk_container_set_border_width(GTK_CONTAINER(note), 3); + gtk_notebook_set_show_border(GTK_NOTEBOOK(note), FALSE); + GTK_WIDGET_UNSET_FLAGS(note, GTK_CAN_FOCUS); + gtk_widget_add_events(GTK_WIDGET(note), GDK_BUTTON1_MOTION_MASK); + gtk_box_pack_start(GTK_BOX(vbox), note, TRUE, TRUE, 0); + gm_app_view_create_keybindings(view); + + view->priv->menu = GTK_MENU_BAR(menu); + view->priv->notebook = GTK_NOTEBOOK(note); + + gtk_window_set_title(GTK_WINDOW(view), "GnoeMoe"); + gtk_window_set_icon(GTK_WINDOW(view), gm_pixbuf_get("gnoemoe_logo.svg")); + + g_signal_connect(view, "destroy", G_CALLBACK(on_gm_app_view_destroy), NULL); + g_signal_connect(view, "focus_in_event", + G_CALLBACK(on_gm_app_view_focus_in), NULL); + + /* Signals for tab reordering */ + g_signal_connect_after(note, "switch-page", + G_CALLBACK(on_gm_app_view_notebook_switch_page), view); + g_signal_connect(note, "button-press-event", + G_CALLBACK(on_gm_app_view_notebook_button_press), view); + g_signal_connect(note, "button-release-event", + G_CALLBACK(on_gm_app_view_notebook_button_release), view); + + view->priv->world_menu_items = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, + (GDestroyNotify)gm_app_view_destroy_world_menu_item); + + view->priv->drag_in_progress = FALSE; + view->priv->motion_notify_handler_id = 0; + view->priv->x_start = 0; + view->priv->y_start = 0; + view->priv->cursor = NULL; + + gm_scripts_dialog_init(); +} + + +// Public functions +GmAppView * +gm_app_view_new(GmApp *application) { + GmAppView *view = GM_APP_VIEW(g_object_new(GM_TYPE_APP_VIEW, NULL)); + gboolean dir; + + view->priv->application = application; + view->priv->active_world = NULL; + + // Toggle search direction + dir = gm_options_get_int(gm_app_options(application), "search_direction") + == 1; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON( + view->priv->check_button_search_direction), dir); + + g_signal_connect(application, "world_added", + G_CALLBACK(on_gm_app_view_world_added), view); + g_signal_connect(application, "world_removed", + G_CALLBACK(on_gm_app_view_world_removed), view); + + return view; +} + +void +notebook_focus_with_label(GtkNotebook * note, gchar * caption) { + int p = gtk_notebook_get_n_pages(note); + int i; + GtkWidget *child; + + for (i = 0; i < p; i++) { + child = gtk_notebook_get_nth_page(note, i); + if (!g_strcasecmp(gtk_notebook_get_tab_label_text(note, child), caption)) { + gtk_notebook_set_current_page(note, i); + break; + } + } +} + +void +gm_app_view_destroy_world_menu_item(GmAppViewWorldMenuItem *item) { + g_object_unref(item->action); + g_free(item); +} + +void +on_gm_app_view_world_removed(GmApp *app, GmWorld *world, GmAppView *view) { + GmAppViewWorldMenuItem *item = (GmAppViewWorldMenuItem *) + g_hash_table_lookup(view->priv->world_menu_items, world); + + gtk_ui_manager_remove_ui(view->priv->manager, item->merge_id); + gtk_action_group_remove_action(view->priv->worlds_action_group, + item->action); + + g_hash_table_remove(view->priv->world_menu_items, world); +} + +void +gm_app_view_set_sensitivity(GmAppView *view, gboolean sens) { + gtk_action_group_set_sensitive(view->priv->action_group, sens); + + if (sens) { + /* TODO: set world info menu item not sensitive if no world info avail + First fix MCP + gtk_widget_set_sensitive(if_main_get_widget("mnuWorldInfo"), sens); */ + } else { + gtk_widget_hide(GTK_WIDGET(view->priv->vbox_find)); + } +} + +GmWorldView * +gm_app_view_active_world_view(GmAppView *view) { + gint n; + + n = gtk_notebook_get_current_page(view->priv->notebook); + + if (n == -1) { + return NULL; + } + + return GM_WORLD_VIEW(gtk_notebook_get_nth_page(view->priv->notebook, n)); +} + +GmWorld * +gm_app_view_active_world(GmAppView *view) { + GmWorldView *world_view = gm_app_view_active_world_view(view); + + if (world_view != NULL) { + return gm_world_view_world(world_view); + } else { + return NULL; + } +} + +void +gm_app_view_update_title(GmAppView *view) { + gchar *title; + GmWorld *world = gm_app_view_active_world(view); + + if (world == NULL) { + gtk_window_set_title(GTK_WINDOW(view), "GnoeMoe"); + } else { + title = g_strconcat(gm_world_name(world), " - GnoeMoe", NULL); + gtk_window_set_title(GTK_WINDOW(view), title); + g_free(title); + } +} + +GtkWidget * +gm_app_view_container_item(GtkContainer *cnt, GType type) { + GList *childFirst = gtk_container_get_children(cnt); + GList *child; + GtkWidget *result = NULL; + + for (child = childFirst; child; child = child->next) { + if (G_TYPE_CHECK_INSTANCE_TYPE(child->data, type)) { + result = GTK_WIDGET(child->data); + break; + } else if (GTK_IS_CONTAINER(child->data)) { + if ((result = gm_app_view_container_item( + GTK_CONTAINER(child->data), type))) { + break; + } + } + } + + g_list_free(childFirst); + return result; +} + +void +gm_app_view_update_connect_button(GmAppView *view, gboolean connected) { + GtkImageMenuItem *img = GTK_IMAGE_MENU_ITEM( + gtk_ui_manager_get_widget(view->priv->manager, + "/MenuBar/WorldMenu/WorldConnectMenu")); + GtkLabel *label = GTK_LABEL(gm_app_view_container_item(GTK_CONTAINER(img), + GTK_TYPE_LABEL)); + GtkWidget *im; + + if (!connected) { + im = gtk_image_new_from_stock("gtk-network", GTK_ICON_SIZE_MENU); + gtk_label_set_text(label, _("Connect")); + } else { + im = gtk_image_new_from_stock("gtk-stop", GTK_ICON_SIZE_MENU); + gtk_label_set_text(label, _("Disconnect")); + } + + gtk_image_menu_item_set_image(img, im); + gtk_widget_show(im); +} + +void +gm_app_view_worlds_unloaded(GmAppView *view) { + gm_app_view_set_sensitivity(view, FALSE); + gm_app_view_update_connect_button(view, FALSE); + gm_app_view_update_title(view); +} + +void +gm_app_view_worlds_loaded(GmAppView *view) { + gm_app_view_set_sensitivity(view, TRUE); +} + +/* TODO: move to custom widget thingie, and put control in gm-app instead + of gm-app-view +static gboolean +have_tray(void) { + Screen *xscreen = DefaultScreenOfDisplay(gdk_display); + Atom selection_atom; + char *selection_atom_name; + + selection_atom_name = g_strdup_printf("_NET_SYSTEM_TRAY_S%d", + XScreenNumberOfScreen(xscreen)); + selection_atom = XInternAtom(DisplayOfScreen(xscreen), + selection_atom_name, False); + g_free(selection_atom_name); + + if (XGetSelectionOwner(DisplayOfScreen(xscreen), selection_atom)) { + return TRUE; + } else { + return FALSE; + } +} + +static gboolean +tray_flash_restore_func(gpointer data) { + switch (app_tray_info.iconnr) { + case TRAY_ICON_ACTIVE: + gtk_image_set_from_pixbuf(GTK_IMAGE(app_tray_info.image), + gnoe_pixbuf_get("tray/active.svg")); + break; + case TRAY_ICON_DEFAULT: + gtk_image_set_from_pixbuf(GTK_IMAGE(app_tray_info.image), + gnoe_pixbuf_get("tray/default.svg")); + break; + case TRAY_ICON_NOTIFY: + gtk_image_set_from_pixbuf(GTK_IMAGE(app_tray_info.image), + gnoe_pixbuf_get("tray/notify.svg")); + break; + } + + app_tray_info.flash_timeout = -1; + return FALSE; +} + +void +tray_activate() { + if (app_tray_info.flash_timeout != 0) { + g_source_remove(app_tray_info.flash_timeout); + } + + gtk_image_set_from_pixbuf(GTK_IMAGE(app_tray_info.image), + gnoe_pixbuf_get("tray/activate.svg")); + app_tray_info.flash_timeout = g_timeout_add(1000, tray_flash_restore_func, NULL); +} + +void +tray_set_notify(gchar *text) { + gboolean active = gtk_window_is_active(GTK_WINDOW(wndMain)); + + if (!active) { + app_tray_info.iconnr = TRAY_ICON_NOTIFY; + tray_flash_restore_func(NULL); + + egg_tray_icon_send_message(app_tray_info.icon, 2000, text, g_utf8_strlen(text, -1)); + gtk_tooltips_set_tip(app_tray_info.tooltips, app_tray_info.event_box, text, text); + } +} + +void +tray_update() { + GList *wlds; + world *wld; + gchar *tmp = NULL, *tmp2 = NULL; + gboolean active = gtk_window_is_active(GTK_WINDOW(wndMain)); + + if (!active) { + for (wlds = world_get_worlds(); wlds; wlds = wlds->next) { + wld = (world *)(wlds->data); + if (wld->loaded && wld->inactive > 0) { + if (tmp == NULL) { + tmp = g_strdup_printf(_("Activity in:\n%s (%d)"), options_get_str( + wld->settings, "name"), wld->inactive); + } else { + tmp2 = g_strdup_printf("%s, %s (%d)", tmp, options_get_str( + wld->settings, "name"), wld->inactive); + g_free(tmp); + tmp = g_strdup(tmp2); + g_free(tmp2); + tmp2 = NULL; + } + } + } + + if (tmp) { + app_tray_info.iconnr = TRAY_ICON_ACTIVE; + tray_activate(); + } else if (app_tray_info.iconnr != TRAY_ICON_NOTIFY) { + app_tray_info.iconnr = TRAY_ICON_DEFAULT; + tray_flash_restore_func(NULL); + } + + if (app_tray_info.iconnr != TRAY_ICON_NOTIFY) { + gtk_tooltips_set_tip(app_tray_info.tooltips, app_tray_info.event_box, tmp, tmp); + } + + g_free(tmp); + } else { + app_tray_info.iconnr = TRAY_ICON_DEFAULT; + tray_flash_restore_func(NULL); + gtk_tooltips_set_tip(app_tray_info.tooltips, app_tray_info.event_box, NULL, NULL); + } +} + +void +if_main_show_hide(gboolean show) { + world *wld; + + if (show || !gtk_window_is_active(GTK_WINDOW(wndMain))) { + gtk_widget_show(wndMain); + gtk_window_present(GTK_WINDOW(wndMain)); + wld = world_get_active(); + + if (wld) { + wld->inactive = 0; + } + + tray_update(); + } else { + gtk_widget_hide(wndMain); + } +}*/ + +void +gm_app_view_update_world_info(gboolean hasInfo) { + /* TODO: do this some other way */ + /*GtkWidget *image; + world *wld = world_get_active(); + gtk_widget_set_sensitive(if_main_get_widget("mnuWorldInfo"), hasInfo); + + if (options_get_str(wld->settings, "logo")) { + image = gtk_image_new_from_pixbuf(gnoe_pixbuf_get_at_size(options_get_str(wld->settings, "logo"), 16, 16)); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(wld->widgets.menuItem), image); + }*/ +} + +GmWorldView * +gm_app_view_world_view_from_world(GmAppView *view, GmWorld *world) { + GtkNotebook *book = view->priv->notebook; + GmWorldView *current_view; + gint i; + + for (i = 0; i < gtk_notebook_get_n_pages(book); i++) { + current_view = GM_WORLD_VIEW(gtk_notebook_get_nth_page(book, i)); + if (gm_world_view_world(current_view) == world) { + return current_view; + } + } + + return NULL; +} + +/* Callbacks */ + +void +on_gm_app_view_destroy(GtkWidget * caller, gpointer user_data) { + gtk_main_quit(); +} + +void +on_gm_app_view_world_close(GtkMenuItem * menuitem, GmAppView *view) { + GmWorld *world = gm_app_view_active_world(view); + + if (world) { + gm_world_unload(world); + } +} + +void +on_gm_app_view_world_close_from_tab(GmWorldTab *tab, GmWorld *world) { + gm_world_unload(world); +} + +void +on_gm_app_view_world_name_changed(GmWorld *world, const gchar *name, + GmAppView *view) { + GmAppViewWorldMenuItem *item = (GmAppViewWorldMenuItem *) + g_hash_table_lookup(view->priv->world_menu_items, world); + g_object_set(G_OBJECT(item->action), "label", name, NULL); + + if (world == gm_app_view_active_world(view)) { + gm_app_view_update_title(view); + } +} + +void +on_gm_app_view_world_added(GmApp *app, GmWorld *world, GmAppView *view) { + static guint id = 0; + GmAppViewWorldMenuItem *item = g_new0(GmAppViewWorldMenuItem, 1); + gchar *name = g_strdup_printf("WorldItem%d", id); + gchar *tooltip = g_strconcat(_("Open world "), gm_world_name(world), NULL); + + id++; + + /* TODO: add custom icon from logo */ + item->merge_id = gtk_ui_manager_new_merge_id(view->priv->manager); + item->action = gtk_action_new(name, gm_world_name(world), tooltip, NULL); + + gtk_action_group_add_action(view->priv->worlds_action_group, item->action); + gtk_ui_manager_add_ui(view->priv->manager, item->merge_id, + "/MenuBar/WorldMenu/WorldMenuAdditions", name, name, + GTK_UI_MANAGER_MENUITEM, FALSE); + + g_signal_connect(item->action, "activate", + G_CALLBACK(on_gm_app_view_world_activate), world); + g_signal_connect(world, "load", + G_CALLBACK(on_gm_app_view_world_load), view); + g_signal_connect(world, "unload", + G_CALLBACK(on_gm_app_view_world_unload), view); + g_signal_connect(world, "activate_request", + G_CALLBACK(on_gm_app_view_world_activate_request), view); + g_signal_connect(world, "name_changed", + G_CALLBACK(on_gm_app_view_world_name_changed), view); + g_signal_connect(world, "state_changing", + G_CALLBACK(on_gm_app_view_world_state_changing), view); + g_signal_connect(world, "active_changed", + G_CALLBACK(on_gm_app_view_world_active_changed), view); + + g_hash_table_insert(view->priv->world_menu_items, world, item); + g_free(name); + g_free(tooltip); +} + +void +on_gm_app_view_world_state_changing(GmWorld *world, GmNetState state, + GmAppView *view) { + if (world == gm_app_view_active_world(view)) { + switch (state) { + case GM_NET_STATE_CONNECTED: + gm_app_view_update_connect_button(view, TRUE); + break; + case GM_NET_STATE_DISCONNECTED: + gm_app_view_update_connect_button(view, FALSE); + break; + default: + break; + } + } +} + +void +on_gm_app_view_world_load(GmWorld *world, GmAppView *view) { + GtkWidget *world_view = gm_world_view_new(world); + GmWorldTab *tab = gm_world_tab_new(world); + + g_signal_connect(tab, "close", + G_CALLBACK(on_gm_app_view_world_close_from_tab), world); + + gtk_notebook_append_page(view->priv->notebook, world_view, + GTK_WIDGET(tab)); + gtk_widget_show_all(world_view); + + gm_world_view_set_userlist_width(GM_WORLD_VIEW(world_view), + gm_options_get_int(gm_world_options(world), "pane_position")); + + if (gtk_notebook_get_n_pages(view->priv->notebook) == 1) { + gm_app_view_worlds_loaded(view); + } +} + +void +on_gm_app_view_world_unload(GmWorld *world, GmAppView *view) { + GmWorldView *world_view = gm_app_view_world_view_from_world(view, world); + + gtk_notebook_remove_page(view->priv->notebook, + gtk_notebook_page_num(view->priv->notebook, GTK_WIDGET(world_view))); + + if (gtk_notebook_get_n_pages(view->priv->notebook) == 0) { + gm_app_view_worlds_unloaded(view); + } +} + +void +on_gm_app_view_world_activate_request(GmWorld *world, GmAppView *view) { + gm_world_set_active(world, TRUE); +} + +void +on_gm_app_view_world_quit(GtkMenuItem * menuitem, GmAppView *view) { + gtk_widget_destroy(GTK_WIDGET(view)); +} + +void +on_gm_app_view_edit_worlds(GtkMenuItem * menuitem, GmAppView *view) { + gm_worlds_list_dialog_run(); +} + +void +on_gm_app_view_accel_switch_page(GtkAccelGroup * accelgroup, GObject * arg1, + guint arg2, GdkModifierType arg3, AccelInfo *info) { + + gtk_notebook_set_current_page(info->view->priv->notebook, info->num); +} + +void +on_gm_app_view_accel_switch_edit(GtkAccelGroup * accelgroup, GObject * arg1, + guint arg2, GdkModifierType arg3, AccelInfo *info) { + GmWorld *world = gm_app_view_active_world(info->view); + + if (world) { + gtk_notebook_set_current_page(info->view->priv->notebook, info->num); + } +} + +void +on_gm_app_view_accel_cycle_page(GtkAccelGroup * accelgroup, GObject * arg1, + guint arg2, GdkModifierType arg3, AccelInfo *info) { + GtkNotebook *note = info->view->priv->notebook; + int p = gtk_notebook_get_current_page(note) + info->num; + + if (p < 0) { + gtk_notebook_set_current_page(note, gtk_notebook_get_n_pages(note) - 1); + } else if (p > gtk_notebook_get_n_pages(note) - 1) { + gtk_notebook_set_current_page(note, 0); + } else { + gtk_notebook_set_current_page(note, p); + } +} + +void +on_gm_app_view_world_activate(GtkAction * action, GmWorld *world) { + gm_world_load(world); +} + +GmWorldViewSearchFlags +gm_app_view_search_flags(GmAppView *view) { + gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( + view->priv->check_button_search_direction)); + + if (active) { + return GM_WORLD_VIEW_SEARCH_BACKWARDS; + } else { + return GM_WORLD_VIEW_SEARCH_FORWARDS; + } +} + +gboolean +gm_app_view_find_first(GmAppView *view) { + GmWorldView *world_view = gm_app_view_active_world_view(view); + GdkColor red; + GtkEntry *entry = view->priv->entry_find; + + if (world_view) { + gdk_color_parse ("#ff6666", &red); + //gdk_color_parse ("white", &white); + + if (g_utf8_strlen(gtk_entry_get_text(entry), -1) == 0 || + gm_world_view_find_first(world_view, gtk_entry_get_text(entry), + gm_app_view_search_flags(view))) { + gtk_widget_modify_base(GTK_WIDGET(entry), GTK_STATE_NORMAL, NULL); + } else { + gtk_widget_modify_base(GTK_WIDGET(entry), GTK_STATE_NORMAL, &red); + } + } + + return FALSE; +} + +void +on_gm_app_view_entry_find_changed(GtkEditable *editable, GmAppView *view) { + g_idle_add((GSourceFunc)gm_app_view_find_first, view); +} + +void +on_gm_app_view_entry_find_activate(GtkEntry *entry, GmAppView *view) { + on_gm_app_view_edit_find_next(NULL, view); +} + +void +gm_app_view_show_find_box(GmAppView *view) { + GmWorldView *world_view; + int i, n; + + if (!GTK_WIDGET_VISIBLE(view->priv->vbox_find)) { + n = gtk_notebook_get_n_pages(view->priv->notebook); + + for (i = 0; i < gtk_notebook_get_n_pages(view->priv->notebook); i++) { + world_view = GM_WORLD_VIEW(gtk_notebook_get_nth_page( + view->priv->notebook, i)); + + gm_world_view_scroll_end_prepare(world_view); + } + + gtk_widget_show(GTK_WIDGET(view->priv->vbox_find)); + gm_do_events(); + + for (i = 0; i < gtk_notebook_get_n_pages(view->priv->notebook); i++) { + world_view = GM_WORLD_VIEW(gtk_notebook_get_nth_page( + view->priv->notebook, i)); + + gm_world_view_scroll_end(world_view); + } + } +} + +void +on_gm_app_view_edit_find(GtkMenuItem * menuitem, GmAppView *view) { + GtkWidget *entry = GTK_WIDGET(view->priv->entry_find); + + gm_app_view_show_find_box(view); + gtk_widget_grab_focus(entry); + gtk_widget_modify_base(entry, GTK_STATE_NORMAL, NULL); +} + +void +on_gm_app_view_edit_find_next(GtkMenuItem * menuitem, GmAppView *view) { + gm_app_view_show_find_box(view); + + gm_world_view_find_next(gm_app_view_active_world_view(view), + gtk_entry_get_text(view->priv->entry_find), + gm_app_view_search_flags(view)); +} + +void +on_gm_app_view_button_find_clicked(GtkButton *button, gpointer user_data) { + on_gm_app_view_edit_find_next(NULL, NULL); +} + +gboolean +on_gm_app_view_entry_find_key_press(GtkWidget *widget, GdkEventKey *event, + GmAppView *view) { + if (event->keyval == GDK_Escape) { + on_gm_app_view_button_find_close_clicked(NULL, view); + gtk_widget_grab_focus(GTK_WIDGET(gm_app_view_active_world_view(view))); + return TRUE; + } else { + return FALSE; + } +} + +void +on_gm_app_view_button_find_close_clicked(GtkButton *button, + GmAppView *view) { + GmWorldView *world_view = gm_app_view_active_world_view(view); + + gtk_widget_hide(GTK_WIDGET(view->priv->vbox_find)); + + if (world_view) { + gm_world_view_set_focus(world_view); + } +} + +void +on_gm_app_view_check_button_search_direction_toggled(GtkToggleButton *button, + GmAppView *view) { + gboolean active = gtk_toggle_button_get_active(button); + + gm_options_set_int(gm_app_options(view->priv->application), + "search_direction", active ? 1 : 0); +} + +void +on_gm_app_view_edit_preferences(GtkMenuItem * menuitem, GmAppView *view) { + /* TODO: implementation */ + //create_preferences(main_get_options()); +} + +/* Tab moving from gedit */ + +gint +gm_app_view_find_tab_num_at_pos(GmAppView *view, gint abs_x, + gint abs_y) { + GtkNotebook *notebook = view->priv->notebook; + GtkPositionType tab_pos; + int page_num = 0; + GtkWidget *page; + + tab_pos = gtk_notebook_get_tab_pos(notebook); + + if (notebook->first_tab == NULL) { + return -1; + } + + /* For some reason unfullscreen + quick click can + cause a wrong click event to be reported to the tab */ + /*if (!is_in_notebook_window(notebook, abs_x, abs_y)) { + return -1; + }*/ + + while ((page = gtk_notebook_get_nth_page(notebook, page_num)) != NULL) { + GtkWidget *tab; + gint max_x, max_y; + gint x_root, y_root; + + tab = gtk_notebook_get_tab_label(notebook, page); + g_return_val_if_fail(tab != NULL, -1); + + if (!GTK_WIDGET_MAPPED(GTK_WIDGET(tab))) { + ++page_num; + continue; + } + + gdk_window_get_origin(GDK_WINDOW(tab->window), &x_root, &y_root); + max_x = x_root + tab->allocation.x + tab->allocation.width; + max_y = y_root + tab->allocation.y + tab->allocation.height; + + if (((tab_pos == GTK_POS_TOP) || (tab_pos == GTK_POS_BOTTOM)) && + (abs_x <= max_x)) { + return page_num; + } else if (((tab_pos == GTK_POS_LEFT) || (tab_pos == GTK_POS_RIGHT)) && + (abs_y <= max_y)) { + return page_num; + } + + ++page_num; + } + + return -1; +} + +void +gm_app_view_drag_stop(GmAppView *view) { + view->priv->drag_in_progress = FALSE; + + if (view->priv->motion_notify_handler_id != 0) { + g_signal_handler_disconnect(G_OBJECT(view->priv->notebook), + view->priv->motion_notify_handler_id); + + view->priv->motion_notify_handler_id = 0; + } +} + +void +gm_app_view_drag_start(GmAppView *view, guint32 time) { + view->priv->drag_in_progress = TRUE; + + /* get a new cursor, if necessary */ + /* FIXME multi-head */ + if (view->priv->cursor == NULL) + view->priv->cursor = gdk_cursor_new(GDK_FLEUR); + + /* grab the pointer */ + gtk_grab_add(GTK_WIDGET(view->priv->notebook)); + + /* FIXME multi-head */ + if (!gdk_pointer_is_grabbed()) { + gdk_pointer_grab(GTK_WIDGET(view->priv->notebook)->window, + FALSE, GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, + view->priv->cursor, time); + } +} + +void +gm_app_view_move_current_tab(GmAppView *view, gint dest_position) { + gint cur_page_num; + cur_page_num = gtk_notebook_get_current_page(view->priv->notebook); + + if (dest_position != cur_page_num) { + GtkWidget *cur_tab; + + cur_tab = gtk_notebook_get_nth_page(view->priv->notebook, cur_page_num); + gtk_notebook_reorder_child(view->priv->notebook, cur_tab, dest_position); + } +} + +gboolean +on_gm_app_view_motion_notify(GmAppView *view, GdkEventMotion *event, + gpointer data) { + gint page_num; + + if (view->priv->drag_in_progress == FALSE) { + if (gtk_drag_check_threshold(GTK_WIDGET(view->priv->notebook), + view->priv->x_start, + view->priv->y_start, + event->x_root, + event->y_root)) { + gm_app_view_drag_start(view, event->time); + return TRUE; + } + + return FALSE; + } + + page_num = gm_app_view_find_tab_num_at_pos(view, event->x_root, + event->y_root); + + if (page_num != -1) { + gm_app_view_move_current_tab(view, page_num); + } + + return FALSE; +} + +gboolean +on_gm_app_view_notebook_button_press(GtkNotebook *notebook, + GdkEventButton *event, GmAppView *view) { + gint tab_clicked; + + if (view->priv->drag_in_progress) + return TRUE; + + tab_clicked = gm_app_view_find_tab_num_at_pos(view, event->x_root, + event->y_root); + + if ((event->button == 1) && (event->type == GDK_BUTTON_PRESS) && + (tab_clicked >= 0)) { + view->priv->x_start = event->x_root; + view->priv->y_start = event->y_root; + + view->priv->motion_notify_handler_id = g_signal_connect(G_OBJECT(notebook), + "motion-notify-event", G_CALLBACK(on_gm_app_view_motion_notify), NULL); + } else if ((event->type == GDK_BUTTON_PRESS) && (event->button == 3)) { + /* Switch to the page the mouse is over, but don't consume the event */ + //gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), tab_clicked); + } + + return FALSE; +} + +gboolean +on_gm_app_view_notebook_button_release(GtkNotebook *notebook, + GdkEventButton *event, GmAppView *view) { + if (view->priv->drag_in_progress) { + gint cur_page_num; + GtkWidget *cur_page; + + cur_page_num = gtk_notebook_get_current_page(notebook); + cur_page = gtk_notebook_get_nth_page(notebook, cur_page_num); + + /* ungrab the pointer if it's grabbed */ + if (gdk_pointer_is_grabbed()) { + gdk_pointer_ungrab(event->time); + } + + gtk_grab_remove(GTK_WIDGET(notebook)); + } + + /* This must be called even if a drag isn't happening */ + gm_app_view_drag_stop(view); + + return FALSE; +} + +void +on_gm_app_view_notebook_switch_page(GtkNotebook * notebook, + GtkNotebookPage * page, guint page_num, GmAppView *view) { + GmWorld *world = gm_app_view_active_world(view); + + debug_msg(0, "%d", page_num); + + if (view->priv->active_world == world) { + return; + } + + if (view->priv->active_world) { + gm_world_set_active(view->priv->active_world, FALSE); + } + + if (world) { + view->priv->active_world = world; + gm_world_set_active(view->priv->active_world, TRUE); + } +} + +static const gchar *authors[] = { + N_("Jesse van den Kieboom"), + N_("Sjoerd Simons (debian package)"), + NULL +}; + +static const gchar *artists[] = { + N_("Simon Gijsen"), + NULL +}; + +void +on_gm_app_view_help_about(GtkMenuItem *menuitem, GmAppView *view) { + gtk_show_about_dialog(GTK_WINDOW(view), + "name", _("GnoeMoe"), + "version", IVERSION, + "copyright", _("(C) 2004-2005 Icecrew.nl"), + "comments", _("GnoeMoe Gnome MOO Client"), + "authors", authors, + "artists", artists, + "logo", gm_pixbuf_get("gnoemoe_logo.svg"), + NULL); +} + +void +on_gm_app_view_edit_cut(GtkMenuItem * menuitem, GmAppView *view) { + GtkWidget *active = gtk_window_get_focus(GTK_WINDOW(view)); + GtkClipboard *clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + GtkTextBuffer *buf; + + if (active && GTK_IS_TEXT_VIEW(active)) { + buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(active)); + gtk_text_buffer_cut_clipboard(buf, clip, TRUE); + } +} + +void +on_gm_app_view_edit_copy(GtkMenuItem * menuitem, GmAppView *view) { + GtkWidget *active = gtk_window_get_focus(GTK_WINDOW(view)); + GtkClipboard *clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + GtkTextBuffer *buf; + GtkTextIter start, end; + GmWorldView *world_view; + + world_view = gm_app_view_active_world_view(view); + + if (world_view && active == GTK_WIDGET(gm_world_view_input(world_view))) { + buf = gm_world_view_buffer(world_view); + if (gtk_text_buffer_get_selection_bounds(buf, &start, &end)) { + gtk_text_buffer_copy_clipboard(buf, clip); + return; + } + } + + if (active && GTK_IS_TEXT_VIEW(active)) { + buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(active)); + gtk_text_buffer_copy_clipboard(buf, clip); + } +} + +void +on_gm_app_view_edit_paste(GtkMenuItem * menuitem, GmAppView *view) { + GtkWidget *active = gtk_window_get_focus(GTK_WINDOW(view)); + GtkClipboard *clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + GtkTextBuffer *buf; + + if (active && GTK_IS_TEXT_VIEW(active)) { + buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(active)); + gtk_text_buffer_paste_clipboard(buf, clip, NULL, TRUE); + } +} + +void +on_gm_app_view_world_new(GtkMenuItem * menuitem, GmAppView *view) { + gm_world_properties_dialog_run_new(NULL); +} + +void +on_gm_app_view_edit_world(GtkMenuItem * menuitem, GmAppView *view) { + GmWorld *active = gm_app_view_active_world(view); + + gm_world_properties_dialog_run(active); +} + +void +on_gm_app_view_world_connect(GtkMenuItem * menuitem, GmAppView *view) { + GmWorld *world = gm_app_view_active_world(view); + + if (world != NULL) { + if (gm_world_connected(world)) { + gm_world_disconnect(world); + } else if (gm_world_disconnected(world)) { + gm_world_connect(world); + } + } +} + +void +on_gm_app_view_world_info(GtkMenuItem *menuitem, GmAppView *view) { + GmWorld *world = gm_app_view_active_world(view); + + if (world) { + gm_world_info_dialog_new(gm_world_info(world)); + } else { + gm_app_view_update_world_info(FALSE); + } +} + + +void +on_gm_app_view_view_mcp(GtkMenuItem * menuitem, GmAppView *view) { + /* TODO: implementation */ + //if_mcpconsole_create(); +} + +void +on_gm_app_view_view_scripts(GtkMenuItem * menuitem, GmAppView *view) { +#ifdef HAVE_RUBY + gm_scripts_dialog_run(view); +#endif +} + +void +gm_app_view_open_log_progress(long bytes_read, long bytes_total, gchar *buf, + GtkProgressBar *progress) { + gtk_progress_bar_set_fraction(progress, (double)bytes_read / (double)bytes_total); + gm_do_events(); +} + +void +on_gm_app_view_world_logs(GtkMenuItem * menuitem, GmAppView *view) { + GtkDialog *dlg; + GtkTreeView *tview; + GtkTreeModel *smodel; + gchar *tmp, *tmp2; + GtkTreeIter iter; + gboolean done = FALSE; + static GdkCursor *wait_cursor = NULL; + GmWorld *world = gm_app_view_active_world(view); + GtkProgressBar *pgs; + + dlg = gm_world_logs_dialog_new(gm_app_view_active_world(view), &tview, &pgs); + + if (dlg != NULL) { + smodel = gtk_tree_view_get_model(tview); + + while (!done) { + done = TRUE; + switch (gtk_dialog_run(dlg)) { + case GTK_RESPONSE_OK: + if (gtk_tree_selection_get_selected( + gtk_tree_view_get_selection(tview), &smodel, &iter)) { + gtk_tree_model_get(smodel, &iter, 0, &tmp, -1); + tmp2 = g_strconcat(gm_world_path(world), tmp, NULL); + gtk_widget_set_sensitive(GTK_WIDGET(dlg), FALSE); + + if (wait_cursor == NULL) { + wait_cursor = gdk_cursor_new(GDK_WATCH); + } + + gdk_window_set_cursor(GTK_WIDGET(dlg)->window, wait_cursor); + gtk_widget_show(GTK_WIDGET(pgs)); + gm_world_view_open_log(gm_app_view_active_world_view(view), tmp2, + (OpenLogProgress)gm_app_view_open_log_progress, + pgs); + + gtk_widget_hide(GTK_WIDGET(pgs)); + + gdk_window_set_cursor(GTK_WIDGET(dlg)->window, NULL); + g_free(tmp2); + g_free(tmp); + } else { + gm_error_dialog(_("You didn't select a log file"), NULL); + done = FALSE; + } + break; + default: + break; + } + } + + gtk_widget_destroy(GTK_WIDGET(dlg)); + } +} + +void +on_gm_app_view_world_active_changed(GmWorld *world, gboolean active, + GmAppView *view) { + GmWorldView *world_view = gm_app_view_world_view_from_world(view, world); + + if (active) { + gtk_notebook_set_current_page(view->priv->notebook, + gtk_notebook_page_num(view->priv->notebook, + GTK_WIDGET(world_view))); + gm_app_view_update_title(view); + } +} + +gboolean +on_gm_app_view_focus_in(GtkWidget *widget, GdkEventFocus *event, + gpointer user_data) { + /* TODO: implementation + world *wld; + + wld = world_get_active(); + + if (wld) { + world_activated(wld); + } + + app_tray_info.iconnr = TRAY_ICON_DEFAULT; + tray_flash_restore_func(NULL);*/ + + return FALSE; +} diff --git a/src/gm-app-view.h b/src/gm-app-view.h new file mode 100644 index 0000000..20cf0a3 --- /dev/null +++ b/src/gm-app-view.h @@ -0,0 +1,77 @@ +#ifndef __GM_APP_VIEW_H__ +#define __GM_APP_VIEW_H__ + +#include +#include +#include +#include "gm-world.h" +#include "gm-app.h" + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GM_TYPE_APP_VIEW (gm_app_view_get_type()) +#define GM_APP_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_APP_VIEW, GmAppView)) +#define GM_APP_VIEW_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_APP_VIEW, GmAppView const)) +#define GM_APP_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_APP_VIEW, GmAppViewClass)) +#define GM_IS_APP_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_APP_VIEW)) +#define GM_IS_APP_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_APP_VIEW)) +#define GM_APP_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_APP_VIEW, GmAppViewClass)) + +/* Private structure type */ +typedef struct _GmAppViewPrivate GmAppViewPrivate; + +/* + * Main object structure + */ +typedef struct _GmAppView GmAppView; + +struct _GmAppView { + GtkWindow window; + + /*< private > */ + GmAppViewPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GmAppViewClass GmAppViewClass; + +struct _GmAppViewClass { + GtkWindowClass parent_class; + + /* Signals */ +}; + +GType gm_app_view_get_type(void) G_GNUC_CONST; +GmAppView *gm_app_view_new(GmApp *application); + +/* Callbacks */ +void on_gm_app_view_world_new(GtkMenuItem * menuitem, GmAppView *view); +void on_gm_app_view_world_connect(GtkMenuItem * menuitem, GmAppView *view); +void on_gm_app_view_world_logs(GtkMenuItem * menuitem, GmAppView *view); +void on_gm_app_view_world_info(GtkMenuItem * menuitem, GmAppView *view); +void on_gm_app_view_world_close(GtkMenuItem * menuitem, GmAppView *view); +void on_gm_app_view_world_quit(GtkMenuItem * menuitem, GmAppView *view); + +void on_gm_app_view_edit_cut(GtkMenuItem * menuitem, GmAppView *view); +void on_gm_app_view_edit_copy(GtkMenuItem * menuitem, GmAppView *view); +void on_gm_app_view_edit_paste(GtkMenuItem * menuitem, GmAppView *view); +void on_gm_app_view_edit_worlds(GtkMenuItem * menuitem, GmAppView *view); +void on_gm_app_view_edit_world(GtkMenuItem * menuitem, GmAppView *view); +void on_gm_app_view_edit_find(GtkMenuItem * menuitem, GmAppView *view); +void on_gm_app_view_edit_find_next(GtkMenuItem * menuitem, GmAppView *view); +void on_gm_app_view_edit_preferences( + GtkMenuItem * menuitem, GmAppView *view); + +void on_gm_app_view_view_mcp(GtkMenuItem * menuitem, GmAppView *view); +void on_gm_app_view_view_scripts(GtkMenuItem * menuitem, GmAppView *view); + +void on_gm_app_view_help_about(GtkMenuItem * menuitem, GmAppView *view); + +G_END_DECLS + +#endif /* __GM_APP_VIEW__ */ diff --git a/src/gm-app.c b/src/gm-app.c new file mode 100644 index 0000000..3cce64d --- /dev/null +++ b/src/gm-app.c @@ -0,0 +1,587 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include "gm-app-view.h" +#include "gm-world.h" +#include "gm-color-table.h" + +//#include "mcp/mcp.h" +#include "gm-app.h" +//#include "editor.h" +#include "debug.h" +#include "gm-pixbuf.h" +#include "gm-support.h" +#include "gm-scripts.h" + +static int debug_level = 0; +static gboolean show_version = FALSE; +static gboolean recover = FALSE; +static gchar *load_worlds = NULL; +static GmApp *application; + +//static void gm_app_tray_create(tray_info *t); +//gboolean on_gm_app_tray_button_press(GtkWidget *widget, GdkEventButton *event, +// gpointer user_data); +//gboolean on_gm_app_tray_destroy(GtkWidget *widget, gpointer user_data); +void on_gm_app_view_size_allocate(GmAppView *view, GtkAllocation *allocation, + GmApp *app); + +struct poptOption poptions[] = { + { + "debug", + 'd', + POPT_ARG_INT, + &debug_level, + 0, + N_ + ("Enable debugging. Debugging can be done in different depth levels, each " + "level inherits all messages from the levels below: " + "0 = None, 1 = Default and 2 = MCP"), + N_("DBGGLEVEL") + }, + { + "version", + 'v', + POPT_ARG_NONE, + &show_version, + 0, + N_("Show application version"), + NULL}, + { + "load", + 'l', + POPT_ARG_STRING, + &load_worlds, + 0, + N_("Load specified worlds, seperated by a `,'"), + N_("WORLDS")}, + { + "recover", + 'r', + POPT_ARG_NONE, + &recover, + 0, + N_("Recover from previous session (used with gnome session)"), + NULL}, + { + NULL, + '\0', + 0, + NULL, + 0, + NULL, + NULL} +}; + +#define GM_APP_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), \ + GM_TYPE_APP, GmAppPrivate)) +void gm_app_destroy_worlds(GmApp *app); + +struct _GmAppPrivate { + gchar *path; + gchar *worlds_path; + gchar *options_path; + + GmAppView *view; + GmOptions *options; + GmColorTable *color_table; + GnomeClient *client; + GList *worlds; + + #ifdef HAVE_RUBY + GmScripts *scripts; + #endif + + //tray_info tray; +}; + +/* Signals */ + +enum { + WORLD_ADDED, + WORLD_REMOVED, + NUM_SIGNALS +}; + +static guint app_signals[NUM_SIGNALS] = {0}; + +G_DEFINE_TYPE(GmApp, gm_app, G_TYPE_OBJECT) + +static void +gm_app_finalize(GObject *object) { + GmApp *app = GM_APP(object); + + gnome_vfs_shutdown(); + //mcp_fini(); + //editor_fini(); + + #ifdef HAVE_RUBY + g_object_unref(app->priv->scripts); + #endif + + //mcpconsole_fini(); + gm_app_destroy_worlds(app); + gm_pixbuf_fini(); + + gm_options_save(app->priv->options); + g_object_unref(app->priv->options); + g_object_unref(app->priv->color_table); + + g_free(app->priv->path); + g_free(app->priv->worlds_path); + g_free(app->priv->options_path); + + G_OBJECT_CLASS(gm_app_parent_class)->finalize(object); +} + +static void +gm_app_class_init(GmAppClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gm_app_finalize; + + app_signals[WORLD_ADDED] = + g_signal_new("world_added", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmAppClass, world_added), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + + app_signals[WORLD_REMOVED] = + g_signal_new("world_removed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmAppClass, world_removed), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + + g_type_class_add_private(object_class, sizeof(GmAppPrivate)); +} + +static void +gm_app_init(GmApp *app) { + app->priv = GM_APP_GET_PRIVATE(app); + + app->priv->path = NULL; + app->priv->worlds_path = NULL; + app->priv->options_path = NULL; + app->priv->options = NULL; + app->priv->client = NULL; + app->priv->worlds = NULL; + + //gm_app_tray_create(&(app->priv->tray)); +} + +/* Private functions */ +void +gm_app_destroy_worlds(GmApp *app) { + // TODO +} + +static void +on_gm_app_session_die(GnomeClient * client, gpointer client_data) { + gtk_main_quit(); +} + +/*static void +gm_app_tray_create(tray_info *t) { + t->flash_timeout = 0; + t->icon = egg_tray_icon_new(_("GnoeMoe Gnome MOO Client")); + t->event_box = gtk_event_box_new(); + + t->iconnr = TRAY_ICON_DEFAULT; + t->image = gtk_image_new_from_pixbuf( + gnoe_pixbuf_get("tray/default.svg")); + + gtk_container_add(GTK_CONTAINER(t->event_box), t->image); + t->tooltips = gtk_tooltips_new(); + + gtk_widget_show(t->event_box); + gtk_widget_show(t->image); + + gtk_container_add(GTK_CONTAINER(t->icon), t->event_box); + gtk_widget_show(GTK_WIDGET(t->icon)); + + gtk_widget_add_events(GTK_WIDGET(t->icon), GDK_BUTTON_PRESS_MASK); + + g_signal_connect(t->icon, "button_press_event", + G_CALLBACK(on_gm_app_tray_button_press), NULL); + + // Handles when the area is removed from the panel. + g_signal_connect(t->icon, "destroy", G_CALLBACK(on_gm_app_tray_destroy), + t->event_box); +}*/ + +static gboolean +on_gm_app_save_session(GnomeClient * client, gint phase, + GnomeSaveStyle save_style, gint is_shutdown, + GnomeInteractStyle interact_style, gint is_fast, gchar ** client_data) { + gchar **argv; + gint argc = sizeof(client_data) / sizeof(gchar *) + 1; + int i; + GString *ws = g_string_new(""); + GList *elem, *list; + GmWorld *world; + + argv = g_new(gchar *, argc); + argv[0] = client_data[0]; + argv[1] = "--recover"; + + for (i = 1; i < argc - 1; i++) { + argv[i + 1] = client_data[i]; + } + + gnome_client_set_clone_command(client, argc, argv); + gnome_client_set_restart_command(client, argc, argv); + + // Saving worlds state + list = gm_app_worlds(application); + + for (elem = list; elem; elem = elem->next) { + world = (GmWorld *) (elem->data); + + if (gm_world_loaded(world)) { + if (strlen(ws->str) != 0) { + ws = g_string_append_c(ws, ';'); + } + + ws = g_string_append(ws, gm_world_name(world)); + } + } + + g_list_free(list); + gm_options_set(gm_app_options(application), "worlds_saved_state", ws->str); + gm_options_save(gm_app_options(application)); + g_string_free(ws, TRUE); + + return TRUE; +} + +void +gm_app_create_settings(GmApp *app) { + app->priv->options = gm_options_new(); + + gm_options_set(app->priv->options, "editor_alternative", "0"); + gm_options_set(app->priv->options, "editor_embed", "0"); + gm_options_set(app->priv->options, "editor_needs_terminal", "0"); + gm_options_set(app->priv->options, "font-family", "Monospace 8"); + gm_options_set(app->priv->options, "worlds_saved_state", ""); + gm_options_set(app->priv->options, "search_direction", "1"); +} + +void +gm_app_load_worlds(GmApp *app, gboolean autoload) { + GDir *handle = g_dir_open(app->priv->worlds_path, 0, NULL); + char *name, *path; + GmWorld *new_world; + + if (handle != NULL) { + while ((name = (char *) g_dir_read_name(handle)) != NULL) { + path = g_strconcat(app->priv->worlds_path, G_DIR_SEPARATOR_S, + name, NULL); + + if (g_file_test(path, G_FILE_TEST_IS_DIR)) { + new_world = gm_world_new(path); + + if (new_world != NULL) { + gm_app_add_world(app, new_world); + + if (autoload && gm_options_get_int( + gm_world_options(new_world), "autoload")) { + gm_world_load(new_world); + } + } + } else { + debug_msg(0, "Nee: %s", path); + } + + g_free(path); + } + + g_dir_close(handle); + } else { + debug_msg(1, "GmApp.load_worlds: failed to open worlds path %s", + app->priv->path); + } +} + +void +gm_app_initialize(GmApp *app) { + debug_set_level(debug_level); + + app->priv->worlds_path = g_strconcat(app->priv->path, G_DIR_SEPARATOR_S, + "worlds", NULL); + app->priv->options_path = g_strconcat(app->priv->path, G_DIR_SEPARATOR_S, + "settings", NULL); + + if (!g_file_test(app->priv->path, G_FILE_TEST_EXISTS)) { + mkdir(app->priv->path, 0755); + mkdir(app->priv->worlds_path, 0755); + } + + #ifdef HAVE_RUBY + app->priv->scripts = gm_scripts_new(); + #endif + + /* Create the main view */ + gm_app_create_settings(app); + gm_options_load(app->priv->options, app->priv->options_path); + app->priv->color_table = + gm_color_table_new_from_options(app->priv->options); +} + +void +gm_app_run(GmApp *app) { + gchar **wrlds; + const gchar *savedState; + int i = 0; + GmWorld *world; + int width, height; + + app->priv->view = gm_app_view_new(app); + + width = gm_options_get_int(app->priv->options, "width"); + height = gm_options_get_int(app->priv->options, "height"); + + if (height > 10 && width > 10) { + gtk_window_set_default_size(GTK_WINDOW(app->priv->view), width, height); + } + + gtk_widget_show(GTK_WIDGET(app->priv->view)); + + //mcpconsole_init(); + + g_signal_connect(app->priv->view, "size_allocate", + G_CALLBACK(on_gm_app_view_size_allocate), app); + + #ifdef HAVE_RUBY + gm_scripts_load(app->priv->scripts); + #endif + + gm_app_load_worlds(app, !(recover || load_worlds)); + + if (recover) { + savedState = gm_options_get(app->priv->options, "worlds_saved_state"); + + if (strlen(savedState) != 0) { + wrlds = g_strsplit(savedState, ";", -1); + + for (i = 0; wrlds[i]; i++) { + if (strlen(wrlds[i]) != 0) { + world = gm_app_world_by_name(app, wrlds[i]); + + if (world) { + gm_world_load(world); + } + } + } + + g_strfreev(wrlds); + } + } else if (load_worlds) { + wrlds = g_strsplit(load_worlds, ",", -1); + + for (i = 0; wrlds[i]; i++) { + world = gm_app_world_by_name(app, wrlds[i]); + + if (world) { + gm_world_load(world); + } + } + + g_strfreev(wrlds); + } + + gtk_main(); +} + +/* Public functions */ +GmApp * +gm_app_new(int argc, char *argv[]) { + GmApp *app = GM_APP(g_object_new(GM_TYPE_APP, NULL)); + + app->priv->path = gnome_util_home_file("gnoemoe"); + + if (!app->priv->path) { + printf(_("GnoeMoe Application: there is no application directory, " + "this is very bad!!!\n")); + return app; + } + +#ifdef ENABLE_NLS + bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + textdomain(GETTEXT_PACKAGE); +#endif + + gtk_set_locale(); + gtk_init(&argc, &argv); + gnome_program_init(PACKAGE, VERSION, LIBGNOMEUI_MODULE, + argc, argv, GNOME_PARAM_POPT_TABLE, poptions, + GNOME_PARAM_APP_DATADIR, PACKAGE_DATA_DIR, NULL); + + if (show_version) { + printf(_("Current version of GnoeMoe is %s\n"), VERSION); + return app; + } + + app->priv->client = gnome_master_client(); + + gtk_signal_connect(GTK_OBJECT(app->priv->client), "save_yourself", + GTK_SIGNAL_FUNC(on_gm_app_save_session), argv); + gtk_signal_connect(GTK_OBJECT(app->priv->client), "die", + GTK_SIGNAL_FUNC(on_gm_app_session_die), NULL); + + /* Initialize everything */ + gnome_vfs_init(); + glade_init(); + //mcp_init(); + gm_pixbuf_init(); + gm_app_initialize(app); + //editor_init(); + + return app; +} + +void +gm_app_add_world(GmApp *app, GmWorld *world) { + app->priv->worlds = g_list_append(app->priv->worlds, g_object_ref(world)); + g_signal_emit(app, app_signals[WORLD_ADDED], 0, world); +} + +void +gm_app_remove_world(GmApp *app, GmWorld *world) { + // Only remove when not loaded + const gchar *path = gm_world_path(world); + + if (!gm_world_loaded(world)) { + app->priv->worlds = g_list_remove(app->priv->worlds, world); + g_signal_emit(app, app_signals[WORLD_REMOVED], 0, world); + + g_object_unref(world); + gm_directory_remove_all(path, TRUE); + } +} + +GmWorld * +gm_app_world_by_name(GmApp *app, gchar *name) { + GList *elem; + const gchar *world_name; + + for (elem = app->priv->worlds; elem; elem = elem->next) { + world_name = gm_world_name(GM_WORLD(elem->data)); + + if (!g_strcasecmp(name, world_name)) { + return GM_WORLD(elem->data); + } + } + + return NULL; +} + +GmOptions * +gm_app_options(GmApp *app) { + return app->priv->options; +} + +#ifdef HAVE_RUBY +GmScripts * +gm_app_scripts(GmApp *app) { + return app->priv->scripts; +} +#endif + +const gchar * +gm_app_worlds_path(GmApp *app) { + return app->priv->worlds_path; +} + +const gchar * +gm_app_path(GmApp *app) { + return app->priv->path; +} + +GList * +gm_app_worlds(GmApp *app) { + return g_list_copy(app->priv->worlds); +} + +GmColorTable * +gm_app_color_table(GmApp *app) { + return app->priv->color_table; +} + +GmApp * +gm_app_instance() { + return application; +} + +int +main(int argc, char *argv[]) { + g_type_init(); + application = gm_app_new(argc, argv); + gm_app_run(application); + g_object_unref(application); + return 0; +} + +/* Callbacks */ +void +on_gm_app_view_size_allocate(GmAppView *view, GtkAllocation *allocation, + GmApp *app) { + gm_options_set_int(app->priv->options, "width", allocation->width); + gm_options_set_int(app->priv->options, "height", allocation->height); +} + +/*gboolean +on_gm_app_tray_button_press(GtkWidget *widget, GdkEventButton *event, + gpointer user_data) { + if (event->type == GDK_2BUTTON_PRESS || + event->type == GDK_3BUTTON_PRESS) { + return FALSE; + } + + switch (event->button) { + case 1: + if_main_show_hide(!GTK_WIDGET_VISIBLE(wndMain)); + break; + default: + return FALSE; + } + + return TRUE; +} + +gboolean +on_gm_app_tray_destroy(GtkWidget *widget, gpointer user_data) { + gtk_widget_destroy(GTK_WIDGET(app_tray_info.icon)); + app_tray_info.icon = NULL; + app_tray_info.event_box = NULL; + app_tray_info.image = NULL; + app_tray_info.tooltips = NULL; + + if (app_tray_info.flash_timeout != 0) { + g_source_remove(app_tray_info.flash_timeout); + } + + tray_create(&app_tray_info); + + if (!have_tray()) { + gtk_widget_show(if_main_get_widget("wndMain")); + } + + return TRUE; +}*/ diff --git a/src/gm-app.h b/src/gm-app.h new file mode 100644 index 0000000..e8f9449 --- /dev/null +++ b/src/gm-app.h @@ -0,0 +1,79 @@ +#ifndef __GM_APP_H__ +#define __GM_APP_H__ + +//#define G_XML PACKAGE_DATA_DIR "/" PACKAGE "/ui/gm-main.glade" + +#include +#include "gm-world.h" +#include "gm-options.h" +#include "gm-color-table.h" +#include "gm-scripts.h" + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GM_TYPE_APP (gm_app_get_type()) +#define GM_APP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + GM_TYPE_APP, GmApp)) +#define GM_APP_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + GM_TYPE_APP, GmApp const)) +#define GM_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \ + GM_TYPE_APP, GmAppClass)) +#define GM_IS_APP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + GM_TYPE_APP)) +#define GM_IS_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + GM_TYPE_APP)) +#define GM_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ + GM_TYPE_APP, GmAppClass)) + +/* Private structure type */ +typedef struct _GmAppPrivate GmAppPrivate; + +/* + * Main object structure + */ +typedef struct _GmApp GmApp; + +struct _GmApp { + GObject object; + + /*< private > */ + GmAppPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GmAppClass GmAppClass; + +struct _GmAppClass { + GObjectClass parent_class; + + /* Signals */ + void (* world_added) (GmApp *app, GmWorld *world); + void (* world_removed) (GmApp *app, GmWorld *world); +}; + +GmApp *gm_app_instance(); +GType gm_app_get_type(void) G_GNUC_CONST; +GmApp *gm_app_new(int argc, char *argv[]); + +void gm_app_add_world(GmApp *app, GmWorld *world); +void gm_app_remove_world(GmApp *app, GmWorld *world); + +const gchar *gm_app_worlds_path(GmApp *app); +const gchar *gm_app_path(GmApp *app); +GmOptions *gm_app_options(GmApp *app); +GList *gm_app_worlds(GmApp *app); +GmColorTable *gm_app_color_table(GmApp *app); +GmWorld *gm_app_world_by_name(GmApp *app, gchar *name); + +#ifdef HAVE_RUBY +GmScripts *gm_app_scripts(GmApp *app); +#endif + +G_END_DECLS + +#endif /* __GM_APP_H__ */ diff --git a/src/gm-bogus.h b/src/gm-bogus.h new file mode 100644 index 0000000..a90465f --- /dev/null +++ b/src/gm-bogus.h @@ -0,0 +1,8 @@ +#ifndef __GM_BOGUS_H__ +#define __GM_BOGUS_H__ + +typedef GObject GmMcp; +GmMcp *gm_mcp_new(GmWorld *world) { return NULL; } +void gm_mcp_handle(GmMcp *mcp, gchar *line) {} + +#endif /* __GM_BOGUS_H__ */ diff --git a/src/gm-color-table.c b/src/gm-color-table.c new file mode 100644 index 0000000..4641cf6 --- /dev/null +++ b/src/gm-color-table.c @@ -0,0 +1,281 @@ +#include +#include "gm-color-table.h" +#include "string.h" +#include "ansi.h" +#include "debug.h" + +#define GM_COLOR_TABLE_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_COLOR_TABLE, GmColorTablePrivate)) + +void gm_color_table_item_free(gpointer item); + +typedef struct _GmColorTableItem GmColorTableItem; + +struct _GmColorTableItem { + gchar *hex; + GdkColor color; +}; + +struct _GmColorTablePrivate { + gboolean bold; + GHashTable *colors; + gchar *font_description; +}; + +/* Signals */ + +enum { + COLOR_CHANGED, + BOLD_TOGGLED, + FONT_CHANGED, + NUM_SIGNALS +}; + +static guint color_table_signals[NUM_SIGNALS] = {0}; + +G_DEFINE_TYPE(GmColorTable, gm_color_table, G_TYPE_OBJECT) + +static void +gm_color_table_finalize(GObject *object) { + //GmColorTable *table = GM_COLOR_TABLE(object); + + G_OBJECT_CLASS(gm_color_table_parent_class)->finalize(object); +} + +static void +gm_color_table_class_init(GmColorTableClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gm_color_table_finalize; + + color_table_signals[COLOR_CHANGED] = + g_signal_new("color_changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmColorTableClass, color_changed), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + color_table_signals[BOLD_TOGGLED] = + g_signal_new("bold_toggled", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmColorTableClass, bold_toggled), + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, + 1, + G_TYPE_BOOLEAN); + + color_table_signals[FONT_CHANGED] = + g_signal_new("font_changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmColorTableClass, font_changed), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + g_type_class_add_private(object_class, sizeof(GmColorTablePrivate)); +} + +static void +gm_color_table_init(GmColorTable *table) { + table->priv = GM_COLOR_TABLE_GET_PRIVATE(table); + table->priv->colors = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, gm_color_table_item_free); +} + +/* Private functions */ +void +gm_color_table_item_free(gpointer item) { + GmColorTableItem *i = (GmColorTableItem *)(item); + + g_free(i->hex); + g_free(i); +} + +void +gm_color_table_initialize(GmColorTable *table) { + gm_color_table_set_bold(table, FALSE); + gm_color_table_set_font_description(table, "Monospace 10"); + + /*gm_color_table_set(table, "fg_default", "#D6B5D6B5D6B5"); + gm_color_table_set(table, "fg_black", "#2D6B2D6B2D6B"); + gm_color_table_set(table, "fg_red", "#FFFF00000000"); + gm_color_table_set(table, "fg_green", "#0000FFFF0000"); + gm_color_table_set(table, "fg_yellow", "#FFFFD0450000"); + gm_color_table_set(table, "fg_blue", "#3EF73EF7BFFF"); + gm_color_table_set(table, "fg_purple", "#A0A02020F0F0"); + gm_color_table_set(table, "fg_cyan", "#0000FFFFFFFF"); + gm_color_table_set(table, "fg_white", "#D8C5D8C5D8C5"); + + gm_color_table_set(table, "fg_default_h", "#FFFFFFFFFFFF"); + gm_color_table_set(table, "fg_black_h", "#529452945294"); + gm_color_table_set(table, "fg_red_h", "#FFFF785F785F"); + gm_color_table_set(table, "fg_green_h", "#66ADFFFF66AD"); + gm_color_table_set(table, "fg_yellow_h", "#FFFFFFFF58C6"); + gm_color_table_set(table, "fg_blue_h", "#86318631FFFF"); + gm_color_table_set(table, "fg_purple_h", "#C6576A18FFFF"); + gm_color_table_set(table, "fg_cyan_h", "#86EEFFFFFFFF"); + gm_color_table_set(table, "fg_white_h", "#FFFFFFFFFFFF"); + + gm_color_table_set(table, "bg_default", "#000000000000"); + gm_color_table_set(table, "bg_black", "#2B5B2B5B2B5B"); + gm_color_table_set(table, "bg_red", "#FFFF00000000"); + gm_color_table_set(table, "bg_green", "#000080000000"); + gm_color_table_set(table, "bg_yellow", "#C047C0470000"); + gm_color_table_set(table, "bg_blue", "#00000000FFFF"); + gm_color_table_set(table, "bg_purple", "#A0A02020F0F0"); + gm_color_table_set(table, "bg_cyan", "#0000B74CB74C"); + gm_color_table_set(table, "bg_white", "#FFFFFFFFFFFF");*/ + + gm_color_table_set(table, "fg_default", "#000000"); + gm_color_table_set(table, "fg_black", "#000000"); + gm_color_table_set(table, "fg_red", "#663822"); + gm_color_table_set(table, "fg_green", "#445632"); + gm_color_table_set(table, "fg_yellow", "#D1940C"); + gm_color_table_set(table, "fg_blue", "#314E6C"); + gm_color_table_set(table, "fg_purple", "#494066"); + gm_color_table_set(table, "fg_cyan", "#0000FFFFFFFF"); + gm_color_table_set(table, "fg_white", "#BAB5AB"); + + gm_color_table_set(table, "fg_default_h", "#565248"); + gm_color_table_set(table, "fg_black_h", "#565248"); + gm_color_table_set(table, "fg_red_h", "#990000"); + gm_color_table_set(table, "fg_green_h", "#267726"); + gm_color_table_set(table, "fg_yellow_h", "#EED680"); + gm_color_table_set(table, "fg_blue_h", "#9DB8D2"); + gm_color_table_set(table, "fg_purple_h", "#ADA7C8"); + gm_color_table_set(table, "fg_cyan_h", "#86EEFFFFFFFF"); + gm_color_table_set(table, "fg_white_h", "#807D74"); + + gm_color_table_set(table, "bg_default", "#EAE8E3"); + gm_color_table_set(table, "bg_black", "#000000"); + gm_color_table_set(table, "bg_red", "#663822"); + gm_color_table_set(table, "bg_green", "#445632"); + gm_color_table_set(table, "bg_yellow", "#D1940C"); + gm_color_table_set(table, "bg_blue", "#314E6C"); + gm_color_table_set(table, "bg_purple", "#494066"); + gm_color_table_set(table, "bg_cyan", "#0000FFFFFFFF"); + gm_color_table_set(table, "bg_white", "#FFFFFFFFFFFF"); +} + +/* Public functions */ + +GmColorTable * +gm_color_table_new(void) { + GmColorTable *table = GM_COLOR_TABLE(g_object_new(GM_TYPE_COLOR_TABLE, NULL)); + + gm_color_table_restore_defaults(table); + return table; +} + +GmColorTable * +gm_color_table_new_from_options(GmOptions *options) { + GmColorTable *table = GM_COLOR_TABLE(g_object_new(GM_TYPE_COLOR_TABLE, NULL)); + + gm_color_table_set_from_options(table, options); + return table; +} + +void +gm_color_table_restore_defaults(GmColorTable *table) { + gm_color_table_initialize(table); +} + +void +gm_color_table_set(GmColorTable *table, const gchar *name, const gchar *hex) { + GmColorTableItem *item; + + item = g_hash_table_lookup(table->priv->colors, name); + + if (!item) { + item = g_new0(GmColorTableItem, 1); + g_hash_table_insert(table->priv->colors, g_strdup(name), item); + } + + if (item->hex == NULL || strcmp(hex, item->hex) != 0) { + g_free(item->hex); + item->hex = g_strdup(hex); + gdk_color_parse(item->hex, &(item->color)); + + g_signal_emit(table, color_table_signals[COLOR_CHANGED], 0, name); + } +} + +gboolean +gm_color_table_get(GmColorTable *table, const gchar *name, GdkColor *color) { + GmColorTableItem *item; + + item = g_hash_table_lookup(table->priv->colors, name); + + if (item != NULL) { + *color = item->color; + return TRUE; + } else { + return FALSE; + } +} + +void +gm_color_table_set_bold(GmColorTable *table, gboolean bold) { + if (table->priv->bold != bold) { + table->priv->bold = bold; + + g_signal_emit(table, color_table_signals[BOLD_TOGGLED], 0, bold); + } +} + +gboolean +gm_color_table_bold(GmColorTable *table) { + return table->priv->bold; +} + +void +gm_color_table_set_font_description(GmColorTable *table, + const gchar *font_description) { + const gchar *fd; + + if (font_description == NULL) { + fd = "Monospace 10"; + } else { + fd = font_description; + } + + if (table->priv->font_description == NULL || + strcmp(table->priv->font_description, fd) != 0) { + g_free(table->priv->font_description); + table->priv->font_description = g_strdup(fd); + + g_signal_emit(table, color_table_signals[FONT_CHANGED], 0, + table->priv->font_description); + } +} + +const gchar * +gm_color_table_font_description(GmColorTable *table) { + return table->priv->font_description; +} + +void +gm_color_table_set_from_options(GmColorTable *table, GmOptions *options) { + unsigned int i; + const gchar *value; + + for (i = 0; i < sizeof(ansi_colors) / sizeof(ansinamepair); i++) { + value = gm_options_get(options, ansi_colors[i].name); + + if (value != NULL) { + gm_color_table_set(table, ansi_colors[i].name, value); + } + } + + gm_color_table_set_bold(table, gm_options_get_int(options, "bold-colors")); + gm_color_table_set_font_description(table, gm_options_get(options, + "font-family")); +} diff --git a/src/gm-color-table.h b/src/gm-color-table.h new file mode 100644 index 0000000..4106e39 --- /dev/null +++ b/src/gm-color-table.h @@ -0,0 +1,67 @@ +#ifndef __GM_COLOR_TABLE_H__ +#define __GM_COLOR_TABLE_H__ + +#include +#include "gm-options.h" + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GM_TYPE_COLOR_TABLE (gm_color_table_get_type()) +#define GM_COLOR_TABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_COLOR_TABLE, GmColorTable)) +#define GM_COLOR_TABLE_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_COLOR_TABLE, GmColorTable const)) +#define GM_COLOR_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_COLOR_TABLE, GmColorTableClass)) +#define GM_IS_COLOR_TABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_COLOR_TABLE)) +#define GM_IS_COLOR_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_COLOR_TABLE)) +#define GM_COLOR_TABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_COLOR_TABLE, GmColorTableClass)) + +/* Private structure type */ +typedef struct _GmColorTablePrivate GmColorTablePrivate; + +/* + * Main object structure + */ +typedef struct _GmColorTable GmColorTable; + +struct _GmColorTable { + GObject object; + + /*< private > */ + GmColorTablePrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GmColorTableClass GmColorTableClass; + +struct _GmColorTableClass { + GObjectClass parent_class; + + /* Signals */ + void (* color_changed) (GmColorTable *table, const gchar *url); + void (* bold_toggled) (GmColorTable *table, gboolean bold); + void (* font_changed) (GmColorTable *table, const gchar *font_description); +}; + +GType gm_color_table_get_type(void) G_GNUC_CONST; +GmColorTable *gm_color_table_new(void); +GmColorTable *gm_color_table_new_from_options(GmOptions *options); +void gm_color_table_restore_defaults(GmColorTable *table); +void gm_color_table_set(GmColorTable *table, const gchar *name, + const gchar *hex); +gboolean gm_color_table_get(GmColorTable *table, const gchar *name, + GdkColor *color); +void gm_color_table_set_bold(GmColorTable *table, gboolean bold); +gboolean gm_color_table_bold(GmColorTable *table); +void gm_color_table_set_font_description(GmColorTable *table, + const gchar *font_description); +const gchar *gm_color_table_font_description(GmColorTable *table); + +void gm_color_table_set_from_options(GmColorTable *table, GmOptions *options); + +G_END_DECLS +#endif /* __GM_COLOR_TABLE_H__ */ + diff --git a/src/gm-editor.c b/src/gm-editor.c new file mode 100644 index 0000000..412c53b --- /dev/null +++ b/src/gm-editor.c @@ -0,0 +1,128 @@ +#include + +#include "gm-editor.h" +#include "gm-world.h" +#include "gm-support.h" + +#define GM_EDITOR_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_EDITOR, GmEditorPrivate)) + +struct _GmEditorPrivate { + gchar *name; + gchar *upload_cmd; + gchar *mcp_type; + + GmEditType type; + gboolean is_code; + + GList *lines; +}; + +/* Signals */ + +enum { + SAVE, + NUM_SIGNALS +}; + +static guint editor_signals[NUM_SIGNALS] = {0}; + +G_DEFINE_TYPE(GmEditor, gm_editor, G_TYPE_OBJECT) + +static void +gm_editor_finalize(GObject *object) { + GmEditor *editor = GM_EDITOR(object); + + g_free(editor->priv->name); + g_free(editor->priv->upload_cmd); + g_free(editor->priv->mcp_type); + g_list_free_simple(editor->priv->lines); + + G_OBJECT_CLASS(gm_editor_parent_class)->finalize(object); +} + +static void +gm_editor_class_init(GmEditorClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gm_editor_finalize; + + editor_signals[SAVE] = + g_signal_new("save", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmEditorClass, save), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + g_type_class_add_private(object_class, sizeof(GmEditorPrivate)); +} + +static void +gm_editor_init(GmEditor *view) { + view->priv = GM_EDITOR_GET_PRIVATE(view); +} + +GmEditor * +gm_editor_new(gchar *name, gchar *uploadcmd, GList *text) { + GmEditor *editor = GM_EDITOR(g_object_new(GM_TYPE_EDITOR, NULL)); + + editor->priv->name = g_strdup(name); + editor->priv->upload_cmd = g_strdup(uploadcmd); + editor->priv->mcp_type = NULL; + editor->priv->lines = g_list_copy(text); + editor->priv->type = E_LEGACY; + editor->priv->is_code = (strncmp(uploadcmd, "@program", 7) == 0); + return editor; +} + +GmEditor * +gm_editor_new_mcp(gchar *name, gchar *reference, gchar *type, + GList *text) { + GmEditor *editor = GM_EDITOR(g_object_new(GM_TYPE_EDITOR, NULL)); + + editor->priv->name = g_strdup(name); + editor->priv->upload_cmd = g_strdup(reference); + editor->priv->mcp_type = g_strdup(type); + editor->priv->lines = g_list_copy(text); + editor->priv->type = E_LEGACY; + editor->priv->is_code = (strcmp(type, "moo-code") == 0); + + return editor; +} + +gboolean +gm_editor_is_code(GmEditor *editor) { + return editor->priv->is_code; +} + +gchar * +gm_editor_name(GmEditor *editor) { + return editor->priv->name; +} + +gchar * +gm_editor_upload_cmd(GmEditor *editor) { + return editor->priv->upload_cmd; +} + +gchar * +gm_editor_mcp_type(GmEditor *editor) { + return editor->priv->mcp_type; +} + +GList * +gm_editor_lines(GmEditor *editor) { + return editor->priv->lines; +} + +GmEditType +gm_editor_type(GmEditor *editor) { + return editor->priv->type; +} + +void +gm_editor_save(GmEditor *editor) { + g_signal_emit(editor, editor_signals[SAVE], 0); +} diff --git a/src/gm-editor.h b/src/gm-editor.h new file mode 100644 index 0000000..8b2da75 --- /dev/null +++ b/src/gm-editor.h @@ -0,0 +1,68 @@ +#ifndef __GM_EDITOR_H__ +#define __GM_EDITOR_H__ + +#include +#include + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GM_TYPE_EDITOR (gm_editor_get_type()) +#define GM_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_EDITOR, GmEditor)) +#define GM_EDITOR_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_EDITOR, GmEditor const)) +#define GM_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_EDITOR, GmEditorClass)) +#define GM_IS_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_EDITOR)) +#define GM_IS_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_EDITOR)) +#define GM_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_EDITOR, GmEditorClass)) + +/* Private structure type */ +typedef struct _GmEditorPrivate GmEditorPrivate; + +/* + * Main object structure + */ +typedef struct _GmEditor GmEditor; + +struct _GmEditor { + GObject object; + + /*< private > */ + GmEditorPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GmEditorClass GmEditorClass; + +struct _GmEditorClass { + GObject parent_class; + + /* Signals */ + void (* save) (GmEditor *editor); +}; + +typedef enum _GmEditType GmEditType; +enum _GmEditType { + E_LEGACY, + E_MCP +}; + +GType gm_editor_get_type(void) G_GNUC_CONST; +GmEditor *gm_editor_new(gchar *name, gchar *uploadcmd, GList *text); +GmEditor *gm_editor_new_mcp(gchar *name, gchar *reference, gchar *type, + GList *text); + +void gm_editor_save(GmEditor *editor); + +gboolean gm_editor_is_code(GmEditor *editor); +gchar *gm_editor_name(GmEditor *editor); +gchar *gm_editor_upload_cmd(GmEditor *editor); +gchar *gm_editor_mcp_type(GmEditor *editor); +GList *gm_editor_lines(GmEditor *editor); +GmEditType gm_editor_type(GmEditor *editor); + +G_END_DECLS +#endif /* __GM_EDITOR_H__ */ diff --git a/src/gm-marshal.list b/src/gm-marshal.list new file mode 100644 index 0000000..bd0a3c1 --- /dev/null +++ b/src/gm-marshal.list @@ -0,0 +1,4 @@ +VOID:STRING,UINT +VOID:STRING,INT +VOID:INT,INT +VOID:INT,STRING diff --git a/src/gm-net.c b/src/gm-net.c new file mode 100644 index 0000000..9f509bf --- /dev/null +++ b/src/gm-net.c @@ -0,0 +1,537 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gm-net.h" +#include "gm-marshal.h" +#include "debug.h" +#include "gm-support.h" + +#define GM_NET_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_NET, GmNetPrivate)) + +struct _GmNetPrivate { + int socket; /**< the connection socket, is -1 if not connected */ + GIOChannel *channel; /**< the channel which is used to 'listen' on the socket via the glib main loop */ + guint source; /**< the id of the socket watch */ + guint connect_timeout_id; /**< the connect timeout id */ + guint connect_check_id; /**< the connect timeout id */ + struct timeval last_connected; /**< contains when the last connect happened */ + int tn_last; /**< used for telnet */ + int tn_subneg; /**< used for telnet */ + + struct addrinfo *addr; + struct addrinfo *current; + + GmNetState state; /**< state of the connection */ + gchar *current_host; + gchar *current_port; +}; + +/* Signals */ + +enum { + STATE_CHANGING, + NET_ERROR, + BYTES_RECV, + NUM_SIGNALS +}; + +static guint net_signals[NUM_SIGNALS] = {0}; + +G_DEFINE_TYPE(GmNet, gm_net, G_TYPE_OBJECT) + +/* These values taken from RFC 854 and RFC 857. */ +#define TN_WILL 251 +#define TN_WONT 252 +#define TN_DO 253 +#define TN_DONT 254 +#define TN_IAC 255 /* Interpret As Command */ +#define TN_SB 250 /* start of subnegotiation */ +#define TN_SE 240 /* end of subnegotiaton */ + +void gm_net_connect_next(GmNet *net); + +gboolean on_gm_net_input_recv(GIOChannel * source, GIOCondition condition, + GmNet *net); +gboolean on_gm_net_connect_check(GIOChannel * source, GIOCondition condition, + GmNet *net); +gboolean on_gm_net_connect_timeout(GmNet *net); + +static void +gm_net_finalize(GObject *object) { + //GmNet *net = GM_NET(object); + + G_OBJECT_CLASS(gm_net_parent_class)->finalize(object); +} + +static void +gm_net_class_init(GmNetClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gm_net_finalize; + + net_signals[STATE_CHANGING] = + g_signal_new("state_changing", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmNetClass, state_changing), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + net_signals[NET_ERROR] = + g_signal_new("net_error", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmNetClass, net_error), + NULL, NULL, + gm_marshal_VOID__STRING_INT, + G_TYPE_NONE, + 2, + G_TYPE_STRING, + G_TYPE_INT); + + net_signals[BYTES_RECV] = + g_signal_new("bytes_recv", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmNetClass, bytes_recv), + NULL, NULL, + gm_marshal_VOID__STRING_UINT, + G_TYPE_NONE, + 2, + G_TYPE_STRING, + G_TYPE_UINT); + + g_type_class_add_private(object_class, sizeof(GmNetPrivate)); +} + +static void +gm_net_init(GmNet *net) { + net->priv = GM_NET_GET_PRIVATE(net); + net->priv->state = GM_NET_STATE_DISCONNECTED; + net->priv->addr = NULL; + net->priv->current = NULL; + net->priv->channel = NULL; + + net->priv->source = 0; + net->priv->connect_timeout_id = 0; + net->priv->connect_check_id = 0; +} + +void +gm_net_set_state(GmNet *net, GmNetState state) { + g_signal_emit(net, net_signals[STATE_CHANGING], 0, state); + net->priv->state = state; +} + +void +gm_net_clean_disconnection(GmNet *net) { + GError *err = NULL; + + if (!net->priv->channel) { + debug_msg(1, "GmNet.CleanDisconnection: NOT clean for %d", + net->priv->socket); + return; + } + + debug_msg(1, "GmNet.CleanDisconnection: clean disconnect for %d", + net->priv->socket); + + // Shutdown the channel + g_io_channel_shutdown(net->priv->channel, TRUE, &err); + + if (err) { + debug_msg(1, "GmNet.CleanDisconnection: error on channel shutdown: " + "%s", err->message); + g_error_free(err); + err = NULL; + } + + g_io_channel_unref(net->priv->channel); + + net->priv->channel = NULL; + net->priv->socket = -1; + + net->priv->tn_last = 0; + net->priv->tn_subneg = 0; + + gm_net_set_state(net, GM_NET_STATE_DISCONNECTED); +} + +void +gm_net_dirty_disconnection(GmNet *net, int err) { + gchar *msg; + + // Pff, stupid, we print a message and pass it on to clean_disconnection + debug_msg(1, "GmNet.DirtyDisconnection: dirty disconnect %d", + net->priv->socket); + + msg = g_strdup_printf(_("Connection lost... (%s)"), strerror(err)); + g_signal_emit(net, net_signals[NET_ERROR], 0, msg, + GM_NET_ERROR_DISCONNECTED); + g_free(msg); + + gm_net_clean_disconnection(net); +} + +void +gm_net_connect_succeed(GmNet *net) { + freeaddrinfo(net->priv->addr); + net->priv->addr = NULL; + net->priv->current = NULL; + + net->priv->source = g_io_add_watch(net->priv->channel, + G_IO_IN | G_IO_HUP, (GIOFunc)on_gm_net_input_recv, net); + + if (net->priv->connect_timeout_id != 0) { + g_source_remove(net->priv->connect_timeout_id); + net->priv->connect_timeout_id = 0; + } + + if (net->priv->connect_check_id != 0) { + g_source_remove(net->priv->connect_check_id); + net->priv->connect_check_id = 0; + } + + gettimeofday(&(net->priv->last_connected), NULL); + gm_net_set_state(net, GM_NET_STATE_CONNECTED); +} + +void +gm_net_connect_failed(GmNet *net, gchar *err, gint code) { + if (net->priv->channel) { + g_io_channel_shutdown(net->priv->channel, TRUE, NULL); + g_io_channel_unref(net->priv->channel); + net->priv->channel = NULL; + } + + g_signal_emit(net, net_signals[NET_ERROR], 0, err, GM_NET_ERROR_CONNECTING); + + if (net->priv->addr && net->priv->current->ai_next) { + net->priv->current = net->priv->current->ai_next; + gm_net_connect_next(net); + } else { + net->priv->socket = -1; + + if (net->priv->addr) { + freeaddrinfo(net->priv->addr); + net->priv->addr = NULL; + net->priv->current = NULL; + } + + // Don't use set_state here because we weren't really connected before + gm_net_set_state(net, GM_NET_STATE_DISCONNECTED); + } +} + +void +gm_net_connect_next(GmNet *net) { + char host[NI_MAXHOST], port[NI_MAXSERV]; + int ret, result; + struct addrinfo *tmp = net->priv->current; + + if (tmp == NULL) { + return; + } else { + if ((ret = getnameinfo(tmp->ai_addr, tmp->ai_addrlen, host, NI_MAXHOST, + port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { + debug_msg(1, "GmNet.ConnectNext: getnameinfo error: %s", + gai_strerror(ret)); + gm_net_connect_failed(net, (gchar *)gai_strerror(ret), ret); + return; + } + + net->priv->current_host = host; + net->priv->current_port = port; + gm_net_set_state(net, GM_NET_STATE_TRY_ADDRESS); + + net->priv->socket = socket(tmp->ai_family, tmp->ai_socktype, + tmp->ai_protocol); + + if (net->priv->socket < 0) { + gm_net_connect_failed(net, strerror(errno), errno); + } else { + fcntl(net->priv->socket, F_SETFL, + fcntl(net->priv->socket, F_GETFL) | O_NONBLOCK); + + if ((result = connect(net->priv->socket, tmp->ai_addr, + net->priv->addr->ai_addrlen)) == -1 && errno != EINPROGRESS) { + gm_net_connect_failed(net, strerror(errno), errno); + } else { + net->priv->channel = g_io_channel_unix_new(net->priv->socket); + g_io_channel_set_close_on_unref(net->priv->channel, TRUE); + + if (result == 0) { + gm_net_connect_succeed(net); + } else { + net->priv->connect_check_id = g_io_add_watch(net->priv->channel, + G_IO_OUT|G_IO_ERR, (GIOFunc)on_gm_net_connect_check, net); + net->priv->connect_timeout_id = g_timeout_add(5000, + (GSourceFunc)on_gm_net_connect_timeout, net); + } + } + } + } +} + +void +gm_net_handle_telnet(GmNet *net, unsigned char *buf, int *len) { + int i, j; + unsigned char c; + + j = 0; + + for (i = 0; i < *len; ++i) { + c = buf[i]; + + if (net->priv->tn_last) { + switch (net->priv->tn_last) { + case TN_WILL: case TN_WONT: case TN_DO: case TN_DONT: + net->priv->tn_last = 0; + break; + case TN_IAC: + switch (c) { + case TN_WILL: case TN_WONT: case TN_DO: case TN_DONT: + net->priv->tn_last = c; + break; + case TN_SB: + net->priv->tn_subneg = 1; + net->priv->tn_last = 0; + break; + case TN_SE: + net->priv->tn_subneg = 0; + net->priv->tn_last = 0; + break; + case TN_IAC: + if (!net->priv->tn_subneg) { + buf[j] = c; + ++j; + } + net->priv->tn_last = 0; + break; + default: + net->priv->tn_last = 0; + break; + } + } + } else if (c == TN_IAC) { + net->priv->tn_last = TN_IAC; + } else if (net->priv->tn_subneg) { + continue; + } else { + buf[j] = c; + ++j; + } + } + + *len = j; //Since j-- is the last written char +} + +/* Public */ +GmNet * +gm_net_new() { + GmNet *net = GM_NET(g_object_new(GM_TYPE_NET, NULL)); + + return net; +} + +GmNetState +gm_net_state(GmNet *net) { + return net->priv->state; +} + +void +gm_net_connect(GmNet *net, const gchar *host, const gchar *port) { + struct addrinfo hint; + int ret; + char shost[NI_MAXHOST], sport[NI_MAXSERV]; + + if (net->priv->state != GM_NET_STATE_DISCONNECTED) { + return; + } + + snprintf(shost, NI_MAXHOST - 1, "%s", host); + snprintf(sport, NI_MAXSERV - 1, "%s", port); + + net->priv->current_host = shost; + net->priv->current_port = sport; + + gm_net_set_state(net, GM_NET_STATE_CONNECTING); + + memset(&hint, 0, sizeof(hint)); + hint.ai_flags = 0; + hint.ai_family = AF_UNSPEC; + hint.ai_socktype = SOCK_STREAM; + hint.ai_protocol = IPPROTO_TCP; + + debug_msg(1, "GmNet.Connect: getaddrinfo: %s : %s", shost, sport); + + if ((ret = getaddrinfo(shost, sport, &hint, &(net->priv->addr))) != 0) { + net->priv->addr = NULL; + debug_msg(1, "GmNet.Connect: getaddrinfo failed: %s", gai_strerror(ret)); + gm_net_connect_failed(net, (gchar *)gai_strerror(ret), ret); + return; + } + + if (net->priv->addr != NULL) { + net->priv->current = net->priv->addr; + gm_net_connect_next(net); + } else { + gm_net_connect_failed(net, _("No addresses available"), 0); + } +} + +void +gm_net_disconnect(GmNet *net) { + if (net->priv->state == GM_NET_STATE_CONNECTED) { + gm_net_set_state(net, GM_NET_STATE_DISCONNECTING); + + // Remove the watch + g_source_remove(net->priv->source); + gm_net_clean_disconnection(net); + } +} + +void +gm_net_send_line(GmNet *net, gchar *line) { + gchar *send_line; + + send_line = (gchar *)(g_strconcat(line, "\r\n", NULL)); + gm_net_send(net, send_line); + g_free(send_line); +} + +void +gm_net_send(GmNet *net, gchar *text) { + int result; + fd_set connect_set; + + if (net->priv->state == GM_NET_STATE_CONNECTED) { + debug_msg(1, "GmNet.Send: %s", text); + + if ((result = send(net->priv->socket, text, strlen(text), 0)) == -1 + && (errno == EAGAIN || errno == EWOULDBLOCK)) { + FD_ZERO(&connect_set); + FD_SET(net->priv->socket, &connect_set); + + // Wait for sending to be done + select(net->priv->socket + 1, NULL, &connect_set, NULL, NULL); + } else if (result == -1) { + debug_msg(1, "GmNet.Send: error on sending line: %s", strerror(errno)); + gm_net_dirty_disconnection(net, errno); + } + } else { + g_signal_emit(net, net_signals[NET_ERROR], 0, _("Not connected"), + GM_NET_ERROR); + debug_msg(1, "GmNet.Send: not connected!"); + } +} + +const gchar * +gm_net_current_host(GmNet *net) { + return net->priv->current_host; +} + +const gchar * +gm_net_current_port(GmNet *net) { + return net->priv->current_port; +} + +/* Callbacks */ + +#define MAX_RECV_BUF 1024 + +gboolean +on_gm_net_input_recv(GIOChannel * source, GIOCondition condition, GmNet *net) { + unsigned char lbuf[MAX_RECV_BUF]; + int len; + + if (condition == G_IO_HUP) { + gm_net_set_state(net, GM_NET_STATE_DISCONNECTING); + gm_net_clean_disconnection(net); + return FALSE; + } + + if (net->priv->state != GM_NET_STATE_CONNECTED) { + debug_msg(1, "GmNet.OnInputRecv: not connected!"); + return FALSE; + } + + // Break the received line by newline (skip \r) + len = recv(net->priv->socket, lbuf, MAX_RECV_BUF - 2, 0); + + debug_msg(1, "GmNet.OnInputRecv: received %d bytes", len); + + if (len < 1) { + // Disconnected, either clean or dirty + // (shouldn't this be caught by G_IO_HUP?) + if (len < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return TRUE; + } + + gm_net_set_state(net, GM_NET_STATE_DISCONNECTING); + gm_net_dirty_disconnection(net, errno); + } else { + gm_net_set_state(net, GM_NET_STATE_DISCONNECTING); + gm_net_clean_disconnection(net); + } + + return FALSE; + } else { + // It's fine, we have text! + gm_net_handle_telnet(net, lbuf, &len); + g_signal_emit(net, net_signals[BYTES_RECV], 0, lbuf, len); + } + + return TRUE; +} + +gboolean +on_gm_net_connect_check(GIOChannel * source, GIOCondition condition, + GmNet *net) { + int option = 0; + socklen_t optionsize = sizeof(option); + + if (net->priv->connect_timeout_id != 0) { + g_source_remove(net->priv->connect_timeout_id); + net->priv->connect_timeout_id = 0; + } + + if (condition == G_IO_ERR) { + getsockopt(net->priv->socket, SOL_SOCKET, SO_ERROR, &option, &optionsize); + gm_net_connect_failed(net, strerror(option), option); + } else { + gm_net_connect_succeed(net); + } + + return FALSE; +} + +gboolean +on_gm_net_connect_timeout(GmNet *net) { + net->priv->connect_timeout_id = 0; + + if (net->priv->connect_check_id != 0) { + g_source_remove(net->priv->connect_check_id); + net->priv->connect_check_id = 0; + } + + gm_net_connect_failed(net, _("Connect timeout (5)"), 0); + return FALSE; +} diff --git a/src/gm-net.h b/src/gm-net.h new file mode 100644 index 0000000..c07c633 --- /dev/null +++ b/src/gm-net.h @@ -0,0 +1,81 @@ +#ifndef __GM_NET_H__ +#define __GM_NET_H__ + +#include +#include + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GM_TYPE_NET (gm_net_get_type()) +#define GM_NET(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + GM_TYPE_NET, GmNet)) +#define GM_NET_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + GM_TYPE_NET, GmNet const)) +#define GM_NET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \ + GM_TYPE_NET, GmNetClass)) +#define GM_IS_NET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + GM_TYPE_NET)) +#define GM_IS_NET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + GM_TYPE_NET)) +#define GM_NET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ + GM_TYPE_NET, GmNetClass)) + +typedef enum _GmNetState { + GM_NET_STATE_CONNECTED, /**< socket connected */ + GM_NET_STATE_DISCONNECTED, /**< socket disconnected */ + GM_NET_STATE_CONNECTING, /**< socket still connecting */ + GM_NET_STATE_TRY_ADDRESS, /**< connecting to address */ + GM_NET_STATE_DISCONNECTING /**< socket still disconnecting */ +} GmNetState; + +typedef enum _GmNetError { + GM_NET_ERROR_CONNECTING, /**< error while connecting */ + GM_NET_ERROR_DISCONNECTED, /**< error while connecting */ + GM_NET_ERROR /**< general error */ +} GmNetError; + +/* Private structure type */ +typedef struct _GmNetPrivate GmNetPrivate; + +/* + * Main object structure + */ +typedef struct _GmNet GmNet; + +struct _GmNet { + GObject object; + + /*< private > */ + GmNetPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GmNetClass GmNetClass; + +struct _GmNetClass { + GObjectClass parent_class; + + /* Signals */ + void (* state_changing) (GmNet *net, guint state); + void (* net_error) (GmNet *net, gchar *error, gint code); + void (* bytes_recv) (GmNet *net, gchar *text); +}; + +GType gm_net_get_type(void) G_GNUC_CONST; +GmNet *gm_net_new(void); + +GmNetState gm_net_state(GmNet *net); +void gm_net_connect(GmNet *net, const gchar *host, const gchar *port); +void gm_net_disconnect(GmNet *net); +void gm_net_send_line(GmNet *net, gchar *line); +void gm_net_send(GmNet *net, gchar *text); +const gchar *gm_net_current_host(GmNet *net); +const gchar *gm_net_current_port(GmNet *net); + +G_END_DECLS +#endif /* __GM_NET_H__ */ diff --git a/src/gm-options.c b/src/gm-options.c new file mode 100644 index 0000000..7929e40 --- /dev/null +++ b/src/gm-options.c @@ -0,0 +1,207 @@ +#include +#include +#include +#include +#include + +#include "gm-options.h" +#include "gm-string.h" +#include "debug.h" + +extern int errno; + +#define GM_OPTIONS_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_OPTIONS, GmOptionsPrivate)) + +struct _GmOptionsPrivate { + GHashTable *options; + gchar *filepath; +}; + +/* Signals */ + +enum { + OPTION_CHANGED, + NUM_SIGNALS +}; + +static guint options_signals[NUM_SIGNALS] = {0}; + +G_DEFINE_TYPE(GmOptions, gm_options, G_TYPE_OBJECT) + +static void +gm_options_finalize(GObject *object) { + GmOptions *options = GM_OPTIONS(object); + + g_hash_table_destroy(options->priv->options); + g_free(options->priv->filepath); + + G_OBJECT_CLASS(gm_options_parent_class)->finalize(object); +} + +static void +gm_options_class_init(GmOptionsClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gm_options_finalize; + + options_signals[OPTION_CHANGED] = + g_signal_new("option_changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmOptionsClass, option_changed), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + g_type_class_add_private(object_class, sizeof(GmOptionsPrivate)); +} + +static void +gm_options_init(GmOptions *options) { + options->priv = GM_OPTIONS_GET_PRIVATE(options); + options->priv->options = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_free); + options->priv->filepath = NULL; +} + +GmOptions * +gm_options_new(void) { + GmOptions *options = GM_OPTIONS(g_object_new(GM_TYPE_OPTIONS, NULL)); + + return options; +} + +void +gm_options_dup_option(gchar *key, gchar *value, GmOptions *copy) { + gm_options_set(copy, key, value); +} + +GmOptions * +gm_options_dup(GmOptions *source) { + GmOptions *copy = gm_options_new(); + + g_hash_table_foreach(source->priv->options, (GHFunc)gm_options_dup_option, + copy); + + return copy; +} + +// Adds an option to opt, even if key already exists +void +gm_options_set(GmOptions * options, const gchar *key, const gchar *value) { + gchar *trimmed = gm_string_trim(key); + gboolean changed = g_hash_table_lookup(options->priv->options, trimmed) + != NULL; + + g_hash_table_insert(options->priv->options, trimmed, g_strdup(value)); + + if (changed) { + g_signal_emit(options, options_signals[OPTION_CHANGED], 0, key); + } +} + +const gchar * +gm_options_get(GmOptions *options, const gchar *key) { + return g_hash_table_lookup(options->priv->options, key); +} + +void +gm_options_set_int(GmOptions *options, const gchar *key, int value) { + gchar val[15]; + + g_snprintf((gchar *) &val, 15, "%d", value); + gm_options_set(options, key, (const gchar *) &val); +} + +int +gm_options_get_int(GmOptions *options, const gchar *key) { + const gchar *val = gm_options_get(options, key); + int ret; + + if (val && gm_string_to_int(val, &ret)) { + return ret; + } else { + return 0; + } +} + +void +gm_options_save_value(gchar *key, gchar *value, FILE *f) { + debug_msg(1, "GmOptions.SaveValue: saving %s, %s", key, value); + fprintf(f, "%s=%s\n", key, value); +} + +void +gm_options_save(GmOptions *options) { + FILE *f; + + if (options->priv->filepath == NULL) { + return; + } + + f = fopen(options->priv->filepath, "w"); + + debug_msg(1, "GmOptions.save: saving options (%s)!", options->priv->filepath); + + if (f) { + g_hash_table_foreach(options->priv->options, + (GHFunc)gm_options_save_value, f); + + fclose(f); + chmod(options->priv->filepath, 0660); + } else { + debug_msg(1, "GmOptions.save: couldn't open option file for saving: %s", + strerror(errno)); + } +} + +void +gm_options_save_as(GmOptions *options, const gchar *filename) { + g_free(options->priv->filepath); + options->priv->filepath = g_strdup(filename); + + gm_options_save(options); +} + +void +gm_options_load(GmOptions *options, const char *filename) { + FILE *f; + gchar **keyvalue, line[1024]; + int i; + + debug_msg(1, "GmOptions.load: loading options (%s)!", filename); + + if ((f = fopen(filename, "r")) != NULL) { + i = 0; + while (fgets((char *) &line, 1024 - 1, f) != NULL) { + line[strlen((char *) &line) - 1] = '\0'; + i++; + + if (strlen(line) != 0) { // Empty lines, we don't need to process those + keyvalue = g_strsplit(line, "=", 2); + // This will return at least 1 element, at most 2, we need 2 + if (strncmp(keyvalue[0], "#", 1) != 0) { // Commented lines, well ignore them too + if (keyvalue[1] != NULL) { + debug_msg(1, "GmOptions.load: adding %s, %s", keyvalue[0], keyvalue[1]); + gm_options_set(options, keyvalue[0], keyvalue[1]); + } else { + debug_msg(1, "GmOptions.load: wrong syntax of options " + "line in %s line %d", filename, i); + } + } + + g_strfreev(keyvalue); + } + } + + fclose(f); + } else { + debug_msg(1, "GmOptions.load: could not retrieve contents of file " + "%s (%s)", filename, strerror(errno)); + } + + g_free(options->priv->filepath); + options->priv->filepath = g_strdup(filename); +} diff --git a/src/gm-options.h b/src/gm-options.h new file mode 100644 index 0000000..14c9fbc --- /dev/null +++ b/src/gm-options.h @@ -0,0 +1,69 @@ +#ifndef __GM_OPTIONS_H__ +#define __GM_OPTIONS_H__ + +#include +#include + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GM_TYPE_OPTIONS (gm_options_get_type()) +#define GM_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + GM_TYPE_OPTIONS, GmOptions)) +#define GM_OPTIONS_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + GM_TYPE_OPTIONS, GmOptions const)) +#define GM_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \ + GM_TYPE_OPTIONS, GmOptionsClass)) +#define GM_IS_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + GM_TYPE_OPTIONS)) +#define GM_IS_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + GM_TYPE_OPTIONS)) +#define GM_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ + GM_TYPE_OPTIONS, GmOptionsClass)) + +/* Private structure type */ +typedef struct _GmOptionsPrivate GmOptionsPrivate; + +/* + * Main object structure + */ +typedef struct _GmOptions GmOptions; + +struct _GmOptions { + GObject object; + + /*< private > */ + GmOptionsPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GmOptionsClass GmOptionsClass; + +struct _GmOptionsClass { + GObjectClass parent_class; + + /* Signals */ + void (* option_changed) (GmOptions *options, const gchar *key); +}; + +GType gm_options_get_type(void) G_GNUC_CONST; +GmOptions *gm_options_new(void); + +GmOptions *gm_options_dup(GmOptions *source); +void gm_options_add(GmOptions *options, const gchar *key, const gchar *value); +void gm_options_set(GmOptions *options, const gchar *key, const gchar *value); +const gchar *gm_options_get(GmOptions *options, const gchar *key); +void gm_options_set_int(GmOptions *options, const gchar *key, int value); +int gm_options_get_int(GmOptions *options, const gchar *key); + +void gm_options_save(GmOptions *options); +void gm_options_save_as(GmOptions *options, const gchar *filename); +void gm_options_load(GmOptions *options, const gchar *filename); + +G_END_DECLS + +#endif /* __GM_OPTIONS_H__ */ diff --git a/src/gm-pixbuf.c b/src/gm-pixbuf.c new file mode 100644 index 0000000..e384c32 --- /dev/null +++ b/src/gm-pixbuf.c @@ -0,0 +1,182 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include "debug.h" +#include "gm-pixbuf.h" + +static GList *gm_pixbuf_directories = NULL; +static GList *gm_pixbufs = NULL; + +GdkPixbuf *gm_pixbuf_create(const gchar * filename, int width, int height); + +void +gm_pixbuf_add_directory(const gchar *directory) { + gm_pixbuf_directories = + g_list_prepend(gm_pixbuf_directories, g_strdup(directory)); +} + +void +gm_pixbuf_init() { + gm_pixbuf_add_directory(PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps"); +} + +void +gm_pixbuf_fini() { + GList *l; + GmPixbufInfo *i; + + for (l = gm_pixbuf_directories; l; l = l->next) { + g_free(l->data); + } + + g_list_free(gm_pixbuf_directories); + + for (l = gm_pixbufs; l; l = l->next) { + i = (GmPixbufInfo *)(l->data); + g_free(i->name); + g_object_unref(i->pixbuf); + g_free(i); + } + + g_list_free(gm_pixbufs); +} + +gchar * +gm_pixbuf_find(const gchar *filename) { + GList *elem; + + if (g_file_test(filename, G_FILE_TEST_EXISTS)) { + return g_strdup(filename); + } + + for (elem = gm_pixbuf_directories; elem; elem = elem->next) { + gchar *pathname = + g_strdup_printf("%s%s%s", (gchar *) elem->data, + G_DIR_SEPARATOR_S, filename); + + if (g_file_test(pathname, G_FILE_TEST_EXISTS)) { + return pathname; + } + + g_free(pathname); + } + + return NULL; +} + +GdkPixbuf * +gm_pixbuf_create(const gchar * filename, int width, int height) { + gchar *pathname = NULL, *ext; + GdkPixbuf *pixbuf = NULL; + GError *error = NULL; + + if (!filename || strlen(filename) == 0) { + return NULL; + } + + pathname = gm_pixbuf_find(filename); + + if (!pathname) { + debug_msg(1, "gm_pixbuf_create: couldn't find pixbuf file: %s", filename); + return NULL; + } + + ext = rindex(pathname, '.'); + + if (width < 1 || height < 1) { + pixbuf = gdk_pixbuf_new_from_file(pathname, &error); + } else { + pixbuf = gdk_pixbuf_new_from_file_at_size(pathname, width, height, &error); + } + + if (!pixbuf) { + debug_msg(1, "gm_pixbuf_create: failed to load pixbuf from file: %s: %s\n", + pathname, error->message); + g_error_free(error); + error = NULL; + } + + g_free(pathname); + return pixbuf; +} + +GdkPixbuf * +gm_pixbuf_get_at_size(const gchar *filename, int width, int height) { + GdkPixbuf *pix; + GList *elem; + GmPixbufInfo *i; + + for (elem = gm_pixbufs; elem; elem = elem->next) { + i = (GmPixbufInfo *) (elem->data); + + if (strcmp(i->name, filename) == 0 && + i->width == width && i->height == height) { + return i->pixbuf; + } + } + + // Not found, so create it + pix = gm_pixbuf_create(filename, width, height); + + if (pix) { + i = g_new(GmPixbufInfo, 1); + i->name = g_strdup(filename); + i->width = width; + i->height = height; + i->pixbuf = pix; + gm_pixbufs = g_list_append(gm_pixbufs, i); + return pix; + } else { + return NULL; + } +} + +// Returns pixbuf if it is already loaded in pixmaps +GdkPixbuf * +gm_pixbuf_get(const gchar *filename) { + return gm_pixbuf_get_at_size(filename, -1, -1); +} + +void gm_pixbuf_set_alpha(GdkPixbuf **pixs, guchar alpha) { + int width, height, n_channels, rowstride, y, x; + GdkPixbuf *pix; + guchar *pixels, *p; + + if (!gdk_pixbuf_get_has_alpha(*pixs)) { + pix = gdk_pixbuf_add_alpha(*pixs, FALSE, 0, 0, 0); + gdk_pixbuf_unref(*pixs); + } else { + pix = *pixs; + } + + n_channels = gdk_pixbuf_get_n_channels(pix); + + if (gdk_pixbuf_get_colorspace(pix) != GDK_COLORSPACE_RGB || + gdk_pixbuf_get_bits_per_sample(pix) != 8 || + !gdk_pixbuf_get_has_alpha(pix) || + n_channels != 4) { + *pixs = pix; + return; + } + + width = gdk_pixbuf_get_width(pix); + height = gdk_pixbuf_get_height(pix); + rowstride = gdk_pixbuf_get_rowstride(pix); + pixels = gdk_pixbuf_get_pixels(pix); + p = pixels; + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + p[3] = alpha; + p = p + n_channels; + } + } + + *pixs = pix; +} diff --git a/src/gm-pixbuf.h b/src/gm-pixbuf.h new file mode 100644 index 0000000..6aed34f --- /dev/null +++ b/src/gm-pixbuf.h @@ -0,0 +1,24 @@ +#ifndef gm_pixbuf_H +#define gm_pixbuf_H 1 + +#include +#include + +typedef struct _GmPixbufInfo { + gchar *name; + int width; + int height; + GdkPixbuf *pixbuf; +} GmPixbufInfo; + +void gm_pixbuf_init(); +void gm_pixbuf_fini(); + +void gm_pixbuf_add_directory(const gchar *directory); + +GdkPixbuf *gm_pixbuf_get(const gchar *filename); +GdkPixbuf *gm_pixbuf_get_at_size(const gchar *filename, int width, int height); + +void gm_pixbuf_set_alpha(GdkPixbuf **pixs, guchar alpha); + +#endif diff --git a/src/gm-script.c.old b/src/gm-script.c.old new file mode 100644 index 0000000..48f4dc9 --- /dev/null +++ b/src/gm-script.c.old @@ -0,0 +1,663 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HASRUBY +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "gm-world.h" +#include "gm-app.h" +#include "gm-script.h" +#include "gm-scripts-dialog.h" +#include "gm-net.h" +#include "gm-support.h" + +#define RB_CALLBACK(x) (VALUE (*)())(x) + +static VALUE script_world_name(VALUE self); +VALUE script_world_create(world *wld); +void script_world_class_init(); +void script_client_class_init(); + +static VALUE cWorld, cClient; +static gboolean didInit = FALSE; +static GList *funcs = NULL; +static GList *scriptFiles = NULL; +static gchar *fileInsp; +static GList *mondirs = NULL; + +void gm_script_monitor_cb(GnomeVFSMonitorHandle *handle, + const gchar *monitor_uri, const gchar *info_uri, + GnomeVFSMonitorEventType event_type, + gpointer user_data) { + gchar *filename = gnome_vfs_get_local_path_from_uri(info_uri); + + switch (event_type) { + case GNOME_VFS_MONITOR_EVENT_CHANGED: + gm_script_reload_file(filename); + break; + case GNOME_VFS_MONITOR_EVENT_DELETED: + gm_script_remove_file(filename); + break; + case GNOME_VFS_MONITOR_EVENT_CREATED: + gm_script_add_file(filename); + break; + default: + g_free(filename); + return; + break; + } + + if_scripts_update(); + g_free(filename); +} + + void + script_destroy_function(script_function *fi) { + g_free(fi->name); + g_free(fi->fname); + g_free(fi->filename); + g_free(fi->description); + g_free(fi); + } + + script_function * + script_find(gchar *name) { + GList *func; + script_function *f; + + for (func = funcs; func; func = func->next) { + f = (script_function *)func->data; + + if (strcasecmp(f->name, name) == 0) { + return f; + } + } + + return NULL; + } + + gboolean + script_add(gchar *name, gchar *fname, gchar *description, gchar *filename) { + script_function *func; + gchar *basedir = PACKAGE_DATA_DIR "/" PACKAGE "/scripts"; + + if (script_find(name) == NULL) { + func = g_new(script_function, 1); + + if (strncmp(filename, basedir, strlen(basedir)) == 0) { + func->type = ST_SHARE; + } else { + func->type = ST_USER; + } + + func->name = g_strdup(name); + func->fname = g_strdup(fname); + func->filename = g_strdup(filename); + func->description = g_strdup(description); + + funcs = g_list_append(funcs, func); + return TRUE; + } else { + return FALSE; + } + } + + VALUE + register_func(int argc, VALUE *argv, VALUE self) { + char *name, *fname = NULL, *description = NULL; + gchar *msg; + + if (argc > 1) { + name = rb_string_value_cstr(&argv[0]); + description = rb_string_value_cstr(&argv[1]); + + if (argc == 2) { + fname = name; + } else { + fname = rb_string_value_cstr(&argv[2]); + } + + if (script_add(name, fname, description, fileInsp)) { + msg = g_strdup_printf(_("Register function '%s' from '%s'"), name, fileInsp); + debug_msg(1, "register_func: adding script function %s from %s", name, fileInsp); + if_scripts_add(SCRIPTC_MSG, msg); + g_free(msg); + return Qtrue; + } else { + msg = g_strdup_printf(_("Script '%s' is already defined"), name); + debug_msg(1, "register_func: script function %s already defined!", name); + if_scripts_add(SCRIPTC_ERROR, msg); + g_free(msg); + return Qfalse; + } + } else { + return Qfalse; + } + } + + VALUE + register_functions_wrap(VALUE arg) { + return rb_eval_string("register_functions"); + } + + void + script_define_world(VALUE *wld) { + rb_define_variable("$world", wld); + } + + void + script_error() { + int c; + VALUE lasterr; + char *err; + gchar *msg; + VALUE ary; + + if(!NIL_P(ruby_errinfo)) { + + lasterr = rb_gv_get("$!"); + err = RSTRING(rb_obj_as_string(lasterr))->ptr; + + debug_msg(1, "script_error: Error while executing Ruby code: %s", err); + + msg = g_strdup_printf(_("Error on executing: %s"), err); + if_scripts_add(SCRIPTC_ERROR, msg); + g_free(msg); + + ary = rb_funcall(ruby_errinfo, rb_intern("backtrace"), 0); + debug_msg(1, "script_error: Ruby backtrace:"); + if_scripts_add(SCRIPTC_ERROR, _("Ruby backtrace:")); + + for (c = 0; c < RARRAY(ary)->len; c++) { + debug_msg(1, "script_error: \tfrom %s", RSTRING(RARRAY(ary)->ptr[c])->ptr); + + msg = g_strdup_printf(_("\tfrom %s"), RSTRING(RARRAY(ary)->ptr[c])->ptr); + if_scripts_add(SCRIPTC_ERROR, msg); + g_free(msg); + } + } + } + + int + do_ruby(VALUE (*body)(), VALUE arg) { + int status; + + rb_protect(body, arg, &status); + + if (status != 0) { + script_error(); + ruby_cleanup(status); + return 0; + } + + return 1; + } + + VALUE + function_run(script_info *arg) { + VALUE ret; + gchar *argstr; + gchar *funcAll; + + if (arg->argstr) { + argstr = str_escape(arg->argstr); + funcAll = g_strconcat(arg->name, "(\"", argstr, "\")", NULL); + g_free(argstr); + } else { + funcAll = g_strconcat(arg->name, "()", NULL); + } + + ret = rb_eval_string(funcAll); + g_free(funcAll); + + return ret; + } + + gboolean + script_run(world *wld, script_function *f, gchar *argstr) { + VALUE rWld; + VALUE rClient; + gchar *msg; + script_info *info = g_new(script_info, 1); + + info->name = g_strdup(f->fname); + + if (argstr) { + info->argstr = g_strdup(argstr); + msg = g_strdup_printf(_("Run script '%s' from '%s' (%s)"), f->fname, + f->filename, argstr); + } else { + info->argstr = NULL; + msg = g_strdup_printf(_("Run script '%s' from '%s' ()"), f->fname, + f->filename); + } + + if_scripts_add(SCRIPTC_RUN, msg); + g_free(msg); + + do_ruby(RB_CALLBACK(rb_load_file), (VALUE) f->filename); + ruby_exec(); + + rWld = script_world_create(wld); + rb_define_variable("$world", &rWld); + + rClient = rb_class_new_instance(0, NULL, cClient); + rb_define_variable("$client", &rClient); + + do_ruby(RB_CALLBACK(function_run), (VALUE) info); + + g_free(info->name); + + if (info->argstr) { + g_free(info->argstr); + } + + g_free(info); + + return TRUE; + } + + GList *script_get_scripts() { + return funcs; + } + + GList *script_get_files() { + return scriptFiles; + } + + void + script_register_functions(gchar *filename) { + gchar *msg; + + fileInsp = filename; + debug_msg(1, "script_register_functions: registering functions in %s", filename); + + msg = g_strdup_printf(_("Registering functions from '%s'"), filename); + if_scripts_add(SCRIPTC_MSG, msg); + g_free(msg); + + if (!do_ruby(RB_CALLBACK(rb_load_file), (VALUE) filename)) { + return; + } + + ruby_exec(); + + rb_define_global_function("register_func", ®ister_func, -1); + + do_ruby(RB_CALLBACK(register_functions_wrap), 0); + } + + void + script_remove_file(const gchar *uri) { + GList *f, *l; + script_function *func; + gchar *msg; + + l = g_list_copy(scriptFiles); + + for (f = l; f; f = f->next) { + if (strcmp(f->data, uri) == 0) { + scriptFiles = g_list_remove(scriptFiles, f->data); + g_free(f->data); + } + } + + g_list_free(l); + + l = g_list_copy(funcs); + + for (f = l; f; f = f->next) { + func = (script_function *)(f->data); + + if (strcmp(func->filename, uri) == 0) { + msg = g_strdup_printf(_("Removing function `%s' from `%s'!"), func->name, + func->filename); + debug_msg(1, "script_remove_file: %s", msg); + if_scripts_add(SCRIPTC_MSG, msg); + g_free(msg); + funcs = g_list_remove(funcs, func); + script_destroy_function(func); + } + } + + g_list_free(l); + } + + gboolean + script_add_file(const gchar *uri) { + GList *f; + gchar *msg; + + for (f = scriptFiles; f; f = f->next) { + if (strcmp(f->data, uri) == 0) { + msg = g_strdup_printf(_("File `%s' already added!"), uri); + debug_msg(1, "script_add_file: %s", msg); + if_scripts_add(SCRIPTC_MSG, msg); + g_free(msg); + return FALSE; + } + } + + msg = g_strdup_printf(_("File `%s' added!"), uri); + debug_msg(1, "script_add_file: %s", msg); + if_scripts_add(SCRIPTC_MSG, msg); + g_free(msg); + scriptFiles = g_list_append(scriptFiles, strdup(uri)); + script_register_functions((gchar *)uri); + + return TRUE; + } + + void + script_reload_file(const gchar *uri) { + script_remove_file(uri); + script_add_file(uri); + } + + void + script_check_dir(gchar *dirname) { + gchar *filename; + gchar *file; + GDir *d; + GnomeVFSMonitorHandle *handle; + + if (g_file_test(dirname, G_FILE_TEST_EXISTS) && + g_file_test(dirname, G_FILE_TEST_IS_DIR)) { + + if ((d = g_dir_open(dirname, 0, NULL))) { + while ((file = (gchar *)g_dir_read_name(d))) { + filename = g_strconcat(dirname, "/", file, NULL); + script_add_file(filename); + g_free(filename); + } + } + } else if (!g_file_test(dirname, G_FILE_TEST_EXISTS)) { + mkdir(dirname, 0750); + } + + if (g_file_test(dirname, G_FILE_TEST_EXISTS)) { + + gnome_vfs_monitor_add(&handle, dirname, GNOME_VFS_MONITOR_DIRECTORY, + script_monitor_cb, NULL); + + mondirs = g_list_append(mondirs, handle); + } + } + + void + script_init() { + gchar *path = g_strconcat(main_get_app_path(), "/scripts", NULL); + + ruby_init(); + + if (!didInit) { + script_world_class_init(); + script_client_class_init(); + } + + script_check_dir(path); + script_check_dir(PACKAGE_DATA_DIR "/" PACKAGE "/scripts"); + + g_free(path); + + didInit = TRUE; + } + + void + script_fini() { + GList *f; + script_function *fi; + gchar *filename; + + for (f = funcs; f; f = f->next) { + fi = (script_function *)f->data; + script_destroy_function(fi); + } + + g_list_free(funcs); + funcs = NULL; + + for (f = scriptFiles; f; f = f->next) { + filename = (gchar *)(f->data); + g_free(filename); + } + + g_list_free(scriptFiles); + scriptFiles = NULL; + + for (f = mondirs; f; f = f->next) { + gnome_vfs_monitor_cancel((GnomeVFSMonitorHandle *)f->data); + } + + g_list_free(mondirs); + mondirs = NULL; + + ruby_finalize(); + } + + void + script_flush_and_reload() { + if_scripts_add(SCRIPTC_MSG, _("Flushing scripts...")); + script_fini(); + + if_scripts_add(SCRIPTC_MSG, _("Reloading scripts...")); + script_init(); + } + + /* CLASS FUNCTIONS */ + + static VALUE + script_world_name(VALUE self) { + world *wld; + + Data_Get_Struct(self, world, wld); + + return rb_str_new2(options_get_str(wld->settings, "name")); + } + + static VALUE + script_world_host(VALUE self) { + world *wld; + + Data_Get_Struct(self, world, wld); + + return rb_str_new2(options_get_str(wld->settings, "host")); + } + + static VALUE + script_world_port(VALUE self) { + world *wld; + + Data_Get_Struct(self, world, wld); + + return rb_str_new2(options_get_str(wld->settings, "port")); + } + + static VALUE + script_world_writeln(VALUE self, VALUE str) { + world *wld; + gchar *strVal; + + Data_Get_Struct(self, world, wld); + strVal = rb_string_value_cstr(&str); + + world_writeln(wld, strVal); + + return Qnil; + } + + static VALUE + script_world_sendln(VALUE self, VALUE str) { + world *wld; + gchar *strVal; + + Data_Get_Struct(self, world, wld); + strVal = rb_string_value_cstr(&str); + + net_send_line(wld, (unsigned char *)strVal); + + return Qnil; + } + + static VALUE + script_world_input(VALUE self, VALUE str) { + world *wld; + gchar *strVal; + + Data_Get_Struct(self, world, wld); + strVal = rb_string_value_cstr(&str); + + world_process_input(wld, strVal); + + return Qnil; + } + + static VALUE + script_world_loaded(VALUE self) { + world *wld; + + Data_Get_Struct(self, world, wld); + + if (wld->loaded) { + return Qtrue; + } else { + return Qfalse; + } + } + + static VALUE + script_world_connected(VALUE self) { + world *wld; + + Data_Get_Struct(self, world, wld); + + if (wld->net.state == NET_CONNECTED) { + return Qtrue; + } else { + return Qfalse; + } + } + + static VALUE + script_world_quit(VALUE self) { + world *wld; + + Data_Get_Struct(self, world, wld); + + world_unload(wld); + return Qnil; + } + + static VALUE + script_world_connect(int argc, VALUE *argv, VALUE self) { + world *wld; + gchar *strHost, *strPort; + + Data_Get_Struct(self, world, wld); + + if (argc == 0) { + strHost = options_get_str(wld->settings, "host"); + } else { + strHost = rb_string_value_cstr(&(argv[0])); + } + + if (argc == 0 || argc == 1) { + strPort = options_get_str(wld->settings, "port"); + } else { + strPort = rb_string_value_cstr(&(argv[1])); + } + + net_connect_to(wld, strHost, strPort); + + return Qnil; + } + + static VALUE + script_world_disconnect(VALUE self) { + world *wld; + + Data_Get_Struct(self, world, wld); + + net_do_disconnect(wld); + return Qnil; + } + + void + script_world_class_init() { + cWorld = rb_define_class("World", rb_cObject); + + rb_define_method(cWorld, "name", script_world_name, 0); + rb_define_method(cWorld, "host", script_world_host, 0); + rb_define_method(cWorld, "port", script_world_port, 0); + + rb_define_method(cWorld, "writeln", script_world_writeln, 1); + rb_define_method(cWorld, "println", script_world_writeln, 1); + rb_define_method(cWorld, "sendln", script_world_sendln, 1); + rb_define_method(cWorld, "input", script_world_input, 1); + rb_define_method(cWorld, "quit", script_world_quit, 0); + rb_define_method(cWorld, "connect", script_world_connect, -1); + rb_define_method(cWorld, "disconnect", script_world_disconnect, 0); + rb_define_method(cWorld, "loaded?", script_world_loaded, 0); + rb_define_method(cWorld, "connected?", script_world_connected, 0); + } + + VALUE + script_world_create(world *wld) { + VALUE tData = Data_Wrap_Struct(cWorld, 0, 0, wld); + + return tData; + } + + + static VALUE + script_client_version(VALUE self) { + return rb_str_new2(VERSION); + } + + static VALUE + script_client_worlds(VALUE self) { + GList *wld; + VALUE ar = rb_ary_new(); + VALUE oWld; + + for (wld = world_get_worlds(); wld; wld = wld->next) { + oWld = script_world_create((world *)(wld->data)); + rb_ary_push(ar, oWld); + } + + return ar; + } + + static VALUE + script_client_open(VALUE self, VALUE str) { + world *wld; + gchar *strVal; + + strVal = rb_string_value_cstr(&str); + + wld = world_get_by_name(strVal); + + if (wld == NULL) { + return Qfalse; + } else { + world_load(wld); + return Qtrue; + } + } + + void + script_client_class_init() { + cClient = rb_define_class("Client", rb_cObject); + + rb_define_method(cClient, "version", script_client_version, 0); + rb_define_method(cClient, "worlds", script_client_worlds, 0); + rb_define_method(cClient, "open", script_client_open, 1); + } +#endif diff --git a/src/gm-script.h.old b/src/gm-script.h.old new file mode 100644 index 0000000..dc8cb89 --- /dev/null +++ b/src/gm-script.h.old @@ -0,0 +1,44 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HASRUBY + #ifndef __GM_SCRIPT_H__ + #define __GM_SCRIPT_H__ + +#include +#include "gm-world.h" + + enum GmScriptType { + ST_USER, + ST_SHARE + }; + + typedef struct _GmScriptFunction { + enum scriptType type; + gchar *name; + gchar *fname; + gchar *filename; + gchar *description; + } GmScriptFunction; + + typedef struct _GmScriptInfo { + gchar *name; + gchar *argstr; + } GmScriptInfo; + + void gm_script_init(); + void gm_script_fini(); + void gm_script_flush_and_reload(); + void gm_script_reload_file(const gchar *uri); + gboolean gm_script_add_file(const gchar *uri); + void gm_script_remove_file(const gchar *uri); + + GmScriptFunction *gm_script_find(gchar *name); + gboolean gm_script_run(GmWorld *world, GmScriptFunction *f, gchar *argstr); + + GList *script_get_scripts(); + GList *script_get_files(); + + #endif /* __GM_SCRIPT_H__ */ +#endif diff --git a/src/gm-scripts-dialog.c b/src/gm-scripts-dialog.c new file mode 100644 index 0000000..7a4041e --- /dev/null +++ b/src/gm-scripts-dialog.c @@ -0,0 +1,844 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_RUBY +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "gm-scripts.h" +#include "gm-world.h" +#include "debug.h" +#include "gm-app.h" +#include "gm-support.h" +#include "gm-app-view.h" + +void on_gm_scripts_dialog_script_added(GmScripts *scripts, GmScript *script, + gpointer user_data); +void on_gm_scripts_dialog_script_changed(GmScripts *scripts, GmScript *script, + gpointer user_data); +void on_gm_scripts_dialog_script_removed(GmScripts *scripts, GmScript *script, + gpointer user_data); +void on_gm_scripts_dialog_message(GmScripts *scripts, gchar *message, + gpointer user_data); +void on_gm_scripts_dialog_error(GmScripts *scripts, gchar *message, + gpointer user_data); +void on_gm_scripts_dialog_run(GmScripts *scripts, gchar *message, + gpointer user_data); + +typedef enum _MessageType { + SCRIPT_MESSAGE, + SCRIPT_ERROR, + SCRIPT_RUN +} MessageType; + +typedef enum _scripts_columns { + SCRIPTS_NAME, + SCRIPTS_OBJECT, + SCRIPTS_N +} scripts_columns; + +typedef struct _SelectionInfo { + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeIter seliter; + GtkTreeIter parent; + gboolean has_parent; + gboolean can_save; + gchar *filename; +} SelectionInfo; + +typedef struct _GmScriptsDialog { + GtkTextTagTable *tag_table; + GladeXML *xml; + GtkTextBuffer *text_buffer_console; + gchar *current_edit; + GtkTextBuffer *text_buffer_editor; + + GtkWidget *dialog; + GtkWidget *text_view_console; + GtkWidget *statusbar_editor; + GtkWidget *button_save; + GtkWidget *tree_view_scripts; + GtkWidget *tree_view_files; + GtkWidget *button_delete; + + SelectionInfo info; +} GmScriptsDialog; + +static GmScriptsDialog *scripts_dialog = NULL; + +#define G_SCRIPTS_XML PACKAGE_DATA_DIR "/" PACKAGE "/ui/gm-scripts.glade" + +gboolean on_gm_scripts_dialog_delete(GtkWidget *widget, GdkEvent *event, + gpointer user_data); + +void on_files_changed(GtkTreeSelection *treeselection, gpointer user_data); +void on_tree_view_files_row_activated(GtkTreeView *treeview, GtkTreePath *arg1, + GtkTreeViewColumn *arg2, gpointer user_data); +void on_text_buffer_editor_modified_changed(GtkTextBuffer *buffer, + gpointer user_data); + +void on_tool_button_new_clicked(GtkToolButton *button, gpointer user_data); +void on_tool_button_save_clicked(GtkToolButton *button, gpointer user_data); +void on_tool_button_save_as_clicked(GtkToolButton *button, gpointer user_data); +void on_tool_button_delete_clicked(GtkToolButton *button, gpointer user_data); + +gboolean +gm_scripts_dialog_can_write(gchar *filename) { + return (access(filename, W_OK) == 0); +} + +void +gm_scripts_dialog_set_status(gchar *msg) { + gtk_statusbar_pop(GTK_STATUSBAR(scripts_dialog->statusbar_editor), + 0); + gtk_statusbar_push(GTK_STATUSBAR(scripts_dialog->statusbar_editor), + 0, msg); +} + +gboolean +gm_scripts_dialog_editor_save(gchar *filename) { + FILE *f; + gchar *msg, *text; + GtkTextIter start, end; + + if (!filename) { + return FALSE; + } + + f = fopen(filename, "w"); + + if (f) { + gtk_text_buffer_get_start_iter(scripts_dialog->text_buffer_editor, + &start); + gtk_text_buffer_get_end_iter(scripts_dialog->text_buffer_editor, &end); + text = gtk_text_buffer_get_text(scripts_dialog->text_buffer_editor, + &start, &end, TRUE); + + fputs(text, f); + + msg = g_strconcat(_("Saved "), filename, NULL); + gm_scripts_dialog_set_status(msg); + g_free(msg); + + if (scripts_dialog->current_edit && + filename != scripts_dialog->current_edit) { + g_free(scripts_dialog->current_edit); + scripts_dialog->current_edit = g_strdup(filename); + } + + fclose(f); + return TRUE; + } else { + msg = g_strconcat(_("Saving failed: "), strerror(errno), NULL); + gm_error_dialog(msg, GTK_WINDOW(scripts_dialog->dialog)); + g_free(msg); + + return FALSE; + } +} + +void +gm_scripts_dialog_editor_load(SelectionInfo *info) { + FILE *f; + gchar line[1024]; + GtkTextIter end; + gchar *lline, *msg; + + gtk_text_buffer_set_text(scripts_dialog->text_buffer_editor, "", 0); + gtk_text_buffer_get_end_iter(scripts_dialog->text_buffer_editor, &end); + + if (info) { + f = fopen(info->filename, "r"); + + if (f) { + while (fgets((char *) &line, 1024 - 1, f) != NULL) { + lline = g_locale_to_utf8((gchar *)&line, strlen((char *)&line), + NULL, NULL, NULL); + + if (lline) { + gtk_text_buffer_insert(scripts_dialog->text_buffer_editor, + &end, lline, strlen(lline)); + g_free(lline); + } else { + gtk_text_buffer_insert(scripts_dialog->text_buffer_editor, + &end, (gchar *)&line, strlen((char *)&line)); + } + } + + fclose(f); + + gtk_widget_set_sensitive(scripts_dialog->button_save, + info->can_save); + + g_free(scripts_dialog->current_edit); + scripts_dialog->current_edit = g_strdup(info->filename); + + msg = g_strconcat(_("Loaded "), info->filename, NULL); + gm_scripts_dialog_set_status(msg); + g_free(msg); + } else { + debug_msg(1, "GmScript.EditorLoad: file (%s) could not be read: %s", + scripts_dialog->current_edit, strerror(errno)); + } + } else { + g_free(scripts_dialog->current_edit); + scripts_dialog->current_edit = NULL; + + gm_scripts_dialog_set_status(_("New ")); + } +} + +void +gm_scripts_dialog_init_console() { + GtkTextView *txt = GTK_TEXT_VIEW(scripts_dialog->text_view_console); + PangoFontDescription *f; + + gtk_text_view_set_buffer(txt, scripts_dialog->text_buffer_console); + gtk_text_view_set_editable(txt, FALSE); + + f = + pango_font_description_from_string("monospace 8"); + + if (f) { + gtk_widget_modify_font(GTK_WIDGET(txt), f); + pango_font_description_free(f); + } +} + +void +gm_scripts_dialog_tree_add_function(GtkTreeStore *model, GtkTreeIter *parent, + GmScriptFunction *fi) { + GtkTreeIter item; + gchar *name, *description, *all, *markup; + + gtk_tree_store_append(model, &item, parent); + name = g_filename_to_utf8(fi->name, strlen(fi->name), NULL, + NULL, NULL); + description = g_locale_to_utf8(fi->description, strlen(fi->description), + NULL, NULL, NULL); + + markup = g_markup_escape_text(description, g_utf8_strlen(description, -1)); + all = g_strconcat("", name, "\n", markup, NULL); + gtk_tree_store_set(model, &item, SCRIPTS_NAME, all, -1); + + g_free(all); + g_free(markup); + g_free(description); + g_free(name); +} + +void +gm_scripts_dialog_tree_update_script_item(GmScript *script, GtkTreeIter *iter) { + GList *list; + GtkTreeStore *model = GTK_TREE_STORE(gtk_tree_view_get_model( + GTK_TREE_VIEW(scripts_dialog->tree_view_scripts))); + GtkTreeIter item; + + if (gtk_tree_model_iter_children(GTK_TREE_MODEL(model), &item, iter)) { + while (gtk_tree_store_remove(model, &item)); + } + + for (list = script->functions; list; list = list->next) { + gm_scripts_dialog_tree_add_function(model, iter, + (GmScriptFunction *)(list->data)); + } +} +void +gm_scripts_dialog_tree_update_script(GmScript *script) { + GtkTreeModel *model = gtk_tree_view_get_model( + GTK_TREE_VIEW(scripts_dialog->tree_view_scripts)); + GtkTreeIter item; + GmScript *obj; + + if (gtk_tree_model_iter_children(model, &item, NULL)) { + do { + gtk_tree_model_get(model, &item, SCRIPTS_OBJECT, + &obj, -1); + + if (obj == script) { + gm_scripts_dialog_tree_update_script_item(script, &item); + break; + } + } while (gtk_tree_model_iter_next(model, &item)); + } +} + +void +gm_scripts_dialog_tree_add_script(GmScript *script) { + GtkTreeStore *model = GTK_TREE_STORE(gtk_tree_view_get_model( + GTK_TREE_VIEW(scripts_dialog->tree_view_scripts))); + GtkTreeIter item; + gchar *name, *base; + + gtk_tree_store_append(model, &item, NULL); + name = g_filename_to_utf8(script->filename, strlen(script->filename), NULL, + NULL, NULL); + base = g_path_get_basename(name); + g_free(name); + + if (script->type == GM_SCRIPT_TYPE_USER) { + name = g_strconcat(_("User: "), base, NULL); + } else { + name = g_strconcat(_("Share: "), base, NULL); + } + + g_free(base); + gtk_tree_store_set(model, &item, SCRIPTS_NAME, name, SCRIPTS_OBJECT, + script, -1); + g_free(name); + + gm_scripts_dialog_tree_update_script_item(script, &item); +} + +void +gm_scripts_dialog_fill_tree() { + GtkTreeView *view = GTK_TREE_VIEW(scripts_dialog->tree_view_scripts); + GtkTreeStore *model = GTK_TREE_STORE(gtk_tree_view_get_model(view)); + GList *scripts; + + gtk_tree_store_clear(model); + + for (scripts = gm_scripts_scripts(gm_app_scripts(gm_app_instance())); + scripts; scripts = scripts->next) { + gm_scripts_dialog_tree_add_script((GmScript *)(scripts->data)); + } +} + +void +gm_scripts_dialog_init_tree() { + GtkTreeView *view = GTK_TREE_VIEW(scripts_dialog->tree_view_scripts); + GtkTreeStore *model = gtk_tree_store_new(SCRIPTS_N, G_TYPE_STRING, + G_TYPE_POINTER); + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Scripts"), renderer, + "markup", SCRIPTS_NAME, NULL); + + gtk_tree_view_append_column(view, column); + gtk_tree_view_set_model(view, GTK_TREE_MODEL(model)); + + gm_scripts_dialog_fill_tree(); +} + +gboolean +gm_scripts_dialog_add_file(gchar *filename) { + GtkTreeView *view = GTK_TREE_VIEW(scripts_dialog->tree_view_files); + GtkTreeStore *model = GTK_TREE_STORE(gtk_tree_view_get_model(view)); + GtkTreeIter share, home, parent, item; + gchar *sharedir = g_strdup(PACKAGE_DATA_DIR "/" PACKAGE "/scripts"); + gchar *homedir = g_strconcat(gm_app_path(gm_app_instance()), "/scripts", + NULL); + gchar *name; + + gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(model), &share, "0"); + gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(model), &home, "1"); + + name = g_filename_to_utf8(filename, strlen(filename), NULL, + NULL, NULL); + + if (strncmp(sharedir, name, strlen(sharedir)) == 0) { + parent = share; + } else if (strncmp(homedir, name, strlen(homedir)) == 0) { + parent = home; + } else { + g_free(sharedir); + g_free(homedir); + g_free(name); + return FALSE; + } + + gtk_tree_store_append(model, &item, &parent); + gtk_tree_store_set(model, &item, SCRIPTS_NAME, g_strrstr(name, "/") + 1, + -1); + + g_free(name); + g_free(sharedir); + g_free(homedir); + + return TRUE; +} + +void +gm_scripts_dialog_fill_files() { + GtkTreeView *view = GTK_TREE_VIEW(scripts_dialog->tree_view_files); + GtkTreeStore *model = GTK_TREE_STORE(gtk_tree_view_get_model(view)); + GtkTreeIter item, share, home, parent; + GtkTreePath *path; + gchar *sharedir; + gchar *name; + GList *scripts; + GmScript *script; + + sharedir = g_strdup(PACKAGE_DATA_DIR "/" PACKAGE "/scripts"); + + gtk_tree_store_clear(model); + + gtk_tree_store_append(model, &share, NULL); + gtk_tree_store_set(model, &share, SCRIPTS_NAME, _("Share"), -1); + + gtk_tree_store_append(model, &home, NULL); + gtk_tree_store_set(model, &home, SCRIPTS_NAME, _("User"), -1); + + for (scripts = gm_scripts_scripts(gm_app_scripts(gm_app_instance())); + scripts; scripts = scripts->next) { + script = (GmScript *)(scripts->data); + + name = g_filename_to_utf8(script->filename, strlen(script->filename), + NULL, NULL, NULL); + + if (strncmp(sharedir, name, strlen(sharedir)) == 0) { + parent = share; + } else { + parent = home; + } + + gtk_tree_store_append(model, &item, &parent); + gtk_tree_store_set(model, &item, SCRIPTS_NAME, + g_strrstr(name, "/") + 1, -1); + + g_free(name); + } + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(model), &home); + gtk_tree_view_expand_to_path(view, path); + gtk_tree_path_free(path); + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(model), &share); + gtk_tree_view_expand_to_path(view, path); + gtk_tree_path_free(path); + + g_free(sharedir); +} + +void +gm_scripts_dialog_init_files() { + GtkTreeView *view = GTK_TREE_VIEW(scripts_dialog->tree_view_files); + GtkTreeStore *model = gtk_tree_store_new(SCRIPTS_N, G_TYPE_STRING, + G_TYPE_POINTER); + GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); + GtkTreeSelection *selection; + GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes( + _("Filename"), renderer, "text", SCRIPTS_NAME, NULL); + + gtk_tree_view_append_column(view, column); + gtk_tree_view_set_model(view, GTK_TREE_MODEL(model)); + + selection = gtk_tree_view_get_selection(view); + + g_signal_connect((gpointer)selection, "changed", + G_CALLBACK(on_files_changed), NULL); + gm_scripts_dialog_fill_files(); +} + +GtkWidget * +gm_scripts_dialog_init_editor() { + GtkSourceLanguagesManager *lm = gtk_source_languages_manager_new(); + GtkSourceLanguage *lang; + GtkWidget *view; + PangoFontDescription *f; + + lang = gtk_source_languages_manager_get_language_from_mime_type(lm, + "application/x-ruby"); + + view = gtk_source_view_new(); + scripts_dialog->text_buffer_editor = gtk_text_view_get_buffer( + GTK_TEXT_VIEW(view)); + + g_signal_connect((gpointer)scripts_dialog->text_buffer_editor, + "modified-changed", + G_CALLBACK(on_text_buffer_editor_modified_changed), NULL); + + if (lang) { + gtk_source_buffer_set_language( + GTK_SOURCE_BUFFER(scripts_dialog->text_buffer_editor), lang); + } + + f = pango_font_description_from_string(gm_options_get(gm_app_options( + gm_app_instance()), "font-family")); + gtk_widget_modify_font(view, f); + + gtk_source_view_set_insert_spaces_instead_of_tabs(GTK_SOURCE_VIEW(view), + TRUE); + gtk_source_view_set_auto_indent(GTK_SOURCE_VIEW(view), TRUE); + gtk_source_view_set_show_line_numbers(GTK_SOURCE_VIEW(view), TRUE); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD_CHAR); + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view), 6); + gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view), 6); + + gtk_source_buffer_set_highlight(GTK_SOURCE_BUFFER( + scripts_dialog->text_buffer_editor), TRUE); + gtk_widget_show(view); + + return view; +} + +GtkTextTag * +gm_scripts_dialog_create_tag(gchar *name, gchar *fg) { + GtkTextTag *tag; + GdkColor col; + + gdk_color_parse(fg, &col); + tag = gtk_text_tag_new(name); + g_object_set(G_OBJECT(tag), "foreground-gdk", &col, NULL); + + return tag; +} + +void +gm_scripts_dialog_init() { + GmScripts *scripts = gm_app_scripts(gm_app_instance()); + + scripts_dialog = g_new0(GmScriptsDialog, 1); + scripts_dialog->tag_table = g_object_ref(gtk_text_tag_table_new()); + scripts_dialog->dialog = NULL; + + gtk_text_tag_table_add(scripts_dialog->tag_table, + gm_scripts_dialog_create_tag("scripts-run", "#445632")); + gtk_text_tag_table_add(scripts_dialog->tag_table, + gm_scripts_dialog_create_tag("scripts-msg", "#314E6C")); + gtk_text_tag_table_add(scripts_dialog->tag_table, + gm_scripts_dialog_create_tag("scripts-error", "#663822")); + + scripts_dialog->text_buffer_console = + g_object_ref(gtk_text_buffer_new(scripts_dialog->tag_table)); + + // Attach signals to GmScripts object to know about changes + g_signal_connect(scripts, "script_added", + G_CALLBACK(on_gm_scripts_dialog_script_added), NULL); + g_signal_connect(scripts, "script_changed", + G_CALLBACK(on_gm_scripts_dialog_script_changed), NULL); + g_signal_connect(scripts, "script_removed", + G_CALLBACK(on_gm_scripts_dialog_script_removed), NULL); + g_signal_connect(scripts, "message", + G_CALLBACK(on_gm_scripts_dialog_message), NULL); + g_signal_connect(scripts, "error", + G_CALLBACK(on_gm_scripts_dialog_error), NULL); + g_signal_connect(scripts, "run", + G_CALLBACK(on_gm_scripts_dialog_run), NULL); +} + +void +gm_scripts_dialog_fini() { + g_object_unref(scripts_dialog->tag_table); + g_object_unref(scripts_dialog->text_buffer_console); + g_free(scripts_dialog); +} + +void +gm_scripts_dialog_run(GmAppView *view) { + GtkWidget *editor; + + if (scripts_dialog->dialog != NULL) { + gtk_widget_show(scripts_dialog->dialog); + return; + } + + scripts_dialog->xml = glade_xml_new(G_SCRIPTS_XML, "gm_scripts_dialog", NULL); + scripts_dialog->dialog = glade_xml_get_widget(scripts_dialog->xml, + "gm_scripts_dialog"); + scripts_dialog->text_view_console = glade_xml_get_widget(scripts_dialog->xml, + "text_view_console"); + scripts_dialog->statusbar_editor = glade_xml_get_widget(scripts_dialog->xml, + "statusbar_editor"); + scripts_dialog->button_save = glade_xml_get_widget(scripts_dialog->xml, + "tool_button_save"); + scripts_dialog->tree_view_scripts = glade_xml_get_widget(scripts_dialog->xml, + "tree_view_scripts"); + scripts_dialog->tree_view_files = glade_xml_get_widget(scripts_dialog->xml, + "tree_view_files"); + scripts_dialog->button_delete = glade_xml_get_widget(scripts_dialog->xml, + "tool_button_delete"); + + gm_scripts_dialog_init_console(); + gm_scripts_dialog_init_tree(); + gm_scripts_dialog_init_files(); + + editor = gm_scripts_dialog_init_editor(); + + gtk_container_add(GTK_CONTAINER(glade_xml_get_widget(scripts_dialog->xml, + "scrolled_window_editor")), editor); + + glade_xml_signal_connect(scripts_dialog->xml, "on_gm_scripts_dialog_delete", + G_CALLBACK(on_gm_scripts_dialog_delete)); + glade_xml_signal_connect(scripts_dialog->xml, + "on_tree_view_files_row_activated", + G_CALLBACK(on_tree_view_files_row_activated)); + glade_xml_signal_connect(scripts_dialog->xml, + "on_tool_button_new_clicked", + G_CALLBACK(on_tool_button_new_clicked)); + glade_xml_signal_connect(scripts_dialog->xml, + "on_tool_button_save_clicked", + G_CALLBACK(on_tool_button_save_clicked)); + glade_xml_signal_connect(scripts_dialog->xml, + "on_tool_button_save_as_clicked", + G_CALLBACK(on_tool_button_save_as_clicked)); + glade_xml_signal_connect(scripts_dialog->xml, + "on_tool_button_delete_clicked", + G_CALLBACK(on_tool_button_delete_clicked)); + + gtk_window_set_transient_for(GTK_WINDOW(scripts_dialog->dialog), + GTK_WINDOW(view)); + gm_scripts_dialog_set_status(_("New ")); + + g_object_unref(scripts_dialog->xml); +} + + +void +gm_scripts_dialog_add(MessageType mtype, gchar *msg) { + gchar *m, *tagName = NULL, *newLine; + gchar p[3] = {' ', ' ', '\0'}; + GtkTextIter end; + + switch (mtype) { + case SCRIPT_RUN: + p[0] = ':'; + tagName = g_strdup("scripts-run"); + break; + case SCRIPT_MESSAGE: + p[0] = '#'; + tagName = g_strdup("scripts-msg"); + break; + case SCRIPT_ERROR: + p[0] = '!'; + tagName = g_strdup("scripts-error"); + break; + } + + m = g_strconcat(p, msg, "\n", NULL); + gtk_text_buffer_get_end_iter(scripts_dialog->text_buffer_console, &end); + + // convert to UTF-8 + newLine = g_locale_to_utf8(m, strlen(m), NULL, NULL, NULL); + + if (newLine == NULL) { + gtk_text_buffer_insert_with_tags_by_name( + scripts_dialog->text_buffer_console, &end, m, strlen(m), + tagName, NULL); + } else { + gtk_text_buffer_insert_with_tags_by_name( + scripts_dialog->text_buffer_console, &end, newLine, + strlen(newLine), tagName, NULL); + g_free(newLine); + } + + g_free(m); + g_free(tagName); +} + +gboolean +gm_scripts_dialog_selection_info(SelectionInfo *info) { + GtkTreeView *view = GTK_TREE_VIEW(scripts_dialog->tree_view_files); + GtkTreeModel *model = gtk_tree_view_get_model(view); + GtkTreeSelection *selection = gtk_tree_view_get_selection(view); + gchar *parentName, *name; + + info->view = view; + info->model = model; + + if (gtk_tree_selection_get_selected(selection, &model, &(info->seliter))) { + info->has_parent = gtk_tree_model_iter_parent(model, + &(info->parent), &(info->seliter)); + + if (info->has_parent) { + gtk_tree_model_get(model, &(info->parent), SCRIPTS_NAME, + &parentName, -1); + gtk_tree_model_get(model, &(info->seliter), SCRIPTS_NAME, + &name, -1); + + if (strcmp(parentName, _("Share")) == 0) { + info->filename = g_strconcat( + PACKAGE_DATA_DIR "/" PACKAGE "/scripts/", name, NULL); + } else { + info->filename = g_strconcat(gm_app_path(gm_app_instance()), + "/scripts/", name, NULL); + } + + g_free(name); + g_free(parentName); + info->can_save = gm_scripts_dialog_can_write(info->filename); + } else { + info->filename = NULL; + } + + return TRUE; + } else { + return FALSE; + } +} + +/* CALLBACKS */ + +gboolean +on_gm_scripts_dialog_delete(GtkWidget *widget, GdkEvent *event, + gpointer user_data) { + g_free(scripts_dialog->current_edit); + scripts_dialog->current_edit = NULL; + scripts_dialog->dialog = NULL; + + return FALSE; +} + +void +on_files_changed(GtkTreeSelection *treeselection, gpointer user_data) { + SelectionInfo info; + + if (gm_scripts_dialog_selection_info(&info)) { + gtk_widget_set_sensitive(scripts_dialog->button_delete, + info.has_parent && info.can_save); + + if (info.filename) { + g_free(info.filename); + } + } +} + +void +on_tree_view_files_row_activated(GtkTreeView *treeview, GtkTreePath *arg1, + GtkTreeViewColumn *arg2, gpointer user_data) { + SelectionInfo si; + + if (gm_scripts_dialog_selection_info(&si) && si.filename) { + gm_scripts_dialog_editor_load(&si); + g_free(si.filename); + } +} + +void +on_text_buffer_editor_modified_changed(GtkTextBuffer *buffer, + gpointer user_data) { + gchar *msg; + + if (scripts_dialog->current_edit) { + msg = g_strconcat(_("Changed "), scripts_dialog->current_edit, NULL); + } else { + msg = g_strdup(_("Changed ")); + } + + gm_scripts_dialog_set_status(msg); + g_free(msg); +} + +void +on_tool_button_new_clicked(GtkToolButton *button, gpointer user_data) { + gm_scripts_dialog_editor_load(NULL); +} + +void +on_tool_button_save_clicked(GtkToolButton *button, gpointer user_data) { + if (scripts_dialog->current_edit) { + gm_scripts_dialog_editor_save(scripts_dialog->current_edit); + gtk_text_buffer_set_modified(scripts_dialog->text_buffer_editor, FALSE); + } else { + on_tool_button_save_as_clicked(button, user_data); + } +} + +void +on_tool_button_save_as_clicked(GtkToolButton *button, gpointer user_data) { + GtkWidget *dlg; + gchar *filename, *di; + + dlg = gtk_file_chooser_dialog_new(_("Save file"), + GTK_WINDOW(scripts_dialog->dialog), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + + if (scripts_dialog->current_edit) { + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dlg), + scripts_dialog->current_edit); + } else { + di = g_strconcat(gm_app_path(gm_app_instance()), + "/scripts/untitled.rb", NULL); + gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(dlg), di); + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dlg), "untitled.rb"); + g_free(di); + } + + if (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_ACCEPT) { + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg)); + + if (gm_scripts_dialog_editor_save(filename)) { + gtk_text_buffer_set_modified(scripts_dialog->text_buffer_editor, + FALSE); + } + + g_free(filename); + } + + gtk_widget_destroy(dlg); +} + +void +on_tool_button_delete_clicked(GtkToolButton *button, gpointer user_data) { + SelectionInfo info; + + if (gm_scripts_dialog_selection_info(&info)) { + if (info.has_parent && info.can_save) { + remove(info.filename); + } + + g_free(info.filename); + } +} + +// Callbacks + +void +on_gm_scripts_dialog_script_added(GmScripts *scripts, GmScript *script, + gpointer user_data) { + if (scripts_dialog->dialog != NULL) { + gm_scripts_dialog_tree_add_script(script); + gm_scripts_dialog_add_file(script->filename); + } +} + +void +on_gm_scripts_dialog_script_changed(GmScripts *scripts, GmScript *script, + gpointer user_data) { + if (scripts_dialog->dialog != NULL) { + gm_scripts_dialog_tree_update_script(script); + } +} + +void +on_gm_scripts_dialog_script_removed(GmScripts *scripts, GmScript *script, + gpointer user_data) { + if (scripts_dialog->dialog != NULL) { + } +} + +void on_gm_scripts_dialog_message(GmScripts *scripts, gchar *message, + gpointer user_data) { + gm_scripts_dialog_add(SCRIPT_MESSAGE, message); +} + +void on_gm_scripts_dialog_error(GmScripts *scripts, gchar *message, + gpointer user_data) { + gm_scripts_dialog_add(SCRIPT_ERROR, message); +} + +void on_gm_scripts_dialog_run(GmScripts *scripts, gchar *message, + gpointer user_data) { + gm_scripts_dialog_add(SCRIPT_RUN, message); +} + +#endif diff --git a/src/gm-scripts-dialog.h b/src/gm-scripts-dialog.h new file mode 100644 index 0000000..fdbb980 --- /dev/null +++ b/src/gm-scripts-dialog.h @@ -0,0 +1,18 @@ +#ifndef __GM_SCRIPTS_DIALOG_H__ +#define __GM_SCRIPTS_DIALOG_H__ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_RUBY +#include +#include "gm-world.h" +#include "gm-app-view.h" + +void gm_scripts_dialog_run(GmAppView *view); +void gm_scripts_dialog_init(); +void gm_scripts_dialog_fini(); + +#endif +#endif /* __GM_SCRIPTS_DIALOG_H__ */ diff --git a/src/gm-scripts.c b/src/gm-scripts.c new file mode 100644 index 0000000..0cf3b16 --- /dev/null +++ b/src/gm-scripts.c @@ -0,0 +1,867 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "gm-world.h" +#include "gm-app.h" +#include "gm-scripts.h" +#include "gm-support.h" +#include "gm-world.h" + +#define GM_SCRIPTS_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), \ + GM_TYPE_SCRIPTS, GmScriptsPrivate)) +#define RB_CALLBACK(x) (VALUE (*)())(x) +#define GM_SCRIPTS_GLOBAL PACKAGE_DATA_DIR "/" PACKAGE "/scripts" + +static VALUE rb_world_class, rb_client_class, rb_scripts_class; + +VALUE script_world_name(VALUE self); +VALUE gm_scripts_rb_world_new(GmWorld *world); +VALUE gm_scripts_rb_scripts_new(GmScripts *scripts); + +void gm_scripts_rb_world_class_init(); +void gm_scripts_rb_client_class_init(); +void gm_scripts_rb_scripts_class_init(); + +void gm_scripts_unload(GmScripts *scripts); +void gm_scripts_rb_init(); + +struct _GmScriptsPrivate { + GList *files; + GmScript *loading; + GList *monitors; +}; + +/* Signals */ + +enum { + SCRIPT_ADDED, + SCRIPT_CHANGED, + SCRIPT_REMOVED, + RELOAD, + MESSAGE, + ERROR, + RUN, + NUM_SIGNALS +}; + +static guint gm_scripts_signals[NUM_SIGNALS] = {0}; + +G_DEFINE_TYPE(GmScripts, gm_scripts, G_TYPE_OBJECT) + + +static void +gm_scripts_finalize(GObject *object) { + GmScripts *scripts = GM_SCRIPTS(object); + GList *monitors; + + gm_scripts_unload(scripts); + + for (monitors = scripts->priv->monitors; monitors; + monitors = monitors->next) { + gnome_vfs_monitor_cancel((GnomeVFSMonitorHandle *)(monitors->data)); + } + + g_list_free(scripts->priv->monitors); + scripts->priv->monitors = NULL; + + G_OBJECT_CLASS(gm_scripts_parent_class)->finalize(object); +} + +static void +gm_scripts_class_init(GmScriptsClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gm_scripts_finalize; + + gm_scripts_signals[SCRIPT_ADDED] = + g_signal_new("script_added", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmScriptsClass, script_added), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + gm_scripts_signals[SCRIPT_CHANGED] = + g_signal_new("script_changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmScriptsClass, script_changed), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + gm_scripts_signals[SCRIPT_REMOVED] = + g_signal_new("script_removed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmScriptsClass, script_removed), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + gm_scripts_signals[RELOAD] = + g_signal_new("reload", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmScriptsClass, reload), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + gm_scripts_signals[MESSAGE] = + g_signal_new("message", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmScriptsClass, message), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + gm_scripts_signals[ERROR] = + g_signal_new("error", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmScriptsClass, error), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + gm_scripts_signals[RUN] = + g_signal_new("run", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmScriptsClass, run), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + g_type_class_add_private(object_class, sizeof(GmScriptsPrivate)); + + gm_scripts_rb_init(); +} + +void +gm_scripts_rb_init(GmScriptsClass *klass) { + ruby_init(); + + gm_scripts_rb_world_class_init(); + gm_scripts_rb_client_class_init(); + gm_scripts_rb_scripts_class_init(); +} + +static void +gm_scripts_init(GmScripts *scripts) { + scripts->priv = GM_SCRIPTS_GET_PRIVATE(scripts); + + scripts->priv->monitors = NULL; + scripts->priv->files = NULL; +} + +GmScripts * +gm_scripts_new() { + GmScripts *scripts = GM_SCRIPTS(g_object_new(GM_TYPE_SCRIPTS, NULL)); + + return scripts; +} + +void gm_scripts_monitor_cb (GnomeVFSMonitorHandle *handle, + const gchar *monitor_uri, const gchar *info_uri, + GnomeVFSMonitorEventType event_type, + GmScripts *scripts) { + gchar *filename = gnome_vfs_get_local_path_from_uri(info_uri); + + switch (event_type) { + case GNOME_VFS_MONITOR_EVENT_CHANGED: + gm_scripts_reload_file(scripts, filename); + break; + case GNOME_VFS_MONITOR_EVENT_DELETED: + gm_scripts_remove_file(scripts, filename); + break; + case GNOME_VFS_MONITOR_EVENT_CREATED: + gm_scripts_add_file(scripts, filename); + break; + default: + break; + } + + g_free(filename); +} + +void +gm_script_function_destroy(GmScriptFunction *fi) { + g_free(fi->name); + g_free(fi->fname); + g_free(fi->description); + g_free(fi); +} + +void +gm_script_destroy_functions(GmScript *script) { + GList *functions; + + for (functions = script->functions; functions; + functions = functions->next) { + gm_script_function_destroy((GmScriptFunction *)(functions->data)); + } + + g_list_free(script->functions); + script->functions = NULL; +} + +void +gm_script_destroy(GmScript *script) { + gm_script_destroy_functions(script); + g_free(script->filename); + g_free(script); +} + +void +gm_scripts_unload(GmScripts *scripts) { + GList *files; + + for (files = scripts->priv->files; files; files = files->next) { + gm_script_destroy((GmScript *)(files->data)); + } + + g_list_free(scripts->priv->files); + scripts->priv->files = NULL; +} + +GmScriptFunction * +gm_scripts_find(GmScripts *scripts, gchar *name) { + GList *files; + GList *functions; + GmScriptFunction *func; + GmScript *script; + + for (files = scripts->priv->files; files; files = files->next) { + script = (GmScript *)(files->data); + for (functions = script->functions; functions; + functions = functions->next) { + func = (GmScriptFunction *)(functions->data); + + if (strcasecmp(func->name, name) == 0) { + return func; + } + } + } + + return NULL; +} + +gboolean +gm_scripts_add(GmScripts *scripts, gchar *name, gchar *fname, + gchar *description) { + GmScriptFunction *func; + + if (gm_scripts_find(scripts, name) == NULL) { + func = g_new(GmScriptFunction, 1); + func->script = scripts->priv->loading; + func->name = g_strdup(name); + func->fname = g_strdup(fname);; + func->description = g_strdup(description); + + scripts->priv->loading->functions = + g_list_append(scripts->priv->loading->functions, func); + return TRUE; + } else { + return FALSE; + } +} + +VALUE +gm_scripts_rb_register_functions_wrap(VALUE arg) { + return rb_eval_string("register_functions"); +} + +void +gm_scripts_rb_script_define_world(VALUE *world) { + rb_define_variable("$world", world); +} + +void +gm_scripts_rb_error(GmScripts *scripts) { + int c; + VALUE lasterr; + char *err; + gchar *msg; + VALUE ary; + + if(!NIL_P(ruby_errinfo)) { + lasterr = rb_gv_get("$!"); + err = RSTRING(rb_obj_as_string(lasterr))->ptr; + + debug_msg(1, "GmScripts.Error: Error while executing Ruby code: %s", + err); + + msg = g_strdup_printf(_("Error in execution: %s"), err); + g_signal_emit(scripts, gm_scripts_signals[MESSAGE], 0, msg); + g_free(msg); + + ary = rb_funcall(ruby_errinfo, rb_intern("backtrace"), 0); + debug_msg(1, "GmScripts.Error: Ruby backtrace:"); + g_signal_emit(scripts, gm_scripts_signals[ERROR], 0, + _("Ruby backtrace:")); + + for (c = 0; c < RARRAY(ary)->len; c++) { + debug_msg(1, "GmScripts.Error: \tfrom %s", + RSTRING(RARRAY(ary)->ptr[c])->ptr); + + msg = g_strdup_printf(_("\tfrom %s"), + RSTRING(RARRAY(ary)->ptr[c])->ptr); + g_signal_emit(scripts, gm_scripts_signals[ERROR], 0, msg); + g_free(msg); + } + } +} + +int +gm_scripts_rb_do(GmScripts *scripts, VALUE (*body)(), VALUE arg) { + int status; + + rb_protect(body, arg, &status); + + if (status != 0) { + gm_scripts_rb_error(scripts); + ruby_cleanup(status); + return 0; + } + + return 1; +} + +VALUE +gm_scripts_run_function(GmScriptInfo *arg) { + VALUE ret; + gchar *argstr; + gchar *funcAll; + + if (arg->argstr) { + argstr = gm_str_escape(arg->argstr); + funcAll = g_strconcat(arg->name, "(\"", argstr, "\")", NULL); + g_free(argstr); + } else { + funcAll = g_strconcat(arg->name, "()", NULL); + } + + ret = rb_eval_string(funcAll); + g_free(funcAll); + + return ret; +} + +gboolean +gm_scripts_run(GmScripts *scripts, GmWorld *world, gchar *name, gchar *argstr) { + VALUE rbWorld, rbClient; + gchar *msg; + GmScriptInfo *info; + GmScriptFunction *f = gm_scripts_find(scripts, name); + + if (!f) { + return FALSE; + } + + info = g_new0(GmScriptInfo, 1); + info->name = g_strdup(f->fname); + + if (argstr) { + info->argstr = g_strdup(argstr); + msg = g_strdup_printf(_("Run script '%s' from '%s' (%s)"), f->fname, + f->script->filename, argstr); + } else { + info->argstr = NULL; + msg = g_strdup_printf(_("Run script '%s' from '%s' ()"), f->fname, + f->script->filename); + } + + g_signal_emit(scripts, gm_scripts_signals[RUN], 0, msg); + g_free(msg); + + gm_scripts_rb_do(scripts, RB_CALLBACK(rb_load_file), + (VALUE)(f->script->filename)); + ruby_exec(); + + rbWorld = gm_scripts_rb_world_new(world); + rb_define_variable("$world", &rbWorld); + + rbClient = rb_class_new_instance(0, NULL, rb_client_class); + rb_define_variable("$client", &rbClient); + + gm_scripts_rb_do(scripts, RB_CALLBACK(gm_scripts_run_function), + (VALUE)info); + + g_free(info->name); + g_free(info->argstr); + g_free(info); + + return TRUE; +} + +VALUE +gm_scripts_register_func_old(int argc, VALUE *argv) { + debug_msg(1, "GmScripts.RegisterFunc: This is the deprecated way to " + "register functions is does no longer work. Use $scripts.register " + "instead."); + + return Qfalse; +} + +void +gm_scripts_register_functions(GmScripts *scripts) { + gchar *msg; + VALUE rbScripts; + + debug_msg(1, "GmScripts.RegisterFunctions: registering functions in %s", + scripts->priv->loading->filename); + + msg = g_strdup_printf(_("Registering functions from '%s'"), + scripts->priv->loading->filename); + g_signal_emit(scripts, gm_scripts_signals[MESSAGE], 0, msg); + g_free(msg); + + if (!gm_scripts_rb_do(scripts, RB_CALLBACK(rb_load_file), + (VALUE) scripts->priv->loading->filename)) { + return; + } + + ruby_exec(); + + rbScripts = gm_scripts_rb_scripts_new(scripts); + rb_define_variable("$scripts", &rbScripts); + rb_define_global_function("register_func", &gm_scripts_register_func_old, + -1); + + gm_scripts_rb_do(scripts, + RB_CALLBACK(gm_scripts_rb_register_functions_wrap), 0); +} + +void +gm_scripts_remove_file(GmScripts *scripts, const gchar *uri) { + GList *f, *l; + GmScript *script; + + l = g_list_copy(scripts->priv->files); + + for (f = l; f; f = f->next) { + script = (GmScript *)(f->data); + + if (strcmp(script->filename, uri) == 0) { + scripts->priv->files = g_list_remove(scripts->priv->files, script); + + debug_msg(1, "GmScripts.RemoveFile: Removing scripts from `%s'", + script->filename); + g_signal_emit(scripts, gm_scripts_signals[SCRIPT_REMOVED], 0, + script); + + gm_script_destroy(script); + } + } + + g_list_free(l); +} + +gboolean +gm_scripts_add_file(GmScripts *scripts, const gchar *uri) { + GList *f; + GmScript *script; + gchar *msg; + + for (f = scripts->priv->files; f; f = f->next) { + script = (GmScript *)(f->data); + + if (strcmp(script->filename, uri) == 0) { + msg = g_strdup_printf(_("File `%s' already loaded"), uri); + + debug_msg(1, "GmScripts.AddFile: %s", msg); + g_signal_emit(scripts, gm_scripts_signals[ERROR], 0, msg); + + g_free(msg); + return FALSE; + } + } + + msg = g_strdup_printf(_("File `%s' added"), uri); + debug_msg(1, "GmScripts.AddFile: %s", msg); + g_free(msg); + + script = g_new0(GmScript, 1); + script->filename = g_strdup(uri); + + if (strncmp(uri, GM_SCRIPTS_GLOBAL, + strlen(GM_SCRIPTS_GLOBAL)) == 0) { + script->type = GM_SCRIPT_TYPE_SHARE; + } else { + script->type = GM_SCRIPT_TYPE_USER; + } + + scripts->priv->files = g_list_append(scripts->priv->files, script); + g_signal_emit(scripts, gm_scripts_signals[SCRIPT_ADDED], 0, script); + + scripts->priv->loading = script; + + gm_scripts_register_functions(scripts); + + return TRUE; +} + +void +gm_scripts_reload_file(GmScripts *scripts, const gchar *uri) { + GList *files; + GmScript *script; + + for (files = scripts->priv->files; files; files = files->next) { + script = (GmScript *)(files->data); + + if (strcmp(script->filename, uri) == 0) { + // Remove all functions and reregister the file + scripts->priv->loading = script; + + gm_script_destroy_functions(script); + gm_scripts_register_functions(scripts); + + g_signal_emit(scripts, gm_scripts_signals[SCRIPT_CHANGED], 0, + script); + return; + } + } + + // If the script does not yet exist, add it + gm_scripts_add_file(scripts, uri); +} + +void +gm_scripts_load_dir(GmScripts *scripts, gchar *dirname) { + gchar *filename; + gchar *file; + GDir *d; + GnomeVFSMonitorHandle *handle; + + if (g_file_test(dirname, G_FILE_TEST_EXISTS) && + g_file_test(dirname, G_FILE_TEST_IS_DIR)) { + + if ((d = g_dir_open(dirname, 0, NULL))) { + while ((file = (gchar *)g_dir_read_name(d))) { + filename = g_strconcat(dirname, "/", file, NULL); + gm_scripts_add_file(scripts, filename); + g_free(filename); + } + } + + gnome_vfs_monitor_add(&handle, dirname, GNOME_VFS_MONITOR_DIRECTORY, + (GnomeVFSMonitorCallback)gm_scripts_monitor_cb, scripts); + scripts->priv->monitors = g_list_append(scripts->priv->monitors, + handle); + } +} + +void +gm_scripts_load(GmScripts *scripts) { + gchar *path; + + if (scripts->priv->files) { + g_signal_emit(scripts, gm_scripts_signals[RELOAD], 0); + gm_scripts_unload(scripts); + } + + path = g_strconcat(gm_app_path(gm_app_instance()), "/scripts", NULL); + + gm_scripts_load_dir(scripts, path); + gm_scripts_load_dir(scripts, GM_SCRIPTS_GLOBAL); +} + +GList * +gm_scripts_scripts(GmScripts *scripts) { + return scripts->priv->files; +} +// Ruby class functions + +VALUE +gm_scripts_rb_world_new(GmWorld *world) { + VALUE tdata = Data_Wrap_Struct(rb_world_class, 0, 0, world); + + return tdata; +} + +VALUE +gm_scripts_rb_scripts_new(GmScripts *scripts) { + VALUE tdata = Data_Wrap_Struct(rb_scripts_class, 0, 0, scripts); + + return tdata; +} + +// Ruby world class functions +static VALUE +gm_scripts_rb_world_name(VALUE self) { + GmWorld *world; + + Data_Get_Struct(self, GmWorld, world); + + return rb_str_new2(gm_options_get(gm_world_options(world), "name")); +} + +static VALUE +gm_scripts_rb_world_host(VALUE self) { + GmWorld *world; + + Data_Get_Struct(self, GmWorld, world); + + return rb_str_new2(gm_options_get(gm_world_options(world), "host")); +} + +static VALUE +gm_scripts_rb_world_port(VALUE self) { + GmWorld *world; + + Data_Get_Struct(self, GmWorld, world); + + return rb_str_new2(gm_options_get(gm_world_options(world), "port")); +} + +static VALUE +gm_scripts_rb_world_writeln(VALUE self, VALUE str) { + GmWorld *world; + gchar *strVal; + + Data_Get_Struct(self, GmWorld, world); + strVal = rb_string_value_cstr(&str); + + gm_world_writeln(world, strVal); + + return Qnil; +} + +static VALUE +gm_scripts_rb_world_sendln(VALUE self, VALUE str) { + GmWorld *world; + gchar *strVal; + + Data_Get_Struct(self, GmWorld, world); + strVal = rb_string_value_cstr(&str); + + gm_world_sendln(world, strVal); + + return Qnil; +} + +static VALUE +gm_scripts_rb_world_input(VALUE self, VALUE str) { + GmWorld *world; + gchar *strVal; + + Data_Get_Struct(self, GmWorld, world); + strVal = rb_string_value_cstr(&str); + + gm_world_process_input(world, strVal); + + return Qnil; +} + +static VALUE +gm_scripts_rb_world_loaded(VALUE self) { + GmWorld *world; + + Data_Get_Struct(self, GmWorld, world); + + if (gm_world_loaded(world)) { + return Qtrue; + } else { + return Qfalse; + } +} + +static VALUE +gm_scripts_rb_world_connected(VALUE self) { + GmWorld *world; + + Data_Get_Struct(self, GmWorld, world); + + if (gm_world_state(world) == GM_NET_STATE_CONNECTED) { + return Qtrue; + } else { + return Qfalse; + } +} + +static VALUE +gm_scripts_rb_world_quit(VALUE self) { + GmWorld *world; + + Data_Get_Struct(self, GmWorld, world); + + gm_world_unload(world); + return Qnil; +} + +static VALUE +gm_scripts_rb_world_connect(int argc, VALUE *argv, VALUE self) { + GmWorld *world; + const gchar *strHost, *strPort; + + Data_Get_Struct(self, GmWorld, world); + + if (argc == 0) { + strHost = gm_options_get(gm_world_options(world), "host"); + } else { + strHost = rb_string_value_cstr(&(argv[0])); + } + + if (argc == 0 || argc == 1) { + strPort = gm_options_get(gm_world_options(world), "port"); + } else { + strPort = rb_string_value_cstr(&(argv[1])); + } + + gm_world_connect_to(world, (gchar *)strHost, (gchar *)strPort); + + return Qnil; +} + +static VALUE +gm_scripts_rb_world_disconnect(VALUE self) { + GmWorld *world; + + Data_Get_Struct(self, GmWorld, world); + + gm_world_disconnect(world); + return Qnil; +} + +// Ruby client class functions + +static VALUE +gm_scripts_rb_client_version(VALUE self) { + return rb_str_new2(VERSION); +} + +static VALUE +gm_scripts_rb_client_worlds(VALUE self) { + GList *world; + VALUE rb_array = rb_ary_new(); + VALUE rb_world; + + for (world = gm_app_worlds(gm_app_instance()); world; world = world->next) { + rb_world = gm_scripts_rb_world_new((GmWorld *)(world->data)); + rb_ary_push(rb_array, rb_world); + } + + return rb_array; +} + +static VALUE +gm_scripts_rb_client_open(VALUE self, VALUE str) { + GmWorld *world; + gchar *strVal; + + strVal = rb_string_value_cstr(&str); + + world = gm_app_world_by_name(gm_app_instance(), strVal); + + if (world == NULL) { + return Qfalse; + } else { + gm_world_load(world); + return Qtrue; + } +} + +// Ruby scripts class functions +VALUE +gm_scripts_rb_scripts_register(int argc, VALUE *argv, VALUE self) { + char *name, *fname = NULL, *description = NULL; + gchar *msg; + GmScripts *scripts; + + Data_Get_Struct(self, GmScripts, scripts); + + if (argc > 1) { + name = rb_string_value_cstr(&argv[0]); + description = rb_string_value_cstr(&argv[1]); + + if (argc == 2) { + fname = name; + } else { + fname = rb_string_value_cstr(&argv[2]); + } + + if (gm_scripts_add(scripts, name, fname, description)) { + msg = g_strdup_printf(_("Register function '%s' from '%s'"), name, + scripts->priv->loading->filename); + debug_msg(1, "GmScripts.RbScriptsRegister: Adding script function " + "%s from %s", name, scripts->priv->loading->filename); + g_signal_emit(scripts, gm_scripts_signals[MESSAGE], 0, msg); + g_free(msg); + return Qtrue; + } else { + msg = g_strdup_printf(_("Script '%s' is already defined"), name); + debug_msg(1, "GmScripts.RbScriptsRegister: Script function %s " + "already defined!", name); + g_signal_emit(scripts, gm_scripts_signals[ERROR], 0, msg); + g_free(msg); + return Qfalse; + } + } else { + return Qfalse; + } +} + +// Ruby class initializations + +void +gm_scripts_rb_world_class_init() { + rb_world_class = rb_define_class("World", rb_cObject); + + rb_define_method(rb_world_class, "name", gm_scripts_rb_world_name, 0); + rb_define_method(rb_world_class, "host", gm_scripts_rb_world_host, 0); + rb_define_method(rb_world_class, "port", gm_scripts_rb_world_port, 0); + + rb_define_method(rb_world_class, "writeln", gm_scripts_rb_world_writeln, 1); + rb_define_method(rb_world_class, "println", gm_scripts_rb_world_writeln, 1); + rb_define_method(rb_world_class, "sendln", gm_scripts_rb_world_sendln, 1); + rb_define_method(rb_world_class, "input", gm_scripts_rb_world_input, 1); + rb_define_method(rb_world_class, "quit", gm_scripts_rb_world_quit, 0); + rb_define_method(rb_world_class, "connect", + gm_scripts_rb_world_connect, -1); + rb_define_method(rb_world_class, "disconnect", + gm_scripts_rb_world_disconnect, 0); + rb_define_method(rb_world_class, "loaded?", gm_scripts_rb_world_loaded, 0); + rb_define_method(rb_world_class, "connected?", + gm_scripts_rb_world_connected, 0); +} + +void +gm_scripts_rb_client_class_init() { + rb_client_class = rb_define_class("Client", rb_cObject); + + rb_define_method(rb_client_class, "version", + gm_scripts_rb_client_version, 0); + rb_define_method(rb_client_class, "worlds", + gm_scripts_rb_client_worlds, 0); + rb_define_method(rb_client_class, "open", gm_scripts_rb_client_open, 1); +} + +void +gm_scripts_rb_scripts_class_init() { + rb_scripts_class = rb_define_class("Scripts", rb_cObject); + + rb_define_method(rb_scripts_class, "register", + gm_scripts_rb_scripts_register, -1); +} diff --git a/src/gm-scripts.h b/src/gm-scripts.h new file mode 100644 index 0000000..f9ddea4 --- /dev/null +++ b/src/gm-scripts.h @@ -0,0 +1,109 @@ +#ifndef __GM_SCRIPTS_H__ +#define __GM_SCRIPTS_H__ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_RUBY +#include +#include + +#include "gm-world.h" + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GM_TYPE_SCRIPTS (gm_scripts_get_type()) +#define GM_SCRIPTS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + GM_TYPE_SCRIPTS, GmScripts)) +#define GM_SCRIPTS_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + GM_TYPE_SCRIPTS, GmScripts const)) +#define GM_SCRIPTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \ + GM_TYPE_SCRIPTS, GmScriptsClass)) +#define GM_IS_SCRIPTS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + GM_TYPE_SCRIPTS)) +#define GM_IS_SCRIPTS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + GM_TYPE_SCRIPTS)) +#define GM_SCRIPTS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ + GM_TYPE_SCRIPTS, GmScriptsClass)) + +/* Private structure type */ +typedef struct _GmScriptsPrivate GmScriptsPrivate; + +/* + * Main object structure + */ +typedef struct _GmScripts GmScripts; + +struct _GmScripts { + GObject object; + + /*< private > */ + GmScriptsPrivate *priv; +}; + +typedef enum _GmScriptType { + GM_SCRIPT_TYPE_USER, + GM_SCRIPT_TYPE_SHARE +} GmScriptType; + +typedef struct _GmScript { + gchar *filename; + GmScriptType type; + GList *functions; +} GmScript; + +typedef struct _GmScriptFunction { + GmScript *script; + gchar *name; + gchar *fname; + gchar *description; +} GmScriptFunction; + +typedef struct _GmScriptInfo { + gchar *name; + gchar *argstr; +} GmScriptInfo; + +/* + * Class definition + */ +typedef struct _GmScriptsClass GmScriptsClass; + +struct _GmScriptsClass { + GObjectClass parent_class; + + /* Signals */ + void (* script_added) (GmScripts *scripts, GmScript *script); + void (* script_changed) (GmScripts *scripts, GmScript *script); + void (* script_removed) (GmScripts *scripts, GmScript *script); + void (* reload) (GmScripts *scripts); + void (* message) (GmScripts *scripts, gchar *message); + void (* error) (GmScripts *scripts, gchar *message); + void (* run) (GmScripts *scripts, gchar *message); +}; + +GType gm_scripts_get_type(void) G_GNUC_CONST; +GmScripts *gm_scripts_new(); + +//void gm_script_init(); +//void gm_script_fini(); + +void gm_scripts_reload_file(GmScripts *scripts, const gchar *uri); +gboolean gm_scripts_add_file(GmScripts *scripts, const gchar *uri); +void gm_scripts_remove_file(GmScripts *scripts, const gchar *uri); + +GmScriptFunction *gm_scripts_find(GmScripts *scripts, gchar *name); +GList *gm_scripts_scripts(GmScripts *scripts); + +gboolean gm_scripts_run(GmScripts *scripts, GmWorld *world, gchar *name, + gchar *argstr); +void gm_scripts_load(GmScripts *scripts); + +G_END_DECLS + +#endif /* HAVE_RUBY */ +#endif /* __GM_SCRIPTS_H__ */ diff --git a/src/gm-string.c b/src/gm-string.c new file mode 100644 index 0000000..68f67f3 --- /dev/null +++ b/src/gm-string.c @@ -0,0 +1,203 @@ +#include +#include +#include + +#include "gm-string.h" +#include "debug.h" + +/* +void +stringlist_add(stringlist * strl, char *data) { + stringlist_item *newStringlistItem = + (stringlist_item *) malloc(sizeof(stringlist_item)); + newStringlistItem->data = strdup(data); + + if (strl->firstItem == NULL) { + strl->firstItem = newStringlistItem; + } else { + newStringlistItem->prev = strl->lastItem; + + if (strl->lastItem != NULL) + strl->lastItem->next = newStringlistItem; + } + + strl->lastItem = newStringlistItem; + newStringlistItem->next = NULL; + + strl->count++; +} + +void +stringlist_remove(stringlist * strl, stringlist_item * removed) { + if (removed == strl->firstItem) { + strl->firstItem = removed->next; + } else { + removed->prev->next = removed->next; + } + + if (removed == strl->lastItem) { + strl->lastItem = removed->prev; + } else { + removed->next->prev = removed->next; + } + + free(removed->data); + free(removed); + strl->count--; +} + +stringlist * +stringlist_create(char *argstr, char *delim) { + stringlist *newStringlist = (stringlist *) malloc(sizeof(stringlist)); + char *data, *work, *working, *strPos; + + newStringlist->firstItem = NULL; + newStringlist->lastItem = NULL; + newStringlist->count = 0; + + if (delim == NULL) { + stringlist_add(newStringlist, argstr); + return newStringlist; + } + + working = strdup(argstr); + working = mystring_cat(working, delim); + work = working; + + while (strlen(work) != 0) { + + if ((strPos = strstr(work, delim)) != NULL && strPos > work) { + data = NULL; + data = mystring_catn(data, work, (strPos - work)); + stringlist_add(newStringlist, data); + free(data); + } + work = strPos + strlen(delim); + + } + + free(working); + return newStringlist; +} + +void +stringlist_destroy(stringlist * strl) { + stringlist_item *curItem = strl->firstItem; + + while (curItem != NULL) { + stringlist_remove(strl, curItem); + curItem = strl->firstItem; + } + + free(strl); +} + +char * +stringlist_glue_it(stringlist * strl, char *glue) { + char *result = NULL; + stringlist_item *curItem = strl->firstItem; + + while (curItem != NULL) { + if (curItem != strl->firstItem) { + result = mystring_cat(result, glue); + result = mystring_cat(result, curItem->data); + } else { + result = strdup(curItem->data); + } + } + + return result; +} +*/ +gboolean +gm_string_to_int(const gchar *str, int *result) { + *result = 0; + + if (str == NULL || *str == '\0') { + return FALSE; + } + + *result = atoi(str); + return TRUE; +} + +gchar * +gm_string_catn(gchar *str, gchar *add, guint n) { + guint relen; + gchar *newstr; + + if (str == NULL) { + relen = 0; + } else { + relen = strlen(str); + } + + if ((newstr = (gchar *) realloc(str, (relen + n + 1) * sizeof(gchar))) == NULL) { + debug_msg(1, "mystring_catn: REALLOC FAILED!"); + return str; + } else { + if (relen == 0) { + newstr[0] = '\0'; + } + + strncat(newstr, add, n); + return newstr; + } +} + +gchar * +gm_string_cat(gchar *str, gchar *add) { + return gm_string_catn(str, add, strlen(add)); +} + +gchar * +gm_string_trim(const gchar *str) { + gchar *newstr = NULL; + const gchar *r, *l; + + if (str == NULL) { + return NULL; + } + + r = str + strlen(str); + l = str; + + while (*l == ' ' || *r == ' ') { + if (l == r) { + break; + } + + if (*l == ' ') { + l++; + } + + if (l == r) { + break; + } + + if (*r == ' ') { + r--; + } + } + + if (l == r) { + return g_strdup(""); + } else { + newstr = g_strndup(l, r - l); + return newstr; + } +} + +void +gm_string_remove_char(char *str, char rem) { + int i, j = 0; + + for (i = 0; str[i] != '\0'; i++) { + if (str[i] != rem) { + str[j] = str[i]; + j++; + } + } + + str[j] = '\0'; +} diff --git a/src/gm-string.h b/src/gm-string.h new file mode 100644 index 0000000..9bd2b03 --- /dev/null +++ b/src/gm-string.h @@ -0,0 +1,36 @@ +#ifndef MY_STRING_H +#define MY_STRING_H 1 + +#include +#include + +/*typedef struct _stringlist_item stringlist_item; +struct _stringlist_item { + char *data; + + stringlist_item *next; + stringlist_item *prev; +}; + +typedef struct _stringlist stringlist; +struct _stringlist { + stringlist_item *firstItem; + stringlist_item *lastItem; + + unsigned int count; +}; + +void stringlist_add(stringlist * strl, char *data); +void stringlist_remove(stringlist * strl, stringlist_item * removed); +stringlist *stringlist_create(char *argstr, char *delim); +void stringlist_destroy(stringlist * strl); +char *stringlist_glue_it(stringlist * strl, char *glue); +*/ + +int gm_string_to_int(const gchar *str, int *result); +char *gm_string_catn(char *str, char *add, unsigned int n); +char *gm_string_cat(char *str, char *add); +char *gm_string_trim(const gchar *str); +void gm_string_remove_char(char *str, char rem); + +#endif diff --git a/src/gm-support.c b/src/gm-support.c new file mode 100644 index 0000000..3693453 --- /dev/null +++ b/src/gm-support.c @@ -0,0 +1,565 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "gm-support.h" +#include "debug.h" +#include "gm-pixbuf.h" +//#include "if_main.h" + +#define URL_REGEXP "(((mailto|news|telnet|nttp|file|http|sftp|ftp|https|dav|callto)://)|(www|ftp)[-A-Za-z0-9]*\\.)[-A-Za-z0-9\\.@:]+[^]''\\.}>\\) ,\\/\\\"\\!]+(:[0-9]*)?(/|/[-A-Za-z0-9_\\$\\.\\+\\!\\*\\(\\),;:@&=\\?/~\\#\\%]*[^]'\\.}>\\) ,\\\"\\!])?" +static regex_t url_regexp; + +gchar * +gm_fix_decimal_point(gchar *line, int len) { + int i; + struct lconv *l = localeconv(); + + for (i = 0; i < len; i++) { + if (line[i] == '.') { + line[i] = l->decimal_point[0]; + } + } + + return line; +} + +gchar * +gm_fix_decimal_point_rev(gchar *line, int len) { + int i; + struct lconv *l = localeconv(); + + for (i = 0; i < len; i++) { + if (line[i] == l->decimal_point[0]) { + line[i] = '.'; + } + } + + return line; +} + +gchar * +gm_ansi_strip(gchar * s) { + int i, j = 0; + + for (i = 0; s[i] != '\0'; i++) { + // Escape sequence, advance to character after 'm' + if (s[i] == '\x1B') { + while (s[i] != '\0' && s[i] != 'm') { + i++; + } + } else if (s[i] != '\x07') { + s[j] = s[i]; + j++; + } + } + + s[j] = '\0'; + return s; +} + +int +garray_length(gchar **s) { + int i = 0; + + while (s[i] != NULL) { + i++; + } + + return i; +} + +void g_list_free_simple(GList *s) { + GList *tmp; + + for (tmp = s; tmp; tmp = tmp->next) { + g_free(tmp->data); + } + + g_list_free(s); +} + +gchar * +g_list_find_simple(GList *s, gchar *f) { + GList *tmp; + + for (tmp = s; tmp; tmp = tmp->next) { + if (strcmp(tmp->data, f) == 0) { + return tmp->data; + } + } + + return NULL; +} + +void +gm_dialog(gchar * message, GtkMessageType messagebox_type, + GtkWindow * parent) { + GtkWidget *dlg; + + if (parent == NULL) { + //parent = GTK_WINDOW(if_main_get_widget("wndMain")); + } + + dlg = + gtk_message_dialog_new(parent, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + messagebox_type, GTK_BUTTONS_OK, message, NULL); + gtk_dialog_run(GTK_DIALOG(dlg)); + gtk_widget_destroy(dlg); +} + +void +gm_error_dialog(gchar * message, GtkWindow * parent) { + gm_dialog(message, GTK_MESSAGE_ERROR, parent); +} + +void +gm_warning_dialog(gchar * message, GtkWindow * parent) { + gm_dialog(message, GTK_MESSAGE_WARNING, parent); +} + +void +gm_info_dialog(gchar * message, GtkWindow * parent) { + gm_dialog(message, GTK_MESSAGE_INFO, parent); +} + +void +gm_question_dialog(gchar * message, GtkWindow * parent) { + gm_dialog(message, GTK_MESSAGE_QUESTION, parent); +} + +void +gm_do_events() { + while (gtk_events_pending()) { + gtk_main_iteration(); + } +} + +gchar * +gm_str_escape(gchar * line) { + gchar *newLine; + int i, j = 0; + + if (strlen(line) == 0) { + return g_strdup(""); + } + + if (strstr(line, "\"") || strstr(line, "\\")) { + newLine = g_new(gchar, (strlen(line) * 2) + 1); + + for (i = 0; i < (int)strlen(line); i++) { + if (line[i] == '"' || line[i] == '\\') { + newLine[j] = '\\'; + j++; + } + newLine[j] = line[i]; + j++; + } + + newLine[j] = '\0'; + return newLine; + } else { + return g_strdup(line); + } +} + +void +gm_directory_remove_all(const gchar * path, gboolean remove_self) { + GDir *cDir; + gchar *name; + gchar *newPath; + + if (g_file_test(path, G_FILE_TEST_IS_DIR)) { + // Iterate through the files and do the right thingie + if ((cDir = g_dir_open(path, 0, NULL))) { + while ((name = (gchar *) (g_dir_read_name(cDir))) != NULL) { + newPath = g_strconcat(path, "/", name, NULL); + gm_directory_remove_all(newPath, TRUE); + g_free(newPath); + } + g_dir_close(cDir); + } + } + + if (remove_self) { + // Its a file, or just empty! MUST...BE...REMOVEEEED! + remove(path); + } +} + +gint +gm_url_regex_match(const gchar *msg, int len, GArray *start, GArray *end) { + static gboolean inited = FALSE; + regmatch_t matches[1]; + gint ret = 0, num_matches = 0, offset = 0; + gchar *tmp; + gint s; + + if (!inited) { + memset (&url_regexp, 0, sizeof (regex_t)); + regcomp (&url_regexp, URL_REGEXP, REG_EXTENDED); + inited = TRUE; + } + + tmp = g_strndup(msg, len); + + while (!ret) { + ret = regexec(&url_regexp, (char *)(tmp + offset), 1, matches, 0); + + if (ret == 0) { + if (matches[0].rm_so > matches[0].rm_eo) { + break; + } + + num_matches++; + + s = matches[0].rm_so + offset; + offset = matches[0].rm_eo + offset; + + g_array_append_val(start, s); + g_array_append_val(end, offset); + } + } + + g_free(tmp); + + return num_matches; +} + +void +gm_open_url (const gchar *url) { + if (!url || strlen (url) == 0) { + return; + } + + /* gnome_url_show doesn't work when there's no protocol, so we might + * need to add one. + */ + if (strstr (url, "://") == NULL) { + gchar *tmp; + + tmp = g_strconcat ("http://", url, NULL); + gnome_url_show(tmp, NULL); + g_free (tmp); + return; + } + + gnome_url_show (url, NULL); +} + +void +gm_fetch_handle_free(GmFetchHandle *g) { + GList *tmp; + + g_free(g->cur_file_name); + + for (tmp = g->source_uri; tmp; tmp = tmp->next) { + gnome_vfs_uri_unref((GnomeVFSURI*)(tmp->data)); + } + g_list_free(g->source_uri); + + + for (tmp = g->dest_uri; tmp; tmp = tmp->next) { + gnome_vfs_uri_unref((GnomeVFSURI*)(tmp->data)); + } + g_list_free(g->dest_uri); + + g_free(g); +} + +GmFetchHandle * +gm_fetch_handle_create(GFunc cb, gpointer user_data) { + GmFetchHandle *g = g_new0(GmFetchHandle, 1); + + g->cb = cb; + g->user_data = user_data; + g->prev_status = GNOME_VFS_XFER_PROGRESS_STATUS_OK; + g->cur_phase = -1; + g->prev_phase = -1; + g->cur_file = -1; + g->prev_file = -1; + g->source_uri = NULL; + g->dest_uri = NULL; + g->cur_file_name = NULL; + g->files_total = 0; + g->done = FALSE; + g->aborted = FALSE; + + return g; +} + +gint +gm_fetch_progress(GnomeVFSAsyncHandle *handle, + GnomeVFSXferProgressInfo *info, + GmFetchHandle *g) { + gchar *name; + const gchar *err; + + g->cur_phase = info->phase; + g->cur_file = info->file_index; + g->files_total = info->files_total; + g->bytes_total = info->bytes_total; + g->file_size = info->file_size; + g->bytes_copied = info->bytes_copied; + g->total_bytes_copied = info->total_bytes_copied; + g->status = info->status; + + if (g->aborted) { + g->cb(g, g->user_data); + gm_fetch_handle_free(g); + return FALSE; + } + + if (info->target_name != NULL) { + if (g->cur_file_name && strcmp(g->cur_file_name, info->target_name) != 0) { + g->cur_phase = GNOME_VFS_XFER_PHASE_FILECOMPLETED; + g->cb(g, g->user_data); + g->cur_phase = info->phase; + + g_free(g->cur_file_name); + g->cur_file_name = NULL; + } + + if (!g->cur_file_name) { + g->cur_file_name = g_strdup(info->target_name); + } + } + + if (info->status == GNOME_VFS_XFER_PROGRESS_STATUS_OVERWRITE) { + name = gnome_vfs_get_local_path_from_uri(info->target_name); + debug_msg(1, "gnoemoe_fetch_progress: asking for overwriting %s: yes", name); + + g->prev_status = GNOME_VFS_XFER_PROGRESS_STATUS_OVERWRITE; + return GNOME_VFS_XFER_OVERWRITE_ACTION_REPLACE; + } else if (info->status == GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR) { + name = gnome_vfs_get_local_path_from_uri(info->target_name); + err = gnome_vfs_result_to_string(info->vfs_status); + + gdk_threads_enter(); + debug_msg(1, "gnoemoe_fetch_progress: error for %s: %s", name, err); + g->cb(g, g->user_data); + gdk_threads_leave(); + + g_free(g->cur_file_name); + g->cur_file_name = NULL; + + g->prev_status = GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR; + g_free(name); + + return GNOME_VFS_XFER_ERROR_ACTION_SKIP; + } + + if (info->phase == GNOME_VFS_XFER_PHASE_COMPLETED) { + if (g->cur_file_name != NULL) { + g->cur_phase = GNOME_VFS_XFER_PHASE_FILECOMPLETED; + g->cb(g, g->user_data); + g->cur_phase = info->phase; + } + + g->done = TRUE; + g->cb(g, g->user_data); + gm_fetch_handle_free(g); + return TRUE; + } + + g->prev_status = info->status; + + return TRUE; +} + +gint +gm_fetch_interact(GnomeVFSXferProgressInfo *info, gpointer user_data) { + return 1; +} + +GmFetchHandle * +gm_fetch(const GList *source, const GList *dest, + GFunc cb, gpointer user_data) { + GmFetchHandle *g = gm_fetch_handle_create(cb, user_data); + gchar *uri; + + for (; source; source = source->next) { + uri = (gchar *)(source->data); + g->source_uri = g_list_append(g->source_uri, gnome_vfs_uri_new(uri)); + } + + for (; dest; dest = dest->next) { + uri = (gchar *)(dest->data); + g->dest_uri = g_list_append(g->dest_uri, gnome_vfs_uri_new(uri)); + } + + gnome_vfs_async_xfer(&(g->handle), g->source_uri, g->dest_uri, + GNOME_VFS_XFER_DEFAULT|GNOME_VFS_XFER_RECURSIVE, + GNOME_VFS_XFER_ERROR_MODE_QUERY, + GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE, + GNOME_VFS_PRIORITY_DEFAULT, + (GnomeVFSAsyncXferProgressCallback)gm_fetch_progress, + g, gm_fetch_interact, g); + return g; +} + +gboolean +gm_is_end_scrolled(GtkScrolledWindow *wnd, guint charHeight) { + GtkAdjustment *ad = gtk_scrolled_window_get_vadjustment(wnd); + + return ((ad->page_size + ad->value) >= ad->upper - (double)charHeight); +} + +void +gm_scroll_end(GtkTextView *view, gboolean needs) { + GtkTextBuffer *buf; + GtkTextMark *mark; + GtkTextIter iter; + + if (!needs) { + return; + } + + buf = gtk_text_view_get_buffer(view); + mark = gtk_text_buffer_get_mark(buf, "end-of-buffer"); + + if (mark == NULL) { + gtk_text_buffer_get_end_iter(buf, &iter); + mark = gtk_text_buffer_create_mark(buf, "end-of-buffer", &iter, FALSE); + } + + gtk_text_view_scroll_to_mark(view, mark, 0.0, TRUE, 1.0, 1.0); +} + +#define MAX_BUF 1024 + +GString * +gm_read_file(const gchar *fname, gboolean readall, OpenLogProgress func, gpointer user_data) { + FILE *f; + gchar line[MAX_BUF], *tmp; + GString *str = NULL; + long bytes_read = 0, bytes_total = 0; + + if (!fname) { + return NULL; + } + + f = fopen(fname, "r"); + + if (f) { + fseek(f, 0, SEEK_END); + bytes_total = ftell(f); + rewind(f); + + str = g_string_new(""); + + while (fgets((char *) &line, MAX_BUF, f) != NULL) { + bytes_read += strlen((char *)&line); + tmp = NULL; + if (g_utf8_validate(line, -1, NULL)) { + if (readall) { + str = g_string_append(str, line); + } + tmp = g_strdup(line); + } else { + tmp = g_locale_to_utf8(line, -1, NULL, NULL, NULL); + + if (!tmp) { + tmp = g_convert(line, -1, "UTF-8", "ISO-8859-15", NULL, NULL, NULL); + } + if (!tmp) { + tmp = g_convert(line, -1, "UTF-8", "ISO-8859-15", NULL, NULL, NULL); + } + + if (readall) { + str = g_string_append(str, tmp); + } + } + + if (func != NULL) { + func(bytes_read, bytes_total, tmp, user_data); + } else { + g_free(tmp); + } + } + + fclose(f); + + return str; + } else { + debug_msg(1, "support_read_file: file (%s) could not be read: %s", + fname, strerror(errno)); + return NULL; + } +} + +GtkWidget * +gm_create_tab_label(const gchar *icon, const gchar *caption, gboolean has_exit, + GmLabelInfo *info) { + /* First create the gbox (size 3) which will contain an icon, a label and a + exit button if has_exit is true */ + GtkWidget *hboxTabLabel; + gint h, w; + + hboxTabLabel = gtk_hbox_new(FALSE, 3); + + gtk_widget_show(hboxTabLabel); + + info->image_icon = gtk_image_new_from_pixbuf( + gm_pixbuf_get_at_size(icon, 16, 16)); + gtk_widget_set_size_request(info->image_icon, 16, 16); + gtk_widget_show(info->image_icon); + gtk_box_pack_start(GTK_BOX(hboxTabLabel), info->image_icon, TRUE, TRUE, 0); + + info->label_name = gtk_label_new(caption); + gtk_widget_show(info->label_name); + gtk_box_pack_start(GTK_BOX(hboxTabLabel), info->label_name, FALSE, FALSE, 0); + + if (has_exit) { + info->button_exit = gtk_button_new(); + gtk_widget_show(info->button_exit); + + gtk_box_pack_end(GTK_BOX(hboxTabLabel), info->button_exit , FALSE, + FALSE, 0); + + gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &w, &h); + gtk_widget_set_size_request(info->button_exit , w + 2, h + 2); + gtk_button_set_relief(GTK_BUTTON(info->button_exit ), GTK_RELIEF_NONE); + gtk_button_set_focus_on_click(GTK_BUTTON(info->button_exit), FALSE); + + info->image_exit = gtk_image_new_from_stock("gtk-close", + GTK_ICON_SIZE_MENU); + gtk_widget_show(info->image_exit); + gtk_container_add(GTK_CONTAINER(info->button_exit), info->image_exit); + } + + return hboxTabLabel; +} + +void +gm_widget_destroy_data(GtkWidget *caller, GtkWidget *destroyer) { + if (GTK_IS_WIDGET(destroyer)) { + gtk_widget_destroy(destroyer); + } +} + +const gchar * +gm_default_charset() { + const gchar *loc = NULL; + g_get_charset(&loc); + + if (loc == NULL || strlen(loc) == 0) { + loc = "ISO-8859-15"; + } + + return loc; +} diff --git a/src/gm-support.h b/src/gm-support.h new file mode 100644 index 0000000..cc8d1c3 --- /dev/null +++ b/src/gm-support.h @@ -0,0 +1,148 @@ +#ifndef __GM_SUPPORT_H__ +#define __GM_SUPPORT_H__ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +/* + * Standard gettext macros. + */ +#ifdef ENABLE_NLS +# include +# undef _ +# define _(String) dgettext (PACKAGE, String) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define N_(String) (String) +# endif +#else +# define textdomain(String) (String) +# define gettext(String) (String) +# define dgettext(Domain,Message) (Message) +# define dcgettext(Domain,Message,Type) (Message) +# define bindtextdomain(Domain,Directory) (Domain) +#ifndef _ +# define _(String) (String) +#endif +# define N_(String) (String) +#endif + +#define CALC_COLOR_RANGE(x) (int)((-(1/((x * 0.5)+1))+1)*255) + +/** \defgroup support */ + +/** \ingroup support + * \brief Widget container for tabs + * + * Contains all widgets that could be useful to be modified. + * Stored here because it makes it easier to look them up. + * + */ +typedef struct _GmLabelInfo GmLabelInfo; +struct _GmLabelInfo { + /** \brief GtkButton widget + * + * GtkButton widget, reference to the tabs exit button + */ + GtkWidget *button_exit; + + /** \brief GtkImage widget + * + * GtkImage widget, reference to the tabs exit button image + */ + GtkWidget *image_exit; + + /** \brief GtkLabel widget + * + * GtkLabel widget, reference to the tabs label + */ + GtkWidget *label_name; + + /** \brief GtkImage widget + * + * GtkImage widget, reference to the tabs icon + */ + GtkWidget *image_icon; +}; + +/** \ingroup support + * \brief Key/value pair struct + * + * Contains two fields. Used to create hash tables + * + */ +typedef struct _keyvaluepair { + gchar *key; /**< the key */ + gchar *value; /**< the value */ +} keyvaluepair; + +typedef struct _GmFetchHandle GmFetchHandle; +struct _GmFetchHandle { + GnomeVFSAsyncHandle *handle; + + GFunc cb; + gpointer user_data; + + GList *source_uri; + GList *dest_uri; + + GnomeVFSXferPhase cur_phase; + GnomeVFSXferPhase prev_phase; + GnomeVFSXferProgressStatus prev_status; + GnomeVFSXferProgressStatus status; + + GnomeVFSFileSize bytes_total; + GnomeVFSFileSize file_size; + GnomeVFSFileSize bytes_copied; + GnomeVFSFileSize total_bytes_copied; + + gulong files_total; + gulong cur_file; + gulong prev_file; + gchar *cur_file_name; + + gboolean aborted; + gboolean done; +}; + +gchar *gm_fix_decimal_point(gchar *line, int len); +gchar *gm_fix_decimal_point_rev(gchar *line, int len); +gchar *gm_ansi_strip(gchar * s); +int garray_length(gchar **s); +void g_list_free_simple(GList *s); +gchar *g_list_find_simple(GList *s, gchar *f); + +void gm_error_dialog(gchar * message, GtkWindow * parent); +void gm_warning_dialog(gchar * message, GtkWindow * parent); +void gm_info_dialog(gchar * message, GtkWindow * parent); +void gm_question_dialog(gchar * message, GtkWindow * parent); + +void gm_do_events(); + +gchar *gm_str_escape(gchar * line); +void gm_directory_remove_all(const gchar * path, gboolean remove_self); + +gint gm_url_regex_match(const gchar *msg, int len, GArray *start, GArray *end); +void gm_open_url (const gchar *url); + +GmFetchHandle * gm_fetch(const GList *source, const GList *dest, + GFunc cb, gpointer user_data); +void gm_fetch_handle_free(GmFetchHandle *g); + +gboolean gm_is_end_scrolled(GtkScrolledWindow *wnd, guint charHeight); +void gm_scroll_end(GtkTextView *view, gboolean needs); + +typedef void (*OpenLogProgress) (long, long, gchar *, gpointer); +GString *gm_read_file(const gchar *fname, gboolean readall, + OpenLogProgress func, gpointer user_data); +GtkWidget *gm_create_tab_label(const gchar *icon, const gchar *caption, + gboolean has_exit, GmLabelInfo *info); +void gm_widget_destroy_data(GtkWidget *caller, GtkWidget *destroyer); +const gchar *gm_default_charset(); + +#endif /* __GM_SUPPORT_H__ */ diff --git a/src/gm-tray.c b/src/gm-tray.c new file mode 100644 index 0000000..06eb1b5 --- /dev/null +++ b/src/gm-tray.c @@ -0,0 +1,55 @@ + +#include "gm-tray.h" +#include "eggtrayicon.h" + +#define GM_TRAY_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_TRAY, GmTrayPrivate)) + +typedef enum _tray_type { + TRAY_ICON_DEFAULT, + TRAY_ICON_ACTIVE, + TRAY_ICON_NOTIFY +} tray_type; + +struct _GmTrayPrivate { + GtkWidget *event_box; + GtkWidget *image; + GtkTooltips *tooltips; + + GtkWidget *popup_menu; + GtkWidget *show_popup_item; + GtkWidget *hide_popup_item; + + guint flash_timeout; + tray_type iconnr; +}; + +/* Signals */ + +enum { + NUM_SIGNALS +}; + +static guint tray_signals[NUM_SIGNALS] = {0}; + +G_DEFINE_TYPE(GmTray, gm_tray, EGG_TYPE_TRAY_ICON) + +static void +gm_tray_finalize(GObject *object) { + GmTray *view = GM_TRAY(object); + + G_OBJECT_CLASS(gm_tray_parent_class)->finalize(object); +} + +static void +gm_tray_class_init(GmTrayClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gm_tray_finalize; + + g_type_class_add_private(object_class, sizeof(GmTrayPrivate)); +} + +static void +gm_tray_init(GmTray *tray) { + tray->private = GM_TRAY_GET_PRIVATE(tray); +} diff --git a/src/gm-tray.h b/src/gm-tray.h new file mode 100644 index 0000000..22a29e0 --- /dev/null +++ b/src/gm-tray.h @@ -0,0 +1,50 @@ +#ifndef __GM_TRAY_H__ +#define __GM_TRAY_H__ + +#include +#include "eggtrayicon.h" + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GM_TYPE_TRAY (gm_tray_get_type()) +#define GM_TRAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_TRAY, GmTray)) +#define GM_TRAY_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_TRAY, GmTray const)) +#define GM_TRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_TRAY, GmTrayClass)) +#define GM_IS_TRAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_TRAY)) +#define GM_IS_TRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_TRAY)) +#define GM_TRAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_TRAY, GmTrayClass)) + +/* Private structure type */ +typedef struct _GmTrayPrivate GmTrayPrivate; + +/* + * Main object structure + */ +typedef struct _GmTray GmTray; + +struct _GmTray { + EggTrayIcon trayicon; + + /*< private > */ + GmTrayPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GmTrayClass GmTrayClass; + +struct _GmTrayClass { + EggTrayIconClass parent_class; + + /* Signals */ +}; + +GType gm_tray_get_type(void) G_GNUC_CONST; +GmTray *gm_tray_new(void); + +G_END_DECLS +#endif /* __GM_TRAY_H__ */ diff --git a/src/gm-triggers-dialog.c b/src/gm-triggers-dialog.c new file mode 100644 index 0000000..48e9b33 --- /dev/null +++ b/src/gm-triggers-dialog.c @@ -0,0 +1,991 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "gm-support.h" + +#include "debug.h" +#include "gm-triggers-dialog.h" +#include "gm-app.h" +#include "gm-pixbuf.h" +#include "gm-scripts.h" + +typedef struct _GmTriggersDialog { + GtkWidget *dialog; + GladeXML *xml; + GmWorld *world; + GmTrigger *trigger; + gboolean is_new; + + GtkWidget *entry_name; + GtkWidget *button_ok; + GtkWidget *button_next; + GtkWidget *vbox_conditions; + GtkWidget *vbox_actions; + GtkWidget *notebook_triggers; + GtkWidget *tree_view_event_types; + GtkWidget *hbox_add_condition; + GtkWidget *hbox_add_action; + GtkTreeModel *action_model; + GtkTreeModel *condition_model; + GtkTreeModel *highlight_model; + +#ifdef HAVE_RUBY + GtkTreeModel *script_model; +#endif +} GmTriggersDialog; + +enum { + NO_ARGS, + SINGLE_ARGS, + CUSTOM_ARGS +}; + +typedef enum _CustomArgType { + CUSTOM_ARG_CREATE, + CUSTOM_ARG_GET_DATA +} CustomArgType; + +gpointer gm_triggers_dialog_custom_arg_highlight(GmTriggersDialog *triggers, + CustomArgType type, gpointer data, gpointer user_data); +gpointer gm_triggers_dialog_custom_arg_browse(GmTriggersDialog *triggers, + CustomArgType type, gpointer data, gpointer user_data); + +#ifdef HAVE_RUBY +gpointer gm_triggers_dialog_custom_arg_script(GmTriggersDialog *triggers, + CustomArgType type, gpointer data, gpointer user_data); +#endif + +#define CUSTOM_ARG_FUNC(x) (CustomArgFunc *)(x) +typedef gpointer (*CustomArgFunc) (GmTriggersDialog *triggers, + CustomArgType type, gpointer data, gpointer user_data); + +enum { + COLUMN_NAME, + COLUMN_DATA, + N_COLUMNS +}; + +enum { + COLUMN_HIGH_NAME, + COLUMN_HIGH_TAG, + N_COLUMNS_HIGH +}; + +typedef struct _ModelData { + gint type; + gchar *title; + gint args; + CustomArgFunc func; +} ModelData; + +typedef struct _TagPair { + const gchar *name; + const gchar *tag; +} TagPair; + +typedef struct _ComboBoxTypeData { + GmTriggersDialog *triggers; + GmTriggerData *data; +} ComboBoxTypeData; + +static const TagPair highlightTags[] = { + {N_("Black"), "bg_black"}, + {N_("Red"), "bg_red"}, + {N_("Green"), "bg_green"}, + {N_("Yellow"), "bg_yellow"}, + {N_("Blue"), "bg_blue"}, + {N_("Purple"), "bg_purple"}, + {N_("Cyan"), "bg_cyan"}, + {N_("White"), "bg_white"}, + {NULL, NULL} +}; + +static const ModelData dataConditionOutput[] = { + {TCT_CONTAINS, N_("Contains"), SINGLE_ARGS, NULL}, + {TCT_NOT_CONTAINS, N_("Not contains"), SINGLE_ARGS, NULL}, + {TCT_BEGINS, N_("Begins with"), SINGLE_ARGS, NULL}, + {TCT_NOT_BEGINS, N_("Not begins with"), SINGLE_ARGS, NULL}, + {TCT_ENDS, N_("Ends with"), SINGLE_ARGS, NULL}, + {TCT_NOT_ENDS, N_("Not ends with"), SINGLE_ARGS, NULL}, + {TCT_MATCHES, N_("Matches"), SINGLE_ARGS, NULL}, + {TCT_NOT_MATCHES, N_("Not matches"), SINGLE_ARGS, NULL}, + {-1, NULL, 0, NULL} +}; + +static const ModelData dataConditionUsers[] = { + {TCT_USER_ONLINE, N_("Online"), SINGLE_ARGS, NULL}, + {TCT_USER_OFFLINE, N_("Offline"), SINGLE_ARGS, NULL}, + {TCT_USER_IDLE, N_("Idle"), SINGLE_ARGS, NULL}, + {TCT_USER_IDLE_OFF, N_("No longer idle"), SINGLE_ARGS, NULL}, + {TCT_USER_AWAY, N_("Away"), SINGLE_ARGS, NULL}, + {TCT_USER_AWAY_OFF, N_("No longer away"), SINGLE_ARGS, NULL}, + {-1, NULL, 0, NULL} +}; + +static const ModelData dataActionOutput[] = { + {TAT_HIGHLIGHT_LINE, N_("Highlight line"), CUSTOM_ARGS, + gm_triggers_dialog_custom_arg_highlight}, + {TAT_HIGHLIGHT_MATCH, N_("Highlight match"), CUSTOM_ARGS, + gm_triggers_dialog_custom_arg_highlight}, + {TAT_BEEP, N_("Beep"), NO_ARGS, NULL}, + {TAT_PLAY_SOUND, N_("Play sound"), CUSTOM_ARGS, + gm_triggers_dialog_custom_arg_browse}, + {TAT_NOTIFY, N_("Notify"), SINGLE_ARGS, NULL}, + #ifdef HAVE_RUBY + {TAT_RUN_SCRIPT, N_("Run script"), CUSTOM_ARGS, + gm_triggers_dialog_custom_arg_script}, + #endif + {TAT_RUN, N_("Run"), CUSTOM_ARGS, + gm_triggers_dialog_custom_arg_browse}, + {-1, NULL, 0, NULL} +}; + +static const ModelData dataActionUsers[] = { + {TAT_BEEP, N_("Beep"), NO_ARGS, NULL}, + {TAT_PLAY_SOUND, N_("Play sound"), CUSTOM_ARGS, + gm_triggers_dialog_custom_arg_browse}, + {TAT_NOTIFY, N_("Notify"), SINGLE_ARGS, NULL}, + #ifdef HAVE_RUBY + {TAT_RUN_SCRIPT, N_("Run script"), CUSTOM_ARGS, + gm_triggers_dialog_custom_arg_script}, + #endif + {TAT_RUN, N_("Run"), CUSTOM_ARGS, + gm_triggers_dialog_custom_arg_browse}, + {-1, NULL, 0, NULL} +}; + +void on_button_next_clicked(GtkButton *button, GmTriggersDialog *triggers); +void on_button_add_condition_clicked(GtkButton *button, + GmTriggersDialog *triggers); +void on_button_add_action_clicked(GtkButton *button, + GmTriggersDialog *triggers); +void on_notebook_triggers_switch_page(GtkNotebook *notebook, GtkNotebookPage + *page, guint page_num, GmTriggersDialog *triggers); +void on_combo_box_type_changed(GtkComboBox *widget, ComboBoxTypeData *tdata); +void on_combo_box_type_destroy(GtkObject *object, ComboBoxTypeData *tdata); +void on_button_remove_clicked(GtkButton *button, GmTriggersDialog *triggers); +void on_tree_view_event_types_changed(GtkTreeSelection *treeselection, + GmTriggersDialog *triggers); + +#define G_TRIGGERS_XML PACKAGE_DATA_DIR "/" PACKAGE "/ui/gm-triggers.glade" + +void +gm_triggers_dialog_create_models(GmTriggersDialog *triggers, + const ModelData *conditionData, const ModelData *actionData) { + int i; + GtkTreeIter iter; + ModelData *data; + GList *scripts, *item; + + #ifdef HAVE_RUBY + GmScript *script; + GmScriptFunction *func; + #endif + + triggers->action_model = GTK_TREE_MODEL(gtk_list_store_new( + N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER)); + triggers->condition_model = GTK_TREE_MODEL(gtk_list_store_new( + N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER)); + triggers->highlight_model = GTK_TREE_MODEL(gtk_list_store_new( + N_COLUMNS_HIGH, G_TYPE_STRING, G_TYPE_STRING)); + + #ifdef HAVE_RUBY + triggers->script_model = GTK_TREE_MODEL(gtk_list_store_new(1, + G_TYPE_STRING)); + #endif + + for (i = 0; conditionData[i].type != -1; i++) { + data = (ModelData *)(&(conditionData[i])); + gtk_list_store_append(GTK_LIST_STORE(triggers->condition_model), &iter); + gtk_list_store_set(GTK_LIST_STORE(triggers->condition_model), &iter, + COLUMN_NAME, _(data->title), COLUMN_DATA, data, -1); + } + + for (i = 0; actionData[i].type != -1; i++) { + data = (ModelData *)(&(actionData[i])); + gtk_list_store_append(GTK_LIST_STORE(triggers->action_model), &iter); + gtk_list_store_set(GTK_LIST_STORE(triggers->action_model), &iter, + COLUMN_NAME, _(data->title), COLUMN_DATA, data, -1); + } + + for (i = 0; highlightTags[i].name != NULL; i++) { + gtk_list_store_append(GTK_LIST_STORE(triggers->highlight_model), &iter); + gtk_list_store_set(GTK_LIST_STORE(triggers->highlight_model), &iter, + COLUMN_HIGH_NAME, _(highlightTags[i].name), COLUMN_HIGH_TAG, + highlightTags[i].tag, -1); + } + + #ifdef HAVE_RUBY + for (scripts = gm_scripts_scripts(gm_app_scripts(gm_app_instance())); + scripts; scripts = scripts->next) { + script = (GmScript *)(scripts->data); + + for (item = script->functions; item; item = item->next) { + func = (GmScriptFunction *)(item->data); + gtk_list_store_append(GTK_LIST_STORE(triggers->script_model), + &iter); + gtk_list_store_set(GTK_LIST_STORE(triggers->script_model), &iter, + 0, func->name, -1); + } + } + #endif +} + +ModelData * +gm_triggers_dialog_combo_get_selected_data(GmTriggersDialog *triggers, + GtkComboBox *combo) { + GtkTreeIter iter; + ModelData *data; + + gtk_combo_box_get_active_iter(combo, &iter); + gtk_tree_model_get(gtk_combo_box_get_model(combo), &iter, COLUMN_DATA, &data, + -1); + + return data; +} + +GList * +gm_triggers_dialog_collect_rules(GmTriggersDialog *triggers, GtkWidget *vbox) { + gchar *text; + GList *children, *child, *item, *result = NULL; + GtkComboBox *combo; + GtkEntry *entry; + ModelData *data; + GmTriggerData *d; + + children = gtk_container_get_children(GTK_CONTAINER(vbox)); + + for (item = children; item; item = item->next) { + if (item->data == triggers->hbox_add_condition || + item->data == triggers->hbox_add_action) { + break; + } + + if (GTK_IS_HBOX(item->data)) { + child = gtk_container_get_children(GTK_CONTAINER(item->data)); + combo = GTK_COMBO_BOX(child->data); + data = gm_triggers_dialog_combo_get_selected_data(triggers, combo); + d = NULL; + + switch (data->args) { + case NO_ARGS: + d = gm_trigger_data_new(data->type, NULL); + break; + case SINGLE_ARGS: + if (child->next) { + entry = GTK_ENTRY(child->next->data); + text = (gchar *)gtk_entry_get_text(entry); + + if (g_utf8_strlen(text, -1) > 0) { + d = gm_trigger_data_new(data->type, g_strdup(text)); + } + } + break; + case CUSTOM_ARGS: + text = (gchar *)(data->func(triggers, CUSTOM_ARG_GET_DATA, + child->next, NULL)); + + if (text != NULL && g_utf8_strlen(text, -1) > 0) { + d = gm_trigger_data_new(data->type, text); + } else { + g_free(text); + } + break; + default: + break; + } + + if (d) { + result = g_list_append(result, d); + } + + g_list_free(child); + } + } + + g_list_free(children); + return result; +} + +gboolean +gm_triggers_dialog_fill_trigger(GmTriggersDialog *triggers) { + const gchar *name = gtk_entry_get_text(GTK_ENTRY(triggers->entry_name)); + GList *conditions = NULL; + GList *actions = NULL; + + if (g_utf8_strlen(name, -1) == 0) { + gm_error_dialog(_("Please fill in a trigger name"), + GTK_WINDOW(triggers->dialog)); + gtk_widget_grab_focus(triggers->entry_name); + return FALSE; + } + + conditions = gm_triggers_dialog_collect_rules(triggers, + triggers->vbox_conditions); + + if (conditions == NULL) { + gm_error_dialog(_("Please specify at least one condition"), + GTK_WINDOW(triggers->dialog)); + return FALSE; + } + + actions = gm_triggers_dialog_collect_rules(triggers, triggers->vbox_actions); + + if (actions == NULL) { + gm_trigger_free_list(conditions); + gm_error_dialog(_("Please specify at least one action"), + GTK_WINDOW(triggers->dialog)); + return FALSE; + } + + gm_trigger_set_name(triggers->trigger, name); + gm_trigger_set_conditions(triggers->trigger, conditions); + gm_trigger_set_actions(triggers->trigger, actions); + + return TRUE; +} + +void +gm_triggers_dialog_initialize_event_types(GmTriggersDialog *triggers) { + GtkListStore *store = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, + G_TYPE_INT); + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkTreeIter iter; + + gtk_tree_view_set_model(GTK_TREE_VIEW(triggers->tree_view_event_types), + GTK_TREE_MODEL(store)); + + renderer = gtk_cell_renderer_pixbuf_new(); + column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "pixbuf", + 0, NULL); + gtk_tree_view_column_set_min_width(column, 40); + + gtk_tree_view_append_column(GTK_TREE_VIEW(triggers->tree_view_event_types), + column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "markup", + 1, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(triggers->tree_view_event_types), + column); + + gtk_list_store_prepend(store, &iter); + gtk_list_store_set(store, &iter, 0, + gm_pixbuf_get_at_size("ice-userlist/programmer.svg", 32, 32), 1, + _("Player event\nPlayer events are triggered on userlist " + "activity"), 2, TT_USERS, -1); + + gtk_list_store_prepend(store, &iter); + gtk_list_store_set(store, &iter, 0, + gm_pixbuf_get_at_size("world.svg", 32, 32), 1, + _("World event\nWorld events are triggered on incoming " + "lines of text"), 2, TT_OUTPUT, -1); + + gtk_tree_selection_select_iter(gtk_tree_view_get_selection( + GTK_TREE_VIEW(triggers->tree_view_event_types)), &iter); + g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW( + triggers->tree_view_event_types)), "changed", + G_CALLBACK(on_tree_view_event_types_changed), NULL); +} + +GmTrigger * +gm_triggers_dialog_run_dialog(GmTriggersDialog *triggers) { + gboolean done = FALSE; + GmTrigger *result = NULL; + + while (!done) { + done = TRUE; + + switch (gtk_dialog_run(GTK_DIALOG(triggers->dialog))) { + case GTK_RESPONSE_OK: + done = gm_triggers_dialog_fill_trigger(triggers); + + if (done) { + result = triggers->trigger; + } + break; + default: + break; + } + } + + if (triggers->action_model != NULL) + g_object_unref(triggers->action_model); + if (triggers->condition_model != NULL) + g_object_unref(triggers->condition_model); + if (triggers->highlight_model != NULL) + g_object_unref(triggers->highlight_model); + + #ifdef HAVE_RUBY + if (triggers->script_model != NULL) + g_object_unref(triggers->script_model); + #endif + + if (!result && triggers->is_new) { + gm_trigger_free(triggers->trigger); + } + + g_object_unref(triggers->xml); + gtk_widget_destroy(triggers->dialog); + + g_free(triggers); + + + return result; +} + +GmTrigger * +gm_triggers_dialog_run_priv(GmWorld *world, GmTrigger *trigger, + gboolean is_new) { + GmTriggersDialog *triggers = g_new0(GmTriggersDialog, 1); + + triggers->is_new = is_new; + triggers->world = world; + triggers->xml = glade_xml_new(G_TRIGGERS_XML, "gm_triggers_dialog", NULL); + triggers->dialog = glade_xml_get_widget(triggers->xml, + "gm_triggers_dialog"); + triggers->entry_name = glade_xml_get_widget(triggers->xml, "entry_name"); + triggers->vbox_conditions = glade_xml_get_widget(triggers->xml, + "vbox_conditions"); + triggers->vbox_actions = glade_xml_get_widget(triggers->xml, + "vbox_actions"); + triggers->notebook_triggers = glade_xml_get_widget(triggers->xml, + "notebook_triggers"); + triggers->tree_view_event_types = glade_xml_get_widget(triggers->xml, + "tree_view_event_types"); + triggers->button_ok = glade_xml_get_widget(triggers->xml, "button_ok"); + triggers->hbox_add_condition = glade_xml_get_widget(triggers->xml, + "hbox_add_condition"); + triggers->hbox_add_action = glade_xml_get_widget(triggers->xml, + "hbox_add_action"); + triggers->button_next = glade_xml_get_widget(triggers->xml, "button_next"); + + gm_triggers_dialog_initialize_event_types(triggers); + + glade_xml_signal_connect_data(triggers->xml, "on_button_next_clicked", + G_CALLBACK(on_button_next_clicked), triggers); + glade_xml_signal_connect_data(triggers->xml, + "on_button_add_condition_clicked", + G_CALLBACK(on_button_add_condition_clicked), triggers); + glade_xml_signal_connect_data(triggers->xml, "on_button_add_action_clicked", + G_CALLBACK(on_button_add_action_clicked), triggers); + glade_xml_signal_connect_data(triggers->xml, + "on_notebook_triggers_switch_page", + G_CALLBACK(on_notebook_triggers_switch_page), triggers); + + triggers->trigger = trigger; + + if (is_new) { + gtk_notebook_set_current_page( + GTK_NOTEBOOK(triggers->notebook_triggers), 0); + } else { + gtk_notebook_set_current_page( + GTK_NOTEBOOK(triggers->notebook_triggers), 1); + } + + gtk_widget_show_all(triggers->dialog); + return gm_triggers_dialog_run_dialog(triggers); +} + +GmTrigger * +gm_triggers_dialog_run(GmWorld *world, GmTrigger *trigger) { + return gm_triggers_dialog_run_priv(world, trigger, FALSE); +} + +GmTrigger * +gm_triggers_dialog_run_new(GmWorld *world, GmTrigger *trigger) { + if (!trigger) { + trigger = gm_trigger_new(); + } + + return gm_triggers_dialog_run_priv(world, trigger, TRUE); +} + +void +gm_triggers_dialog_select_combo_by_type(GmTriggersDialog *triggers, + GtkComboBox *combo, gint type) { + GtkTreeModel *model = gtk_combo_box_get_model(combo); + GtkTreeIter iter; + ModelData *data; + + if (gtk_tree_model_get_iter_first(model, &iter)) { + do { + gtk_tree_model_get(model, &iter, COLUMN_DATA, &data, -1); + + if (type == data->type) { + gtk_combo_box_set_active_iter(combo, &iter); + return; + } + } while (gtk_tree_model_iter_next(model, &iter)); + } +} + +GtkWidget * +gm_triggers_dialog_create_item(GmTriggersDialog *triggers, GtkTreeModel *model, + GmTriggerData *t) { + GtkWidget *hbox, *combo, *button; + ComboBoxTypeData *data; + GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); + hbox = gtk_hbox_new(FALSE, 6); + + combo = gtk_combo_box_new_with_model(model); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer, "text", + COLUMN_NAME, NULL); + + + data = g_new0(ComboBoxTypeData, 1); + data->triggers = triggers; + data->data = t; + + g_signal_connect(combo, "changed", G_CALLBACK(on_combo_box_type_changed), + data); + g_signal_connect(combo, "destroy", G_CALLBACK(on_combo_box_type_destroy), + data); + + button = gtk_button_new_from_stock(GTK_STOCK_REMOVE); + g_signal_connect(button, "clicked", G_CALLBACK(on_button_remove_clicked), + triggers); + + gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0); + gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, TRUE, 0); + + gtk_widget_show_all(hbox); + return hbox; +} + +void +gm_triggers_dialog_new_condition(GmTriggersDialog *triggers) { + GtkWidget *hbox; + GList *children; + + hbox = gm_triggers_dialog_create_item(triggers, triggers->condition_model, + NULL); + children = gtk_container_get_children(GTK_CONTAINER(hbox)); + + if (triggers->trigger->event == TT_OUTPUT) { + gm_triggers_dialog_select_combo_by_type(triggers, + GTK_COMBO_BOX(children->data), dataConditionOutput[0].type); + } else { + gm_triggers_dialog_select_combo_by_type(triggers, + GTK_COMBO_BOX(children->data), dataConditionUsers[0].type); + } + + gtk_box_pack_start(GTK_BOX(triggers->vbox_conditions), hbox, FALSE, TRUE, + 0); + g_list_free(children); +} + +void +gm_triggers_dialog_populate_conditions(GmTriggersDialog *triggers) { + GList *item; + GtkWidget *hbox; + GmTriggerData *t; + GList *children; + + if (triggers->trigger->conditions) { + for (item = triggers->trigger->conditions; item; item = item->next) { + t = (GmTriggerData *)(item->data); + + hbox = gm_triggers_dialog_create_item(triggers, + triggers->condition_model, t); + children = gtk_container_get_children(GTK_CONTAINER(hbox)); + + gtk_box_pack_start(GTK_BOX(triggers->vbox_conditions), hbox, + FALSE, TRUE, 0); + gm_triggers_dialog_select_combo_by_type(triggers, + GTK_COMBO_BOX(children->data), t->type); + g_list_free(children); + } + } else { + gm_triggers_dialog_new_condition(triggers); + } +} + +void +gm_triggers_dialog_new_action(GmTriggersDialog *triggers) { + GtkWidget *hbox; + GList *children; + + hbox = gm_triggers_dialog_create_item(triggers, triggers->action_model, + NULL); + children = gtk_container_get_children(GTK_CONTAINER(hbox)); + + if (triggers->trigger->event == TT_OUTPUT) { + gm_triggers_dialog_select_combo_by_type(triggers, + GTK_COMBO_BOX(children->data), dataActionOutput[0].type); + } else { + gm_triggers_dialog_select_combo_by_type(triggers, + GTK_COMBO_BOX(children->data), dataActionUsers[0].type); + } + + gtk_box_pack_start(GTK_BOX(triggers->vbox_actions), hbox, FALSE, TRUE, 0); + g_list_free(children); +} + +void +gm_triggers_dialog_populate_actions(GmTriggersDialog *triggers) { + GList *item; + GtkWidget *hbox; + GmTriggerData *t; + GList *children; + + if (triggers->trigger->actions) { + for (item = triggers->trigger->actions; item; item = item->next) { + t = (GmTriggerData *)(item->data); + hbox = gm_triggers_dialog_create_item(triggers, + triggers->action_model, t); + children = gtk_container_get_children(GTK_CONTAINER(hbox)); + + gtk_box_pack_start(GTK_BOX(triggers->vbox_actions), hbox, FALSE, + TRUE, 0); + gm_triggers_dialog_select_combo_by_type(triggers, + GTK_COMBO_BOX(children->data), t->type); + + g_list_free(children); + } + } else { + gm_triggers_dialog_new_action(triggers); + } +} + +/* CALLBACKS */ +void +on_button_next_clicked(GtkButton *button, GmTriggersDialog *triggers) { + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + gint type; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW( + triggers->tree_view_event_types)); + model = gtk_tree_view_get_model(GTK_TREE_VIEW( + triggers->tree_view_event_types)); + + if (gtk_tree_selection_get_selected(selection, &model, &iter)) { + gtk_tree_model_get(model, &iter, 2, &type, -1); + + triggers->trigger->event = type; + gtk_notebook_set_current_page(GTK_NOTEBOOK( + triggers->notebook_triggers), 1); + } else { + gm_error_dialog(_("Select a event type first"), + GTK_WINDOW(triggers->dialog)); + } +} + +void +on_tree_view_event_types_changed(GtkTreeSelection *treeselection, + GmTriggersDialog *triggers) { + GtkTreeIter iter; + GtkTreeModel *model; + gint type; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW( + triggers->tree_view_event_types)); + + if (!gtk_tree_selection_get_selected(treeselection, &model, &iter)) { + gtk_widget_set_sensitive(triggers->button_next, FALSE); + return; + } + + gtk_widget_set_sensitive(triggers->button_next, TRUE); + gtk_tree_model_get(model, &iter, 2, &type, -1); + + switch (type) { + case TT_OUTPUT: + gtk_window_set_icon(GTK_WINDOW(triggers->dialog), + gm_pixbuf_get_at_size("world.svg", 16, 16)); + break; + case TT_USERS: + gtk_window_set_icon(GTK_WINDOW(triggers->dialog), + gm_pixbuf_get_at_size("ice-userlist/programmer.svg", + 16, 16)); + break; + default: + break; + } +} + +void +on_notebook_triggers_switch_page(GtkNotebook *notebook, GtkNotebookPage *page, + guint page_num, GmTriggersDialog *triggers) { + const ModelData *conditionData = NULL; + const ModelData *actionData = NULL; + + if (page_num != 1) { + return; + } + + gtk_widget_set_sensitive(triggers->button_ok, TRUE); + + switch (triggers->trigger->event) { + case TT_OUTPUT: + gtk_window_set_icon(GTK_WINDOW(triggers->dialog), + gm_pixbuf_get_at_size("world.svg", 16, 16)); + conditionData = dataConditionOutput; + actionData = dataActionOutput; + break; + case TT_USERS: + gtk_window_set_icon(GTK_WINDOW(triggers->dialog), + gm_pixbuf_get_at_size("ice-userlist/programmer.svg", + 16, 16)); + conditionData = dataConditionUsers; + actionData = dataActionUsers; + break; + default: + break; + } + + gm_triggers_dialog_create_models(triggers, conditionData, actionData); + + if (triggers->trigger->name) { + gtk_entry_set_text(GTK_ENTRY(triggers->entry_name), + triggers->trigger->name); + } + + gm_triggers_dialog_populate_conditions(triggers); + gm_triggers_dialog_populate_actions(triggers); +} + +gpointer +gm_triggers_dialog_custom_arg_highlight(GmTriggersDialog *triggers, + CustomArgType type, gpointer data, gpointer user_data) { + GtkWidget *hbox; + GtkWidget *combo; + GtkCellRenderer *renderer; + GList *item; + GtkTreeIter iter; + GtkTreeModel *model; + gchar *tag; + GmTriggerData *t = (GmTriggerData *)(user_data); + + switch (type) { + case CUSTOM_ARG_CREATE: + hbox = GTK_WIDGET(data); + combo = gtk_combo_box_new_with_model(triggers->highlight_model); + renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer, + "text", COLUMN_HIGH_NAME, NULL); + + if (!user_data) { + gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); + } else { + model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); + + if (gtk_tree_model_get_iter_first(model, &iter)) { + do { + gtk_tree_model_get(model, &iter, COLUMN_HIGH_TAG, + &tag, -1); + + if (strcmp(tag, t->data) == 0) { + gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), + &iter); + break; + } + + g_free(tag); + } while (gtk_tree_model_iter_next(model, &iter)); + } + } + + gtk_widget_show(combo); + gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0); + break; + case CUSTOM_ARG_GET_DATA: + item = (GList *)(data); + model = gtk_combo_box_get_model(GTK_COMBO_BOX(item->data)); + gtk_combo_box_get_active_iter(GTK_COMBO_BOX(item->data), &iter); + gtk_tree_model_get(model, &iter, COLUMN_HIGH_TAG, &tag, -1); + + return tag; + break; + } + + return NULL; +} + +void +on_button_browse_clicked(GtkButton *widget, GtkEntry *entry) { + gchar *tmp; + GtkWidget *d = gtk_file_chooser_dialog_new(_("Select file"), + NULL, GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + + if (gtk_dialog_run(GTK_DIALOG(d)) == GTK_RESPONSE_ACCEPT) { + tmp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(d)); + gtk_entry_set_text(entry, tmp); + g_free(tmp); + } + + gtk_widget_destroy(d); +} + +gpointer +gm_triggers_dialog_custom_arg_browse(GmTriggersDialog *triggers, + CustomArgType type, gpointer data, gpointer user_data) { + GtkWidget *hbox; + GtkWidget *entry; + GtkWidget *browse, *tmp; + GmTriggerData *t = (GmTriggerData *)(user_data); + GList *item; + + switch (type) { + case CUSTOM_ARG_CREATE: + hbox = GTK_WIDGET(data); + entry = gtk_entry_new(); + + if (t) { + gtk_entry_set_text(GTK_ENTRY(entry), t->data); + } + + browse = gtk_button_new(); + tmp = gtk_hbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(browse), tmp); + gtk_box_pack_start(GTK_BOX(tmp), gtk_image_new_from_stock( + GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON), FALSE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(tmp), gtk_label_new(_("Browse")), + TRUE, TRUE, 0); + + g_signal_connect(browse, "clicked", + G_CALLBACK(on_button_browse_clicked), entry); + + gtk_widget_show(entry); + gtk_widget_show_all(browse); + gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(hbox), browse, FALSE, TRUE, 0); + break; + case CUSTOM_ARG_GET_DATA: + item = (GList *)(data); + return g_strdup(gtk_entry_get_text(GTK_ENTRY(item->data))); + break; + } + + return NULL; +} + +void +on_combo_box_type_changed(GtkComboBox *widget, ComboBoxTypeData *tdata) { + GtkWidget *parent = gtk_widget_get_parent(GTK_WIDGET(widget)); + GList *item, *children = gtk_container_get_children(GTK_CONTAINER(parent)); + ModelData *data; + GtkTreeIter iter; + GtkWidget *tmp; + + for (item = children->next; item && item->next; item = item->next) { + gtk_widget_destroy(GTK_WIDGET(item->data)); + } + + g_list_free(children); + + if (gtk_combo_box_get_active_iter(widget, &iter)) { + gtk_tree_model_get(gtk_combo_box_get_model(widget), &iter, + COLUMN_DATA, &data, -1); + + switch (data->args) { + case SINGLE_ARGS: + tmp = gtk_entry_new(); + + if (tdata->data) { + gtk_entry_set_text(GTK_ENTRY(tmp), tdata->data->data); + } + + gtk_widget_show(tmp); + gtk_box_pack_start(GTK_BOX(parent), tmp, TRUE, TRUE, 0); + break; + case CUSTOM_ARGS: + data->func(tdata->triggers, + CUSTOM_ARG_CREATE, (gpointer)parent, + (gpointer)tdata->data); + break; + default: + break; + } + + if (tdata->data != NULL) { + tdata->data = NULL; + } + } else { + debug_msg(0, "No active iter!"); + } +} + +void +on_combo_box_type_destroy(GtkObject *object, ComboBoxTypeData *tdata) { + g_free(tdata); +} + +#ifdef HAVE_RUBY +gpointer +gm_triggers_dialog_custom_arg_script(GmTriggersDialog *triggers, + CustomArgType type, gpointer data, gpointer user_data) { + GtkWidget *hbox; + GtkWidget *entry; + GtkEntryCompletion *entry_completion; + GList *item; + GmTriggerData *t = (GmTriggerData *)(user_data); + + switch (type) { + case CUSTOM_ARG_CREATE: + hbox = GTK_WIDGET(data); + entry = gtk_entry_new(); + entry_completion = gtk_entry_completion_new(); + gtk_entry_completion_set_model(entry_completion, + triggers->script_model); + gtk_entry_completion_set_text_column(entry_completion, 0); + gtk_entry_set_completion(GTK_ENTRY(entry), entry_completion); + + if (user_data) { + gtk_entry_set_text(GTK_ENTRY(entry), t->data); + } + + gtk_widget_show(entry); + gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, FALSE, 0); + break; + case CUSTOM_ARG_GET_DATA: + item = (GList *)(data); + return g_strdup(gtk_entry_get_text(GTK_ENTRY(item->data))); + break; + } + + return NULL; +} +#endif + +gboolean +gm_triggers_dialog_idle_remove_item(GtkWidget *parent) { + gtk_widget_destroy(parent); + return FALSE; +} + +void +on_button_remove_clicked(GtkButton *button, GmTriggersDialog *triggers) { + g_idle_add((GSourceFunc)(gm_triggers_dialog_idle_remove_item), + gtk_widget_get_parent(GTK_WIDGET(button))); +} + +void +on_button_add_condition_clicked(GtkButton *button, GmTriggersDialog *triggers) { + gm_triggers_dialog_new_condition(triggers); +} + +void +on_button_add_action_clicked(GtkButton *button, GmTriggersDialog *triggers) { + gm_triggers_dialog_new_action(triggers); +} diff --git a/src/gm-triggers-dialog.h b/src/gm-triggers-dialog.h new file mode 100644 index 0000000..81db91d --- /dev/null +++ b/src/gm-triggers-dialog.h @@ -0,0 +1,10 @@ +#ifndef __GM_TRIGGERS_DIALOG__ +#define __GM_TRIGGERS_DIALOG__ + +#include "gm-triggers.h" +#include "gm-world.h" + +GmTrigger *gm_triggers_dialog_run(GmWorld *world, GmTrigger *trig); +GmTrigger *gm_triggers_dialog_run_new(GmWorld *world, GmTrigger *trig); + +#endif /* __GM_TRIGGERS_DIALOG__ */ diff --git a/src/gm-triggers.c b/src/gm-triggers.c new file mode 100644 index 0000000..0a95389 --- /dev/null +++ b/src/gm-triggers.c @@ -0,0 +1,440 @@ +#include +#include + +#include +#include "gm-triggers.h" +#include "debug.h" + +#define GM_TRIGGERS_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_TRIGGERS, GmTriggersPrivate)) + +typedef struct _trigger_trans { + gint type; + const gchar *name; +} trigger_trans; + +static const trigger_trans table_conditions[] = { + {TCT_CONTAINS, "contains"}, + {TCT_NOT_CONTAINS, "not-contains"}, + {TCT_BEGINS, "begins"}, + {TCT_NOT_BEGINS, "not-begins"}, + {TCT_ENDS, "ends"}, + {TCT_NOT_ENDS, "not-ends"}, + {TCT_MATCHES, "matches"}, + {TCT_NOT_MATCHES, "not-matches"}, + {TCT_USER_ONLINE, "online"}, + {TCT_USER_OFFLINE, "offline"}, + {TCT_USER_IDLE, "idle"}, + {TCT_USER_IDLE_OFF, "idle-off"}, + {TCT_USER_AWAY, "away"}, + {TCT_USER_AWAY_OFF, "away-off"}, + {-1, NULL} +}; + +static const trigger_trans table_actions[] = { + {TAT_HIGHLIGHT_LINE, "highlight-line"}, + {TAT_HIGHLIGHT_MATCH, "highlight-match"}, + {TAT_BEEP, "beep"}, + {TAT_PLAY_SOUND, "play-sound"}, + {TAT_NOTIFY, "notify"}, + #ifdef HASRUBY + {TAT_RUN_SCRIPT, "run-script"}, + #endif + {TAT_RUN, "run"}, + {-1, NULL} +}; + +struct _GmTriggersPrivate { + GList *triggers; + gchar *path; +}; + +/* Signals */ + +/*enum { + PROTO + NUM_SIGNALS +}; + +static guint triggers_signals[NUM_SIGNALS] = {0};*/ + +G_DEFINE_TYPE(GmTriggers, gm_triggers, G_TYPE_OBJECT) + +static void +gm_triggers_finalize(GObject *object) { + GmTriggers *trg = GM_TRIGGERS(object); + GList *item; + + for (item = trg->priv->triggers; item; item = item->next) { + gm_trigger_free((GmTrigger *)(item->data)); + } + + g_free(trg->priv->path); + g_list_free(trg->priv->triggers); + + G_OBJECT_CLASS(gm_triggers_parent_class)->finalize(object); +} + +static void +gm_triggers_class_init(GmTriggersClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gm_triggers_finalize; + + g_type_class_add_private(object_class, sizeof(GmTriggersPrivate)); +} + +static void +gm_triggers_init(GmTriggers *trg) { + trg->priv = GM_TRIGGERS_GET_PRIVATE(trg); + trg->priv->triggers = NULL; + trg->priv->path = NULL; +} + +GmTrigger * +gm_trigger_new() { + return g_new0(GmTrigger, 1); +} + +GList * +gm_trigger_list_dup(GList *list) { + GList *result = NULL; + GList *item; + GmTriggerData *data; + + for (item = list; item; item = item->next) { + data = (GmTriggerData *)(item->data); + result = g_list_append(result, gm_trigger_data_new(data->type, + g_strdup(data->data))); + } + + return result; +} + +GmTrigger * +gm_trigger_dup(GmTrigger *source) { + GmTrigger *result = gm_trigger_new(); + + result->name = g_strdup(source->name); + result->event = source->event; + result->conditions = gm_trigger_list_dup(source->conditions); + result->actions = gm_trigger_list_dup(source->actions); + + return result; +} + +void +gm_trigger_set_name(GmTrigger *trigger, const gchar *name) { + g_free(trigger->name); + trigger->name = g_strdup(name); +} + +void +gm_trigger_free_list(GList *list) { + GList *item; + GmTriggerData *data; + + for (item = list; item; item = item->next) { + data = (GmTriggerData *)(item->data); + gm_trigger_data_free(data); + } + + g_list_free(list); +} + +void +gm_trigger_free(GmTrigger *trigger) { + gm_trigger_free_list(trigger->conditions); + gm_trigger_free_list(trigger->actions); + g_free(trigger->name); + + g_free(trigger); +} + +GmTriggerData * +gm_trigger_data_new(gint type, gchar *data) { + GmTriggerData *tdata = g_new0(GmTriggerData, 1); + tdata->type = type; + tdata->data = data; + + memset(&(tdata->expr), 0, sizeof(regex_t)); + + switch (tdata->type) { + case TCT_MATCHES: case TCT_NOT_MATCHES: case TCT_USER_ONLINE: + case TCT_USER_OFFLINE: case TCT_USER_IDLE: case TCT_USER_IDLE_OFF: + case TCT_USER_AWAY: case TCT_USER_AWAY_OFF: + regcomp(&(tdata->expr), tdata->data, REG_EXTENDED); + break; + default: + break; + } + + return tdata; +} + +void +gm_trigger_data_free(GmTriggerData *tdata) { + g_free(tdata->data); + g_free(tdata); +} + +void +gm_trigger_add_condition(GmTrigger *trigger, GmTriggerData *condition) { + trigger->conditions = g_list_append(trigger->conditions, condition); +} + +void +gm_trigger_add_action(GmTrigger *trigger, GmTriggerData *action) { + trigger->actions = g_list_append(trigger->actions, action); +} + +void +gm_trigger_set_conditions(GmTrigger *trigger, GList *conditions) { + gm_trigger_free_list(trigger->conditions); + trigger->conditions = conditions; +} + +void +gm_trigger_set_actions(GmTrigger *trigger, GList *actions) { + gm_trigger_free_list(trigger->actions); + trigger->actions = actions; +} + +gint +gm_trigger_type_from_name(const gchar *name, + const trigger_trans *trans_table) { + int i; + + for (i = 0; trans_table[i].type != -1; i++) { + if (strcasecmp(trans_table[i].name, name) == 0) { + return trans_table[i].type; + } + } + + return -1; +} + +const gchar * +gm_trigger_name_from_type(gint type, const trigger_trans *trans_table) { + int i; + + for (i = 0; trans_table[i].type != -1; i++) { + if (trans_table[i].type == type) { + return trans_table[i].name; + } + } + + return NULL; +} + +void +gm_triggers_parse_trigger(GmTriggers *trg, xmlDocPtr doc, xmlNodePtr node) { + GmTrigger *result = gm_trigger_new(); + xmlChar *tmp, *tmp2; + gint type; + + tmp = xmlGetProp(node, (const xmlChar *)"name"); + result->name = (gchar *)tmp; + + tmp = xmlGetProp(node, (const xmlChar *)"event"); + + if (xmlStrcmp(tmp, (const xmlChar *)("world")) == 0) { + result->event = TT_OUTPUT; + } else if (xmlStrcmp(tmp, (const xmlChar *)("player")) == 0){ + result->event = TT_USERS; + } else { + xmlFree(tmp); + gm_trigger_free(result); + return; + } + + xmlFree(tmp); + + for (node = node->xmlChildrenNode; node; node = node->next) { + tmp = xmlGetProp(node, (const xmlChar *)"type"); + tmp2 = xmlGetProp(node, (const xmlChar *)"data"); + + if (xmlStrcmp(node->name, (const xmlChar *)("condition")) == 0) { + type = gm_trigger_type_from_name((const gchar *)(tmp), + table_conditions); + + if (type != -1) { + gm_trigger_add_condition(result, gm_trigger_data_new(type, + g_strdup((char *)tmp2))); + } + } else if (xmlStrcmp(node->name, (const xmlChar *)("action")) == 0) { + type = gm_trigger_type_from_name((const gchar *)(tmp), + table_actions); + + if (type != -1) { + gm_trigger_add_action(result, gm_trigger_data_new(type, + g_strdup((char *)tmp2))); + } + } + + xmlFree(tmp2); + xmlFree(tmp); + } + + gm_triggers_add(trg, result); +} + +GmTriggers * +gm_triggers_new() { + GmTriggers *trg = GM_TRIGGERS(g_object_new(GM_TYPE_TRIGGERS, NULL)); + + return trg; +} + +void +gm_triggers_add(GmTriggers *trg, GmTrigger *t) { + trg->priv->triggers = g_list_append(trg->priv->triggers, t); +} + +void +gm_triggers_clear(GmTriggers *trg) { + GList *item; + + for (item = trg->priv->triggers; item; item = item->next) { + gm_trigger_free((GmTrigger *)(item->data)); + } + + g_list_free(trg->priv->triggers); + trg->priv->triggers = NULL; +} + +GmTriggers * +gm_triggers_new_from_file(gchar *filename) { + GmTriggers *trg = GM_TRIGGERS(g_object_new(GM_TYPE_TRIGGERS, NULL)); + xmlDocPtr doc; + xmlNodePtr cur; + + if (!g_file_test(filename, G_FILE_TEST_EXISTS)) { + debug_msg(1, "GmTriggers.NewFromFile: Trigger file does not exist"); + return trg; + + } + + trg->priv->path = g_strdup(filename); + doc = xmlParseFile(filename); + + if (doc == NULL) { + debug_msg(1, "GmTriggers.NewFromFile: Error on parsing triggers"); + return trg; + } + + cur = xmlDocGetRootElement(doc); + + if (cur == NULL) { + xmlFreeDoc(doc); + return trg; + } + + if (xmlStrcmp(cur->name, (const xmlChar *)("triggers"))) { + debug_msg(1, "GmTriggers.NewFromFile: invalid root node"); + xmlFreeDoc(doc); + return trg; + } + + for (cur = cur->xmlChildrenNode; cur; cur = cur->next) { + if (!xmlStrcmp(cur->name, (const xmlChar *)("trigger"))) { + gm_triggers_parse_trigger(trg, doc, cur); + } + } + + xmlFreeDoc(doc); + return trg; +} + +void +gm_triggers_set_path(GmTriggers *trg, gchar *path) { + g_free(trg->priv->path); + trg->priv->path = g_strdup(path); +} + +void +gm_trigger_rules_xml(GmTrigger *t, xmlNodePtr trig) { + GmTriggerData *data; + GList *item; + xmlNodePtr child; + + for (item = t->conditions; item; item = item->next) { + data = (GmTriggerData *)(item->data); + child = xmlNewChild(trig, NULL, (const xmlChar *)("condition"), NULL); + xmlNewProp(child, (const xmlChar *)("type"), (const xmlChar *) + (gm_trigger_name_from_type(data->type, table_conditions))); + + if (data->data) { + xmlNewProp(child, (const xmlChar *)("data"), (const xmlChar *) + (data->data)); + } + } + + for (item = t->actions; item; item = item->next) { + data = (GmTriggerData *)(item->data); + child = xmlNewChild(trig, NULL, (const xmlChar *)("action"), NULL); + xmlNewProp(child, (const xmlChar *)("type"), (const xmlChar *) + (gm_trigger_name_from_type(data->type, table_actions))); + + if (data->data) { + xmlNewProp(child, (const xmlChar *)("data"), (const xmlChar *) + (data->data)); + } + } +} + +void +gm_triggers_save(GmTriggers *trg) { + xmlDocPtr doc; + xmlNodePtr root; + xmlNodePtr trig; + GList *item; + GmTrigger *t; + + g_return_if_fail(trg->priv->path != NULL); + + doc = xmlNewDoc((const xmlChar *)("1.0")); + root = xmlNewNode(NULL, (const xmlChar *)("triggers")); + xmlDocSetRootElement(doc, root); + + for (item = trg->priv->triggers; item; item = item->next) { + t = (GmTrigger *)(item->data); + trig = xmlNewChild(root, NULL, (const xmlChar *)("trigger"), NULL); + xmlNewProp(trig, (const xmlChar *)("name"), (const xmlChar *)(t->name)); + + if (t->event == TT_OUTPUT) { + xmlNewProp(trig, (const xmlChar *)("event"), (const xmlChar *)("world")); + } else { + xmlNewProp(trig, (const xmlChar *)("event"), (const xmlChar *)("player")); + } + + gm_trigger_rules_xml(t, trig); + } + + xmlSaveFormatFileEnc(trg->priv->path, doc, "UTF-8", 1); + xmlFreeDoc(doc); +} + +void +gm_triggers_save_as(GmTriggers *trg, const gchar *path) { + g_free(trg->priv->path); + trg->priv->path = g_strdup(path); + gm_triggers_save(trg); +} + +GmTriggers * +gm_triggers_dup(GmTriggers *source) { + GmTriggers *trg = GM_TRIGGERS(g_object_new(GM_TYPE_TRIGGERS, NULL)); + GList *item; + + for (item = source->priv->triggers; item; item = item->next) { + trg->priv->triggers = g_list_append(trg->priv->triggers, + gm_trigger_dup((GmTrigger *)(item->data))); + } + + return trg; +} + +const GList * +gm_triggers_list(GmTriggers *trg) { + return trg->priv->triggers; +} diff --git a/src/gm-triggers.h b/src/gm-triggers.h new file mode 100644 index 0000000..c2f9196 --- /dev/null +++ b/src/gm-triggers.h @@ -0,0 +1,118 @@ +#ifndef __GM_TRIGGERS_H__ +#define __GM_TRIGGERS_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GM_TYPE_TRIGGERS (gm_triggers_get_type()) +#define GM_TRIGGERS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_TRIGGERS, GmTriggers)) +#define GM_TRIGGERS_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_TRIGGERS, GmTriggers const)) +#define GM_TRIGGERS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_TRIGGERS, GmTriggersClass)) +#define GM_IS_TRIGGERS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_TRIGGERS)) +#define GM_IS_TRIGGERS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_TRIGGERS)) +#define GM_TRIGGERS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_TRIGGERS, GmTriggersClass)) + +/* Private structure type */ +typedef struct _GmTriggersPrivate GmTriggersPrivate; + +/* + * Main object structure + */ +typedef struct _GmTriggers GmTriggers; + +struct _GmTriggers { + GObject object; + + /*< private > */ + GmTriggersPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GmTriggersClass GmTriggersClass; + +struct _GmTriggersClass { + GObjectClass parent_class; + + /* Signals */ +}; + +typedef enum _GmTriggerType { + TT_OUTPUT = 0, + TT_USERS +} GmTriggerType; + +typedef enum _GmTriggerConditionType { + TCT_CONTAINS = 0, + TCT_NOT_CONTAINS, + TCT_BEGINS, + TCT_NOT_BEGINS, + TCT_ENDS, + TCT_NOT_ENDS, + TCT_MATCHES, + TCT_NOT_MATCHES, + TCT_USER_ONLINE, + TCT_USER_OFFLINE, + TCT_USER_IDLE, + TCT_USER_IDLE_OFF, + TCT_USER_AWAY, + TCT_USER_AWAY_OFF +} GmTriggerConditionType; + +typedef enum _GmTriggerActionType { + TAT_HIGHLIGHT_LINE = 0, + TAT_HIGHLIGHT_MATCH, + TAT_BEEP, + TAT_PLAY_SOUND, + TAT_NOTIFY, + TAT_RUN_SCRIPT, + TAT_RUN +} GmTriggerActionType; + +typedef struct _GmTriggerData { + gint type; + gchar *data; + regex_t expr; +} GmTriggerData; + +typedef struct _GmTrigger { + gchar *name; + GmTriggerType event; + + GList *conditions; + GList *actions; +} GmTrigger; + +GType gm_triggers_get_type(void) G_GNUC_CONST; +GmTriggers *gm_triggers_new(void); +GmTriggers *gm_triggers_new_from_file(gchar *filename); +void gm_triggers_save(GmTriggers *triggers); +void gm_triggers_save_as(GmTriggers *trg, const gchar *path); +void gm_triggers_set_path(GmTriggers *trg, gchar *path); +GmTriggers *gm_triggers_dup(GmTriggers *source); +const GList *gm_triggers_list(GmTriggers *trg); +void gm_triggers_add(GmTriggers *trg, GmTrigger *t); +void gm_triggers_clear(GmTriggers *trg); + +GmTrigger *gm_trigger_new(); +void gm_trigger_free(GmTrigger *trigger); +GmTrigger *gm_trigger_dup(GmTrigger *source); +void gm_trigger_set_name(GmTrigger *trigger, const gchar *name); +void gm_trigger_free_list(GList *list); +void gm_trigger_add_condition(GmTrigger *trigger, GmTriggerData *condition); +void gm_trigger_add_action(GmTrigger *trigger, GmTriggerData *action); +void gm_trigger_set_conditions(GmTrigger *trigger, GList *conditions); +void gm_trigger_set_actions(GmTrigger *trigger, GList *actions); + +GmTriggerData *gm_trigger_data_new(gint type, gchar *data); +void gm_trigger_data_free(GmTriggerData *tdata); + +G_END_DECLS +#endif /* __GM_TRIGGERS_H__ */ diff --git a/src/gm-ui.h b/src/gm-ui.h new file mode 100644 index 0000000..5799ac9 --- /dev/null +++ b/src/gm-ui.h @@ -0,0 +1,77 @@ +#ifndef __GM_UI_H__ +#define __GM_UI_H__ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include "gm-app-view.h" +#include "gm-support.h" + +G_BEGIN_DECLS + +static const GtkActionEntry gm_sensitive_menu_entries[] = +{ + /* Toplevel */ + {"World", NULL, N_("_World")}, + {"Edit", NULL, N_("_Edit")}, + {"View", NULL, N_("_View")}, + {"Help", NULL, N_("_Help")}, + + /* World menu */ + {"WorldNew", GTK_STOCK_NEW, N_("New World..."), "N", + N_("Create a new world"), G_CALLBACK(on_gm_app_view_world_new)}, + {"WorldQuit", GTK_STOCK_QUIT, NULL, NULL, + N_("Quit the program"), G_CALLBACK(on_gm_app_view_world_quit)}, + + /* Edit menu */ + {"EditWorlds", NULL, N_("Worlds..."), "L", + N_("Edit worlds"), G_CALLBACK(on_gm_app_view_edit_worlds)}, + {"EditPreferences", GTK_STOCK_PREFERENCES, NULL, NULL, + N_("Configure the application"), + G_CALLBACK(on_gm_app_view_edit_preferences)}, + + /* View menu */ + {"ViewMcp", NULL, N_("MCP"), NULL, + N_("View MCP console"), G_CALLBACK(on_gm_app_view_view_mcp)}, + {"ViewScripts", NULL, N_("Scripts"), NULL, + N_("View scripts"), G_CALLBACK(on_gm_app_view_view_scripts)}, + + /* Help menu */ + {"HelpAbout", GTK_STOCK_ABOUT, NULL, NULL, + N_("About this application"), G_CALLBACK(on_gm_app_view_help_about)} +}; + +static const GtkActionEntry gm_menu_entries[] = +{ + /* File menu */ + {"WorldConnect", GTK_STOCK_NETWORK, N_("Connect"), "C", + N_("Connect or disconnect the current world"), + G_CALLBACK(on_gm_app_view_world_connect)}, + {"WorldClose", GTK_STOCK_CLOSE, NULL, NULL, + N_("Close current world"), G_CALLBACK(on_gm_app_view_world_close)}, + {"WorldLogs", GTK_STOCK_FILE, N_("Logs"), NULL, + N_("View current world logs"), G_CALLBACK(on_gm_app_view_world_logs)}, + {"WorldInfo", GTK_STOCK_ABOUT, N_("In_fo"), NULL, + N_("View current world info"), G_CALLBACK(on_gm_app_view_world_info)}, + + /* Edit menu */ + {"EditCut", GTK_STOCK_CUT, NULL, "X", + N_("Cut the selection"), G_CALLBACK(on_gm_app_view_edit_cut)}, + {"EditCopy", GTK_STOCK_COPY, NULL, "C", + N_("Copy the selection"), G_CALLBACK(on_gm_app_view_edit_copy)}, + {"EditPaste", GTK_STOCK_PASTE, NULL, "V", + N_("Paste the clipboard"), G_CALLBACK(on_gm_app_view_edit_paste)}, + {"EditWorld", NULL, N_("Current world..."), "E", + N_("Edit the current world"), G_CALLBACK(on_gm_app_view_edit_world)}, + {"EditFind", GTK_STOCK_FIND, NULL, "F", + N_("Find text"), G_CALLBACK(on_gm_app_view_edit_find)}, + {"EditFindNext", GTK_STOCK_FIND, N_("Find next"), "G", + N_("Find next occurence"), + G_CALLBACK(on_gm_app_view_edit_find_next)}, +}; + +G_END_DECLS + +#endif /* __GEDIT_UI_H__ */ diff --git a/src/gm-world-info-dialog.c b/src/gm-world-info-dialog.c new file mode 100644 index 0000000..2e9b619 --- /dev/null +++ b/src/gm-world-info-dialog.c @@ -0,0 +1,111 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include "gm-world.h" +#include "gm-support.h" +#include "gm-pixbuf.h" + +#define G_WORLD_INFO_XML PACKAGE_DATA_DIR "/" PACKAGE "/ui/gm-world-info.glade" + +gboolean on_gm_world_info_dialog_url_button_release(GtkWidget *button, + GdkEventButton *event, gchar *link); + +/* Private */ +void +gm_world_info_dialog_set_label(GtkLabel *label, gchar *text) { + if (text) { + gtk_label_set_markup(label, text); + } else { + gtk_label_set_markup(label, _("unspecified")); + } +} + +/* Public */ + +GtkDialog * +gm_world_info_dialog_new(GmWorldInfo world_info) { + GladeXML *xml; + GtkWidget *dlg, *widget; + gchar *tmp; + + xml = glade_xml_new(G_WORLD_INFO_XML, "dlgWorldInfo", NULL); + dlg = glade_xml_get_widget(xml, "dlgWorldInfo"); + + gm_world_info_dialog_set_label(GTK_LABEL(glade_xml_get_widget(xml, + "lblName")), world_info.admin); + gm_world_info_dialog_set_label(GTK_LABEL(glade_xml_get_widget(xml, + "lblLocation")), world_info.location); + gm_world_info_dialog_set_label(GTK_LABEL(glade_xml_get_widget(xml, + "lblSystem")), world_info.system); + gm_world_info_dialog_set_label(GTK_LABEL(glade_xml_get_widget(xml, + "lblCharset")), world_info.charset); + gm_world_info_dialog_set_label(GTK_LABEL(glade_xml_get_widget(xml, + "lblLanguage")), world_info.language); + + if (world_info.homepage) { + tmp = g_strconcat("", world_info.homepage, + "", NULL); + glade_xml_signal_connect_data(xml, + "on_gm_world_info_dialog_url_button_release", + G_CALLBACK(on_gm_world_info_dialog_url_button_release), NULL); + widget = glade_xml_get_widget(xml, "lblHomepage"); + gtk_label_set_markup(GTK_LABEL(widget), tmp); + g_free(tmp); + } + + if (world_info.contact) { + tmp = g_strconcat("", world_info.contact, + "", NULL); + glade_xml_signal_connect_data(xml, + "on_gm_world_info_dialog_url_button_release", + G_CALLBACK(on_gm_world_info_dialog_url_button_release), "mailto:"); + widget = glade_xml_get_widget(xml, "lblEmail"); + gtk_label_set_markup(GTK_LABEL(widget), tmp); + g_free(tmp); + } + + if (world_info.logo) { + gtk_image_set_from_pixbuf(GTK_IMAGE(glade_xml_get_widget(xml, + "imageLogo")), gm_pixbuf_get(world_info.logo)); + } + + glade_xml_signal_connect(xml, "on_dlgWorldInfo_delete", + G_CALLBACK(gtk_widget_destroy)); + glade_xml_signal_connect_data(xml, "on_buttonClose_clicked", + G_CALLBACK(gm_widget_destroy_data), dlg); + + gtk_widget_show_all(dlg); + + g_object_unref(xml); + return GTK_DIALOG(dlg); +} + +/* Callbacks */ + +gboolean +on_gm_world_info_dialog_url_button_release(GtkWidget *label, + GdkEventButton *event, gchar *prefix) { + GError *err = NULL; + gchar *tmp, *link; + + if (prefix != NULL) { + link = g_strconcat(prefix, gtk_label_get_text(GTK_LABEL(label)), NULL); + } else { + link = g_strdup(gtk_label_get_text(GTK_LABEL(label))); + } + + if (!gnome_url_show(link, &err)) { + tmp = g_strdup_printf("Could not open link: %s", err->message); + gm_error_dialog(tmp, NULL); + g_free(tmp); + g_error_free(err); + } + + g_free(link); + return FALSE; +} diff --git a/src/gm-world-info-dialog.h b/src/gm-world-info-dialog.h new file mode 100644 index 0000000..32e0bce --- /dev/null +++ b/src/gm-world-info-dialog.h @@ -0,0 +1,6 @@ +#ifndef __GM_WORLD_INFO_DIALOG_H__ +#define __GM_WORLD_INFO_DIALOG_H__ + +GtkDialog *gm_world_info_dialog_new(GmWorldInfo world_info); + +#endif /* __GM_WORLD_INFO_DIALOG_H__ */ diff --git a/src/gm-world-input-view.c b/src/gm-world-input-view.c new file mode 100644 index 0000000..6a2c274 --- /dev/null +++ b/src/gm-world-input-view.c @@ -0,0 +1,383 @@ +#include +#include +#include +#include + +#include "gm-world-input-view.h" +#include "gm-world-view.h" +#include "gm-world.h" +#include "gm-color-table.h" +#include "debug.h" + +#define GM_WORLD_INPUT_VIEW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_WORLD_INPUT_VIEW, GmWorldInputViewPrivate)) + +void on_gm_world_input_view_color_table_color_changed(GmColorTable *table, + gchar *name, GmWorldInputView *view); +void on_gm_world_input_view_color_table_font_changed(GmColorTable *table, + gchar *font_description, GmWorldInputView *view); + +gboolean on_gm_world_input_view_key_pressed(GtkWidget *widget, + GdkEventKey * event, gpointer userdata); +gboolean on_gm_world_input_view_key_released(GtkWidget *widget, + GdkEventKey *event, gpointer userdata); + +struct _GmWorldInputViewPrivate { + GmColorTable *color_table; + GList **history; + GList *position; + gchar *prefix; +}; + +/* Signals */ + +enum { + TEXT_ACTIVATE, + NUM_SIGNALS +}; + +static guint world_input_view_signals[NUM_SIGNALS] = {0}; + +G_DEFINE_TYPE(GmWorldInputView, gm_world_input_view, GTK_TYPE_TEXT_VIEW) + +/* Private functions */ +static void +gm_world_input_view_finalize(GObject *object) { + GmWorldInputView *view = GM_WORLD_INPUT_VIEW(object); + + gm_world_input_view_set_color_table(view, NULL); + + G_OBJECT_CLASS(gm_world_input_view_parent_class)->finalize(object); +} + +static void +gm_world_input_view_class_init(GmWorldInputViewClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gm_world_input_view_finalize; + + world_input_view_signals[TEXT_ACTIVATE] = + g_signal_new("text_activate", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmWorldInputViewClass, text_activate), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + g_type_class_add_private(object_class, sizeof(GmWorldInputViewPrivate)); +} + +static void +gm_world_input_view_init(GmWorldInputView *view) { + view->priv = GM_WORLD_INPUT_VIEW_GET_PRIVATE(view); + + view->priv->color_table = NULL; + view->priv->history = NULL; + view->priv->position = NULL; + view->priv->prefix = NULL; + + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD_CHAR); + gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(view), TRUE); + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view), 3); + gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view), 3); + gtk_text_view_set_pixels_above_lines(GTK_TEXT_VIEW(view), 1); + gtk_text_view_set_pixels_below_lines(GTK_TEXT_VIEW(view), 1); + + g_signal_connect(view, "key_press_event", + G_CALLBACK(on_gm_world_input_view_key_pressed), NULL); + g_signal_connect(view, "key_release_event", + G_CALLBACK(on_gm_world_input_view_key_released), NULL); +} + +gchar * +gm_world_input_view_str_new_value(gchar *old, gchar *new) { + g_free(old); + + if (new) { + return g_strdup(new); + } else { + return NULL; + } +} + +void +gm_world_input_view_set_text(GmWorldInputView *view, gchar *text, gint len) { + GtkTextIter end; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view)); + + gtk_text_buffer_set_text(buffer, text, len); + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_place_cursor(buffer, &end); +} + +void +gm_world_input_view_update_colors(GmWorldInputView *view) { + GdkColor col; + + if (view->priv->color_table != NULL) { + if (gm_color_table_get(view->priv->color_table, "bg_default", &col)) { + gtk_widget_modify_base(GTK_WIDGET(view), GTK_STATE_NORMAL, + &col); + } + if (gm_color_table_get(view->priv->color_table, "fg_default", &col)) { + gtk_widget_modify_text(GTK_WIDGET(view), GTK_STATE_NORMAL, + &col); + } + } +} + +void +gm_world_input_view_update_font(GmWorldInputView *view) { + PangoFontDescription *f = pango_font_description_from_string( + gm_color_table_font_description(view->priv->color_table)); + + if (f != NULL) { + gtk_widget_modify_font(GTK_WIDGET(view), f); + pango_font_description_free(f); + } +} + +/* Public functions */ +GtkWidget * +gm_world_input_view_new() { + GtkWidget *result; + GmColorTable *table = gm_color_table_new(); + result = gm_world_input_view_new_with_color_table(table); + g_object_unref(table); + + return result; +} + +GtkWidget * +gm_world_input_view_new_with_color_table(GmColorTable *color_table) { + GmWorldInputView *view = GM_WORLD_INPUT_VIEW( + g_object_new(GM_TYPE_WORLD_INPUT_VIEW, NULL)); + + gm_world_input_view_set_color_table(view, color_table); + + return GTK_WIDGET(view); +} + +void +gm_world_input_view_set_history(GmWorldInputView *view, GList **history) { + view->priv->history = history; + + view->priv->position = NULL; + view->priv->prefix = NULL; +} + +GList ** +gm_world_input_view_history(GmWorldInputView *view) { + return view->priv->history; +} + +void +gm_world_input_view_set_color_table(GmWorldInputView *view, + GmColorTable *color_table) { + if (view->priv->color_table != NULL) { + g_signal_handlers_disconnect_by_func(view->priv->color_table, + on_gm_world_input_view_color_table_color_changed, view); + g_signal_handlers_disconnect_by_func(view->priv->color_table, + on_gm_world_input_view_color_table_font_changed, view); + g_object_unref(view->priv->color_table); + } + + if (color_table != NULL) { + view->priv->color_table = g_object_ref(color_table); + g_signal_connect(view->priv->color_table, "color_changed", + G_CALLBACK(on_gm_world_input_view_color_table_color_changed), view); + g_signal_connect(view->priv->color_table, "font_changed", + G_CALLBACK(on_gm_world_input_view_color_table_font_changed), view); + + gm_world_input_view_update_colors(view); + gm_world_input_view_update_font(view); + } else { + view->priv->color_table = NULL; + } +} + +GmColorTable * +gm_world_input_view_color_table(GmWorldInputView *view) { + return view->priv->color_table; +} + +/* Callbacks */ +void +on_gm_world_input_view_color_table_color_changed(GmColorTable *table, + gchar *name, GmWorldInputView *view) { + if (strcmp(name, "fg_default") == 0 || strcmp(name, "bg_default") == 0) { + gm_world_input_view_update_colors(view); + } +} + +void +on_gm_world_input_view_color_table_font_changed(GmColorTable *table, + gchar *font_description, GmWorldInputView *view) { + gm_world_input_view_update_font(view); +} + +gboolean +on_gm_world_input_view_key_released(GtkWidget *widget, GdkEventKey *event, + gpointer userdata) { + GmWorldInputView *view = GM_WORLD_INPUT_VIEW(widget); + GtkTextBuffer *buffer; + GtkTextIter start, end; + gchar *text; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view)); + gtk_text_buffer_get_bounds(buffer, &start, &end); + text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); + + switch (event->keyval) { + case GDK_Delete: case GDK_BackSpace: + if (text[0] == '\0') { + // Reset position to the last item in the history + view->priv->position = g_list_last(*view->priv->history); + + // Reset prefix to NULL + view->priv->prefix = gm_world_input_view_str_new_value( + view->priv->prefix, NULL); + } + break; + } + + g_free(text); + return FALSE; +} + +gboolean +on_gm_world_input_view_key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer userdata) { + GmWorldInputView *view = GM_WORLD_INPUT_VIEW(widget); + gchar *text; + gboolean result = TRUE, isUp; + GtkTextBuffer *buf; + GtkTextIter start, end; + GList *item, *found = NULL; + + buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view)); + + gtk_text_buffer_get_bounds(buf, &start, &end); + text = gtk_text_buffer_get_text(buf, &start, &end, FALSE); + + switch (event->keyval) { + case GDK_Up: case GDK_Down: + isUp = event->keyval == GDK_Up; + + if (!view->priv->history) { + break; + } + + // If the current position is empty then append a new history item + if (!view->priv->position) { + *view->priv->history = g_list_append(*view->priv->history, + g_strdup(text)); + view->priv->position = g_list_last(*view->priv->history); + } + + // If there is nowhere to move, don't even bother + if ((isUp && !view->priv->position->prev) + || (!isUp && !view->priv->position->next)) { + break; + } + + // If the current prefix is NULL then we set the new prefix to + // the current text + if (view->priv->prefix == NULL) { + debug_msg(0, "Prefix: %s", text); + view->priv->prefix = g_strdup(text); + } + + // If the prefix is an empty string then simply advance to the + // next item + if (view->priv->prefix[0] == '\0') { + if (isUp) { + found = view->priv->position->prev; + } else { + found = view->priv->position->next; + } + } else { + // Else find the closest matching history line + item = isUp ? view->priv->position->prev : + view->priv->position->next; + while (item) { + if (strncmp((gchar *)item->data, view->priv->prefix, + strlen(view->priv->prefix)) == 0) { + // Change current position to the matched item + found = item; + break; + } + + item = isUp ? item->prev : item->next; + } + } + + // If a match is found then set this history text + if (found) { + // Change the data of the current position to the text + // now in the buffer. + view->priv->position->data = + gm_world_input_view_str_new_value( + view->priv->position->data, text); + gm_world_input_view_set_text(view, (gchar *)found->data, -1); + view->priv->position = found; + + if (found == g_list_last(*view->priv->history)) { + view->priv->prefix = gm_world_input_view_str_new_value( + view->priv->prefix, NULL); + } + } + break; + case GDK_Return: + // Emit the text_activate signal + if (!(event->state & GDK_CONTROL_MASK) && + !(event->state & GDK_SHIFT_MASK)) { + + g_signal_emit(view, world_input_view_signals[TEXT_ACTIVATE], 0, + text); + + debug_msg(0, "%d", view->priv->history); + + if (view->priv->history) { + item = g_list_last(*view->priv->history); + + if (item) { + item->data = gm_world_input_view_str_new_value( + (gchar *)(item->data), text); + } else { + *view->priv->history = + g_list_append(*view->priv->history, + g_strdup(text)); + } + } + + // TODO: manage history length + + // Append new empty history item which will become our new + // current item + if (view->priv->history) { + *view->priv->history = + g_list_append(*view->priv->history, g_strdup("")); + view->priv->position = g_list_last(*view->priv->history); + } + + // Reset prefix to NULL + view->priv->prefix = gm_world_input_view_str_new_value( + view->priv->prefix, NULL); + + // Set textview text to an empty string + gm_world_input_view_set_text(view, "", 0); + } else { + result = FALSE; + } + break; + default: + result = FALSE; + break; + } + + g_free(text); + return result; +} diff --git a/src/gm-world-input-view.h b/src/gm-world-input-view.h new file mode 100644 index 0000000..60baf1a --- /dev/null +++ b/src/gm-world-input-view.h @@ -0,0 +1,57 @@ +#ifndef __GM_WORLD_INPUT_VIEW_H__ +#define __GM_WORLD_INPUT_VIEW_H__ + +#include +#include "gm-color-table.h" + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GM_TYPE_WORLD_INPUT_VIEW (gm_world_input_view_get_type()) +#define GM_WORLD_INPUT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD_INPUT_VIEW, GmWorldInputView)) +#define GM_WORLD_INPUT_VIEW_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD_INPUT_VIEW, GmWorldInputView const)) +#define GM_WORLD_INPUT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_WORLD_INPUT_VIEW, GmWorldInputViewClass)) +#define GM_IS_WORLD_INPUT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_WORLD_INPUT_VIEW)) +#define GM_IS_WORLD_INPUT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_WORLD_INPUT_VIEW)) +#define GM_WORLD_INPUT_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_WORLD_INPUT_VIEW, GmWorldInputViewClass)) + +/* Private structure type */ +typedef struct _GmWorldInputViewPrivate GmWorldInputViewPrivate; + +/* + * Main object structure + */ +typedef struct _GmWorldInputView GmWorldInputView; + +struct _GmWorldInputView { + GtkTextView textview; + + /*< private > */ + GmWorldInputViewPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GmWorldInputViewClass GmWorldInputViewClass; + +struct _GmWorldInputViewClass { + GtkTextViewClass parent_class; + + /* Signals */ + void (* text_activate) (GmWorldInputView *view, const gchar *text); +}; + +GType gm_world_input_view_get_type(void) G_GNUC_CONST; +GtkWidget *gm_world_input_view_new(); +GtkWidget *gm_world_input_view_new_with_color_table(GmColorTable *table); +void gm_world_input_view_set_color_table(GmWorldInputView *view, + GmColorTable *color_table); +GmColorTable *gm_world_input_view_color_table(GmWorldInputView *view); +void gm_world_input_view_set_history(GmWorldInputView *view, GList **history); +GList **gm_world_input_view_history(GmWorldInputView *view); + +G_END_DECLS +#endif /* __GM_WORLD_INPUT_VIEW_H__ */ diff --git a/src/gm-world-logs-dialog.c b/src/gm-world-logs-dialog.c new file mode 100644 index 0000000..5a9519b --- /dev/null +++ b/src/gm-world-logs-dialog.c @@ -0,0 +1,116 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include "gm-world.h" +#include "gm-support.h" + +#define G_WORLD_LOGS_XML PACKAGE_DATA_DIR "/" PACKAGE "/ui/gm-world-info.glade" + +void +on_gm_world_logs_dialog_row_activated(GtkTreeView *treeview, GtkTreePath *arg1, + GtkTreeViewColumn *arg2, GtkDialog *dlg); + +/* Public */ + +GtkDialog * +gm_world_logs_dialog_new(GmWorld *world, GtkTreeView **view, + GtkProgressBar **progress) { + GladeXML *xml = glade_xml_new(G_WORLD_LOGS_XML, "dlgLogs", NULL); + GtkDialog *dlg; + GtkTreeModel *model, *smodel; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + gchar *path, *tmp, *tmp2; + const gchar *fname; + GDir *dir; + GError *err; + GtkTreeIter iter; + FILE *f; + long fsize; + int res = 0; + + dlg = GTK_DIALOG(glade_xml_get_widget(xml, "dlgLogs")); + *view = GTK_TREE_VIEW(glade_xml_get_widget(xml, "tvwLogs")); + + model = GTK_TREE_MODEL(gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING)); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Logs"), renderer, + "text", 1, NULL); + gtk_tree_view_append_column(*view, column); + + smodel = gtk_tree_model_sort_new_with_model(model); + gtk_tree_view_column_set_sort_column_id(column, 0); + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(smodel), 0, + GTK_SORT_DESCENDING); + + gtk_tree_view_set_model(*view, GTK_TREE_MODEL(smodel)); + + /* Fill it in */ + path = g_strdup_printf("%s/logs/", gm_world_path(world)); + dir = g_dir_open(path, 0, &err); + + if (dir) { + while ((fname = g_dir_read_name(dir)) != NULL) { + res++; + tmp = g_strconcat(path, fname, NULL); + f = fopen(tmp, "r"); + + if (f) { + fseek(f, 0, SEEK_END); + fsize = ftell(f); + fclose(f); + g_free(tmp); + + tmp = gnome_vfs_format_file_size_for_display(fsize); + tmp2 = g_strconcat(fname, " (", tmp, ")", NULL); + + gtk_list_store_prepend(GTK_LIST_STORE(model), &iter); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, fname, 1, + tmp2, -1); + g_free(tmp2); + } + + g_free(tmp); + } + + g_dir_close(dir); + + if (res != 0) { + gtk_tree_model_get_iter_first(smodel, &iter); + gtk_tree_selection_select_iter(gtk_tree_view_get_selection(*view), &iter); + glade_xml_signal_connect_data(xml, "on_tvwLogs_row_activated", + G_CALLBACK(on_gm_world_logs_dialog_row_activated), dlg); + + *progress = GTK_PROGRESS_BAR(glade_xml_get_widget(xml, + "progressBarLoading")); + } else { + gm_info_dialog(_("There are no log files for this world"), NULL); + gtk_widget_destroy(GTK_WIDGET(dlg)); + dlg = NULL; + } + } else { + tmp = g_strdup_printf(_("Couldn't open the log directory: %s"), err->message); + gm_error_dialog(tmp, NULL); + g_error_free(err); + g_free(tmp); + gtk_widget_destroy(GTK_WIDGET(dlg)); + dlg = NULL; + } + + g_free(path); + g_object_unref(xml); + + return dlg; +} + +/* Callbacks */ + +void +on_gm_world_logs_dialog_row_activated(GtkTreeView *treeview, GtkTreePath *arg1, + GtkTreeViewColumn *arg2, GtkDialog *dlg) { + gtk_dialog_response(dlg, GTK_RESPONSE_OK); +} diff --git a/src/gm-world-logs-dialog.h b/src/gm-world-logs-dialog.h new file mode 100644 index 0000000..c057402 --- /dev/null +++ b/src/gm-world-logs-dialog.h @@ -0,0 +1,10 @@ +#ifndef __GM_WORLD_LOGS_DIALOG_H__ +#define __GM_WORLD_LOGS_DIALOG_H__ + +#include +#include "gm-world.h" + +GtkDialog *gm_world_logs_dialog_new(GmWorld *world, GtkTreeView **view, + GtkProgressBar **progress); + +#endif /* __GM_WORLD_LOGS_DIALOG_H__ */ diff --git a/src/gm-world-properties-dialog.c b/src/gm-world-properties-dialog.c new file mode 100644 index 0000000..3184d25 --- /dev/null +++ b/src/gm-world-properties-dialog.c @@ -0,0 +1,674 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "gm-world-properties-dialog.h" +#include "gm-triggers.h" +#include "gm-support.h" +#include "gm-app.h" +#include "debug.h" +#include "gm-options.h" +#include "gm-pixbuf.h" + +typedef struct _GmWorldPropertiesDialog { + GtkWidget *dialog; + GtkWidget *combo_box_charset; + GtkWidget *tree_view_triggers; + + GladeXML *xml; + GmWorld *world; + gboolean is_new; + + gulong handler_id; +} GmWorldPropertiesDialog; + +static const encoding encodings[] = { + {"ISO-8859-1", N_("Western")}, + {"ISO-8859-2", N_("Central European")}, + {"ISO-8859-3", N_("South European")}, + {"ISO-8859-4", N_("Baltic")}, + {"ISO-8859-5", N_("Cyrillic")}, + {"ISO-8859-6", N_("Arabic")}, + {"ISO-8859-7", N_("Greek")}, + {"ISO-8859-8", N_("Hebrew Visual")}, + {"ISO-8859-8-I", N_("Hebrew")}, + {"ISO-8859-9", N_("Turkish")}, + {"ISO-8859-10", N_("Nordic")}, + {"ISO-8859-13", N_("Baltic")}, + {"ISO-8859-14", N_("Celtic")}, + {"ISO-8859-15", N_("Western")}, + {"ISO-8859-16", N_("Romanian")}, + {"UTF-7", N_("Unicode")}, + {"UTF-8", N_("Unicode")}, + {"UTF-16", N_("Unicode")}, + {"UCS-2", N_("Unicode")}, + {"UCS-4", N_("Unicode")}, + {"ARMSCII-8", N_("Armenian")}, + {"BIG5", N_("Chinese Traditional")}, + {"BIG5-HKSCS", N_("Chinese Traditional")}, + {"CP866", N_("Cyrillic/Russian")}, + + {"EUC-JP", N_("Japanese")}, + {"EUC-KR", N_("Korean")}, + {"EUC-TW", N_("Chinese Traditional")}, + {"GB18030", N_("Chinese Simplified")}, + {"GB2312", N_("Chinese Simplified")}, + {"GBK", N_("Chinese Simplified")}, + {"GEORGIAN-ACADEMY", N_("Georgian")}, + {"HZ", N_("Chinese Simplified")}, + + {"IBM850", N_("Western")}, + {"IBM852", N_("Central European")}, + {"IBM855", N_("Cyrillic")}, + {"IBM857", N_("Turkish")}, + {"IBM862", N_("Hebrew")}, + {"IBM864", N_("Arabic")}, + + {"ISO-2022-JP", N_("Japanese")}, + {"ISO-2022-KR", N_("Korean")}, + {"ISO-IR-111", N_("Cyrillic")}, + {"JOHAB", N_("Korean")}, + {"KOI8R", N_("Cyrillic")}, + {"KOI8-R", N_("Cyrillic")}, + {"KOI8U", N_("Cyrillic/Ukrainian")}, + + {"SHIFT_JIS", N_("Japanese")}, + {"TCVN", N_("Vietnamese")}, + {"TIS-620", N_("Thai")}, + {"UHC", N_("Korean")}, + {"VISCII", N_("Vietnamese")}, + + {"WINDOWS-1250", N_("Central European")}, + {"WINDOWS-1251", N_("Cyrillic")}, + {"WINDOWS-1252", N_("Western")}, + {"WINDOWS-1253", N_("Greek")}, + {"WINDOWS-1254", N_("Turkish")}, + {"WINDOWS-1255", N_("Hebrew")}, + {"WINDOWS-1256", N_("Arabic")}, + {"WINDOWS-1257", N_("Baltic")}, + {"WINDOWS-1258", N_("Vietnamese")} +}; + +static GList *gm_world_properties_dialog_open = NULL; + +void gm_world_properties_dialog_run_dialog(GmWorldPropertiesDialog *properties); + +void on_button_add_trigger_clicked(GtkButton *button, + GmWorldPropertiesDialog *properties); +void on_button_edit_trigger_clicked(GtkButton *button, + GmWorldPropertiesDialog *properties); +void on_button_delete_trigger_clicked(GtkButton *button, + GmWorldPropertiesDialog *properties); +void on_tree_view_triggers_row_activated(GtkTreeView *treeview, + GtkTreePath *arg1, GtkTreeViewColumn *arg2, + GmWorldPropertiesDialog *properties); +void on_gm_world_properties_dialog_app_world_removed(GmApp *app, GmWorld *world, + GmWorldPropertiesDialog *properties); + +void on_gm_world_properties_dialog_response(GtkDialog *dialog, gint response, + GmWorldPropertiesDialog *properties); + +GtkWidget * +gm_world_properties_dialog_widget(GmWorldPropertiesDialog *properties, + gchar *name) { + return glade_xml_get_widget(properties->xml, name); +} + +void +gm_world_properties_dialog_populate_charsets( + GmWorldPropertiesDialog *properties) { + int i; + const gchar *select = gm_options_get(gm_world_options(properties->world), + "charset"); + gchar text[255]; + GtkListStore *store; + GtkComboBoxEntry *combo = GTK_COMBO_BOX_ENTRY(properties->combo_box_charset); + store = gtk_list_store_new(1, G_TYPE_STRING); + gtk_combo_box_set_model(GTK_COMBO_BOX(combo), GTK_TREE_MODEL(store)); + gtk_combo_box_entry_set_text_column(combo, 0); + + for (i = 0; i < (int)(sizeof(encodings) / sizeof(encoding)); i++) { + sprintf(text, "%s - %s", encodings[i].charset, _(encodings[i].name)); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo), text); + + if (strcasecmp(encodings[i].charset, select) == 0) { + gtk_entry_set_text(GTK_ENTRY(GTK_BIN(combo)->child), text); + } + } +} + +void +gm_world_properties_dialog_remove_trigger(GmWorldPropertiesDialog *properties, + GtkTreeIter *iter) { + GtkTreeView *tree_view = GTK_TREE_VIEW(properties->tree_view_triggers); + GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(tree_view)); + + gtk_list_store_remove(store, iter); + + if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL) == 0) { + gtk_widget_set_sensitive(gm_world_properties_dialog_widget(properties, + "button_edit_trigger"), FALSE); + gtk_widget_set_sensitive(gm_world_properties_dialog_widget(properties, + "button_delete_trigger"), FALSE); + } +} + +void +gm_world_properties_dialog_update_trigger(GmWorldPropertiesDialog *properties, + GtkTreeIter *iter, GmTrigger *t) { + GtkTreeView *tree_view = GTK_TREE_VIEW(properties->tree_view_triggers); + GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(tree_view)); + gchar *text; + + text = g_strdup_printf(_("%s\nConditions: %d\nActions: %d"), + t->name, g_list_length(t->conditions), g_list_length(t->actions)); + gtk_list_store_set(store, iter, 1, text, 2, t, -1); + + switch (t->event) { + case TT_OUTPUT: + gtk_list_store_set(store, iter, 0, + gm_pixbuf_get_at_size("world.svg", 32, 32), -1); + break; + case TT_USERS: + gtk_list_store_set(store, iter, 0, + gm_pixbuf_get_at_size("ice-userlist/programmer.svg", 32, + 32), -1); + break; + default: + break; + } + + g_free(text); +} + +void +gm_world_properties_dialog_add_trigger(GmWorldPropertiesDialog *properties, + GmTrigger *t) { + GtkTreeView *tree_view = GTK_TREE_VIEW(properties->tree_view_triggers); + GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(tree_view)); + GtkTreeIter iter; + + gtk_list_store_append(store, &iter); + gm_world_properties_dialog_update_trigger(properties, &iter, t); + + gtk_tree_selection_select_iter(gtk_tree_view_get_selection(tree_view), + &iter); + + gtk_widget_set_sensitive(gm_world_properties_dialog_widget(properties, + "button_edit_trigger"), TRUE); + gtk_widget_set_sensitive(gm_world_properties_dialog_widget(properties, + "button_deleteTrigger"), TRUE); +} + +void +gm_world_properties_dialog_populate_tree_view_triggers( + GmWorldPropertiesDialog *properties) { + GtkTreeView *tree_view = GTK_TREE_VIEW(properties->tree_view_triggers); + GtkListStore *store = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, + G_TYPE_POINTER); + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + const GList *item; + GmTrigger *t; + + gtk_tree_view_set_model(GTK_TREE_VIEW(tree_view), GTK_TREE_MODEL(store)); + + renderer = gtk_cell_renderer_pixbuf_new(); + column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "pixbuf", + 0, NULL); + gtk_tree_view_column_set_min_width(column, 40); + + gtk_tree_view_append_column(tree_view, column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "markup", + 1, NULL); + gtk_tree_view_append_column(tree_view, column); + + for (item = gm_triggers_list(gm_world_triggers(properties->world)); item; + item = item->next) { + t = gm_trigger_dup((GmTrigger *)(item->data)); + gm_world_properties_dialog_add_trigger(properties, t); + } + + if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL) == 0) { + gtk_widget_set_sensitive(gm_world_properties_dialog_widget(properties, + "button_edit_trigger"), FALSE); + gtk_widget_set_sensitive(gm_world_properties_dialog_widget(properties, + "button_delete_trigger"), FALSE); + } +} + +GmWorldPropertiesDialog * +gm_world_properties_dialog_find(GmWorld *world) { + GmWorldPropertiesDialog *result = NULL; + GList *item; + + for (item = gm_world_properties_dialog_open; item; item = item->next) { + result = (GmWorldPropertiesDialog *)(item->data); + + if (result->world == world) { + return result; + } + } + + return NULL; +} + +void +gm_world_properties_dialog_initialize(GmWorldPropertiesDialog *properties) { + GmOptions *options = gm_world_options(properties->world); + const gchar *logo; + + gtk_entry_set_text(GTK_ENTRY(gm_world_properties_dialog_widget(properties, + "entry_name")), gm_options_get(options, "name")); + gtk_entry_set_text(GTK_ENTRY(gm_world_properties_dialog_widget(properties, + "entry_host")), gm_options_get(options, "host")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(gm_world_properties_dialog_widget( + properties, "spin_button_port")), (double)(gm_options_get_int( + options, "port"))); + gtk_entry_set_text(GTK_ENTRY(gm_world_properties_dialog_widget(properties, + "entry_player_name")), gm_options_get(options, "player_name")); + gtk_entry_set_text(GTK_ENTRY(gm_world_properties_dialog_widget(properties, + "entry_password")), gm_options_get(options, "password")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON( + gm_world_properties_dialog_widget(properties, + "check_button_auto_reconnect")), + gm_options_get_int(options, "reconnect")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON( + gm_world_properties_dialog_widget(properties, + "check_button_auto_load")), + gm_options_get_int(options, "autoload")); + + gm_world_properties_dialog_populate_charsets(properties); + gm_world_properties_dialog_populate_tree_view_triggers(properties); + + glade_xml_signal_connect_data(properties->xml, + "on_button_add_trigger_clicked", + G_CALLBACK(on_button_add_trigger_clicked), properties); + glade_xml_signal_connect_data(properties->xml, + "on_button_edit_trigger_clicked", + G_CALLBACK(on_button_edit_trigger_clicked), properties); + glade_xml_signal_connect_data(properties->xml, + "on_button_delete_trigger_clicked", + G_CALLBACK(on_button_delete_trigger_clicked), properties); + glade_xml_signal_connect_data(properties->xml, + "on_tree_view_triggers_row_activated", + G_CALLBACK(on_tree_view_triggers_row_activated), properties); + + logo = gm_options_get(options, "logo"); + + if (logo != NULL) { + gtk_window_set_icon(GTK_WINDOW(properties->dialog), + gm_pixbuf_get(logo)); + } else { + gtk_window_set_icon(GTK_WINDOW(properties->dialog), + gm_pixbuf_get("world.svg")); + } +} + +#define G_WORLD_PROPERTIES_DIALOG_XML PACKAGE_DATA_DIR "/" PACKAGE \ + "/ui/gm-world-properties.glade" + +void +gm_world_properties_dialog_run_priv(GmWorld *world, gboolean is_new) { + GmWorldPropertiesDialog *properties = + gm_world_properties_dialog_find(world); + gchar *title; + + if (properties) { + gtk_widget_show(properties->dialog); + gtk_window_present(GTK_WINDOW(properties->dialog)); + return; + } + + properties = g_new0(GmWorldPropertiesDialog, 1); + gm_world_properties_dialog_open = g_list_append( + gm_world_properties_dialog_open, properties); + + properties->is_new = is_new; + properties->world = world; + properties->xml = glade_xml_new(G_WORLD_PROPERTIES_DIALOG_XML, + "gm_world_properties_dialog", NULL); + + title = g_strconcat(_("World properties - "), gm_world_name(world), NULL); + properties->dialog = gm_world_properties_dialog_widget(properties, + "gm_world_properties_dialog"); + properties->tree_view_triggers = gm_world_properties_dialog_widget( + properties, "tree_view_triggers"); + properties->combo_box_charset = gm_world_properties_dialog_widget( + properties, "combo_box_charset"); + + gtk_window_set_title(GTK_WINDOW(properties->dialog), title); + g_free(title); + + gm_world_properties_dialog_initialize(properties); + + if (!properties->is_new) { + // Connect signal so we can close the dialog when a world gets removed + properties->handler_id = g_signal_connect(gm_app_instance(), + "world_removed", + G_CALLBACK(on_gm_world_properties_dialog_app_world_removed), + properties); + } else { + properties->handler_id = 0; + } + + gm_world_properties_dialog_run_dialog(properties); +} + +void +gm_world_properties_dialog_run(GmWorld *world) { + gm_world_properties_dialog_run_priv(world, FALSE); +} + +void +gm_world_properties_dialog_run_new(GmWorld *world) { + if (!world) { + world = gm_world_new(NULL); + } + + gm_world_properties_dialog_run_priv(world, TRUE); +} + +gchar * +gm_world_properties_dialog_get_charset(gchar *charset) { + gchar *ch; + int i; + + for (i = 0; i < (int)(sizeof(encodings) / sizeof(encoding)); i++) { + ch = g_strconcat(encodings[i].charset, " - ", _(encodings[i].name), + NULL); + + if (g_strcasecmp(charset, ch) == 0) { + g_free(ch); + return g_strdup(encodings[i].charset); + } + + g_free(ch); + } + + return g_strdup(charset); +} + +void +gm_world_properties_dialog_free_triggers(GmWorldPropertiesDialog *properties) { + GtkTreeView *tree_view = GTK_TREE_VIEW(properties->tree_view_triggers); + GtkTreeModel *store = gtk_tree_view_get_model(tree_view); + GtkTreeIter iter; + GmTrigger *t; + + if (gtk_tree_model_get_iter_first(store, &iter)) { + do { + gtk_tree_model_get(store, &iter, 2, &t, -1); + gm_trigger_free(t); + } while (gtk_tree_model_iter_next(store, &iter)); + } +} + +void +gm_world_properties_dialog_set_triggers(GmWorldPropertiesDialog *properties) { + GtkTreeView *tree_view = GTK_TREE_VIEW(properties->tree_view_triggers); + GtkTreeModel *store = gtk_tree_view_get_model(tree_view); + GtkTreeIter iter; + GmTrigger *t; + GmTriggers *triggers = gm_world_triggers(properties->world); + + gm_triggers_clear(triggers); + + if (gtk_tree_model_get_iter_first(store, &iter)) { + do { + gtk_tree_model_get(store, &iter, 2, &t, -1); + gm_triggers_add(triggers, t); + } while (gtk_tree_model_iter_next(store, &iter)); + } +} + +gboolean +gm_world_properties_dialog_check_values(GmWorldPropertiesDialog *properties) { + GmWorld *same_world; + GmOptions *options = gm_world_options(properties->world); + GtkWidget *dialog; + GtkEntry *entry_name = GTK_ENTRY(gm_world_properties_dialog_widget( + properties, "entry_name")); + gchar *name = g_strdup(gtk_entry_get_text(entry_name)); + GtkEntry *entry_host = GTK_ENTRY(gm_world_properties_dialog_widget( + properties, "entry_host")); + gchar *host = g_strdup(gtk_entry_get_text(entry_host)); + + g_strstrip(name); + g_strstrip(host); + + same_world = gm_app_world_by_name(gm_app_instance(), name); + + if (strlen(name) == 0) { + dialog = gtk_message_dialog_new(GTK_WINDOW(properties->dialog), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, + "Name can not be empty, please fill in a name."); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + gtk_notebook_set_current_page(GTK_NOTEBOOK( + gm_world_properties_dialog_widget(properties, + "notebook_main")), 0); + gtk_widget_grab_focus(GTK_WIDGET(entry_name)); + g_free(name); + g_free(host); + return FALSE; + } + + + if (same_world && same_world != properties->world) { + dialog = gtk_message_dialog_new(GTK_WINDOW(properties->dialog), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, + _("Name can not be %s because a world with that name " + "already exists."), + name); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + + gtk_notebook_set_current_page(GTK_NOTEBOOK( + gm_world_properties_dialog_widget(properties, + "notebook_main")), 0); + gtk_widget_grab_focus(GTK_WIDGET(entry_name)); + g_free(name); + g_free(host); + return FALSE; + } + + if (strlen(host) == 0) { + dialog = gtk_message_dialog_new(GTK_WINDOW(properties->dialog), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, + _("Host can not be empty, please fill in a host.")); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + + gtk_notebook_set_current_page(GTK_NOTEBOOK( + gm_world_properties_dialog_widget(properties, + "notebook_main")), 0); + gtk_widget_grab_focus(GTK_WIDGET(entry_host)); + g_free(name); + g_free(host); + + return FALSE; + } + + gm_world_properties_dialog_set_triggers(properties); + + gm_options_set(options, "host", host); + gm_options_set_int(options, "port", gtk_spin_button_get_value_as_int( + GTK_SPIN_BUTTON(gm_world_properties_dialog_widget(properties, + "spin_button_port")))); + + gm_options_set_int(options, "reconnect", gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(gm_world_properties_dialog_widget(properties, + "check_button_auto_reconnect")))); + gm_options_set_int(options, "autoload", gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(gm_world_properties_dialog_widget(properties, + "check_button_auto_load")))); + + gm_options_set(options, "player_name", (gchar *)gtk_entry_get_text( + GTK_ENTRY(gm_world_properties_dialog_widget(properties, + "entry_player_name")))); + gm_options_set(options, "password", (gchar *)gtk_entry_get_text( + GTK_ENTRY(gm_world_properties_dialog_widget(properties, + "entry_password")))); + gm_options_set(options, "charset", gm_world_properties_dialog_get_charset( + (gchar *)gtk_entry_get_text(GTK_ENTRY(GTK_BIN( + properties->combo_box_charset)->child)))); + + // Only change the name when it has actually changed, when the name + // changes the options and triggers are automatically saved to the new + // world location + if (gm_options_get(options, "name") == NULL || + strcmp(name, gm_options_get(options, "name")) != 0) { + gm_options_set(options, "name", name); + } else { + // Now we need to save the options and triggers ourselfs + gm_options_save(options); + gm_triggers_save(gm_world_triggers(properties->world)); + } + + g_free(name); + g_free(host); + + return TRUE; +} + +void +gm_world_properties_dialog_run_dialog(GmWorldPropertiesDialog *properties) { + g_signal_connect(properties->dialog, "response", + G_CALLBACK(on_gm_world_properties_dialog_response), properties); + + gtk_widget_show(GTK_WIDGET(properties->dialog)); +} + +/* CALLBACKS */ + +void +on_gm_world_properties_dialog_response(GtkDialog *dialog, gint response, + GmWorldPropertiesDialog *properties) { + gboolean is_okay = TRUE; + + switch (response) { + case GTK_RESPONSE_OK: + is_okay = gm_world_properties_dialog_check_values(properties); + break; + default: + gm_world_properties_dialog_free_triggers(properties); + break; + } + + if (!is_okay) { + return; + } + + if (properties->is_new) { + if (response == GTK_RESPONSE_OK) { + gm_app_add_world(gm_app_instance(), properties->world); + } else { + g_object_unref(properties->world); + } + } + + gtk_widget_destroy(properties->dialog); + + if (properties->handler_id > 0) { + g_signal_handler_disconnect(gm_app_instance(), properties->handler_id); + } + + g_object_unref(properties->xml); + gm_world_properties_dialog_open = g_list_remove( + gm_world_properties_dialog_open, properties); + + g_free(properties); +} + +void +on_button_add_trigger_clicked(GtkButton *button, + GmWorldPropertiesDialog *properties) { + // TODO +/* Trigger *t = create_dlgTriggers(loadedWld, NULL); + + if (t != NULL) { + add_trigger(t); + }*/ +} + +GmTrigger * +gm_world_properties_dialog_selected_trigger(GmWorldPropertiesDialog *properties, + GtkTreeIter *iter) { + GtkTreeView *tree_view = GTK_TREE_VIEW(properties->tree_view_triggers); + GtkTreeModel *model = gtk_tree_view_get_model(tree_view); + GmTrigger *result = NULL; + + if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(tree_view), + &model, iter)) { + gtk_tree_model_get(model, iter, 2, &result, -1); + } + + return result; +} + +void +on_button_edit_trigger_clicked(GtkButton *button, + GmWorldPropertiesDialog *properties) { + // TODO + /*GtkTreeIter iter; + GmTrigger *t, *newt; + + t = gm_world_properties_dialog_selected_trigger(properties, &iter); + + if (t) { + newt = create_dlgTriggers(loadedWld, t); + + if (newt) { + update_trigger(&iter, t); + } + } else { + gnoemoe_error_dialog(_("Select a trigger to edit first"), GTK_WINDOW(if_world_properties_get_widget("dlgWorldProperties"))); + }*/ +} + +void +on_button_delete_trigger_clicked(GtkButton *button, + GmWorldPropertiesDialog *properties) { + GtkTreeIter iter; + GmTrigger *t; + + t = gm_world_properties_dialog_selected_trigger(properties, &iter); + + if (t) { + gm_world_properties_dialog_remove_trigger(properties, &iter); + gm_trigger_free(t); + } else { + gm_error_dialog(_("First select a trigger to remove"), + GTK_WINDOW(properties->dialog)); + } +} + +void +on_tree_view_triggers_row_activated(GtkTreeView *treeview, GtkTreePath *arg1, + GtkTreeViewColumn *arg2, GmWorldPropertiesDialog *properties) { + on_button_edit_trigger_clicked(NULL, properties); +} + +void +on_gm_world_properties_dialog_app_world_removed(GmApp *app, GmWorld *world, + GmWorldPropertiesDialog *properties) { + if (properties->world == world) { + gtk_dialog_response(GTK_DIALOG(properties->dialog), GTK_RESPONSE_CLOSE); + } +} diff --git a/src/gm-world-properties-dialog.h b/src/gm-world-properties-dialog.h new file mode 100644 index 0000000..f77dfb6 --- /dev/null +++ b/src/gm-world-properties-dialog.h @@ -0,0 +1,16 @@ +#ifndef __GM_WORLD_PROPERTIES_DIALOG__ +#define __GM_WORLD_PROPERTIES_DIALOG__ + +#include +#include "gm-world.h" +#include + +typedef struct _encoding { + const gchar *charset; + const gchar *name; +} encoding; + +void gm_world_properties_dialog_run(GmWorld *world); +void gm_world_properties_dialog_run_new(GmWorld *world); + +#endif /* __GM_WORLD_PROPERTIES_DIALOG__ */ diff --git a/src/gm-world-tab.c b/src/gm-world-tab.c new file mode 100644 index 0000000..3c15b61 --- /dev/null +++ b/src/gm-world-tab.c @@ -0,0 +1,196 @@ +#include +#include "gm-world.h" +#include "gm-world-tab.h" +#include "gm-support.h" +#include "gm-pixbuf.h" +#include "debug.h" + +#define GM_WORLD_TAB_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_WORLD_TAB, GmWorldTabPrivate)) + +void on_gm_world_tab_button_clicked(GtkButton *button, GmWorldTab *tab); +void on_gm_world_tab_world_activity_changed(GmWorld *world, gint activity, + GmWorldTab *tab); +void on_gm_world_tab_world_name_changed(GmWorld *world, gchar *text, + GmWorldTab *tab); + +struct _GmWorldTabPrivate { + GmWorld *world; + + GtkImage *image; + GtkLabel *label; + GtkButton *button; + + guint source_id; +}; + +// Signals + +enum { + CLOSE, + NUM_SIGNALS +}; + +static guint gm_world_tab_signals[NUM_SIGNALS] = {0}; + +G_DEFINE_TYPE(GmWorldTab, gm_world_tab, GTK_TYPE_HBOX) + +static void +gm_world_tab_stop_recolor(GmWorldTab *tab) { + if (tab->priv->source_id != 0) { + g_source_remove(tab->priv->source_id); + tab->priv->source_id = 0; + } +} + +static void +gm_world_tab_finalize(GObject *object) { + GmWorldTab *obj = GM_WORLD_TAB(object); + + gm_world_tab_stop_recolor(obj); + + if (obj->priv->world) { + g_signal_handlers_disconnect_by_func(obj->priv->world, + G_CALLBACK(on_gm_world_tab_world_activity_changed), obj); + g_signal_handlers_disconnect_by_func(obj->priv->world, + G_CALLBACK(on_gm_world_tab_world_name_changed), obj); + g_object_unref(obj->priv->world); + } + + G_OBJECT_CLASS(gm_world_tab_parent_class)->finalize(object); +} + +static void +gm_world_tab_class_init(GmWorldTabClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gm_world_tab_finalize; + + gm_world_tab_signals[CLOSE] = + g_signal_new("close", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmWorldTabClass, close), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + g_type_class_add_private(object_class, sizeof(GmWorldTabPrivate)); +} + +static void +gm_world_tab_create_interface(GmWorldTab *obj) { + GtkWidget *image, *label, *button, *image_button; + + image = gtk_image_new(); + label = gtk_label_new(""); + button = gtk_button_new(); + + image_button = gtk_image_new_from_stock(GTK_STOCK_CLOSE, + GTK_ICON_SIZE_BUTTON); + gtk_widget_set_size_request(button, 16, 16); + gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); + gtk_container_add(GTK_CONTAINER(button), image_button); + + gtk_box_pack_start(GTK_BOX(obj), image, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(obj), label, FALSE, FALSE, 0); + gtk_box_pack_end(GTK_BOX(obj), button, FALSE, FALSE, 0); + + gtk_box_set_spacing(GTK_BOX(obj), 6); + + obj->priv->image = GTK_IMAGE(image); + obj->priv->label = GTK_LABEL(label); + obj->priv->button = GTK_BUTTON(button); +} + +static void +gm_world_tab_init(GmWorldTab *obj) { + obj->priv = GM_WORLD_TAB_GET_PRIVATE(obj); + obj->priv->source_id = 0; + obj->priv->world = NULL; + + gm_world_tab_create_interface(obj); + + g_signal_connect(obj->priv->button, "clicked", + G_CALLBACK(on_gm_world_tab_button_clicked), obj); + + gtk_widget_show_all(GTK_WIDGET(obj)); + gtk_widget_hide(GTK_WIDGET(obj)); +} + +gboolean +gm_world_tab_update_color(GmWorldTab *tab) { + gchar *text; + gint activity = gm_world_activity(tab->priv->world); + + text = g_strdup_printf("%s [%i]", + CALC_COLOR_RANGE(activity), + gm_world_name(tab->priv->world), activity); + + gtk_label_set_markup(tab->priv->label, text); + g_free(text); + + tab->priv->source_id = 0; + return FALSE; +} + +static void +gm_world_tab_update(GmWorldTab *tab) { + gint activity = gm_world_activity(tab->priv->world); + gchar *text; + + gm_world_tab_stop_recolor(tab); + + if (activity) { + text = g_strdup_printf("%s [%i]", + CALC_COLOR_RANGE(activity), + gm_world_name(tab->priv->world), activity); + gtk_image_set_from_pixbuf(tab->priv->image, + gm_pixbuf_get_at_size("world_activity.svg", 16, 16)); + tab->priv->source_id = g_idle_add((GSourceFunc) + gm_world_tab_update_color, tab); + } else { + text = g_strdup(gm_world_name(tab->priv->world)); + gtk_image_set_from_pixbuf(tab->priv->image, + gm_pixbuf_get_at_size("world.svg", 16, 16)); + } + + gtk_label_set_markup(tab->priv->label, text); + g_free(text); +} + +// Public + +GmWorldTab * +gm_world_tab_new(GmWorld *world) { + GmWorldTab *obj = GM_WORLD_TAB(g_object_new(GM_TYPE_WORLD_TAB, NULL)); + + obj->priv->world = g_object_ref(world); + + g_signal_connect(world, "activity_changed", + G_CALLBACK(on_gm_world_tab_world_activity_changed), obj); + g_signal_connect(world, "name_changed", + G_CALLBACK(on_gm_world_tab_world_name_changed), obj); + + gm_world_tab_update(obj); + return obj; +} + +// Callbacks + +void +on_gm_world_tab_button_clicked(GtkButton *button, GmWorldTab *tab) { + g_signal_emit(tab, gm_world_tab_signals[CLOSE], 0); +} + +void +on_gm_world_tab_world_activity_changed(GmWorld *world, gint activity, + GmWorldTab *tab) { + gm_world_tab_update(tab); +} + +void +on_gm_world_tab_world_name_changed(GmWorld *world, gchar *text, + GmWorldTab *tab) { + gm_world_tab_update(tab); +} diff --git a/src/gm-world-tab.h b/src/gm-world-tab.h new file mode 100644 index 0000000..de165ad --- /dev/null +++ b/src/gm-world-tab.h @@ -0,0 +1,50 @@ +#ifndef __GM_WORLD_TAB_H__ +#define __GM_WORLD_TAB_H__ + +#include + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GM_TYPE_WORLD_TAB (gm_world_tab_get_type()) +#define GM_WORLD_TAB(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD_TAB, GmWorldTab)) +#define GM_WORLD_TAB_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD_TAB, GmWorldTab const)) +#define GM_WORLD_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_WORLD_TAB, GmWorldTabClass)) +#define GM_IS_WORLD_TAB(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_WORLD_TAB)) +#define GM_IS_WORLD_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_WORLD_TAB)) +#define GM_WORLD_TAB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_WORLD_TAB, GmWorldTabClass)) + +/* Private structure type */ +typedef struct _GmWorldTabPrivate GmWorldTabPrivate; + +/* + * Main object structure + */ +typedef struct _GmWorldTab GmWorldTab; + +struct _GmWorldTab { + GtkHBox parent; + + /*< private > */ + GmWorldTabPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GmWorldTabClass GmWorldTabClass; + +struct _GmWorldTabClass { + GtkHBoxClass parent_class; + + // Signals + void (* close) (GmWorldTab *obj); +}; + +GType gm_world_tab_get_type(void) G_GNUC_CONST; +GmWorldTab *gm_world_tab_new(GmWorld *world); + +G_END_DECLS +#endif /* __GM_WORLD_TAB_H__ */ diff --git a/src/gm-world-text-view.c b/src/gm-world-text-view.c new file mode 100644 index 0000000..c43de67 --- /dev/null +++ b/src/gm-world-text-view.c @@ -0,0 +1,954 @@ +#include +#include + +#include "gm-world-text-view.h" +#include "gm-color-table.h" +#include "gm-marshal.h" + +#include "ansi.h" +#include "debug.h" +//#include "gm-support.h" + +/* Callback definitions */ +void on_gm_world_text_view_style_set(GmWorldTextView *view, + GtkStyle *previous_style, gpointer user_data); +gboolean on_gm_world_text_view_url_event(GtkTextTag *tag, GObject *object, + GdkEvent *event, GtkTextIter *iter, GmWorldTextView *view); +gboolean on_gm_world_text_view_event(GmWorldTextView *view, + GdkEventMotion *event, GtkTextTag *tag); + +void on_gm_world_text_view_color_table_bold_toggled(GmColorTable *table, + gboolean bold, GmWorldTextView *view); +void on_gm_world_text_view_color_table_color_changed(GmColorTable *table, + gchar *name, GmWorldTextView *view); +void on_gm_world_text_view_color_table_font_changed(GmColorTable *table, + gchar *font_description, GmWorldTextView *view); + +void gm_world_text_view_create_tags(GmWorldTextView *view); +void gm_world_text_view_init_tags(GmWorldTextView *view); +void gm_world_text_view_update_font(GmWorldTextView *view); + +#define GM_WORLD_TEXT_VIEW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_WORLD_TEXT_VIEW, GmWorldTextViewPrivate)) +#define GM_WORLD_TEXT_VIEW_BUFFER(view) gtk_text_view_get_buffer(GTK_TEXT_VIEW(view)) + +typedef struct _GmWorldTextViewInsertInfo GmWorldTextViewInsertInfo; + +struct _GmWorldTextViewInsertInfo { + GList *tags; + gboolean bold; + gboolean inverse; + gchar *text; +}; + +struct _GmWorldTextViewPrivate { + gboolean is_hand; + + gint character_width; + gint character_height; + gint max_lines; + GmWorldTextViewInsertInfo last_info; + GmColorTable *color_table; +}; + +/* Signals */ + +enum { + URL_ACTIVATE, + CHARACTER_SIZE_CHANGED, + NUM_SIGNALS +}; + +static guint world_text_view_signals[NUM_SIGNALS] = {0}; + +G_DEFINE_TYPE(GmWorldTextView, gm_world_text_view, GTK_TYPE_TEXT_VIEW) + +/* Class object functions etc */ +static void +gm_world_text_view_finalize(GObject *object) { + GmWorldTextView *view = GM_WORLD_TEXT_VIEW(object); + + gm_world_text_view_set_color_table(view, NULL); + g_list_free(view->priv->last_info.tags); + g_free(view->priv->last_info.text); + + G_OBJECT_CLASS(gm_world_text_view_parent_class)->finalize(object); +} + +static void +gm_world_text_view_class_init(GmWorldTextViewClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gm_world_text_view_finalize; + + world_text_view_signals[URL_ACTIVATE] = + g_signal_new("url_activate", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmWorldTextViewClass, url_activate), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + world_text_view_signals[CHARACTER_SIZE_CHANGED] = + g_signal_new("character_size_changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmWorldTextViewClass, character_size_changed), + NULL, NULL, + gm_marshal_VOID__INT_INT, + G_TYPE_NONE, + 2, + G_TYPE_INT, + G_TYPE_INT); + + g_type_class_add_private(object_class, sizeof(GmWorldTextViewPrivate)); +} + +static void +gm_world_text_view_init(GmWorldTextView *view) { + view->priv = GM_WORLD_TEXT_VIEW_GET_PRIVATE(view); + + // This view can't focus + GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(view), GTK_CAN_FOCUS); + + // Therefore we need the active state to be the selected state + gtk_widget_modify_base(GTK_WIDGET(view), GTK_STATE_ACTIVE, + &(GTK_WIDGET(view)->style->base[GTK_STATE_SELECTED])); + gtk_widget_modify_text(GTK_WIDGET(view), GTK_STATE_ACTIVE, + &(GTK_WIDGET(view)->style->text[GTK_STATE_SELECTED])); + + // Margins + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view), 3); + gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view), 3); + + // Set default wrapping mode + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD_CHAR); + + view->priv->is_hand = FALSE; + view->priv->color_table = NULL; + + view->priv->max_lines = 2000; + view->priv->last_info.bold = FALSE; + view->priv->last_info.inverse = FALSE; + view->priv->last_info.tags = NULL; + view->priv->last_info.text = NULL; + + // Connect style set signal + g_signal_connect(view, "style-set", + G_CALLBACK(on_gm_world_text_view_style_set), NULL); +} + +/* Private functions */ +void +gm_world_text_view_update_color_tag(GmWorldTextView *view, const gchar *name, + GtkTextTag *tag) { + GtkTextBuffer *buf = GM_WORLD_TEXT_VIEW_BUFFER(view); + GdkColor col; + + if (tag == NULL) { + tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buf), name); + } + + if (view->priv->color_table != NULL) { + gm_color_table_get(view->priv->color_table, name, &col); + } + + if (name[0] == 'f') { + g_object_set(G_OBJECT(tag), "foreground-gdk", &col, NULL); + + if (name[strlen(name) - 1] == 'h') { + /* Color tag for bold colors */ + //g_object_set(G_OBJECT(tag), "weight", PANGO_WEIGHT_NORMAL, NULL); + if (view->priv->color_table == NULL || + !gm_color_table_bold(view->priv->color_table)) { + g_object_set(G_OBJECT(tag), "foreground-set", FALSE, NULL); + } + } + } else { + g_object_set(G_OBJECT(tag), "background-gdk", &col, NULL); + } +} + +void +gm_world_text_view_check_buffer_size(GmWorldTextView *view) { + GtkTextBuffer *buf = GM_WORLD_TEXT_VIEW_BUFFER(view); + GtkTextIter start, end; + int d = gtk_text_buffer_get_line_count(buf) - view->priv->max_lines; + + if (d > 0) { + gtk_text_buffer_get_iter_at_line(buf, &start, 0); + gtk_text_buffer_get_iter_at_line(buf, &end, d); + gtk_text_buffer_delete(buf, &start, &end); + } +} + +void +gm_world_text_view_create_tags(GmWorldTextView *view) { + /* Create all the tags */ + int i; + GtkTextTag *tag; + GtkTextBuffer *buf = GM_WORLD_TEXT_VIEW_BUFFER(view); + GdkColor col; + + for (i = 0; i < (int)(sizeof(ansi_colors) / sizeof(ansinamepair)); i++) { + tag = gtk_text_buffer_create_tag(buf, ansi_colors[i].name, NULL); + gm_world_text_view_update_color_tag(view, ansi_colors[i].name, tag); + } + + if (view->priv->color_table != NULL) { + gm_color_table_get(view->priv->color_table, "fg_default", &col); + } + + gtk_text_buffer_create_tag(buf, "inverse_bg", "background-gdk", &col, NULL); + + if (view->priv->color_table != NULL) { + gm_color_table_get(view->priv->color_table, "bg_default", &col); + } + + gtk_text_buffer_create_tag(buf, "inverse_fg", "background-gdk", &col, NULL); + + for (i = 0; i < (int)(sizeof(ansi_styles) / sizeof(ansinamepair)); i++) { + tag = gtk_text_buffer_create_tag(buf, ansi_styles[i].name, NULL); + + switch (ansi_styles[i].code) { + case A_BOLD: + g_object_set(G_OBJECT(tag), "weight", PANGO_WEIGHT_ULTRABOLD, NULL); + break; + case A_BOLD_OFF: + g_object_set(G_OBJECT(tag), "weight", PANGO_WEIGHT_NORMAL, NULL); + break; + case A_FAINT: + g_object_set(G_OBJECT(tag), "weight", PANGO_WEIGHT_ULTRALIGHT, NULL); + break; + case A_UNDERLINE: + g_object_set(G_OBJECT(tag), "underline", PANGO_UNDERLINE_SINGLE, NULL); + break; + case A_DOUBLE_UNDERLINE: + g_object_set(G_OBJECT(tag), "underline", PANGO_UNDERLINE_DOUBLE, NULL); + break; + case A_UNDERLINE_OFF: + g_object_set(G_OBJECT(tag), "underline", PANGO_UNDERLINE_NONE, NULL); + break; + case A_CROSSOUT: + g_object_set(G_OBJECT(tag), "strikethrough", TRUE, NULL); + break; + case A_CROSSOUT_OFF: + g_object_set(G_OBJECT(tag), "strikethrough", FALSE, NULL); + break; + case A_ITALIC: + g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_ITALIC, NULL); + break; + case A_ITALIC_OFF: + g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_NORMAL, NULL); + break; + case A_INVISIBLE: + g_object_set(G_OBJECT(tag), "invisible", TRUE, NULL); + break; + case A_INVISIBLE_OFF: + g_object_set(G_OBJECT(tag), "invisible", FALSE, NULL); + break; + case A_NOWRAP: + g_object_set(G_OBJECT(tag), "wrap-mode", GTK_WRAP_NONE, NULL); + break; + default: + gtk_text_tag_table_remove(gtk_text_buffer_get_tag_table(buf), tag); + break; + } + } + + /* Url tag */ + tag = gtk_text_buffer_create_tag(buf, "url", "foreground", "steelblue", + "underline", PANGO_UNDERLINE_SINGLE, NULL); + + g_signal_connect(tag, "event", G_CALLBACK(on_gm_world_text_view_url_event), view); + g_signal_connect(view, "event", G_CALLBACK(on_gm_world_text_view_event), tag); +} + +void +gm_world_text_view_update_tags(GmWorldTextView *view) { + int i; + GtkTextTag *tag; + + for (i = 0; i < (int)(sizeof(ansi_colors) / sizeof(ansinamepair)); i++) { + gm_world_text_view_update_color_tag(view, ansi_colors[i].name, NULL); + } + + tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table( + GM_WORLD_TEXT_VIEW_BUFFER(view)), "bold"); + + if (gm_color_table_bold(view->priv->color_table)) { + g_object_set(G_OBJECT(tag), "weight", PANGO_WEIGHT_NORMAL, NULL); + } else { + g_object_set(G_OBJECT(tag), "weight", PANGO_WEIGHT_ULTRABOLD, NULL); + } +} + +void +gm_world_text_view_init_tags(GmWorldTextView *view) { + GdkColor col; + + gm_color_table_get(view->priv->color_table, "fg_default", &col); + gtk_widget_modify_text(GTK_WIDGET(view), GTK_STATE_NORMAL, &col); + + gm_color_table_get(view->priv->color_table, "bg_default", &col); + gtk_widget_modify_base(GTK_WIDGET(view), GTK_STATE_NORMAL, &col); + + gm_world_text_view_update_tags(view); +} + +void +gm_world_text_view_update_font(GmWorldTextView *view) { + PangoFontDescription *f = pango_font_description_from_string( + gm_color_table_font_description(view->priv->color_table)); + + if (f != NULL) { + gtk_widget_modify_font(GTK_WIDGET(view), f); + pango_font_description_free(f); + } +} + +gboolean +g_utf8_toint(gchar *str, guint *result) { + gunichar c; + *result = 0; + + while ((c = g_utf8_get_char(str)) != '\0') { + if (g_unichar_isdigit(c)) { + *result = (*result * 10) + g_unichar_digit_value(c); + } else { + return FALSE; + } + str = g_utf8_next_char(str); + } + + return TRUE; +} + +void +gm_world_text_view_insert_text(GmWorldTextView *view, const gchar *text, GList *tags) { + GtkTextIter end_iter, start_iter; + GtkTextBuffer *tb = GM_WORLD_TEXT_VIEW_BUFFER(view); + gint start_offset; + gchar *name; + + gtk_text_buffer_get_end_iter(tb, &end_iter); + start_offset = gtk_text_iter_get_offset(&end_iter); + + gtk_text_buffer_insert(tb, &end_iter, text, g_utf8_strlen(text, -1)); + + if (tags) { + gtk_text_buffer_get_iter_at_offset(tb, &start_iter, start_offset); + gtk_text_buffer_get_end_iter(tb, &end_iter); + + for (; tags; tags = tags->next) { + g_object_get(GTK_TEXT_TAG(tags->data), "name", &name, NULL); + g_free(name); + gtk_text_buffer_apply_tag(tb, GTK_TEXT_TAG(tags->data), &start_iter, + &end_iter); + } + } +} + +const gchar * +gm_world_text_view_tagname_from_code(guint code) { + int i, len; + + len = (sizeof(ansi_colors) / sizeof(ansinamepair)); + + for (i = 0; i < len; i++) { + if (ansi_colors[i].code == code) { + return ansi_colors[i].name; + } + } + + len = sizeof(ansi_styles) / sizeof(ansinamepair); + for (i = 0; i < len; i++) { + if (ansi_styles[i].code == code) { + return ansi_styles[i].name; + } + } + + return NULL; +} + +GList * +gm_world_text_view_tags_fix_for_bold(GmWorldTextView *view, GList *tags) { + GList *t, *item; + GtkTextTag *tag, *htag; + gboolean fg_set; + gchar *name, *hname; + gboolean added = FALSE; + GtkTextTagTable *tag_table = gtk_text_buffer_get_tag_table( + GM_WORLD_TEXT_VIEW_BUFFER(view)); + + t = g_list_copy(tags); + + for (item = t; item; item = item->next) { + tag = GTK_TEXT_TAG(item->data); + + g_object_get(G_OBJECT(tag), "foreground-set", &fg_set, NULL); + g_object_get(G_OBJECT(tag), "name", &name, NULL); + + if (fg_set) { + hname = g_strconcat(name, "_h", NULL); + htag = gtk_text_tag_table_lookup(tag_table, hname); + + if (htag) { + if (!g_list_find(tags, htag)) { + tags = g_list_append(tags, htag); + } + added = TRUE; + } + + g_free(hname); + } + + g_free(name); + } + + /* If there is no high color added than this means we have a default color */ + if (!added) { + tags = g_list_append(tags, gtk_text_tag_table_lookup(tag_table, + "fg_default_h")); + } + + g_list_free(t); + return tags; +} + +GList * +gm_world_text_view_tags_fix_for_inverse(GmWorldTextView *view, GList *tags) { + gboolean bold = view->priv->last_info.bold; + gboolean fg_changed = FALSE, bg_changed = FALSE; + GList *new_tags = NULL, *item; + GtkTextTag *tag; + gchar *name, *base, *tagname; + int i; + GtkTextTagTable *tab = gtk_text_buffer_get_tag_table( + GM_WORLD_TEXT_VIEW_BUFFER(view)); + + for (item = tags; item; item = item->next) { + tag = GTK_TEXT_TAG(item->data); + + g_object_get(G_OBJECT(tag), "name", &name, NULL); + + if (strncmp(name, "fg_", 3) == 0) { + base = name + 3; + bg_changed = TRUE; + i = 0; + + while (base[i] != '_' && base[i] != '\0') + i++; + + base[i] = '\0'; + tagname = g_strconcat("bg_", base, NULL); + + new_tags = g_list_append(new_tags, gtk_text_tag_table_lookup(tab, tagname)); + g_free(tagname); + } else if (strncmp(name, "bg_", 3) == 0) { + base = name + 3; + fg_changed = TRUE; + + if (bold) { + tagname = g_strconcat("fg_", base, "_h", NULL); + } else { + tagname = g_strconcat("fg_", base, NULL); + } + + new_tags = g_list_append(new_tags, gtk_text_tag_table_lookup(tab, tagname)); + g_free(tagname); + } else if (strcmp(name, "inverse_bg") == 0) { + fg_changed = TRUE; + new_tags = g_list_append(new_tags, gtk_text_tag_table_lookup(tab, "fg_default")); + } else if (strcmp(name, "inverse_fg") == 0) { + bg_changed = TRUE; + new_tags = g_list_append(new_tags, gtk_text_tag_table_lookup(tab, "bg_default")); + } else { + new_tags = g_list_append(new_tags, tag); + } + + g_free(name); + } + + if (!bg_changed) { + new_tags = g_list_append(new_tags, gtk_text_tag_table_lookup(tab, "inverse_bg")); + } + if (!fg_changed) { + new_tags = g_list_append(new_tags, gtk_text_tag_table_lookup(tab, "inverse_fg")); + } + + g_list_free(tags); + return new_tags; +} + +static const gchar * tag_checks[] = { + "foreground-set", + "background-set", + "weight-set", + "underline-set", + "style-set", + "wrap-mode-set", + "invisible-set", + NULL +}; + +gboolean +gm_world_text_view_tags_overlap(GtkTextTag *t1, GtkTextTag *t2) { + int i = 0; + gboolean val1, val2; + + while (tag_checks[i]) { + g_object_get(G_OBJECT(t1), tag_checks[i], &val1, NULL); + g_object_get(G_OBJECT(t2), tag_checks[i], &val2, NULL); + + if (val1 && val2) { + return TRUE; + } + + i++; + } + + return FALSE; +} + +GList * +gm_world_text_view_tags_remove_obsolete(GmWorldTextView *view, GList *tags, + GtkTextTag *tag) { + GList *t = g_list_copy(tags), *item; + GtkTextTag *tag2; + + for (item = t; item; item = item->next) { + tag2 = GTK_TEXT_TAG(item->data); + + if (gm_world_text_view_tags_overlap(tag, tag2)) { + tags = g_list_remove(tags, tag2); + } + } + + g_list_free(t); + return tags; +} + +GList * +gm_world_text_view_tags_remove_high(GmWorldTextView *view, GList *tags) { + GList *t = g_list_copy(tags), *item; + gchar *name; + + for (item = t; item; item = item->next) { + g_object_get(G_OBJECT(item->data), "name", &name, NULL); + + if (strncmp(name, "fg_", 3) == 0 && name[strlen(name) - 1] == 'h') { + tags = g_list_remove(tags, item->data); + } + g_free(name); + } + + g_list_free(t); + return tags; +} + +GList * +gm_world_text_view_tags_add(GmWorldTextView *view, GList *tags, GtkTextTag *tag, + gboolean inverse) { + gboolean weight; + gboolean fg_set; + gchar *name, *nameh; + GtkTextTag *tagh; + + tags = gm_world_text_view_tags_remove_obsolete(view, tags, tag); + + g_object_get(G_OBJECT(tag), "foreground-set", &fg_set, NULL); + + if (fg_set) { + tags = gm_world_text_view_tags_remove_high(view, tags); + g_object_get(G_OBJECT(tag), "name", &name, NULL); + nameh = g_strconcat(name, "_h", NULL); + + tagh = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table( + GM_WORLD_TEXT_VIEW_BUFFER(view)), nameh); + if (tagh) { + tags = g_list_append(tags, tagh); + } + + g_free(name); + g_free(nameh); + } + + g_object_get(G_OBJECT(tag), "weight-set", &weight, NULL); + + if (weight) { + tags = gm_world_text_view_tags_fix_for_bold(view, tags); + } + + return g_list_append(tags, tag); +} + +/* Public/exported functions */ +GtkWidget * +gm_world_text_view_new() { + GtkWidget *result; + GmColorTable *table = gm_color_table_new(); + result = gm_world_text_view_new_with_color_table(table); + g_object_unref(table); + + return result; +} + +GtkWidget * +gm_world_text_view_new_with_color_table(GmColorTable *color_table) { + GmWorldTextView *view = GM_WORLD_TEXT_VIEW(g_object_new(GM_TYPE_WORLD_TEXT_VIEW, NULL)); + + gm_world_text_view_create_tags(view); + gm_world_text_view_set_color_table(view, color_table); + + return GTK_WIDGET(view); +} + +void +gm_world_text_view_set_color_table(GmWorldTextView *view, + GmColorTable *color_table) { + if (view->priv->color_table != NULL) { + g_signal_handlers_disconnect_by_func(view->priv->color_table, + on_gm_world_text_view_color_table_bold_toggled, view); + g_signal_handlers_disconnect_by_func(view->priv->color_table, + on_gm_world_text_view_color_table_color_changed, view); + g_signal_handlers_disconnect_by_func(view->priv->color_table, + on_gm_world_text_view_color_table_font_changed, view); + g_object_unref(view->priv->color_table); + } + + if (color_table != NULL) { + view->priv->color_table = g_object_ref(color_table); + g_signal_connect(view->priv->color_table, "bold_toggled", + G_CALLBACK(on_gm_world_text_view_color_table_bold_toggled), view); + g_signal_connect(view->priv->color_table, "color_changed", + G_CALLBACK(on_gm_world_text_view_color_table_color_changed), view); + g_signal_connect(view->priv->color_table, "font_changed", + G_CALLBACK(on_gm_world_text_view_color_table_font_changed), view); + + gm_world_text_view_init_tags(view); + gm_world_text_view_update_font(view); + } else { + view->priv->color_table = NULL; + } +} + +GmColorTable * +gm_world_text_view_color_table(GmWorldTextView *view) { + return view->priv->color_table; +} + +gchar * +gm_world_text_view_insert(GmWorldTextView *view, const gchar *text) { + gchar *ptr, *ansi_start, *ansi_stop, **ansis; + gchar *name; + int i; + guint seq; + GtkTextBuffer *buffer = GM_WORLD_TEXT_VIEW_BUFFER(view); + GtkTextTagTable *tab = gtk_text_buffer_get_tag_table(buffer); + GmWorldTextViewInsertInfo *info = &(view->priv->last_info); + GString *new_line = NULL; + GtkTextIter started, ended; + GtkTextMark *start_mark; + GtkTextTag *tag; + gboolean skip; + + g_return_val_if_fail(g_utf8_validate(text, -1, NULL), NULL); + + if (info->text != NULL) { + new_line = g_string_new(info->text); + g_free(info->text); + info->text = NULL; + } else { + new_line = g_string_new(""); + } + + g_string_append(new_line, text); + + if ((ptr = g_utf8_strchr(new_line->str, -1, '\x07'))) { + gdk_beep(); + + while (ptr) { + g_string_erase(new_line, g_utf8_pointer_to_offset(new_line->str, ptr), 1); + ptr = g_utf8_strchr(new_line->str, -1, '\x07'); + } + } + + debug_msg(1, "gm_world_text_view_insert: %s", new_line->str); + gtk_text_buffer_get_end_iter(buffer, &started); + start_mark = gtk_text_buffer_create_mark(buffer, "last-insertion", + &started, TRUE); + + while (new_line != NULL && + (ansi_start = strstr(new_line->str, "\x1B["))) { + i = g_utf8_pointer_to_offset(new_line->str, ansi_start); + + if (i != 0) { + ptr = g_strndup(new_line->str, i); + gm_world_text_view_insert_text(view, ptr, info->tags); + g_free(ptr); + } + + if ((ansi_stop = g_utf8_strchr(ansi_start, -1, 'm'))) { + /* Advance to the ansi sequence */ + ansi_start = ansi_start + 2; + ptr = g_strndup(ansi_start, ansi_stop - ansi_start); + ansis = g_strsplit(ptr, ";", -1); + g_free(ptr); + + for (i = 0; ansis[i] != NULL; i++) { + if (ansis[i][0] != '\0' && g_utf8_toint(ansis[i], &seq)) { + skip = FALSE; + switch (seq) { + case A_FG_DEFAULT: case A_FG_BLACK: case A_FG_RED: case A_FG_GREEN: + case A_FG_YELLOW: case A_FG_BLUE: case A_FG_PURPLE: case A_FG_CYAN: + case A_FG_WHITE: + if (info->inverse) { + seq += 10; + } + break; + case A_BG_DEFAULT: case A_BG_BLACK: case A_BG_RED: case A_BG_GREEN: + case A_BG_YELLOW: case A_BG_BLUE: case A_BG_PURPLE: case A_BG_CYAN: + case A_BG_WHITE: + if (info->inverse) { + seq -= 10; + } + break; + case A_BOLD: + info->bold = TRUE; + + if (info->inverse) + skip = TRUE; + break; + case A_BOLD_OFF: + info->bold = FALSE; + + if (info->inverse) + skip = TRUE; + break; + case A_INVERSE: + info->inverse = !(info->inverse); + info->tags = gm_world_text_view_tags_fix_for_inverse(view, info->tags); + break; + case A_INVERSE_OFF: + if (info->inverse) { + info->tags = gm_world_text_view_tags_fix_for_inverse(view, info->tags); + info->inverse = FALSE; + } + break; + case A_DEFAULT: + g_list_free(info->tags); + info->tags = NULL; + info->bold = FALSE; + info->inverse = FALSE; + break; + } + + name = (gchar *)gm_world_text_view_tagname_from_code(seq); + + if (name != NULL && !skip) { + tag = gtk_text_tag_table_lookup(tab, name); + + if (tag != NULL) { + info->tags = gm_world_text_view_tags_add(view, info->tags, tag, + info->inverse); + } + } + } + } + + g_strfreev(ansis); + g_string_erase(new_line, 0, + g_utf8_pointer_to_offset(new_line->str, ansi_stop) + 1); + } else { + info->text = ansi_start; + g_string_free(new_line, TRUE); + new_line = NULL; + } + } + + if (new_line && new_line->len != 0) { + gm_world_text_view_insert_text(view, new_line->str, info->tags); + } + + g_string_free(new_line, TRUE); + gm_world_text_view_check_buffer_size(view); + + gtk_text_buffer_get_iter_at_mark(buffer, &started, start_mark); + gtk_text_buffer_get_end_iter(buffer, &ended); + gtk_text_buffer_delete_mark(buffer, start_mark); + return gtk_text_buffer_get_text(buffer, &started, &ended, FALSE); +} + +/* Callbacks */ +void +on_gm_world_text_view_style_set(GmWorldTextView *view, GtkStyle *previous_style, + gpointer user_data) { + GtkRcStyle *style = gtk_widget_get_modifier_style(GTK_WIDGET(view)); + PangoContext *pc = gtk_widget_create_pango_context(GTK_WIDGET(view)); + PangoLayout *pl; + gint cwidth, cheight; + + if (style->font_desc != NULL) { + pango_context_set_font_description(pc, style->font_desc); + pl = pango_layout_new(pc); + + pango_layout_set_text(pl, "G", 1); + pango_layout_get_pixel_size(pl, &(cwidth), + &(cheight)); + + if (view->priv->character_width != cwidth || + view->priv->character_height != cheight) { + view->priv->character_width = cwidth; + view->priv->character_height = cheight; + + g_signal_emit(view, world_text_view_signals[CHARACTER_SIZE_CHANGED], 0, + cwidth, cheight); + } + + g_object_unref(pl); + } + + g_object_unref(pc); +} + +gboolean +on_gm_world_text_view_url_event(GtkTextTag *tag, GObject *object, + GdkEvent *event, GtkTextIter *iter, GmWorldTextView *view) { + GtkTextIter start, end; + gchar *str; + GtkTextBuffer *buffer = GM_WORLD_TEXT_VIEW_BUFFER(view); + + /* If the link is being selected, don't do anything. */ + gtk_text_buffer_get_selection_bounds(buffer, &start, &end); + + if (gtk_text_iter_get_offset(&start) != gtk_text_iter_get_offset(&end)) { + return FALSE; + } + + if (event->type == GDK_BUTTON_RELEASE && event->button.button == 1) { + start = end = *iter; + + if (gtk_text_iter_backward_to_tag_toggle(&start, tag) && + gtk_text_iter_forward_to_tag_toggle(&end, tag)) { + str = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); + g_signal_emit(view, world_text_view_signals[URL_ACTIVATE], 0, str); + g_free(str); + } + } + + return FALSE; +} + +gboolean +on_gm_world_text_view_event(GmWorldTextView *view, GdkEventMotion *event, + GtkTextTag *tag) { + static GdkCursor *hand = NULL; + static GdkCursor *beam = NULL; + GtkTextWindowType type; + GtkTextIter iter; + GdkWindow *win; + gint x, y, buf_x, buf_y; + gboolean has_tag; + + type = gtk_text_view_get_window_type(GTK_TEXT_VIEW(view), event->window); + + if (type != GTK_TEXT_WINDOW_TEXT) { + return FALSE; + } + + /* Get where the pointer really is. */ + win = gtk_text_view_get_window(GTK_TEXT_VIEW(view), type); + gdk_window_get_pointer(win, &x, &y, NULL); + + /* Get the iter where the cursor is at */ + gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(view), type, + x, y, &buf_x, &buf_y); + + gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(view), &iter, buf_x, buf_y); + + if (!hand) { + hand = gdk_cursor_new(GDK_FLEUR); + beam = gdk_cursor_new(GDK_XTERM); + } + + has_tag = gtk_text_iter_has_tag(&iter, tag); + + if (has_tag && !view->priv->is_hand) { + view->priv->is_hand = TRUE; + gdk_window_set_cursor(win, hand); + } else if (!has_tag && view->priv->is_hand) { + view->priv->is_hand = FALSE; + gdk_window_set_cursor(win, beam); + } + + return FALSE; +} + +void +gm_world_text_view_color_table_fix_high_color(GtkTextTag *tag, gpointer data) { + gboolean bold = (gboolean)(GPOINTER_TO_INT(data)); + gchar *name; + GdkColor *col; + + g_object_get(G_OBJECT(tag), "name", &name, NULL); + + if (strncmp(name, "fg_", 3) == 0 && name[strlen(name) - 1] == 'h') { + g_object_get(G_OBJECT(tag), "foreground-gdk", &col, NULL); + + if (bold) { + g_object_set(G_OBJECT(tag), "foreground-gdk", col, NULL); + } else { + g_object_set(G_OBJECT(tag), "foreground-set", FALSE, NULL); + } + } + + g_free(name); +} + +void +on_gm_world_text_view_color_table_bold_toggled(GmColorTable *table, + gboolean bold, GmWorldTextView *view) { + GtkTextTag *tag; + GtkTextBuffer *buf = GM_WORLD_TEXT_VIEW_BUFFER(view); + GtkTextTagTable *tb = gtk_text_buffer_get_tag_table(buf); + + /* Fix bold tag */ + tag = gtk_text_tag_table_lookup(tb, "bold"); + + if (tag != NULL) { + if (bold) { + g_object_set(G_OBJECT(tag), "weight", PANGO_WEIGHT_NORMAL, NULL); + } else { + g_object_set(G_OBJECT(tag), "weight", PANGO_WEIGHT_ULTRABOLD, NULL); + } + + /* Fix high color tags */ + gtk_text_tag_table_foreach(tb, gm_world_text_view_color_table_fix_high_color, + GINT_TO_POINTER(bold)); + } +} + +void +on_gm_world_text_view_color_table_color_changed(GmColorTable *table, + gchar *name, GmWorldTextView *view) { + GdkColor col; + + gm_world_text_view_update_color_tag(view, name, NULL); + + if (strcmp(name, "fg_default") == 0) { + gm_color_table_get(view->priv->color_table, "fg_default", &col); + gtk_widget_modify_text(GTK_WIDGET(view), GTK_STATE_NORMAL, &col); + } else if (strcmp(name, "bg_default") == 0) { + gm_color_table_get(view->priv->color_table, "bg_default", &col); + gtk_widget_modify_base(GTK_WIDGET(view), GTK_STATE_NORMAL, &col); + } +} + +void +on_gm_world_text_view_color_table_font_changed(GmColorTable *table, + gchar *font_description, GmWorldTextView *view) { + gm_world_text_view_update_font(view); +} diff --git a/src/gm-world-text-view.h b/src/gm-world-text-view.h new file mode 100644 index 0000000..99eb111 --- /dev/null +++ b/src/gm-world-text-view.h @@ -0,0 +1,59 @@ +#ifndef __GM_WORLD_TEXT_VIEW_H__ +#define __GM_WORLD_TEXT_VIEW_H__ + +#include +#include "gm-color-table.h" + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GM_TYPE_WORLD_TEXT_VIEW (gm_world_text_view_get_type()) +#define GM_WORLD_TEXT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD_TEXT_VIEW, GmWorldTextView)) +#define GM_WORLD_TEXT_VIEW_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD_TEXT_VIEW, GmWorldTextView const)) +#define GM_WORLD_TEXT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_WORLD_TEXT_VIEW, GmWorldTextViewClass)) +#define GM_IS_WORLD_TEXT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_WORLD_TEXT_VIEW)) +#define GM_IS_WORLD_TEXT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_WORLD_TEXT_VIEW)) +#define GM_WORLD_TEXT_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_WORLD_TEXT_VIEW, GmWorldTextViewClass)) + +/* Private structure type */ +typedef struct _GmWorldTextViewPrivate GmWorldTextViewPrivate; + +/* + * Main object structure + */ +typedef struct _GmWorldTextView GmWorldTextView; + +struct _GmWorldTextView { + GtkTextView textview; + + /*< private > */ + GmWorldTextViewPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GmWorldTextViewClass GmWorldTextViewClass; + +struct _GmWorldTextViewClass { + GtkTextViewClass parent_class; + + /* Signals */ + void (* url_activate) (GmWorldTextView *view, const gchar *url); + void (* character_size_changed) (GmWorldTextView *view, gint width, + gint height); +}; + +GType gm_world_text_view_get_type(void) G_GNUC_CONST; +GtkWidget *gm_world_text_view_new(void); +GtkWidget *gm_world_text_view_new_with_color_table(GmColorTable *color_table); +gchar *gm_world_text_view_insert(GmWorldTextView *view, const gchar *text); +GmColorTable *gm_world_text_view_color_table(GmWorldTextView *view); +void gm_world_text_view_set_color_table(GmWorldTextView *view, + GmColorTable *color_table); + +G_END_DECLS +#endif /* __GM_WORLD_TEXT_VIEW_H__ */ + diff --git a/src/gm-world-view.c b/src/gm-world-view.c new file mode 100644 index 0000000..a87625d --- /dev/null +++ b/src/gm-world-view.c @@ -0,0 +1,459 @@ +#include "gm-app.h" +#include "gm-world-view.h" +#include "gm-world-text-view.h" +#include "gm-world-input-view.h" +#include "debug.h" + +#define GM_WORLD_VIEW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_WORLD_VIEW, GmWorldViewPrivate)) + +struct _GmWorldViewPrivate { + GmWorld *world; + + GtkStatusbar *statusbar; + GtkHPaned *hpaned; + GtkTreeView *tree_view_userlist; + GtkTreeModel *tree_model_userlist; + GmWorldTextView *text_view_world; + GmWorldInputView *text_view_input; +}; + +enum { + GM_WORLD_VIEW_USERLIST_ICON, + GM_WORLD_VIEW_USERLIST_NAME, + GM_WORLD_VIEW_USERLIST_OBJ, + GM_WORLD_VIEW_USERLIST_SORT, + GM_WORLD_VIEW_USERLIST_N_COLUMNS +}; + +void on_gm_world_view_world_text_received(GmWorld *world, gchar *text, + GmWorldView *view); +void on_gm_world_view_world_error(GmWorld *world, gchar *text, gint code, + GmWorldView *view); +void on_gm_world_input_view_world_text_activate(GmWorldInputView *iview, + gchar *text, GmWorldView *view); +void on_gm_world_view_world_state_changing(GmWorld *world, guint state, + GmWorldView *view); +void on_gm_world_view_world_active_changed(GmWorld *world, gboolean active, + GmWorldView *view); + +/* Signals */ + +/*enum { + NUM_SIGNALS +}; + +static guint world_view_signals[NUM_SIGNALS] = {0};*/ + +G_DEFINE_TYPE(GmWorldView, gm_world_view, GTK_TYPE_NOTEBOOK) + +static void +gm_world_view_finalize(GObject *object) { + GmWorldView *view = GM_WORLD_VIEW(object); + + g_signal_handlers_disconnect_by_func(view->priv->world, + G_CALLBACK(on_gm_world_view_world_text_received), view); + g_signal_handlers_disconnect_by_func(view->priv->world, + G_CALLBACK(on_gm_world_view_world_error), view); + g_signal_handlers_disconnect_by_func(view->priv->world, + G_CALLBACK(on_gm_world_view_world_state_changing), view); + g_signal_handlers_disconnect_by_func(view->priv->world, + G_CALLBACK(on_gm_world_view_world_active_changed), view); + + g_object_unref(view->priv->world); + G_OBJECT_CLASS(gm_world_view_parent_class)->finalize(object); +} + +static void +gm_world_view_class_init(GmWorldViewClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gm_world_view_finalize; + + g_type_class_add_private(object_class, sizeof(GmWorldViewPrivate)); +} + +GtkTreeModel * +gm_world_view_userlist_model_new(GmWorldView *view) { + GtkListStore *store = + gtk_list_store_new(GM_WORLD_VIEW_USERLIST_N_COLUMNS, GDK_TYPE_PIXBUF, + G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING); + GtkTreeModel *model = gtk_tree_model_sort_new_with_model( + GTK_TREE_MODEL(store)); + + view->priv->tree_model_userlist = model; + return model; +} + +GtkWidget * +gm_world_view_userlist_new(GmWorldView *view) { + GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL); + GtkTreeModel *model = gm_world_view_userlist_model_new(view); + GtkWidget *tree_view = gtk_tree_view_new_with_model(model); + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window), + GTK_SHADOW_IN); + gtk_widget_set_size_request(scrolled_window, 150, -1); + gtk_widget_set_size_request(tree_view, 150, -1); + + gtk_container_add(GTK_CONTAINER(scrolled_window), tree_view); + + renderer = gtk_cell_renderer_pixbuf_new(); + column = gtk_tree_view_column_new_with_attributes(_("I"), renderer, "pixbuf", + GM_WORLD_VIEW_USERLIST_ICON, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column); + gtk_tree_view_column_set_min_width(column, 30); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, + "text", GM_WORLD_VIEW_USERLIST_NAME, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column); + + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), + GM_WORLD_VIEW_USERLIST_SORT, GTK_SORT_ASCENDING); + + GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(tree_view), GTK_CAN_FOCUS); + gtk_tree_view_columns_autosize(GTK_TREE_VIEW(tree_view)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE); + view->priv->tree_view_userlist = GTK_TREE_VIEW(tree_view); + + return scrolled_window; +} + +GtkWidget * +gm_world_view_create_input_text_view(GmWorldView *view) { + GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL); + GtkWidget *input_text_view = gm_world_input_view_new_with_color_table( + gm_app_color_table(gm_app_instance())); + + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), + GTK_POLICY_NEVER, GTK_POLICY_NEVER); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window), + GTK_SHADOW_IN); + gtk_container_add(GTK_CONTAINER(scrolled_window), input_text_view); + + view->priv->text_view_input = GM_WORLD_INPUT_VIEW(input_text_view); + + return scrolled_window; +} + +GtkWidget * +gm_world_view_create_world_text_view(GmWorldView *view) { + GtkWidget *world_text_view = gm_world_text_view_new_with_color_table( + gm_app_color_table(gm_app_instance())); + GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL); + + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), + GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window), + GTK_SHADOW_IN); + gtk_container_add(GTK_CONTAINER(scrolled_window), world_text_view); + + view->priv->text_view_world = GM_WORLD_TEXT_VIEW(world_text_view); + return scrolled_window; +} + +GtkWidget * +gm_world_view_world_page_new(GmWorldView *view) { + GtkWidget *vbox = gtk_vbox_new(FALSE, 0); + GtkWidget *hpaned = gtk_hpaned_new(); + GtkWidget *status = gtk_statusbar_new(); + GtkWidget *vbox_world = gtk_vbox_new(FALSE, 3); + + gtk_box_pack_start(GTK_BOX(vbox_world), + gm_world_view_create_world_text_view(view), TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox_world), + gm_world_view_create_input_text_view(view), FALSE, FALSE, 0); + + gtk_container_set_border_width(GTK_CONTAINER(vbox_world), 3); + gtk_paned_pack1(GTK_PANED(hpaned), vbox_world, TRUE, TRUE); + gtk_paned_pack2(GTK_PANED(hpaned), gm_world_view_userlist_new(view), + FALSE, TRUE); + + gtk_statusbar_push(GTK_STATUSBAR(status), 0, + _("Welcome to GnoeMoe, explorer of new worlds!")); + gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(status), FALSE); + + gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox), status, FALSE, FALSE, 0); + + view->priv->statusbar = GTK_STATUSBAR(status); + view->priv->hpaned = GTK_HPANED(hpaned); + + return vbox; +} + +static void +gm_world_view_init(GmWorldView *view) { + GtkWidget *label; + GmLabelInfo info; + + view->priv = GM_WORLD_VIEW_GET_PRIVATE(view); + + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(view), FALSE); + gtk_notebook_set_show_border(GTK_NOTEBOOK(view), FALSE); + gtk_notebook_set_tab_pos(GTK_NOTEBOOK(view), GTK_POS_BOTTOM); + gtk_notebook_set_scrollable(GTK_NOTEBOOK(view), TRUE); + + label = gm_create_tab_label("world.svg", _("World"), FALSE, &info); + gtk_notebook_append_page(GTK_NOTEBOOK(view), + gm_world_view_world_page_new(view), label); +} + +void +on_gm_world_view_destroy(GmWorldView *view, gpointer user_data) { + gm_options_set_int(gm_world_options(view->priv->world), "pane_position", + GTK_WIDGET(view->priv->hpaned)->allocation.width - + gtk_paned_get_position(GTK_PANED(view->priv->hpaned))); + + debug_msg(0, "%d", gm_options_get_int(gm_world_options(view->priv->world), "pane_position")); + gm_options_save(gm_world_options(view->priv->world)); +} + +void +on_gm_world_view_show(GmWorldView *view, gpointer user_data) { + gm_do_events(); + + debug_msg(0, "%d", GTK_WIDGET(view->priv->hpaned)->allocation.width); + gtk_paned_set_position(GTK_PANED(view->priv->hpaned), + GTK_WIDGET(view->priv->hpaned)->allocation.width + - gm_options_get_int(gm_world_options(view->priv->world), + "pane_position")); +} + +GtkWidget * +gm_world_view_new(GmWorld *world) { + GmWorldView *view = GM_WORLD_VIEW(g_object_new(GM_TYPE_WORLD_VIEW, NULL)); + + view->priv->world = g_object_ref(world); + + debug_msg(0, "%d", gm_world_history(view->priv->world)); + + gm_world_input_view_set_history(view->priv->text_view_input, + gm_world_history(view->priv->world)); + + g_signal_connect(world, "text_received", + G_CALLBACK(on_gm_world_view_world_text_received), view); + g_signal_connect(world, "world_error", + G_CALLBACK(on_gm_world_view_world_error), view); + g_signal_connect(world, "state_changing", + G_CALLBACK(on_gm_world_view_world_state_changing), view); + g_signal_connect(world, "active_changed", + G_CALLBACK(on_gm_world_view_world_active_changed), view); + g_signal_connect(view->priv->text_view_input, "text_activate", + G_CALLBACK(on_gm_world_input_view_world_text_activate), view); + g_signal_connect(view, "destroy", + G_CALLBACK(on_gm_world_view_destroy), NULL); + g_signal_connect(view, "show", + G_CALLBACK(on_gm_world_view_show), NULL); + + return GTK_WIDGET(view); +} + +gboolean +gm_world_view_find_first(GmWorldView *view, const gchar *str, + GmWorldViewSearchFlags flags) { + GtkTextView *tview = GTK_TEXT_VIEW(view->priv->text_view_world); + GtkTextIter iter; + GtkTextBuffer *buffer; + + if (tview) { + buffer = gtk_text_view_get_buffer(tview); + + if (buffer) { + if (flags & GM_WORLD_VIEW_SEARCH_BACKWARDS) { + gtk_text_buffer_get_end_iter(buffer, &iter); + } else { + gtk_text_buffer_get_start_iter(buffer, &iter); + } + + gtk_text_buffer_place_cursor(buffer, &iter); + + return gm_world_view_find_next(view, str, flags); + } + } + + return FALSE; +} + +gboolean +gm_world_view_find_next(GmWorldView *view, const gchar *str, + GmWorldViewSearchFlags flags) { + GtkTextBuffer *buffer = NULL; + GtkTextView *tview = NULL; + GtkTextIter end, start, matchStart, matchEnd; + gboolean found = FALSE; + + if (*str == '\0') { + return FALSE; + } + + tview = GTK_TEXT_VIEW(view->priv->text_view_world); + + if (tview) { + buffer = gtk_text_view_get_buffer(tview); + } + + if (buffer) { + if (!gtk_text_buffer_get_selection_bounds(buffer, &start, &end)) { + if (flags & GM_WORLD_VIEW_SEARCH_BACKWARDS) { + gtk_text_buffer_get_end_iter(buffer, &end); + } else { + gtk_text_buffer_get_start_iter(buffer, &end); + } + + start = end; + } + + if (flags & GM_WORLD_VIEW_SEARCH_FORWARDS) { + found = gtk_text_iter_forward_search(&end, str, + GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY, + &matchStart, &matchEnd, NULL); + } else { + found = gtk_text_iter_backward_search(&start, str, + GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY, + &matchStart, &matchEnd, NULL); + } + + if (found) { + gtk_text_buffer_place_cursor(buffer, &matchStart); + gtk_text_buffer_move_mark_by_name(buffer, "selection_bound", + &matchEnd); + gtk_text_view_scroll_to_iter(tview, &matchStart, + 0.0, FALSE, 0.0, 0.0); + } + } + + return found; +} + +gboolean +gm_world_view_text_active(GmWorldView *view) { + return gtk_notebook_get_current_page(GTK_NOTEBOOK(view)) == 0; +} + +void +gm_world_view_scroll_end_prepare(GmWorldView *view) { + // TODO +} + +void +gm_world_view_scroll_end(GmWorldView *view) { + // TODO +} + +GmWorld * +gm_world_view_world(GmWorldView *view) { + return view->priv->world; +} + +GmWorldInputView * +gm_world_view_input(GmWorldView *view) { + return view->priv->text_view_input; +} + +GtkTextBuffer * +gm_world_view_buffer(GmWorldView *view) { + return gtk_text_view_get_buffer(GTK_TEXT_VIEW(view->priv->text_view_world)); +} + +void +gm_world_view_open_log(GmWorldView *view, const gchar *filename, + OpenLogProgress callback, gpointer user_data) { +} + +void +gm_world_view_set_userlist_width(GmWorldView *view, gint width) { + gtk_paned_set_position(GTK_PANED(view->priv->hpaned), + GTK_WIDGET(view->priv->hpaned)->allocation.width - width); +} + +void +gm_world_view_set_focus(GmWorldView *view) { + gtk_widget_grab_focus(GTK_WIDGET(view->priv->text_view_input)); +} + +/* Callbacks */ +void +on_gm_world_input_view_world_text_activate(GmWorldInputView *iview, gchar *text, + GmWorldView *view) { + gm_world_process_input(view->priv->world, text); +} + +void +on_gm_world_view_world_text_received(GmWorld *world, gchar *text, + GmWorldView *view) { + gchar *inserted = + gm_world_text_view_insert(view->priv->text_view_world, text); + + g_free(inserted); +} + +void +on_gm_world_view_world_error(GmWorld *world, gchar *text, gint code, + GmWorldView *view) { + gchar *line; + + switch (code) { + case GM_NET_ERROR_CONNECTING: + line = g_strdup_printf(_("# Connect failed: %s"), text); + break; + case GM_NET_ERROR_DISCONNECTED: + line = g_strdup_printf(_("# Connection lost... (%s)"), text); + break; + default: + line = g_strdup_printf(_("# Error: %s"), text); + break; + } + + gm_world_writeln(world, line); + g_free(line); +} + +void +on_gm_world_view_world_state_changing(GmWorld *world, guint state, + GmWorldView *view) { + gchar *line = NULL; + GmNetState pstate = gm_world_state(world); + + switch (state) { + case GM_NET_STATE_TRY_ADDRESS: + line = g_strdup_printf(_("# Trying %s port %s"), + gm_world_current_host(world), gm_world_current_port(world)); + break; + case GM_NET_STATE_CONNECTING: + line = g_strdup_printf(_("# Connecting to %s port %s"), + gm_world_current_host(world), gm_world_current_port(world)); + break; + case GM_NET_STATE_CONNECTED: + line = g_strdup(_("# Connected")); + break; + case GM_NET_STATE_DISCONNECTED: + if (pstate == GM_NET_STATE_CONNECTED || + pstate == GM_NET_STATE_DISCONNECTING) { + line = g_strdup(_("# Disconnected")); + } + break; + case GM_NET_STATE_DISCONNECTING: + line = g_strdup(_("# Disconnecting")); + break; + default: + break; + } + + if (line) { + gm_world_writeln(world, line); + g_free(line); + } +} + +void +on_gm_world_view_world_active_changed(GmWorld *world, gboolean active, + GmWorldView *view) { + if (active) { + gtk_widget_grab_focus(GTK_WIDGET(view->priv->text_view_input)); + } +} diff --git a/src/gm-world-view.h b/src/gm-world-view.h new file mode 100644 index 0000000..1e4d7ce --- /dev/null +++ b/src/gm-world-view.h @@ -0,0 +1,75 @@ +#ifndef __GM_WORLD_VIEW_H__ +#define __GM_WORLD_VIEW_H__ + +#include +#include "gm-support.h" +#include "gm-world.h" +#include "gm-world-input-view.h" + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GM_TYPE_WORLD_VIEW (gm_world_view_get_type()) +#define GM_WORLD_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD_VIEW, GmWorldView)) +#define GM_WORLD_VIEW_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD_VIEW, GmWorldView const)) +#define GM_WORLD_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_WORLD_VIEW, GmWorldViewClass)) +#define GM_IS_WORLD_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_WORLD_VIEW)) +#define GM_IS_WORLD_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_WORLD_VIEW)) +#define GM_WORLD_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_WORLD_VIEW, GmWorldViewClass)) + +/* Private structure type */ +typedef struct _GmWorldViewPrivate GmWorldViewPrivate; + +/* + * Main object structure + */ +typedef struct _GmWorldView GmWorldView; + +struct _GmWorldView { + GtkNotebook notebook; + + /*< private > */ + GmWorldViewPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GmWorldViewClass GmWorldViewClass; + +struct _GmWorldViewClass { + GtkNotebookClass parent_class; + + /* Signals */ +}; + +typedef enum _GmWorldViewSearchFlags { + GM_WORLD_VIEW_SEARCH_NONE = 0, + GM_WORLD_VIEW_SEARCH_FORWARDS = 1 << 0, + GM_WORLD_VIEW_SEARCH_BACKWARDS = 1 << 1 +} GmWorldViewSearchFlags; + +GType gm_world_view_get_type(void) G_GNUC_CONST; +GtkWidget *gm_world_view_new(GmWorld *world); + +gboolean gm_world_view_find_first(GmWorldView *view, const gchar *str, + GmWorldViewSearchFlags flags); +gboolean gm_world_view_find_next(GmWorldView *view, const gchar *str, + GmWorldViewSearchFlags flags); + +void gm_world_view_scroll_end_prepare(GmWorldView *view); +void gm_world_view_scroll_end(GmWorldView *view); +GmWorld *gm_world_view_world(GmWorldView *view); +GmWorldInputView *gm_world_view_input(GmWorldView *view); +GtkTextBuffer *gm_world_view_buffer(GmWorldView *view); +gboolean gm_world_view_text_active(GmWorldView *view); + +void gm_world_view_open_log(GmWorldView *view, const gchar *filename, + OpenLogProgress callback, gpointer user_data); +void gm_world_view_set_userlist_width(GmWorldView *view, gint width); +void gm_world_view_set_focus(GmWorldView *view); + +G_END_DECLS +#endif /* __GM_WORLD_VIEW_H__ */ diff --git a/src/gm-world.c b/src/gm-world.c new file mode 100644 index 0000000..f1da49b --- /dev/null +++ b/src/gm-world.c @@ -0,0 +1,876 @@ +#include +#include +#include + +#include "gm-world.h" +#include "gm-app.h" +#include "gm-triggers.h" +#include "gm-marshal.h" +#include "gm-net.h" +#include "gm-bogus.h" +#include "gm-support.h" +#include "debug.h" + +#define GM_WORLD_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_WORLD, GmWorldPrivate)) + +void gm_world_save_input_history(GmWorld *world); +void gm_world_load_input_history(GmWorld *world); +void gm_world_load_triggers(GmWorld *world); + +void on_gm_world_net_state_changing(GmNet *net, GmNetState state, + GmWorld *world); +void on_gm_world_net_net_error(GmNet *net, gchar *error, gint code, + GmWorld *world); +void on_gm_world_net_bytes_recv(GmNet *net, gchar *text, gint len, + GmWorld *world); +void on_gm_world_options_option_changed(GmOptions *options, gchar *key, + GmWorld *world); + +struct _GmWorldPrivate { + gchar *path; + gboolean loaded; + gboolean active; + guint activity; + gchar *buffer; + + GmOptions *options; + GmTriggers *triggers; + GmNet *net; + GmMcp *mcp; + GList *history; + GList *editors; + GmWorldInfo info; + GmEditingInfo editing_info; +}; + +/* Signals */ + +enum { + ACTIVATE_REQUEST, + LOAD, + UNLOAD, + STATE_CHANGING, + WORLD_ERROR, + TEXT_RECEIVED, + EDITOR_ADDED, + EDITOR_REMOVED, + NAME_CHANGED, + ACTIVE_CHANGED, + ACTIVITY_CHANGED, + DELETE, + NUM_SIGNALS +}; + +static guint world_signals[NUM_SIGNALS] = {0}; + +G_DEFINE_TYPE(GmWorld, gm_world, G_TYPE_OBJECT) + +static void +gm_world_finalize(GObject *object) { + GmWorld *world = GM_WORLD(object); + gchar *tmp_dir; + + gm_options_save(world->priv->options); + gm_world_save_input_history(world); + gm_triggers_save(world->priv->triggers); + + // Removing all tmp files + tmp_dir = g_strconcat(world->priv->path, "/tmp", NULL); + gm_directory_remove_all(tmp_dir, FALSE); + g_free(tmp_dir); + + g_list_free_simple(world->priv->history); + + g_free(world->priv->path); + g_free(world->priv->buffer); + + g_free(world->priv->editing_info.name); + g_free(world->priv->editing_info.upload); + g_list_free(world->priv->editing_info.lines); + + g_object_unref(world->priv->triggers); + g_object_unref(world->priv->options); + g_object_unref(world->priv->net); + G_OBJECT_CLASS(gm_world_parent_class)->finalize(object); +} + +static void +gm_world_class_init(GmWorldClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gm_world_finalize; + + world_signals[ACTIVATE_REQUEST] = + g_signal_new("activate_request", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmWorldClass, activate_request), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + world_signals[LOAD] = + g_signal_new("load", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmWorldClass, load), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + world_signals[UNLOAD] = + g_signal_new("unload", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmWorldClass, unload), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + world_signals[STATE_CHANGING] = + g_signal_new("state_changing", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmWorldClass, state_changing), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + world_signals[WORLD_ERROR] = + g_signal_new("world_error", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmWorldClass, world_error), + NULL, NULL, + gm_marshal_VOID__STRING_INT, + G_TYPE_NONE, + 2, + G_TYPE_STRING, + G_TYPE_INT); + + world_signals[TEXT_RECEIVED] = + g_signal_new("text_received", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmWorldClass, text_received), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + world_signals[EDITOR_ADDED] = + g_signal_new("editor_added", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmWorldClass, editor_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + world_signals[EDITOR_REMOVED] = + g_signal_new("editor_removed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmWorldClass, editor_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + world_signals[NAME_CHANGED] = + g_signal_new("name_changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmWorldClass, name_changed), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + world_signals[ACTIVE_CHANGED] = + g_signal_new("active_changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmWorldClass, active_changed), + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, + 1, + G_TYPE_BOOLEAN); + + world_signals[ACTIVITY_CHANGED] = + g_signal_new("activity_changed", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmWorldClass, activity_changed), + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, + 1, + G_TYPE_INT); + + g_type_class_add_private(object_class, sizeof(GmWorldPrivate)); +} + +void +gm_world_create_default_settings(GmWorld *world) { + const gchar *loc = gm_default_charset(); + + world->priv->options = gm_options_new(); + + gm_options_set(world->priv->options, "name", ""); + gm_options_set(world->priv->options, "autoload", "0"); + gm_options_set(world->priv->options, "host", ""); + gm_options_set(world->priv->options, "port", "1111"); + gm_options_set(world->priv->options, "player_name", ""); + gm_options_set(world->priv->options, "reconnect", "0"); + gm_options_set(world->priv->options, "password", ""); + gm_options_set(world->priv->options, "charset", (gchar *)loc); + gm_options_set(world->priv->options, "history_length", "500"); + + // Non configurable options + gm_options_set(world->priv->options, "pane_position", "150"); +} + +static void +gm_world_init(GmWorld *world) { + world->priv = GM_WORLD_GET_PRIVATE(world); + + gm_world_create_default_settings(world); + world->priv->path = NULL; + world->priv->loaded = FALSE; + world->priv->history = NULL; + world->priv->activity = 0; + world->priv->triggers = gm_triggers_new(); + world->priv->net = gm_net_new(); + world->priv->mcp = gm_mcp_new(world); + world->priv->buffer = NULL; + world->priv->editing_info.is_editing = FALSE; + + g_signal_connect(world->priv->net, "state_changing", + G_CALLBACK(on_gm_world_net_state_changing), world); + g_signal_connect(world->priv->net, "net_error", + G_CALLBACK(on_gm_world_net_net_error), world); + g_signal_connect(world->priv->net, "bytes_recv", + G_CALLBACK(on_gm_world_net_bytes_recv), world); + g_signal_connect(world->priv->options, "option_changed", + G_CALLBACK(on_gm_world_options_option_changed), world); +} + +void +gm_world_load_input_history(GmWorld *world) { + FILE *f; + gchar line[1024], *filename; + GString *str; + + filename = g_strconcat(world->priv->path, G_DIR_SEPARATOR_S, "history", + NULL); + + debug_msg(1, "GmWorld.LoadInputHistory: loading history (%s)!", filename); + + if ((f = fopen(filename, "r")) != NULL) { + str = g_string_new(""); + + while (fgets(line, 1024 - 1, f) != NULL) { + g_string_append(str, line); + if (line[strlen(line) - 1] == '\n') { + if (line[1] != '\0') { // Empty lines, we don't need to process those + world->priv->history = g_list_append(world->priv->history, + g_strndup(str->str, strlen(str->str) - 1)); + } + g_string_erase(str, 0, -1); + } + } + + g_string_free(str, TRUE); + fclose(f); + } else { + debug_msg(1, "GmWorld.LoadInputHistory: could not retrieve contents of " + "file %s (%s)", filename, strerror(errno)); + } + + g_free(filename); +} + +void +gm_world_save_input_history(GmWorld *world) { + FILE *f; + gchar *filename; + GList *elem; + + if (world->priv->path == NULL) { + return; + } + + filename = g_strconcat(world->priv->path, G_DIR_SEPARATOR_S, "history", NULL); + f = fopen(filename, "w"); + + if (f) { + debug_msg(1, "GmWorld.SaveInputHistory: saving input history to %s", + filename); + + for (elem = world->priv->history; elem; elem = elem->next) { + fprintf(f, "%s\n", (gchar *) (elem->data)); + } + + fclose(f); + + chmod(filename, 0660); + } else { + debug_msg(1, "GmWorld.SaveInputHistory: couldn't open history file (%s)" + " for saving: %s", filename, strerror(errno)); + } + + g_free(filename); +} + +void +gm_world_load_triggers(GmWorld *world) { + gchar *path; + + if (world->priv->triggers) { + g_object_unref(world->priv->triggers); + } + + path = g_strconcat(world->priv->path, G_DIR_SEPARATOR_S, "triggers", NULL); + world->priv->triggers = gm_triggers_new_from_file(path); + g_free(path); +} + +/* Public */ +GmWorld * +gm_world_new(gchar *path) { + GmWorld *world = GM_WORLD(g_object_new(GM_TYPE_WORLD, NULL)); + gchar *options_path; + + if (path != NULL) { + options_path = g_strconcat(path, "/settings", NULL); + + debug_msg(1, "GmWorld.new: creating new world for %s", path); + world->priv->path = g_strdup(path); + + debug_msg(1, "GmWorld.new: creating default world settings for %s", path); + gm_options_load(world->priv->options, options_path); + + if (strlen(gm_options_get(world->priv->options, "charset")) == 0) { + gm_options_set(world->priv->options, "charset", + gm_default_charset()); + } + + g_free(options_path); + + gm_world_load_input_history(world); + gm_world_load_triggers(world); + } + + /* CHECK: all done? */ + + return world; +} + +GmWorld * +gm_world_dup(GmWorld *source) { + GmWorld *copy = gm_world_new(NULL); + + g_object_unref(copy->priv->options); + + copy->priv->options = gm_options_dup(source->priv->options); + copy->priv->triggers = gm_triggers_dup(source->priv->triggers); + + return copy; +} + +void +gm_world_load(GmWorld *world) { + if (world->priv->loaded) { + g_signal_emit(world, world_signals[ACTIVATE_REQUEST], 0); + return; + } + + world->priv->loaded = TRUE; + g_signal_emit(world, world_signals[LOAD], 0); + g_signal_emit(world, world_signals[ACTIVATE_REQUEST], 0); + gm_world_connect(world); +} + +void +gm_world_unload(GmWorld *world) { + //GList *w; + + if (world->priv->loaded) { + world->priv->loaded = FALSE; + gm_world_disconnect(world); + + //TODO: Destroy editors + //editors_close(wld); + + g_signal_emit(world, world_signals[UNLOAD], 0); + } +} + +GmTriggers * +gm_world_triggers(GmWorld *world) { + return world->priv->triggers; +} + +GmOptions * +gm_world_options(GmWorld *world) { + return world->priv->options; +} + +const gchar * +gm_world_name(GmWorld *world) { + return gm_options_get(world->priv->options, "name"); +} + +const gchar * +gm_world_path(GmWorld *world) { + return world->priv->path; +} + +GList ** +gm_world_history(GmWorld *world) { + return &(world->priv->history); +} + +GmWorldInfo +gm_world_info(GmWorld *world) { + return world->priv->info; +} + +gboolean +gm_world_loaded(GmWorld *world) { + return world->priv->loaded; +} + +gboolean +gm_world_connected(GmWorld *world) { + return gm_net_state(world->priv->net) == GM_NET_STATE_CONNECTED; +} + +gboolean +gm_world_disconnected(GmWorld *world) { + return gm_net_state(world->priv->net) == GM_NET_STATE_DISCONNECTED; +} + +GmNetState +gm_world_state(GmWorld *world) { + return gm_net_state(world->priv->net); +} + +const gchar * +gm_world_current_host(GmWorld *world) { + return gm_net_current_host(world->priv->net); +} + +const gchar * +gm_world_current_port(GmWorld *world) { + return gm_net_current_port(world->priv->net); +} + +void +gm_world_connect(GmWorld *world) { + /*if (strlen(gm_options_get(world->priv->options, "host")) == 0) { + g_signal_emit(world, world_signals[WORLD_ERROR], 0, + _("World has no host, please fill in a host first")); + } else if (strlen(gm_options_get(world->priv->options, "port")) == 0) { + g_signal_emit(world, world_signals[WORLD_ERROR], 0, + _("World has no port, please fill in a port first")); + } else {*/ + gm_net_connect(world->priv->net, + gm_options_get(world->priv->options, "host"), + gm_options_get(world->priv->options, "port")); + //} +} + +void +gm_world_connect_to(GmWorld *world, gchar *host, gchar *port) { + gm_net_connect(world->priv->net, host, port); +} + +void +gm_world_disconnect(GmWorld *world) { + gm_net_disconnect(world->priv->net); +} + +void +gm_world_log(GmWorld *world, GmLogType type, gchar *text) { + FILE *f; + GString *s; + gchar *start, *log, *no_ansi; + struct tm *timet; + time_t timer; + + timer = time(0); + timet = localtime(&timer); + + log = g_strdup_printf("%s/logs/%04d-%02d-%02d.log", world->priv->path, + timet->tm_year + 1900, timet->tm_mon + 1, timet->tm_mday); + + f = fopen(log, "a"); + g_free(log); + + if (!f) { + return; + } + + start = g_strdup_printf("[%02d:%02d:%02d] ", timet->tm_hour, timet->tm_min, + timet->tm_sec); + + s = g_string_new(start); + g_free(start); + + switch (type) { + case LOG_IN: + s = g_string_append_c(s, '<'); + break; + case LOG_OUT: + s = g_string_append_c(s, '>'); + break; + case LOG_MCP_IN: + s = g_string_append(s, "[MCP] <"); + break; + case LOG_MCP_OUT: + s = g_string_append(s, "[MCP] >"); + break; + case LOG_STATUS: + s = g_string_append_c(s, '#'); + break; + } + + s = g_string_append(s, " "); + s = g_string_append(s, text); + + //no_ansi = gm_ansi_strip(g_strdup(s->str)); + no_ansi = g_strdup(s->str); + + if (no_ansi[strlen(no_ansi) - 1] != '\n') { + fputs(no_ansi, f); + fputc('\n', f); + } else { + fputs(no_ansi, f); + } + + fclose(f); + + g_free(no_ansi); + g_string_free(s, TRUE); +} + +void +gm_world_parse_legacy_editing_start(GmWorld *world, gchar *line) { + gchar *name_start, *upload_start; + GmEditingInfo *einfo = &(world->priv->editing_info); + + name_start = strstr(line, "name: "); + + if (!name_start) { + return; + } + + upload_start = strstr(line, " upload: "); + + if (!upload_start) { + return; + } + + einfo->is_editing = TRUE; + einfo->name = g_strndup(name_start + 6, (upload_start - name_start) - 6); + einfo->upload = g_strndup(upload_start + 9, (upload_start - line) + 9); + einfo->lines = NULL; +} + +void +gm_world_process_line(GmWorld *world, gchar *line) { + gchar *non_text_start = NULL; + GmEditingInfo *einfo = &(world->priv->editing_info); + + if (strncmp(line, "\x1B[0m", 4) == 0) { + non_text_start = g_strdup(line + 4); + } else { + non_text_start = g_strdup(line); + } + + if (einfo->is_editing) { + if (strcmp(non_text_start, ".") == 0) { + //TODO: implementation, create new editor object, invoke signal + gm_world_add_editor(world, gm_editor_new(einfo->name, einfo->upload, + einfo->lines)); + + einfo->is_editing = FALSE; + g_free(einfo->name); + einfo->name = NULL; + g_free(einfo->upload); + einfo->upload = NULL; + g_list_free(einfo->lines); + einfo->lines = NULL; + } else { + einfo->lines = g_list_append(einfo->lines, g_strdup(non_text_start)); + } + } else if (strncmp(non_text_start, "#$#", 3) == 0) { + if (strncasecmp(non_text_start + 3, " edit ", 6) == 0) { + gm_world_parse_legacy_editing_start(world, non_text_start + 9); + } else { + gm_mcp_handle(world->priv->mcp, non_text_start + 3); + //gm_world_log(world, LOG_MCP_IN, non_text_start); + } + } else { + if (!gm_world_active(world)) { + gm_world_set_activity(world, world->priv->activity + 1); + } + + if (strncmp(non_text_start, "#$\"", 3) == 0) { + if (strlen(non_text_start) != strlen(line)) { + g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, "\x1B[0m"); + } + + gm_world_log(world, LOG_IN, non_text_start + 3); + g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, non_text_start + 3); + } else { + g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, line); + gm_world_log(world, LOG_IN, line); + } + } + + g_free(non_text_start); +} + +void +gm_world_process_input(GmWorld *world, gchar *text) { +#ifdef HAVE_RUBY + gchar *space, *script, *argstr; + gboolean script_ran = FALSE; + + if (g_utf8_strlen(text, -1) > 1) { + if (text[0] == '/' && text[1] != '/') { + space = strstr(text, " "); + + if (space == NULL) { + script = g_strdup(text + 1); + argstr = g_strdup(""); + } else { + script = g_strndup(text + 1, (space - text) - 1); + argstr = g_strdup(space + 1); + } + + debug_msg(1, "GmWorld.ProcessInput: Trying script %s (%s)", script, + argstr); + script_ran = gm_scripts_run(gm_app_scripts(gm_app_instance()), + world, script, argstr); + + g_free(script); + g_free(argstr); + + if (script_ran) { + return; + } + } else if (text[0] == '/' && text[1] == '/') { + text = text + 1; + } + } +#endif + + gm_world_sendln(world, text); +} + +void +gm_world_writeln(GmWorld *world, gchar *text) { + gchar *newline = g_strconcat(text, "\n", NULL); + g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, newline); + + g_free(newline); +} + +void +gm_world_remove_editor(GmWorld *world, GmEditor *editor) { + g_return_if_fail(g_list_find(world->priv->editors, editor) != NULL); + + world->priv->editors = g_list_remove(world->priv->editors, editor); + g_signal_emit(world, world_signals[EDITOR_REMOVED], 0, G_OBJECT(editor)); + + g_object_unref(editor); +} + +void +gm_world_add_editor(GmWorld *world, GmEditor *editor) { + world->priv->editors = g_list_append(world->priv->editors, editor); + + g_signal_emit(world, world_signals[EDITOR_ADDED], 0, G_OBJECT(editor)); +} + +void +gm_world_sendln(GmWorld *world, gchar *text) { + gchar *normal; + GError *err = NULL; + gint written; + + // Convert text from utf-8 to the correct locale + normal = g_convert_with_fallback(text, g_utf8_strlen(text, -1), + gm_options_get(world->priv->options, "charset"), "UTF-8", "?", + NULL, &written, &err); + + if (!normal) { + debug_msg(1, "GmWorld.Send: conversion failed: %s (written %d)!", + err->message, written); + g_error_free(err); + err = NULL; + normal = g_strdup(text); + } + + gm_world_log(world, LOG_OUT, text); + gm_net_send_line(world->priv->net, normal); + g_free(normal); +} + +void +gm_world_set_active(GmWorld *world, gboolean active) { + world->priv->active = active; + + g_signal_emit(world, world_signals[ACTIVE_CHANGED], 0, active); + + if (active) { + gm_world_set_activity(world, 0); + } +} + +gboolean +gm_world_active(GmWorld *world) { + return world->priv->active; +} + +void +gm_world_set_activity(GmWorld *world, gint activity) { + world->priv->activity = activity; + + g_signal_emit(world, world_signals[ACTIVITY_CHANGED], 0, activity); +} + +gint +gm_world_activity(GmWorld *world) { + return world->priv->activity; +} + +void +gm_world_name_changed(GmWorld *world) { + gchar *tmp_path = world->priv->path ? g_strdup(world->priv->path) : NULL; + gchar *new_path = g_strconcat(gm_app_worlds_path(gm_app_instance()), "/", + gm_options_get(world->priv->options, "name"), NULL); + + if (tmp_path && strcmp(tmp_path, new_path) == 0) { + g_free(new_path); + g_free(tmp_path); + return; + } + + g_free(world->priv->path); + world->priv->path = new_path; + + if (tmp_path && g_file_test(tmp_path, G_FILE_TEST_EXISTS)) { + // Then! rename the old_path to the new_path + rename(tmp_path, world->priv->path); + } else if (!g_file_test(world->priv->path, G_FILE_TEST_EXISTS)) { + // There was no old path, so a new dir should be made + mkdir(world->priv->path, 0755); + } + + g_free(tmp_path); + + tmp_path = g_strconcat(world->priv->path, G_DIR_SEPARATOR_S, "settings", + NULL); + gm_options_save_as(world->priv->options, tmp_path); + g_free(tmp_path); + + tmp_path = g_strconcat(world->priv->path, G_DIR_SEPARATOR_S, "triggers", + NULL); + gm_triggers_save_as(world->priv->triggers, tmp_path); + g_free(tmp_path); + + g_signal_emit(world, world_signals[NAME_CHANGED], 0, + gm_options_get(world->priv->options, "name")); +} + +/* Callbacks */ + +void +on_gm_world_net_state_changing(GmNet *net, GmNetState state, GmWorld *world) { + g_signal_emit(world, world_signals[STATE_CHANGING], 0, state); +} + +void +on_gm_world_net_net_error(GmNet *net, gchar *error, gint code, + GmWorld *world) { + g_signal_emit(world, world_signals[WORLD_ERROR], 0, error, code); +} + +void +on_gm_world_net_bytes_recv(GmNet *net, gchar *text, gint len, + GmWorld *world) { + gchar *all, *utext, *ptr, *line, *p; + gsize read, written; + GError *err = NULL; + gint i; + + utext = g_convert_with_fallback(text, len, "UTF-8", + gm_options_get(world->priv->options, "charset"), + NULL, &read, &written, &err); + + if (!utext) { + debug_msg(1, "GmWorld.NetBytesRecv: conversion failed: %s (written %d, " + "read %d)!", err->message, written, read); + g_error_free(err); + err = NULL; + utext = g_strndup(text, len); + } + + if (world->priv->buffer != NULL) { + all = g_strconcat(world->priv->buffer, utext, NULL); + g_free(utext); + g_free(world->priv->buffer); + world->priv->buffer = NULL; + } else { + all = utext; + } + + line = (gchar *)(malloc((strlen(all) * sizeof(gchar)) + 1)); + i = 0; + p = all; + + /* Find lines in `all' and process them */ + for (ptr = all; *ptr != '\0'; ptr++) { + if (*ptr == '\n') { + line[i] = '\n'; + line[i + 1] = '\0'; + gm_world_log(world, LOG_MCP_IN, line); + gm_world_process_line(world, line); + p = ptr + 1; + i = 0; + } else if (*ptr != '\r') { + line[i] = *ptr; + i++; + } + } + + if (i > 0) { + world->priv->buffer = g_strdup(p); + } + + g_free(all); +} + +void +on_gm_world_options_option_changed(GmOptions *options, gchar *key, + GmWorld *world) { + if (strcmp(key, "name") == 0) { + gm_world_name_changed(world); + } +} diff --git a/src/gm-world.h b/src/gm-world.h new file mode 100644 index 0000000..9cc786d --- /dev/null +++ b/src/gm-world.h @@ -0,0 +1,136 @@ +#ifndef __GM_WORLD_H__ +#define __GM_WORLD_H__ + +#include +#include "gm-options.h" +#include "gm-net.h" +#include "gm-editor.h" +#include "gm-triggers.h" + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GM_TYPE_WORLD (gm_world_get_type()) +#define GM_WORLD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD, GmWorld)) +#define GM_WORLD_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD, GmWorld const)) +#define GM_WORLD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_WORLD, GmWorldClass)) +#define GM_IS_WORLD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_WORLD)) +#define GM_IS_WORLD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_WORLD)) +#define GM_WORLD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_WORLD, GmWorldClass)) + +typedef struct _GmWorldInfo { + gchar *homepage; + gchar *location; + gchar *admin; + gchar *contact; + gchar *charset; + gchar *language; + gchar *system; + gchar *logo; +} GmWorldInfo; + +/** \ingroup world + * \brief struct for world editing information + * + * Struct which contains various fields for storing editing information. This + * is only used if editing via mcp_simpleedit can't be used. + */ +typedef struct _GmEditingInfo { + gboolean is_editing; /**< are we editing at the moment? */ + GList *lines; /**< the lines to be edited */ + gchar *name; /**< the name of the editor */ + gchar *upload; /**< the command to send when sending the editor contents */ +} GmEditingInfo; + +/** \ingroup world + * \brief enum indicating log type + * + * Enumeration which indicates the log type + */ +typedef enum _GmLogType GmLogType; +enum _GmLogType { + LOG_IN, /**< incoming lines */ + LOG_OUT, /**< outgoing lines */ + LOG_MCP_IN, /**< mcp incoming lines */ + LOG_MCP_OUT, /**< mcp outgoing lines */ + LOG_STATUS /**< status lines (such as connecting information) */ +}; + +/* Private structure type */ +typedef struct _GmWorldPrivate GmWorldPrivate; + +/* + * Main object structure + */ +typedef struct _GmWorld GmWorld; + +struct _GmWorld { + GObject object; + + /*< private > */ + GmWorldPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GmWorldClass GmWorldClass; + +struct _GmWorldClass { + GObjectClass parent_class; + + /* Signals */ + void (* activate_request) (GmWorld *world); + void (* load) (GmWorld *world); + void (* unload) (GmWorld *world); + void (* state_changing) (GmWorld *world, guint state); + void (* world_error) (GmWorld *world, gchar *error, gint code); + void (* text_received) (GmWorld *world, gchar *text); + void (* editor_added) (GmWorld *world, GObject *editor); + void (* editor_removed) (GmWorld *world, GObject *editor); + void (* name_changed) (GmWorld *world, gchar *name); + void (* active_changed) (GmWorld *world, gboolean active); + void (* activity_changed) (GmWorld *world, gint activity); +}; + +GType gm_world_get_type(void) G_GNUC_CONST; +GmWorld *gm_world_new(gchar *path); + +GmWorld *gm_world_dup(GmWorld *source); +void gm_world_load(GmWorld *world); +void gm_world_unload(GmWorld *world); + +const gchar *gm_world_name(GmWorld *world); +GmOptions *gm_world_options(GmWorld *world); +GmWorldInfo gm_world_info(GmWorld *world); +const gchar *gm_world_path(GmWorld *world); +GList **gm_world_history(GmWorld *world); +gint gm_world_activity(GmWorld *world); +gboolean gm_world_active(GmWorld *world); +GmTriggers *gm_world_triggers(GmWorld *world); + +const gchar *gm_world_current_host(GmWorld *world); +const gchar *gm_world_current_port(GmWorld *world); + +void gm_world_set_name(GmWorld *world, const gchar *name); +void gm_world_set_activity(GmWorld *world, gint activity); +void gm_world_set_active(GmWorld *world, gboolean active); + +gboolean gm_world_loaded(GmWorld *world); +GmNetState gm_world_state(GmWorld *world); +gboolean gm_world_connected(GmWorld *world); +gboolean gm_world_disconnected(GmWorld *world); + +void gm_world_connect(GmWorld *world); +void gm_world_connect_to(GmWorld *world, gchar *host, gchar *port); +void gm_world_disconnect(GmWorld *world); +void gm_world_add_editor(GmWorld *world, GmEditor *editor); +void gm_world_remove_editor(GmWorld *world, GmEditor *editor); +void gm_world_sendln(GmWorld *world, gchar *text); +void gm_world_writeln(GmWorld *world, gchar *text); +void gm_world_process_input(GmWorld *world, gchar *text); + +G_END_DECLS +#endif /* __GM_WORLD_H__ */ diff --git a/src/gm-worlds-list-dialog.c b/src/gm-worlds-list-dialog.c new file mode 100644 index 0000000..5abf6c2 --- /dev/null +++ b/src/gm-worlds-list-dialog.c @@ -0,0 +1,466 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "gm-support.h" +#include "debug.h" +#include "gm-app.h" +#include "gm-worlds-list-dialog.h" +#include "gm-world-properties-dialog.h" +#include "gm-pixbuf.h" + +typedef struct _GmWorldsListDialog { + GladeXML *xml; + GtkWidget *dialog; + GtkTreeView *tree_view_worlds; + GtkTreeModel *model_worlds; +} GmWorldsListDialog; + +void on_gm_worlds_list_dialog_button_delete_clicked(GtkButton * utton, + gpointer user_data); +void on_gm_worlds_list_dialog_button_new_clicked(GtkButton *button, + gpointer user_data); +void on_gm_worlds_list_dialog_button_modify_clicked(GtkButton *button, + gpointer user_data); +void on_gm_worlds_list_dialog_button_duplicate_clicked(GtkButton *button, + gpointer user_data); +void on_gm_worlds_list_dialog_button_connect_clicked(GtkButton *button, + gpointer user_data); + +void on_gm_worlds_list_dialog_tree_view_worlds_key_press(GtkWidget *widget, + GdkEventKey *event, gpointer user_data); + +void on_gm_worlds_list_dialog_app_world_added(GmApp *app, GmWorld *world, + gpointer user_data); +void on_gm_worlds_list_dialog_app_world_removed(GmApp *app, GmWorld *world, + gpointer user_data); +void on_gm_worlds_list_dialog_world_option_changed(GmOptions *options, + const gchar *key, GmWorld *world); +void on_gm_worlds_list_dialog_response(GtkDialog *dialog, gint response, + gpointer user_data); + +static GmWorldsListDialog *gm_worlds_list_dialog_instance; + +enum { + LOGO_COLUMN, + NAME_COLUMN, + WORLD_COLUMN, + N_COLUMNS +}; + +GtkWidget * +gm_worlds_list_dialog_widget(gchar *name) { + return glade_xml_get_widget(gm_worlds_list_dialog_instance->xml, name); +} + +gchar * +gm_worlds_list_dialog_world_text(GmWorld *world) { + gchar *text; + const gchar *player, *server; + GmOptions *options = gm_world_options(world); + + player = gm_options_get(options, "player_name"); + server = gm_options_get(options, "host"); + + text = g_strconcat("", gm_options_get(options, "name"), + _("\nServer: "), + ((server && server[0] != '\0') ? server : _("unspecified")), + _("\nPlayer: "), + ((player && player[0] != '\0') ? player : _("unspecified")), + NULL); + + return text; +} + +static void +gm_worlds_list_dialog_add_world(GmWorld *world) { + GtkTreeIter iter; + GdkPixbuf *pix_logo = NULL; + gchar *name = gm_worlds_list_dialog_world_text(world); + const gchar *logo = gm_options_get(gm_world_options(world), "logo"); + GtkListStore *store = GTK_LIST_STORE( + gm_worlds_list_dialog_instance->model_worlds); + + if (logo) { + pix_logo = gm_pixbuf_get_at_size(logo, 32, 32); + } else { + pix_logo = gm_pixbuf_get_at_size("world.svg", 32, 32); + } + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, LOGO_COLUMN, pix_logo, NAME_COLUMN, + name, WORLD_COLUMN, world, -1); + + g_signal_connect(gm_world_options(world), "option_changed", + G_CALLBACK(on_gm_worlds_list_dialog_world_option_changed), world); + + g_free(name); +} + +#define G_WORLDS_LISTS_DIALOG_XML PACKAGE_DATA_DIR "/" PACKAGE \ + "/ui/gm-worlds-list.glade" + +static void +gm_worlds_list_dialog_populate_worlds() { + GmApp *app = gm_app_instance(); + GList *worlds = gm_app_worlds(app); + GList *item; + GmWorld *world; + + for (item = worlds; item; item = item->next) { + world = (GmWorld *)(item->data); + gm_worlds_list_dialog_add_world(world); + } + + g_list_free(worlds); +} + +static void +gm_worlds_list_dialog_create_tree_view_worlds() { + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkTreeView *tree_view; + GtkTreeModel *model; + + model = GTK_TREE_MODEL(gtk_list_store_new(N_COLUMNS, GDK_TYPE_PIXBUF, + G_TYPE_STRING, G_TYPE_POINTER)); + tree_view = GTK_TREE_VIEW(gm_worlds_list_dialog_widget("tree_view_worlds")); + gtk_tree_view_set_model(tree_view, model); + gtk_tree_selection_set_mode(gtk_tree_view_get_selection(tree_view), + GTK_SELECTION_MULTIPLE); + + renderer = gtk_cell_renderer_pixbuf_new(); + column = gtk_tree_view_column_new_with_attributes(_("Logo"), renderer, + "pixbuf", LOGO_COLUMN, NULL); + gtk_tree_view_column_set_min_width(column, 40); + gtk_tree_view_append_column(tree_view, column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, + "markup", NAME_COLUMN, NULL); + gtk_tree_view_append_column(tree_view, column); + + gm_worlds_list_dialog_instance->tree_view_worlds = tree_view; + gm_worlds_list_dialog_instance->model_worlds = model; + + gm_worlds_list_dialog_populate_worlds(); +} + +void +gm_worlds_list_dialog_run() { + GladeXML *xml; + + if (gm_worlds_list_dialog_instance) { + gtk_widget_show(gm_worlds_list_dialog_instance->dialog); + gtk_window_present(GTK_WINDOW(gm_worlds_list_dialog_instance->dialog)); + return; + } + + gm_worlds_list_dialog_instance = g_new0(GmWorldsListDialog, 1); + xml = glade_xml_new(G_WORLDS_LISTS_DIALOG_XML, "gm_worlds_list_dialog", + NULL); + gm_worlds_list_dialog_instance->xml = xml; + + gm_worlds_list_dialog_create_tree_view_worlds(); + + glade_xml_signal_connect(xml, "on_button_delete_clicked", + G_CALLBACK(on_gm_worlds_list_dialog_button_delete_clicked)); + glade_xml_signal_connect(xml, "on_button_new_clicked", + G_CALLBACK(on_gm_worlds_list_dialog_button_new_clicked)); + glade_xml_signal_connect(xml, "on_button_modify_clicked", + G_CALLBACK(on_gm_worlds_list_dialog_button_modify_clicked)); + glade_xml_signal_connect(xml, "on_button_duplicate_clicked", + G_CALLBACK(on_gm_worlds_list_dialog_button_connect_clicked)); + glade_xml_signal_connect(xml, "on_button_connect_clicked", + G_CALLBACK(on_gm_worlds_list_dialog_button_connect_clicked)); + + glade_xml_signal_connect(xml, "on_tree_view_worlds_key_press", + G_CALLBACK(on_gm_worlds_list_dialog_tree_view_worlds_key_press)); + + gm_worlds_list_dialog_instance->dialog = + gm_worlds_list_dialog_widget("gm_worlds_list_dialog"); + + g_signal_connect(gm_app_instance(), "world_added", + G_CALLBACK(on_gm_worlds_list_dialog_app_world_added), NULL); + g_signal_connect(gm_app_instance(), "world_removed", + G_CALLBACK(on_gm_worlds_list_dialog_app_world_removed), NULL); + + gtk_window_set_icon(GTK_WINDOW(gm_worlds_list_dialog_instance->dialog), + gm_pixbuf_get("world.svg")); + gtk_widget_show(gm_worlds_list_dialog_instance->dialog); + + g_signal_connect(gm_worlds_list_dialog_instance->dialog, "response", + G_CALLBACK(on_gm_worlds_list_dialog_response), NULL); +} + +static void +gm_worlds_list_dialog_delete_selected_worlds() { + GtkTreeView *tree_view = gm_worlds_list_dialog_instance->tree_view_worlds; + GtkTreeModel *model = gm_worlds_list_dialog_instance->model_worlds; + GtkTreeSelection *selection = gtk_tree_view_get_selection(tree_view); + GtkTreeIter iter; + GList *paths = gtk_tree_selection_get_selected_rows(selection, &model); + GList *refs = NULL; + GList *node = NULL; + gchar *message; + gpointer proxy = g_object_newv(G_TYPE_OBJECT, 0, NULL); + GmWorld *world; + + for (node = paths; node; node = node->next) { + refs = g_list_append(refs, + gtk_tree_row_reference_new_proxy(proxy, model, node->data)); + gtk_tree_path_free(node->data); + } + + g_list_free(paths); + + if (refs) { + for (node = refs; node; node = node->next) { + GtkTreePath *path = gtk_tree_row_reference_get_path(node->data); + + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, WORLD_COLUMN, &world, -1); + + if (!(world && gm_world_loaded(world))) { + gtk_list_store_remove(GTK_LIST_STORE(model), &iter); + gm_app_remove_world(gm_app_instance(), world); + } else { + message = g_strdup_printf(_ + ("Can't remove the world %s because it is loaded. " + "First close the world and then try again."), + gm_world_name(world)); + gm_error_dialog(message, + GTK_WINDOW(gm_worlds_list_dialog_instance->dialog)); + g_free(message); + } + + gtk_tree_row_reference_deleted(proxy, path); + gtk_tree_path_free(path); + gtk_tree_row_reference_free(node->data); + } + } else { + gm_error_dialog(_("You first need to select a world to delete."), + GTK_WINDOW(gm_worlds_list_dialog_instance->dialog)); + } + + g_list_free(refs); + g_object_unref(proxy); +} + +void +gm_worlds_list_dialog_modify_world() { + GtkTreeView *tree_view = gm_worlds_list_dialog_instance->tree_view_worlds; + GtkTreeModel *model = gm_worlds_list_dialog_instance->model_worlds; + GtkTreeSelection *selection = gtk_tree_view_get_selection(tree_view); + GtkTreeIter iter; + GList *first_path = gtk_tree_selection_get_selected_rows(selection, &model); + GmWorld *world; + + if (first_path) { + gtk_tree_model_get_iter(model, &iter, first_path->data); + gtk_tree_model_get(model, &iter, WORLD_COLUMN, &world, -1); + + gm_world_properties_dialog_run(world); + } else { + gm_error_dialog(_("You first need to select a world to modify."), + GTK_WINDOW(gm_worlds_list_dialog_instance->dialog)); + } + + g_list_free(first_path); +} + +GList * +gm_worlds_list_dialog_selected_path() { + GtkTreeView *tree_view = gm_worlds_list_dialog_instance->tree_view_worlds; + GtkTreeModel *model = gm_worlds_list_dialog_instance->model_worlds; + GtkTreeSelection *selection = gtk_tree_view_get_selection(tree_view); + + return gtk_tree_selection_get_selected_rows(selection, &model); +} + +GmWorld * +gm_worlds_list_dialog_selected_world() { + GtkTreeIter iter; + GList *first_path = gm_worlds_list_dialog_selected_path(); + GmWorld *world = NULL; + + if (first_path) { + gtk_tree_model_get_iter(gm_worlds_list_dialog_instance->model_worlds, + &iter, first_path->data); + gtk_tree_model_get(gm_worlds_list_dialog_instance->model_worlds, &iter, + WORLD_COLUMN, &world, -1); + } + + g_list_free(first_path); + return world; +} + +gboolean +gm_worlds_list_dialog_find(GmWorld *world, GtkTreeIter *iter) { + GtkTreeModel *model = gm_worlds_list_dialog_instance->model_worlds; + GmWorld *list_world; + + if (gtk_tree_model_get_iter_first(model, iter)) { + do { + gtk_tree_model_get(model, iter, WORLD_COLUMN, &list_world, -1); + + if (world == list_world) { + return TRUE; + } + } while (gtk_tree_model_iter_next(model, iter)); + } + + return FALSE; +} + +// Callbacks + +void +on_gm_worlds_list_dialog_response(GtkDialog *dialog, gint response, + gpointer user_data) { + g_object_unref(gm_worlds_list_dialog_instance->xml); + gtk_widget_destroy(gm_worlds_list_dialog_instance->dialog); + g_free(gm_worlds_list_dialog_instance); + gm_worlds_list_dialog_instance = NULL; +} + +void +on_gm_worlds_list_dialog_button_delete_clicked(GtkButton *button, + gpointer user_data) { + gm_worlds_list_dialog_delete_selected_worlds(); +} + +void +on_gm_worlds_list_dialog_tree_view_worlds_key_press(GtkWidget *widget, + GdkEventKey *event, gpointer user_data) { + switch (event->keyval) { + case GDK_D: case GDK_d: + gm_worlds_list_dialog_delete_selected_worlds(); + break; + case GDK_O: case GDK_o: + break; + case GDK_N: case GDK_n: + gm_world_properties_dialog_run_new(NULL); + break; + case GDK_M: case GDK_m: + gm_worlds_list_dialog_modify_world(); + break; + } +} + +void +on_gm_worlds_list_dialog_button_new_clicked(GtkButton *button, + gpointer user_data) { + gm_world_properties_dialog_run_new(NULL); +} + +void +on_gm_worlds_list_dialog_button_modify_clicked(GtkButton *button, + gpointer user_data) { + gm_worlds_list_dialog_modify_world(); +} + +void +on_gm_worlds_list_dialog_button_duplicate_clicked(GtkButton *button, + gpointer user_data) { + GmWorld *world = gm_worlds_list_dialog_selected_world(); + GmWorld *new_world; + + if (world) { + // now duplicate the world + new_world = gm_world_dup(world); + + // Yeah we got some world, now show the props! + gm_world_properties_dialog_run_new(new_world); + } else { + gm_error_dialog( + _("You first need to select a world to duplicate."), + GTK_WINDOW(gm_worlds_list_dialog_instance->dialog)); + } +} + +void +on_gm_worlds_list_dialog_button_connect_clicked(GtkButton *button, + gpointer user_data) { + GList *first_path = gm_worlds_list_dialog_selected_path(); + GList *path; + GmWorld *world; + GtkTreeIter iter; + + if (first_path) { + for (path = first_path; path; path = path->next) { + gtk_tree_model_get_iter( + gm_worlds_list_dialog_instance->model_worlds, &iter, + first_path->data); + gtk_tree_model_get(gm_worlds_list_dialog_instance->model_worlds, + &iter, WORLD_COLUMN, &world, -1); + gm_world_load(world); + } + } else { + gm_error_dialog( + _("You first need to select a world to connect to."), + GTK_WINDOW(gm_worlds_list_dialog_instance->dialog)); + } + + g_list_free(first_path); +} + +void +on_gm_worlds_list_dialog_app_world_added(GmApp *app, GmWorld *world, + gpointer user_data) { + gm_worlds_list_dialog_add_world(world); +} + +void +on_gm_worlds_list_dialog_app_world_removed(GmApp *app, GmWorld *world, + gpointer user_data) { + GtkTreeIter iter; + + if (gm_worlds_list_dialog_find(world, &iter)) { + gtk_list_store_remove(GTK_LIST_STORE( + gm_worlds_list_dialog_instance->model_worlds), &iter); + } +} + +void +on_gm_worlds_list_dialog_world_option_changed(GmOptions *options, + const gchar *key, GmWorld *world) { + GtkTreeIter iter; + GtkListStore *store = + GTK_LIST_STORE(gm_worlds_list_dialog_instance->model_worlds); + gchar *text; + const gchar *logo; + GdkPixbuf *pix_logo; + + if (!gm_worlds_list_dialog_find(world, &iter)) { + return; + } + + if (strcmp(key, "name") == 0 || + strcmp(key, "player_name") == 0 || + strcmp(key, "host") == 0) { + text = gm_worlds_list_dialog_world_text(world); + gtk_list_store_set(store, &iter, NAME_COLUMN, text, -1); + g_free(text); + } else if (strcmp(key, "logo")) { + logo = gm_options_get(options, "logo"); + + if (logo) { + pix_logo = gm_pixbuf_get_at_size(logo, 32, 32); + } else { + pix_logo = gm_pixbuf_get_at_size("world.svg", 32, 32); + } + + gtk_list_store_set(store, &iter, LOGO_COLUMN, pix_logo, -1); + } +} diff --git a/src/gm-worlds-list-dialog.h b/src/gm-worlds-list-dialog.h new file mode 100644 index 0000000..6d38b52 --- /dev/null +++ b/src/gm-worlds-list-dialog.h @@ -0,0 +1,6 @@ +#ifndef __GM_WORLDS_LIST_DIALOG__ +#define __GM_WORLDS_LIST_DIALOG__ + +void gm_worlds_list_dialog_run(); + +#endif /* __GM_WORLDS_LIST_DIALOG */ diff --git a/src/gtktemplate.c b/src/gtktemplate.c new file mode 100644 index 0000000..a1bf9af --- /dev/null +++ b/src/gtktemplate.c @@ -0,0 +1,57 @@ +#include +#include "gm-{template-}.h" + +#define GM_{TEMPLATE}_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_{TEMPLATE}, Gm{Template}Private)) + +struct _Gm{Template}Private { + +}; + +/* Signals + +enum { + PROTO + NUM_SIGNALS +}; + +static guint gm_{template_}_signals[NUM_SIGNALS] = {0};*/ + +G_DEFINE_TYPE(Gm{Template}, gm_{template_}, GTK_TYPE_{PARENT}) + +static void +gm_{template_}_finalize(GObject *object) { + //Gm{Template} *obj = GM_{TEMPLATE}(object); + + G_OBJECT_CLASS(gm_{template_}_parent_class)->finalize(object); +} + +static void +gm_{template_}_class_init(Gm{Template}Class *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gm_{template_}_finalize; + + /*gm_{template_}_signals[PROTO] = + g_signal_new("proto", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(Gm{Template}Class, proto), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0);*/ + + g_type_class_add_private(object_class, sizeof(Gm{Template}Private)); +} + +static void +gm_{template_}_init(Gm{Template} *obj) { + obj->priv = GM_{TEMPLATE}_GET_PRIVATE(obj); +} + +Gm{Template} * +gm_{template_}_new() { + Gm{Template} *obj = GM_{TEMPLATE}(g_object_new(GM_TYPE_{TEMPLATE}, NULL)); + + return obj; +} diff --git a/src/gtktemplate.h b/src/gtktemplate.h new file mode 100644 index 0000000..4cdc18a --- /dev/null +++ b/src/gtktemplate.h @@ -0,0 +1,50 @@ +#ifndef __GM_{TEMPLATE}_H__ +#define __GM_{TEMPLATE}_H__ + +#include + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GM_TYPE_{TEMPLATE} (gm_{template_}_get_type()) +#define GM_{TEMPLATE}(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_{TEMPLATE}, Gm{Template})) +#define GM_{TEMPLATE}_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_{TEMPLATE}, Gm{Template} const)) +#define GM_{TEMPLATE}_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_{TEMPLATE}, Gm{Template}Class)) +#define GM_IS_{TEMPLATE}(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_{TEMPLATE})) +#define GM_IS_{TEMPLATE}_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_{TEMPLATE})) +#define GM_{TEMPLATE}_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_{TEMPLATE}, Gm{Template}Class)) + +/* Private structure type */ +typedef struct _Gm{Template}Private Gm{Template}Private; + +/* + * Main object structure + */ +typedef struct _Gm{Template} Gm{Template}; + +struct _Gm{Template} { + Gtk{Parent} parent; + + /*< private > */ + Gm{Template}Private *priv; +}; + +/* + * Class definition + */ +typedef struct _Gm{Template}Class Gm{Template}Class; + +struct _Gm{Template}Class { + Gtk{Parent}Class parent_class; + + /* Signals + void (* proto) (Gm{Template} *obj); */ +}; + +GType gm_{template_}_get_type(void) G_GNUC_CONST; +Gm{Template} *gm_{template_}_new(void); + +G_END_DECLS +#endif /* __GM_{TEMPLATE}_H__ */ diff --git a/stamp-h.in b/stamp-h.in new file mode 100644 index 0000000..9788f70 --- /dev/null +++ b/stamp-h.in @@ -0,0 +1 @@ +timestamp diff --git a/ui/Makefile.am b/ui/Makefile.am new file mode 100644 index 0000000..56e7381 --- /dev/null +++ b/ui/Makefile.am @@ -0,0 +1,13 @@ +uidir = $(pkgdatadir)/ui +ui_DATA = gm-main.glade \ + gm-scripts.glade \ + gm-preferences.glade \ + gm-worlds-list.glade \ + gm-world-properties.glade \ + gm-triggers.glade \ + gm-world.glade \ + gm-editor.glade \ + gm-world-info.glade \ + gm-ui.xml + +EXTRA_DIST = $(ui_DATA) diff --git a/ui/fixglade.rb b/ui/fixglade.rb new file mode 100755 index 0000000..7755675 --- /dev/null +++ b/ui/fixglade.rb @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby1.8 + +files = Dir[File.dirname($0) + "/*.glade"] + +files.each do |path| + lines = IO.readlines(path) + File.rename(path, path + ".tmp") + + fw = File.new(path, "w"); + + lines.each do |line| + line = line.sub(/([^\/]+?<\/property>)/, '../pixmaps/\2') + fw.puts(line) + end + + fw.close +end diff --git a/ui/gm-editor.glade b/ui/gm-editor.glade new file mode 100644 index 0000000..9d6f436 --- /dev/null +++ b/ui/gm-editor.glade @@ -0,0 +1,80 @@ + + + + + + True + False + 0 + + + + True + GTK_ORIENTATION_HORIZONTAL + GTK_TOOLBAR_ICONS + True + True + + + + True + Save + gtk-save + True + True + False + + + + False + True + + + + + + True + Save and close + + True + ../pixmaps/saveclose.xpm + True + True + False + + + + False + True + + + + + + True + Close + gtk-close + True + True + False + + + + False + True + + + + + 0 + False + False + + + + + + + + + diff --git a/ui/gm-main.glade b/ui/gm-main.glade new file mode 100644 index 0000000..99690e9 --- /dev/null +++ b/ui/gm-main.glade @@ -0,0 +1,707 @@ + + + + + + + + + GnoeMoe + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + 700 + 500 + True + False + ../pixmaps/gnoemoe_logo.svg + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + True + + + + + + + True + True + + + + True + GTK_SHADOW_NONE + + + + True + + + + True + _World + True + + + + + + + True + Create a new world + New World... + True + + + + + + True + gtk-new + 1 + 0.5 + 0.5 + 0 + 0 + + + + + + + + True + + + + + + True + False + Connect or disconnect world + Connect + True + + + + + + True + gtk-network + 1 + 0.5 + 0.5 + 0 + 0 + + + + + + + + True + View world logs + Logs + True + + + + + True + gtk-file + 1 + 0.5 + 0.5 + 0 + 0 + + + + + + + + True + GNOMEUIINFO_MENU_ABOUT_ITEM + + + + + + + + + + + + True + + + + + + True + GNOMEUIINFO_MENU_CLOSE_ITEM + + + + + + + True + GNOMEUIINFO_MENU_EXIT_ITEM + + + + + + + + + + + True + _Edit + True + + + + + + + True + False + GNOMEUIINFO_MENU_CUT_ITEM + + + + + + + True + False + GNOMEUIINFO_MENU_COPY_ITEM + + + + + + + True + False + GNOMEUIINFO_MENU_PASTE_ITEM + + + + + + + True + + + + + + True + Worlds... + True + + + + + + + + True + False + Current World... + True + + + + + + + + True + + + + + + True + GNOMEUIINFO_MENU_FIND_ITEM + + + + + + + True + GNOMEUIINFO_MENU_FIND_AGAIN_ITEM + + + + + + + True + + + + + + True + _Preferences + True + + + + + True + gtk-preferences + 1 + 0.5 + 0.5 + 0 + 0 + + + + + + + + + + + + True + GNOMEUIINFO_MENU_VIEW_TREE + + + + + + + True + MCP + True + + + + + True + ../pixmaps/terminal.png + 0.5 + 0.5 + 0 + 0 + + + + + + + + True + Scripts + True + + + + + True + ../pixmaps/editor.xpm + 0.5 + 0.5 + 0 + 0 + + + + + + + + + + + + True + GNOMEUIINFO_MENU_HELP_TREE + + + + + + + True + GNOMEUIINFO_MENU_ABOUT_ITEM + + + + + + + + + + + + BONOBO_DOCK_TOP + 0 + 0 + 0 + BONOBO_DOCK_ITEM_BEH_EXCLUSIVE|BONOBO_DOCK_ITEM_BEH_NEVER_VERTICAL|BONOBO_DOCK_ITEM_BEH_LOCKED + + + + + + 3 + True + False + 3 + + + + False + 3 + + + + 3 + True + False + 6 + + + + True + Find: + False + True + GTK_JUSTIFY_LEFT + False + False + 1 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + True + True + 0 + + True + * + False + + + + + + 0 + False + False + + + + + + True + True + GTK_RELIEF_NORMAL + True + + + + + True + 0.5 + 0.5 + 0 + 0 + 0 + 0 + 0 + 0 + + + + True + False + 2 + + + + True + gtk-find + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + Volgende zoeken + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + + 0 + False + False + + + + + + True + True + gtk-close + True + GTK_RELIEF_NORMAL + True + + + + 0 + False + False + GTK_PACK_END + + + + + + True + True + Search backwards + True + GTK_RELIEF_NORMAL + True + True + False + True + + + 0 + False + False + + + + + 0 + False + True + + + + + + True + + + 0 + False + True + + + + + 0 + False + True + + + + + + + 0 + True + True + + + + + + True + Select log file to open + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_CENTER_ON_PARENT + True + 300 + 300 + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + True + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_END + + + + True + True + True + gtk-close + True + GTK_RELIEF_NORMAL + True + -7 + + + + + + True + True + True + gtk-open + True + GTK_RELIEF_NORMAL + True + -5 + + + + + 0 + False + True + GTK_PACK_END + + + + + + 6 + True + False + 6 + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + False + True + False + True + False + False + False + + + + + + 0 + True + True + + + + + + GTK_PROGRESS_LEFT_TO_RIGHT + 0 + 0.10000000149 + PANGO_ELLIPSIZE_NONE + + + 0 + False + False + + + + + 0 + True + True + + + + + + + diff --git a/ui/gm-preferences.glade b/ui/gm-preferences.glade new file mode 100644 index 0000000..c960671 --- /dev/null +++ b/ui/gm-preferences.glade @@ -0,0 +1,1539 @@ + + + + + + + 500 + 450 + True + Preferences + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_CENTER + False + False + True + True + True + True + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_END + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + -6 + + + + + + True + True + True + gtk-apply + True + GTK_RELIEF_NORMAL + True + -10 + + + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + -5 + + + + + 0 + False + True + GTK_PACK_END + + + + + + 12 + True + True + True + True + GTK_POS_TOP + False + False + + + + 6 + 16 + 32 + True + False + 0 + + + + 3 + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + 6 + True + 1 + 2 + False + 6 + 6 + + + + True + Font-family: + False + False + GTK_JUSTIFY_LEFT + False + False + 1 + 0.5 + 0 + 0 + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + True + True + True + True + True + True + + + 1 + 2 + 0 + 1 + + + + + + + + + True + Font + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + label_item + + + + + 0 + False + False + + + + + False + True + + + + + + True + Font + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + tab + + + + + + 6 + True + False + 0 + + + + 3 + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + True + Use high colors for bold text + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + + + + + True + Bold + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + label_item + + + + + 0 + False + True + + + + + + 3 + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + 6 + True + 2 + 3 + False + 3 + 3 + + + + True + Foreground: + False + False + GTK_JUSTIFY_LEFT + False + False + 1 + 0.5 + 0 + 0 + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + Background: + False + False + GTK_JUSTIFY_LEFT + False + False + 1 + 0.5 + 0 + 0 + + + 0 + 1 + 1 + 2 + fill + + + + + + + True + Foreground + True + False + True + + + 1 + 2 + 0 + 1 + fill + + + + + + + True + Foreground High + True + False + True + + + 2 + 3 + 0 + 1 + fill + + + + + + + True + Background + True + False + True + + + 1 + 2 + 1 + 2 + fill + + + + + + + + + + + True + Default + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + label_item + + + + + 0 + False + False + + + + + + 3 + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + 6 + True + 2 + 8 + False + 3 + 3 + + + + True + Black + True + False + True + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + Red + True + False + True + + + 1 + 2 + 0 + 1 + fill + + + + + + + True + Red High + True + False + True + + + 1 + 2 + 1 + 2 + fill + + + + + + + True + Green + True + False + True + + + 2 + 3 + 0 + 1 + fill + + + + + + + True + Green High + True + False + True + + + 2 + 3 + 1 + 2 + fill + + + + + + + True + Yellow + True + False + True + + + 3 + 4 + 0 + 1 + fill + + + + + + + True + Yellow High + True + False + True + + + 3 + 4 + 1 + 2 + fill + + + + + + + True + Blue + True + False + True + + + 4 + 5 + 0 + 1 + fill + + + + + + + True + Blue High + True + False + True + + + 4 + 5 + 1 + 2 + fill + + + + + + + True + Purple + True + False + True + + + 5 + 6 + 0 + 1 + fill + + + + + + + True + Purple High + True + False + True + + + 5 + 6 + 1 + 2 + fill + + + + + + + True + Cyan + True + False + True + + + 6 + 7 + 0 + 1 + fill + + + + + + + True + Cyan High + True + False + True + + + 6 + 7 + 1 + 2 + fill + + + + + + + True + White + True + False + True + + + 7 + 8 + 0 + 1 + fill + + + + + + + True + White High + True + False + True + + + 7 + 8 + 1 + 2 + fill + + + + + + + True + Black High + True + False + True + + + 0 + 1 + 1 + 2 + fill + + + + + + + + + + + True + Foreground + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + label_item + + + + + 0 + False + True + + + + + + 3 + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + 6 + True + 1 + 8 + False + 3 + 3 + + + + True + Black + True + False + True + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + Red + True + False + True + + + 1 + 2 + 0 + 1 + fill + + + + + + + True + Green + True + False + True + + + 2 + 3 + 0 + 1 + fill + + + + + + + True + Yellow + True + False + True + + + 3 + 4 + 0 + 1 + fill + + + + + + + True + Blue + True + False + True + + + 4 + 5 + 0 + 1 + fill + + + + + + + True + Purple + True + False + True + + + 5 + 6 + 0 + 1 + fill + + + + + + + True + Cyan + True + False + True + + + 6 + 7 + 0 + 1 + fill + + + + + + + True + White + True + False + True + + + 7 + 8 + 0 + 1 + fill + + + + + + + + + + + True + Background + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + label_item + + + + + 0 + False + True + + + + + False + True + + + + + + True + Colors + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + tab + + + + + + 6 + True + False + 0 + + + + 3 + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 3 + + + + True + True + Use alternative editor + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + 0 + False + False + + + + + + True + True + True + True + 0 + + True + * + False + + + 0 + False + False + + + + + + True + True + Embed editor into GnoeMoe (implies terminal) + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + 0 + False + False + + + + + + True + True + Needs terminal + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + + + + + True + Program + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + label_item + + + + + 0 + False + False + + + + + + 3 + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + 3 + True + False + 6 + + + + 200 + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + False + True + False + True + + + + + 0 + False + True + + + + + + True + False + 0 + + + + 6 + True + False + 6 + + + + True + False + Bold + True + GTK_RELIEF_NORMAL + True + False + False + + + + + True + gtk-bold + 4 + 0.5 + 0.5 + 0 + 0 + + + + + 0 + False + False + + + + + + True + False + Italic + True + GTK_RELIEF_NORMAL + True + False + False + + + + + True + gtk-italic + 4 + 0.5 + 0.5 + 0 + 0 + + + + + 0 + False + False + + + + + + True + False + Underline + True + GTK_RELIEF_NORMAL + True + False + False + + + + + True + gtk-underline + 4 + 0.5 + 0.5 + 0 + 0 + + + + + 0 + False + False + + + + + + True + False + Strikethrough + True + GTK_RELIEF_NORMAL + True + False + False + + + + + True + gtk-strikethrough + 4 + 0.5 + 0.5 + 0 + 0 + + + + + 0 + False + False + + + + + 0 + False + False + + + + + + 6 + True + 2 + 2 + False + 6 + 6 + + + + True + False + True + Foreground + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + False + True + Background + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + 0 + 1 + 1 + 2 + fill + + + + + + + True + False + True + False + True + + + + 1 + 2 + 0 + 1 + fill + + + + + + + True + False + True + False + True + + + + 1 + 2 + 1 + 2 + fill + + + + + + 0 + True + True + + + + + 0 + True + True + + + + + + + + + + True + Colors + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + label_item + + + + + 0 + True + True + + + + + False + True + + + + + + True + Editor + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + tab + + + + + 0 + True + True + + + + + + + diff --git a/ui/gm-scripts.glade b/ui/gm-scripts.glade new file mode 100644 index 0000000..05a5086 --- /dev/null +++ b/ui/gm-scripts.glade @@ -0,0 +1,357 @@ + + + + + + + True + Scripts + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + 500 + 400 + True + False + ../pixmaps/editor.xpm + True + True + True + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + + + + + 6 + True + True + True + True + GTK_POS_TOP + False + False + + + + 6 + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + 3 + True + True + True + False + True + GTK_JUSTIFY_LEFT + GTK_WRAP_WORD + True + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + False + True + + + + + + True + Console + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + tab + + + + + + 6 + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + False + True + False + True + False + False + False + + + + + False + True + + + + + + True + Overview + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + tab + + + + + + True + False + 3 + + + + 3 + True + GTK_ORIENTATION_HORIZONTAL + GTK_TOOLBAR_BOTH + True + True + + + + 3 + True + gtk-new + True + True + False + + + + False + True + + + + + + 3 + True + gtk-save + True + True + False + + + + False + True + + + + + + 3 + True + gtk-save-as + True + True + False + + + + False + True + + + + + + 2 + True + False + gtk-delete + True + True + False + + + + False + True + + + + + 0 + False + False + + + + + + 3 + True + True + 150 + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + True + True + False + True + False + False + False + + + + + + True + False + + + + + + True + False + 0 + + + + 3 + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + + + + 0 + True + True + + + + + + True + False + + + 0 + False + False + + + + + True + True + + + + + 0 + True + True + + + + + False + True + + + + + + True + Editor + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + tab + + + + + + + diff --git a/ui/gm-triggers.glade b/ui/gm-triggers.glade new file mode 100644 index 0000000..bd44da6 --- /dev/null +++ b/ui/gm-triggers.glade @@ -0,0 +1,459 @@ + + + + + + + Triggers + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + True + 400 + 300 + True + True + ../pixmaps/world.svg + True + True + True + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + True + + + + True + False + 6 + + + + True + GTK_BUTTONBOX_END + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + -6 + + + + + + True + False + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + -5 + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + False + False + GTK_POS_TOP + False + False + + + + + 6 + True + False + 6 + + + + True + Choose which type of event you want to create a trigger for. + False + False + GTK_JUSTIFY_LEFT + True + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + False + True + False + False + False + False + False + + + + + 0 + True + True + + + + + + True + GTK_BUTTONBOX_END + 6 + + + + True + True + True + gtk-media-next + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + False + True + + + + + False + True + + + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + tab + + + + + + 6 + True + 4 + 2 + False + 6 + 6 + + + + True + Name: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + True + True + True + 0 + + True + * + False + + + 1 + 2 + 0 + 1 + + + + + + + True + Actions: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0 + 0 + 3 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 3 + 4 + fill + fill + + + + + + True + + + 0 + 2 + 2 + 3 + fill + fill + + + + + + True + Conditions: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0 + 0 + 3 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 1 + 2 + fill + fill + + + + + + True + False + 6 + + + + True + False + 6 + + + + True + True + gtk-add + True + GTK_RELIEF_NORMAL + True + + + + 0 + False + True + GTK_PACK_END + + + + + 0 + False + True + GTK_PACK_END + + + + + 1 + 2 + 1 + 2 + fill + + + + + + True + False + 6 + + + + True + False + 6 + + + + True + True + gtk-add + True + GTK_RELIEF_NORMAL + True + + + + 0 + False + True + GTK_PACK_END + + + + + 0 + False + True + GTK_PACK_END + + + + + 1 + 2 + 3 + 4 + fill + + + + + False + True + + + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + tab + + + + + 0 + True + True + + + + + + + diff --git a/ui/gm-ui.xml b/ui/gm-ui.xml new file mode 100644 index 0000000..f0b707a --- /dev/null +++ b/ui/gm-ui.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/gm-world-info.glade b/ui/gm-world-info.glade new file mode 100644 index 0000000..bb90899 --- /dev/null +++ b/ui/gm-world-info.glade @@ -0,0 +1,559 @@ + + + + + + + True + World info + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + 300 + 300 + True + False + True + True + True + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + + + + + True + False + 6 + + + + True + GTK_BUTTONBOX_END + + + + True + True + True + gtk-close + True + GTK_RELIEF_NORMAL + True + -7 + + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 6 + + + + 6 + True + 2 + 2 + False + 6 + 6 + + + + True + <b>Name:</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + <b>Email:</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 1 + 1 + 2 + fill + + + + + + + True + True + + False + True + GTK_JUSTIFY_LEFT + False + True + 0 + 0.5 + 0 + 0 + + + 1 + 2 + 0 + 1 + + + + + + + True + True + + False + True + GTK_JUSTIFY_LEFT + False + True + 0 + 0.5 + 0 + 0 + + + + 1 + 2 + 1 + 2 + + + + + + 0 + True + True + + + + + + + 0 + False + True + + + + + + + + + + True + <b>Administrator</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + label_item + + + + + 0 + False + True + + + + + + True + + + 0 + False + True + + + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + 6 + True + 5 + 2 + False + 6 + 6 + + + + True + <b>Homepage:</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + <b>Character set:</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 1 + 4 + 5 + fill + + + + + + + True + <b>Language:</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 1 + 1 + 2 + fill + + + + + + + True + <b>Location:</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 1 + 2 + 3 + fill + + + + + + + True + <b>System:</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 1 + 3 + 4 + fill + + + + + + + True + True + + False + True + GTK_JUSTIFY_LEFT + False + True + 0 + 0.5 + 0 + 0 + + + + 1 + 2 + 0 + 1 + + + + + + + True + True + + False + True + GTK_JUSTIFY_LEFT + False + True + 0 + 0.5 + 0 + 0 + + + 1 + 2 + 1 + 2 + fill + + + + + + + True + True + + False + True + GTK_JUSTIFY_LEFT + False + True + 0 + 0.5 + 0 + 0 + + + 1 + 2 + 2 + 3 + fill + + + + + + + True + True + + False + True + GTK_JUSTIFY_LEFT + False + True + 0 + 0.5 + 0 + 0 + + + 1 + 2 + 3 + 4 + fill + + + + + + + True + True + + False + True + GTK_JUSTIFY_LEFT + False + True + 0 + 0.5 + 0 + 0 + + + 1 + 2 + 4 + 5 + fill + + + + + + + + + + + True + <b>World</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + label_item + + + + + 0 + True + True + + + + + + + diff --git a/ui/gm-world-properties.glade b/ui/gm-world-properties.glade new file mode 100644 index 0000000..92ac6af --- /dev/null +++ b/ui/gm-world-properties.glade @@ -0,0 +1,864 @@ + + + + + + + 400 + 350 + True + World properties + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + False + False + True + True + True + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + False + True + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_END + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + -6 + + + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + -5 + + + + + 0 + False + True + GTK_PACK_END + + + + + + 12 + True + True + True + True + GTK_POS_TOP + False + False + + + + 6 + True + False + 0 + + + + 3 + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + 6 + True + 2 + 2 + False + 6 + 6 + + + + True + True + True + True + 0 + + True + * + False + + + 1 + 2 + 0 + 1 + + + + + + + True + Name: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + True + Load on startup + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 1 + 2 + 1 + 2 + fill + + + + + + + + + True + General + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + False + + + + + + 3 + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + 6 + True + 3 + 2 + False + 6 + 6 + + + + True + True + True + True + 0 + + True + * + False + + + 1 + 2 + 0 + 1 + + + + + + + True + Host: + False + False + GTK_JUSTIFY_LEFT + False + False + 1 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + Port: + False + False + GTK_JUSTIFY_LEFT + False + False + 1 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 1 + 2 + fill + + + + + + + True + True + Use autoreconnect + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 1 + 2 + 2 + 3 + + + + + + + True + True + 1 + 0 + True + GTK_UPDATE_ALWAYS + False + False + 1111 1000 99999 1 10 10 + + + 1 + 2 + 1 + 2 + + + + + + + + + True + Server + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + False + + + + + + + + + False + True + + + + + + True + World + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + tab + + + + + + 6 + True + False + 0 + + + + 3 + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + 6 + True + 2 + 2 + False + 6 + 6 + + + + True + True + True + True + 0 + + True + * + False + + + 1 + 2 + 0 + 1 + + + + + + + True + True + True + False + 0 + + True + * + False + + + 1 + 2 + 1 + 2 + + + + + + + True + Password: + False + False + GTK_JUSTIFY_LEFT + False + False + 1 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 1 + 2 + fill + + + + + + + True + Player name: + False + False + GTK_JUSTIFY_LEFT + False + False + 1 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 0 + 1 + fill + + + + + + + + + True + Auto-login + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + False + + + + + + + + + False + True + + + + + + True + Autologin + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + tab + + + + + + 6 + True + False + 0 + + + + 3 + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + 6 + True + 1 + 2 + False + 6 + 6 + + + + True + Charset: + False + False + GTK_JUSTIFY_LEFT + False + False + 1 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + False + True + True + + + 1 + 2 + 0 + 1 + + + + + + + + + True + Language + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + False + + + + + + + + + False + True + + + + + + True + Language + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + tab + + + + + + 6 + True + False + 6 + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + False + True + True + False + False + False + False + + + + + + 0 + True + True + + + + + + True + GTK_BUTTONBOX_START + 6 + + + + True + True + True + gtk-add + True + GTK_RELIEF_NORMAL + True + + + + + + + True + False + True + True + gtk-edit + True + GTK_RELIEF_NORMAL + True + + + + + + + True + False + True + True + gtk-remove + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + False + True + + + + + False + True + + + + + + True + Triggers + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + tab + + + + + 0 + True + True + + + + + + + diff --git a/ui/gm-world.glade b/ui/gm-world.glade new file mode 100644 index 0000000..f082921 --- /dev/null +++ b/ui/gm-world.glade @@ -0,0 +1,242 @@ + + + + + + + True + window1 + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + + + + + True + False + False + GTK_POS_BOTTOM + True + False + + + + + + True + False + 0 + + + + True + True + 1 + + + + 3 + True + False + 3 + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_ALWAYS + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + False + False + True + GTK_JUSTIFY_LEFT + GTK_WRAP_WORD + False + 0 + 0 + 0 + 3 + 3 + 0 + + + + + + + + 0 + True + True + + + + + + True + True + GTK_POLICY_NEVER + GTK_POLICY_NEVER + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + 6 + True + True + True + False + True + GTK_JUSTIFY_LEFT + GTK_WRAP_WORD + True + 1 + 1 + 0 + 3 + 3 + 0 + + + + + + + 0 + False + False + + + + + True + True + + + + + + 150 + True + True + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + 150 + False + False + False + False + False + False + False + + + + + True + False + + + + + 0 + True + True + + + + + + 3 + True + False + + + 0 + False + False + + + + + False + True + + + + + + True + False + 3 + + + + True + 0.5 + 0.5 + 0 + 0 + + + 0 + True + True + + + + + + True + World + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + tab + + + + + + + diff --git a/ui/gm-worlds-list.glade b/ui/gm-worlds-list.glade new file mode 100644 index 0000000..cf0eb98 --- /dev/null +++ b/ui/gm-worlds-list.glade @@ -0,0 +1,310 @@ + + + + + + + 400 + 300 + True + Worlds listing + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_CENTER + False + 400 + 300 + False + False + True + True + True + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + False + True + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_END + + + + True + True + True + gtk-close + True + GTK_RELIEF_NORMAL + True + -7 + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + False + 0 + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + False + True + False + True + False + False + False + + + + + + 0 + True + True + + + + + + 6 + True + GTK_BUTTONBOX_START + 6 + + + + True + True + True + gtk-new + True + GTK_RELIEF_NORMAL + True + + + + + + + 2 + True + True + True + GTK_RELIEF_NORMAL + True + + + + + True + 0.5 + 0.5 + 0 + 0 + 0 + 0 + 0 + 0 + + + + True + False + 2 + + + + True + gtk-copy + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + _Duplicate + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + + + + + True + True + True + gtk-edit + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + gtk-delete + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + GTK_RELIEF_NORMAL + True + + + + + True + 0.5 + 0.5 + 0 + 0 + 0 + 0 + 0 + 0 + + + + True + False + 2 + + + + True + gtk-network + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + _Connect + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + + + + 6 + False + True + + + + + 0 + True + True + + + + + + +