commit bad14bdfddf00d46f9aca8f6b88d426f80c5bccb Author: Jesse van den Kieboom Date: Sun Oct 9 08:53:36 2005 +0000 Initial import 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 0000000..299a635 Binary files /dev/null and b/pixmaps/terminal.png differ diff --git a/pixmaps/tray/Makefile.am b/pixmaps/tray/Makefile.am new file mode 100644 index 0000000..bcd2cc3 --- /dev/null +++ b/pixmaps/tray/Makefile.am @@ -0,0 +1,7 @@ +traydir = $(pkgdatadir)/pixmaps/tray +tray_DATA = default.svg \ + active.svg \ + activate.svg \ + notify.svg + +EXTRA_DIST = $(tray_DATA) diff --git a/pixmaps/tray/activate.svg b/pixmaps/tray/activate.svg new file mode 100644 index 0000000..6c9e68c --- /dev/null +++ b/pixmaps/tray/activate.svg @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 0000000..7043522 Binary files /dev/null and b/po/nl.gmo differ 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 0000000..626b93d Binary files /dev/null and b/src/.swp differ diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..53bfca7 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,47 @@ +## Process this file with automake to produce Makefile.in +SUBDIRS = test +INCLUDES = \ + -DPACKAGE_DATA_DIR=\""$(datadir)"\" \ + -DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \ + @PACKAGE_CFLAGS@ \ + @RUBYINCLUDE@ + +bin_PROGRAMS = gnoemoe +BUILT_SOURCES = gm-marshal.c gm-marshal.h + +gnoemoe_SOURCES = $(BUILT_SOURCES) \ + gm-app.c gm-app.h gm-app-view.c gm-app-view.h \ + gm-options.c gm-options.h gm-color-table.c gm-color-table.h \ + gm-net.c gm-net.h gm-triggers.c gm-triggers.h \ + gm-world.c gm-world.h gm-world-view.c gm-world-view.h \ + gm-world-tab.c gm-world-tab.h \ + gm-world-text-view.c gm-world-text-view.h \ + gm-string.c gm-string.h gm-support.c gm-support.h \ + gm-editor.c gm-editor.h \ + gm-world-input-view.c gm-world-input-view.h \ + gm-pixbuf.c gm-pixbuf.h debug.c debug.h \ + gm-world-info-dialog.c gm-world-info-dialog.h \ + gm-world-logs-dialog.c gm-world-logs-dialog.h \ + gm-worlds-list-dialog.c gm-worlds-list-dialog.h \ + gm-world-properties-dialog.c gm-world-properties-dialog.h \ + gm-triggers-dialog.c gm-triggers-dialog.h \ + gm-scripts-dialog.c gm-scripts-dialog.h + +if HAVE_SCRIPT +gnoemoe_SOURCES += gm-scripts.c gm-scripts.h +endif +gm-marshal.h: gm-marshal.list $(GLIB_GENMARSHAL) + $(GLIB_GENMARSHAL) $< --header --prefix=gm_marshal > $@ + +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 + + + + + + +